diff --git a/arch/esp32/task_pinning.c b/arch/esp32/task_pinning.c new file mode 100644 index 00000000..5236173a --- /dev/null +++ b/arch/esp32/task_pinning.c @@ -0,0 +1,67 @@ +/* + * arch/esp32/task_pinning.c + * + * Pins all FreeRTOS tasks with no explicit core affinity (tskNO_AFFINITY) to + * core 0, keeping core 1 exclusively for the Arduino loop (loopTask) where + * LoRa packet processing runs. + * + * Background: + * On ESP32-S3 (dual-core), ARDUINO_RUNNING_CORE=1 pins loopTask to core 1. + * However, ESP-IDF v4 creates MQTT task with tskNO_AFFINITY, so FreeRTOS + * may schedule them on core 1 under load, preempting LoRa processing. + * + * ESP-IDF v4 provides no public API to change a task's core affinity after + * creation (vTaskCoreAffinitySet is IDF v5+ only), and esp_mqtt_client_config_t + * has no core selection field in IDF v4. The linker --wrap mechanism is the + * only way to intercept xTaskCreatePinnedToCore at the call site inside + * precompiled library code. + * + * It's intentionally do not filtered by task name — this keeps the approach + * robust against internal ESP-IDF task name changes and also catches any + * other library tasks that may happen to be created with tskNO_AFFINITY. + * Core 1 is reserved exclusively for loopTask (LoRa / application logic). + * + * Mechanism: + * -Wl,--wrap=xTaskCreatePinnedToCore redirects every call to + * xTaskCreatePinnedToCore (including the xTaskCreate macro which expands to + * it with tskNO_AFFINITY) through __wrap_xTaskCreatePinnedToCore below. + * Tasks that are already explicitly pinned to a core are passed through + * unchanged. + * + * Verified task layout after this patch: + * core 0: mqtt_task(5), httpd(2), tiT/LwIP(18), wifi(23), esp_timer(22), ipc0(24) + * core 1: loopTask(1), ipc1(24), IDLE1(0) + * + * Note: this file is compiled only for [esp32_base] targets (IDF v4). + * ESP32-C6 uses pioarduino (IDF v5) where vTaskCoreAffinitySet() is available. + */ + +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +/* The real function, renamed by the linker to __real_xTaskCreatePinnedToCore. */ +extern BaseType_t __real_xTaskCreatePinnedToCore( + TaskFunction_t pvTaskCode, + const char * const pcName, + const uint32_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pvCreatedTask, + const BaseType_t xCoreID +); + +BaseType_t __wrap_xTaskCreatePinnedToCore( + TaskFunction_t pvTaskCode, + const char * const pcName, + const uint32_t usStackDepth, + void * const pvParameters, + UBaseType_t uxPriority, + TaskHandle_t * const pvCreatedTask, + const BaseType_t xCoreID +) { + /* Pin all tasks with no affinity to core 0. */ + BaseType_t core = (xCoreID == tskNO_AFFINITY) ? 0 : xCoreID; + return __real_xTaskCreatePinnedToCore( + pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pvCreatedTask, core + ); +} diff --git a/platformio.ini b/platformio.ini index 3a8901b9..976213bf 100644 --- a/platformio.ini +++ b/platformio.ini @@ -64,7 +64,9 @@ extra_scripts = build_flags = ${arduino_base.build_flags} -D ESP32_PLATFORM ; -D ESP32_CPU_FREQ=80 ; change it to your need + -Wl,--wrap=xTaskCreatePinnedToCore build_src_filter = ${arduino_base.build_src_filter} + +<../arch/esp32/task_pinning.c> [esp32_ota] lib_deps =