Arduino 4×4薄膜鍵盤模組實驗(三):結合LCD顯示器的密碼輸入介面

本文將結合4×4薄膜按鍵以及文字型LCD顯示器,製作一個密碼輸入介面。程式一開始在LCD顯示兩列文字,第0列始終顯示“Knock, knock…”(敲、敲…)。第1列提示用戶輸入密碼(pin number):

若按下’*’鍵,可清除輸入的字元,重新輸入;按下’#’鍵,代表「確認」輸入。

按下”#”號之後,若密碼正確,則在第1列顯示“Welcome home!”(歡迎回家!):

若密碼錯誤,則在第1列顯示“***WRONG!!***”(錯誤!!):

I2C介面的LCD顯示器模組以及實驗材料

本單元的實驗材料如下:

  • Arduino Uno控制板,一片。
  • 4×4薄膜鍵盤,一個。
  • 1602文字型LCD顯示器模組,一個。
  • 自製LCD模組的序列轉接板或現成的I2C介面轉接板,一個。

文字型LCD顯示器模組,請依《超圖解Arduino互動設計入門》第九章「串列連接LCD顯示模組」一節說明,配合74LS164組裝好,或者,購買I2C介面的Hitachi HD44780U 1602 LCD點陣液晶模組,底下是I2C介面的轉接板和1602 LCD模組的外觀,轉接板和LCD模組上的排針和排插可能要自行焊接:

Hitachi HD44780U 1602 LCD點陣液晶模組

I2C介面的顯示器轉接板,都是依照LCD模組的接腳設計,兩者可直接焊在一起。結合LCD顯示器和I2C介面模組的模樣:

Hitachi HD44780U 1602 LCD點陣液晶模組

實驗電路接線

請依照下圖連接薄膜鍵盤和LCD序列顯示模組,即便是採用74LS164組裝的LCD模組的資料和時脈線,也能接在類比輸入埠,因為Arduino的類比輸入埠兼具數位輸出∕入功能

連接薄膜鍵盤和LCD序列顯示模組

實驗程式:清除LCD顯示字元

本單元程式需要補充說明的,應該只有清除顯示字元部份。清除顯示字元的方法,就是用「空白字元」蓋掉原本的字元。

用戶按下“*”鍵,程式需要從第1列第4行到第15行,填入空白字元:

清除LCD顯示字元

在顯示代表「歡迎回家」或者「錯誤!」的訊息之前,則需要清除整個第1列。筆者把清除文字的程式寫成名叫clearRow()的自訂函式,它將接收一個參數,代表從第1列的第n個位置開始清除。

void clearRow(byte n) {
  byte last = 16 - n;  // 計算需要清除的字數
  lcd.setCursor(n, 1); // 移動到第2行,"PIN:"之後

  for (byte i = 0; i < last; i++) {
    lcd.print(" ");
  }
  lcd.setCursor(n, 1); // 移回第2行,"PIN:"之後
}

執行 clearRow( 4 ),代表從第4個字元開始清除,函式裡的last值將是12,也就是從第4行開始,填入12個空白字元。

使用74LS164電路的LCD序列顯示器的完整程式碼如下,程式庫沿用書本第九章採行的LiquidCrystal_SR程式庫。

#include <Keypad.h>
#include <Wire.h>
#include <LiquidCrystal_SR.h>  // 引用LCD序列顯示器的程式庫

#define KEY_ROWS 4  // 薄膜按鍵的列數
#define KEY_COLS 4   // 薄膜按鍵的行數
#define LCD_ROWS 2  // LCD顯示器的列數
#define LCD_COLS 16  // LCD顯示器的行數

// 建立LCD顯示器物件:lcd(資料腳, 時脈腳, TWO_WIRE);
LiquidCrystal_SR lcd(A4,A5,TWO_WIRE);

