2021年9月17日 星期五

ESP32 CAMRA 二維碼(QR Code)辨識之門鎖控制 04-攝像模組之二維碼辨識

網頁最後修改時間:2021/09/17

之前在 ESP32 CAMRA 二維碼(QR Code)辨識之門鎖控制(下面簡稱 ESP32QRDoorLock) 系列的第二篇-週邊裝置的控制中,沒有談論到攝像頭的部分,主要是因為它是整個門鎖控制的主角,值得獨立寫一篇!

這一篇是 ESP32QRDoorLock 系列的第四篇:主要是說明如何使用 ESP32-CAM 辨識二維碼,比對出辨識後的二維碼,是否與預先定義的字串相符合,進而觸發相對應的處理動作。

內容有:


【(00)簡介】

本篇的目的很簡單,就是把二維碼辨識的功能,繼續加入到上一篇 x03_ESP32CamWiFiWatchdog.ino 中,並設定相對應的二維碼辨識後的狀態,做為系統工作流程的狀態轉換依據。


【(01)ESP32-CAM 二維碼辨識】

ESP32QRDoorLock 二維碼辨識的功能來自於函式庫的支援,使用者不需要深入了解二維碼辨識的方法或是撰寫相關的程式碼,只要設定好定期呼叫函式取得辨識的結果即可(這部分採用上一篇所介紹的 FreeRTOS 工作任務)。

到底要怎麼做呢?看下面的說明。


/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* (01-01)函式庫下載與安裝

函式庫的下載以及安裝教學都在下面的兩個連結中,進行下面步驟之前,請完成該函式庫的安裝。

** 函式庫下載:

** 函式庫安裝教學:


/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* (01-02)生成二維碼圖示

在網路上有很多網站提供二維碼圖示的生成服務,除了可自行尋找使用之外,要不就用我用的這個網站:QR碼產生器:免費、彩色、加LOGO (ioi.tw)

這裡要生成的二維碼圖示,主要是開門用的,它代表的文字內容是 Qh@4#I_6+。另外,也生成了文字內容 relay0.0,1 和 relay0.0,0 兩個二維碼的圖示。這幾個圖示後續會用到,請先準備好,不知道怎麼做的,看下面的說明。

打開瀏覽器輸入連結:https://qr.ioi.tw/zh/,依序進行設定:

  • QR碼內容輸入
    • 選擇QR碼內容純文字
    • 原生資料Qh@4#I_6+
  • QR碼設定
    • 檔案名稱:自行命名
    • 按下 "下載QR碼" 按鈕下載檔案

二維碼圖示還可以加上 LOGO,例如加上一行文字:

  • LOGO模式設定
    • 選擇LOGO模式文字-方塊
    • 選擇LOGO模式unlock


/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* (01-03)參考範例與說明

安裝函式庫之後,請由 Arduino IDE 選單中選擇開啟 "File/Examples/ESP32QRCodeReader/basic" 範例程式。

我對這個範例做了一些註解,並新增了 Line 10, 11 這兩行,用來輸出該工作任務所運行的核心號碼為何?

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include <Arduino.h>
#include <ESP32QRCodeReader.h>

//* 引數 CAMERA_MODEL_AI_THINKER,代表使用的是 ESP32-CAM 開發板
ESP32QRCodeReader reader(CAMERA_MODEL_AI_THINKER);

void onQrCodeTask(void *pvParameters)
{
    //* 輸出此工作任務執行的核心號碼
    Serial.print("onQrCodeTask: Executing on core ");
    Serial.println(xPortGetCoreID());

    //* 此結構用來儲存取得的二維碼辨識資料
    struct QRCodeData qrCodeData;

    while (true)
    {
        //* 取回二維碼辨識之後的資料,最多等待 100ms
        if (reader.receiveQrCode(&qrCodeData, 100))
        {
            Serial.println("Found QRCode");
            if (qrCodeData.valid)   // 如果資料有效
            {
                Serial.print("Payload: ");
                Serial.println((const char *)qrCodeData.payload);
            }
            else                    // 如果資料無效
            {
                Serial.print("Invalid: ");
                Serial.println((const char *)qrCodeData.payload);
            }
        }
        vTaskDelay(100 / portTICK_PERIOD_MS);
    }
}

