2018/3/13

  • 星期二晚上到货
  • 取开发资料
  • 浏览用户手册

2018/3/14

  • 根据用户手册进行模块接线
  • 接入串口,利用开发包中的上位机对指纹模块进行操作
  • 用串口调试软件,输入相应的指令,进行指纹采集等操作,但进行相应的操作总是无法实现
  • 原因:对原有指令进行了改造,而校验码并没有改变
  • 对上位机软件进行改造,使其输出相应操作流程的指令

    在OEMHostDlg.cpp中查找对应的按钮执行的内容

  • 1.找到void COEMHostDlg::OnBtnVerify()

void COEMHostDlg::OnBtnVerify() 
{
    int     w_nRet, w_nTmplNo, w_nFpStatus, w_nLearned;
    DWORD   w_dwTime;
    CString w_strTmp;

    UpdateData(TRUE);

    if (!CheckUserID())
        return;

    EnableControl(FALSE);
    GetDlgItem(IDC_BTN_DISCONNECT)->EnableWindow(FALSE);
    GetDlgItem(IDC_BTN_STOP)->EnableWindow(TRUE);
    
    m_bCancel = FALSE;

    w_nTmplNo = m_nUserID;

    //. Check if fp is exist
    w_nRet = m_clsCommu.Run_GetStatus(w_nTmplNo, &w_nFpStatus);
    
    if( w_nRet != ERR_SUCCESS )
    {
        m_strCmdResult = GetErrorMsg(w_nRet);
        goto l_exit;
    }
    
    if( w_nRet == ERR_SUCCESS && w_nFpStatus == GD_TEMPLATE_EMPTY )
    {
        m_strCmdResult = _T("Template is empty");
        goto l_exit;
    }
    
    m_clsCommu.Run_SLEDControl(1);

    m_strCmdResult = _T("Input your finger.");
    UpdateData(FALSE);
    
    w_dwTime = GetTickCount();
    
    //. Get Image
    while (1)
    {
        DoEvents();

        if(m_bCancel)
        {
            m_strCmdResult = _T("Operation Canceled");
            goto l_exit;
        }

        //. Get Image
        w_nRet = m_clsCommu.Run_GetImage();
        
        if(w_nRet == ERR_SUCCESS)
            break;
        else if(w_nRet == ERR_CONNECTION)
        {
            m_strCmdResult = GetErrorMsg(w_nRet);
            goto l_exit;
        }                   
    }
    
    m_strCmdResult = "Release your finger";
    UpdateData(FALSE);
    DoEvents();

    //. Up Image
    if (m_chkShowImage.GetCheck())
    {
        m_strCmdResult = _T("Uploading image...");
        UpdateData(FALSE);
        DoEvents();

        w_nRet = m_clsCommu.Run_UpImage(0, g_FpImageBuf, &g_nImageWidth, &g_nImageHeight);

        if(w_nRet != ERR_SUCCESS)
        {
            m_strCmdResult = GetErrorMsg(w_nRet);
            goto l_exit;
        }   

        m_wndFinger.SetImage(g_FpImageBuf, g_nImageWidth, g_nImageHeight);

        m_strCmdResult = _T("Uploading image is successed.");
        UpdateData(FALSE);
        DoEvents();
    }

    w_dwTime = GetTickCount();

    //. Create Template
    w_nRet = m_clsCommu.Run_Generate(0);
    
    if (w_nRet != ERR_SUCCESS)
    {
        m_strCmdResult = GetErrorMsg(w_nRet);
        goto l_exit;
    }

    //. Verify 
    w_nRet = m_clsCommu.Run_Verify(w_nTmplNo, 0, &w_nLearned);
    
    w_dwTime = GetTickCount() - w_dwTime;

    if (w_nRet == ERR_SUCCESS)
    {
        m_strCmdResult.Format(_T("Result : Success\r\nTemplate No : %d, Learn Result : %d\r\nMatch Time : %dms"), w_nTmplNo, w_nLearned, w_dwTime );
    }
    else
    {
        m_strCmdResult = GetErrorMsg(w_nRet);
    }
    
l_exit:
    m_clsCommu.Run_SLEDControl(0);
    UpdateData(FALSE);  
    EnableControl(TRUE);
    GetDlgItem(IDC_BTN_DISCONNECT)->EnableWindow(TRUE);
}

2.查看m_clsCommu.Run_SLEDControl(0),跳转到Communication.cpp

