#include "platform/system_info.h" #include #include #include #include #include #include #include #include #include "version.h" namespace roro::system_info { const char* resetReason() { switch (esp_reset_reason()) { case ESP_RST_POWERON: return "power-on"; case ESP_RST_EXT: return "reset pin"; case ESP_RST_SW: return "restart"; case ESP_RST_PANIC: return "panic"; case ESP_RST_INT_WDT: return "interrupt watchdog"; case ESP_RST_TASK_WDT: return "task watchdog"; case ESP_RST_WDT: return "watchdog"; case ESP_RST_DEEPSLEEP: return "deep sleep"; case ESP_RST_BROWNOUT: return "brownout"; case ESP_RST_SDIO: return "sdio"; case ESP_RST_USB: return "usb"; case ESP_RST_JTAG: return "jtag"; default: return "unknown"; } } bool resetWasCrash() { switch (esp_reset_reason()) { case ESP_RST_PANIC: case ESP_RST_INT_WDT: case ESP_RST_TASK_WDT: case ESP_RST_WDT: return true; default: return false; } } static const char* stateName(esp_ota_img_states_t s) { switch (s) { case ESP_OTA_IMG_NEW: return "new"; case ESP_OTA_IMG_PENDING_VERIFY: return "pending verify"; case ESP_OTA_IMG_VALID: return "valid"; case ESP_OTA_IMG_INVALID: return "invalid"; case ESP_OTA_IMG_ABORTED: return "aborted"; default: return "undefined"; } } void printSystem(Print& out) { uint32_t s = millis() / 1000; out.printf("firmware: %s %s\n", kProductName, versionString()); out.printf("uptime: %luh%02lum%02lus, last start: %s\n", (unsigned long)(s / 3600), (unsigned long)(s / 60 % 60), (unsigned long)(s % 60), resetReason()); out.printf("heap: %u free, %u lowest, %u largest block\n", ESP.getFreeHeap(), ESP.getMinFreeHeap(), (unsigned)heap_caps_get_largest_free_block(MALLOC_CAP_8BIT)); out.printf("chip: %s rev %d, %lu MHz, %.1f C\n", ESP.getChipModel(), ESP.getChipRevision(), (unsigned long)ESP.getCpuFreqMHz(), temperatureRead()); } static std::string slotKey(const esp_partition_t* slot) { return std::string("ver_") + slot->label; } void recordSlotVersion(KeyValueStore& store, const esp_partition_t* slot, const char* version) { std::string known; if (slot && !(store.getString(slotKey(slot).c_str(), known) && known == version)) store.putString(slotKey(slot).c_str(), version); } void printSlots(Print& out, KeyValueStore& store) { const esp_partition_t* running = esp_ota_get_running_partition(); const esp_partition_t* next = esp_ota_get_boot_partition(); esp_partition_iterator_t it = esp_partition_find(ESP_PARTITION_TYPE_APP, ESP_PARTITION_SUBTYPE_ANY, nullptr); for (; it; it = esp_partition_next(it)) { // the last next() releases the iterator const esp_partition_t* p = esp_partition_get(it); esp_app_desc_t desc; bool hasApp = esp_ota_get_partition_description(p, &desc) == ESP_OK; std::string version = "(unknown version)"; store.getString(slotKey(p).c_str(), version); esp_ota_img_states_t state; bool hasState = esp_ota_get_state_partition(p, &state) == ESP_OK; out.printf("slot %s: %s%s%s, %s\n", p->label, hasApp ? version.c_str() : "(empty)", p == running ? ", running" : "", p == next ? ", boots next" : "", hasState ? stateName(state) : "no OTA state"); } } void printTasks(Print& out) { UBaseType_t n = uxTaskGetNumberOfTasks(); std::vector tasks(n + 4); uint32_t total = 0; n = uxTaskGetSystemState(tasks.data(), tasks.size(), &total); out.printf("%-16s %-4s %3s %6s %5s %s\n", "task", "st", "pri", "stack", "cpu%", "core"); static const char kStates[] = "RrBSD"; // running, ready, blocked, suspended, deleted for (UBaseType_t i = 0; i < n; i++) { const TaskStatus_t& t = tasks[i]; unsigned cpu = total ? (unsigned)((uint64_t)t.ulRunTimeCounter * 100 / total) : 0; int core = t.xCoreID == tskNO_AFFINITY ? -1 : (int)t.xCoreID; out.printf("%-16s %-4c %3u %6u %5u %d\n", t.pcTaskName, t.eCurrentState < 5 ? kStates[t.eCurrentState] : '?', (unsigned)t.uxCurrentPriority, (unsigned)t.usStackHighWaterMark, cpu, core); } } const char* bootOtherSlot() { const esp_partition_t* other = esp_ota_get_next_update_partition(nullptr); esp_app_desc_t desc; if (!other || esp_ota_get_partition_description(other, &desc) != ESP_OK) return "the other slot is empty"; if (esp_ota_set_boot_partition(other) != ESP_OK) return "the other slot doesn't hold a valid image"; delay(200); esp_restart(); return ""; } } // namespace roro::system_info