void setup()
{
    Serial.begin(115200);
    Serial.println();

    //* ESP32-CAM 攝像機及其接腳初始化
    reader.setup();

    Serial.println("Setup QRCode Reader");

    // 二維碼辨識工作任務由 Core 1 負責,優先級別 5
    reader.beginOnCore(1);

    Serial.println("Begin on Core 1");

    // 設定一工作任務,一但有資料在二維碼辨識的資料在 Queue 中,就取回並顯示出辨識結果
    // 執行核心此時未知,在 onQrCodeTask 輸出執行核心號碼,優先級別 4
    xTaskCreate(onQrCodeTask, "onQrCode", 4 * 1024, NULL, 4, NULL);
}

void loop()
{
    delay(100);
}

這範例程式主要分為兩個部分:一個是函式庫初始化的部分,另一個是工作任務的建立。

完成範例程式的修改後,編譯、上傳後打開串口調試助手,底板開關一次後再按下 "打開串口" 按鈕(或按下 "打開串口" 按鈕,再按下 ESP32-CAM 開發板的 RST 按鈕)開始通訊。

輸出欄位中,紫色框住的文字部分是 onQrCodeTask 工作任務執行的核心號碼,往下則是進行二維碼辨識的結果。


/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* (01-04)提高二維碼辨識成功率與速度

從上一節的辨識二維碼的輸出可以得知,並不是每一次的辨識都會是成功的,雖然這是在測試時故意為之的,但實際做辨識時就是會這樣發生,而且辨識成功的機率還有可能會更低(應該會更低),原因是下面幾個因素所造成:

  • 二維碼圖示使用的材質
  • 二維碼圖示的清晰度
  • 二維碼圖片的大小
  • 攝像機與二維碼圖示的距離

二維碼圖示可以用列印的或是直接由手持裝置做顯示,基本來說用什麼材質沒關係,但它所呈現的清晰度一定要好,黑就是黑白就是白,主要就是要清楚,這不難理解吧!

屏除掉前面兩個因素的影響,後面的兩點,個人覺得是最至關重要影響辨識成功與否的關鍵因素!

攝像頭與二維碼之間的距離,取的是最小可辨識的距離,兩者之間的間距一定要大於這個值才行,使用之前請先搞清楚!

如下圖所示為影片中使用的二維碼圖示:一個材質為紙張,另一個為手機畫面。左邊圖示大小 62mm * 62mm,右邊圖示為 71mm * 71mm,攝像頭距離紙箱開口處 12.5mm。


【(02)參考程式碼】

參考(01-03)裡的範例,把二維碼辨識的程式碼加到 x03_ESP32CamWiFiWatchdog.ino 裡,裡面做了一些小修改,加入了辨識結果的狀態碼,和新增了一個工作任務。


/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* (02-01)監控二維碼辨識狀態

首先,在檔案的開頭處加入函式庫的標頭檔文件。

20
#include <ESP32QRCodeReader.h>

然後修改 Watchdog 逾時時間為 30 秒,不然還沒連線成功 ESP32 就會自己重置。

40
41
/*================== WATCHDOG =================*/
#define WDT_TIMEOUT     30      // 設定 Watchdog 計時器的時間

WATCHDOG 區域的下面,建立一個二維碼辨識的函式庫的全局實例。

43
44
/*================== QR CODE READER =================*/
ESP32QRCodeReader qrcodereader (CAMERA_MODEL_AI_THINKER);

lsInterrupt 區域的上方插入下面的程式碼。

此處新增了一個二維碼辨識結果的狀態全局變數(初始狀態為 OFF),和一個控制代碼(handle)。

76
77
78
79
80
81
82
83
84
85
/*================== TASK: ONQRCODE =================*/
enum onqrcode_state_t {
    OFF,
    WORKING,
    MATCH,
    UNMATCH,
    INVALID
} xOnQrCodeState = OFF;

TaskHandle_t xOnQrCodeTaskHandle;

往下,建立一個用來與二維碼辨識結果作比對的字串陣列的全局變數。現在只有第一個會用到,其他的可自發揮。

91
92
93
94
95
96
// 定義特定任務的 QR Code
// https://qr.ioi.tw/zh/
const char *pcQrCode[] {
    "Qh@4#I_6+",    // unlock
    "PfgW~!$&U"     // do nothing
};

