在上一篇文章自制 Arduino RFID门锁中,我们制作了Arduino RFID门锁,并且能够使用RFID标签或键盘来解锁。那么,如果您想要进一步简化流程该怎么办呢?在第二部分中,我们将添加一个附加功能:使用智能手机来解锁。这是一种更简单的控制门的办法,尤其是当所有人都希望能够通过一个设备就能控制所有物品的时候。那么我们该怎么做呢?我们将会把蓝牙模块连接到先前的设置中,并连接电子门。
硬件
- HC-05 蓝牙模块
- ABK-704L 电气设备
- Arduino Nano
- RFID RC522
- 压电蜂鸣器
- 2x 330 电阻
- 4×4 键盘
- LCD适配器I2C
- LCD16X2BL
- DC-DC 降压模块 MP1584EN
软件
- MIT App Inventor 2
- Arduino IDE
- Github
步骤1:连接蓝牙
在这一步中,我们将深入研究HC-05蓝牙模块。
Arduino-info Wiki
有以下两种操作模式:
- 指令模式 – 将AT指令发送到蓝牙模块;
- 数据模式 – 从另一个蓝牙模块接收和传输数据。默认的模式是数据模式,默认设置如下:
- 波特率:9600 bps,数据:8位,停止位:1位,奇偶校验:无
- 密码:1234
- 设备名称:HC-05
针对我们的应用程序,我们将使用数据模式,因为我们仅需要使用串行通信来从手机接收信息。
有关串行通信的更多信息,请参考Arduino串行通信教程。
在此应用程序中,我们需要使用以下引脚—Arduino-info Wiki:
- VCC: +5 电源
- GND: 系统 / Arduino接地
- TX: 将串行数据从HC-05传输到 Arduino 串行接收端口
- RX: 接收来自Arduino 串行发送端口的串行数据
代码
蓝牙使用串行通信;“Serial.write(Serial.read());”连接了RX和TX引脚后,该指令将会正常运行。如果这对蓝牙模块不适用,则下面的代码返回的内容与您在Arduino IDE中编写的文本相同。
void setup(){
Serial.begin(9600);
Serial.println("What did you say?:");
}
void loop(){
if (Serial.available())
Serial.write(Serial.read());
}
您需要在设备管理器中找到Arduino的编程端口;连接蓝牙后,将出现“蓝牙链接上的标准串行”,您必须选择“USB-SERIAL”才能对Arduino进行编程。您需要使用串行电缆上传程序,不能使用无线蓝牙将程序上传到开发板。
连接RX和TX后,您将收到许多错误信息,如下所示:
为了避免这些错误,您需要在蓝牙的TX引脚没有连接到Arduino开发板的情况下上传程序。蓝牙的TX引脚具有低阻抗,而Arduino的RX输入具有高阻抗。最终的阻抗将为较低的一个,而RX输入将会被绕过(从USB端口产生的电流直接通过蓝牙输出,而不是Arduino开发板)。这就是数据无法传输到期望位置点的原因。另外,电流直接流入设备的输出端将会在模块之间产生错误的连接,会导致电气故障。我们想要做的是对Arduino进行编程,而不是将数据发送到蓝牙模块。
断开蓝牙的TX引脚后,一切都会正常工作,您就可以上传代码了。
上传代码后,下一步是连接到电脑上的蓝牙。您需要重新连接蓝牙的TX引脚,因为现在我们将使用无线通信,串行电缆只用于供电。
请按照以下步骤将蓝牙连接到PC(在Windows 10系统上):
- 打开设置(Settings)
- 点击设备(Devices)
- 选择蓝牙(Bluetooth)
- 点击开启(ON)
- 找到“HC-05”并点击“PAIR”
- 输入密码“1234”
如果您已经按照上述步骤进行了操作,那么您的蓝牙连接应该就成功了,您可以对模块进行测试。有一种简单的检查连接是否成功建立的方法:在Arduino IDE中键入一个单词,然后查看是否返回相同的单词,如果是的话,就说明一切正常,您可以继续往下进行了!如果不是,请返回之前的步骤并再次对蓝牙配对。
因为我们不再使用通过电缆的串行通信,所以需要找到蓝牙的COM。我们需要回到端口(PORTS)部分的设备管理器(Device Manager),并搜索蓝牙设备。在我的应用中是PORT 17:
在设备管理器中找到COM后,您需要在Arduino IDE中执行相同的操作(即将COM端口设置为COM 17)。您需要点击TOOLS → PORT → COM 17。
选择正确的COM后,您可以对模块进行测试,查看是否工作正常。
请确保您在串行监视器中选择了以下两个选项:
- NL & CR
- 9600 波特
在我们的演示中,我们将输入信息“Hello!”
输出内容应与输入内容相同:
步骤2:设备
让我们参考一下上一篇文章自制Arduino RFID门锁!我们将使用与之前相同的组件,然后增加一个蓝牙模块(HC-05)。为了将数据从智能手机传输到Arduino,我们需要蓝牙模块。
断开蓝牙的TX引脚很重要,否则我们将会遇到跟步骤1中相同的错误。
有三种方式可以打开门锁:
- 从键盘输入密码
- 将标签放在RFID附近
- 从智能手机输入密码
该锁具有很高的电流量(800mA)。我们添加一个绿色LED灯来查看代码是否运行正常。键入“*123456#” 代码且系统解锁后,LED指示灯将为高电平。
在下一步中,我们将添加电池和继电器。我们使用两节9V电池给门锁供电。如果您打算将设备电压设置为固定值,则需要购买一种可以从开关电源的220V电压中获得5V电压的设备。
因为锁的电流消耗很高,所以电池并联连接。在这种情况下,Arduino由电池供电。我们需要使用将9V转换为5V的DC-DC降压模块。输入电压由Arduino开发板的Vin引脚进入。我们将不再使用计算机USB的供电。
降压模块的输出电压要固定在5V。您需要使用万用表进行校准;输入电压为9V,我们将旋扭电位器,直到输出电压为5V。
完整代码
#include <EEPROM.h>
#include <SPI.h>
#include <MFRC522.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <Keypad.h>
int state_bt=1;
int relPin;
int stare=0;
byte COD[10];
byte AUX[10];
int k=0;
String codacces="*123456#";
String codpairing="*654321#";
//nfc
#define RST_PIN 9 // Configurable, see typical pin layout above
#define SS_PIN 10 // Configurable, see typical pin layout above
MFRC522 mfrc522(SS_PIN, RST_PIN); // Create MFRC522 instance
#define NEW_UID {0xDE, 0xAD, 0xBE, 0xEF}
MFRC522::MIFARE_Key key;
//lcd
LiquidCrystal_I2C lcd(0x27,16,2);
//TASTATURA
const byte numRows= 4; //number of rows on the keypad
const byte numCols= 4; //number of columns on the keypad
//keymap defines the key pressed according to the row and columns just as appears on the keypad
char keymap[numRows][numCols]=
{
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
//Code that shows the the keypad connections to the arduino terminals
byte rowPins[numRows] = {2,3,4,5}; //Rows 0 to 3
byte colPins[numCols]= {A0,7,8,9}; //Columns 0 to 3
//initializes an instance of the Keypad class
Keypad myKeypad= Keypad(makeKeymap(keymap), rowPins, colPins, numRows, numCols);
void setup() {
pinMode(A0,OUTPUT);
digitalWrite(A0,HIGH);
pinMode(A3,OUTPUT);
digitalWrite(A3,HIGH);
pinMode(A1,OUTPUT);
digitalWrite(A1,HIGH);
pinMode(A2,OUTPUT);
digitalWrite(A2,LOW);
pinMode(6,OUTPUT);
digitalWrite(6,HIGH);
//nfc
Serial.begin(9600); // Initialize serial communications with the PC
Serial.println("What did you say?:");
while (!Serial); // Do nothing if no serial port is opened (added for Arduinos based on ATMEGA32U4)
SPI.begin(); // Init SPI bus
mfrc522.PCD_Init(); // Init MFRC522 card
for (byte i = 0; i < 6; i++) {
key.keyByte[i] = 0xFF;
}
lcd.init();
lcd.backlight();
lcd.setCursor(0,0);
lcd.clear();
lcd.print("BLOCKED");
}
void citireNFC(){
for (byte i =0; i<(mfrc522.uid.size); i++) {
COD[i]=mfrc522.uid.uidByte[i];
}
Serial.print("COD");
Serial.print(COD[0]);
Serial.print(COD[1]);
Serial.print(COD[2]);
Serial.print(COD[3]);
}
void pairNFC(){
Serial.println("COD in pair");
Serial.print(COD[0]);
Serial.print(COD[1]);
Serial.print(COD[2]);
Serial.print(COD[3]);
long r=0;
int c=0;
for(int i=1;i<=EEPROM.read(0);i++){
switch(i%4){
case 1 :{AUX[0]=EEPROM.read(i); break;}
case 2 :{AUX[1]=EEPROM.read(i); break;}
case 3 :{AUX[2]=EEPROM.read(i); break;}
case 0 :{AUX[3]=EEPROM.read(i); break;}
}
if((i)%4==0)
{Serial.println(r);
if( AUX[0]==COD[0] && AUX[1]==COD[1] && AUX[2]==COD[2] && AUX[3]==COD[3] ){
lcd.clear();
lcd.setCursor(0,0);
lcd.print("CODE ALREADY IN");
lcd.setCursor(0,1);
lcd.print("SYSTEM");
delay(2000);
c=1;
break;}
}
}
if(c==0){int ttt=EEPROM.read(0);
Serial.println("CODE PAIRED");
Serial.print(COD[0]);
Serial.print(COD[1]);
Serial.print(COD[2]);
Serial.print(COD[3]);
EEPROM.write(ttt+1,COD[0]);
EEPROM.write(ttt+2,COD[1]);
EEPROM.write(ttt+3,COD[2]);
EEPROM.write(ttt+4,COD[3]);
ttt=ttt+4;
Serial.println("ttt");
Serial.println(ttt);
EEPROM.write(0,0);
EEPROM.write(0,ttt);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("CODE PAIRED");
delay(2000);}
}
boolean validareNFC(){
boolean c=false;
for(int i=1;i<=EEPROM.read(0);i++){
switch(i%4){
case 1 :{AUX[0]=EEPROM.read(i); break;}
case 2 :{AUX[1]=EEPROM.read(i); break;}
case 3 :{AUX[2]=EEPROM.read(i); break;}
case 0 :{AUX[3]=EEPROM.read(i); break;}
}
if((i)%4==0)
{
if( AUX[0]==COD[0] && AUX[1]==COD[1] && AUX[2]==COD[2] && AUX[3]==COD[3])
c=true;
}}
return c;
}
int comparareCOD(String a)
{
if(a.equals(codacces))
return 1;
else if(a.equals(codpairing)) return 2;
else return 0;
}
String iaCOD(char x)
{ char vec[10];
vec[0]=x;
lcd.setCursor(0,0);
lcd.clear();
lcd.print('X');
for(int i=1;i<8;i++)
{vec[i]=myKeypad.waitForKey();
lcd.print('X');}
vec[8]=NULL;
String str(vec);
return str;
}
void loop() {
//Start BT autentification
if(Serial.available())
{
char c=Serial.read();
switch (state_bt) {
case 1:
if(c=='*') state_bt=2;
else state_bt=1;
break;
case 2:
if(c=='1') state_bt=3;
else state_bt=1;
break;
case 3:
if(c=='2') state_bt=4;
else state_bt=1;
break;
case 4:
if(c=='3') state_bt=5;
else state_bt=1;
break;
case 5:
if(c=='4') state_bt=6;
else state_bt=1;
break;
case 6:
if(c=='5') state_bt=7;
else state_bt=1;
break;
case 7:
if(c=='6') state_bt=8;
else state_bt=1;
break;
case 8:
if(c=='#') state_bt=9;
else state_bt=1;
break;
case 9:
lcd.init();
lcd.backlight();
lcd.print("OPEN");
digitalWrite(6,LOW);
delay(5000);
digitalWrite(6,HIGH);
lcd.init();
lcd.backlight();
lcd.print("BLOCKED");
state_bt=1;
break;
default:
break;
}
}
switch(stare){
case 0: {
mfrc522.PCD_Init();
if ( mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial() ){
citireNFC();
if(validareNFC())
{stare=1;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("VALID NFC CODE");
delay(1000);
return;
}
else{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("INVALID NFC CODE");
delay(1000);
lcd.setCursor(0,0);
lcd.clear();
lcd.print("BLOCKED");
return;
}
}
char c=myKeypad.getKey();
if(c != NO_KEY){
String codcurent=iaCOD(c);
int A=comparareCOD(codcurent);
if(A==0)
{lcd.clear();
lcd.print("INVALID CODE");
delay(2000);
lcd.setCursor(0,0);
lcd.clear();
lcd.print("BLOCKED");
return;}
if(A==1)
{lcd.setCursor(0,0);
lcd.clear();
lcd.print("VALID CODE");
delay(2000);
stare = 1;
return;}
if(A==2);
{stare=2;
lcd.clear();
lcd.setCursor(0,0);
lcd.print("Pairing...");
delay(2000);
return;}
} break;
}
case 1:{
lcd.clear();
lcd.setCursor(0,0);
lcd.print("UNLOCKED");
digitalWrite(A3,LOW);
digitalWrite(A1,LOW);
digitalWrite(A2,HIGH);
//tone(6,3000,5010);
digitalWrite(6,LOW);
delay(5000);
digitalWrite(6,HIGH);
digitalWrite(A3,HIGH);
digitalWrite(A1,HIGH);
digitalWrite(A2,LOW);
stare=0;
lcd.setCursor(0,0);
lcd.clear();
lcd.print("BLOCKED");
return;
}
case 2:{
mfrc522.PCD_Init();
if ( mfrc522.PICC_IsNewCardPresent() && mfrc522.PICC_ReadCardSerial() ){
citireNFC();
pairNFC();
stare=0;
delay(2000);
lcd.clear();
lcd.setCursor(0,0);
lcd.print("BLOCKED"); }
break;
}
}
}
步骤3:应用程序
在此步骤中,我们将为系统创建应用程序。我使用的是MIT App Inventor 2程序。这个程序很简单,不需要很高的编程技能,可以在此处下载:MIT AppInventor。为了打开该应用程序,您需要:
- 下载应用程序
- 用您的Google账号进行登录
- 开始一个新的项目
在安装了程序之后,您会找到如下的一个图形界面:
为了对您的程序进行测试,您需要在手机上从Google Play下载“MIT AI2”应用程序。这是一个免费的应用程序,专门用于将网络应用程序同步到智能手机上。
如果您想要在手机上查看应用程序的屏幕,需要点击Connect → AI Companion。
点击之后,该应用程序将为您提供两种操作方式:
- 您可以扫描条形码(在您的设备上启动MIT AI2 Companion,然后扫描条形码或键入用于连接的代码,以对您的应用程序进行实时测试)
- 在您的应用程序中输入6个字母的代码
我将使用第二种方法,将电脑产生的6个字母的代码输入到手机应用程序中。
我选择这个应用程序是因为它易于使用;您无需具备编程技能即可实现如上所述的简单代码。您只需要构思一下应用程序的外观,在该应用程序中进行外观设置也非常简单。
我们先从用户界面开始:
您可以在屏幕上添加应用程序的任何元素(按钮,图像,文本框,标签)。在右侧菜单中,您可以选择想要从屏幕上获取的特征(水平对齐,应用名称,标题)。如果您想要背景图像,可以从背景图像(Background Image)菜单中进行选择。在该应用程序中,我选择在蓝牙和手机之间的连接打开时显示绿色背景,在连接关闭时显示红色背景。
该应用程序中屏幕上的三个要素:
传感器组件 & 用户界面组件 – MIT App Inventor
- 1.Bluetooth Client1 – 检测蓝牙设备的组件。该系统中返回以下内容的函数:
- 配对的蓝牙设备的地址和名称 – AddressesAndNames
- 设备上的蓝牙是否可用 – 可用(Available)
- 是否启用蓝牙 – 是(Enabled)
- 2.Clock1 – 不可见的组件,通过手机上的内置时钟提供即时时间。它可以按照设置的时间间隔定期地触发计时器,并执行时间的计算、操作和转换。
- 3.Notifier1 – 通知器组件显示警报对话框、消息和临时警报,并通过以下方式创建Android日志条目:
- ShowMessageDialog: 显示一条用户必须通过按下按钮才能关闭的消息。
- LogError: 将错误消息记录到Android日志中。
- LogInfo: 将信息消息记录到Android日志中。
如果未连接蓝牙,您会收到一个错误信息,该错误信息会显示在连接状态(Link Status)部分中。我在没有连接设备的情况下输入了打开门的密码,应用程序提醒我设备没有打开。这是一个很好的通知器,因为您有可能会忘记进行设备的配对,这样就无法把门打开。
该应用程序有三个按钮:
- 1. 连接:该按钮被启动时,将会显示一个文本列表供用户选择。此按钮相当于蓝牙查找器。如果您使用该应用时附近开启的蓝牙设备多余一个,则这些蓝牙将会显示在搜索框中。我的附近只有一个打开的蓝牙,即HC-05模块。
- 2. 断开连接:此按钮将会把应用程序与手机之间的连接断开。点击按钮后,背景色变为红色。
- 3. 发送文本:仅在建立连接时有效。程序通过蓝牙的“SendText”函数发送文本数据。在状态部分将显示消息“消息已发送”(Message Sent)。
应用程序是如何工作的 – Pevest App Inventor 2: 学习编码
“当输入Clock1.Timer指令时,该应用程序会检查蓝牙是否打开。变量 ByteAvailable 和 CommandByte将被初始化设置为0,以用于传输。如果获取到了有关数据,则使用ReceiveSigned1ByteNumber读取传入数据的第一个字节。这是指令字节。根据指令的值,将采用合适的蓝牙模块中的方法(method)来读取下一个字节。”
结论
该项目由于需要使用大量的外围设备之间的通信而具有一定的挑战性,因此我花了一些时间来寻找连接所有组件的理想方式。它帮助我了解了制造复杂设备的过程中可能发生的一些错误,以及避免这些错误的方法。