实训七:WiFi上传信息
一、实验目的
使用TCP搭建客户端,实现几种开门方式的信息上传和撬门与门未关好的报警信息,将信息上传至PacketSender,可保存信息至本地主机
二、实验内容
1.在ESP32上搭建TCP客户端,PacketSender作为服务器端
2.四种开门方式——指纹开门、蓝牙开门、密码开门、按键开门,开门后上传信息到服务器
3.两种报警消息——有人撬门、门未关好,报警信息上传到服务器
4.消息在TCP服务器端的显示
三、实验设备
1.ESP32开发板
2.SG90舵机
3.FPM10A光学指纹模块
4.3*4按键模块
5.门磁
6.RFID模块
7.支持蓝牙4.0以上协议的手机
四、实验步骤
1)引脚连接
2) 上传代码
/*Author :王滨伟
* Time :2020.6.11
*/
#include <dummy.h>
#include <Servo.h>
#include <BLEDevice.h>
#include <BLE2902.h>
#include <String.h>
#include <Keypad.h>
#include <SPI.h>
#include <MFRC522.h>
#include <Adafruit_Fingerprint.h>
#include <WiFi.h>
/*
* ESP32 引脚17(TX)和16(RX)上的Serial2
* 指纹模块 连 接 ESP32
RX黄 --- 17
TX绿 --- 16
*/
#define mySerial Serial2
#define SS_PIN 21 //定义RC522的SDA引脚的接线位置
#define RST_PIN 22 //定义RC522的RST引脚的接线位置
#define SERVO_PIN 15 //舵机的脚位
#define NormolClose 13
#define SERVICE_UUID "6E400001-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_RX "6E400002-B5A3-F393-E0A9-E50E24DCCA9E"
#define CHARACTERISTIC_UUID_TX "6E400003-B5A3-F393-E0A9-E50E24DCCA9E"
#define openInterval 5000
#define fpInterval 10000
BLECharacteristic *pCharacteristic;
bool deviceConnected = false;
char* SSID = "TP-LINK_9C1F"; //wifi名称
char* PassWord = "cyykj2018"; //wifi密码
/*
* 按键初始量
*/
const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'*','0','#'}
};
byte rowPins[ROWS] = {33, 27, 14, 12}; //connect to the row pinouts of the keypad
byte colPins[COLS] = {4, 0, 2}; //connect to the column pinouts of the keypad
/*
* RFID标签结构体
*/
struct RFIDTag { //Tag标签结构体
uint8_t uid[4];
char *name;
};
MFRC522 rfid(SS_PIN, RST_PIN); //实例化类
struct RFIDTag tags[20] = { // 初始化结构资料,请自行修改RFID识别码
{{233, 232, 210, 126}, "Mini_Tag"},
{{0, 0, 0, 0}, "Mini_Tag1"},
{{1, 1, 1, 1}, "Mini_Tag2"},
};
/*wifi IP和端口配置*/
const IPAddress serverIP(192,168,0,113); //欲访问的地址
uint16_t serverPort = 53123; //服务器端口号,修改自己服务器的端口号
WiFiClient client; //声明一个客户端对象,用于与服务器进行连接
byte totalTags = sizeof(tags) / sizeof(RFIDTag); //计算结构资料的数量
bool door_open_flag; //开门动作标志位
bool alarm_flag;//报警标志位
static bool lock_back_flag=false; //回锁标志位
Keypad keypad = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );
byte ble_rx_buffer[20]; //接收蓝牙指令
byte verfi_code[6] = {0x31,0x32,0x33,0x34,0x35,0x36};//初始密码
byte init_pw[6] = {1,2,3,4,5,6};//初始按键密码
Servo myservo;
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);
bool FP_busy=false;
short active_fp = -1; //激活指纹模块, -1休眠,1激活,2指纹模块正在激活时间
int pos=0;
bool toggle; //开锁判断位
byte message_type;//上传信息的类型
void locker(bool toggle) { //开锁
myservo.attach(SERVO_PIN);
if (toggle) {
myservo.write(90); // 开锁
} else {
myservo.write(0); // 关锁
}
delay(500); // 等马达转到定位
myservo.detach();
}
class MyServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
deviceConnected = true;
};
void onDisconnect(BLEServer* pServer) {
deviceConnected = false;
}
};
/* C++多态,创建类MyCallbacks,继承类BLECharacteristicCallbacks
* 类MyCallbacks,下的成员函数onWrite(),接收蓝牙消息处理函数
* 55aa1000313233343536 蓝牙发送开门指令,hex 16进制发送
* 55aa2000363534333231 蓝牙修改密码指令
* 55aa3000000000000000 蓝牙查询指令
* 55aa3100310000000000 蓝牙注册指纹 注册id号为01
* 55aa3200330000000000 蓝牙删除指纹 删除id号为03
*/
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string rxValue = pCharacteristic->getValue();
byte op;
for (int i = 0; i < rxValue.length(); i++){
ble_rx_buffer[i] = rxValue[i];
Serial.print(rxValue[i]);
}
if(ble_rx_buffer[0] == 0x55 && ble_rx_buffer[1] == 0xaa){
op=ble_rx_buffer[2];
switch(op){
case 0x10:{ //蓝牙开门
if(memcmp(ble_rx_buffer+4,verfi_code,6) == 0){ //匹配成功
toggle = true;
message_type = 6;
Serial.println("开门成功");
}
else{
Serial.println("密码错误");
}
break;
}
case 0x20:{ //设置密码
static byte temp_pw[6]; //临时密码
for(byte i=0;i<6;i++){
if(ble_rx_buffer[i+4]<=0x39 && ble_rx_buffer[i+4]>=0x30){ //保证密码在0-9之间
temp_pw[i]=ble_rx_buffer[i+4]-0x30;
}
else{ //无效字符
break;
}
if(i==5){ //密码修改完成
Serial.println("密码修改完成");
memcpy(init_pw,temp_pw,6);
}
}
break;
}
case 0x30:{ //查看指纹
FP_busy = true;
finger.getTemplateCount(); //查当前指纹模板的数量
Serial.print("Sensor contains ");
Serial.print(finger.templateCount);
Serial.println(" templates");
FP_busy = false;
break;
}
case 0x31:{//添加指纹
byte id;
FP_busy = true;
id = ble_rx_buffer[4] - 0x30; //注册指纹id
getFingerprintEnroll(id);
FP_busy = false;
break;
}
case 0x32:{ //删除指纹
byte id;
FP_busy = true;
id = ble_rx_buffer[4] - 0x30; //删除指纹id
Serial.print("Deleting ID #");
Serial.println(id);
deleteFingerprint(id);
FP_busy = false;
break;
}
}
}
}
};
/*配置蓝牙
* 参数:BLEName 蓝牙名字
* 返回值: 无
*/
void setupBLE(String BLEName){
const char *ble_name = BLEName.c_str();
BLEDevice::init(ble_name);
BLEServer *pServer = BLEDevice::createServer(); //创建服务
pServer->setCallbacks(new MyServerCallbacks());
BLEService *pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_TX,BLECharacteristic::PROPERTY_NOTIFY);
pCharacteristic->addDescriptor(new BLE2902()); //添加描述
BLECharacteristic *pCharacteristic = pService->createCharacteristic(CHARACTERISTIC_UUID_RX,BLECharacteristic::PROPERTY_WRITE);
pCharacteristic->setCallbacks(new MyCallbacks()); //receive message callback
pService->start();// Start the service
pServer->getAdvertising()->start();// Start advertising
Serial.println("Waiting a client connection to notify...");
}
/*配置Wifi函数
* 输入参数: ssid: wifi名字,password: wifi密码
* 返回值:无
*/
void configure_Wifi(char* ssid, char* password){
WiFi.mode(WIFI_STA);
WiFi.setSleep(false); //关闭STA模式下wifi休眠,提高响应速度
WiFi.begin(ssid, password);
if(WiFi.status() != WL_CONNECTED){
delay(500);
Serial.println("Connecting to WiFi fail..");
}
else{
Serial.println("Connected to the WiFi network");
Serial.print("IP Address:");
Serial.println(WiFi.localIP());
}
}
/*TCP上传消息函数
* 输入值:type=value
* value=1时,表示撬门
* value=2时, 表示门未关好
* value=3时,表示RFID开门
* value=4时,表示指纹开门
* value=5时,表示按键开门
* value=6时,表示蓝牙开门
* 返回值:true时,表示TCP连接成功
* false时,表示TCP连接失败
*/
bool SendOnCloud(byte type){
//Serial.println("尝试连接服务器");
char value = type+'0';
if(type>0 && type<=6){ //在消息协议范围内
if (client.connect(serverIP, serverPort)){ //尝试访问目标地址
Serial.print("访问成功:");
Serial.println(value);
client.println(value);//向服务器发送数据
message_type =0;
return 1;
}
else{
Serial.println("访问失败");
client.stop(); //关闭客户端
message_type =0;
return 0;
}
}
else{ //无效协议
//Serial.println("Invalid Message");
return 0;
}
}
/*按键输入的匹配函数
* 参数:key为一次按键输入的值
*/
bool compare_pw(char key){
static byte pw[20];
static byte keys_len = 0;
bool pw_flag = false; //匹配密码的标志位
if(key){
//FP_busy = false;//唤醒指纹模块,可以开始匹配
Serial.println(key);
active_fp = 1;
//Serial.println(active_fp);
switch(key){
case '#':{ //确认键
if(keys_len == 6 && memcmp(pw,init_pw,6)==0){ //匹配成功,开锁
Serial.println("开门成功");
pw_flag = true;
message_type = 5;
}
else{
Serial.println("密码错误");
}
keys_len=0; //清零
break;
}
case '*':{ //回退键
if(keys_len >0)
keys_len--;
break;
}
default:{ //默认数字键0-9
pw[keys_len++]=key-'0';
if(keys_len>19){
Serial.println("OverSize");
keys_len=0;
}
break;
}
}
}
return pw_flag;
}
void printDec(byte *buffer, byte bufferSize) {
for (byte i = 0; i < bufferSize; i++){
//Serial.print(buffer[i] < 0x10 ? " 0" : "");
Serial.print(buffer[i]);
Serial.print(" ");
}
}
/*
* RFID匹配函数
* 参数:card_num为卡片资料的总数
*/
bool Match_Card(byte card_num){
bool foundTag = false; //是否找到记录中的标签,初始是false
if (rfid.PICC_IsNewCardPresent() && rfid.PICC_ReadCardSerial()) { //寻找新卡
byte *id = rfid.uid.uidByte; // 取得卡片的UID
byte idSize = rfid.uid.size; // 取得UID的长度
Serial.print("十进制UID:");
printDec(rfid.uid.uidByte, rfid.uid.size);
Serial.println();
for (byte i = 0; i < card_num; i++) {
if (memcmp(tags[i].uid, id, idSize) == 0) { // 比对阵列资料值
Serial.println(tags[i].name); //显示标签名字
foundTag = true;
message_type=3;
break;
}
if(i==card_num-1){
Serial.println("验证失败");
}
}
// 使放置在读卡区的IC卡进入休眠状态,不再重复读卡
rfid.PICC_HaltA();
// 停止读卡模块编码
rfid.PCD_StopCrypto1();
}
return foundTag;
}
/*门磁中断函数
* 参数 open_flag:正常开门的标志位
*/
void handleInterrupt() {
delay(100);
if (digitalRead(NormolClose)){// 电平高,门开
detachInterrupt(digitalPinToInterrupt(NormolClose)); //屏蔽中断脚位
door_open_flag = 1;
if(!lock_back_flag){//门没在开锁时间,门磁变化,算撬门报警
alarm_flag = 1;
}
Serial.println("door open");
}
}
/*指纹模块配置函数
* configure_FP()
*/
void configure_FP(){
finger.begin(57600);
if (finger.verifyPassword()) {
Serial.println("Found fingerprint sensor!");
finger.getTemplateCount();
Serial.print("Sensor contains ");
Serial.print(finger.templateCount);
Serial.println(" templates");
Serial.println("Waiting for valid finger...");
}
else {
delay(1000);
Serial.println("Did not find fingerprint sensor :");
}
}
/* 匹配指纹函数
* 传入:searchFinger,表示匹配指纹与否
* returns -1 if failed, otherwise returns ID
*/
int getFingerprintIDez() {
uint8_t p = finger.getImage();
if (p != FINGERPRINT_OK) return -1;
p = finger.image2Tz();
if (p != FINGERPRINT_OK) return -1;
p = finger.fingerFastSearch();
if (p != FINGERPRINT_OK) return -1;
// Found a match!
//searchFinger = 1;
//idSearch = finger.fingerID;
Serial.print("Found ID #"); Serial.print(finger.fingerID);
Serial.print(" with confidence of "); Serial.println(finger.confidence);
message_type = 4;
return finger.fingerID;
}
/*指纹注册函数
* 输入参数: id为注册指纹的id
* 返回值: uint8_t p, p=0时录入成功
*/
uint8_t getFingerprintEnroll(byte id) {
int p = -1;
Serial.print("Waiting for valid finger to enroll as ");
Serial.println(id);
while (p != FINGERPRINT_OK) {
p = finger.getImage();
switch (p) {
case FINGERPRINT_OK:
Serial.println("Image taken");
break;
case FINGERPRINT_NOFINGER:
Serial.println(".");
break;
case FINGERPRINT_PACKETRECIEVEERR:
Serial.println("Communication error");
break;
case FINGERPRINT_IMAGEFAIL:
Serial.println("Imaging error");
break;
default:
Serial.println("Unknown error");
break;
}
}
//第一次采集结束
//槽位置放置要素模板
p = finger.image2Tz(1);
switch (p) {
case FINGERPRINT_OK:
Serial.println("Image converted");
break;
case FINGERPRINT_IMAGEMESS:
Serial.println("Image too messy");
return p;
case FINGERPRINT_PACKETRECIEVEERR:
Serial.println("Communication error");
return p;
case FINGERPRINT_FEATUREFAIL:
Serial.println("Could not find fingerprint features");
return p;
case FINGERPRINT_INVALIDIMAGE:
Serial.println("Could not find fingerprint features");
return p;
default:
Serial.println("Unknown error");
return p;
}
//OK Success
Serial.println("Remove finger");
delay(2000);
p = 0;
while (p != FINGERPRINT_NOFINGER) {
p = finger.getImage(); //请求传感器采集指纹
}
Serial.print("ID ");
Serial.println(id);
p = -1;
Serial.println("Place same finger again");
while (p != FINGERPRINT_OK) {
p = finger.getImage();
switch (p) {
case FINGERPRINT_OK:
Serial.println("Image taken");
break;
case FINGERPRINT_NOFINGER:
Serial.print(".");
break;
case FINGERPRINT_PACKETRECIEVEERR:
Serial.println("Communication error");
break;
case FINGERPRINT_IMAGEFAIL:
Serial.println("Imaging error");
break;
default:
Serial.println("Unknown error");
break;
}
}
// OK success!
p = finger.image2Tz(2);
switch (p) {
case FINGERPRINT_OK:
Serial.println("Image converted");
break;
case FINGERPRINT_IMAGEMESS:
Serial.println("Image too messy");
return p;
case FINGERPRINT_PACKETRECIEVEERR:
Serial.println("Communication error");
return p;
case FINGERPRINT_FEATUREFAIL:
Serial.println("Could not find fingerprint features");
return p;
case FINGERPRINT_INVALIDIMAGE:
Serial.println("Could not find fingerprint features");
return p;
default:
Serial.println("Unknown error");
return p;
}
// OK converted!
Serial.print("Creating model for =");
Serial.println(id);
p = finger.createModel(); //要求传感器采用两个打印特征模板并创建模型
if (p == FINGERPRINT_OK) {
Serial.println("Print matched");
} else if (p == FINGERPRINT_PACKETRECIEVEERR) {
Serial.println("Communication error");
return p;
} else if (p == FINGERPRINT_ENROLLMISMATCH) {
Serial.println("Fingerprints did not match");
return p;
} else {
Serial.println("Unknown error");
return p;
}
Serial.print("ID: ");
Serial.println(id);
p = finger.storeModel(id); //要求传感器存储计算模型以进行后续匹配
if (p == FINGERPRINT_OK) {
Serial.println("Stored!");
return 1;
} else if (p == FINGERPRINT_PACKETRECIEVEERR) {
Serial.println("Communication error");
return p;
} else if (p == FINGERPRINT_BADLOCATION) {
Serial.println("Could not store in that location");
return p;
} else if (p == FINGERPRINT_FLASHERR) {
Serial.println("Error writing to flash");
return p;
} else {
Serial.println("Unknown error");
return p;
}
}
/*删除指纹函数
* 输入参数:id为指纹id
* 返回值:p,p=0时,表示指纹模块删除成功
*/
uint8_t deleteFingerprint(uint8_t id) {
uint8_t p = -1;
p = finger.deleteModel(id); //删除指纹成功返回0x00
if (p == FINGERPRINT_OK) {
Serial.println("Deleted!");
} else if (p == FINGERPRINT_PACKETRECIEVEERR) {
Serial.println("Communication error");
return p;
} else if (p == FINGERPRINT_BADLOCATION) {
Serial.println("Could not delete in that location");
return p;
} else if (p == FINGERPRINT_FLASHERR) {
Serial.println("Error writing to flash");
return p;
} else {
Serial.print("Unknown error: 0x");
Serial.println(p, HEX);
return p;
}
}
void setup() {
Serial.begin(115200);
setupBLE("ESP32 Device");//设置蓝牙名称
myservo.attach(SERVO_PIN); //引脚12为PWM脚
SPI.begin(); // 初始化SPI总线
rfid.PCD_Init(); // 初始化 MFRC522
configure_FP();//配置指纹模块
configure_Wifi(SSID, PassWord);//配置WiFi模块
pinMode(NormolClose, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(NormolClose), handleInterrupt, RISING);
}
void loop() {
static unsigned long toggle_time = millis();
static unsigned long fp_time = millis();
static bool door_forget_alarm; //门未关好标志位
static bool door_illegal_alarm;//撬门报警
//message_type = 0;//消息初始化
short idSearch = -1; //匹配的指纹id
if (!digitalRead(NormolClose)){ //电平低,门关
attachInterrupt(digitalPinToInterrupt(NormolClose), handleInterrupt, RISING); // 设置中断脚位,检测下降沿电压
door_open_flag = 0;
}
if(!FP_busy && active_fp==2) //指纹模块空闲时进行指纹匹配 && 指纹模块正在激活期间
idSearch=getFingerprintIDez(); //指纹匹配
if(compare_pw(keypad.getKey()) || Match_Card(totalTags) || idSearch!=-1){ //密码正确 or 刷卡成功 or 指纹开门
toggle = true;
alarm_flag = false; //解除报警
}
if(toggle){ //开门
door_forget_alarm=0; //门未关好标志位
door_illegal_alarm=0;//门撬门标志位
locker(toggle); //开锁
toggle = false;
lock_back_flag = true;
toggle_time = millis();
}
if(millis() - toggle_time > openInterval && lock_back_flag){ //回锁时间5s
lock_back_flag = false;
locker(toggle);//回锁
}
if(active_fp == 1){ //激活指纹模块
fp_time = millis();
active_fp=2;
}
if(active_fp==2 && millis() - fp_time > fpInterval){ //指纹激活时间5s
active_fp = -1; //休眠
}
//报警信息:1、门未关好 2、有人撬门
if(!lock_back_flag && door_open_flag){//关锁 and 门的状态是开
if(alarm_flag){ //非法撬门,一直滴
if(door_illegal_alarm==0){ //消息报警传送一次
Serial.println("非法撬门");
message_type = 1;
door_illegal_alarm=1;
}
}
else{ //门未关好,滴一声
if(door_forget_alarm==0){
Serial.println("门未关好");
door_forget_alarm=1;
message_type = 2;
}
}
}
SendOnCloud(message_type);//上传相应的wifi消息
}
3) WiFi上传信息协议
字段 | 标识 |
---|---|
长度 | 1B |
标识value,有以下几种类型
value=1时,表示撬门
value=2时, 表示门未关好
value=3时,表示RFID开门
value=4时,表示指纹开门
value=5时,表示按键开门
value=6时,表示蓝牙开门
4) 测试结果
首先,打开Packet Sender,根据服务器配置好ESP32上传消息的IP和端口号,服务器和ESP32需要连接在同一个区域网。
指纹开锁,上传的标识为4。
串口显示
按键开门,上传开门标识为5。
串口显示
其他几种的开门方式和报警消息的上传,上述例子类似,这里不再详述。