*可断电保存(使用内置EEPROM)
*可蓝牙和硬件开门,改密码
*可以找回密码
原创内容----转载请说明。
源代码文件:
1.个人gitHub: GitHub
2.文章结尾
*源代码可以直接编译通过.
使用说明:
一,关于密码
(1).默认密码和密码存储失败:
(默认)密码长度为5时:01234
ps:可以通过修改源代码更改默认密码和密码长度。
(2).串口/键盘输入密码有时间限制[应该是我忘记了,来自作者2022-8-2注]
ps:反正可以修改源代码,等我有空把这方面优化下,并重新发布个89C系列的(如stc89c51),毕竟价格更便宜点。
(3).关于密码
密码其实硬说有二个:
一个是保存的临时(在寄存器中),另一个是EEPROM内的。
情况一:修改密码只要没有提示:“错误,请重新输入”话会把寄存器旧密码替换成新输入的,只要没有提示“密码保存失败”就会擦写EEPROM(把旧替换新密码)
情况二:不断电下会使用临时(在寄存器中)的密码,断电重新上电情况下会第一步读入EEPROM内保存的密码。 注:修改密码无错误下会把密码写入EEPROM中(见情况一)。
二,使用(1)接键说明:
先看按键布局:
1 2 3
4 5 6
7 8 9
* 0 #
①功能键:
*键为返回键 #键为删除键
(2)蓝牙说明:
使用手机,电脑连接蓝牙用软件发送 1为开门,输入2为改密码。
每次提醒输入时只有大概4秒,超过为失败。
注:蓝牙操作时硬件输入密码不可用.
3.密码找回:
用USB连接上单片机,串口选择9800,随便输入一个值可以打印密码 .
4.其它说明:
默认开机打印密码和EEPROM内密码串口打印两串:'xxxxx<'(根据长度)
EEPROM内密码和临时密码(在寄存器中),'x'为数字
区别是
第一个为开机读取EEPROM内的密码,
第二个为临时的密码,保存在寄存器中的。
注意,全文临时密码虽然说是临时但是学过的应该都知道该临时是整个“系统”(程序)上电时的密码,而EEPROM只是为了断电/上电保存/读入临时密码而已。
材料(只是主要的):
名称 | 个数 |
Arduino UNO | 1 |
LCD1602A--IIC(要带IIC转接板) | 1 |
矩形按键3*4 (也可以自制) | 1 |
继电器模块 (也可以自制 Q8550+IN4148) | 1 |
任意V电磁锁 | 1 |
5mm或3mm led | 2 |
杜邦线 | n |
接口定义
名称 | 接口 单片机 硬件 |
备注 |
蓝牙 | 10------->TX 11<------RX A0------->STATE VCC---+5V GND----GND |
SoftwareSerial.h 原0(RX)口无法正常通讯 |
LCD1602AIIC | SDA---SDA SCL---SCL VCC---+5V GND---GND |
void |
矩形按键3*4 | 2, 3, 4, 5 //行 6, 7, 8 //列 |
void |
达成启动 (继电器) |
9---->IN | |
错误led | 13------->+ | 严重错误(密码输入错误等) |
Passwordlock.h | 自定义 |
Keypad.h | 标 |
Wire.h | 标 |
LiquidCrystal_I2C.h | 标 |
SoftwareSerial.h | 标 |
EEPROM.h |
标 |
如果无法下载所使用的标库文件请留言或联系我.
使用的是多文件编写,主要使用的是C(不包括库文件{头文件}),现在只是写完,但为完善
例如:
下次添加(可选):
1.开门一直不关检测 2.NFC开门(刷卡) 3.WLAN(无线,互联网,远程)开门
4.可找回密码(需要USB连入电脑/手机)并且要管理员密码.....
5.增加指纹识别
√想办法进行断电保存...
不多说了上代码:
Passwordlock.ino
#include"Passwordlock.h"
#define ROWS 4 //行
#define COLS 3 //列
//辅助脚位定义
#define reach 9 //达成启动-脚位
#define led_errors 13 //警告-错误-脚位
#define buzzer_errors 12 //蜂鸣器
#define BT_testing 0 //蓝牙检测 A0
#define reac_time 3000 //达成启动-时间
bool Hardware_password(); //硬件密码处理
bool Hardware_pd_Acquisition();//硬件密码获取
char Serial_port();//串口接收到信息 并返回
void Strand_door(); //串口开门
void Error_lnterrupt(byte );//错误中断
//按键接口定义
byte rowePins[ROWS] = {2, 3, 4, 5}; //行
byte colPins[COLS] = {6, 7, 8}; //列
//定义矩形按键,键位值
char Keys[ROWS][COLS] = {
{'1', '2', '3'},
{'4', '5', '6'},
{'7', '8', '9'},
{'*', '0', '#'}
};
//用户密码(+串口)-用于保存
char User_Password[Password_Max];
char Temporary_password[Password_Max]; //临时串口,硬件密码 用于比较
byte ergodic=10; //遍历 10-每次只让loop当中if执行一遍,否则重复打印
short Number_Errors=0; //错误次数
//定义rx,tx蓝牙接口
SoftwareSerial BT(10,11); //10-TX 11-RX
//设置LCD1602设备地址,具体看模块手册
LiquidCrystal_I2C lcd(0x27, 16, 2);
//设置矩形键盘
Keypad keypad = Keypad(makeKeymap(Keys), rowePins, colPins, ROWS, COLS);
void setup() //在main ()中调用
{
pinMode(buzzer_errors, OUTPUT);
pinMode(led_errors, OUTPUT);
pinMode(reach, OUTPUT); //达成
digitalWrite(reach, HIGH);
Serial.begin(9600);
BT.begin(38400);
lcd.init(); // 初始化LCD
lcd.backlight(); //设置LCD背景等亮
//密码长度
if(!IF_MAX())
while(1)
Error_lnterrupt(0);
if(!EEPROM_ReadIn(User_Password))
{
if(!Initial_Password(User_Password)) //初始密码
while(1)
Error_lnterrupt(1);
}
}
void loop()//在main ()中for( ; ; ;)调用
{
char auxiliary; //辅助键使用(决定程序走向)
//每次只让执行一遍,否则重复打印
if (Serial.available())
{
while (Serial.available())
{ // 当串口还有信息后
Serial.read();
}//清空缓冲区+密码错误
Serial.println();
for(ergodic=0;ergodic<Password_Max;ergodic++)
Serial.print(User_Password[ergodic]);
}
lcd.clear(); // 清屏
lcd.setCursor(4, 0); //设置显示指针
lcd.print("Welcome");
lcd.setCursor(1, 1);
lcd.print("# Key start");
Strand_door();//串口开门
auxiliary = keypad.getKey();
delay(5);
if ((auxiliary != NO_KEY) && auxiliary == '#')
{
Unequal:
auxiliary =NO_KEY;
//开始
lcd.clear(); // 清屏
lcd.setCursor(0, 0);
lcd.print("# key in pd"); //输入密码
lcd.setCursor(0, 1);
lcd.print("* key ce pd"); //改密码
while(1)
{
auxiliary = keypad.getKey();
delay(5);
if ((auxiliary != NO_KEY)&&(auxiliary == '#'||auxiliary == '*'))
{
lcd.clear(); // 清屏
lcd.setCursor(0, 0);
lcd.print("pd:");
// delay(3000);
if(auxiliary == '#')
{
//
if(Hardware_password())
{
lcd.clear(); // 清屏
lcd.setCursor(0, 0);
Number_Errors=0;
break; //密码相等
}
else
goto Unequal;//密码不相等
}
else if(auxiliary == '*')
{
if(Hardware_password())
{
//密码相等
lcd.clear(); // 清屏
lcd.setCursor(0, 0);
lcd.print("IN New pd:");
Hardware_pd_Acquisition(); //硬件密码获取
lcd.clear(); // 清屏
lcd.setCursor(0, 0);
if(Temporary_password[0]!='Q')
{
//
for (ergodic = 0;(ergodic < Password_Max);)
{
User_Password[ergodic]=Temporary_password[ergodic];
++ergodic;
}
//保存密码
if(!EEPROM_WriteIn(User_Password))
{
// 密码保存失败进行-初始密码
if(!Initial_Password( User_Password))
while(1)
Error_lnterrupt(1);
}
lcd.print("saved");
}
else
lcd.print("NO!");
delay(2000);
break; //密码相等
}
else
goto Unequal;//密码不相等
}
}
}//
}
delay(100);
}
bool Hardware_password() //硬件密码处理
{
//硬件密码获取
while(1)
{
if(Hardware_pd_Acquisition()!=false)
{
//密码比较 临时,用户
if (Password_Comparison(Temporary_password, User_Password))
{ //相等
lcd.clear(); // 清屏
lcd.setCursor(0, 0);
lcd.print("Yes");
digitalWrite(reach, LOW);
delay(reac_time);
digitalWrite(reach, HIGH);
return true;
}
else
{
++Number_Errors;
lcd.clear(); // 清屏
lcd.setCursor(0, 0);
lcd.print("NO,Password");
if(Number_Errors)
{
digitalWrite(led_errors, HIGH);
delay(2500);
if(Number_Errors>=3)
{
lcd.setCursor(0, 1);
lcd.print("Half a minute");
delay(30000);
}
digitalWrite(led_errors, LOW);
}
return false;
}//else
}
else
return false;
}
}
//硬件密码获取
bool Hardware_pd_Acquisition()
{
//获得按键值
for (ergodic = 0; ergodic < Password_Max;)
{
Temporary_password[ergodic] = keypad.getKey();
delay(5);
if ((Temporary_password[ergodic]!= NO_KEY)&&Temporary_password[ergodic]!='#')
{
if(Temporary_password[ergodic] == '*')
{
Temporary_password[0]='Q';
return false;
}
lcd.setCursor(ergodic, 1);
lcd.print("*");
++ergodic;
}
else if(Temporary_password[ergodic] == '#')//删除
{
if(ergodic)
{
Temporary_password[ergodic--]==NO_KEY;
Temporary_password[ergodic]==NO_KEY;
lcd.setCursor(ergodic, 1);
lcd.print(' ');
}
}
}
return true;
}
//串口接收到信息 并返回
char Serial_port()
{
char serialData;
//等待缓冲区拿到值
delay(10);
if (BT.available())
{ // 当串口接收到信息后
serialData = BT.read();
}
return serialData;
}
//串口开门
void Strand_door()
{
char option='.';
while((analogRead(BT_testing))>300)
{
lcd.clear(); // 清屏
lcd.setCursor(0, 0);
lcd.print("Serial input");
delay(5);
option=Serial_port(); //获取 1-开门 2-改密码
if (option== '1'||option=='2') //1-开门
{
while (BT.available())
{ // 当串口还有信息后
BT.read();
}//主要清空缓冲区
//请输入密码
BT.println("In password:");
//获取
delay(4500); //4.5s等待用户输入
for (ergodic = 0; ergodic < Password_Max; ergodic++)
Temporary_password[ergodic] = Serial_port();
delay(5);
while(BT.available())
{ // 当串口还有信息后
BT.read();
Temporary_password[Password_Max]='Q';
}//清空缓冲区+密码错误
if(!BtSrialPort_data_processing(Temporary_password))
break;
//比较
if(Password_Comparison(Temporary_password,User_Password))
{
//相等
BT.println("Yes");
//相等
lcd.clear(); // 清屏
lcd.setCursor(0, 0);
lcd.print("Yes");
if(option=='1'&&option!='2')
{
//开门
digitalWrite(reach, LOW);
delay(reac_time);
digitalWrite(reach, HIGH);
return ; //完成返回
}
else{
//改密码
BT.println("In new pd:");
//获取
delay(4500); //4.5s等待用户输入
for (ergodic = 0; ergodic < Password_Max; ergodic++)
Temporary_password[ergodic] = Serial_port();
delay(5);
while (BT.available())
{ // 当串口还有信息后
BT.read();
Temporary_password[Password_Max]='Q';
}//清空缓冲区+密码错误
if((BtSrialPort_data_processing(Temporary_password))==false)
break;
for (ergodic = 0;ergodic < Password_Max;)
{
User_Password[ergodic]=Temporary_password[ergodic];
++ergodic;
}
//保存密码
if(!EEPROM_WriteIn(User_Password))
{
// 密码保存失败进行-初始密码
if(!Initial_Password( User_Password))
while(1)
Error_lnterrupt(1);
}
BT.println("New pd:");
//打印密码
for (ergodic = 0; ergodic < Password_Max; ergodic++)
BT.print(User_Password[ergodic]);
BT.println();
delay(5);
return ; //完成返回
}
}
else
break;//密码不相等
}
else
continue;
}
if(option!='.'||Temporary_password[Password_Max]=='Q')
{
BT.println("Pd error");
while (BT.available())
{ // 当串口还有信息后
BT.read();
}//清空缓冲区+密码错误
return ;//完成返回
}
delay(10);
}//Strand_door()
void Error_lnterrupt(byte errs)//错误中断
{
delay(8000);
lcd.clear(); // 清屏
lcd.setCursor(0, 0);
lcd.print("error code:");
lcd.println(errs);
switch (errs)
{
case 0: //密码过长
case 1: //密码初始化失败
default:
while (1)
{
digitalWrite(led_errors, HIGH);
delay(300);
digitalWrite(led_errors, LOW);
}
break;
}
}
Otherfunctions.cpp:
#include"Passwordlock.h"
//对比
int IF_MAX()
{
return (Password_Max <= Passwordlock);
}
//初始密码
bool Initial_Password(char *password)
{
byte ergodic;
char inits='0';
for (ergodic = 0; ergodic < Password_Max;ergodic++)
password[ergodic] =inits++;
if (ergodic >= Password_Max)
return true;
else
return false;
}
//密码比较 临时密码,用户密码 strcmp();
bool Password_Comparison(const char *Temporary_pd, const char *User_Pd)
{
byte temporary, db;
for (temporary = 0,db = 1; temporary < Password_Max; )
{
db *= (User_Pd[temporary] == Temporary_pd[temporary]);
++temporary;
}
if (db)
return true;
else
return false;
}
//密码更改 临时密码,用户密码
bool Password_Change (char *Temporary_pd , char *use_pdn)
{
if (Password_Comparison(Temporary_pd, use_pdn))
return true;
byte temporary ;
for (temporary = 0; temporary < Password_Max; )
{
use_pdn[temporary] = Temporary_pd[temporary];
++temporary;
}
if (Password_Comparison(Temporary_pd, use_pdn))
return true;
else
{
for (temporary = 0; temporary < Password_Max; )
{
use_pdn[temporary] = temporary;
++temporary;
}
return false;
}
}
//串口数据处理
bool BtSrialPort_data_processing(char* Temporary)
{
byte ergodic,number,option;
for (option=ergodic = 0; ergodic < Password_Max; ergodic++)
for(number=48;number<=57;number++)
option+=(number==(int)Temporary[ergodic]);
if(option>=Password_Max)
return true;
else
return false;
}
bool EEPROM_WriteIn(char* InPassword) //写入EEPROM
{
byte ergodic;
for(ergodic=0;ergodic<Password_Max;ergodic++)
{
EEPROM.write(ergodic,(int)InPassword[ergodic]);
delay(10);
}
return BtSrialPort_data_processing(InPassword);
}
bool EEPROM_ReadIn(char* InPassword) //读入EEPROM
{
byte ergodic;
for(ergodic=0;ergodic<Password_Max;ergodic++)
{
InPassword[ergodic]=(char)EEPROM.read(ergodic);
delay(10);
}
for(ergodic=0;ergodic<Password_Max;ergodic++)
Serial.print(InPassword[ergodic]);
Serial.println();
return BtSrialPort_data_processing(InPassword);
}
Passwordlock.h:
#ifndef Passwordlock//防止重复包含
#define Passwordlock 15 //限制密码的长度
#include<Keypad.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h> //引用I2C库
#include <SoftwareSerial.h>
#include <EEPROM.h>
#define Password_Max 5 //密码长度
//对比 #define IF_MAX(...)(__VA_ARGS__<=Passwordlock);
int IF_MAX();
//初始密码
bool Initial_Password(char *);
//密码比较 临时密码,用户密码 strcmp();
bool Password_Comparison(const char *,const char *);
//密码更改 临时密码,用户密码
bool Password_Change(char *,char *);
void Failure_function(); //失效函数
void Strand_door(); //串口
char Serial_port();//串口接收到信息 并返回
bool BtSrialPort_data_processing(char* Temporary); //串口数据处理
bool EEPROM_WriteIn(char* InPassword); //写入EEPROM
bool EEPROM_ReadIn(char* InPassword); //读入EEPROM
#endif
本人是自学(而且专业还不是这方面QAQ).原创内容--转载请务必说明!!
说实话还挺简单的,比贪吃蛇更简单(*´゚∀゚`)ノ 就是蓝牙RX(0)通讯耗了时间,还有Arduino IDE 真的不好用,个人.用的是vscode.
后续会发Windows cmd下贪吃蛇,连点器,俄罗斯方块……
个人邮箱:[email protected]
有BUG反馈.
原创内容--转载请务必说明!
更新时间:
2021年10月9日18:06
更新 串口与按键同密码,自定义库添加防止重复包含,检测密码长度是否超上限.
在此更新开始 动态内存不够用了.23:18更新 修复BUG
2021年10月10日17:22
更新 优化Passwordlock.ino,如果各位动态内存不够用可以删除很多else(如->获取失败,初始化失败.....)
2021年10月17日23:58
优化代码(重写),减少动态内存
2021年10月19日11:27
增加:可断电保存,优化代码,减少动态内存使用(简写字符串),15:59优化蓝牙代码