int CCommunication::Run_SLEDControl(int p_nState)
{
    BOOL    w_bRet;
    BYTE    w_abyData[2];
    
    w_abyData[0] = LOBYTE(p_nState);
    w_abyData[1] = HIBYTE(p_nState);
    
    InitCmdPacket(CMD_SLED_CTRL, m_bySrcDeviceID, m_byDstDeviceID, (BYTE*)&w_abyData, 2);
    
    SEND_COMMAND(CMD_SLED_CTRL, w_bRet, m_bySrcDeviceID, m_byDstDeviceID);
    
    if(!w_bRet)
        return ERR_CONNECTION;
    
    return RESPONSE_RET;
}

3.查看SEND_COMMAND定义,跳转到Command.cpp

BOOL SendCommand(WORD p_wCMDCode, BYTE p_bySrcDeviceID, BYTE p_byDstDeviceID)
{
    DWORD   w_nSendCnt = 0;
    LONG    w_nResult = 0;

    g_Serial.Purge();   

    ::SendMessage(g_hMainWnd, WM_CMD_PACKET_HOOK, 0, 0);

    w_nResult = g_Serial.Write(g_Packet, g_dwPacketSize , &w_nSendCnt, NULL, COMM_TIMEOUT);

    if(ERROR_SUCCESS != w_nResult)
    {
        return FALSE;
    }

    return ReceiveAck(p_wCMDCode, p_bySrcDeviceID, p_byDstDeviceID);
}

4.可以看到串口输出函数g_Serial.Write,g_packet应该为输出的内容,现在要做的就是将g_packet打印出来 5.顺势根据return ReceiveAck(p_wCMDCode, p_bySrcDeviceID, p_byDstDeviceID)语句找到ReceiveAck函数,分析可知发送了消息通过ReceiveAck函数接收响应内容,则在此也添加Print_gPacket(false)以打印接收的内容

BOOL ReceiveAck(WORD p_wCMDCode, BYTE p_bySrcDeviceID, BYTE p_byDstDeviceID)
{
    Print_gPacket(true);
    DWORD   w_nAckCnt = 0;
    LONG    w_nResult = 0;
    DWORD   w_dwTimeOut = COMM_TIMEOUT;

    if (p_wCMDCode == CMD_TEST_CONNECTION)
        w_dwTimeOut = 2000;
    else if (p_wCMDCode == CMD_ADJUST_SENSOR)
        w_dwTimeOut = 30000;
    
l_read_packet:

    //w_nResult = g_Serial.Read(g_Packet, sizeof(ST_RCM_PACKET), &w_nAckCnt, NULL, w_dwTimeOut);    
    if (!ReadDataN(g_Packet, sizeof(ST_RCM_PACKET), w_dwTimeOut))
    {
        return FALSE;
    }

    g_dwPacketSize = sizeof(ST_RCM_PACKET); 

    ::SendMessage(g_hMainWnd, WM_RCM_PACKET_HOOK, 0, 0);

    if (!CheckReceive(g_Packet, sizeof(ST_RCM_PACKET), RCM_PREFIX_CODE, p_wCMDCode))
        return FALSE;
    
    if (g_pCmdPacket->m_byDstDeviceID != p_bySrcDeviceID)
    {
        goto l_read_packet;
    }

    Print_gPacket(false);
    return TRUE;
}

6.由于该软件为mfc程序,所以在command添加如下代码,在输出的debug内容中显示相应的内容

#include "windows.h"
#ifdef _DEBUG    

#define DP0(fmt) {TCHAR sOut[256];_stprintf_s(sOut,_T(fmt));OutputDebugString(sOut);}    
#define DP1(fmt,var) {TCHAR sOut[256];_stprintf_s(sOut,_T(fmt),var);OutputDebugString(sOut);}    
#define DP2(fmt,var1,var2) {TCHAR sOut[256];_stprintf_s(sOut,_T(fmt),var1,var2);OutputDebugString(sOut);}    
#define DP3(fmt,var1,var2,var3) {TCHAR sOut[256];_stprintf_s(sOut,_T(fmt),var1,var2,var3);OutputDebugString(sOut);}    

#endif

7.添加一个函数,用于打印g_packet,

/******************************
 create by Jacky  2018/3/14
 To print command
 type true  for send
      false for recive
*******************************/
void Print_gPacket(bool type) {
    if (type) {
        DP0("发送的数据:");
    }
    else {
        DP0("接收的数据:");
    }
    for (int i = 0; i < 26; i++) {
        DP1("%02x", g_Packet[i]);
    }
    DP0("\n");
}
  • 总结出采集验证指纹的过程总共有以下几条指令
*****************************
*    指纹模块一次指纹比对 
*    host命令
*****************************
// 采集指纹
55aa000020000000000000000000000000000000000000001f01

// 模板生成到rambuffer0
55aa000060000200000000000000000000000000000000006101

// 1:N对比
55aa00006300060000000100f401000000000000000000005e02

// 关灯
55aa000024000200000000000000000000000000000000002501

