ESP32: pin all tskNO_AFFINITY tasks to core 0 via linker wrap
On dual-core ESP32-S3 (ARDUINO_RUNNING_CORE=1), the Arduino loop and LoRa processing run exclusively on core 1. ESP-IDF v4 creates mqtt_task with tskNO_AFFINITY, meaning FreeRTOS may schedule them on core 1 under load, preempting the LoRa loop. ESP-IDF v4 provides no public API to change a task's core affinity after creation (vTaskCoreAffinitySet is IDF v5+ only), and the esp_mqtt_client config struct has no task_core_id field. The precompiled Arduino-ESP32 framework cannot be patched via sdkconfig. Instead, use the GCC/LD --wrap linker mechanism to intercept every call to xTaskCreatePinnedToCore. Any task created with tskNO_AFFINITY is redirected to core 0. Tasks that are already explicitly pinned (Wi-Fi driver, LwIP, httpd, esp_timer, ipc0/ipc1) are passed through unchanged. We intentionally do not filter by task name. Pinning all unpinned tasks makes the approach robust against internal ESP-IDF task name changes and catches any future tasks that may be added with tskNO_AFFINITY. Verified task layout after the change: loopTask pri=1 core=1 (Arduino loop / LoRa — unchanged) mqtt_task pri=5 core=0 (was tskNO_AFFINITY, now pinned) httpd pri=2 core=0 (core_id set explicitly in WebPanelServer) tiT pri=18 core=0 (LwIP, already pinned by ESP-IDF) wifi pri=23 core=0 (Wi-Fi driver, already pinned) esp_timer pri=22 core=0 (already pinned) ipc0/ipc1 pri=24 core=0/1 (IPC, already pinned per-core) The -Wl,--wrap flag and arch/esp32/task_pinning.c are added only to [esp32_base] (IDF v4). The ESP32-C6 pioarduino target (IDF v5) is unaffected and can use vTaskCoreAffinitySet() if needed in the future.
This commit is contained in:
@@ -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
|
||||
);
|
||||
}
|
||||
@@ -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 =
|
||||
|
||||
Reference in New Issue
Block a user