#include "utils.h" #include "freertos_stats.h" #include "sdkconfig.h" #include "freertos/FreeRTOS.h" #include "freertos/portable.h" #include "esp_heap_caps.h" #include "esp_system.h" #include "esp_spi_flash.h" #include "esp_partition.h" #include "LittleFS.h" #include #include #include #define FREERTOS_TASK_NUMBER_MAX_NUM 256 // RunTime stats for how many Tasks to be stored std::string printBits(uint32_t value) { std::string result; for (int i = 31; i >= 0; i--) { // ottieni il singolo bit result += ((value >> i) & 1) ? '1' : '0'; // aggiungi uno spazio ogni 8 bit, tranne dopo l'ultimo if (i % 8 == 0 && i != 0) { result += ' '; } } return result; } // ANSI colors #define BAR_WIDTH 30 #define COLOR_RESET "\033[0m" #define COLOR_RED "\033[31m" #define COLOR_GREEN "\033[32m" #define COLOR_BLUE "\033[34m" #define COLOR_MAGENTA "\033[35m" #define COLOR_CYAN "\033[36m" #define COLOR_YELLOW "\033[33m" #define COLOR_WHITE "\033[37m" #define COLOR_LBLUE "\033[94m" void printBar(Print &printer, const char *label, size_t used, size_t total, const char *color) { float perc = total > 0 ? ((float)used / total) : 0; int filled = perc * BAR_WIDTH; printer.printf("%s%-12s [" COLOR_RESET, color, label); for (int i = 0; i < BAR_WIDTH; i++) { if (i < filled) printer.printf("%s#%s", color, COLOR_RESET); else printer.printf("-"); } printer.printf("] %s%6.2f%%%s (%5.3f/%5.3f)MB\n", color, perc * 100.0, COLOR_RESET, (used / 1024.0f / 1024.0f), (total / 1024.0f / 1024.0f)); } void printRunningTasksMod(Print &printer, std::function orderBy) { static const char *taskStates[] = {"Running", "Ready", "Blocked", "Suspended", "Deleted", "Invalid"}; static uint32_t ulRunTimeCounters[FREERTOS_TASK_NUMBER_MAX_NUM]; static uint32_t ulLastRunTime = 0; uint32_t ulCurrentRunTime = 0, ulTaskRunTime = 0; uint32_t ulTotalRunTime = 0; std::vector pxTaskStatusArray; UBaseType_t uxArraySize = 0; // Take a snapshot of the number of tasks in case it changes while this function is executing. uxArraySize = uxTaskGetNumberOfTasks(); pxTaskStatusArray.resize(uxArraySize); // Generate raw status information about each task. uxArraySize = uxTaskGetSystemState(pxTaskStatusArray.data(), uxArraySize, &ulTotalRunTime); if (orderBy == nullptr) std::sort(pxTaskStatusArray.begin(), pxTaskStatusArray.end(), [](const TaskStatus_t &a, const TaskStatus_t &b) { return a.xTaskNumber < b.xTaskNumber; }); else std::sort(pxTaskStatusArray.begin(), pxTaskStatusArray.end(), orderBy); // Compute system total runtime ulCurrentRunTime = ulTotalRunTime - ulLastRunTime; ulLastRunTime = ulTotalRunTime; // PRINT MEMORY INFO printer.printf("\033[H"); printer.printf(COLOR_LBLUE "=================== ESP32 SYSTEM MONITOR ===================\n" COLOR_RESET); std::string buffer; time_t now = time(nullptr); struct tm *t = localtime(&now); buffer.resize(64); strftime(buffer.data(), sizeof(buffer), "%Y-%m-%d %H:%M:%S", t); printer.printf(COLOR_YELLOW "=============== Datetime: %s ===============\n\n" COLOR_RESET, buffer.c_str()); // ===== HEAP ===== size_t freeHeap = esp_get_free_heap_size(); size_t totalHeap = heap_caps_get_total_size(MALLOC_CAP_DEFAULT); printBar(printer, "HEAP", totalHeap - freeHeap, totalHeap, COLOR_GREEN); // ===== RAM INTERNA ===== size_t freeInternal = heap_caps_get_free_size(MALLOC_CAP_INTERNAL); size_t totalInternal = heap_caps_get_total_size(MALLOC_CAP_INTERNAL); printBar(printer, "INTERNAL", totalInternal - freeInternal, totalInternal, COLOR_BLUE); // ===== PSRAM ===== size_t totalPsram = heap_caps_get_total_size(MALLOC_CAP_SPIRAM); if (totalPsram > 0) { size_t freePsram = heap_caps_get_free_size(MALLOC_CAP_SPIRAM); printBar(printer, "PSRAM", totalPsram - freePsram, totalPsram, COLOR_MAGENTA); } printer.printf("\n"); // ===== FLASH APP (approssimato) ===== const esp_partition_t *app_partition = esp_partition_find_first(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_APP_FACTORY, NULL); if (app_partition) { size_t totalAPP = app_partition->size; // dimensione reale partizione size_t sketchSize = ESP.getSketchSize(); printBar(printer, "FLASH APP", sketchSize, totalAPP, COLOR_CYAN); } else { printer.printf(COLOR_YELLOW "%-12s [NOT FOUND]\n" COLOR_RESET, "FLASH APP"); } // ===== LITTLEFS (corretto con partition table) ===== const esp_partition_t *fs_partition = esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_LITTLEFS, "littlefs"); if (fs_partition) { size_t totalFS = fs_partition->size; // dimensione reale partizione size_t usedFS = LittleFS.usedBytes(); // spazio usato reale printBar(printer, "LITTLEFS", usedFS, totalFS, COLOR_YELLOW); } else { printer.printf(COLOR_YELLOW "%-12s [NOT FOUND]\n" COLOR_RESET, "LITTLEFS"); } // ===== MIN HEAP ===== size_t minHeap = esp_get_minimum_free_heap_size(); printer.printf("%s\nMin Heap Ever:%s %u KB\n\n", COLOR_RED, COLOR_RESET, minHeap / 1024); // Print Runtime Information printer.printf("Tasks: %u, Runtime: %lus, Period: %luus\r\n", uxArraySize, ulTotalRunTime / 1000000, ulCurrentRunTime); // Print Task Headers printer.printf("Num\t Name\tLoad\tPrio\t Free\tCore\tState\r\n"); for (const auto &task : pxTaskStatusArray) { ulTaskRunTime = (task.ulRunTimeCounter - ulRunTimeCounters[task.xTaskNumber]); ulRunTimeCounters[task.xTaskNumber] = task.ulRunTimeCounter; ulTaskRunTime = (ulTaskRunTime * 100) / ulCurrentRunTime; // in percentage printer.printf( "%3u\t%16s" "\t%3lu%%" "\t%4u\t%5lu" "\t%4c" "\t%s\r\n", task.xTaskNumber, task.pcTaskName, ulTaskRunTime, task.uxCurrentPriority, task.usStackHighWaterMark, (task.xCoreID == tskNO_AFFINITY) ? '*' : ('0' + task.xCoreID), taskStates[task.eCurrentState]); } printer.println(); }