简体   繁体   English

任务看门狗被触发 - 任务没有及时重置看门狗

[英]Task watchdog got triggered - The tasks did not reset the watchdog in time

I am trying to write a little asynchronous WebServer.我正在尝试编写一个小异步 WebServer。 Let me briefly describe the scenario:让我简要描述一下场景:

My ESP32 as well is a router.我的 ESP32 也是一个路由器。 So if I connect with my mobile phone into the WiFi the ESP32 is spreading and call the ip address and a special path with a browser, a WebSite is delivered.因此,如果我将手机连接到 WiFi 中,ESP32 正在传播,并使用浏览器调用 ip 地址和特殊路径,就会提供一个网站。 Here a button is displayed.这里显示一个按钮。 Until this point it works pretty well.到目前为止,它工作得很好。 Now, if I click on that button, a HTTPS Web Request (method: GET) is sent to a special machine.现在,如果我单击该按钮,则会将 HTTPS Web 请求(方法:GET)发送到特殊机器。 This machine answers and returns a JSON.本机应答并返回 JSON。 This could last a couple of seconds.这可能会持续几秒钟。 After extracting a value out of the JSON string, this value shall be displayed.从 JSON 字符串中提取一个值后,应显示该值。

To achieve this, I am using the following libraries:为此,我使用以下库:

I know (by another sketch) that the last three ones work without any problems.我知道(通过另一个草图)最后三个工作没有任何问题。

Unfortunately, when I click the button, the following output appears onto my serial monitor:不幸的是,当我单击按钮时,以下 output 出现在我的串行监视器上:

Starting connection to server...开始连接服务器...
[HTTPS] begin... Path: https://192.168.4.101/api/unlock/generate_pin [HTTPS]开始... 路径: https://192.168.4.101/api/unlock/generate_pin
[HTTPS] GET... [HTTPS] 获取...
E (137906) task_wdt: Task watchdog got triggered. E (137906) task_wdt:任务看门狗被触发。 The following tasks did not reset the watchdog in time:以下任务没有及时重置看门狗:
E (137906) task_wdt: - async_tcp (CPU 0/1) E (137906) task_wdt: - async_tcp (CPU 0/1)
E (137906) task_wdt: Tasks currently running: E (137906) task_wdt:当前正在运行的任务:
E (137906) task_wdt: CPU 0: IDLE0 E (137906) task_wdt: CPU 0: IDLE0
E (137906) task_wdt: CPU 1: loopTask E (137906) task_wdt: CPU 1: loopTask
E (137906) task_wdt: Aborting. E (137906) task_wdt:中止。
abort() was called at PC 0x400e08af on core 0 abort() 在核心 0 上的 PC 0x400e08af 被调用

Backtrace: 0x4008cc18:0x3ffbe170 0x4008ce49:0x3ffbe190 0x400e08af:0x3ffbe1b0 0x40084f21:0x3ffbe1d0 0x4016581b:0x3ffbc120 0x400e1c66:0x3ffbc140 0x4008ab21:0x3ffbc160 0x4008932d:0x3ffbc180 Backtrace: 0x4008cc18:0x3ffbe170 0x4008ce49:0x3ffbe190 0x400e08af:0x3ffbe1b0 0x40084f21:0x3ffbe1d0 0x4016581b:0x3ffbc120 0x400e1c66:0x3ffbc140 0x4008ab21:0x3ffbc160 0x4008932d:0x3ffbc180

Rebooting...正在重新启动...
ets Jun 8 2016 00:22:57等 2016 年 6 月 8 日 00:22:57

rst:0xc (SW_CPU_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT) rst:0xc (SW_CPU_RESET),boot:0x17 (SPI_FAST_FLASH_BOOT)
configsip: 0, SPIWP:0xee configsip: 0, SPIWP:0xee
clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00 clk_drv:0x00,q_drv:0x00,d_drv:0x00,cs0_drv:0x00,hd_drv:0x00,wp_drv:0x00
mode:DIO, clock div:1模式:DIO,时钟 div:1
load:0x3fff0018,len:4负载:0x3fff0018,长度:4
load:0x3fff001c,len:1044负载:0x3fff001c,长度:1044
load:0x40078000,len:8896加载:0x40078000,长度:8896
load:0x40080400,len:5816加载:0x40080400,长度:5816
entry 0x400806ac条目 0x400806ac
Serial initial done串行初始完成

Anyone any idea what is happening and how to fix this?任何人都知道发生了什么以及如何解决这个问题? So that the GET request is sent correctly / the answer is received?以便正确发送 GET 请求/收到答案?

I am using a Heltec WiFi Kit 32 .我正在使用Heltec WiFi 套件 32

Would be very happy about every answer, thanks in advance.对每一个答案都会很高兴,在此先感谢。

Best regards此致

PS: Please let me finally add my code: PS:请让我最后添加我的代码:

#include <heltec.h>
#include "WiFi.h"
#include "ESPAsyncWebServer.h"

