網頁最後修改時間: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 程式的撰寫。
/**------------------------------------------------------------------------ | |
* x04_ESP32CamQRCodeReader.ino | |
* ESP32-CAM 二維碼辨識。 | |
* | |
* Adapted by Ruten.Proteus | |
* Test Date: 2021/09/17 | |
* | |
* {功能說明} | |
* + [o] 修改 Watchdog 逾時時間; | |
* + [o] 移除 x03 輸出秒數的程式碼; | |
* + [o] 加入二維碼辨識的功能; | |
* | |
* {Result} Wrok !!! | |
*------------------------------------------------------------------------**/ | |
#include <Arduino.h> | |
#include <WiFi.h> | |
#include <esp_task_wdt.h> | |
#include <FastLED.h> | |
#include <ESP32QRCodeReader.h> | |
/** 是否使用 OTA 功能? 不使用就註解下一行 */ | |
#define ENABLE_OTA | |
#ifdef ENABLE_OTA | |
#include <ArduinoOTA.h> | |
#endif | |
/**------------------------------------------------------------------------ | |
* GLOBAL VARIABLES | |
*------------------------------------------------------------------------**/ | |
/*================== WIFI =================*/ | |
//** 無線分享器名稱與密碼 */ | |
const char* SSID = "填入無線分享器名稱"; | |
const char* PASSWORD = "填入無線分享器連線密碼"; | |
// 裝置名稱 | |
const char* DEVICENAME = "ESP32QRDoorLock"; | |
/*================== WATCHDOG =================*/ | |
#define WDT_TIMEOUT 30 // 設定 Watchdog 計時器的時間 | |
/*================== QR CODE READER =================*/ | |
ESP32QRCodeReader qrcodereader (CAMERA_MODEL_AI_THINKER); | |
/*================== FASTLED =================*/ | |
const int NUM_LEDS = #1; // 填入 LED 的數量取代 #1 | |
const uint8_t DATA_PIN = #2; // 填入 WS2812 通訊接腳號碼取代 #2 | |
// Define the array of leds | |
CRGB ws2812[NUM_LEDS]; | |
/*================== GPIO =================*/ | |
/** 電磁鎖 */ | |
const uint8_t DOORLOCK = #3; // 填入電磁鎖驅動模組控制接腳號碼取代 #3 | |
/** 門磁(磁簧)開關 */ | |
const uint8_t LIMITSWITCH = #4; // 填入門磁開關訊號接接腳號碼取代 #4 | |
// 門的狀態 | |
#define OPEN HIGH | |
#define CLOSE LOW | |
#define dDoorState digitalRead(LIMITSWITCH) | |
/*================== TASK: WIFIRECONNECT =================*/ | |
enum wifi_reconnect_state { | |
UNKNOWN, | |
RUNNING, | |
CONNECTING, | |
CONNECTED, | |
DISCONNECTED, | |
} xWifiReconnectState = UNKNOWN; | |
bool OTA_status = false; | |
TaskHandle_t xWiFiReConnectTaskHandle; | |
/*================== TASK: ONQRCODE =================*/ | |
enum onqrcode_state_t { | |
OFF, | |
WORKING, | |
MATCH, | |
UNMATCH, | |
INVALID | |
} xOnQrCodeState = OFF; | |
TaskHandle_t xOnQrCodeTaskHandle; | |
/*================== lsInterrupt =================*/ | |
volatile bool bIntTrigger = false; | |
unsigned long ulLastMicros; | |
// 定義特定任務的 QR Code | |
// https://qr.ioi.tw/zh/ | |
const char *pcQrCode[] { | |
"Qh@4#I_6+", // unlock | |
"PfgW~!$&U" // do nothing | |
}; | |
/*======================== END OF GLOBAL VARIABLES ========================*/ | |
/**------------------------------------------------------------------------ | |
* ENUM AND STRUCT | |
*------------------------------------------------------------------------**/ | |
/*================== FASTLED =================*/ | |
// Colors definition for WS2812 | |
#define BLACK CRGB ( 0, 0 , 0) // Black | |
#define WHITE CRGB (255, 255, 255) // White | |
#define RED CRGB (255, 0, 0) // Red | |
#define ORANGE CRGB (255, 127, 0) // Orange | |
#define YELLOW CRGB (255, 255, 0) // Yellow | |
#define GREEN CRGB ( 0, 255, 0) // Green | |
#define BLUE CRGB ( 0, 0, 255) // Blue | |
#define INDIGO CRGB ( 0, 255, 255) | |
#define VIOLET CRGB (127, 0, 255) // Violet | |
/*========================= END OF ENUM AND STRUCT ========================*/ | |
void blink (const int8_t blink_count, const TickType_t delay, const CRGB color); | |
/**======================================================================== | |
** ioInit() | |
*? ESP32 接腳和 FastLED 初始化 | |
*========================================================================**/ | |
void ioInit () { | |
pinMode (DOORLOCK, OUTPUT); | |
digitalWrite (DOORLOCK, LOW); | |
pinMode (LIMITSWITCH, INPUT_PULLUP); | |
FastLED.addLeds<WS2812B, DATA_PIN, GRB>(ws2812, NUM_LEDS); | |
//* 開機關閉 LED | |
ws2812[0] = BLACK; | |
FastLED.show (); | |
} | |
/**======================================================================== | |
** lsInterrupt() | |
*? 中斷處理函式 | |
*========================================================================**/ | |
void IRAM_ATTR lsInterrupt () { | |
if((long)(micros() - ulLastMicros) >= 150000L) { | |
bIntTrigger = true; | |
detachInterrupt (digitalPinToInterrupt(LIMITSWITCH)); | |
} | |
ulLastMicros = micros(); | |
} | |
/**======================================================================== | |
** blink() | |
*? 系統運行狀態的指示 | |
*@param blink_count const int8_t | |
*@param color CRGB | |
*========================================================================**/ | |
void blink (const int8_t blink_count, // 閃爍次數 | |
const CRGB color) // WS2812 顏色 | |
{ | |
uint8_t state = LOW; | |
for (int x = 0; x < (blink_count << 1); ++x) { | |
if (state ^= HIGH) { | |
ws2812[0] = color; | |
} else { | |
ws2812[0] = BLACK; | |
} | |
FastLED.show(); | |
vTaskDelay (pdMS_TO_TICKS(50)); | |
} | |
} | |
/**======================================================================== | |
** doorControl() | |
*? 門鎖開關控制 | |
*@param isMotor const bool | |
* 門鎖的形式。true: DC Motor; false: 電磁開關 | |
*========================================================================**/ | |
void doorControl (const bool isMotor) { | |
if (isMotor) { | |
// 如果使用電動鎖頭,寫程式在這裡 | |
// ... | |
} else { | |
digitalWrite (DOORLOCK, HIGH); | |
vTaskDelay (pdMS_TO_TICKS(1000)); | |
digitalWrite (DOORLOCK, LOW); | |
} | |
} | |
/**======================================================================== | |
** wifiReconnectTask() | |
*? 監測無線連線狀態的任務。若出現問題會造成 WatchDog 重置系統重新連線。 | |
*@param pvParameter void* | |
*========================================================================**/ | |
void wifiReconnectTask (void *pvParameters) { | |
int wifiStatus = 0; | |
int wifiPrev = WL_CONNECTED; | |
int n = 0; | |
unsigned long previousMillis = 0; | |
const long interval = 1000; | |
esp_task_wdt_add(NULL); // 添加當前任務到看門狗計時器 | |
for(;;) { | |
wifiStatus = WiFi.status(); | |
#ifdef ENABLE_OTA | |
ArduinoOTA.handle(); | |
#endif | |
if (OTA_status) // 如果有 OTA,重置 Watchdog 計時器 | |
esp_task_wdt_reset(); | |
/** 確認 WiFi 狀態是否已經改變? */ | |
if ((wifiStatus == WL_CONNECTED)&&(wifiPrev != WL_CONNECTED)) { | |
xWifiReconnectState = CONNECTED; | |
esp_task_wdt_reset(); // 重置 Watchdog 計時器 | |
Serial.printf ("\nConnected to WiFi!\nLocal IP address: %s\n\n", | |
(WiFi.localIP ().toString ()).c_str ()); | |
} | |
else if ((wifiStatus != WL_CONNECTED)&&(wifiPrev == WL_CONNECTED)) { | |
xWifiReconnectState = DISCONNECTED; | |
esp_task_wdt_reset(); // 重置 Watchdog 計時器 | |
Serial.println(F("\nWiFi disconnected!")); | |
WiFi.disconnect(); | |
Serial.println(F("Scanning for WiFi networks")); | |
n = WiFi.scanNetworks(false, true); | |
Serial.println(F("Scan done")); | |
if (n == 0) { | |
Serial.println(F("No networks found. Resetting ESP32.")); | |
esp_restart(); | |
} else { | |
Serial.print(n); | |
Serial.println(F(" networks found")); | |
for (int i = 0; i < n; ++i) { | |
/** 列出每一個找到的網路的 SSID 和 RSSI */ | |
Serial.print(i + 1); Serial.print(F(": ")); | |
Serial.print(WiFi.SSID(i)); Serial.print(F(" (")); | |
Serial.print(WiFi.RSSI(i)); Serial.print(F(")")); | |
Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN)?" ":"*"); | |
vTaskDelay (pdMS_TO_TICKS(10)); | |
} | |
} | |
esp_task_wdt_reset(); // 重置 Watchdog 計時器 | |
Serial.println(); | |
Serial.println(F("Please, check if your WiFi network is on the list and check if it's strong enough (greater than -90).")); | |
Serial.println("ESP32 will reset itself after "+String(WDT_TIMEOUT)+" seconds if can't connect to the network"); | |
Serial.println("Connecting to: " + String(SSID)); | |
xWifiReconnectState = CONNECTING; | |
WiFi.reconnect(); | |
} | |
else if ((wifiStatus == WL_CONNECTED)&&(wifiPrev == WL_CONNECTED)) { | |
xWifiReconnectState = RUNNING; | |
esp_task_wdt_reset(); // 重置 Watchdog 計時器 | |
vTaskDelay (pdMS_TO_TICKS(1000)); | |
} | |
else { | |
/** 不要重置 Watchdoh 計時器,如果計時器到達設定的時間就會重置 ESP32 */ | |
unsigned long currentMillis = millis(); | |
if (currentMillis - previousMillis >= interval) { | |
previousMillis = currentMillis; | |
Serial.print (F(".")); | |
} | |
} | |
wifiPrev = wifiStatus; | |
} | |
} | |
/**======================================================================== | |
** 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)); | |
} | |
} | |
/**======================================================================== | |
** setupOTA | |
*? ESP32 OTA 韌體上傳功能 | |
*========================================================================**/ | |
void setupOTA () { | |
#ifdef ENABLE_OTA | |
ArduinoOTA | |
.onStart([]() { | |
String type; | |
if (ArduinoOTA.getCommand() == U_FLASH) | |
type = "sketch"; | |
else // U_SPIFFS | |
type = "filesystem"; | |
// NOTE: if updating SPIFFS this would be the place | |
// to unmount SPIFFS using SPIFFS.end() | |
Serial.println("Start updating " + type); | |
OTA_status = true; | |
}) | |
.onEnd([]() { | |
Serial.println(F("\nEnd")); | |
}) | |
.onProgress([](unsigned int progress, unsigned int total) { | |
Serial.printf("Progress: %u%%\r", (progress / (total / 100))); | |
esp_task_wdt_reset (); | |
OTA_status = true; | |
}) | |
.onError([](ota_error_t error) { | |
Serial.printf("Error[%u]: ", error); | |
if (error == OTA_AUTH_ERROR) Serial.println(F("Auth Failed")); | |
else if (error == OTA_BEGIN_ERROR) Serial.println(F("Begin Failed")); | |
else if (error == OTA_CONNECT_ERROR) Serial.println(F("Connect Failed")); | |
else if (error == OTA_RECEIVE_ERROR) Serial.println(F("Receive Failed")); | |
else if (error == OTA_END_ERROR) Serial.println(F("End Failed")); | |
OTA_status = false; | |
esp_restart (); | |
}); | |
ArduinoOTA.setHostname (DEVICENAME); | |
ArduinoOTA.begin(); | |
#endif | |
} | |
void setup () { | |
delay (2000); | |
//* UART 初始化 | |
Serial.begin (115200); | |
Serial.println (F("\nx04_ESP32CamQRCodeReader Booting...")); | |
//* WiFi 設定 | |
WiFi.mode (WIFI_STA); | |
WiFi.setHostname (DEVICENAME); | |
WiFi.begin (SSID, PASSWORD); | |
//* 外設初始化 | |
ioInit (); | |
//* OTA 設定 | |
OTA_status = false; | |
setupOTA (); | |
//* WatchDog 初始化 | |
esp_task_wdt_init (WDT_TIMEOUT, true); | |
/*======= WIFI RECONNECT =======*/ | |
xTaskCreatePinnedToCore ( | |
wifiReconnectTask, "wifiReconnectTask", | |
100000, NULL, | |
0, // Priotity 0 | |
&xWiFiReConnectTaskHandle, 0 // Core 0 | |
); | |
delay (250); | |
/*======= ESP32QRCODEREADER =======*/ | |
qrcodereader.setup (); | |
Serial.print (F("Setup QRCode Reader")); | |
qrcodereader.beginOnCore (1); | |
Serial.println (F("Begin on Core 1")); | |
xTaskCreatePinnedToCore ( | |
onQrCodeTask, "onQrCodeTask", | |
4 * 1024, NULL, | |
2, // Priority 2 | |
&xOnQrCodeTaskHandle, 0 // Core 0 | |
); | |
delay (250); | |
//* 印出 ESP32 Heap 和 PSRAM 的資訊 | |
log_d("Total heap: %d", ESP.getHeapSize ()); | |
log_d("Free heap: %d", ESP.getFreeHeap ()); | |
log_d("Total PSRAM: %d", ESP.getPsramSize ()); | |
log_d("Free PSRAM: %d", ESP.getFreePsram ()); | |
} | |
void loop () {} |
儲存上面的檔案並上傳到 ESP32-CAM 後,開啟串口調試助手,重置或重啟 ESP32-CAM,完成開機並連線到無線網路,可得到與下面類似的輸出訊息。
接著,利用之前建立的二維碼圖示,讓 ESP32-CAM 進行辨識,可得到與下面類似的輸出訊息。
【(03)結論】
二維碼辨識是整個 ESP32QRDoorLock 中最重要的環節,因此知道怎麼去做辨識和使用函式庫就很重要。函式庫內部使用修改版的 Quire 函式庫和 MaixPy 專案的一些 OpenMV 的程式碼, 辨識的結果儲存於 FreeRTOS Queue 等待被讀出...,想深入了解的話就看看函式庫裡的程式碼吧!
下一篇,是整個系列的最後一篇,將整合這兩個工作任務執行時的輸出狀態值來做監控,轉換為整個系統工作流程中的狀態值,用來觸發相對應的處理動作
.
.
<<部落格相關文章>>
.
.
不好意思,我是高職學生,我想問一下我找不到zip檔要去哪裡找,我把檔案加進去,但它顯示沒有符合的zip檔,所以來跟你請教,我的專題製作也跟您的檔案有相關,如果能得到解惑,會很感謝您。
回覆刪除在網頁開頭裡面有需要的函式庫下載連結以及安裝的說明,詳細看網頁裡的說明。
刪除我已經照著上面做了,但在編譯的時候找不到ESP32QRCodeReader.h這個檔案,但我有把他家到程式裡,草稿碼資料夾也有,那我該怎麼辦。
刪除你安裝的函式庫沒放在預設的資料夾中,所以 Arduino IDE 執行時找不到;所謂預設的資料夾可以看"手動函式庫安裝教學":https://www.arduino.cc/en/guide/libraries 連結中步驟的說明。
刪除