使用 WiFiClient.client.read() 在 ESP32 上下载文件失败——"Task watchdog" 错误
Downloading file on ESP32 using WiFiClient.client.read() fails -- "Task watchdog" error
我正在测试从服务器下载大文件(大约 1mb OTA 二进制文件)的代码
下载中途发生错误:
-> E (15787) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
-> E (15787) task_wdt: - async_tcp (CPU 0/1)
-> E (15787) task_wdt: Tasks currently running:
-> E (15787) task_wdt: CPU 0: IDLE0
-> E (15787) task_wdt: CPU 1: IDLE1
-> E (15787) task_wdt: Aborting.
-> abort() was called at PC 0x400e16af on core 0
根据 this ESP32 github link,我目前的理解是下载过程阻止了 ESP 执行必要的后台功能。
在运行 client.read()
从服务器获取文件的 while()
循环期间发生故障(在下面的代码中)。
我尝试测试 delay()
和 vTaskDelay()
看看它们是否有帮助。不确定这是否释放了东西或只是进一步增加了任何任务阻塞。都没有帮助。 (而且我认为 they're the same function 无论如何,对吗?)
我不是 100% 确定阻止甚至是问题所在。使用 Serial.println(thisClient.available())
监控下载显示剩余 字节 从大约 7k 增加到大约 5k,然后将备份跳到 7k —— 并重复这样做。这表明可能存在服务器问题。但是同一个服务器,将相同的文件下载到 JS 编码的 ajax 请求工作得很好。
下面的测试代码改编自Espressif OTA Example。
我还是 C++ 的新手,所以请原谅在 char 数组上使用 String。使用这些字符时遇到问题。
#include <WiFi.h>
#include <Update.h>
#include "AsyncJson.h"
#include "ArduinoJson.h"
AsyncWebServer EspServer(80);
void setup(){
Serial.begin(115200);
const char* ClientSsid = "***";
const char* ClientPwd = "***";
Serial.print("Connecting to LAN ");
WiFi.begin(ClientSsid, ClientPwd);
int CreepConnect;
CreepConnect=0;
while (WiFi.status()!=WL_CONNECTED && CreepConnect<30){
delay(250);
Serial.print(".");
CreepConnect++;
}
Serial.println(" on SSID " + WiFi.SSID() + " at " + String(WiFi.localIP()));
EspServer.on("*", HTTP_POST, [](AsyncWebServerRequest * Req){
AsyncWebParameter* keyVal=Req->getParam(0);
String key=keyVal->name();
String val=keyVal->value();
if(key=="req" && val=="execOTA"){
Serial.println("Updating...");
Req->send(200, "text/plain", "Updating...");
//EspServer.end(); //Tested disabling server
execOTA();
}else{
Serial.println("Request ignored");
Req->send(200, "text/plain", "Request ignored");
}
});
EspServer.begin();
//execOTA(); This does work. It only fails under the callback above.
}
String execOTA(){
WiFiClient thisClient;
IPAddress thisHost(192, 168, 1, 10);
char thisPath[]="/testBigFile.ino.esp32.bin"; //Big = about 800k OTA bin file
String thisPart, theseHeaders, thisBody;
if(!thisClient.connect(thisHost, 8465)){
Serial.println("Connection Failed");
return "{\"Error\":\"Connection Failed\"}";
}
Serial.println(thisPath);
Serial.println(String(thisPath));
Serial.println("Connection succeeded");
Serial.print("thisClient.available(): ");
Serial.println(thisClient.available());
String thisReq=String("GET ") + String(thisPath) + " HTTP/1.1\r\n" +
"Host: 192.168.1.10:8465\r\n" +
"Cache-Control: no-cache\r\n" +
"Connection: close\r\n\r\n";
Serial.println("thisReq: " + thisReq);
thisClient.print(thisReq);
unsigned long timeout = millis();
while(thisClient.available()==0) {
if(millis()-timeout > 5000){
Serial.println("Client timed out");
thisClient.stop();
Serial.println("Client timed out");
return "{\"Error\":\"Client timed out\"}";
}
}
Serial.println("Headers Begin");
thisPart="Header";
while(thisClient.available()){
Serial.println(thisClient.available());
if(thisPart=="Header"){
String thisLine=thisClient.readStringUntil('\n');
theseHeaders.concat(thisLine);
thisLine.trim();
if(!thisLine.length()){
Serial.println("Headers Complete:\n" + theseHeaders + "\n------\n");
thisPart="Body";
}
}else{ //*** Task Watchdog Error happens in this block, after about 50 successful character reads with delay ***
char thisChar=thisClient.read();
thisBody.concat(thisChar);
//delay(10); //Tested at various durations to see if it adds to or frees up blocking. It seems to add further blocking?
//vTaskDelay(15); //Also tested, at various durations.
}
}
Serial.println("Body Complete");
thisClient.stop();
return "{\"Headers\":\"" + theseHeaders + "\",\"Body\":\"" + thisBody + "\"}";
}
String getHeaderValue(String header, String headerName){
return header.substring(strlen(headerName.c_str()));
}
您在 HTTP 回调中做的太多了。回调为 运行ning 时,无法重置看门狗定时器。如果这种情况发生的时间太长,您将得到您所看到的错误 - Task watchdog got triggered
。最大的线索是它发生在 async_tcp
任务中。
尝试重写您的代码,让 HTTP_POST
处理程序设置一个全局变量来指示 execOTA()
需要被调用,而不是自己调用它。然后让 loop()
完成繁重的工作。
像这样:
boolean exec_ota_flag = false;
void setup() {
...
EspServer.on("*", HTTP_POST, [](AsyncWebServerRequest * Req){
AsyncWebParameter* keyVal=Req->getParam(0);
String key=keyVal->name();
String val=keyVal->value();
if(key=="req" && val=="execOTA"){
exec_ota_flag = true;
Req->send(200, "text/plain", "Updating...");
}else{
...
void loop() {
if(exec_ota_flag) {
exec_ota_flag = false;
execOTA();
}
}
此外,execOTA 中的 while
循环需要 delay()
调用。尝试这样的事情:
while(thisClient.available()==0) {
delay(1);
if(millis()-timeout > 5000){
当您调用 delay()
时,您给其他任务一个机会 运行,这将使看门狗定时器被重置。
从你的问题来看,不清楚你是否知道 watchdog 或 IDLE 任务是什么,更不用说是什么触发了它们。因此,我希望这会有所帮助:FreeRTOS & IDLE Tasks
基本上,任何代码都不应该 100% 地阻塞 CPU。但是如果需要的话,你可以,只要你为它创建一个单独的任务,并给它分配tskIDLE_PRIORITY。有关详细信息,请参阅 link。
我正在测试从服务器下载大文件(大约 1mb OTA 二进制文件)的代码
下载中途发生错误:
-> E (15787) task_wdt: Task watchdog got triggered. The following tasks did not reset the watchdog in time:
-> E (15787) task_wdt: - async_tcp (CPU 0/1)
-> E (15787) task_wdt: Tasks currently running:
-> E (15787) task_wdt: CPU 0: IDLE0
-> E (15787) task_wdt: CPU 1: IDLE1
-> E (15787) task_wdt: Aborting.
-> abort() was called at PC 0x400e16af on core 0
根据 this ESP32 github link,我目前的理解是下载过程阻止了 ESP 执行必要的后台功能。
在运行 client.read()
从服务器获取文件的 while()
循环期间发生故障(在下面的代码中)。
我尝试测试 delay()
和 vTaskDelay()
看看它们是否有帮助。不确定这是否释放了东西或只是进一步增加了任何任务阻塞。都没有帮助。 (而且我认为 they're the same function 无论如何,对吗?)
我不是 100% 确定阻止甚至是问题所在。使用 Serial.println(thisClient.available())
监控下载显示剩余 字节 从大约 7k 增加到大约 5k,然后将备份跳到 7k —— 并重复这样做。这表明可能存在服务器问题。但是同一个服务器,将相同的文件下载到 JS 编码的 ajax 请求工作得很好。
下面的测试代码改编自Espressif OTA Example。 我还是 C++ 的新手,所以请原谅在 char 数组上使用 String。使用这些字符时遇到问题。
#include <WiFi.h>
#include <Update.h>
#include "AsyncJson.h"
#include "ArduinoJson.h"
AsyncWebServer EspServer(80);
void setup(){
Serial.begin(115200);
const char* ClientSsid = "***";
const char* ClientPwd = "***";
Serial.print("Connecting to LAN ");
WiFi.begin(ClientSsid, ClientPwd);
int CreepConnect;
CreepConnect=0;
while (WiFi.status()!=WL_CONNECTED && CreepConnect<30){
delay(250);
Serial.print(".");
CreepConnect++;
}
Serial.println(" on SSID " + WiFi.SSID() + " at " + String(WiFi.localIP()));
EspServer.on("*", HTTP_POST, [](AsyncWebServerRequest * Req){
AsyncWebParameter* keyVal=Req->getParam(0);
String key=keyVal->name();
String val=keyVal->value();
if(key=="req" && val=="execOTA"){
Serial.println("Updating...");
Req->send(200, "text/plain", "Updating...");
//EspServer.end(); //Tested disabling server
execOTA();
}else{
Serial.println("Request ignored");
Req->send(200, "text/plain", "Request ignored");
}
});
EspServer.begin();
//execOTA(); This does work. It only fails under the callback above.
}
String execOTA(){
WiFiClient thisClient;
IPAddress thisHost(192, 168, 1, 10);
char thisPath[]="/testBigFile.ino.esp32.bin"; //Big = about 800k OTA bin file
String thisPart, theseHeaders, thisBody;
if(!thisClient.connect(thisHost, 8465)){
Serial.println("Connection Failed");
return "{\"Error\":\"Connection Failed\"}";
}
Serial.println(thisPath);
Serial.println(String(thisPath));
Serial.println("Connection succeeded");
Serial.print("thisClient.available(): ");
Serial.println(thisClient.available());
String thisReq=String("GET ") + String(thisPath) + " HTTP/1.1\r\n" +
"Host: 192.168.1.10:8465\r\n" +
"Cache-Control: no-cache\r\n" +
"Connection: close\r\n\r\n";
Serial.println("thisReq: " + thisReq);
thisClient.print(thisReq);
unsigned long timeout = millis();
while(thisClient.available()==0) {
if(millis()-timeout > 5000){
Serial.println("Client timed out");
thisClient.stop();
Serial.println("Client timed out");
return "{\"Error\":\"Client timed out\"}";
}
}
Serial.println("Headers Begin");
thisPart="Header";
while(thisClient.available()){
Serial.println(thisClient.available());
if(thisPart=="Header"){
String thisLine=thisClient.readStringUntil('\n');
theseHeaders.concat(thisLine);
thisLine.trim();
if(!thisLine.length()){
Serial.println("Headers Complete:\n" + theseHeaders + "\n------\n");
thisPart="Body";
}
}else{ //*** Task Watchdog Error happens in this block, after about 50 successful character reads with delay ***
char thisChar=thisClient.read();
thisBody.concat(thisChar);
//delay(10); //Tested at various durations to see if it adds to or frees up blocking. It seems to add further blocking?
//vTaskDelay(15); //Also tested, at various durations.
}
}
Serial.println("Body Complete");
thisClient.stop();
return "{\"Headers\":\"" + theseHeaders + "\",\"Body\":\"" + thisBody + "\"}";
}
String getHeaderValue(String header, String headerName){
return header.substring(strlen(headerName.c_str()));
}
您在 HTTP 回调中做的太多了。回调为 运行ning 时,无法重置看门狗定时器。如果这种情况发生的时间太长,您将得到您所看到的错误 - Task watchdog got triggered
。最大的线索是它发生在 async_tcp
任务中。
尝试重写您的代码,让 HTTP_POST
处理程序设置一个全局变量来指示 execOTA()
需要被调用,而不是自己调用它。然后让 loop()
完成繁重的工作。
像这样:
boolean exec_ota_flag = false;
void setup() {
...
EspServer.on("*", HTTP_POST, [](AsyncWebServerRequest * Req){
AsyncWebParameter* keyVal=Req->getParam(0);
String key=keyVal->name();
String val=keyVal->value();
if(key=="req" && val=="execOTA"){
exec_ota_flag = true;
Req->send(200, "text/plain", "Updating...");
}else{
...
void loop() {
if(exec_ota_flag) {
exec_ota_flag = false;
execOTA();
}
}
此外,execOTA 中的 while
循环需要 delay()
调用。尝试这样的事情:
while(thisClient.available()==0) {
delay(1);
if(millis()-timeout > 5000){
当您调用 delay()
时,您给其他任务一个机会 运行,这将使看门狗定时器被重置。
从你的问题来看,不清楚你是否知道 watchdog 或 IDLE 任务是什么,更不用说是什么触发了它们。因此,我希望这会有所帮助:FreeRTOS & IDLE Tasks
基本上,任何代码都不应该 100% 地阻塞 CPU。但是如果需要的话,你可以,只要你为它创建一个单独的任务,并给它分配tskIDLE_PRIORITY。有关详细信息,请参阅 link。