#include <WiFiClientSecure.h>
#include <HTTPClient.h>
 
const char* ssid = "MyWiFiSSID";
const char* password =  "MyWiFiPW";
 
AsyncWebServer server(80);

void setup() {

  Heltec.begin(true, false, true, true, 470E6);

  WiFi.softAP(ssid, password);
  
  IPAddress IP = WiFi.softAPIP();
  Serial.print("AccessPoint IP address: ");
  Serial.println(IP);
  
  server.on("/hello", HTTP_GET, [](AsyncWebServerRequest *request){
    
    request->send(200, "text/html", "<!DOCTYPE html><html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" charset=\"UTF-8\"><link rel=\"icon\" href=\"data:,\"><style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}.button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}</style></head><body><h1>Welcome to the Landing Page of the Web Server</h1><p><a href=\"/get_unlock_pin\"><button class=\"button\">Click Me</button></a></p></body></html>");
  });

  server.on("/get_unlock_pin", HTTP_GET, [](AsyncWebServerRequest *request){

    String firstpartofrawhtmlcode = "<!DOCTYPE html><html><head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" charset=\"UTF-8\"><link rel=\"icon\" href=\"data:,\"><style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}.button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px; text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}</style></head><body><h2>Received Pin: </h2><h2 style=\"color: #FF0000\">";
    String receivedPin = getPin("192.168.4.101");
    String secondpartofrawhtmlcode = "</h2></body></html>";
    String fullrawhtmlcode;
    firstpartofrawhtmlcode = firstpartofrawhtmlcode.concat(receivedPin);
    fullrawhtmlcode = firstpartofrawhtmlcode.concat(secondpartofrawhtmlcode);
    request->send(200, "text/html", fullrawhtmlcode);
  });
 
  server.begin();
}

void loop() {

}

String getPin(String ip){
    Serial.println("\nStarting connection to server...");  
    WiFiClientSecure *wificlient = new WiFiClientSecure;

    HTTPClient https;
    https.setAuthorization("MyUserName", "MyPassword");

    String path = "https://" + ip + "/api/unlock/generate_pin";
      
    Serial.print("[HTTPS] begin... Path: " + path + "\n");
    if (https.begin(*wificlient, path)) { 
        Serial.print("[HTTPS] GET...\n");
        int httpCode = https.GET();
        if (httpCode > 0) {
          Serial.printf("[HTTPS] GET... code: %d\n", httpCode);
  
          if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
            String payload = https.getString();
            Serial.println(payload);
            //Extract Pin from JSON
            String tmp = payload.substring(payload.indexOf(':'), payload.indexOf('}'));
            String tmp2 = tmp.substring(tmp.indexOf('"')+1,tmp.lastIndexOf('"')); 
            if(tmp2.substring(0,1) == "-"){
               return "-";
            }else{
               return tmp2;
            }              
          }
        } else {
               Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
        }  
        https.end();
    } else {
        Serial.printf("[HTTPS] Unable to connect\n");
    }
}

The way the question is formulated, I assume you don't know what a watchdog is nor why it is triggering.问题的表述方式,我假设你不知道看门狗是什么,也不知道它为什么会触发。 Hence:因此:

Arduino for the ESP is built on the ESP-IDF, which in turn is built around FreeRTOS. ESP 的 Arduino 建立在 ESP-IDF 之上,而 ESP-IDF 又围绕 FreeRTOS 构建。 FreeRTOS creates one IDLE task per core. FreeRTOS 为每个内核创建一个 IDLE 任务。 It also sets watchdog timers on these Tasks.它还在这些任务上设置看门狗定时器。 This means if these tasks get starved of execution time, then after a timeout period, the watchdog gets triggered and resets the chip.这意味着如果这些任务的执行时间不足,那么在超时时间之后,看门狗就会被触发并重置芯片。 The IDLE tasks do some important FreeRTOS "household" work in the background, so you have to give them time. IDLE 任务在后台执行一些重要的 FreeRTOS “家务”工作,因此您必须给他们时间。 They also have the lowest possible priority.它们也具有最低的优先级。 So if any task is running with a higher priority (such as your callbacks), these will always run first while the IDLE tasks will have to wait.因此,如果任何任务以更高的优先级运行(例如您的回调),这些将始终首先运行,而 IDLE 任务将不得不等待。 Thus all higher priority tasks must be short enough to avoid triggering the watchdog.因此,所有更高优先级的任务必须足够短以避免触发看门狗。 If this isn't feasible, you have to insert pauses in sufficient intervals, for example by calling vTaskDelay(...) or by performing some IO function that blocks long enough.如果这不可行,则必须以足够的间隔插入暂停,例如通过调用 vTaskDelay(...) 或执行一些足够长的阻塞 IO function。 In either case, the current task will go to sleep, and FreeRTOS will kick in another task, and if there are no other higher priority tasks waiting, it will finally allow the IDLE tasks to execute.无论哪种情况,当前任务都会 go 进入睡眠状态,FreeRTOS 将启动另一个任务,如果没有其他更高优先级的任务等待,它最终会允许 IDLE 任务执行。 What all this means is that usually no code you write should ever preoccupy the CPU for 100% of the time for any period longer than the watchdog timeout period.这一切意味着通常你编写的任何代码都不应该在比看门狗超时时间长的任何时间段内 100% 地占用 CPU。 Moreover, you can't turn this off in Arduino.此外,您不能在 Arduino 中将其关闭。 It is only configurable in ESP-IDF, meaning you have to code within that framework or recompile Arduino on your own.它只能在 ESP-IDF 中配置,这意味着您必须在该框架内编写代码或自行重新编译 Arduino。 Moreover it is a bad idea to turn it off anyways.此外,无论如何关闭它是一个坏主意。

