本篇代码参考文章《Arduino宿舍门禁,实现刷卡(NFC)开门》。在此基础上增加了一点功能完成的计时刷卡门禁,仅用于期末结课,并在此分享。(有不妥请联系我!我会立马删除!!!)
实验器材如下
- RFID-RC522 RFID射频IC卡感应模块
- SG90舵机
- LCD1602 I2C液晶显示屏
- Arduino主板
- 无源蜂鸣器
- LED灯*2
- 220Ω电阻*2
- 面包板
- 杜邦线若干
在购买实验器材时,如果所在环境没有焊接条件,请记得购买焊接好的器材啊啊啊啊!!!没焊接的器材是无法正常使用的。
液晶显示屏推荐使用LCD1602 I2C,因为部分显示屏显示的字母会乱码(怀疑是跟rfid使用的库有冲突?屏幕单独使用很正常,连在一起就不可以)
电路图
以下是代码部分
记得下好库哦
#include <SPI.h>
#include <RFID.h>
#include <Servo.h>//舵机需要用的库
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x27,16,2); //配置LCD地址及行列
RFID rfid(10,9); //D10--读卡器MOSI引脚、D5--读卡器RST引脚,指定了 SDA 引脚为 10,RST 引脚为 9。
int temp=0;//判断卡号是否录入
int r=0;//判断具体用户
Servo myservo;//定义舵机变量名
// 定义蜂鸣器引脚
const int BUZZER_PIN = 4;
//定义红绿led灯引脚
const int Green_pin=5;
const int Red_pin=6;
unsigned long startTime = 0; // 用于存储计时器开始的时间
unsigned long elapsedTime = 0; // 用于存储经过的时间
bool timing = false; // 标志位,表示计时是否正在进行
// 定义马里奥主题曲的音符和节奏
const int melody[] = {659, 659, 0, 659, 0, 523, 659, 0, 784, 0, 0, 0, 392, 0, 0, 0};//音符
const int rhythm[] = {8, 8, 8, 8, 8, 8, 4, 8, 4, 8, 8, 8, 4, 8, 8, 8};//每个音符的节奏长度,一个拍的几分之几
//刷卡有误会报警报,警报的声音
const int melody1[] = {1000, 0, 1000, 0, 1000, 0, 1000, 0};//音符
const int rhythm1[] = {500, 500, 500, 500, 500, 500, 500, 500};//每个音符的节奏长度,一个拍的几分之几,500代表每个音符持续半拍
void setup()
{
Serial.begin(9600);//设置波特率
SPI.begin();//初始化arduino上的串行外设接口,SPI接口常与外部设备进行数据交换
rfid.init();//初始化RFID模块,准备好与卡片进行通信。进行的操作有:1.启动rfid模块;2.配置rfid模块参数;3.初始化与rfid模块通信所需的引脚和其他设置。
pinMode(BUZZER_PIN,OUTPUT);//初始化蜂鸣器
pinMode(Green_pin,OUTPUT);
pinMode(Red_pin,OUTPUT);
myservo.attach(7);//将舵机与7引脚连接初始化。舵机通过PWM脉冲宽度信号进行控制。
digitalWrite(BUZZER_PIN,LOW);//蜂鸣器初始状态设置为LOW
lcd.init(); //初始化LCD
lcd.backlight(); //打开背光
}
void loop()
{
myservo.write(0);//将舵机设置到0度的位置
//找卡
if (rfid.isCard()) {//判断硬件是否接触到卡
Serial.println("找到卡");
//读取卡序列号
if (rfid.readCardSerial()) {//是否读到了卡片序列号,读到了存储在rfid.serNum数组中
Serial.print("卡号");
Serial.print(rfid.serNum[0],HEX);
Serial.print(" ");
Serial.print(rfid.serNum[1],HEX);
Serial.print(" ");
Serial.print(rfid.serNum[2],HEX);
Serial.print(" ");
Serial.print(rfid.serNum[3],HEX);
Serial.print(" ");
Serial.print(rfid.serNum[4],HEX);
Serial.print(" ");
Serial.println(" ");
//将读到的卡片序号通过串口监视器打印出来
if(rfid.serNum[0]==0x61&&rfid.serNum[1]==0x93&&rfid.serNum[2]==0x2f&&rfid.serNum[3]==0x90&&rfid.serNum[4]==0x4d)//我的学生卡
{
temp=1;//cc
r=1;
}
//按照串口监视器读取数值填入
else if(rfid.serNum[0]==0x61&&rfid.serNum[1]==0x93&&rfid.serNum[2]==0x3E&&rfid.serNum[3]==0x7F&&rfid.serNum[4]==0xB3)//学生1的学生卡
{
temp=1;//学生1
r=2;
}
else if(rfid.serNum[0]==0x61&&rfid.serNum[1]==0x93&&rfid.serNum[2]==0x3E&&rfid.serNum[3]==0x60&&rfid.serNum[4]==0xAC)//学生2的学生卡
{
temp=1;//学生2
r=3;
}
else if(rfid.serNum[0]==0x01&&rfid.serNum[1]==0x31&&rfid.serNum[2]==0xAC&&rfid.serNum[3]==0x80&&rfid.serNum[4]==0x1C)//学生3的手机
{
temp=1;//学生3
r=2;
}
else if(rfid.serNum[0]==0x61&&rfid.serNum[1]==0x93&&rfid.serNum[2]==0x04&&rfid.serNum[3]==0x8A&&rfid.serNum[4]==0x7C)//学生4的学生卡
{
temp=1;//学生4
r=4;
}
else if(rfid.serNum[0]==0x61&&rfid.serNum[1]==0x93&&rfid.serNum[2]==0x17&&rfid.serNum[3]==0x59&&rfid.serNum[4]==0xBC)//学生5的学生卡
{
temp=1;//学生5
r=5;
}
else{
temp=2;
}
}
//选卡,可返回卡容量(锁定卡片,防止多数读取),去掉本行将连续读卡。选择模块中特定的卡片,选择卡片后,可以执行进一步的操作,如读取卡片上的数据、写入数据到卡片等。
rfid.selectTag(rfid.serNum);
}
if(temp==1)//temp==1表示改卡片信息已经录入
{
timing = false;//每当有人刷卡就重新计时
lcd.clear();
lcd.setCursor(0,0);//设置 LCD 光标的位置.第一个参数表示要设置的光标所在行数(从 0 开始计数),第二个参数表示要设置的光标所在列数(同样从 0 开始计数)。不设置光标位置的话,重复刷卡会显示错乱。
lcd.print("Welcome home!");//显示字符数据
digitalWrite(Green_pin, HIGH); //绿色led灯亮起
myservo.write(85);//舵机开始转动,转动85度
switch(r){//判断当前刷卡的是哪位用户,并显示相应的名字
case 1:Serial.println("欢迎cc回家!");lcd.setCursor(4,1);lcd.print("Dear cc.");break;
case 2:Serial.println("欢迎sy回家!");lcd.setCursor(4,1);lcd.print("Dear sy.");break;
case 3:Serial.println("欢迎yw回家!");lcd.setCursor(4,1);lcd.print("Dear yw.");break;
case 4:Serial.println("欢迎jj回家!");lcd.setCursor(4,1);lcd.print("Dear jj.");break;
case 5:Serial.println("欢迎dd回家!");lcd.setCursor(4,1);lcd.print("Dear dd.");break;
default:Serial.println("欢迎回家!");break;
}
//刷卡成功,绿色led灯亮起,并播放马里奥主题曲作为提示
for (int i = 0; i < sizeof(melody) / sizeof(int); i++) {//遍历音符和节奏数组。在 C/C++ 中,sizeof 运算符可以返回给定类型或变量所占用的字节数。通过将数组的总字节数除以单个元素的字节数,可以得到数组中元素的个数。
if (melody[i] == 0) {//如果当前音符为0,则停顿,只持续节奏时间
// 休息时间
delay(rhythm[i] * 20);//延迟一段时间,使当前停顿时间得以保持
} else {
// 发出声音并持续一段时间
tone(BUZZER_PIN, melody[i]);//在蜂鸣器上播放当前音符声音,该函数指定引脚产生特定频率值的方波,以产生声音。tone()和noTone()是内置函数,不属于特定的库。
delay(rhythm[i] * 20);//延迟当前音符节奏长度,说白了就是播放这个音符多久
noTone(BUZZER_PIN);//关闭蜂鸣器
}
// 稍微延迟一下,使音符之间有一定的间隔
delay(50);
}
delay(2000);//让舵机持续85度一段时间
myservo.write(0);//舵机归位
temp=0;//录入变量归0
r=0;//用户判断变量归0
digitalWrite(Green_pin, LOW);//led灯灭
lcd.clear();//lcd显示屏清屏
}
else if(temp==2){//当前卡片未录入
timing = false;//每当有人刷卡就重新计时
lcd.clear();
lcd.print("ERROR:");//显示字符数据
lcd.print(rfid.serNum[2],HEX);//以16进制的方式打印卡号第3-5个。(因为校园卡前两位相同)
lcd.print(" ");
lcd.print(rfid.serNum[3],HEX);
lcd.print(" ");
lcd.print(rfid.serNum[4],HEX);
digitalWrite(Red_pin, HIGH);
Serial.println("卡号错误,请换卡!");
temp=0;
//刷卡失败,红色led灯伴随音乐闪烁作为提示
for (int i = 0; i < sizeof(melody1) / sizeof(int); i++) {
if (melody1[i] == 0) {
// 休息时间,蜂鸣器不响,灯也灭
digitalWrite(Red_pin, LOW);
delay(rhythm1[i]);
} else {
// 发出声音并持续一段时间,同时红色led灯亮起
digitalWrite(Red_pin, HIGH);
tone(BUZZER_PIN, melody1[i]);
delay(rhythm1[i]);
noTone(BUZZER_PIN);
}
// 稍微延迟一下,使音符之间有一定的间隔
delay(50);
}
//lcd.clear();//lcd清屏,否则会一直显示
}else{//lcd显示计时功能
if (!timing) {
startTime = millis(); // 开始计时
timing = true;
}
elapsedTime = millis() - startTime; // 计算经过的时间
int seconds = elapsedTime / 1000;
int minutes = seconds / 60;
int hours = minutes / 60;
int ac_seco=seconds-minutes*60-hours*60*60;//实际打印的秒
int ac_mins=minutes-hours*60*60;//实际打印的分
// 显示计时结果
lcd.setCursor(0,1);
lcd.print("Last: ");
lcd.print(hours);
lcd.print(":");
if (ac_mins< 10) {
lcd.print("0");
}
lcd.print(ac_mins);
lcd.print(":");
if (ac_seco < 10) {
lcd.print("0");
}
lcd.print(ac_seco);
//Serial.println(seconds-minutes*60-hours*60*60);
}
rfid.halt();//RFID模块休眠。当RFID模块不需要再读取卡片时,可以使用rfid.halt()函数将其置于休眠状态,以节省功耗并延长模块的使用寿命。同时,这个函数也可以释放RFID模块与卡片之间的通信,使得其他设备可以使用SPI总线。
}
使用这个代码需要下载两个库, rfid的可以直接看参考链接,里面写的很详细。
LCD的库请搜索LCDIIC有关教程。
功能展示
RFID门禁计时系统。
每重新启动主板或有人刷卡时,开始重新计时(如图1所示)。
图 1
RFID读卡器模块识别校园卡,通过RFID库自带的函数检测当前RFID模块是否接触了校园卡并读取当前卡片的序列号,用rfid.serNum[],即RFID模块中用于存储卡片序列号的数组来存储该卡片的前五位序列号。
如果代码判断部分中包含该卡的序列号,则舵机转动,带动门把手实现开门。同时绿色led灯亮起,蜂鸣器播放马里奥主题曲,lcd屏幕显示“Welcome home!”(如图3所示)持续一段时间。串口监视器显示该卡片序列号,以及学生姓名(如图2所示)。Lcd显示一段时间后重新回到计时界面,开始新一轮计时(如图1所示)。
图 2
图 3
如果代码判断部分中不包含该卡的序列号,则舵机不转动。红色led灯伴随蜂鸣器播放的警报进行闪烁,lcd屏幕显示“ERROR:卡号后三位”因为学生卡的前两位相同,所以只显示后三位以作区分(如图5所示),串口监视器显示该卡片序列号,并提示卡号错误(如图4所示)。蜂鸣器关闭后,LCD在显示未录入的卡号的同时,实时显示距上次刷卡所经过时间的计时,直到重启主板或下一次刷卡才做更新(如图6所示)。
图 4
图 5
图 6
增加了显示名字和计时的功能,剩下的请参考文章顶部链接!
想起来再回来补充 ;-)