Загрузка файла на ESP32 с использованием WiFiClient.client.read () завершается неудачно - ошибка «Task watchdog» - PullRequest
0 голосов
/ 02 мая 2020

Я тестирую код загрузки больших файлов (приблизительно 1 МБ бинарного файла 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

Мое текущее понимание основано на эта ссылка ESP32 github означает, что процесс загрузки блокирует ESP от выполнения необходимых фоновых функций.

Сбой (в приведенном ниже коде) происходит во время while() l oop, что запускает client.read() для извлечения файла с сервера.

Я попытался проверить delay() и vTaskDelay(), чтобы посмотреть, могут ли они помочь. Не был уверен, что это освобождает вещи или просто добавляет дополнительную блокировку задач. Ни то, ни другое не помогло. (и я думаю это одна и та же функция в любом случае, правильно?)

Я не уверен на 100%, что проблема даже в блокировке. Наблюдение за загрузкой с использованием Serial.println(thisClient.available()) показало, что байтов, оставшихся , увеличилось с 7k до 5k, прежде чем перейти к резервному копированию на 7k - и повторять это несколько раз. Это говорит о возможной проблеме с сервером. НО тот же сервер, загрузка того же файла в JS -кодированный запрос ajax работает просто отлично.

Тестовый код, приведенный ниже, адаптирован из Espressif OTA Example . Я все еще новичок в C ++, поэтому простите использование 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()));
}

1 Ответ

1 голос
/ 03 мая 2020

Вы делаете слишком много в обратном вызове HTTP. Во время выполнения обратного вызова сторожевой таймер не может быть сброшен. Если это произойдет слишком долго, вы получите сообщение об ошибке - 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();
  }
}

Также while l oop в execOTA требуется вызов delay(). Попробуйте что-то вроде этого:

    while(thisClient.available()==0) {
        delay(1);
        if(millis()-timeout > 5000){

Когда вы звоните delay(), вы даете возможность другим задачам запускаться, что позволяет сбросить сторожевой таймер.

...