ESP32 multicore task scheduling and its watchdog timers (WDT)

ESP32 task scheduling

Introduction

As outlined in the programming guide, the ESP-IDF FreeRTOS derivate introduces a small but significant change to the vanilla FreeRTOS round robin scheduler: Tasks in the ESP-IDF FreeRTOS are designed to run on a particular core.

Saying this, the ESP-IDF FreeRTOS has the notion of a PRO_CPU and an APP_CPU:

The vanilla FreeRTOS is designed to run on a single core. However the ESP32 is dual core containing a Protocol CPU (known as CPU 0 or PRO_CPU ) and an Application CPU (known as CPU 1 or APP_CPU ). The two cores are identical in practice and share the same memory. This allows the two cores to run tasks interchangeably between them.

Arduino on ESP32

As you might have imagined already, the Arduino program will be started as a FreeRTOS task. By using the CPU pinning outlined above, the environment will ensure that the well known main() and loop() functions of Arduino Core will execute on the same CPU core, which is CPU 1 by default, see https://github.com/espressif/arduino-esp32/blob/1.0.1/cores/esp32/main.cpp:

#if CONFIG_FREERTOS_UNICORE
#define ARDUINO_RUNNING_CORE 0
#else
#define ARDUINO_RUNNING_CORE 1
#endif

xTaskCreatePinnedToCore(loopTask, "loopTask", 8192, NULL, 1, &loopTaskHandle, ARDUINO_RUNNING_CORE);

Take care!

The cool thing about this design choice is that it will give you a breakfast for free as basic programs written for Arduino will still run contained to a single CPU core, so they will not experience being scheduled between both CPU cores.

In that way, these programs will provide a fully linear execution flow behaving like being executed on AVRs or other less-featured microcontrollers as usual. Otherwise, you would have to protect each and every variable in your program sketch against memory corruption and race conditions.

Saying that, you will nevertheless have to Protect code when accessing shared resources like bus peripherals and other hardware attached to the board, especially when involving ISRs.

Do it yourself

If you want to spawn tasks on a specific core on your own behalf, these tutorials will get you started:

1 Like

ESP32 watchdog timers (WDT)

While we already learned to feed the ESP8266 watchdog timer appropriately (see My ESP crashes running some code. How to troubleshoot it? β€” ESP8266 Arduino Core 2.5.0 documentation). In this spirit, we should also learn about the ESP32.

Please note that knowing about the WDT subsystem really helps when running into runtime problems you might diagnose as weird behavior, so this is equally important as Protect code when accessing shared resources.

Overview

The ESP-IDF has support for two types of watchdogs: The Interrupt Watchdog Timer and the Task Watchdog Timer (TWDT). The Interrupt Watchdog Timer and the TWDT can both be enabled using make menuconfig , however the TWDT can also be enabled during runtime. The Interrupt Watchdog is responsible for detecting instances where FreeRTOS task switching is blocked for a prolonged period of time. The TWDT is responsible for detecting instances of tasks running without yielding for a prolonged period.

– Watchdogs β€” ESP-IDF Programming Guide v4.0-dev-141-g106dc0590 documentation

See also:

Observations

How the watchdogs will behave on ESP32 in respect to Arduino is still in flux, as you can see when following some comments from upstream at https://github.com/espressif/arduino-esp32/issues/595 and beyond.

GitHub issues

Sep 2, 2017 [me-no-dev]

I don’t know what is going on here, but I can tell you that I will not add vTaskDelay(10) after each loop. For you to trigger WDT in the loop, you have to be running as IDF component and have WDT enabled for the Idle task on Core1. Those are disabled for Arduino in order to give to most Arduino-like behavior under FreeRTOS. You can add vTaskDelay(10) to your loop if you need it and achieve the same result.

10ms between each loop is ages in processor time ;) 2.4 million wasted CPU cycles, to be exact. Nothing else is running on Core1 ;)

– goto

Dec 28, 2018 [me-no-dev]

Arduino does have access to all IDF things, but is built with WDT disabled for Core 1 (where loop runs). This is made on purpose to allow code written for other platforms to run on ESP32 without constantly causing resets. If you want to trigger WDT, then spin a task on core 0

– goto

Jan 5, 2019 [me-no-dev]

WDT is now enabled to reset the board (by default on Core0 only, but can be enabled for Core1 or the loop task also). Update your Arduino and try again ;)

– goto

Only recently, CONFIG_TASK_WDT_PANIC=y became the default per


References

https://github.com/espressif/arduino-esp32/issues/595
https://github.com/espressif/arduino-esp32/issues/341