隨著網路技術和嵌入式技術的發展,物聯網開始引領新一波熱潮。製作這個項目的想法起源於我的宿舍生活。每次進入宿舍都需要找鑰匙非常不方便,在網路上看到關於智能門鎖的介紹,於是便想製作一個門禁系統,利用隨身攜帶的校園卡或者手機進行解鎖開門。
根據項目目標,本項目硬體上有以下要求:
根據以上要求,項目選型如下
(一)開發板與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 | 射頻卡 | 門禁 |