{getToc} $title={Índice}
Introdução
Task Watchdog Timer (TWDT) é um dos três modos de watchdog presentes no ESP32. Esse modo é responsável por monitorar tasks de maneira isolada, de modo a monitorar instâncias que fiquem muito tempo sem execução. Para o caso descrito no documento, utilizou-se um ESP32-S3 em conjuto com a SDK ESP-IDF v5.1.2 [1].
Funcionamento
Inicialização
O TWDT pode ser habilitado tanto no configurator quanto durante o runtime. Os parâmetros a serem configurados são:
Parâmetro | Descrição | Padrão |
---|---|---|
Enable Task Watchdog Timer | Habilita ou desabilita o TWDT | Habilitado |
Initilize Task Watchdog Timer on Startup |
Inicializa a contagem do TWDT | Habilitado |
Invoke panic handler on Task Watchdog Timer |
Em caso de acionamento do TWDT, o panic handler será invocado ou não |
Desabilitado |
Task Watchdog timeout period (seconds) |
Tempo em segundos do timeout do TWDT | 5 |
Watch CPU0 Idle Task | Supervisiona a task de modo ocioso do ESP (CPU0) |
Habilitado |
Watch CPU1 Idle Task | Supervisiona a task de modo ocioso do ESP (CPU1) |
Habilitado |
Para abrir o menuconfig, basta executar o comando "idf.py menuconfig" em um ambiente previamente preparado com o ESP-IDF. A configuração do TWDT pode ser encontrada em "Component config" → "ESP System Settings", onde são encontradas todas as configurações do recurso.
Configuração em runtime
Inicialmente, para seu funcionamento deve-se incluir o header "esp_task_wdt.h". A configuração em runtime é feita utilizado a função "esp_task_wdt_init( config )", sendo config um ponteiro de struct com os parâmetros a serem configurados. O parâmetro "Enable Task Watchdog Timer" deve estar habilitado para que essa inicialização funcione corretamente.
```C
typedef struct {
uint32_t timeout_ms; /**< TWDT timeout duration in milliseconds */
uint32_t idle_core_mask; /**< Mask of the cores who's idle task should be subscribed on initialization */
bool trigger_panic; /**< Trigger panic when timeout occurs */
} esp_task_wdt_config_t;
```
Uma vez configurado o TWDT, ou mesmo se a o parâmetro "Initilize Task Watchdog Timer on Startup" estiver previamente habilitado via menuconfig, a função "esp_task_wdt_init" irá retornar "ESP_ERR_INVALID_STATE", indicando que houve um erro ao inicializar. Portanto, nesses casos é necessário utilizar a função "esp_task_wdt_reconfigure( config ) ".
Incluindo uma task ao monitoramento
Para incluir uma task para ser monitorada, deve-se utilizar a função "esp_task_wdt_add(NULL)", invocando-a na task de interesse.
O reset do TWDT é feito invocando a função "esp_task_wdt_reset(NULL)", sendo necessário para evitar o gatilho do TWDT.
Caso seja de interesse a desinscrição da task, ou seja, deixar de monitorar a mesma, utiliza-se a função "esp_task_wdt_delete(NULL)", invocada também pela task de interesse.
Tratamento da task em caso de gatilho e Panic Handler
Se o timeout do TWDT for acionado, o comportamento padrão é apenas printar um warning com as informações sobre os registros relacionados ao watchdog. Porém, caso for de interesse do usuário, é possível habilitar o Panic Handler, ocasionando assim um reset na placa. O comportamento do Panic Handler pode ser configurado através do configurator do ESP.
Após o reboot, é possível identificar a causa a partir da função "esp_reset_reason()", que deve ser igual a ESP_RST_TASK_WDT, se a causa do reset for devido ao TWDT.
Exemplos
Abaixo estão alguns cenários para ilustrar as aplicações possíveis com o Task Watchdog Timer.
Através do menuconfig, foram feitas as configurações abaixo. Task WDT habilitado, mas não inicializado e com o Panic Handler configurado como "Print registers and reboot".
Este primeiro cenário simula o funcionamento esperado de uma aplicação real [2].
```C
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/FreeRTOSConfig.h"
#include "freertos/event_groups.h"
#include "esp_system.h"
#include "esp_log.h"
#include "esp_task_wdt.h"
#define TAG "[MAIN]"
void foo_task(void *pvParameters)
{
// Habilita o monitoramento nessa task
esp_task_wdt_add(NULL);
while(true)
{
// Realimenta o WDT
esp_task_wdt_reset();
vTaskDelay(pdMS_TO_TICKS(1000));
ESP_LOGI("Task", "OK");
}
}
void app_main(void)
{
esp_err_t err;
esp_task_wdt_config_t wdt_config={0};
// Seta o timeout para 3 segundos
wdt_config.timeout_ms = 3000;
// Monitorando idle tasks de todos os nucleos
wdt_config.idle_core_mask = (1 << portNUM_PROCESSORS) - 1;
// Gatilho do panic ativado
wdt_config.trigger_panic = true;
// Verifica se TWDT ainda nao foi inicializado
#if !CONFIG_ESP_TASK_WDT_INIT
err = esp_task_wdt_init(&wdt_config);
ESP_LOGI(TAG, "Initialization %s", (err == ESP_OK)?"success":"fail");
#else
// Se ja foi inicializado, apenas reconfigura
err = esp_task_wdt_reconfigure(&wdt_config);
ESP_LOGI(TAG, "Reconfigure %s", (err == ESP_OK)?"success":"fail");
#endif
// Verifica se foi resetado pelo TWDT
if(esp_reset_reason() == ESP_RST_TASK_WDT)
{
ESP_LOGI(TAG, "Reset caused by Task WDT");
}
// Inicializa a Task
ESP_LOGI(TAG, "Foo task created");
xTaskCreate(foo_task, "Foo task", 2048, NULL, 1, NULL);
}
```
No exemplo acima, inicializa-se o TWDT e é adicionado ao seu monitoramento a task "foo_task". Como é possível observar, o WDT é realimentado a cada 1s, e como o contador é resetado a cada 3s esse evento acaba por não acontecer. Isso é verificável através do log, pois em momento algum o panic handler é invocado.
```C
void foo_task(void *pvParameters)
{
// Habilita o monitoramento nessa task
esp_task_wdt_add(NULL);
while(true)
{
// Realimentacao do WDT removida
// esp_task_wdt_reset();
vTaskDelay(pdMS_TO_TICKS(1000));
ESP_LOGI("Task", "OK");
}
}
```
```bash
...
E (3320) task_wdt: Task watchdog got triggered. The following tasks/users did not reset the watchdog in time:
E (3320) task_wdt: - Foo task (CPU 0/1)
E (3320) task_wdt: Tasks currently running:
E (3320) task_wdt: CPU 0: IDLE
E (3320) task_wdt: CPU 1: IDLE
...
```
Dentre as tasks monitoradas, é listada a task em que o erro aconteceu (Foo task) e as que ainda estão em execução (CPU0: IDLE e CPU1: IDLE).
Não só isso, como também após o reboot, é possível ver que o reset aconteceu devido ao TWDT:
```bash
...
I(320) [MAIN]: Initialization success
I(330) [MAIN]: Reset cause by Task WDT
I(330) [MAIN]: Foo task created
...
```