ESP32双核优化RFID门锁:解决串行执行的交互卡顿问题
优化ESP32 RFID门锁交互体验方案
问题分析
当前代码采用串行执行逻辑,依赖delay()实现电磁阀导通和屏幕显示延迟,导致:
- 屏幕更新必须等待开锁动作完成,反之亦然,交互卡顿
- 同步HTTP请求过程中,整个程序处于阻塞状态,无法响应其他操作
以下提供两种优化方向的具体实现方案:
方案一:非双核轻量化优化(优先推荐)
无需使用双核,通过替换阻塞操作、拆分逻辑即可解决卡顿问题。
1. 用millis()替代delay()实现非阻塞延迟
定义全局变量记录定时器状态,避免阻塞主循环:
unsigned long lockStartTime = 0; bool lockActive = false; unsigned long displayStartTime = 0; bool displayActive = false; String currentDisplayText = "";
2. 异步处理HTTP请求
替换同步HTTP客户端为ESPAsyncHTTPClient,请求完成后通过回调处理结果,不阻塞主循环:
#include <ESPAsyncHTTPClient.h> void restlookup(String UID) { String url = "http://" + String(server) + "/webapi/api/EmpAuth?id=" + UID; asyncHTTPRequest.beginGet(url); asyncHTTPRequest.onReadyStateChange([](void *optParm, asyncHTTPRequest *request, int readyState) { if (readyState == 4) { // 请求完成 String response = request->responseText(); response.replace("\"", ""); if (response == "fail") { currentDisplayText = "Not Authorised"; } else { // 立即触发开锁 lockActive = true; lockStartTime = millis(); digitalWrite(13, HIGH); currentDisplayText = "Welcome\n" + response; } // 触发屏幕更新 displayActive = true; displayStartTime = millis(); } }); asyncHTTPRequest.send(); }
3. 重构主循环为非阻塞逻辑
主循环只负责轮询RFID、处理定时器和屏幕更新:
void loop() { // 轮询RFID卡片 if (nfc.tagPresent()) { NfcTag tag = nfc.read(); String scannedUID = tag.getUidString(); scannedUID.replace(" ", "-"); Serial.println(scannedUID); restlookup(scannedUID); // 短时间内避免重复扫描 delay(500); } // 处理电磁阀非阻塞关闭 if (lockActive && millis() - lockStartTime >= timeOpen) { digitalWrite(13, LOW); lockActive = false; } // 处理屏幕显示延迟 if (displayActive) { display.clearDisplay(); displaytextcent(currentDisplayText); display.display(); if (millis() - displayStartTime >= 2000) { restdisplay(); // 恢复默认显示 displayActive = false; } } }
方案二:双核任务拆分(利用ESP32多核特性)
通过FreeRTOS将不同任务分配到两个核心,实现并行执行:
1. 定义任务队列(用于任务间通信)
QueueHandle_t messageQueue; // 消息结构体,传递验证结果和显示内容 typedef struct { bool isAuthorized; String displayText; } TaskMessage;
2. 核心0任务:RFID扫描与HTTP验证
void rfidHttpTask(void *parameter) { while (true) { if (nfc.tagPresent()) { NfcTag tag = nfc.read(); String scannedUID = tag.getUidString(); scannedUID.replace(" ", "-"); Serial.println(scannedUID); // 同步HTTP请求 String readString = ""; if (client.connect(server, 80)) { String getReq = "GET /webapi/api/EmpAuth?id=" + scannedUID; client.println(getReq); client.println(); while (client.connected()) { if (client.available()) { char c = client.read(); if (readString.length() < 100) { readString += c; } } } client.stop(); readString.replace("\"", ""); TaskMessage msg; if (readString == "fail") { msg.isAuthorized = false; msg.displayText = "Not Authorised"; } else { msg.isAuthorized = true; msg.displayText = "Welcome\n" + readString; } // 发送消息到核心1的任务 xQueueSend(messageQueue, &msg, portMAX_DELAY); } vTaskDelay(pdMS_TO_TICKS(500)); // 避免重复扫描 } vTaskDelay(pdMS_TO_TICKS(10)); // 让出CPU } }
3. 核心1任务:硬件控制与屏幕显示
void hardwareDisplayTask(void *parameter) { while (true) { TaskMessage msg; // 等待队列中的消息 if (xQueueReceive(messageQueue, &msg, portMAX_DELAY) == pdPASS) { // 立即更新屏幕 display.clearDisplay(); displaytextcent(msg.displayText); display.display(); // 处理开锁 if (msg.isAuthorized) { digitalWrite(13, HIGH); vTaskDelay(pdMS_TO_TICKS(timeOpen)); digitalWrite(13, LOW); } // 保持显示2秒 vTaskDelay(pdMS_TO_TICKS(2000)); restdisplay(); } } }
4. 在setup()中创建任务
void setup() { // 初始化串口、NFC、显示屏、GPIO等 Serial.begin(115200); // ...其他初始化代码 // 创建消息队列 messageQueue = xQueueCreate(5, sizeof(TaskMessage)); // 核心0运行RFID和HTTP任务 xTaskCreatePinnedToCore( rfidHttpTask, "RFID_HTTP_Task", 4096, // 栈大小 NULL, 1, // 任务优先级 NULL, 0 // 核心编号 ); // 核心1运行硬件控制和显示任务 xTaskCreatePinnedToCore( hardwareDisplayTask, "Hardware_Display_Task", 4096, NULL, 1, NULL, 1 // 核心编号 ); } // 主循环留空或做辅助操作 void loop() { vTaskDelay(pdMS_TO_TICKS(1000)); }
内容的提问来源于stack exchange,提问作者Andrew Taylor
相关产品推荐
相关产品推荐

