Skip to content

Commit 8a6db60

Browse files
committed
First version
1 parent fb3ef8e commit 8a6db60

12 files changed

+608
-1
lines changed

.github/workflows/cpp-linter.yml

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: cpp-linter
2+
3+
on:
4+
push:
5+
paths-ignore: "doc/**"
6+
pull_request:
7+
paths-ignore: "doc/**"
8+
9+
jobs:
10+
cpp-linter:
11+
runs-on: ubuntu-latest
12+
steps:
13+
- uses: actions/checkout@v3
14+
- uses: cpp-linter/cpp-linter-action@master
15+
id: linter
16+
env:
17+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
18+
with:
19+
style: file
20+
21+
- name: Fail fast?!
22+
if: steps.linter.outputs.checks-failed > 0
23+
run: |
24+
echo "Some files failed the linting checks!"
25+
# for actual deployment
26+
# run: exit 1

.gitignore

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# Build results
2+
build/
3+
4+
# VSCode
5+
.vscode/
6+
7+
# Project
8+
sdkconfig

README.md

+76-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,76 @@
1-
# ESP32_Task_Monitor
1+
[![GitHub](https://img.shields.io/github/license/VPavlusha/ESP32_Task_Monitor?color=blue&label=License&logo=github)](LICENSE)
2+
[![cpp-linter](https://github.com/VPavlusha/ESP32_Task_Monitor/actions/workflows/cpp-linter.yml/badge.svg)](https://github.com/VPavlusha/ESP32_Task_Monitor/actions/workflows/cpp-linter.yml)
3+
[![GitHub release (latest by date)](https://img.shields.io/github/v/release/VPavlusha/ESP32_Task_Monitor?label=Release&logo=github)](https://github.com/VPavlusha/ESP32_Task_Monitor/releases)
4+
[![Stand With Ukraine](https://raw.githubusercontent.com/vshymanskyy/StandWithUkraine/main/badges/StandWithUkraine.svg)](https://stand-with-ukraine.pp.ua)
5+
[![Made in Ukraine](https://img.shields.io/badge/Made_in-Ukraine-ffd700.svg?labelColor=0057b7)](https://stand-with-ukraine.pp.ua)
6+
---
7+
8+
# ESP32 Task Monitor
9+
The ESP32 Task Monitor is a project that provides a simple way to monitor the tasks running on an ESP32 microcontroller in real-time. The project provides a tool for developing and debugging complex embedded systems.
10+
11+
#### Table of Contents
12+
 [1. Features](#1-features)
13+
 [2. ESP32 Task Monitor](#2-esp32-task-monitor)
14+
 [3. Basic Usage](#3-basic-usage)
15+
 [4. Getting Started](#4-getting-started)
16+
 [5. Example](#5-example)
17+
 [6. Contributing](#6-contributing)
18+
 [7. License](#7-license)
19+
20+
## 1. Features
21+
- ESP-IDF v5.0.2
22+
- Real-time monitoring of running multiple tasks for ESP32 microcontrollers.
23+
- Written in C language.
24+
- MIT License.
25+
26+
## 2. ESP32 Task Monitor
27+
<img src="./doc/img/task_monitor.svg" alt="Task Monitor" width="650"/>
28+
29+
## 3. Basic Usage
30+
**NOTE:** USE_TRACE_FACILITY and GENERATE_RUN_TIME_STATS must be defined as **1** in FreeRTOSConfig.h for this API function task_monitor() to be available.
31+
```C
32+
// 1. Include header:
33+
#include "task_monitor.h"
34+
35+
void app_main(void)
36+
{
37+
// 2. Start task monitor:
38+
task_monitor();
39+
}
40+
```
41+
## 4. Getting Started
42+
To get started with the ESP32 Task Monitor project, you'll need an ESP32 microcontroller and a host computer running Python. You'll also need to install the ESP-IDF development framework and the required Python packages.
43+
44+
### 4.1 Clone the project repository:
45+
```C
46+
git clone https://github.com/VPavlusha/ESP32_Task_Monitor.git
47+
```
48+
### 4.2 Build the project:
49+
```C
50+
cd ESP32_Task_Monitor/example
51+
idf.py build
52+
```
53+
### 4.3 Flash onto your ESP32 microcontroller:
54+
```C
55+
idf.py -p PORT [-b BAUD] flash
56+
```
57+
Replace PORT with your ESP32 board’s serial port name.
58+
You can also change the flasher baud rate by replacing BAUD with the baud rate you need. The default baud rate is 460800.<br/>
59+
### 4.4 Monitor the output:
60+
```C
61+
idf.py -p <PORT> monitor
62+
```
63+
Do not forget to replace PORT with your serial port name.
64+
65+
More information how to build project: [ESP-IDF Programming Guide](https://docs.espressif.com/projects/esp-idf/en/v5.0.1/esp32/get-started/start-project.html).
66+
67+
## 5. Example
68+
This project includes an [example](https://github.com/VPavlusha/ESP32_Task_Monitor/tree/main/example) that showcases the functionality of the Task Monitor library. This example provides a practical demonstration of how to use the Task Monitor API to monitor tasks in your own applications.
69+
70+
## 6. Contributing
71+
Contributions to the ESP32 Task Monitor project are welcome. If you find a bug or have a feature request, please submit an issue on the project's GitHub page. If you'd like to contribute code, please submit a pull request.
72+
73+
## 7. License
74+
The ESP32 Task Monitor project is licensed under the MIT License. See the [MIT license] file for more information.
75+
76+
[MIT license]: http://www.opensource.org/licenses/mit-license.html

doc/img/task_monitor.svg

+3
Loading

example/CMakeLists.txt

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# The following lines of boilerplate have to be in your project's
2+
# CMakeLists in this exact order for cmake to work correctly
3+
cmake_minimum_required(VERSION 3.16)
4+
5+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
6+
project(task_monitor)

example/main/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
idf_component_register(SRCS "main.c"
2+
"task_monitor.c"
3+
INCLUDE_DIRS "" "include")
4+

example/main/Kconfig.projbuild

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
menu "Task Monitor Project Configuration"
2+
3+
config FREERTOS_USE_TRACE_FACILITY
4+
bool "Enable trace facility"
5+
default y
6+
help
7+
Enables additional structure members and functions to assist with execution visualization and tracing
8+
(see configUSE_TRACE_FACILITY documentation for more details).
9+
10+
config FREERTOS_GENERATE_RUN_TIME_STATS
11+
bool "Enable trace facility"
12+
default y
13+
help
14+
Enables collection of run time statistics for each task
15+
(see configGENERATE_RUN_TIME_STATS documentation for more details).
16+
17+
endmenu

example/main/include/task_monitor.h

+50
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/*
2+
* MIT License
3+
*
4+
* Copyright (c) 2023 Volodymyr Pavlusha
5+
*
6+
* Permission is hereby granted, free of charge, to any person obtaining a copy
7+
* of this software and associated documentation files (the "Software"), to deal
8+
* in the Software without restriction, including without limitation the rights
9+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10+
* copies of the Software, and to permit persons to whom the Software is
11+
* furnished to do so, subject to the following conditions:
12+
*
13+
* The above copyright notice and this permission notice shall be included in all
14+
* copies or substantial portions of the Software.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22+
* SOFTWARE.
23+
*/
24+
25+
#ifndef TASK_MONITOR_H_
26+
#define TASK_MONITOR_H_
27+
28+
#include "esp_err.h"
29+
30+
#ifdef __cplusplus
31+
extern "C" {
32+
#endif
33+
34+
// NOTE: USE_TRACE_FACILITY and GENERATE_RUN_TIME_STATS must be defined as 1
35+
// in FreeRTOSConfig.h for this API function task_monitor() to be available.
36+
37+
/**
38+
* @brief Create a new task to monitor the status and activity of other FreeRTOS tasks in the system
39+
*
40+
* @return
41+
* - ESP_OK Success.
42+
* - ESP_ERR_NO_MEM Out of memory.
43+
*/
44+
esp_err_t task_monitor(void);
45+
46+
#ifdef __cplusplus
47+
}
48+
#endif
49+
50+
#endif // TASK_MONITOR_H_

example/main/main.c

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#include "task_monitor.h"
2+
3+
void app_main(void)
4+
{
5+
// NOTE: USE_TRACE_FACILITY and GENERATE_RUN_TIME_STATS must be defined as 1
6+
// in FreeRTOSConfig.h for this API function task_monitor() to be available.
7+
task_monitor();
8+
}

example/main/task_monitor.c

+180
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
#include "task_monitor.h"
2+
3+
#include <stdio.h>
4+
5+
#include "freertos/FreeRTOS.h"
6+
#include "freertos/task.h"
7+
8+
#include "esp_err.h"
9+
#include "esp_heap_caps.h"
10+
#include "esp_system.h"
11+
#include "esp_timer.h"
12+
#include "sdkconfig.h"
13+
14+
#if !defined(CONFIG_FREERTOS_USE_TRACE_FACILITY) || !defined(CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS)
15+
#error "USE_TRACE_FACILITY and GENERATE_RUN_TIME_STATS must be defined!"
16+
#endif
17+
18+
#define COLOR_BLACK "30"
19+
#define COLOR_RED "31"
20+
#define COLOR_GREEN "32"
21+
#define COLOR_YELLOW "33"
22+
#define COLOR_BLUE "34"
23+
#define COLOR_PURPLE "35"
24+
#define COLOR_CYAN "36"
25+
#define COLOR_WHITE "37"
26+
27+
#define COLOR(COLOR) "\033[0;" COLOR "m"
28+
#define RESET_COLOR "\033[0m"
29+
#define UNDERLINE "\033[4m" // TODO: FIX. Does not work!
30+
#define BOLD "\033[1m" // TODO: FIX. Does not work!
31+
32+
#define BLACK COLOR(COLOR_BLACK)
33+
#define RED COLOR(COLOR_RED)
34+
#define GREEN COLOR(COLOR_GREEN)
35+
#define YELLOW COLOR(COLOR_YELLOW)
36+
#define BLUE COLOR(COLOR_BLUE)
37+
#define PURPLE COLOR(COLOR_PURPLE)
38+
#define CYAN COLOR(COLOR_CYAN)
39+
#define WHITE COLOR(COLOR_WHITE)
40+
41+
static uint32_t get_current_time_ms(void)
42+
{
43+
return xTaskGetTickCount() * 1000 / configTICK_RATE_HZ;
44+
}
45+
46+
47+
static float get_current_heap_free_percent(void)
48+
{
49+
size_t current_size = heap_caps_get_free_size(MALLOC_CAP_DEFAULT);
50+
size_t total_size = heap_caps_get_total_size(MALLOC_CAP_DEFAULT);
51+
return ((float) current_size / total_size) * 100.0;
52+
}
53+
54+
static float get_minimum_heap_free_percent(void)
55+
{
56+
size_t minimum_size = heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT);
57+
size_t total_size = heap_caps_get_total_size(MALLOC_CAP_DEFAULT);
58+
return ((float) minimum_size / total_size) * 100.0;
59+
}
60+
61+
static const char* int_to_string(int number, char *string)
62+
{
63+
sprintf(string, "%d", number);
64+
return string;
65+
}
66+
67+
static const char* task_state_to_string(eTaskState state) {
68+
switch (state) {
69+
case eRunning:
70+
return "Running";
71+
case eReady:
72+
return "Ready";
73+
case eBlocked:
74+
return "Blocked";
75+
case eSuspended:
76+
return "Suspended";
77+
case eDeleted:
78+
return "Deleted";
79+
case eInvalid:
80+
return "Invalid";
81+
default:
82+
return "Unknown state";
83+
}
84+
}
85+
86+
static void sort_tasks_by_runtime(TaskStatus_t *tasks_status_array, size_t number_of_tasks)
87+
{
88+
for (size_t i = 0; i < number_of_tasks - 1; ++i) {
89+
for (size_t k = i + 1; k < number_of_tasks; ++k) {
90+
if (tasks_status_array[k].ulRunTimeCounter > tasks_status_array[i].ulRunTimeCounter) {
91+
TaskStatus_t temp = tasks_status_array[i];
92+
tasks_status_array[i] = tasks_status_array[k];
93+
tasks_status_array[k] = temp;
94+
}
95+
}
96+
}
97+
}
98+
99+
static void task_status_monitor_task(void *params)
100+
{
101+
while (true) {
102+
UBaseType_t number_of_tasks = uxTaskGetNumberOfTasks();
103+
TaskStatus_t *p_tasks_status_array = pvPortMalloc(number_of_tasks * sizeof(TaskStatus_t));
104+
105+
if (p_tasks_status_array != NULL) {
106+
uint32_t total_run_time;
107+
number_of_tasks = uxTaskGetSystemState(p_tasks_status_array, number_of_tasks, &total_run_time);
108+
109+
if (total_run_time > 0) { // Avoid divide by zero error
110+
sort_tasks_by_runtime(p_tasks_status_array, number_of_tasks);
111+
112+
printf("I (%lu) tm: " CYAN "%-18.16s %-11.10s %-7.6s %-8.7s %-11.10s %-12.10s %-15.15s %-s"
113+
RESET_COLOR,
114+
get_current_time_ms(),
115+
"TASK NAME:",
116+
"STATE:",
117+
"CORE:",
118+
"NUMBER:",
119+
"PRIORITY:",
120+
"STACK_MIN:",
121+
"RUNTIME, µs:",
122+
"RUNTIME, %:\n"
123+
);
124+
125+
for (size_t i = 0; i < number_of_tasks; ++i) {
126+
char string[10]; // 10 - maximum number of characters for int
127+
printf("I (%lu) tm: " YELLOW "%-18.16s %-11.10s %-7.6s %-8d %-11d %-12lu %-14lu %-10.3f\n"
128+
RESET_COLOR,
129+
get_current_time_ms(),
130+
p_tasks_status_array[i].pcTaskName,
131+
task_state_to_string(p_tasks_status_array[i].eCurrentState),
132+
133+
xTaskGetAffinity(p_tasks_status_array[i].xHandle) == tskNO_AFFINITY ?
134+
"Any" : int_to_string((int)xTaskGetAffinity(p_tasks_status_array[i].xHandle), string),
135+
136+
p_tasks_status_array[i].xTaskNumber,
137+
p_tasks_status_array[i].uxCurrentPriority,
138+
p_tasks_status_array[i].usStackHighWaterMark,
139+
p_tasks_status_array[i].ulRunTimeCounter,
140+
(p_tasks_status_array[i].ulRunTimeCounter * 100.0) / total_run_time);
141+
}
142+
143+
printf("I (%lu) tm: " YELLOW "Total heap free size: " GREEN "%d" YELLOW " bytes\n" RESET_COLOR,
144+
get_current_time_ms(), heap_caps_get_total_size(MALLOC_CAP_DEFAULT));
145+
146+
printf("I (%lu) tm: " YELLOW "Current heap free size: " GREEN "%d" YELLOW " bytes (" GREEN "%.2f"
147+
YELLOW " %%)\n" RESET_COLOR, get_current_time_ms(), heap_caps_get_free_size(MALLOC_CAP_DEFAULT),
148+
get_current_heap_free_percent());
149+
150+
printf("I (%lu) tm: " YELLOW "Minimum heap free size: " GREEN "%d" YELLOW " bytes (" GREEN "%.2f"
151+
YELLOW " %%)\n" RESET_COLOR, get_current_time_ms(),
152+
heap_caps_get_minimum_free_size(MALLOC_CAP_DEFAULT), get_minimum_heap_free_percent());
153+
154+
printf("I (%lu) tm: " YELLOW "Total RunTime: " GREEN "%lu" YELLOW " µs (" GREEN "%lu" YELLOW
155+
" seconds)\n" RESET_COLOR, get_current_time_ms(), total_run_time, total_run_time / 1000000);
156+
157+
uint64_t current_time = esp_timer_get_time();
158+
printf("I (%lu) tm: " YELLOW "System UpTime: " GREEN "%llu" YELLOW " µs (" GREEN "%llu" YELLOW
159+
" seconds)\n\n" RESET_COLOR, get_current_time_ms(), current_time, current_time / 1000000);
160+
}
161+
vPortFree(p_tasks_status_array);
162+
} else {
163+
printf("I (%lu) tm: " RED "Could not allocate required memory\n" RESET_COLOR, get_current_time_ms());
164+
}
165+
vTaskDelay(pdMS_TO_TICKS(30 * 1000));
166+
}
167+
}
168+
169+
esp_err_t task_monitor(void)
170+
{
171+
BaseType_t status = xTaskCreatePinnedToCore(task_status_monitor_task, "monitor_task", configMINIMAL_STACK_SIZE * 4,
172+
NULL, tskIDLE_PRIORITY + 1, NULL, tskNO_AFFINITY);
173+
if (status != pdPASS) {
174+
printf("I (%lu) tm: task_status_monitor_task(): Task was not created. Could not allocate required memory\n",
175+
get_current_time_ms());
176+
return ESP_ERR_NO_MEM;
177+
}
178+
printf("I (%lu) tm: task_status_monitor_task() started successfully\n", get_current_time_ms());
179+
return ESP_OK;
180+
}

0 commit comments

Comments
 (0)