
48
Enabling runtime statistics collection in ESP32-arduino
The main issue is that in the pre-built ESP32-arduino library, the functions for collecting runtime statistics (vTaskGetRunTimeStats
and vTaskList
) are disabled. The library is used as provided in Platformio. Guides on building your own libraries are outdated (404), and there aren't any readily available instructions. Moreover, issues regarding adding these functions don't address the problem since they are related to using a third-party library.
To add these functions, you need to rebuild the library with modified parameters. We will use Ubuntu as the operating system (Windows users can launch a VM with WSL).
Esp32 Arduino Library Builder
Instructions for installing the builder and compiling can be found on the official website. First, install the dependencies:
sudo apt-get install git wget curl libssl-dev libncurses-dev flex bison gperf cmake ninja-build ccache jq python3
sudo pip install --upgrade pip
pip install --user setuptools pyserial click cryptography future pyparsing pyelftools
Next, you can clone the repository and start the build process. Keep in mind that downloading all dependencies and building takes roughly an hour.
Then, you might as well discard the build output because the versions of the builder and all used libraries differ, making them unusable directly in Platformio.
We need specific versions of each package, but the builder does not provide that option out of the box, so we have to assist it. You can check the current package versions used in Platformio by looking at ~/.platformio/packages/framework-arduinoespressif32/tools/sdk/version.txt
. For me, the file looks like this:
esp-idf: v4.4.7 38eeba213a
arduino: idf-38eeba213a d75795f5
esp-dl: master 0632d24
esp-rainmaker: master d8e9345
esp32-camera: master f0bb429
esp_littlefs: master 41873c2
espressif__esp-dsp: master 9b4a8b4
tinyusb: master a0e5626bc
By some miracle, based on one of the commit hashes from Platformio, I found a Japanese article that explains what exactly needs to be done to rebuild the library. The builder was installed for a reason, but here come the adventures – you need to manually install each of these libraries.
First, clone the builder repository and switch to the appropriate version:
git clone https://github.com/espressif/esp32-arduino-lib-builder
cd esp32-arduino-lib-builder
git switch release/v4.4
Next, to lock each library version, comment out the automatic component update in the builder (in build.sh
, around line 84):
84 #echo "* Installing/Updating ESP-IDF and all components..."
85 # update components from git
86 #./tools/update-components.sh
87 #if [ $? -ne 0 ]; then exit 1; fi
Now, you will have to manually download all components, including ESP-IDF and the Arduino core for ESP32.
TL;DR: Install all dependencies with one command:
git clone -b v4.4.7 --recursive https://github.com/espressif/esp-idf.git && git -C ./esp-idf/ reset --hard 38eeba213a && git clone -b 2.0.17 --recursive https://github.com/espressif/arduino-esp32 ./components/arduino && git -C ./components/arduino reset --hard d75795f5 && git clone --recursive https://github.com/espressif/esp32-camera ./components/esp32-camera && git -C ./components/esp32-camera reset --hard f0bb429 && git clone --recursive https://github.com/espressif/esp-dl.git ./components/esp-dl && git -C ./components/esp-dl reset --hard 0632d24 && git clone --recursive https://github.com/joltwallet/esp_littlefs.git ./components/esp_littlefs && git -C ./components/esp_littlefs reset --hard 41873c2 && git -C ./components/esp_littlefs submodule update --init --recursive && git clone --recursive https://github.com/espressif/esp-rainmaker.git ./components/esp-rainmaker && git -C ./components/esp-rainmaker reset --hard d8e9345 && git -C ./components/esp-rainmaker submodule update --init --recursive && git clone --recursive https://github.com/espressif/esp-dsp.git ./components/espressif__esp-dsp && git -C ./components/espressif__esp-dsp reset --hard 9b4a8b4 && git clone --recursive https://github.com/hathach/tinyusb.git ./components/arduino_tinyusb/tinyusb && git -C ./components/arduino_tinyusb/tinyusb reset --hard a0e5626bc
Below is a breakdown for each library:
esp-idf
git clone -b v4.4.7 --recursive https://github.com/espressif/esp-idf.git
git -C ./esp-idf/ reset --hard 38eeba213a
arduino-esp32
git clone -b 2.0.17 --recursive https://github.com/espressif/arduino-esp32 ./components/arduino
git -C ./components/arduino reset --hard d75795f5
esp32-camera
git clone --recursive https://github.com/espressif/esp32-camera ./components/esp32-camera
git -C ./components/esp32-camera reset --hard f0bb429
esp-dl
git clone --recursive https://github.com/espressif/esp-dl.git ./components/esp-dl
git -C ./components/esp-dl reset --hard 0632d24
esp_littlefs
git clone --recursive https://github.com/joltwallet/esp_littlefs.git ./components/esp_littlefs
git -C ./components/esp_littlefs reset --hard 41873c2
git -C ./components/esp_littlefs submodule update --init --recursive
esp-rainmaker
git clone --recursive https://github.com/espressif/esp-rainmaker.git ./components/esp-rainmaker
git -C ./components/esp-rainmaker reset --hard d8e9345
git -C ./components/esp-rainmaker submodule update --init --recursive
espressif__esp-dsp
git clone --recursive https://github.com/espressif/esp-dsp.git ./components/espressif__esp-dsp
git -C ./components/espressif__esp-dsp reset --hard 9b4a8b4
tinyusb
git clone --recursive https://github.com/hathach/tinyusb.git ./components/arduino_tinyusb/tinyusb
git -C ./components/arduino_tinyusb/tinyusb reset --hard a0e5626bc
Build
Now, you can start the build for ESP32 (if you have a different board revision, adjust accordingly):
./build.sh -t esp32
The build takes a significant amount of time. If a directory named out
is created after completion, the build was successful:
$ ls out
package_esp32_index.template.json platform.txt tools
Sdkconfig
If you wish, you can view the current sdkconfig settings:
. ./esp-idf/export.sh
idf.py menuconfig
Note: menuconfig
is only for viewing the build settings; you cannot edit the settings here.
The option you need is located under Component config → FreeRTOS → Enable FreeRTOS to collect run time stats. As you can see, by default, this flag is disabled:
You need to modify the default sdkconfig. For ESP32, edit configs/defconfig.esp32
. For ESP32-C2 and ESP32-C3, the resulting filenames are defconfig.esp32c2
and defconfig.esp32c3
respectively (other revisions are available alongside).
CONFIG_BTDM_CTRL_MODE_BTDM=y
CONFIG_BTDM_SCAN_DUPL_CACHE_SIZE=20
CONFIG_BT_BTC_TASK_STACK_SIZE=8192
CONFIG_BT_BTU_TASK_STACK_SIZE=8192
CONFIG_BT_CLASSIC_ENABLED=y
CONFIG_BT_A2DP_ENABLE=y
CONFIG_BT_SPP_ENABLED=y
CONFIG_BT_HFP_ENABLE=y
CONFIG_BT_STACK_NO_LOG=y
CONFIG_BT_BLE_DYNAMIC_ENV_MEMORY=y
CONFIG_ESP32_SPIRAM_SUPPORT=y
CONFIG_SPIRAM_OCCUPY_HSPI_HOST=y
CONFIG_ESP32_ULP_COPROC_ENABLED=y
CONFIG_ESP32_XTAL_FREQ_AUTO=y
# CONFIG_ESP_TASK_WDT_CHECK_IDLE_TASK_CPU1 is not set
CONFIG_FREERTOS_FPU_IN_ISR=y
# CONFIG_USE_WAKENET is not set
# CONFIG_USE_MULTINET is not set
CONFIG_TWAI_ERRATA_FIX_BUS_OFF_REC=y
CONFIG_TWAI_ERRATA_FIX_TX_INTR_LOST=y
CONFIG_TWAI_ERRATA_FIX_RX_FRAME_INVALID=y
CONFIG_TWAI_ERRATA_FIX_RX_FIFO_CORRUPT=y
CONFIG_FREERTOS_WATCHPOINT_END_OF_STACK=y
CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS=y
I added the last line to enable runtime statistics collection; all other settings were already in the file.
Rebuild everything:
./build.sh -t esp32
After the build completes, check the build configuration again (remember to export the environment each time):
. ./esp-idf/export.sh
idf.py menuconfig
If the flag is enabled, then everything is set up correctly.
Platformio
The final step is to copy our library into Platformio.
First, backup the existing library and then copy the built library into Platformio:
mv -r ~/.platformio/packages/framework-arduinoespressif32/tools/sdk/ ./platformio-sdk-backup/
cp -r ./out/tools/sdk ~/.platformio/packages/framework-arduinoespressif32/tools
You can now switch to Platformio and try compiling your firmware; the functions should be available.
Below is a minimal code example you can use for testing:
#include <Arduino.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
void TaskLog(void *pvParameters) {
static const int statsBufferSize = 1024;
static char statsBuffer[statsBufferSize];
static const int listBufferSize = 1024;
static char listBuffer[listBufferSize];
while (true) {
Serial.println("\n============ Task Stats ============");
// Get runtime stats for CPU usage
// This requires configGENERATE_RUN_TIME_STATS to be enabled
vTaskGetRunTimeStats(statsBuffer);
Serial.println("Run Time Stats:");
Serial.println(statsBuffer);
// Get task list with state, priority, stack, and task number
// Note: vTaskList output depends on configuration and may not include core affinities by default
vTaskList(listBuffer);
Serial.println("Task List:");
Serial.println(listBuffer);
Serial.println("=====================================");
vTaskDelay(5000 / portTICK_PERIOD_MS);
}
}
void setup() {
// Initialize the serial port
Serial.begin(115200);
Serial.println("ESP32 example with stats");
// Create a task for logging on core 1
xTaskCreatePinnedToCore(
TaskLog, // Task function
"Log", // Task name
2048, // Increased stack size for additional calculations
NULL, // Task parameters
1, // Task priority
NULL, // Task handle
1 // Core on which the task will run
);
}
void loop() {
delay(1000);
}
Every 5 seconds, the console will output CPU usage statistics for each task, along with the task list:
============ Task Stats ============
Run Time Stats:
Log 11614 <1%
IDLE0 20176378 97%
IDLE1 20618561 99%
loopTask 1060 <1%
esp_timer 24 <1%
Tmr Svc 22 <1%
ipc1 47571 <1%
ipc0 32460 <1%
Task List:
Log X 1 624 10
IDLE0 R 0 552 5
IDLE1 R 0 548 6
loopTask B 1 7292 8
Tmr Svc B 1 1584 7
ipc1 B 24 484 2
ipc0 B 24 508 1
esp_timer S 22 4096 3
=====================================
In this convoluted way, you can change the parameters of the esp32-arduino library for use in Platformio.
The range of available parameters goes far beyond just enabling the runtime statistics functions; it's a pity that IDEs don't offer a more convenient way to configure the core libraries.
In solving this task, unfortunately, none of the AI models were of any help, even though I persistently tried to get o1, sonnet 3.5, and o3-mini to assist me. Google, digging through forum archives, and the only article on the subject in Japanese (I never cease to be amazed by this fact) still provide a perfectly workable pipeline.
At least for now, at least for embedding.
Afterword
Why might you need this runtime statistics collection?
- To optimize your firmware and overall system performance.
- To compare different approaches.
- Simply out of curiosity about how various functions work.
I originally planned to write a benchmark for the ESP32-CAM (and I haven't completely abandoned this idea) to compare different video streaming and control protocols for the device. You'll learn more in the next articles.
The point is, I struggled with obtaining runtime statistics using the standard ESP-IDF functions on the Arduino framework. I switched to using ESP-IDF in Platformio and configured the statistics output – it was fantastic.
Later, while using ESP-IDF, I tried a simple WiFi connection following the Getting Started guide, and was overwhelmed by how low-level it was compared to the Arduino libraries, so I decided to steer clear of ESP-IDF afterward.
However, the need for runtime statistics kept nagging at me, so I implemented a simple custom collector like this:
TaskStats httpTaskStats = {"HTTP Server", 1, 0, 0, 0, 0, false};
void updateTaskStats(TaskStats &stats, bool startActive) {
unsigned long currentTime = micros();
unsigned long timeDiff = currentTime - stats.lastCheck;
if (startActive && !stats.isActive) {
stats.lastActive = currentTime;
stats.isActive = true;
}
else if (!startActive && stats.isActive) {
stats.activeTime += currentTime - stats.lastActive;
stats.isActive = false;
}
stats.totalTime += timeDiff;
stats.lastCheck = currentTime;
}
That is, by manually calling this function at the beginning and end of a task using micros()
, I attempted to measure each task's CPU usage. The statistics over a 5-second period came out like this:
Task Usage Statistics:
Blink (Core 0): 0.0066% (Active: 267 us / Total: 4021757 us)
HTTP Server (Core 1): 16.6422% (Active: 831341 us / Total: 4995383 us)
Log (Core 1): 0.0000% (Active: 0 us / Total: 5000384 us)
Aside from the development challenges—since this manual method only accounts for user-defined tasks—notice that the HTTP server's displayed CPU usage was sometimes as high as 50% without any requests. When a request did occur, the percentage even dropped.
Now, compare that to the statistics obtained using the built-in function:
HttpServer 1382186 <1%
In reality, the HTTP server consumes almost no CPU time. The discrepancy arises because, unlike the manual method, the built-in function accounts for the fact that the HTTP server does not occupy its task continuously while waiting for requests, relying instead on timers or events. Hence, the custom implementation fails to accurately capture its usage.
In conclusion, although it's a convoluted process, this method allows you to modify the ESP32-arduino library parameters for use in Platformio/Arduino IDE.
UPD
The PlatformIO community suggested using pioarduino, a community fork of platform-espressif32, to enable support for the latest Espressif32 Arduino 3.x in PlatformIO. This version includes built-in support for statistical functions. PlatformIO does not yet officially support Arduino 3.x. Additionally, there are quite significant differences between these versions.
No comments yet
-
Gift box with AI
The time has come when AI can not only respond in chats and be used in digital… -
Making images clickable on the website
I often insert high-quality images, but until now there wasn't a convenient way… -
Enabling runtime statistics collection in ESP32-arduino
This article will focus on the FreeRTOS functions vTaskGetRunTimeStats and vTas… -
Adding view counter to bolt CMS
Let's create a view counter for articles in Bolt CMS. We'll explore possible im… -
Hidden Disc Holder
Looking for a place to store your disc collection, and find shelf storage borin… -
Student48
For LSTU Computer Science graduates of 2014-2021, this name means a lot - let's…