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

ESP32使用ESP_NOW时受loop中delay阻塞,如何实现异步接收?

ESP-NOW本身就原生支持和ESPAsyncWebServer一致的事件驱动异步接收能力,你遇到的接收延迟完全是delay()阻塞函数导致的问题,解决方案如下:


1. 问题本质

delay()是完全阻塞内核的函数,调用期间除了最高优先级硬件中断外,所有用户态的事件回调(包括ESP-NOW接收回调、ESPAsyncWebServer的请求回调)都会被推迟到阻塞结束后才能执行,所以才会出现数据接收延迟。

另外你对millis()时间比对的担心是完全不必要的:单次时间差判断仅为一次整数运算,开销可以忽略不计,loop空转时ESP32会自动进入低功耗空闲状态,根本不会长期占用资源。


2. 实现ESP-NOW异步接收

ESP-NOW的接收逻辑不需要在loop中轮询,直接注册接收回调即可,数据到达时会自动触发回调执行,和ESPAsyncWebServer的请求回调逻辑完全一致:

// 第一步:定义和发送端格式匹配的接收数据结构体
typedef struct {
  int light_switch;
  int pump_switch;
} EspnowRecvData;

// 第二步:定义接收回调函数,收到数据时自动触发
void onEspnowRecv(const uint8_t *macAddr, const uint8_t *data, int dataLen) {
  EspnowRecvData recvBuf;
  memcpy(&recvBuf, data, sizeof(recvBuf));
  // 直接在回调内更新全局变量即可,不需要轮询
  light_switch = recvBuf.light_switch;
  pump_switch = recvBuf.pump_switch;
}

// 第三步:setup中初始化ESP-NOW后注册回调
void setup() {
  // 省略WiFi、GPIO、ESP-NOW基础初始化代码
  esp_now_register_recv_cb(onEspnowRecv);
}

3. 改造loop去掉阻塞延迟

将原有delay()替换为非阻塞的时间比对逻辑,确保内核不会被长时间挂起,回调可以随时触发:

// 全局变量,记录上次温度检测的时间戳
unsigned long lastTempCheck = 0;
// 温度检测间隔5000ms
const long CHECK_INTERVAL = 5000;

void loop() {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("[ ERR ] Wifi not connected. Exiting program");
    delay(9999);
    exit(0);
  }

  // 仅到间隔时间才执行一次检测逻辑
  unsigned long currentMillis = millis();
  if (currentMillis - lastTempCheck >= CHECK_INTERVAL) {
    lastTempCheck = currentMillis;
    
    float temp_check = dht.readTemperature();
    if (temp_check == 0) {
      delay(1000);
      temp_check = dht.readTemperature();
    }

    if (temp_check > 32.0 && *cooling_switch_p == false) {
      Serial.println("[ INF ] Too high temperature, switch on a cooling system");
      digitalWrite(COOLING_RELAY, LOW);
      *cooling_switch_p = true;
    }
    else if (temp_check < 30.0  && *cooling_switch_p == true) {
      Serial.println("[ INF ] Normal temperature, switch off a cooling system");
      digitalWrite(COOLING_RELAY, HIGH);
      *cooling_switch_p = false;
    }

    Serial.print("[ DBG ] Light Switch: ");
    Serial.println(String(light_switch));
    Serial.println("");

    Serial.print("[ DBG ] Pump Switch: ");
    Serial.println(String(pump_switch));
    Serial.println("");
  }
}

4. 进阶优化方案(可选)

如果不想在loop里做时间比对,可以直接用ESP32自带的FreeRTOS任务调度:把温度控制逻辑单独放到独立任务中,用RTOS的vTaskDelay()做延迟,该延迟只会挂起当前任务,不会阻塞整个内核,ESP-NOW和WebServer的回调都能正常执行,loop可以完全留空:

// 温度控制任务函数
void tempControlTask(void *param) {
  for(;;) {
    // 原有温度检测、继电器控制、串口打印逻辑
    float temp_check = dht.readTemperature();
    if (temp_check == 0) {
      vTaskDelay(pdMS_TO_TICKS(1000));
      temp_check = dht.readTemperature();
    }
    // 省略温度判断逻辑,和原有代码一致
    
    // 延迟5s,仅挂起当前任务
    vTaskDelay(pdMS_TO_TICKS(5000));
  }
}

void setup() {
  // 其他初始化逻辑
  xTaskCreate(tempControlTask, "TempCtrl", 2048, NULL, 1, NULL);
}

void loop() {
  // 无需任何代码
}

内容的提问来源于stack exchange,提问作者user14603676

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 04:54:03