You must use a value of atleast 1 in the call to vTaskDelay(...).您必须在调用 vTaskDelay(...) 时使用至少 1 的值。 Due to integer arithmetic and the various expressions passed as a parameter, it could end up amounting to 0, ie no delay at all.由于 integer 算法和作为参数传递的各种表达式,它最终可能为 0,即完全没有延迟。

If you really need to run a task without pause, you'll have to create the task on your own and give it a priority of tskIDLE_PRIORITY, such as:如果你真的需要不停顿地运行一个任务,你必须自己创建这个任务,并给它一个 tskIDLE_PRIORITY 的优先级,例如:

xTaskCreate(someFunction, "HumanReadableNameofTask", 4096, NULL, tskIDLE_PRIORITY, NULL);

where your function has the following signature:您的 function 具有以下签名:

void someFunction(void* arg) {...}

You can do this in Arduino too.您也可以在 Arduino 中执行此操作。 So, either keep the code that runs in the callbacks to a minimum, or shift the heavy lifting to a separate task and have the callbacks simply forward any relevant information to that task (such as using volatile variables and semaphores, ie the usual methods of synchronization in parallel processing. that's beyond the scope of this answer).因此,要么将在回调中运行的代码保持在最低限度,要么将繁重的工作转移到一个单独的任务中,让回调简单地将任何相关信息转发给该任务(例如使用 volatile 变量和信号量,即常用的方法)并行处理中的同步。这超出了这个答案的 scope)。

Note I'm not sure if calling delay(...) in Arduino has the same effect as vTaskDelay, but it probably does (with some subtleties).注意我不确定在 Arduino 中调用 delay(...) 是否与 vTaskDelay 具有相同的效果,但它可能会(有一些细微之处)。 Also, there is an official way for a task to "yield" to another lower task, I'm not sure about the details though.此外,还有一种正式的方式可以让任务“屈服”到另一个较低的任务,不过我不确定细节。

Important: Using delays (such as vTaskDelay(...)) within callbacks, especially timer callbacks, is a bad idea, since they block other callbacks (within the same task) from executing.重要提示:在回调(尤其是计时器回调)中使用延迟(例如 vTaskDelay(...))是一个坏主意,因为它们会阻止其他回调(在同一任务中)执行。 Hence the best option is to pass information to a separate task running at idle priority (ie priority 0).因此,最好的选择是将信息传递给以空闲优先级(即优先级 0)运行的单独任务。 The reason why a task running at 0 priority doesn't trigger the watchdog, is because the IDLE tasks also have that priority, and FreeRTOS round-robins all those tasks at the same priority, ie gives them interleaving slices of time to execute in "parallel".以 0 优先级运行的任务不会触发看门狗的原因是,空闲任务也具有该优先级,并且 FreeRTOS 以相同的优先级循环所有这些任务,即给它们交错的时间片来执行“平行”。 But when you have two tasks with differing priorites, the higher one ALWAYS executes until it is done or stalls/sleeps, only then will the lower priority run, either until it itself finishes, or until the higher priority task wakes up again and demands execution time.但是,当您有两个具有不同优先级的任务时,较高的任务总是执行直到它完成或停止/休眠,只有这样较低优先级的任务才会运行,直到它自己完成,或者直到较高优先级的任务再次唤醒并要求执行时间。

Update:更新:

Keeping the Arduino loop() empty, will definately trigger the wdt, because it isn't really "empty", because the loop() function is internally wrapped in an infinite loop, so the CPU consumption would actually shoot to 100% without doing anything useful.保持 Arduino loop() 为空,肯定会触发 wdt,因为它并不是真正的“空”,因为 loop() function 内部包裹在一个无限循环中,因此 CPU 消耗实际上会飙升至 100% 而无需执行任何有用的东西。

ESPAsyncWebServer callbacks prevent the watchdog timer from being reset while they run. ESPAsyncWebServer 回调可防止看门狗计时器在运行时被重置。 This means they are not meant for doing any actual processing.这意味着它们不适用于进行任何实际处理。 Register the request and defer processing to the main loop (or some other thread).注册请求并将处理推迟到主循环(或其他线程)。 Have a look at this question for details.有关详细信息,请查看此问题

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM