本文主要是介绍MQTT协议的智能家居之指纹锁,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
基于之前的智能家居项目,这次添加了指纹锁设备的设计,硬件端主要使用0.96oled显示屏,ESP8266-NODEMCU,BYJ48步进电机以及配套驱动板,AS608指纹模块。
整个项目除了硬件端,还接入了我自己的服务器,可通过APP进行云端远程操控,对指纹锁进行模式设置和指纹设置。
装置器件图放在下面,以供参考:
附上演示视频:
智能家居-基于mqtt协议的APP控制指纹锁装置
代码通过arduino编写,也一并贴在下面
/*** 本项目用于AS608指纹模块使用,使用到GPIO5(D1)引脚作为模拟输出TX引脚*/
#include <SoftwareSerial.h>
#include <FS.h>
#include <U8g2lib.h> //U8g2lib库
#include <Wire.h> //Wire库
#include <Ticker.h> //Ticker库
#include <ESP8266WebServer.h>
#include <WiFiManager.h>
#include <ArduinoJson.h>
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#define PRODUCT_ID "TOP" //服务器用户名
#define API_KEY "tx6WM==zmW21Z2pt4susBRlHMuY="//用户密码
#define DEVICE_ID "door_device"//设备名
#define TOPIC "door_control"//订阅主题
//
bool door_clock=false;
//
WiFiClient wifiClient;
int count = 0; //ticker1控制 数据上传下发的间隔时间(s)
PubSubClient mqttClient(wifiClient);
const char* mqttServer = "***********;//服务器地址
const uint16_t mqttPort =****;//mqtt接口端口
char msgJson[75];//存json下发信息数据
char msg_buf[200];//存json上传数据及标识位
SoftwareSerial mySerial(D3,D4);
const unsigned char welcome[] = {0X30,0X01,0X00,0X30,0X00,0X40,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X7C,0X00,0X00,0XC0,0X9F,0X1F,
0XFF,0X01,0X00,0XFC,0XC0,0X30,0X82,0X00,0X00,0X80,0X60,0X20,0XC2,0X00,0X00,0XC0,
0X20,0X20,0X62,0X00,0X00,0XC0,0X20,0X30,0X3A,0X00,0X00,0X40,0X30,0X10,0X0F,0X00,
0X00,0X40,0X10,0X18,0X03,0X00,0X00,0X40,0X20,0X0C,0X03,0X00,0X00,0X40,0XE0,0X07,
0X03,0X00,0X00,0X00,0XC0,0X01,0X02,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XE0,0X00,0X00,0X00,
0X00,0X00,0XF8,0X7F,0X00,0X00,0X00,0X00,0XFC,0X7F,0X00,0X00,0X00,0X00,0XFE,0XFF,
0X00,0X00,0X00,0X00,0XFF,0XFF,0X01,0X00,0X00,0X00,0XFF,0XFF,0X01,0X00,0X00,0X80,
0XFF,0XFC,0X03,0X00,0X00,0X80,0X1F,0XF0,0X03,0X00,0X00,0XC0,0X1F,0XE0,0X07,0X00,
0X00,0XC0,0X0F,0XC0,0X07,0X00,0X00,0XC0,0X0F,0XC0,0X07,0X00,0X00,0XC0,0X03,0X00,
0X07,0X00,0X00,0XC0,0X01,0X20,0X07,0X00,0X00,0XC0,0X7D,0XFC,0X02,0X00,0X00,0X80,
0X00,0X00,0X02,0X00,0X00,0X00,0X00,0X7A,0X03,0X00,0X00,0X00,0X18,0X10,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X80,0X02,0X00,0X00,0X00,0X00,
0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0X00,0XE0,0X0F,0X00,0X00,
0X00,0X00,0X82,0X44,0X00,0X00,0X00,0XE0,0X07,0X60,0X00,0X00,0X00,0XF8,0X0F,0XF0,
0X00,0X00,0X00,0XFC,0X37,0XF0,0X03,0X00,0X00,0XFE,0XC7,0XFF,0X07,0X00,0X80,0XFF,
0X07,0XFF,0X1F,0X00,0XC0,0XFD,0X07,0X70,0X3F,0X00,0XF8,0XFF,0X03,0X70,0XFF,0X01,
0XFE,0XFF,0X07,0X70,0XFE,0X0F,0XFF,0XFF,0X09,0X78,0XFE,0X1F,0XFF,0XFF,0X03,0X7C,
0XFC,0X3F,0XFF,0XFF,0X23,0X7E,0XFC,0X3F,0XFF,0XFF,0XC3,0X3F,0XF8,0X7F,0XFF,0XFF,
0X87,0X3F,0XF8,0XFF,0XFF,0XFF,0X07,0X18,0XF8,0XFF,0XFF,0XFF,0X07,0X00,0XF8,0XFF,
0XFF,0XFF,0X07,0X00,0XF8,0XFF,0XFF,0XFF,0X0F,0X00,0XF8,0XFF,0XFF,0XFF,0X3F,0X00,
0XF8,0XFF,0XFF,0XFF,0XDF,0X00,0XF8,0XFF,0XFF,0XFF,0X1F,0X00,0XF8,0XFF,0XFF,0XFF,
0X1F,0X00,0XF0,0XFF,0XFF,0XFF,0X1F,0X00,0XF0,0XFF,0XFF,0XFF,0X1F,0X00,0XF0,0XFF,};
U8G2_SSD1306_128X64_NONAME_F_SW_I2C u8g2(U8G2_R0, SCL, SDA, U8X8_PIN_NONE);//D7 GPIO13 D8 GPIO15// D1 D2
byte buf_getfinger[20]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x01,0x00,0x05};//录一个指纹
byte buf_clear[20]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x0d,0x00,0x11};//清空指纹信息
byte buf_delete[20]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x07,0x0c,0x00,0x01,0x11};//删除指纹信息 本条只是模板需要更改
byte buf_finger_char[20]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0X04,0x02,0x01,0x00,0x08};//生成指纹特征
byte buf_identity[20]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0X03,0x11,0x00,0x15};//工作模式,读取指纹数据进行比对 返回ID
byte buf_getlist[20]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0x03,0x1f,0x00,0x23};//获取指纹目录,数据是001010101格式 不是很好用,所以采用了新方法使用(在录入指纹时编写记事本文件存储)
byte buf_add[20]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF,0x01,0x00,0X03,0x10,0x00,0x14};//注册添加指纹 返回ID
byte buf_logo[6]={0xEF,0x01,0xFF,0xFF,0xFF,0xFF};
byte buf[32];
String file_name = "/AS608/users.txt"; //被读取的文件位置和名称
String file_list="";
int work_index=0;
void setup() {Serial.begin(57600);mySerial.begin(57600);//不采用外部按钮控制,不够灵活且占用引脚,改为软件控制,空出的引脚可作为门锁开关电机控制/*pinMode(D5,INPUT_PULLUP);//正常工作模式pinMode(D6,INPUT_PULLUP);//添加模式pinMode(D7,INPUT_PULLUP);//删除模式pinMode(D8,INPUT_PULLUP);//查询指纹列表digitalWrite(D6,HIGH);digitalWrite(D7,HIGH);digitalWrite(D5,HIGH);digitalWrite(D8,HIGH);*///控制电机pinMode(D5,OUTPUT);pinMode(D6,OUTPUT);pinMode(D7,OUTPUT);pinMode(D8,OUTPUT);pinMode(D0,OUTPUT);////pinMode(10,OUTPUT);//pinMode(9,OUTPUT);// WiFi.mode(WIFI_STA);WiFiManager wifiManager;// 自动连接WiFi。以下语句的参数是连接ESP8266时的WiFi名称wifiManager.autoConnect("门锁WiFi设置");mqttClient.setServer(mqttServer, mqttPort);// 设置MQTT订阅回调函数mqttClient.setCallback(receiveCallback);connectMQTTServer();if(SPIFFS.begin()){ // 启动SPIFFSSerial.println("SPIFFS Started.");} else {Serial.println("SPIFFS Failed to Start.");}u8g2.begin();//允许显示UTF8字符u8g2.enableUTF8Print();//文本的参考点为左上角u8g2.setFontPosTop();u8g2.clearBuffer();u8g2.drawXBMP(40, 0, 48, 64, welcome);u8g2.sendBuffer();delay(1000);
}
void connectMQTTServer() {String clientId = DEVICE_ID;String productId = PRODUCT_ID;String apiKey = API_KEY;// 连接MQTT服务器if (mqttClient.connect(clientId.c_str(), productId.c_str(), apiKey.c_str())) {Serial.println("MQTT Server Connected.");Serial.println("Server Address: ");Serial.println(mqttServer);Serial.println("ClientId:");Serial.println(clientId);subscribeTopic(); // 订阅指定主题} else {Serial.print("MQTT Server Connect Failed. Client State:");Serial.println(mqttClient.state());delay(1000);}
}
// 订阅指定主题
void subscribeTopic() {// 这么做是为确保不同设备使用同一个MQTT服务器测试消息订阅时,所订阅的主题名称不同String topicString = TOPIC;char subTopic[topicString.length() + 1];strcpy(subTopic, topicString.c_str());// 通过串口监视器输出是否成功订阅主题以及订阅的主题名称if (mqttClient.subscribe(subTopic)) {Serial.println("Subscrib Topic:");Serial.println(subTopic);} else {Serial.print("Subscribe Fail...");}
}
//获取下发指令topic 指定主题 payload 下发信息,以字节存储 length 下发信息长度
void receiveCallback(char* topic, byte* payload, unsigned int length) {Serial.print("Message Received [");Serial.print(topic);Serial.print("] ");String receiveMessage;for (int i = 0; i < length; i++) {Serial.print((char)payload[i]);receiveMessage += (char)payload[i];}Serial.println("----" + receiveMessage + "----");Serial.print("Message Length(Bytes) ");Serial.println(length);///StaticJsonDocument<128> doc;deserializeJson(doc, receiveMessage);String door_control = doc["door_cmd"]; // String door_message = doc["message"];//Serial.print(control);if(door_control=="add"){Serial.println("添加指纹模式");work_index=1;String msg;StaticJsonDocument<48> doc;doc["door_msg"] = "add ok";serializeJson(doc, msg);char publishMsg[msg.length() + 1];strcpy(publishMsg, msg.c_str());int lens = strlen(publishMsg);mqttClient.publish("getMessage", publishMsg, lens); }else if(door_control=="delete"){Serial.println("删除指纹模式");work_index=2;String msg;StaticJsonDocument<48> doc;doc["door_msg"] = "delete ok";serializeJson(doc, msg);char publishMsg[msg.length() + 1];strcpy(publishMsg, msg.c_str());int lens = strlen(publishMsg);mqttClient.publish("getMessage", publishMsg, lens); }else if(door_control=="list"){Serial.println("查看指纹模式");work_index=3;File dataFile = SPIFFS.open(file_name, "r"); String user_list;String msg;for(int i=0;i<dataFile.size();i++){user_list+=(char)dataFile.read();}dataFile.close(); StaticJsonDocument<80> doc;doc["door_msg"] = user_list;serializeJson(doc, msg);char publishMsg[msg.length() + 1];strcpy(publishMsg, msg.c_str());int lens = strlen(publishMsg);mqttClient.publish("getMessage", publishMsg, lens); }else if(door_control=="work"){Serial.println("工作模式");work_index=0;String msg;StaticJsonDocument<48> doc;doc["door_msg"] = "work ok";serializeJson(doc, msg);char publishMsg[msg.length() + 1];strcpy(publishMsg, msg.c_str());int lens = strlen(publishMsg);mqttClient.publish("getMessage", publishMsg, lens);}
}
void finger_add(){u8g2.setFontPosTop();u8g2.setFont(u8g2_font_crox2hb_tr);u8g2.clearBuffer();u8g2.drawStr(30, 0, "add finger");u8g2.drawLine(0, 14, 128, 14);u8g2.sendBuffer();mySerial.write(buf_add, 12);u8g2.setFont(u8g2_font_crox2hb_tr);u8g2.drawStr(0, 20, "pressing");delay(2000);int i = 0;/* while (Serial.available()<6) {delay(1000);u8g2.drawStr(i+64, 20, ".");Serial.println("正在连接");i = i + 5;u8g2.sendBuffer();}*/if(Serial.available()>6){size_t len = Serial.available(); //返回可读数据的长度uint8_t sbuf[len];Serial.readBytes(sbuf, len);if(sbuf[6]==0x07){//Serial.println("验证指纹");if(sbuf[9]==0x00){u8g2.drawStr(0, 34, "ok!");u8g2.drawStr(0, 48, "new ID:");u8g2.setCursor(80, 48);u8g2.print(sbuf[10]);u8g2.print(sbuf[11]);u8g2.sendBuffer();Serial.println("注册成功,存在");Serial.print("ID:");Serial.print(sbuf[10]);Serial.print(sbuf[11]);Serial.println();File dataFile = SPIFFS.open(file_name, "a");dataFile.print("用户");dataFile.print(sbuf[10]);dataFile.println(sbuf[11]); dataFile.println(","); dataFile.close(); }else if(sbuf[9]==0x01){Serial.println("识别错误,请重新按下指纹");u8g2.drawStr(0, 20, "error!");u8g2.drawStr(0, 22, "press again!");u8g2.sendBuffer();}else if(sbuf[9]==0x1e){Serial.println("注册失败!");u8g2.drawStr(0, 20, "fail!");u8g2.sendBuffer();}delay(1000);}}}
void getlist(){//读取文件内容并且通过串口监视器输出文件信息u8g2.setFontPosTop();u8g2.setFont(u8g2_font_crox2hb_tr);u8g2.clearBuffer();u8g2.drawStr(30, 0, "finger list");u8g2.drawLine(0, 14, 128, 14);u8g2.sendBuffer();u8g2.setCursor(0, 26);File dataFile = SPIFFS.open(file_name, "r"); String user_list;for(int i=0;i<dataFile.size();i++){user_list+=(char)dataFile.read();}u8g2.print(user_list);Serial.print(user_list); u8g2.sendBuffer();dataFile.close(); delay(1000);
}
void finger_clear(){mySerial.write(buf_clear, 12);u8g2.setFontPosTop();u8g2.setFont(u8g2_font_crox2hb_tr);u8g2.clearBuffer();u8g2.drawStr(30, 0, "finger clear");u8g2.drawLine(0, 14, 128, 14);u8g2.sendBuffer();u8g2.setCursor(0, 26);delay(1000);if(Serial.available()>6){size_t len = Serial.available(); //返回可读数据的长度uint8_t sbuf[len];Serial.readBytes(sbuf, len);/*for(int i=0; i<len; i++){ // 然后通过串口监视器输出readBytesSerial.print(sbuf[i],HEX); // 函数所读取的信息}*/if(sbuf[6]==0x07){//Serial.println("验证指纹");if(sbuf[9]==0x00){u8g2.println("success!");u8g2.sendBuffer();Serial.println("删除成功!");File dataFile = SPIFFS.open(file_name, "w");dataFile.print("");dataFile.close(); Serial.println();}else if(sbuf[9]==0x01){Serial.println("包错误!");u8g2.println("error!");u8g2.sendBuffer();}else if(sbuf[9]==0x11){u8g2.println("fail!");u8g2.sendBuffer();Serial.println("删除失败!");}delay(2000);}}
}
void work(){mySerial.write(buf_identity, 12);u8g2.setFontPosTop();u8g2.setFont(u8g2_font_crox2hb_tr);u8g2.clearBuffer();u8g2.drawStr(10, 0, "finger working");u8g2.drawLine(0, 14, 128, 14);u8g2.drawStr(0, 18, "press checking!");u8g2.sendBuffer();u8g2.setCursor(0, 32);delay(1000);if(Serial.available()>6){size_t len = Serial.available(); //返回可读数据的长度uint8_t sbuf[len];Serial.readBytes(sbuf, len);/*for(int i=0; i<len; i++){ // 然后通过串口监视器输出readBytesSerial.print(sbuf[i],HEX); // 函数所读取的信息}*/if(sbuf[6]==0x07){//Serial.println("验证指纹");if(sbuf[9]==0x00){u8g2.print("wecome!");u8g2.setCursor(0, 46);u8g2.print("ID:");u8g2.print(sbuf[10]);u8g2.print(sbuf[11]);u8g2.sendBuffer();Serial.println("识别成功,存在");Serial.print("ID:");Serial.print(sbuf[10]);Serial.print(sbuf[11]);Serial.println();/*** 添加开门模块* 可改为步进电机更为精确和稳定* 暂时手里只有个N20减速电机,加个驱动板拿来用用*/for(int i=0;i<200;i++){delay(2);digitalWrite(D5, HIGH);digitalWrite(D6, HIGH);digitalWrite(D7, LOW);digitalWrite(D8, LOW);delay(2);digitalWrite(D5, LOW);digitalWrite(D6, HIGH);digitalWrite(D7, HIGH);digitalWrite(D8, LOW);delay(2);digitalWrite(D5, LOW);digitalWrite(D6, LOW);digitalWrite(D7, HIGH);digitalWrite(D8, HIGH);delay(2);digitalWrite(D5, HIGH);digitalWrite(D6, LOW);digitalWrite(D7, LOW);digitalWrite(D8, HIGH);}for(int i=0;i<200;i++){delay(2);digitalWrite(D5, HIGH);digitalWrite(D6, HIGH);digitalWrite(D7, LOW);digitalWrite(D8, LOW);delay(2);digitalWrite(D5, HIGH);digitalWrite(D6, LOW);digitalWrite(D7, LOW);digitalWrite(D8, HIGH);delay(2);digitalWrite(D5, LOW);digitalWrite(D6, LOW);digitalWrite(D7, HIGH);digitalWrite(D8, HIGH);delay(2);digitalWrite(D5, LOW);digitalWrite(D6, HIGH);digitalWrite(D7, HIGH);digitalWrite(D8, LOW);}}else if(sbuf[9]==0x01){u8g2.print("error!");u8g2.setCursor(0, 46);u8g2.print("press again!");u8g2.sendBuffer();Serial.println("识别错误,请重新按下指纹");}else if(sbuf[9]==0x09){u8g2.print("no user!");u8g2.setCursor(0, 46);u8g2.print("press again!");u8g2.sendBuffer();Serial.println("没有检索到该用户,请重新按下指纹");}delay(1000);}}
}
void loop() {//mySerial.listen(); //软件控制方式if (mqttClient.connected()) { // 如果开发板成功连接服务器// 每隔2秒钟发布一次信息// 保持心跳 若电机正在运转,暂时不发信息(由于mcu没有多线程,不能同时运转电机和上传下发数据)mqttClient.loop();} else { // 如果开发板未能成功连接服务器connectMQTTServer(); // 则尝试连接服务器}//硬件控制方式/*if(digitalRead(D5)==LOW){Serial.println("工作模式0");work_index=0;//工作模式}else if(digitalRead(D6)==LOW){Serial.println("工作模式1");work_index=1;//添加模式}else if(digitalRead(D7)==LOW){Serial.println("工作模式2");work_index=2;//删除模式}else if(digitalRead(D8)==LOW){Serial.println("工作模式3");work_index=3;//查询模式}*/if(work_index==0){work();}else if(work_index==1){finger_add();}else if(work_index==2){finger_clear();}else if(work_index==3){getlist();}}
app上面由于加入了我整个项目中,不是很好展示,不过可以进行硬件端控制,代码中注释掉了,有需要可以自己改动一下IO口即可使用。
下个项目准备做一个基于esp32-cam的云端视频监控,然后加入到我的智能家居里面,提高拓展性。
这篇关于MQTT协议的智能家居之指纹锁的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!