Arduino密码电子锁,可远程,可断电保存(蓝牙)HC05+LCD1602+IIC

*可断电保存(使用内置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优化蓝牙代码

猜你喜欢

转载自blog.csdn.net/asd2387/article/details/120661189