基於WeMos D1(ESP8266)的校園卡門禁系統

視頻封面

00:15刷卡視頻封面

00:24電腦端控制視頻封面

00:19手機端控制

一、項目背景

隨著網路技術和嵌入式技術的發展,物聯網開始引領新一波熱潮。製作這個項目的想法起源於我的宿舍生活。每次進入宿舍都需要找鑰匙非常不方便,在網路上看到關於智能門鎖的介紹,於是便想製作一個門禁系統,利用隨身攜帶的校園卡或者手機進行解鎖開門。

二、硬體平台

根據項目目標,本項目硬體上有以下要求:

  1. 單片機能進行數據處理
  2. 實現對舵機的控制
  3. 接入wifi,並通過HTML接收信息
  4. 對射頻卡進行掃描

根據以上要求,項目選型如下

(一)開發板與wifi模塊

項目使用卡發版WeMos D1,其核心使用ESP-8266,具有11個I/O引腳,除D0引腳,均支持PWM、IIC、中斷等,具有1個模擬量輸入I/O。由於核心為ESP8266,因此具備wifi功能,可以在AP模式、station模式和混合模式下運行。符合要求。

項目開發使用arduino IDE,開發驗證簡單。

(二)射頻讀卡模塊

PN532是一個高度集成的非接觸讀寫晶元,它包含80C51微控制器內核,集成了13.56MHz下的各種主動/被動式非接觸通信方法和協議。支持SPI、IIC、UART通訊方式,支持校園卡標準,性價比高,適合本次項目開發。

本次使用該模塊的UART通信方式與單片機連接。

(三)舵機等

機械傳動部分使用舵機,選用90g舵機,其功率較小,可以使用單片機I/O直接驅動,扭矩能滿足拉開門鎖的要求。

三、開發環境

(一)開發板

WeMos D1開發板使用Arduino IDE進行開發,使用C++語言,面向對象的開發方式。

本次項目在windows平台開發,使用Arduino IDE 1.8.5,調試使用串口獵人。系統串口通信運行在比特率115200、無校驗位、數據位8bit、停止位1bit狀態。

(二)終端控制平台

由於本次使用HTML與架設在路由器內網的路由器進行通信,因此終端設備只需要支持HTML即可。手機等設備可以直接使用瀏覽器進行控制,因此項目的通用性很強。

(三)網路環境

本系統在學校宿舍wifi環境下測試。我的宿舍使用小米路由器路由校園網路。WeMos D1連接宿舍wifi後會被分配到內網ip,由於未做內網透傳並且沒有設計校園網登陸部分,因此只有同樣連接在宿舍wifi下的設備,才能對系統進行控制。

四、基本原理

(一)物聯網

物聯網是互聯網、傳統電信網等信息承載體,讓所有能行使獨立功能的普通物體實現互聯互通的網路。隨著網路的快速發展,越來越多的設備開始接入互聯網,為人們的生活帶來了極大的便利。

(二)射頻標籤

射頻識別,RFID(Radio Frequency Identification)技術,又稱無線射頻識別,是一種通信技術,可通過無線電訊號識別特定目標並讀寫相關數據,而無需識別系統與特定目標之間建立機械或光學接觸。

我們的校園卡是工作在13.56MHZ的IC非接觸式射頻卡,資料顯示,這種射頻卡有8KB儲存空間,分為16個扇區,每個扇區分為4個塊。序號從第0塊到第63塊,其中第0塊未加密,保存著射頻卡的序列號(UID),根據UID可以識別射頻卡。

(三)HTML 1.1

HTML是用來在Internet上傳送超文本的傳送協議,運行在TCP/IP協議簇之上的HTTP應用協議,它可以使瀏覽器更加高效,使網路傳輸減少。使用此協議,可以很方便開發不同=平台之間的文本傳輸。

(四)項目目標

本次設計計劃製作校園卡門禁系統,可以接入宿舍wifi,當識別到特定校園卡時,或者當使用手機發送固定內容時,控制舵機進行響應。

五、程序實現

程序流圖

六、調試

本項目通過串口進行調試。串口列印狀態信息。

有兩種開鎖方式,掃描學生卡和網頁端控制。

我在程序中提前寫入了舍友的校園卡UID,當掃描到已知卡放在讀卡器上時,串口輸出「你好,XXX」,XXX為舍友的名字。此時舵機旋轉,表示拉動門鎖完成開門。

刷卡示意