2018/3/15

  • 由于esp8266与指纹模块通讯的过程中串口的数据交换并不可见,所以想到透传
  • 上午上完课回来,进行透传程序Transmission的编写
#include <ESP8266WiFi.h>

const char *ssid = "";
const char *password = "";

const int tcpPort = 80;//要建立的TCP服务的端口号

WiFiServer server(tcpPort);


void setup()
{
    Serial.begin(115200);
    delay(10);
    Serial.println();
    Serial.println();
    Serial.print("Connecting to ");
    Serial.println(ssid);

    WiFi.begin(ssid, password);

    while (WiFi.status() != WL_CONNECTED)
    {
        delay(500);
        Serial.print(".");
    }

    Serial.println("");
    Serial.println("WiFi connected");
    Serial.println("IP address: ");
    Serial.println(WiFi.localIP());
    server.begin();
}


void loop()
{
    WiFiClient client = server.available();
    if (client) {
        while (client.connected()) {
            /*while (client.available()) {
            char c = client.read();
            Serial.write(c);
            }*/

            if(client.available()){
            size_t counti=client.available();
            uint8_t *buf=new uint8_t[counti];
            Serial.write((const uint8_t*)buf,counti);
            delete(buf);
            }

            delay(1000);   //由于指纹模块是首先发送响应头再发响应数据,若不加这句,响应数据会分两次读取

            if (Serial.available())
            {
                size_t counti = Serial.available();
                uint8_t *sbuf = new uint8_t[counti];
                Serial.readBytes(sbuf, counti);
                client.write((const uint8_t*)sbuf, counti);

                delete(sbuf);
            }
        }
        client.stop();
    }

}
  • 注:
  • 1.有坑,请看注释
  • 2.遇到下列报错:
Compiling debug version of 'Transmission' for 'NodeMCU 1.0 (ESP-12E Module)'
 
Error compiling project sources
Debug build failed for project 'Transmission'
ESP8266WiFi.h:39: In file included from
Transmission.ino: from
WiFiClient.h: In instantiation of size_t WiFiClient::write(T&, size_t) [with T = unsigned char*; size_t = unsigned int]
Transmission.ino:60: required from here
 
WiFiClient.h: 123:36: error: request for member 'available' in 'source', which is of non-class type 'unsigned char*
   size_t left = source.available()
 
WiFiClient.h: 127:5: error: request for member 'read' in 'source', which is of non-class type 'unsigned char*
   source.read(buffer.get(), will_send)

原因是client的write函数必须传入一个buffer,但是通过网上查找解决方案,发现在第一个参数前加上(const uint8_t*)进行强制转换也可以编译通过

2018/3/16

透传实现了,那么就开始改造原有的mySmartLock.ino,首先第一版的程序构思为:

循环 * 1.读取指纹模块的手指感应信号(有信号就逐条发送指纹验证指令) * 2.读取网络端是否传来请求(有就判断请求url是否包含指定的字符串) * 3.读取透传客户端是否已连接(若已连接且传入debug()就更改标志,(考虑到指令录指纹时会造成录指纹和验证指纹指令的冲突)停止1的指纹验证,并允许串口穿透输入与输出,若为exit()就修改标志,重新启用1的指纹验证,只输出串口通讯的信息)

第一版的编程实现,采用的串行处理模式

  • 坑1:在判断串口传入数据是否为debug()或exit()的过程中,用到了strcmp()函数,但是貌似读取出来的unsigned char*类型的字节流是不带结束符的而”debug()“是带结束符的长度为8的char数组,所以一直判断不等,经过查阅资料,发现还有一个函数就是strncmp()可以进行一定长度字符串的比较,所以改用 strncmp((const char*)command, "debug()", counti)语句进行判断,经测试完全可行!!

但是问题来了,在arduino的loop()函数中,是循环执行的,当透传客户端连接上之后,除非用while (client.connected())语句进行连接的保持,否则当前的transclient实例会在一次循环后被销毁,但是若保持连接又无法输出当前指纹模块与esp8266的通讯交互信息

发现还是程序的状态图没分清楚,构思总结出改进的三个状态

  • 1.透传连接,并通过debug()进入debug模式
  • 2.透传连接,保持默认状态,或在debug模式下通过exit()退出调试模式的,就只输出esp8266与指纹通讯的信息
  • 3.串口没有连接,esp8266与指纹模块正常通讯

编程实现

  • 1.将指纹验证和读取网络请求两部分封装成loopBody(bool modeSwitch)
  • 2.用modeSwitch标记是否进入了debug模式,通过判断modeSwitch来判断是否进行透传输出,modeSwitch通过是否传入debug()和exit()命令进行切换
  • 3.在transclient可用时进入while(client.connected())的循环,可以进行回话的保持,返回当前串口通讯的数据
  • 4.源码见github