You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 16:54:52