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