網頁端控制通過HTML1.1協議,可以在手機或電腦等終端運行。當系統以客戶端的形式接入宿舍的wifi時,路由器會分配一個區域網ip給ESP8266,如果統一區域網下的設備登陸「10.15.8.42/gpio/0」(10.15.8.42為分配的ip),相當於向設備發送gpio/0的信息,經過識別可以控制舵機運轉,同時ESP8266可以通過HTML1.1協議傳回一個信息,程序中設計的是傳回鎖頭狀態。

網頁端控制

七、代碼

#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#define print1Byte(args) mySerial.write(args)
#define print1lnByte(args) mySerial.write(args),Serial.println()
#else
#include "WProgram.h"
#define print1Byte(args) Serial.print(args,BYTE)
#define print1lnByte(args) Serial.println(args,BYTE)
#endif

#include <SoftwareSerial.h>
SoftwareSerial mySerial(D3,D4); //PN532軟串口操作,SCL->D3,SDA->D4

#include <ESP8266WiFi.h>
WiFiClient client;
WiFiServer server(80);

#include <Servo.h>
#define LOCKANGLE 10 //開鎖角度值
#define UNLOCKANGLE 170 //鎖定角度值
#define MYLOCK D2 //舵機連接D2
int lastangle;
Servo Lock; //定義鎖

struct date { //程序運行狀態結構體
char lock_state; //0 off 1 on
char wifi_state; //0 disconnected 1 connected
char* wifi_ip; //ip地址
char nfc_state; //NFC狀態 0 disconnected 1 connected
}state;

unsigned int EntireI = 0; //程序運行次數,最小單位秒數

///////////////PN532////////////////

const unsigned char wake[24] = {
0x55, 0x55, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0x03, 0xfd, 0xd4, 0x14, 0x01, 0x17, 0x00}; //喚醒NFC模塊指令
const unsigned char wakeup[15] = {
0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0x00, 0x00, 0xFF, 0x02, 0xFE, 0xD5, 0x15, 0x16, 0x00}; //喚醒後接收到信息
const unsigned char tag[11] = {
0x00, 0x00, 0xFF, 0x04, 0xFC, 0xD4, 0x4A, 0x01, 0x00, 0xE1, 0x00}; //掃描卡指令
unsigned char nocard[25] = { //無卡狀態
0x00, 0x00, 0xFF, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};

unsigned char receive_ACK[25]; //暫存信息

struct ID {
char name[10];
unsigned char uid[4];
};

int Mumnum = 5; //成員數量
struct ID room[5] {
{"李漠雨",{0xCD, 0x03, 0xD0, 0x24}},
{"宋廣軒",{0x1E, 0x16, 0xBC, 0xDF}},
{"何 鵬",{0x2E, 0xD4, 0xC2, 0xDF}},
{"楊澤宇",{0xB3, 0xA7, 0xF9, 0xF0}},
{"楊 帆",{0x43, 0x53, 0x56, 0xFA}},
};

////////////////ESP8266////////////////
const char* ssid = "5#121";
const char* password = "qazwsx123";
//const char* ssid = "TP-LINK_A06E";
//const char* password = "tuanxue520";
int val = 10;
int Connum = 3;
const String order_inside[] = {
"121contral/0 ", "121contral/1 ","nop"};
unsigned char order; //控制命令 接收

//////////////////////////////////////

void setup() {
Serial.begin(9600); //計算機通信串口波特率
mySerial.begin(115200); //PN532串口波特率

delay(500);
check_card_state(); //檢查NFC狀態

delay(100);
Wifi_connect(); //連接Wifi

}

void loop() {

if(EntireI % 15 == 0){
check_card_state(); //檢查NFC狀態
}

delay(10);

if(EntireI % 1 == 0){
switch (read_card()){
case -1:break; //沒有卡
case -2:Serial.println("未知");break; //未錄入的卡(LED燈亮示意)
default:lock_unlock();Serial.println("開門");break; //有卡
}
}

if ( check_wifi_state() ) {
client = server.available();
if (!client) {
delay(10);
}
}
else { Wifi_connect(); }

char temp = read_html();
switch (temp){
case e:Serial.println("指令錯誤");break; //指令錯誤
case w:break; //指令格式錯誤或者無指令
case 0 :lock_lock();Serial.println("關門");break; //返回0
case 1 :lock_unlock();Serial.println("開門");break; //返回1
}

EntireI++;
Serial.println();
if(EntireI >= 10000){EntireI = 0;}
delay(700);

}

int read_card() {

send_tag();
delay(60);
read_ACK(25);
delay(100);

int i;
for (i = 0;i < Mumnum;i++) {
if(compare_rec(room[i].uid,19,4)) {
Serial.print("你好 ");
Serial.println(room[i].name);
Serial.println(i);
return i;
break;
}
else if ( i == Mumnum-1 ) {
if(compare_rec(nocard,0,25)) {return -1;break;}
//Serial.println("未知");
display(25);
Serial.println(i);
return -2;
}
}
}

