#include <SPI.h> // SPI库
#include <Wire.h> // IIC库
#include <Keypad.h> // 键盘库
#include <U8g2lib.h> // 屏幕库
#include <MFRC522.h> // RFID库
#define SS_PIN 10 // RC522 SS引脚
#define RST_PIN 9 // RC522 RST引脚
#define relayPin A0 // 继电器引脚
// 以下两个常量的单位都是 ms
#define OpenLockTime 3000 // 开门时间
#define DetectionPeriod 500 // 检测周期
// 以上两个常量的单位都是 ms
const byte COLS = 3; // 定义矩阵键盘的列数
const byte ROWS = 4; // 定义矩阵键盘的行数
char keys[ROWS][COLS] = { // 定义键盘按钮的布局
{ '1', '2', '3' },
{ '4', '5', '6' },
{ '7', '8', '9' },
{ '*', '0', '#' }
};
byte colPins[COLS] = { 4, 3, 2 }; // 连接列引脚的Arduino引脚
byte rowPins[ROWS] = { 8, 7, 6, 5 }; // 连接行引脚的Arduino引脚
MFRC522 rfid(SS_PIN, RST_PIN); // 创建RC522模块对象
byte masterid1[4] = { 0X53, 0X56, 0X6B, 0X14 }; // UNID1
byte masterid2[4] = { 0X03, 0XAD, 0X5D, 0X14 }; // UNID2
U8G2_SSD1306_128X64_NONAME_1_HW_I2C u8g2(U8G2_R0,U8X8_PIN_NONE); // 创建屏幕对象
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS); // 创建Keypad对象
#define passwordLenght 5 // 密码长度
int passwordSubscript = 0; // 密码数组下标
char password1[passwordLenght] = "1234"; // 第一个密码
char password2[passwordLenght] = "5678"; // 第二个密码
char password3[passwordLenght] = "9012"; // 第三个密码
char enteredPassword[passwordLenght]; // 存储用户输入的密码
bool oledState = false; // OLED显示状态
bool lockState = false; // 锁状态 false:上锁 true:开锁
unsigned long lastOpenLockTime = 0; // 记录上一次开锁时间
unsigned long lastDetectionTime = 0; // 记录上一次检测时间
// 检查密码是否正确
bool checkPassword(char* enteredPassword, char* correctPassword) {
return strcmp(enteredPassword, correctPassword); // 比较字符串,相同返回0
}
// UNID1对比
bool checkMaster1(byte uuid[]) {
for (int i = 0; i < 4; i++) {
if (uuid[i] != masterid1[i])
return false;
}
return true;
}
// UNID2对比
bool checkMaster2(byte uuid[]) {
for (int i = 0; i < 4; i++) {
if (uuid[i] != masterid2[i])
return false;
}
return true;
}
// 打印UNID函数
void printHex(byte* buffer, byte bufferSize) {
for (byte i = 0; i < bufferSize; i++) {
Serial.print(buffer[i] < 0x10 ? " 0" : " ");
Serial.print(buffer[i], HEX);
}
Serial.println("\n");
}
// OLED显示函数
void oledShow(){
// 开锁时屏幕显示内容
// OLED标志位为false并且开锁标志位为true
if(!oledState && lockState){
oledState = true; // OLED标志位置true
u8g2.clear(); // 清除屏幕显示内容
do {
u8g2.setCursor(25, 38); // 设置坐标
u8g2.print(F("Unlocked!")); // 设置显示内容
} while (u8g2.nextPage());
}
// 未开锁时屏幕显示内容
// OLED标志位为true并且开锁标志位为false
if(oledState && !lockState){
oledState = false; // OLED标志位置false
u8g2.clear(); // 清除屏幕显示内容
do {
u8g2.setCursor(5, 15);
u8g2.print(F("Enter password"));
u8g2.setCursor(50, 38);
u8g2.print(F("Or"));
u8g2.setCursor(20, 55);
u8g2.print(F("Swipe card"));
} while (u8g2.nextPage());
}
}
// 初始化
void setup() {
Serial.begin(9600); // 初始化串口通信
pinMode(relayPin, OUTPUT); // 将继电器控制引脚设为输出模式
SPI.begin(); // SPI初始化
rfid.PCD_Init(); // RC522模块初始化
u8g2.begin(); // 屏幕初始化
u8g2.firstPage(); // 设置屏幕首页面
u8g2.setFontDirection(0); // 设置屏幕方向
u8g2.setFont(u8g2_font_ncenB10_tr); // 设置字体大小
u8g2.clear();
do {
u8g2.setCursor(5, 15);
u8g2.print(F("Enter password"));
u8g2.setCursor(50, 38);
u8g2.print(F("Or"));
u8g2.setCursor(20, 55);
u8g2.print(F("Swipe card"));
} while (u8g2.nextPage());
Serial.println(F("Initialization finished")); // 串口打印
}
// 主函数
void loop() {
char key = keypad.getKey(); // 获取按键值
if (key) {
enteredPassword[passwordSubscript++] = key; // 输入的密码存入数组
Serial.print("*"); // 输出消息
if (passwordSubscript == passwordLenght - 1) { // 如果输入密码长度等于设定密码长度
Serial.println(); // 换行
if (!checkPassword(enteredPassword, password1)) { // 检查第一个密码是否正确
Serial.println("Password 1 correct!"); // 输出消息
lastOpenLockTime = millis(); // 更新上一次开锁的时间
lockState = true; // 将锁状态标志位置ture
digitalWrite(relayPin, HIGH); // 开锁
} else if (!checkPassword(enteredPassword, password2)) { // 检查第二个密码是否正确
Serial.println("Password 2 correct!"); // 输出消息
lastOpenLockTime = millis(); // 更新上一次开锁的时间
lockState = true; // 将锁状态标志位置ture
digitalWrite(relayPin, HIGH); // 开锁
} else if (!checkPassword(enteredPassword, password3)) { // 检查第三个密码是否正确
Serial.println("Password 3 correct!"); // 输出消息
lastOpenLockTime = millis(); // 更新上一次开锁的时间
lockState = true; // 将锁状态标志位置ture
digitalWrite(relayPin, HIGH); // 开锁
} else { // 如果密码都不正确
Serial.println("Incorrect password!"); // 输出消息
}
passwordSubscript = 0; // 数组下标置0
memset(enteredPassword, 0, sizeof(enteredPassword)); // 重置输入的密码全部为0
}
}
if (millis() - lastDetectionTime > DetectionPeriod) { // 如果当前时间 - 上一次检测时间 > 检测周期
if (rfid.PICC_IsNewCardPresent()) { // 如果读卡器上有新卡
if (!rfid.PICC_ReadCardSerial()) return; // 验证NUID是否已被读取
Serial.println(F("The NUID tag is:")); // 打印卡的UNID
printHex(rfid.uid.uidByte, rfid.uid.size);
if (checkMaster1(rfid.uid.uidByte) || checkMaster2(rfid.uid.uidByte)) { // 进行UNID对比,如果相同就开锁
lastOpenLockTime = millis(); // 更新上一次开锁的时间
lockState = true; // 将锁状态标志位置ture
digitalWrite(relayPin, HIGH); // 开锁
}
}
if (lockState) { // 如果锁状态标志位为true
if (millis() - lastOpenLockTime > OpenLockTime) { // 当开锁时间大于3s后,将锁置为上锁状态
lockState = false; // 锁状态标志位置false
digitalWrite(relayPin, LOW); // 上锁
}
}
oledShow();
lastDetectionTime = millis(); // 更新上一次检测时间
}
}