学生使用NFC打卡,显示在屏幕上,显示前十名的名字,每天显示两次;有个按键,一按就可以显示每个人一周前十名的总次数
1、项目简介
2、实现逻辑
#NFC检测到id卡,将卡号通过串口传送给mcu
#mcu获取卡号的最后一位做为识别人的标志,列一个数组将卡号最后一位和真人对应起来
#nodemcu正常联网成功会进行指示灯闪烁
#nodemcu一段时间连不上网会重启
#人员刷卡时间为上午和下午某个时间段,超出这个时间认为迟到,超出时间的或者不在打卡前十名的不显示在屏幕上
#串口屏上有10个位置,正常情况为空,在时间段内有人打卡就显示人名,超出时间段,会在屏幕右下角显示这个人迟到,屏幕人名会在上午和下午统计前清除上一次状态
#有一个按键,按键按下显示周排名前十的人名,并进行次数多少排序以及将进前十的次数显示出来,在周日的某个时间清零
#再次按显示周排名按键,重新显示当日某时间段进前十的人名
3、应用场景
#学生打卡
#nfc签到
4、核心代码梳理
uint8_t *data_nfc = (uint8_t *) malloc(50);
uint8_t stu_nums_t[STU_SUB] = {
0xF0, 0x05, 0x21, 0x20, 0xBB, 0x4F, 0x70, 0xE9, 0x25,
0xEF, 0x64, 0xD4, 0xDF, 0x0F, 0xFC, 0xED, 0x23, 0x98,
0x68, 0x0A, 0x4B, 0x3B, 0x1B, 0xCC, 0x47, 0xE8, 0x77,
0x71, 0xC1, 0xE3, 0x9F, 0xF2, 0x7A, 0x1A, 0x8E, 0x10,
0x2E, 0xDE, 0x7B, 0x63, 0xB2, 0xE7, 0xC7, 0xE2, 0xAD,
0x09, 0xF8, 0x8D, 0x08, 0x1E, 0x33, 0xBC, 0x4D, 0x61,
0x3C, 0x86, 0x2A
};
/wifi&time///
const char *ssid = "fei";//WIFI
const char *password = "0123456789";//WIFI密码 0123456789
const char* ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 8*3600;//这里采用UTC计时,中国为东八区,就是 8*60*60
const int daylightOffset_sec = 0;//同上
void printLocalTime()
{
struct tm timeinfo;
if(!getLocalTime(&timeinfo)){
Serial.println("Failed to obtain time");
return;
}
Serial.println(&timeinfo, "%A, %B %d %Y %H:%M:%S");
}
void setup(void)
{
//USART
Serial.begin(115200);
Serial2.begin(9600); //io16 rx - tx; io17 tx - rx; Serial1 rx fail
pinMode(LED_BUILTIN, OUTPUT);
pinMode(KEY1, INPUT);
pinMode(KEY1, INPUT_PULLUP);
Serial.print("START");
Serial2.print("START");
WiFi.begin(ssid, password);//连接到网络
while (WiFi.status() != WL_CONNECTED) {//等待网络连接成功
delay(500);
Serial.print(".");
if(rst_num++ > 20)//10s没连上就重启
{
esp_restart();
}
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());//打印模块IP
//init and get the time
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
printLocalTime();
for(uint8_t i=0; i<10; i++)
{
stus_early_day[i] = 0xff;
}
}
void loop(void)
{
//led///
digitalWrite(LED_BUILTIN, HIGH);
delay(100);
digitalWrite(LED_BUILTIN, LOW);
delay(100);
//check///
check = digitalRead(KEY1);
if(check == 0)
{
delay(100);
check = digitalRead(KEY1);
while(!check) //wait
{
check = digitalRead(KEY1);
delay(100);
}
check_flag = !check_flag;
//Serial.printf("KEY: %d\r\n",check_flag);
if(check_flag) //查看周排名
{
uint8_t b[STU_SUB];
for(uint8_t i=0; i<STU_SUB; i++) //变量i代表比较的趟数
{
b[i] = i;
}
//前十次数排序 57个数中找到最大的10个数对应的下标
for(uint8_t i=1; i<STU_SUB; i++) //变量i代表比较的趟数
{
for(uint8_t j=0; j<STU_SUB-i; j++) //变最j代表每趟两两比较的次数
{
if(stus_sub_num[b[j]] < stus_sub_num[b[j+1]])
{
uint8_t t = b[j]; //利用中间变量实现两值互换
b[j] = b[j+1];
b[j+1] = t;
}
}
}
for(uint8_t i=0; i<10; i++) //获取周排名前十的学生序号,一次没进过就不显示了
{
if(stus_sub_num[b[i]] == 0)
{
break;
}
stus_early_wek[i] = b[i];
}
//显示前十及前十对应的次数
for(uint8_t i=0; i<10; i++)
{
//stus_early_wek[i]
//stus_sub_num[stus_early_wek[i]]
delay(50);
tx_data[5] = i+1;
tx_data[7] = stus_early_day[i];
for(uint8_t j=0; j<8; j++) //显示人名
{
Serial.printf("%c", tx_data[j]);
}
delay(50);
tx_data[5] = i+16;
if(tx_data[7] == 58)
tx_data[7] = 0;
else tx_data[7] = stus_sub_num[stus_early_wek[i]];
for(uint8_t j=0; j<8; j++) //显示排名
{
Serial.printf("%c", tx_data[j]);
}
//Serial.printf("studen %d weed nums is :%d\r\n", stus_early_wek[i], stus_sub_num[stus_early_wek[i]]);
}
}
else//恢复日常显示
{
for(uint8_t i=0; i<10; i++)
{
//显示前十
delay(50);
tx_data[5] = i+1;
tx_data[7] = stus_early_day[i];
for(uint8_t j=0; j<8; j++) //显示人名
{
Serial.printf("%c", tx_data[j]);
}
delay(50);
tx_data[5] = i+16;
tx_data[7] = 0;
for(uint8_t j=0; j<8; j++) //显示排名
{
Serial.printf("%c", tx_data[j]);
}
}
}
}
//receive nfc///
int len = Serial2.readBytes(data_nfc, 50); //04 0C 02 20 00 04 00 31 04 0C 02 20 00 04 00 01
//get time///
struct tm timeinfo;
if(!getLocalTime(&timeinfo)){
Serial.println("Failed to obtain time");
}
if((timeinfo.tm_hour == 21) && (timeinfo.tm_min >= 0) && (timeinfo.tm_min <= 39)) //6:35- 6:41
{
//check_flag = 0;
//显示前十名 并记录
if(len > 0)
{
if((data_nfc[0] == 0x04) && (data_nfc[1] == 0x0C))
{
//判断是否已经刷过卡 stus_early_day[10]
uint8_t card_d = 0;
uint8_t stu_num = STU_SUB + 1;
for(uint8_t i=0; i<STU_SUB; i++)
{
if(stu_nums_t[i] == data_nfc[11])
{
stu_num = i;
break;
}
}
for(uint8_t i=0; i<10; i++)
{
if(stus_early_day[i] == stu_num)
card_d = 1;
}
//记录
if(!card_d)
{
if(name_add < 10)
{
stus_early_day[name_add++] = stu_num;
stus_sub_num[stu_num] ++;//周前十次数加一
//Serial.printf("studen`s weed num is :%d\r\n", stus_sub_num[stu_num]);
}
}
//显示
for(uint8_t i=0; i<10; i++)
{
delay(50);
//显示前十
//Serial.printf("studen`s name is :%d\r\n", stus_early_day[i]);
tx_data[5] = i+1;
tx_data[7] = stus_early_day[i];
for(uint8_t j=0; j<8; j++) //显示人名
{
Serial.printf("%c", tx_data[j]);
}
}
}
}
}
else if((timeinfo.tm_hour == 21) && (timeinfo.tm_min >= 40) && (timeinfo.tm_min <= 58)) //13:30- 13:38
{
//check_flag = 0;
//显示前十名 并记录
if(len > 0)
{
if((data_nfc[0] == 0x04) && (data_nfc[1] == 0x0C))
{
//判断是否已经刷过卡 stus_early_day[10]
uint8_t card_d = 0;
uint8_t stu_num = STU_SUB + 1;
for(uint8_t i=0; i<STU_SUB; i++)
{
if(stu_nums_t[i] == data_nfc[11])
{
stu_num = i;
break;
}
}
for(uint8_t i=0; i<10; i++)
{
if(stus_early_day[i] == stu_num)
card_d = 1;
}
//记录
if(!card_d)
{
if(name_add < 10)
{
stus_early_day[name_add++] = stu_num;
}
}
stus_sub_num[stu_num] ++;//周前十次数加一
//Serial.printf("studen`s weed num is :%d\r\n", stus_sub_num[stu_num]);
//显示
for(uint8_t i=0; i<10; i++)
{
delay(50);
//Serial.printf("studen`s name is :%d\r\n", stus_early_day[i]);
tx_data[5] = i+1;
tx_data[7] = stus_early_day[i];
for(uint8_t j=0; j<8; j++) //显示人名
{
Serial.printf("%c", tx_data[j]);
}
}
}
}
}
else if((timeinfo.tm_hour == 22) && (timeinfo.tm_min == 00))
{
for(uint8_t i=0; i<10; i++)
{
delay(50);
stus_early_day[i] = 58;
if(!check_flag)//不是查看周排名模式 就前10显示屏全清
{
tx_data[5] = i+1;
tx_data[7] = stus_early_day[i];
for(uint8_t j=0; j<8; j++) //显示人名
{
Serial.printf("%c", tx_data[j]);
}
}
}
name_add = 0;
}
else //显示迟到
{
if(len > 0)
{
if((data_nfc[0] == 0x04) && (data_nfc[1] == 0x0C))
{
uint8_t stu_num = STU_SUB + 1;
for(uint8_t i=0; i<STU_SUB; i++)
{
if(stu_nums_t[i] == data_nfc[11])
{
stu_num = i;
break;
}
}
delay(50);
tx_data[5] = 11;
tx_data[7] = stu_num;
for(uint8_t j=0; j<8; j++) //显示人名
{
Serial.printf("%c", tx_data[j]);
}
delay(1000);
tx_data[5] = 11;
tx_data[7] = STU_SUB;
for(uint8_t j=0; j<8; j++) //显示迟到
{
Serial.printf("%c", tx_data[j]);
}
delay(1000);
tx_data[5] = 11;
tx_data[7] = 58;
for(uint8_t j=0; j<8; j++) //清除迟到
{
Serial.printf("%c", tx_data[j]);
}
}
}
}
if((timeinfo.tm_hour == 22) && (timeinfo.tm_wday == 7)) //周日晚上10点清空这周排名
{
for(uint8_t i=0; i<STU_SUB; i++)
{
stus_sub_num[i] = 0;
}
for(uint8_t i=0; i<10; i++)
{
stus_early_wek[i] = 58;
delay(50);
tx_data[5] = i+1;
tx_data[7] = stus_early_day[i];
for(uint8_t j=0; j<8; j++) //显示人名
{
Serial.printf("%c", tx_data[j]);
}
delay(50);
tx_data[5] = i+16;
tx_data[7] = 0;
for(uint8_t j=0; j<8; j++) //显示排名
{
Serial.printf("%c", tx_data[j]);
}
//Serial.printf("studen %d weed nums is :%d\r\n", stus_early_wek[i], stus_sub_num[stus_early_wek[i]]);
}
}
while (WiFi.status() != WL_CONNECTED) {//等待网络连接成功
delay(500);
Serial.print(".");
if(rst_num++ > 20)//10s没连上就重启
{
esp_restart();
}
}
}
5、部分参考资料
#修改卡号使用HF高频读写测试软件V1.0.4.exe
#串口屏使用sd卡烧录固件,因为屏太大使用外部供电
#需要在资料包中看一下esp32接线和串口屏通信接口相关的内容
6、注意事项
#显示屏使用外部12v供电,nodemcu是5v,中间加了一个电压转换
#按键识别使用的算法不需要加外部电容滤波,但需要长按一会,在弹起的那一刻识别
#现程序中在打卡时间外打卡没显示迟到字样,只显示一下刷卡的人名然后消失,可以取消那部分注释进行显示