setupOTA 函式的上方,插入下面作為工作任務的二維碼辨識資料處理的函式。

在這裡除了基本的辨識資料的輸出之外,就是設定相對應的二維碼辨識的狀態。不過,在這裡沒有使用這些狀態碼,下一篇總結的時候才會說到。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
/**========================================================================
 **                           onQrCodeTask()
 *?  監測二維碼辨識資料是否成功與否的任務
 *@param pvParameter void*  
 *========================================================================**/
void onQrCodeTask (void *pvParameters) {
    Serial.print("onQrCodeTask: Executing on core ");
    Serial.println(xPortGetCoreID());
    
    struct QRCodeData qrCodeData;

    while (true) {
        if (xOnQrCodeState == OFF) xOnQrCodeState = WORKING;

        if (qrcodereader.receiveQrCode(&qrCodeData, 100)) {
            Serial.println (F("Found QRCode"));
            if (qrCodeData.valid) {
                Serial.print (F("Payload: "));
                Serial.println ((const char*)qrCodeData.payload);
                if (strcmp((const char*)qrCodeData.payload, pcQrCode[0]) == 0) {
                    xOnQrCodeState = MATCH;
                } else {
                    xOnQrCodeState = UNMATCH;
                }
            } else {
                Serial.print (F("Invalid: "));
                Serial.println ((const char *)qrCodeData.payload);
                xOnQrCodeState = INVALID;
            }
       }
        vTaskDelay (pdMS_TO_TICKS(100));
    }
}


/*--*//**---/*///**---*-*////***--*/*///***----*///--*/*///**--*/*//**--**/*//
* (02-02)完整程式碼

整合上一小節所說的部份,將這些程式碼插入到 x03_ESP32CamWiFiWatchdog.ino 中就完成 x04_ESP32CamQRCodeReader.ino 程式的撰寫。

儲存上面的檔案並上傳到 ESP32-CAM 後,開啟串口調試助手,重置或重啟 ESP32-CAM,完成開機並連線到無線網路,可得到與下面類似的輸出訊息。

接著,利用之前建立的二維碼圖示,讓 ESP32-CAM 進行辨識,可得到與下面類似的輸出訊息。


【(03)結論】

二維碼辨識是整個 ESP32QRDoorLock 中最重要的環節,因此知道怎麼去做辨識和使用函式庫就很重要。函式庫內部使用修改版的 Quire 函式庫和 MaixPy 專案的一些 OpenMV 的程式碼, 辨識的結果儲存於 FreeRTOS Queue 等待被讀出...,想深入了解的話就看看函式庫裡的程式碼吧!

下一篇,是整個系列的最後一篇,將整合這兩個工作任務執行時的輸出狀態值來做監控,轉換為整個系統工作流程中的狀態值,用來觸發相對應的處理動作


.

.


<<部落格相關文章>>


    .

    .

    4 則留言:

    1. 不好意思,我是高職學生,我想問一下我找不到zip檔要去哪裡找,我把檔案加進去,但它顯示沒有符合的zip檔,所以來跟你請教,我的專題製作也跟您的檔案有相關,如果能得到解惑,會很感謝您。

      回覆刪除
      回覆
      1. 在網頁開頭裡面有需要的函式庫下載連結以及安裝的說明,詳細看網頁裡的說明。

        刪除
      2. 我已經照著上面做了,但在編譯的時候找不到ESP32QRCodeReader.h這個檔案,但我有把他家到程式裡,草稿碼資料夾也有,那我該怎麼辦。

        刪除
      3. 你安裝的函式庫沒放在預設的資料夾中,所以 Arduino IDE 執行時找不到;所謂預設的資料夾可以看"手動函式庫安裝教學":https://www.arduino.cc/en/guide/libraries 連結中步驟的說明。

        刪除

    留言屬名為"Unknown"或"不明"的用戶,大多這樣的留言都會直接被刪除掉,不會得到任何回覆!

    發問問題,請描述清楚你(妳)的問題,別人回答前不會想去 "猜" 問題是什麼?

    不知道怎麼發問,請看 [公告] 部落格提問須知 - 如何問問題 !