Task Watchdog Timer (TWDT) в ESP-IDF
Сообщение «Task watchdog got triggered» в логе ESP32 — не аппаратный сброс, а сторожевой таймер FreeRTOS-уровня, который заметил, что какая-то задача не отдаёт процессор уже 5 секунд. Разбираемся, кого он сторожит и как его кормить.
Обновлено Редакция GAW
Содержание
Task Watchdog Timer (TWDT, сторожевой таймер задач) — механизм ESP-IDF, который проверяет, что подписанные на него задачи FreeRTOS регулярно получают процессор и сбрасывают таймер. По умолчанию он следит за idle-задачами каждого ядра: если idle-задача не выполнялась дольше таймаута, значит, кто-то монополизировал процессор — спин-цикл по периферии или бесконечный цикл. В отличие от классического сторожевого таймера, TWDT по умолчанию не сбрасывает чип, а печатает предупреждение с backtrace. Разбор самого сообщения и типичных ошибок в коде — в вопросе «Почему ESP32 пишет Task watchdog got triggered».
Параметры по умолчанию
| Параметр | Значение | Опция |
|---|---|---|
| Таймаут | 5 с (диапазон 1–60 с) | CONFIG_ESP_TASK_WDT_TIMEOUT_S |
| Кого сторожит | idle-задачи CPU0 и CPU1 | CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0/1 |
| Инициализация | автоматически при старте | CONFIG_ESP_TASK_WDT_INIT |
| При срабатывании | предупреждение и backtrace, работа продолжается | CONFIG_ESP_TASK_WDT_PANIC выключена |
| Аппаратная основа | MWDT группы таймеров 0, по истечении — прерывание | — |
Голодание idle-задачи ESP-IDF считает вредным само по себе: от неё зависят служебные задачи FreeRTOS. Поэтому TWDT — это не столько защита от зависания, сколько детектор плохо написанного кода, который не уступает процессор.
API
esp_task_wdt_init()— инициализирует TWDT и подписывает idle-задачи; вызывать только после запуска планировщика. Структураesp_task_wdt_config_t:timeout_ms,idle_core_mask,trigger_panic. Повторный вызов меняет таймаут на лету — ESP-IDF советует так делать перед стиранием большой области флеш.esp_task_wdt_add(handle)— подписывает задачу;NULL— текущую. Подписанная задача обязана периодически зватьesp_task_wdt_reset().esp_task_wdt_delete(handle)— отписывает;esp_task_wdt_deinit()— отписывает idle-задачи и выключает TWDT.esp_task_wdt_add_user(),esp_task_wdt_reset_user(),esp_task_wdt_delete_user()— «пользователи»: контроль не задачи, а конкретного участка кода или функции, которая должна вызываться периодически.esp_task_wdt_isr_user_handler()— пользовательская функция, вызываемая из обработчика прерывания при таймауте (ограничения ISR, безESP_LOGx).
Где именно сбрасывать сторожевой таймер в приложении и почему не стоит делать это из таймера, — в статье «Сторожевой таймер: где его сбрасывать». Если задача ждёт события, вместо спин-цикла используют блокирующие вызовы — например, уведомления задачи: заблокированная задача отдаёт процессор, и idle-задача успевает сбросить TWDT.
С чем не путать
- Interrupt Watchdog (IWDT) — другой таймер (группа 1), следит за тем, что не заблокированы прерывания и тик FreeRTOS; его таймаут задаётся в миллисекундах, а при срабатывании по умолчанию вызывается обработчик паники.
- RTC_WDT — аппаратный таймер загрузчика: сторожит время от старта ПЗУ до
app_main()и перезагружает чип. - Четыре стадии у аппаратных MWDT и RTC_WDT: прерывание, сброс ядра, сброс системы, сброс системы вместе с RTC (только RTC_WDT) или отключено — ESP-IDF обычно ставит прерывание на ранние стадии и сброс на последние.
При отладке через OpenOCD оба таймера, TWDT и IWDT, отключаются на первой точке останова и обратно не включаются.
Частые вопросы
Что значит «Task watchdog got triggered: IDLE0 did not reset the watchdog in time»?
Idle-задача ядра 0 не получала процессор дольше таймаута TWDT (по умолчанию 5 с), а значит, какая-то задача с более высоким приоритетом всё это время не уступала — крутилась в цикле ожидания периферии или в бесконечном цикле. В той же строке ESP-IDF печатает, какие задачи выполнялись на каждом ядре в этот момент. Лечится так: добавить в долгий цикл vTaskDelay или ожидание события, вынести тяжёлую работу в задачу с меньшим приоритетом либо, если долгая работа ожидаема, поднять CONFIG_ESP_TASK_WDT_TIMEOUT_S.
Как подписать свою задачу на Task Watchdog и как его сбрасывать?
Вызвать esp_task_wdt_add(NULL) из самой задачи (или передать её handle), после чего задача обязана периодически вызывать esp_task_wdt_reset() — иначе наступит таймаут. Перед удалением задачи её отписывают через esp_task_wdt_delete(). Если нужно контролировать не задачу, а конкретный участок кода, ESP-IDF предлагает «пользователей»: esp_task_wdt_add_user() выдаёт handle, который сбрасывают через esp_task_wdt_reset_user().
Перезагружает ли Task Watchdog чип?
По умолчанию нет: ESP-IDF печатает предупреждение и backtrace, а приложение продолжает работать. Чтобы таймаут вызывал обработчик паники и сброс, включают CONFIG_ESP_TASK_WDT_PANIC (или trigger_panic в структуре esp_task_wdt_config_t при инициализации на лету). При отладке по JTAG таймер не сработает вовсе: OpenOCD отключает TWDT и IWDT на первой же точке останова и обратно не включает.
Источники
- Espressif. ESP-IDF Programming Guide v6.1, ESP32: Watchdogs — Overview: TWDT обнаруживает задачи, которые слишком долго работают без уступки процессора; аппаратно — MWDT_WDT группы таймеров 0, при истечении — прерывание; по умолчанию следит за idle-задачами каждого ядра, подписаться может любая задача; симптом — спин-цикл по периферии или бесконечный цикл; esp_task_wdt_isr_user_handler — пользовательская функция на событие; Usage: esp_task_wdt_init() инициализирует и подписывает idle-задачи, esp_task_wdt_add() подписывает задачу, esp_task_wdt_reset() сбрасывает таймер из задачи, esp_task_wdt_delete() отписывает, esp_task_wdt_deinit(); «пользователи» — esp_task_wdt_add_user(), esp_task_wdt_reset_user(), esp_task_wdt_delete_user(); Configuration: CONFIG_ESP_TASK_WDT_TIMEOUT_S — не меньше самого долгого ожидаемого монопольного участка, меняется на лету через esp_task_wdt_init(); стирание больших областей флеш может вызвать срабатывание; опции CONFIG_ESP_TASK_WDT_EN, _INIT, _CHECK_IDLE_TASK_CPU0, _CHECK_IDLE_TASK_CPU1 включены по умолчанию; по умолчанию при срабатывании печатается предупреждение и backtrace, выполнение продолжается, CONFIG_ESP_TASK_WDT_PANIC переводит в панику и сброс; Timeout Stages: четыре стадии — прерывание, сброс ядра, сброс системы, сброс системы и RTC (только RTC_WDT), отключено; JTAG & Watchdogs: OpenOCD отключает таймеры на точках останова и не включает обратно; текст ошибки «Task watchdog got triggered. The following tasks/users did not reset the watchdog in time: - IDLE0 (CPU 0), Tasks currently running: CPU 0: main, CPU 1: IDLE1»; API: esp_task_wdt_init() только после запуска планировщика; esp_task_wdt_config_t — timeout_ms, idle_core_mask, trigger_panic; esp_task_wdt_status(), esp_task_wdt_reconfigure(), esp_task_wdt_print_triggered_tasks()
- Espressif. ESP-IDF Programming Guide v6.1, ESP32: Configuration Options Reference; Fatal Errors — CONFIG_ESP_TASK_WDT_TIMEOUT_S — диапазон 1–60 с, по умолчанию 5; CONFIG_ESP_TASK_WDT_PANIC — по умолчанию выключено; CONFIG_ESP_TASK_WDT_EN, CONFIG_ESP_TASK_WDT_INIT, CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU0 — включены; описание: голодание idle-задачи вредно, потому что от неё зависят служебные задачи FreeRTOS; Fatal Errors: таймаут TWDT фатален только при CONFIG_ESP_TASK_WDT_PANIC
Нашли ошибку? ·
Смотрите также
- Почему ESP32 пишет «Task watchdog got triggered»?Цикл без vTaskDelay блокирует задачу простоя; тайм-аут по умолчанию 5 с
- Сторожевой таймерWatchdog: сброс МК при зависании программы
- Задача простоя (idle task)Задача FreeRTOS с наименьшим приоритетом: работает, когда больше некому, чистит память удалённых задач и вызывает хук простоя
- Interrupt Watchdog Timer (IWDT)Сторож прерываний ESP-IDF: заблокированный тик FreeRTOS, долгие ISR и критические секции
- Обработчик паники ESP-IDFGuru Meditation Error, дамп регистров, backtrace и четыре варианта поведения после сбоя
- Сторожевой таймерWDT AVR, IWDG и WWDG STM32, таймаут, окно, контроль задач, причина сброса
- Уведомление задачи (task notification)Событие FreeRTOS, отправляемое прямо задаче: быстрее и экономнее семафора, но получатель только один