void check_card_state(){
delay(100);
wake_card(); //喚醒卡
delay(100);
read_ACK(15);
delay(100);
if(compare_rec(wakeup,0,15)) {
state.nfc_state = 1;
Serial.println("PN532正常!");
}
else {
state.nfc_state = 0;
Serial.println("PN532失敗!");
}
}

void UART1_Send_Byte(unsigned char command_data) {//send byte to device
print1Byte(command_data);
;;
#if defined(ARDUINO) && ARDUINO >= 100
mySerial.flush();// complete the transmission of outgoing serial data
#endif
}

void UART_Send_Byte(unsigned char command_data) {//send byte to PC
Serial.print(command_data,HEX);
Serial.print(" ");
}

void read_ACK(unsigned char temp) {//read ACK into reveive_ACK[]
unsigned char i;
for(i=0;i<temp;i++) {
receive_ACK[i]= mySerial.read();
}
}

void wake_card(void) {//send wake[] to device
unsigned char i;
for(i=0;i<24;i++) //send command
UART1_Send_Byte(wake[i]);
}

void send_tag(void) {//send tag[] to device
unsigned char i;
for(i=0;i<11;i++) //send command
UART1_Send_Byte(tag[i]);
}

void display(unsigned char tem) {//send receive_ACK[] to PC
unsigned char i;
for(i=0;i<tem;i++) //send command
UART_Send_Byte(receive_ACK[i]);
Serial.println();
}

int compare_rec(const unsigned char temp[],int n,int m) {
int flag = 1;
int i;
for(i = 0;i < m-1;n++,i++) {
if(receive_ACK[n] == temp[i]) {
//print1lnByte(receive_ACK[n]);
}
else {
flag = 0;
break;
}
flag = 1;
}
if(flag==1) {
return 1;
}
else {
return 0;
}
}

void Wifi_connect() { // 連接WiFi

Serial.println();
Serial.print("Connecting to ");
Serial.println(ssid);

WiFi.begin(ssid, password);

int errortimes;
for (errortimes = 0;WiFi.status() != WL_CONNECTED && errortimes < 15;errortimes++) {
delay(100);
Serial.print(".");
}

if (errortimes < 14) {
Serial.println("WiFi啟動正常");
server.begin();
state.wifi_state = 1;
}
else
{
Serial.println("WiFi啟動失敗");
state.wifi_state = 0;
}

}

int check_wifi_state() { // 檢查WiFi

if (WiFi.status() == WL_CONNECTED) {
Serial.println("WiFi正常");
state.wifi_state = 1;
Serial.println( WiFi.localIP());
}
else
{
Serial.println("WiFi失敗");
state.wifi_state = 0;
}
return state.wifi_state;

}

char read_html() { //從http/1.1獲得數據

String req = client.readStringUntil(
);
Serial.print("讀取伺服器: ");
Serial.println(req);
client.flush();

if (req.indexOf("/121contral/") != -1) {
Serial.println("收到指令");
int i;
for (i = 0;i<Connum;i++) {
if (req.indexOf(order_inside[i]) != -1){
order = i;
break;
}
}
if (i == Connum-1){
client.print("HTTP/1.1 404
");
order = e; //指令錯誤
}
}
else {
order = w; //指令格式錯誤
}

String s;
if(order==1)
{
s = "HTTP/1.1 200 OK
Content-Type: text/html

<!DOCTYPE HTML>
<html>
已經開門</html>
";
}

if(order==0)
{
s = "HTTP/1.1 200 OK
Content-Type: text/html

<!DOCTYPE HTML>
<html>
已經關門</html>
";
}

client.flush();
// Send the response to the client
client.print(s);
delay(1);

return order;

}

void changeservo(int angle) { //舵機緩慢變化
Serial.print(lastangle);
Serial.print("....>");
Serial.println(angle);
if(angle == lastangle) {
Lock.write(lastangle);
delay(20);
}
while(angle != lastangle) {
if (angle < lastangle) {
lastangle--;
Lock.write(lastangle);
delay(20);
}
else if(angle > lastangle) {
lastangle++;
Lock.write(lastangle);
delay(20);
}
}
}

void lock_lock()
{
Lock.attach(MYLOCK);
changeservo(LOCKANGLE);
delay(1);
Lock.detach();
}

void lock_unlock()
{
Lock.attach(MYLOCK);
changeservo(UNLOCKANGLE);
delay(1);
Lock.detach();

delay(2000);
lock_lock();
}

推薦閱讀:

TAG:Arduino | 射頻卡 | 門禁 |