ESP32 Arduino环境httpSecureClient在核心0运行无故返回-1错误
问题根因
- 核心问题是非线程安全的网络对象跨核心调用+WiFi资源抢占:ESP32 Arduino框架的
WiFiClientSecure、HTTPClient库本身不支持多线程并发访问,你声明了全局的ApiClient、ApiHttpClient对象,在双核心下交替访问时会出现资源状态错乱,同时核心1运行的MQTT连接和核心0的HTTP请求共享同一个WiFi硬件资源,没有互斥机制的情况下会出现资源抢占,导致偶发连接失败返回错误码-1。 - 次因是任务优先级设置不合理:你给心跳任务设置的优先级为25,高于ESP32系统默认WiFi协议栈任务的优先级(通常为15左右),高优先级的用户任务会抢占WiFi协议栈的CPU时间,导致网络包收发异常。
- 其他潜在问题:请求前未检查WiFi连接状态、全局网络对象复用未清理上次请求残留状态。
修复方案
1. 替换全局网络对象为任务局部对象
不要在全局声明WiFiClientSecure和HTTPClient,将两个对象放到sendHeartBeat函数内部创建,每次请求结束后自动销毁,避免跨调用的状态残留。
2. 增加WiFi资源互斥锁
全局创建互斥量,所有网络操作(心跳HTTP请求、MQTT收发操作)执行前先获取互斥锁,执行完成后释放锁,避免WiFi资源并发抢占。
3. 调整任务优先级+增加WiFi连接检查
将心跳任务优先级降到5以下,每次发送请求前先判断当前WiFi是否处于连接状态,未连接则跳过本次请求。
4. 增加错误重试逻辑
返回错误码-1时,延迟100ms重试1~2次,覆盖偶发的网络波动场景。
示例修改代码
// 全局仅声明互斥量,不要声明网络对象 SemaphoreHandle_t wifiSemaphore; // 初始化阶段创建互斥量 void setup() { // ...其他初始化代码 wifiSemaphore = xSemaphoreCreateMutex(); // 创建心跳任务,优先级改为3 xTaskCreatePinnedToCore(sendHeartBeat, "Send Heartbeat", 20000, NULL, 3, &heartBeatTask, 0); } void sendHeartBeat(void *param) { for(;;) { // 先检查WiFi连接 if(WiFi.status() != WL_CONNECTED) { vTaskDelay(pdMS_TO_TICKS(1000)); continue; } // 拿WiFi互斥锁,最多等5秒 if(xSemaphoreTake(wifiSemaphore, pdMS_TO_TICKS(5000)) == pdTRUE) { // 网络对象放到局部创建 WiFiClientSecure ApiClient; HTTPClient ApiHttpClient; StaticJsonDocument<256> doc; doc["mac"] = deviceMacAddress; doc["key"] = DEVICE_SECRET; doc["type"] = DIGITAL_HQ_SOFTWARE_TYPE; String heartbeatData; serializeJson(doc, heartbeatData); ApiClient.setInsecure(); Serial.println("Sending Heartbeat"); int responseCode = -1; // 最多重试2次 for(int retry=0; retry<2 && responseCode!=200; retry++) { if(ApiHttpClient.begin(ApiClient, DIGITAL_HQ_HEARTBEAT_ENDPOINT)) { ApiHttpClient.addHeader("Content-Type", "application/json"); ApiHttpClient.setUserAgent(DIGITAL_HQ_USER_AGENT); responseCode = ApiHttpClient.POST(heartbeatData); ApiHttpClient.end(); } if(responseCode != 200) { vTaskDelay(pdMS_TO_TICKS(100)); } } if (responseCode != 200) { failedApiCalls ++; } Serial.print("ResponseCode from heartbeat: "); Serial.println(responseCode); // 释放互斥锁 xSemaphoreGive(wifiSemaphore); } // 心跳间隔,按你实际需求修改 vTaskDelay(pdMS_TO_TICKS(30000)); } }
注意:核心1的MQTT操作也要对应加互斥锁,每次MQTT publish/subscribe执行前拿锁,执行完成后释放,否则还是会出现资源抢占问题。
内容的提问来源于stack exchange,提问作者Robin
相关产品推荐
相关产品推荐

