Task Watchdog Timer




{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
Configuração via menuconfig
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.


Fazendo uma simples alteração, na task, ao remover a alimentação do WDT, podemos observar um comportamento diferente.
```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");
    }
}
```

Dessa vez, como o WDT não está sendo realimentado, após invocação do Panic Handler, são printadas as seguintes informações:

```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
...
```

Referências

Postar um comentário

Deixe seu comentário ou sua sugestão!

Postagem Anterior Próxima Postagem

Formulário de contato