|
本帖最后由 X-iaowh 于 2020-4-4 16:40 编辑
1 项目描述
1.1 项目说明
项目主要使用乐信的ESP32开发板作为控制器,搭配RFID-RC522模块及电磁门锁等制作了一个门禁系统,实现用主卡进行门禁卡信息录入或删除,用已录入的门禁卡实现开门的动作。后增加了点灯科技的Blianker APP的使用,构建了一个简单物联网项目,以此现实远程通过MQTT通信达到远程控制的目的,或者在同一网络下时,用局域网通信控制开门动作。由此,项目除了实现用门禁卡开门的功能外,还可实现以下的功能。例如,当有钟点工来打扫卫生或者家里有亲戚/客人来访刚好不在家的时候,就可以通过远程开门,让他们进入住所。此外,如果是熟人到访,自己又窝在被窝/沙发上不想起身时,就可以用APP开个门,就非常方便。
目前项目还未实现门禁卡信息记录及上传云端的功能,如果后面时间充裕,将修改补充此文档,如有朋友已经有过类似的开发经历,望指导赐教。
也可以直接看放在B站的视频链接,来了解这个门禁系统:https://www.bilibili.com/video/BV1g54y1d7DW
1.2 流程图
程序主要功能现实的流程图如下,不包含远程控制部分,如有些许错误,欢迎指出
主要功能流程图
2 前期准备
2.1 软件
· Arduino IDE
可在Arduino官网进行下载最新版本:https://www.arduino.cc/en/Main/Software
如果下载的是可执行文件版本(.exe)直接安装即可,如果是压缩包(.zip),解压即可运行
详细安装教程可参考社区:Arduino IDE安装教程 教程提供百度云安装方式,比官网下载来的快
· Blinker APP安装
Blinker APP 安装地址:https://www.diandeng.tech/doc/app-download
自行选择Android或iOS版本进行下载安装,iOS也可在应用商店直接搜索到
· Blinker 库文件(blinker-library-master.zip)
blinker-library-master.zip文件下载地址:https://www.diandeng.tech/doc/sdk-download
选择Arduino(C++) SDK下载即可,将下载好的库文件压缩包解压安装到"我的电脑->文档->Arduino->libraries"文件夹中
· RFID 库文件
Arduino RFID Library for MFRC522下载地址:https://github.com/miguelbalboa/rfid
点击红色框内的"Clone or download"后选择"Download ZIP"解压于libraries文件夹中
RFID下载提示
· 安装ESP32 SDK
使用代理直接在Arduino IDE进行安装,或使用论坛中的压缩包进行安装,直接运行并解压即可-->文件及安装教程
2.2 硬件
· ESP32开发板
个人本次使用的ESP32开发板是UNO D1 R32或Node MCU开发板,如下图所示
UNO D1 R32开发板
Node MCU开发板
· MFRC522射频模块
MFRC522套装包括RFID-RC522模块、标准S50空白卡、S50异型卡及排针,如下图所示
RFID-RC522模块
· 继电器
继电器所采用的是可选择触发方式的继电器
触发方式可选 继电器
· 电磁门锁
电磁门锁采用12V小型电磁门锁,方便开关电源供电,实际项目时可根据需要选择,如下图所示
电磁门锁
· 其他
LED灯、220Ω电阻、按键、面包板、杜邦线、电源等...
3 电气接线
电气接线目前使用的板子的UNO D1 R32,程序对应的也是UNO D1 R32的引脚,而在Fritzing官方社区中找不到此开发板,暂且不放Fritzing的接线图,放一张实物接线图,后期有时间会将Frizing的接线图放上来,实物接线图如下
UNO D1 R32的接线图
其中MFRC522采用的是SPI通信,按照SPI的通信方式接就可以了,至于从机选择SS/SDA以及RST可以进行自定义,按钮上拉输入的方式,可直接在程序中进行设置,需要主要的MFRC522一般采用3.3V供电。
4 Blinker APP 界面设置
Blinker App 的简单应用及界面设置可以参考论坛的Blinker 快速接入指南 ,最终设置结果如下
门禁系统APP端远程控制界面
5 Arduino 程序
以下程序尽可能注释清楚了,如果有疑问,大部分应该可以在注释中得到解决
[mw_shl_code=arduino,true]/*
基于RFID的门禁系统
程序流程:
识别卡片ID,判断是否为管理员卡片,
是管理员ID,则再次读卡,
如果为管理员卡片,则退出
如果为已知ID则从存储器删除,为未知ID则写入存储器钟中;
如果第一次读卡不是管理员ID,
若为已知ID,则开门,若为为止ID,则不允许通过;
Author: X-iaowh
Version: 3.3 for ESP32/ESP8266/Arduino...
修复了1.0版本中的以下问题:
[1] 无法将门禁信息写入EEPROM中
[2] 完善继电器选择模式
[3] 解决运行过程中,擦除按键无法清拆主卡信息的问题
[4] 解决除主卡外,录入的第一门禁卡无法识别的问题
在2.0版本基础上增加远程控制
Date: 2020-04-02
*/
#define BLINKER_WIFI // 定义ESP32开发板为WiFi的连接方式,通信方式的选择必须定义在头文件之前
#include<EEPROM.h> // 在EEPROM中读写射频卡的ID信息,以保证断电数据不丢失
#include<SPI.h> // RC522模块采用SPI通信协议
#include<MFRC522.h> // 读取射频卡的类库
#include<Blinker.h> // 导入Blinker头文件,以便使用Blinker APP进行远程控制
// 本项目采用继电器对电磁门锁进行控制
/*
为了观察硬件的运行状态,使用一些LED灯、一个控制电磁门锁的继电器以及一个用来擦除所有数据的按钮
使用共阳极的LED灯,写入HIGH将关灯
若使用共阴极的LED灯,写入HIGH将开灯
如果使用共阴极的LED灯,注释掉 #define COMMOM_ANODE
使用低电平触发模式的继电器,注释掉 #define HIGH_RELAY后,OPEN将为开门
*/
#define HIGH_RELAY
#ifdef HIGH_RELAY
#define OPEN HIGH
#define CLOSE LOW
#else
#define OPEN LOW
#define CLOSE HIGH
#endif
//#define COMMOM_ANODE
#ifdef COMMOM_ANODE
#define LED_ON LOW
#define LED_OFF HIGH
#else
#define LED_ON HIGH
#define LED_OFF LOW
#endif
#define size 1304 // EEPROM的操作空间的字节数
#define redLed 14 // 简单的交互界面采用LED灯作为基础,在此设置LED灯连接的引脚
#define greenLed 27
#define blueLed 16
#define relay 26 // 设置继电器输出引脚
#define wipeB 25 // 设置数据清楚按键的引脚
bool programMode = false; // 初始化编程为False,即默认不进入program Mode
uint8_t successRead; // 用于保存成功从读卡器读到的数据信息 [uint8_t为无符号的8位数,unsigned char]
byte storedCard[4]; // 存储来自EEPROM的一个ID信息
byte readCard[4]; // 存储从RFID模块扫描到ID信息
byte masterCard[4]; // 存储从EEPROM读取到的管理员的ID信息
// 创建一个RFID_RC522对象实例
#define SS_PIN 5
#define RST_PIN 13
MFRC522 mfrc522(SS_PIN, RST_PIN);
// WiFi连接的账号与密码设置
char auth[] = "填入Blinker APP中获取到的设备密钥"; // 填入Blinker APP中获取到的设备密钥
char ssid[] = "接入的WiFi热点名称"; // 接入的WiFi热点名称
char pswd[] = "所接入的WiFi密码"; // 所接入的WiFi密码
// 新建一个按钮组件对象Button_OpenDoor,用于进行远程开门
BlinkerButton Button_OpenDoor("btn-abc"); // 对象Button_OpenDoor所对应得按键为APP上编号为 btn-abc 的按键
/////////////////////////////////// 初始化 ///////////////////////////////////
void setup()
{
// Arduino的引脚模式设置
pinMode(redLed, OUTPUT);
pinMode(greenLed, OUTPUT);
pinMode(blueLed, OUTPUT);
pinMode(relay,OUTPUT);
pinMode(wipeB, INPUT_PULLUP); // 上拉输入设置
// 设置初始的交互界面,以及注意继电器的在复位或接通Arduino电源的动作
digitalWrite(relay, CLOSE); // 确保门锁处于关闭的状态
digitalWrite(redLed, LED_OFF); // 保证redLed关闭
digitalWrite(greenLed, LED_OFF); // 保证greenLed关闭
digitalWrite(blueLed, LED_OFF); // 保证blueLed关闭
// 通讯协议初始化
Serial.begin(115200); // 用于调试时查看程序的运行状态
SPI.begin(); // MFRC522使用SPI通信协议
BLINKER_DEBUG.stream(Serial); // 在串口打印Blinker的调试信息
Blinker.begin(auth, ssid, pswd); // Blinker的初始化
Button_OpenDoor.attach(OpenDoor); // 按下远程开门键调用OpenDoor()函数
// 初始化MFRC522硬件
mfrc522.PCD_Init();
// 设置天线增益至最大,可以提高射频读卡器的感应距离
// mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max);
Serial.print(F("门禁管理系统 Version: 0.1")); // 出于调试目的
ShowReaderDetails(); // 用于显示射频读卡器的详细信息,函数实现置于主程序后
// 擦除数据 ———— 在设置(setup)运行时按下擦除按钮(wipeB),则会擦除EEPROM中的数据
if (digitalRead(wipeB) == LOW) // 按钮通过上拉电阻接地,所以当按钮按下时为低电平
{
digitalWrite(redLed, LED_ON); // 点亮红灯以提醒用正在准备擦除数据
Serial.println(F("擦除按钮已按下!"));
Serial.println(F("你有10秒钟的时间松开按钮,以取消擦除操作"));
Serial.println(F("这将删除所有的记录信息,且不可撤销"));
bool buttonState = monitorWipeButton(10000); // 给用户留足时间来取消操作,monitorWipeButton()函数实现在主程序之后
if(buttonState == true && digitalRead(wipeB) == LOW) // 如果擦除按键持续按下10秒,则擦除EEPROM中的数据
{
Serial.println(F("开始擦除EEPROM中的数据"));
EEPROM.begin(size);
for(uint16_t x = 1280; x < size; x = x +1) // 擦除EEPROM数据的循环程序,此处将原来的EEPROM.length()直接更改为size
// 至于从1280开始是因为ESP32模拟出EEPROM中的0-1279的空间被用作Blinker的自动控制
{
if(EEPROM.read(x) == 0)
{
// 什么都不做,因为数据已清除,继续读取下一个地址的数据,以节约时间并且减少EEPRM的写入次数
}
else
{
EEPROM.write(x, 0);
}
}
EEPROM.end();
// 数据清楚完成的交互指示
Serial.println(F("EEPROM中的数据已清楚完毕"));
digitalWrite(redLed, LED_OFF);
Blinker.delay(200);
digitalWrite(redLed, LED_ON);
Blinker.delay(200);
digitalWrite(redLed, LED_OFF);
Blinker.delay(200);
digitalWrite(redLed, LED_ON);
Blinker.delay(200);
digitalWrite(redLed, LED_OFF);
}
else
{
Serial.println(F("数据清除已取消")); // 10秒内松开清楚按钮wipeB,则取消EEPROM数据清楚操作
digitalWrite(redLed, LED_OFF);
}
}
// 检查主卡ID信息是否已经录入,没有录入则让用户选择一张主卡录入信息
// 同样适用于重新设置一张主卡ID
// 同时可以保存其他的EEPROM的记录信息,只要不将“143”写入地址1中
// 在EEPROM的地址1281中写入“143”,则认为已经有主卡的ID信息
EEPROM.begin(size);
if (EEPROM.read(1281) != 143)
{
Serial.println(F("暂未定义主卡"));
Serial.println(F("请扫描一张射频卡作为主卡"));
do
{
successRead = getID(); // 如果成功从读卡器中读到信息则置1,否则置0,直到读取到射频卡信息;getID()函数实现位于主程序之后
digitalWrite(blueLed, LED_ON);
Blinker.delay(200);
digitalWrite(blueLed, LED_OFF);
Blinker.delay(200);
} while (!successRead);
// 将读取到的射频卡信息进行存储,并设置为主卡
EEPROM.begin(size);
for (uint8_t i = 0; i < 4; i++) // 读取射频卡的4位数据
{
EEPROM.write( 1282+i, readCard ); // 将射频卡的信息写入EEPROM中
}
EEPROM.write(1281, 143);
Serial.println(F("已完成主卡定义"));
EEPROM.end();
}
// 打印出已保存于EEPROM中的主卡的ID信息
Serial.println(F("----------------------"));
Serial.println(F("主卡ID信息如下"));
EEPROM.begin(size);
for (uint8_t i = 0; i < 4; i++)
{
masterCard = EEPROM.read(i + 1282);
Serial.print(masterCard, HEX); // 以十六进制打印主卡的ID信息
}
// 门禁系统准备就绪用户交互展示
Serial.println("");
Serial.println(F("----------------------"));
Serial.println(F("已准备就绪,等待射频卡扫描"));
cycleLeds(); // 当所有设置准备就绪后,通过此循环闪烁LED提示用户,cycleLeds()函数实现位于主程序之后
}
/////////////////////////////////// 主循环 ///////////////////////////////////
void loop()
{
Blinker.run(); // 启动远程连接服务,保证设备在线
do
{
successRead = getID(); // 如果成功从读卡器中读到信息则置1,否则置0,直到读取到射频卡信息;getID()函数实现位于主程序之后
// 当在设备运行时按下擦除按键10秒,将擦除EEPROM中的主卡信息
if (digitalRead(wipeB) == LOW)
{
// 当按下擦除按键后,正常的操作流程被打断,通过闪烁红色的LED灯来警告用户
digitalWrite(redLed, LED_ON);
digitalWrite(greenLed, LED_OFF);
digitalWrite(blueLed, LED_OFF);
// 打印串口调试信息
Serial.println(F("擦除按钮已按下!"));
Serial.println(F("主卡信息将在10秒后被擦除!"));
bool buttonState = monitorWipeButton(10000); // 给用户留足时间来取消操作,monitorWipeButton()函数实现在主程序之后
// 擦除按键按下10秒后,擦除EEPROM中的主卡设定信息
if (buttonState == 1 && digitalRead(wipeB) == LOW)
{
EEPROM.begin(size);
EEPROM.write(1281, 0);
EEPROM.end();
Serial.println(F("主卡信息已从设备中擦除"));
Serial.println(F("请按复位键(reset)以重新设置所需要的主卡"));
while(1);
}
Serial.println(F("擦除主卡的操作已被取消"));
}
// 当擦除按键没有按下时
if (programMode) // 当前为添加新卡或删除旧卡的programMode时
{
cycleLeds(); // programMode时,循环闪烁红绿蓝灯以等待读取一张新的射频卡;cycleLeds()函数实现位于主程序之后
}
else
{
normalModeOn(); // 一般读卡模式,蓝灯常亮,其他灯全灭;normalModeOn()函数实现位于主程序之后
}
} while (!successRead); // 程序将持续读取射频卡,直到成功读取到射频卡
// programMode下添加新卡或删除旧卡
if (programMode)
{
// 编程模式下,首先检测是否为主卡再次扫描,如果是,则退出编辑模式
if ( isMaster(readCard) ) // isMaster()函数实现位于主程序之后
{
Serial.println(F("扫描到的为主卡"));
Serial.println(F("退出编辑模式(Program Mode)"));
Serial.println(F("----------------------"));
programMode = false;
return;
}
// 若不是主卡,则进行添加或删除卡片信息操作
else
{
if ( findID(readCard) ) // 如果检测到的是已知的射频卡,则删除;findID()函数实现位于主程序之后
{
Serial.println(F("这是一张已知的门禁卡,删除中..."));
deleteID(readCard); // deleteID()函数实现位于主程序之后
Serial.println(F("----------------------"));
Serial.println(F("可继续添加或移除门禁卡,或使用主卡退出编辑模式"));
}
else // 若扫描到位置卡片,则添加卡片信息
{
Serial.println(F("这是一张未知的门禁卡,添加中..."));
writeID(readCard); // writeID()函数实现位于主程序之后
Serial.println(F("----------------------"));
Serial.println(F("可继续添加或移除门禁卡,或使用主卡退出编辑模式"));
}
}
}
// 使用主卡进入编辑模式(programMode),或者对门禁卡判断,进行开锁动作
else
{
// 扫描到的门禁卡为主卡,则进入编辑模式
if ( isMaster(readCard) )
{ EEPROM.begin(size);
programMode = true;
Serial.println(F("管理员你好,将进入编辑模式"));
uint8_t count = EEPROM.read(1280); // 读取EEPROM用于存储用户数量的地址0中的信息
Serial.print(F("当前系统中有 "));
Serial.print(count);
Serial.print(F(" 条门禁卡信息"));
Serial.println("");
Serial.println(F("你可以扫描一张门禁卡以添加到系统中,或从系统中移除"));
Serial.println(F("再次扫描主卡可退出编辑模式"));
Serial.println(F("----------------------"));
}
// 若扫描到的不是主卡,则系统进行开锁判断
else
{
if ( findID(readCard) ) // 如果是系统已知的门禁卡,则开锁
{
Serial.println(F("门锁已打开,欢迎"));
granted(3000); // 设置锁打开的时间3秒,granted()函数实现位于主函数之后
}
else // 扫描到的是系统未录入的门禁卡
{
Serial.println(F("此门禁卡未录入!!!"));
denied(); // denied()函数现实位于主函数之后
}
}
}
}
/////////////////////////////////////// 函 数 现 实 部 分 ///////////////////////////////////////
////////////////////////////////////// 门禁判断 //////////////////////////////////////
////////////////////////////////////// 门禁允许通过函数 granted() 实现 //////////////////////////////////////
void granted( uint16_t setDelay )
{
// 设置指示灯,保持绿灯亮
digitalWrite(blueLed, LED_OFF);
digitalWrite(redLed, LED_OFF);
digitalWrite(greenLed, LED_ON);
digitalWrite(relay, OPEN); // 继电器触发,以此开门
Blinker.delay(setDelay); // 设置继电器通电时间,即锁舌回缩时间
digitalWrite(relay, CLOSE); // 继电器复位,锁舌复位,关门
Blinker.delay(1000); // 保持绿灯亮1秒钟时间
}
////////////////////////////////////// 门禁拒绝通过函数 denied() 实现 //////////////////////////////////////
void denied()
{
digitalWrite(redLed, LED_ON);
digitalWrite(greenLed, LED_OFF);
digitalWrite(blueLed, LED_OFF);
Blinker.delay(1000);
}
////////////////////////////////////// RFID卡信息获取 //////////////////////////////////////
////////////////////////////////////// 射频卡ID读取函数getID()实现 //////////////////////////////////////
uint8_t getID()
{
// 读取射频卡准备
if( ! mfrc522.PICC_IsNewCardPresent() ) // 如果放置一张新的射频卡到读卡器则继续getID()函数
{
return 0;
}
if( ! mfrc522.PICC_ReadCardSerial() ) // 当一张射频卡放置于读卡器上后,读取串号并继续getID()函数
// 默认射频卡信息为4位
// 读取射频卡的ID串号
Serial.print(F("门禁卡ID信息:"));
for (uint8_t i = 0; i < 4; i++)
{
readCard = mfrc522.uid.uidByte;
Serial.print(readCard, HEX);
}
Serial.println("");
mfrc522.PICC_HaltA(); // 停止读取
return 1;
}
///////////////////////////////////// 读卡器详情展示函数ShowReaderDetails()实现 //////////////////////////////////////
void ShowReaderDetails()
{
// 获取RC522软件版本
byte v = mfrc522.PCD_ReadRegister(mfrc522.VersionReg);
Serial.print(F("RC522的软件版本为0x"));
Serial.print(v, HEX);
if (v == 0x91)
{
Serial.println(F(" = v1.0"));
}
else if (v == 0x92)
{
Serial.println(F(" = v2.0"));
}
else
{
Serial.println(F(",此版本未知"));
}
// 如果v的返回值是0x00或0xFF,说明通讯失败
if (v == 0x00 || v == 0xFF)
{
Serial.println(F("警告:通信失败,请检查RC522是否成功连接?"));
Serial.println(F("系统停止:请检查连接..."));
// 系统暂停时的交互界面,亮红灯
digitalWrite(redLed, LED_ON);
digitalWrite(greenLed, LED_OFF);
digitalWrite(blueLed, LED_OFF);
while(true); // 系统暂停
}
}
////////////////////////////////////// 灯光提示函数 //////////////////////////////////////
////////////////////////////////////// 编程模式循环灯函数cycleLeds()实现 //////////////////////////////////////
void cycleLeds()
{
Blinker.run(); // 在这个循环加入,为了确保设备在编程也不中断,删除的话编程模时会导致设备中断,有兴趣可以试试
digitalWrite(redLed, LED_OFF);
digitalWrite(greenLed, LED_ON);
digitalWrite(blueLed, LED_OFF);
Blinker.delay(200);
digitalWrite(redLed, LED_OFF);
digitalWrite(greenLed, LED_OFF);
digitalWrite(blueLed, LED_ON);
Blinker.delay(200);
digitalWrite(redLed, LED_ON);
digitalWrite(greenLed, LED_OFF);
digitalWrite(blueLed, LED_OFF);
Blinker.delay(200);
}
////////////////////////////////////// 正常模式提示灯函数normalModeOn()实现 //////////////////////////////////////
void normalModeOn()
{
Blinker.run(); // 在这个循环加入,为了确保设备不中断,删除的会导致设备中断,有兴趣可以试试
digitalWrite(redLed, LED_OFF);
digitalWrite(greenLed, LED_OFF);
digitalWrite(blueLed, LED_ON);
digitalWrite(relay, CLOSE); // 继电器复位,以保证门锁闭合
}
////////////////////////////////////// EEPROM操作部分 //////////////////////////////////////
////////////////////////////////////// 读EEPROM中ID函数readID()函数实现 //////////////////////////////////////
void readID(uint8_t number)
{
EEPROM.begin(size);
uint8_t start = ( number * 4 ) + 1282; // 找到ID的起始地址
for (uint8_t i = 0; i < 4; i++)
{
storedCard = EEPROM.read(start + i);
}
}
////////////////////////////////////// 添加新ID到EEPROM的writeID()函数实现 //////////////////////////////////////
void writeID( byte a[] )
{
if ( ! findID(a) ) // 写入ID前先判断EEPROM中是否有保存此门禁卡ID,findID()的函数实现在后面
{
EEPROM.begin(size);
uint8_t num = EEPROM.read(1280); // EEPROM的地址0用于存放门禁卡ID的数量
uint8_t start = ( num * 4 ) + 1286; // 找到用于写入新卡的位置,+6而不是+4,因为0存放数量,1存放主卡标记,往后4个地址存放主卡ID
// 增加系统中的一个门禁卡数量
num++;
EEPROM.write(1280, num);
// 在EEPROM中写入门禁卡的ID信息
for (uint8_t i = 0; i < 4; i++)
{
EEPROM.write(start + i, a);
}
EEPROM.end();
successWrite(); // successWrite()的函数实现在后面
Serial.println(F("已成功在门禁系统中添加新的门禁卡ID"));
}
else
{
failedWrite();
Serial.println(F("失败:此门禁卡ID错误或EEPROM损坏"));
}
}
////////////////////////////////////// 从EEPROM中移除已有ID的deleteID()函数实现 //////////////////////////////////////
void deleteID( byte a[] )
{
if ( ! findID(a) )
{
failedWrite();
Serial.println(F("失败:此门禁卡ID错误或EEPROM损坏"));
}
else
{
EEPROM.begin(size);
uint8_t num = EEPROM.read(1280); // EEPROM的地址0用于存放门禁卡ID的数量
uint8_t slot; // 找出卡的槽号
uint8_t start; // = (num * 4) + 1286; // 找到下一个卡槽的开始地址
uint8_t looping; // 移位时,所需要的循环重复的次数
uint8_t j; // 覆盖地址时用到
uint8_t count = EEPROM.read(1280);
slot = findIDSLOT(a); // 找到所要删除的门禁卡的槽号,findIDSLOT()函数实现在后面
start = ( slot * 4 ) + 1282;
looping = (( num - slot) * 4); // 移位循环的次数
// 系统中已知门禁卡数量减1
num--;
EEPROM.write(1280, num);
// 将所要移除的门禁卡ID后的ID往前移动4位
for (uint8_t j = 0; j < looping; j++)
{
EEPROM.write(start + j, EEPROM.read(start + j + 4));
}
// 将位于最后的门禁卡ID的4位信息清0
for (uint8_t i = 0; i < 4; i++)
{
EEPROM.write(start + j + i, 0);
}
successDelete(); // successDelete()函数实现位于后面
Serial.println(F("已成功将此门禁卡ID从系统中移除"));
EEPROM.end();
}
}
////////////////////////////////////// 与EEPROM中ID进行比较的字节比较checkTwo()函数实现 //////////////////////////////////////
bool checkTwo(byte a[], byte b[])
{
for (uint8_t i = 0; i < 4; i++)
{
// 如果数组中有一位不相同,则返回false
if (a != b)
{
return false;
}
}
return true;
}
////////////////////////////////////// 寻找指定门禁卡卡槽位置的findIDSLOT()函数实现 //////////////////////////////////////
uint8_t findIDSLOT( byte find[] )
{
EEPROM.begin(size);
uint8_t count = EEPROM.read(1280); // 读取系统中的门禁卡数量
for (uint8_t i = 1; i <= count; i++)
{
readID(i); // 从系统中读取指定序号的门禁ID,并保存在storedCard[4]数组中
// 检查从EEPROM中读取到并保存在storedCard数组中的ID信息是否与find数组中的ID信息相同
if ( checkTwo(find, storedCard) )
{
return i; // 返回卡槽位置
}
}
}
////////////////////////////////////// 在EEPROM中寻找指定门禁ID的findID()函数实现 //////////////////////////////////////
bool findID( byte find[] )
{
EEPROM.begin(size);
uint8_t count = EEPROM.read(1280); // 读取系统中的门禁卡数量
// 判断EEPROM中是否有此门禁卡的ID信息
for (uint8_t i = 0; i <= count; i++) // 原先此处位 uint8_t i = 1; i < count; i++ ,这样导致第一张录入的门禁卡信息无法识别
{
readID(i);
if (checkTwo(find, storedCard))
{
return true;
}
}
return false;
}
////////////////////////////////////// 成功将门禁卡信息写入EEPROM的successWrite()函数实现 //////////////////////////////////////
void successWrite()
{
digitalWrite(redLed, LED_OFF);
digitalWrite(greenLed, LED_OFF);
digitalWrite(blueLed, LED_OFF);
Blinker.delay(200);
digitalWrite(greenLed, LED_ON);
Blinker.delay(200);
digitalWrite(greenLed, LED_OFF);
Blinker.delay(200);
digitalWrite(greenLed, LED_ON);
Blinker.delay(200);
digitalWrite(greenLed, LED_OFF);
Blinker.delay(200);
digitalWrite(greenLed, LED_ON);
Blinker.delay(200);
}
////////////////////////////////////// 门禁卡信息写入EEPROM失败的failedWrite()函数实现 //////////////////////////////////////
void failedWrite()
{
digitalWrite(blueLed, LED_OFF);
digitalWrite(redLed, LED_OFF);
digitalWrite(greenLed, LED_OFF);
Blinker.delay(200);
digitalWrite(redLed, LED_ON);
Blinker.delay(200);
digitalWrite(redLed, LED_OFF);
Blinker.delay(200);
digitalWrite(redLed, LED_ON);
Blinker.delay(200);
digitalWrite(redLed, LED_OFF);
Blinker.delay(200);
digitalWrite(redLed, LED_ON);
}
////////////////////////////////////// 成功将门禁卡信息移除EEPROM的successDelete()函数实现 //////////////////////////////////////
void successDelete()
{
digitalWrite(blueLed, LED_OFF);
digitalWrite(redLed, LED_OFF);
digitalWrite(greenLed, LED_OFF);
Blinker.delay(200);
digitalWrite(blueLed, LED_ON);
Blinker.delay(200);
digitalWrite(blueLed, LED_OFF);
Blinker.delay(200);
digitalWrite(blueLed, LED_ON);
Blinker.delay(200);
digitalWrite(blueLed, LED_OFF);
Blinker.delay(200);
digitalWrite(blueLed, LED_ON);
Blinker.delay(200);
}
///////////////////////////////////////////////////////// 其他 //////////////////////////////////////////////////////////////
/////////////////////////////////// 检查是否位主卡的isMaster()函数实现 ///////////////////////////////////
bool isMaster( byte test[] )
{
return checkTwo(masterCard, test);
}
/////////////////////////////////// 擦除按钮计时的monitorWipeButton()函数实现 ///////////////////////////////////
bool monitorWipeButton(uint32_t interval)
{
uint32_t now = (uint32_t) millis();
while((uint32_t) millis() - now < interval)
{
// 每半秒检查一次按键状态
if (((uint32_t) millis() % 500 ) == 0)
{
if (digitalRead(wipeB) != LOW)
{
return false;
}
}
}
return true;
}
/////////////////////////////////// 远程开门的OpenDoor()函数实现 ///////////////////////////////////
void OpenDoor(const String & state)
{
Blinker.vibrate(); // 手机的震感反馈函数
BLINKER_LOG("get Button state:", state); // 在串口显示按键状态
digitalWrite(relay, OPEN);
digitalWrite(greenLed, LED_ON);
Blinker.delay(3000); // 在远程控制下,要把所有的delay()函数更改为Blinker.delay()
digitalWrite(relay, CLOSE);
digitalWrite(greenLed, LED_OFF); // 非编程模式下,远程开门亮的是绿灯和蓝灯,刷卡开门只亮绿灯
}[/mw_shl_code]
6 演示
这是在B站录的视频(录得挺糙的) https://www.bilibili.com/video/BV1g54y1d7DW
7 注意事项
在实际开发过程遇到很多问题,也查阅挺多资料,后期有时间会将查阅的资料以及注意事项在此更新
|
|