// 設置按鍵模組
char keymap[KEY_ROWS][KEY_COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte rowPins[KEY_ROWS] = {13, 12, 11, 10}; //Rows 0 to 3
byte colPins[KEY_COLS] = {9, 8, 7, 6}; //Columns 0 to 3

Keypad keypad = Keypad(makeKeymap(keymap), rowPins, colPins, KEY_ROWS, KEY_COLS);

String passcode = "4321";   // 預設密碼
String inputCode = "";      // 暫存用戶的按鍵字串
bool acceptKey = true;      // 代表是否接受用戶按鍵輸入的變數,預設為「接受」

void clearRow(byte n) {
  byte last = LCD_COLS - n;
  lcd.setCursor(n, 1); // 移動到第2行,"PIN:"之後

  for (byte i = 0; i < last; i++) {
    lcd.print(" ");
  }
  lcd.setCursor(n, 1); // 移回第2行,"PIN:"之後
}

// 顯示「歡迎光臨」後,重設LCD顯示文字和輸入狀態。
void resetLocker() {
  lcd.clear();
  lcd.print("Knock, knock...");
  lcd.setCursor(0, 1);  // 切換到第2行
  lcd.print("PIN:");
  lcd.cursor();

  acceptKey = true;
  inputCode = "";
}

// 比對用戶輸入的密碼
void checkPinCode() {
  acceptKey = false;  // 暫時不接受用戶按鍵輸入
  clearRow(0);       // 從第0個字元開始清除LCD畫面
  lcd.noCursor();
  lcd.setCursor(0, 1);  // 切換到第2行
  // 比對密碼
  if (inputCode == passcode) {
    lcd.print("Welcome home!");
  } else {
    lcd.print("***WRONG!!***");
  }
  delay(3000);
  resetLocker();     // 重設LCD顯示文字和輸入狀態
}

void setup() {
  Serial.begin(9600);

  lcd.begin(16,2);   // 初始化lcd物件

  resetLocker();
}

void loop() {
  char key = keypad.getKey();

  // 若目前接受用戶輸入,而且有新的字元輸入…
  if (acceptKey && key != NO_KEY) {
if (key == '*') {   // 清除畫面
      clearRow(4);  // 從第4個字元開始清除
      inputCode = "";
    } else if (key == '#') {  // 比對輸入密碼
      checkPinCode();
    } else {
      inputCode += key;  // 儲存用戶的按鍵字元
      lcd.print('*');
    }
  }
}

採用I2C介面的LCD顯示器程式

本單元程式採用的是這個I2C LCD顯示器程式庫,請注意,依I2C介面轉接板上的晶片或者韌體不同,驅動程式和I2C的位址可能不同,常見的位址有0x27, 0x38和0x3F,購買時請向商家確認。如果執行本單元的程式之後,LCD沒有顯示任何文字,請嘗試修改I2C位址,若是出現亂碼或者閃爍情況,請更換程式庫。

底下提供其他兩個具備I2C介面的1602 LCD程式庫當作參考:

不同程式庫的指令語法可能不一樣,請自行參閱程式庫的範例和說明。以本單元的程式庫為例,建立LCD物件的語法是:

LiquidCrystal_I2C lcd(0x27, 16, 2);  // 設定模組位址0x27,以及16行, 2列的顯示形式

並且經由底下的敘述初始化,以及選擇性地開啟背光:

lcd.init();       // 初始化lcd物件
lcd.backlight();  // 開啟背光

完整的程式碼如下:

#include <Keypad.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>  // 引用I2C序列顯示器的程式庫

#define KEY_ROWS 4  // 薄膜按鍵的列數
#define KEY_COLS 4  // 薄膜按鍵的行數
#define LCD_ROWS 2  // LCD顯示器的列數
#define LCD_COLS 16 // LCD顯示器的行數

// 設置按鍵模組
char keymap[KEY_ROWS][KEY_COLS] = {
  {'1', '2', '3', 'A'},
  {'4', '5', '6', 'B'},
  {'7', '8', '9', 'C'},
  {'*', '0', '#', 'D'}
};

byte rowPins[KEY_ROWS] = {13, 12, 11, 10};
byte colPins[KEY_COLS] = {9, 8, 7, 6};

Keypad keypad = Keypad(makeKeymap(keymap), rowPins, colPins, KEY_ROWS, KEY_COLS);

String passcode = "4321";   // 預設密碼
String inputCode = "";      // 暫存用戶的按鍵字串
bool acceptKey = true;      // 代表是否接受用戶按鍵輸入的變數,預設為「接受」

// LCD顯示器
LiquidCrystal_I2C lcd(0x27, 16, 2);  // 設定模組位址0x27,以及16行, 2列的顯示形式

void clearRow(byte n) {
  byte last = LCD_COLS - n;
  lcd.setCursor(n, 1); // 移動到第2行,"PIN:"之後

  for (byte i = 0; i < last; i++) {
    lcd.print(" ");
  }
  lcd.setCursor(n, 1);
}

// 顯示「歡迎光臨」後,重設LCD顯示文字和輸入狀態。
void resetLocker() {
  lcd.clear();
  lcd.print("Knock, knock...");
  lcd.setCursor(0, 1);  // 切換到第2行
  lcd.print("PIN:");
  lcd.cursor();

  acceptKey = true;
  inputCode = "";
}

// 比對用戶輸入的密碼
void checkPinCode() {
  acceptKey = false;  // 暫時不接受用戶按鍵輸入
  clearRow(0);        // 從第0個字元開始清除LCD畫面
  lcd.noCursor();
  lcd.setCursor(0, 1);  // 切換到第2行
  // 比對密碼
  if (inputCode == passcode) {
    lcd.print("Welcome home!");
  } else {
    lcd.print("***WRONG!!***");
  }
  delay(3000);
  resetLocker();     // 重設LCD顯示文字和輸入狀態
}

void setup() {
  Serial.begin(9600);

  lcd.init();       // 初始化lcd物件
  lcd.backlight();  // 開啟背光

  resetLocker();
}

void loop() {
  char key = keypad.getKey();

  // 若目前接受用戶輸入,而且有新的字元輸入…
  if (acceptKey && key != NO_KEY) {
    if (key == '*') {   // 清除畫面
      clearRow(4);  // 從第4個字元開始清除
      inputCode = "";
    } else if (key == '#') {  // 比對輸入密碼
      checkPinCode();
    } else {
      inputCode += key;  // 儲存用戶的按鍵字元
      lcd.print('*');
    }
  }
}

未完,待續…

延伸閱讀

Posts created 467

43 thoughts on “Arduino 4×4薄膜鍵盤模組實驗(三):結合LCD顯示器的密碼輸入介面

  1. 老師:
    請問我用144*32的LCD顯示字元正確,但是一直無法顯示下一行,而用16*2的LCD可換下一行,但是字元都是亂碼?
    我是用74LS164IC的電路.

    #include
    #include // 引用LCD序列顯示器的程式庫
    LiquidCrystal_SR lcd(8,7,TWO_WIRE);
    void setup() {

    lcd.begin(16,2); // 初始化lcd物件

    lcd.home();

    lcd.write(“kiss me”);
    lcd.setCursor(0, 1); // 切換到第2行
    lcd.print(“PIN:”);
    lcd.cursor();
    }
    void loop(){
    }

  2. 使用原作者的範例用144*32的中文LCD,會出現一個笑臉圖左右移動,但是用16*2的LCD則是亂碼?

    1. hi dull-boy:

      因為144×32顯示器是「繪圖型LCD」,它採用的晶片和文字型LCD完全不同,所以程式庫不一樣,程式指令也可能不同。

      thanks,
      jeffrey

  3. 可是我用16*2的LCD會出現亂碼,原作者的函式庫3個版本我都用了,都是亂碼…
    只有144*32的LCD搭配最早版本的函式庫,能正常顯示字元,但是無法出現第2行的字串..

  4. 那104電容我覺得有接沒接都一樣,都試過了,並接在ic電源接腳,就同等並接在整個電路的電源,,兩個LCD我用並列傳輸的方式,都能正常顯示喔

    1. 這樣的話,除了74LS164的電路有問題,我也不知道原因…我測試是正常的。

      thanks,
      jeffrey

  5. 老師我想問一下
    如果密碼輸入正確,跳到下一段可不可以在設定新的一組密碼呢?

  6. 老師抱歉再問你一下
    那如果使用A來設定新的一組密碼的話,那要如何讓程式判定新的密碼?

    1. 因為密碼存在passcode變數,所以程式碼還是一樣:

      // 比對密碼
      if (inputCode == passcode) {
        lcd.print("Welcome home!");
      } else {
        lcd.print("***WRONG!!***");
      }
      

      thanks,
      jeffrey

  7. 老師不好意思,我想請問我已經寫出有關按鍵C的設定可是要如何核對舊的1234這組密碼如果核對正確再輸入新的密碼,我現在卡在這個問題點試過很多方法都會直接跳到上一段輸入正確的地方,拜託老師幫我指點了,謝謝老師。

    1. 請先寫下你預期的程式流程,例如:

      輸入密碼
      按下 ‘C’ 鍵
      若密碼正確
      則在LCD第0列顯示 “New Password:”
       輸入新的密碼
       輸入完畢後再次按下 ‘C’ 鍵儲存

      若密碼不正確,則顯示”WRONG!!”然後返回初始狀態。

      如果是這樣的話,
      程式要判斷 ‘C’ 鍵被按了幾次,
      你可以用一個變數來紀錄按下的次數,
      假設這個變數叫做 pressC,預設為0

      用戶首先輸入密碼,接著按下C鍵,底下的條件式將被執行:

      if (pressC == 0) {
       代表C鍵第一次被按下
       比對輸入值與passcode值

       pressC ++;
      } else {
       代表第2次按下C鍵
       把輸入值存入passcode // 這就紀錄新密碼啦~

       pressC = 0; // 清除按鍵紀錄
      }

      have fun!
      jeffrey

  8. 老師,我想再問一下pressC要宣告在一開始的部分還是放在if判斷句中就好了呢?
    可是如果用pressC那不就(key==’C’)這行就不必使用了嗎?
    謝謝老師好幾次的回答小弟感激不盡。

    1. ㄟ…如果搞不清楚的話,變數宣告就放在程式開頭,至於要不要(key==’C’),就看你的程式需要,還是那句話…你試試看就知道了。

      thanks,
      jeffrey

  9. void checkPinCode() {
    clearRow(0); // 從第0個字元開始清除LCD畫面
    lcd.noCursor();
    lcd.setCursor(0, 1); // 切換到第2行
    // 比對密碼
    if (inputCode == passcode) {
    lcd.clear();
    lcd.print(” Unlocked “);
    }else if(inputCode !=passcode){
    lcd.clear();
    lcd.print(“***False!!***”);
    ccount++;
    if(ccount>=3)//如果輸入密碼錯誤達到3次立即發出警報
    {
    lcd.clear();
    lcd.print(“****ALARM!!****”);
    tone(buzzer,1000);
    delay(1000);
    }
    }
    resetLocker();
    delay(3000);
    }
    老師我想問一下如果在ALARM時候不想讓他復歸可以加上什麼呢?

  10. else if(key==’C’){
    lcd.clear();
    lcd.print(“old password:”);
    lcd.setCursor(0, 1);
    lcd.cursor();
    if (inputCode == passcode) {
    lcd.clear();
    lcd.print(“newpassword”);
    lcd.setCursor(0, 1);
    lcd.cursor();
    pressC++;
    lcd.cursor();
    inputCode = “”;
    inputCode=passcode;
    pressC==0;
    }
    resetLocker;
    delay(300);
    }
    老師是這樣存進去passcode的嗎?
    如果不是的話可以可以幫我點明該如何存進去passcode
    謝謝老師!!

    1. 大致像這樣,程式未驗證:

      byte pressC = 0;
      
      void changePinCode() {
        acceptKey = false;  // 暫時不接受用戶按鍵輸入
        // 比對舊密碼
        if (inputCode == passcode) {
          lcd.clear();
          lcd.noCursor();
          lcd.print("New Pin Code:");  // 請輸入新密碼
          
          acceptKey = true;
          inputCode = "";
        } else {
          resetLocker();     // 重設LCD顯示文字和輸入狀態    
        }
      }
      
      void loop() {
        char key = keypad.getKey();
       
        if (acceptKey && key != NO_KEY) {
          if (key == '*') {
            clearRow(4);
            inputCode = "";
          } else if (key == '#') {
            checkPinCode();
          } else if (key == 'C') {
              pressC ++;
              if (pressC == 1) {
                  changePinCode();  // 比對輸入密碼
              } else {
                  // 請自行加上其他必要措施,例如,禁止密碼空白
                  passcode = inputCode; // 儲存新密碼
                  pressC = 0;
              }
          } else {
            inputCode += key;
            lcd.print('*');
          }
        }
      }
      

      good luck!
      jeffrey

  11. 老師這是我改出來的部分程式驗證過也是可以的,也謝謝老師的教導,學生我感激不盡!!

    int pressC = 0;
    void changePinCode() {
      lcd.clear();
      lcd.noCursor();
      lcd.print("New PW:"); // 請輸入新密碼
      lcd.cursor();
      acceptKey = true;
      inputCode = "";
      pressC++; // 重設LCD顯示文字和輸入狀態
    }
    // 若目前接受用戶輸入,而且有新的字元輸入…
    if (acceptKey && key != NO_KEY) {
      if (key == '*' ) { // 清除畫面
        noTone(buzzer);
        ccount = 0;
        clearRow(9); // 從第4個字元開始清除
        inputCode = "";
      } else if (key == '#' ) { // 比對輸入密碼
        checkPinCode();
      } else if (key == 'A' ) { //進入密碼重設與鎖上介面
        lcd.clear();
        lcd.print("B.LOCK ON”);
        lcd.setCursor(0, 1);
        lcd.print("C.RESET PASSWORD”);
      } else if (key == ' D' ) { //重新復歸
        resetLocker();
      } else if (key == 'C' ) {
        lcd.clear();
        lcd.print("old PW:");
        lcd.cursor();
        if (inputCode == passcode) {
          pressC ++;
        } if (pressC == 1) {
          changePinCode(); // 比對輸入密碼
        } else if (pressC == 2) {
          passcode = inputCode; // 儲存新密碼
        } else if (pressC == 3) {
          pressC = 0;
          resetLocker();
        }
      } else {
        inputCode += key; // 儲存用戶的按鍵字元
        lcd.print(‘*' );
      }
    }
    }
    
  12. 老師我想請教您,程式上基本上沒有錯誤我把RFID(3)的程式貼上來我的密碼鎖裡面後就會顯示exit status 1
    a function-definition is not allowed here before ‘{‘ token在我的void setup()上面請問老師我該如何地去修正它?
    #include
    #include
    #include
    #include
    #include
    #include
    #include // 引用I2C序列顯示器的程式庫
    #include
    #include
    #define KEY_ROWS 4 // 薄膜按鍵的列數
    #define KEY_COLS 4 // 薄膜按鍵的行數
    #define LCD_ROWS 2 // LCD顯示器的列數
    #define LCD_COLS 16 // LCD顯示器的行數
    #define RST_PIN 13 // Reset腳
    #define SS_PIN 53 // 晶片選擇腳
    const int buzzer = 3;
    char keymap[KEY_ROWS][KEY_COLS] = { // 設置按鍵模組
    {‘1’, ‘2’, ‘3’, ‘A’},
    {‘4’, ‘5’, ‘6’, ‘B’},
    {‘7’, ‘8’, ‘9’, ‘C’},
    {‘*’, ‘0’, ‘#’, ‘D’}};
    byte rowPins[KEY_ROWS] = {12, 11, 10, 9};
    byte colPins[KEY_COLS] = {8, 7, 6, 5};
    Keypad keypad = Keypad(makeKeymap(keymap), rowPins, colPins, KEY_ROWS, KEY_COLS);
    int pressC = 0;
    int pressB = 0;
    byte ccount=0;
    unsigned long time;
    int i = 0;
    String passcode = “1234”; // 預設密碼
    String inputCode = “”; // 暫存用戶的按鍵字串
    bool acceptKey = true; //是否接受用戶按鍵輸入的變數,預設為「接受」
    // LCD顯示器
    LiquidCrystal_I2C lcd(0x27,2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);
    #define GPIO_ADDR 0x27
    void clearRow(byte n) {
    byte last = LCD_COLS – n;
    lcd.setCursor(n, 1); // 移動到第2行,”PIN:”之後
    for (byte i = 0; i =3)//如果輸入密碼錯誤達到3次立即發出警報
    {
    lcd.clear();
    lcd.print(“****ALARM!!****”);
    tone(buzzer,1000);
    delay(1000);
    }
    }
    delay(3000);
    resetLocker();
    }
    void changePinCode() {
    lcd.clear();
    lcd.noCursor();
    lcd.print(“New PW:”); // 請輸入新密碼
    lcd.cursor();
    acceptKey = true;
    inputCode = “”;
    pressC++; // 重設LCD顯示文字和輸入狀態
    }
    MFRC522 mfrc522(SS_PIN, RST_PIN); // 建立MFRC522物件

    MFRC522::MIFARE_Key key; // 儲存金鑰

    byte sector = 15; // 指定讀寫的「區段」,可能值:0~15
    byte block = 1; // 指定讀寫的「區塊」,可能值:0~3
    byte blockData[16] = “Keep Hacking!”;// 最多可存入16個字元
    // 若要清除區塊內容,請寫入16個 0
    //byte blockData[16] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

    // 暫存讀取區塊內容的陣列,MIFARE_Read()方法要求至少要18位元組空間,來存放16位元組。
    byte buffer[20];
    int ledPin = 12;

    MFRC522::StatusCode status;

    void writeBlock(byte _sector, byte _block, byte _blockData[]) {
    if (_sector 15 || _block 3) {
    // 顯示「區段或區塊碼錯誤」,然後結束函式。
    Serial.println(F(“Wrong sector or block number.”));
    return;
    }

    if (_sector == 0 && _block == 0) {
    // 顯示「第一個區塊只能讀取」,然後結束函式。
    Serial.println(F(“First block is read-only.”));
    return;
    }

    byte blockNum = _sector * 4 + _block; // 計算區塊的實際編號(0~63)
    byte trailerBlock = _sector * 4 + 3; // 控制區塊編號

    // 驗證金鑰
    status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
    // 若未通過驗證…
    if (status != MFRC522::STATUS_OK) {
    // 顯示錯誤訊息
    Serial.print(F(“PCD_Authenticate() failed: “));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
    }

    // 在指定區塊寫入16位元組資料
    status = (MFRC522::StatusCode) mfrc522.MIFARE_Write(blockNum, _blockData, 16);
    // 若寫入不成功…
    if (status != MFRC522::STATUS_OK) {
    // 顯示錯誤訊息
    Serial.print(F(“MIFARE_Write() failed: “));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
    }

    // 顯示「寫入成功!」
    Serial.println(F(“Data was written.”));
    }

    void readBlock(byte _sector, byte _block, byte _blockData[]) {
    if (_sector 15 || _block 3) {
    // 顯示「區段或區塊碼錯誤」,然後結束函式。
    Serial.println(F(“Wrong sector or block number.”));
    return;
    }

    byte blockNum = _sector * 4 + _block; // 計算區塊的實際編號(0~63)
    byte trailerBlock = _sector * 4 + 3; // 控制區塊編號

    // 驗證金鑰
    status = (MFRC522::StatusCode) mfrc522.PCD_Authenticate(MFRC522::PICC_CMD_MF_AUTH_KEY_A, trailerBlock, &key, &(mfrc522.uid));
    // 若未通過驗證…
    if (status != MFRC522::STATUS_OK) {
    // 顯示錯誤訊息
    Serial.print(F(“PCD_Authenticate() failed: “));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
    }

    byte buffersize = 18;
    status = (MFRC522::StatusCode) mfrc522.MIFARE_Read(blockNum, _blockData, &buffersize);

    // 若讀取不成功…
    if (status != MFRC522::STATUS_OK) {
    // 顯示錯誤訊息
    Serial.print(F(“MIFARE_read() failed: “));
    Serial.println(mfrc522.GetStatusCodeName(status));
    return;
    }

    // 顯示「讀取成功!」
    if(status == MFRC522::STATUS_OK){
    Serial.println(F(“Data was read.”));
    }

    void setup() {
    pinMode(buzzer,OUTPUT);
    Serial.begin(9600);
    lcd.begin(16,2);
    lcd.backlight(); // 開啟背光
    SPI.begin(); // 初始化SPI介面
    mfrc522.PCD_Init(); // 初始化MFRC522卡片
    resetLocker();
    Serial.println(F(“Please scan MIFARE Classic card…”));

    // 準備金鑰(用於key A和key B),出廠預設為6組 0xFF。
    for (byte i = 0; i < 6; i++) {
    key.keyByte[i] = 0xFF;
    }
    void loop() {
    // 查看是否感應到卡片
    if ( ! mfrc522.PICC_IsNewCardPresent()) {
    return; // 退回loop迴圈的開頭
    }
    // 選取一張卡片
    if ( ! mfrc522.PICC_ReadCardSerial()) { // 若傳回1,代表已讀取到卡片的ID
    return;
    }
    writeBlock(sector, block, blockData); // 區段編號、區塊編號、包含寫入資料的陣列
    readBlock(sector, block, buffer); // 區段編號、區塊編號、存放讀取資料的陣列
    Serial.print(F("Read block: ")); // 顯示儲存讀取資料的陣列元素值
    for (byte i = 0 ; i < 16 ; i++) {
    Serial.write (buffer[i]);
    if(i==16)
    {i=0;}
    }
    Serial.println();
    mfrc522.PICC_HaltA();
    // Stop encryption on PCD
    mfrc522.PCD_StopCrypto1();

    char key = keypad.getKey();

    // 若目前接受用戶輸入,而且有新的字元輸入…
    if (acceptKey && key != NO_KEY) {
    if (key == '*') { // 清除畫面
    clearRow(9); // 從第4個字元開始清除
    inputCode = "";
    } else if (key == '#') { // 比對輸入密碼
    checkPinCode();
    } else if(key == 'A'){ //進入密碼重設與鎖上介面
    lcd.clear();
    lcd.print("C.RESET PASSWORD");
    } else if(key == 'B'){
    if(pressB == 0){
    lcd.clear();
    lcd.print("password:");
    lcd.cursor();}
    if(inputCode == passcode){
    pressB++;
    }
    if(pressB==1){
    lcd.clear();
    lcd.print(" correct ");
    noTone(buzzer);
    ccount=0;
    }if(pressB==2){
    pressB=0;
    resetLocker();
    }
    }else if(key=='D'){ //重新復歸
    resetLocker();
    } else if (key == 'C') {
    if(pressC == 0){
    lcd.clear();
    lcd.print("old PW:");
    lcd.cursor();
    }if(inputCode == passcode){
    pressC ++;
    } if (pressC == 1) {
    changePinCode(); // 比對輸入密碼
    } else if(pressC == 2){
    passcode = inputCode; // 儲存新密碼
    } else if(pressC == 3){
    pressC =0;
    resetLocker();
    }
    }else {
    inputCode += key; // 儲存用戶的按鍵字元
    lcd.print('*');
    }
    }
    }

  13. 您好,請問若要把LCD拔除,純粹使用序列埠視窗觀看密碼是否輸入正確的話,程式要如何改?

  14. lcd.init(); 一直顯示無法編譯成功,請問這是什麼原因
    請老師您幫忙解答,謝謝您!

    1. 如果你用I2C介面的顯示器轉接板接LCD,請分別用上文提到的程式庫編譯看看,最好是直接跟模組的賣家索取程式庫。

      thanks,
      jeffrey

  15. 關於LiquidCrystal_I2C lcd(0x27,16,2);
    它說這個 no matching function for call to LiquidCrystal_I2C lcd(0x27,16,2)

    Arduino:1.8.8 (Windows Store 1.8.19.0) (Windows 10), 開發板:”Arduino/Genuino Uno”

    sketch_dec13c:29:32: error: no matching function for call to ‘LiquidCrystal_I2C::LiquidCrystal_I2C(int, int, int)

    1. 那應該是你採用的程式庫和我的不同,程式庫通常會附帶範例程式,你可以參考範例程式或者程式庫的說明頁,修改初始化LCD物件和顯示文字的程式碼。

      thanks,
      jeffrey

  16. 老師你好,你的書籍讓我受益良多,上面的教學可以讓我運用在電子鎖上面,真的感激不盡。

    目前程式測試碼在UNO上面使用一切正常

    切換到 pro mino燒入後LCD只會固定顯示

    Knock,Knock….
    PIN:*

    無法輸入密碼,也無法清除

    請問老師這是因為UNO跟mini 版子上的落差嗎?

    謝謝

  17. 老師您好,我測試與樓上李同學相同…
    程式碼在大板的UNO,MEGA2560,Leonardo R3都可以,但相同的程式碼在小板的ProMini,ProMocro,Nano V3.0 都不行,粉奇怪。
    另外請問老師有沒有WiFi(如ModMCU)相關的運用(第3版書沒有)
    謝謝。

    1. 感謝告知,也謝謝你的實驗!關於Wi-Fi部份,請在本網誌搜尋”ESP8266″關鍵字。

      thanks,
      jeffrey

  18. 老師 請問
    void clearRow(byte n) {
    byte last = LCD_COLS – n;
    lcd.setCursor(n, 1); // 移動到第2行,”PIN:”之後

    for (byte i = 0; i < last; i++) {
    lcd.print(" ");
    }
    lcd.setCursor(n, 1);
    }
    這邊輸入進去後 她會顯示 a function -definition is not allowed here before '{' token
    請問這是什麼樣的狀況??

  19. 請問一下 為什麼我4 * 3鍵盤一按下’#’按鍵,就會直接跑判斷密碼的if…else? 感覺是因為一直有在判斷密碼 所以初始值判斷到密碼不符合,就直接跑else的結果,是哪邊有錯呢~麻煩各位了?

  20. 因為我有使用到LCD,所以簡單來說就是當我按下井字鍵,LCD會直接執行輸出下面這個字樣,然後再跑void resetLocker()裡面的東西,等於說我還沒輸入密碼讓它去比對,就已經自動比對1次,但我不想要讓它一按下井字鍵就做比對的動作這樣.

    //LCD會直接執行輸出下面這個字樣
    else {
    lcd.print(“***WRONG!!***”);
    }

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *

Related Posts

Begin typing your search term above and press enter to search. Press ESC to cancel.

Back To Top