Add persistent OLED UI settings and recovery

This commit is contained in:
2026-08-29 20:18:08 +02:00
parent 16c0c02389
commit 276559536b
14 changed files with 538 additions and 38 deletions
+2
View File
@@ -8,6 +8,8 @@ idf_component_register(
"status_led.c"
"local_display.c"
"local_status_ui.c"
"local_ui_config.c"
"local_ui_console.c"
"local_ui_hw_test.c"
"rs232_hw_test.c"
"rs232_port_owner.c"
+10
View File
@@ -53,6 +53,16 @@ static const char *const s_completion_candidates[] = {
"debug buttons status",
"debug buttons test",
/* Persistent local OLED aging settings. */
"display status",
"display set",
"display set dim-seconds",
"display set off-seconds",
"display save",
"display load",
"display defaults",
"display reset",
/* Serial service lifecycle, persistence, counters, and settings. */
"serial status",
"serial start",
+9
View File
@@ -7,6 +7,7 @@
#include "board_pins.h"
#include "driver/i2c_master.h"
#include "esp_timer.h"
#include "freertos/FreeRTOS.h"
#include "freertos/semphr.h"
#include "freertos/task.h"
@@ -21,6 +22,7 @@
#define LOCAL_DISPLAY_DEFAULT_CONTRAST 127U
#define LOCAL_DISPLAY_COMMAND_CAPACITY 32U
#define LOCAL_DISPLAY_DATA_CHUNK_SIZE 128U
#define LOCAL_DISPLAY_FLUSH_BUDGET_US (500LL * 1000LL)
static const uint8_t s_expected_addresses[] = {0x3cU, 0x3dU};
@@ -172,6 +174,7 @@ static esp_err_t flush_dirty_locked(void)
return ESP_ERR_INVALID_STATE;
}
int64_t started = esp_timer_get_time();
uint8_t transfer[LOCAL_DISPLAY_DATA_CHUNK_SIZE + 1U];
transfer[0] = 0x40U;
for (uint8_t page = 0U; page < LOCAL_DISPLAY_PAGE_COUNT; ++page) {
@@ -179,6 +182,9 @@ static esp_err_t flush_dirty_locked(void)
if ((s_dirty_pages & page_mask) == 0U) {
continue;
}
if ((esp_timer_get_time() - started) >= LOCAL_DISPLAY_FLUSH_BUDGET_US) {
return ESP_ERR_TIMEOUT;
}
const uint8_t commands[] = {
0x21U, 0x00U, (uint8_t)(LOCAL_DISPLAY_WIDTH - 1U),
@@ -188,6 +194,9 @@ static esp_err_t flush_dirty_locked(void)
if (error != ESP_OK) {
return error;
}
if ((esp_timer_get_time() - started) >= LOCAL_DISPLAY_FLUSH_BUDGET_US) {
return ESP_ERR_TIMEOUT;
}
memcpy(&transfer[1], &s_framebuffer[(size_t)page * LOCAL_DISPLAY_WIDTH],
LOCAL_DISPLAY_WIDTH);
error = i2c_master_transmit(s_device, transfer, sizeof(transfer),
+82 -24
View File
@@ -35,8 +35,6 @@
#define LOCAL_STATUS_UI_TEXT_CAPACITY 22U
#define LOCAL_STATUS_UI_CLIENT_ROWS 4U
#define LOCAL_STATUS_UI_DIAGNOSTIC_HOLD_MS 30000U
#define LOCAL_STATUS_UI_DIM_DELAY_MS (5U * 60U * 1000U)
#define LOCAL_STATUS_UI_OFF_DELAY_MS (10U * 60U * 1000U)
#define LOCAL_STATUS_UI_DIM_CONTRAST 1U
#define LOCAL_STATUS_UI_ACTIVE_CONTRAST 127U
#define LOCAL_STATUS_UI_ICON_SIZE 8U
@@ -45,6 +43,7 @@
#define LOCAL_STATUS_UI_MENU_TIMEOUT_MS 30000U
#define LOCAL_STATUS_UI_RESULT_TIMEOUT_MS 2500U
#define LOCAL_STATUS_UI_CONFIRM_HOLD_MS 2000U
#define LOCAL_STATUS_UI_STUCK_BUTTON_MS 10000U
static const char *TAG = "local_status_ui";
@@ -117,6 +116,7 @@ typedef struct {
TickType_t pressed_since;
bool long_emitted;
bool suppress_gesture;
bool quarantined;
} local_status_button_state_t;
typedef struct {
@@ -201,6 +201,8 @@ static portMUX_TYPE s_timing_mux = portMUX_INITIALIZER_UNLOCKED;
static bool s_diagnostic_hold_active;
static TickType_t s_diagnostic_hold_started;
static uint32_t s_external_activity_sequence;
static local_ui_config_t s_config;
static bool s_config_available;
static const gpio_num_t s_button_gpios[LOCAL_STATUS_BUTTON_COUNT] = {
LOCAL_UI_BUTTON_PREVIOUS_GPIO,
@@ -417,7 +419,9 @@ static bool snapshot_has_alert(const local_status_snapshot_t *snapshot)
return !snapshot->broker_available || !snapshot->usb_available ||
!snapshot->wifi_available || !snapshot->web_available ||
!snapshot->web_serial_available || !snapshot->ssh_available ||
(snapshot->wifi_available && snapshot->wifi.last_error != ESP_OK) ||
(snapshot->wifi_available &&
(snapshot->wifi.last_error != ESP_OK ||
snapshot->wifi.counters.queue_drops != 0U)) ||
(snapshot->web_available && snapshot->web.last_error != ESP_OK) ||
(snapshot->ssh_available && snapshot->ssh.last_error != ESP_OK) ||
snapshot->serial_counters.rx_dropped_bytes != 0U ||
@@ -927,9 +931,16 @@ static bool poll_button_event(local_status_button_state_t states[LOCAL_STATUS_BU
}
if (!states[i].stable_pressed) {
states[i].suppress_gesture = false;
states[i].quarantined = false;
}
}
if (states[i].stable_pressed) {
if (states[i].stable_pressed && !states[i].quarantined &&
(now - states[i].pressed_since) >=
pdMS_TO_TICKS(LOCAL_STATUS_UI_STUCK_BUTTON_MS)) {
states[i].quarantined = true;
states[i].suppress_gesture = true;
}
if (states[i].stable_pressed && !states[i].quarantined) {
++pressed_count;
}
}
@@ -945,7 +956,7 @@ static bool poll_button_event(local_status_button_state_t states[LOCAL_STATUS_BU
return false;
}
for (size_t i = 0U; i < LOCAL_STATUS_BUTTON_COUNT; ++i) {
if (pressed_edge[i]) {
if (pressed_edge[i] && !states[i].quarantined) {
*event = (local_status_button_event_t){
.button = (local_status_button_t)i,
.type = LOCAL_STATUS_BUTTON_EVENT_PRESS,
@@ -954,7 +965,8 @@ static bool poll_button_event(local_status_button_state_t states[LOCAL_STATUS_BU
}
}
for (size_t i = 0U; i < LOCAL_STATUS_BUTTON_COUNT; ++i) {
if (states[i].stable_pressed && !states[i].long_emitted &&
if (states[i].stable_pressed && !states[i].quarantined &&
!states[i].long_emitted &&
!states[i].suppress_gesture &&
(now - states[i].pressed_since) >= pdMS_TO_TICKS(LOCAL_STATUS_UI_CONFIRM_HOLD_MS)) {
states[i].long_emitted = true;
@@ -1196,13 +1208,16 @@ static void handle_button_event(local_status_ui_state_t *state,
static void update_display_power(local_status_power_t *power,
TickType_t now,
TickType_t last_activity)
TickType_t last_activity,
const local_ui_config_t *config)
{
TickType_t inactive = now - last_activity;
bool should_stop = *power != LOCAL_STATUS_POWER_OFF &&
inactive >= pdMS_TO_TICKS(LOCAL_STATUS_UI_OFF_DELAY_MS);
bool should_dim = *power == LOCAL_STATUS_POWER_ACTIVE &&
inactive >= pdMS_TO_TICKS(LOCAL_STATUS_UI_DIM_DELAY_MS);
bool should_stop = config->off_timeout_seconds != 0U &&
*power != LOCAL_STATUS_POWER_OFF &&
inactive >= pdMS_TO_TICKS(config->off_timeout_seconds * 1000U);
bool should_dim = config->dim_timeout_seconds != 0U &&
*power == LOCAL_STATUS_POWER_ACTIVE &&
inactive >= pdMS_TO_TICKS(config->dim_timeout_seconds * 1000U);
if ((!should_stop && !should_dim) || s_diagnostic_gate == NULL ||
xSemaphoreTake(s_diagnostic_gate, pdMS_TO_TICKS(100U)) != pdTRUE) {
return;
@@ -1267,21 +1282,14 @@ static void local_status_ui_task(void *context)
last_activity = now;
}
sync_display_power(&power);
bool wake_gesture = false;
if (power != LOCAL_STATUS_POWER_ACTIVE) {
if (diagnostics_held) {
for (size_t i = 0U; i < LOCAL_STATUS_BUTTON_COUNT; ++i) {
if (buttons[i].stable_pressed) {
buttons[i].suppress_gesture = true;
wake_gesture = true;
}
}
if (wake_gesture && !diagnostics_held) {
last_activity = now;
render_requested = wake_display(&power);
}
}
if (have_event && !wake_gesture) {
if (have_event) {
last_activity = now;
if (!diagnostics_held) {
local_status_snapshot_t snapshot;
@@ -1309,7 +1317,11 @@ static void local_status_ui_task(void *context)
render_requested = true;
}
update_display_power(&power, now, last_activity);
local_ui_config_t config;
portENTER_CRITICAL(&s_timing_mux);
config = s_config;
portEXIT_CRITICAL(&s_timing_mux);
update_display_power(&power, now, last_activity, &config);
diagnostics_held = diagnostic_hold_is_active(now);
bool rendered = false;
if (power != LOCAL_STATUS_POWER_OFF && !diagnostics_held &&
@@ -1344,14 +1356,55 @@ void local_status_ui_hold_for_diagnostics(void)
}
}
esp_err_t local_status_ui_start(void)
esp_err_t local_status_ui_get_config(local_ui_config_t *config)
{
if (config == NULL) {
return ESP_ERR_INVALID_ARG;
}
portENTER_CRITICAL(&s_timing_mux);
bool available = s_config_available;
*config = s_config;
portEXIT_CRITICAL(&s_timing_mux);
return available ? ESP_OK : ESP_ERR_INVALID_STATE;
}
esp_err_t local_status_ui_apply_config(const local_ui_config_t *config)
{
esp_err_t error = local_ui_config_validate(config);
if (error != ESP_OK) {
return error;
}
portENTER_CRITICAL(&s_timing_mux);
if (!s_config_available) {
portEXIT_CRITICAL(&s_timing_mux);
return ESP_ERR_INVALID_STATE;
}
s_config = *config;
++s_external_activity_sequence;
portEXIT_CRITICAL(&s_timing_mux);
return ESP_OK;
}
esp_err_t local_status_ui_start(const local_ui_config_t *config)
{
esp_err_t error = local_ui_config_validate(config);
if (error != ESP_OK) {
return error;
}
if (s_task != NULL || s_diagnostic_gate != NULL) {
return ESP_ERR_INVALID_STATE;
}
portENTER_CRITICAL(&s_timing_mux);
s_config = *config;
s_config_available = true;
portEXIT_CRITICAL(&s_timing_mux);
s_diagnostic_gate = xSemaphoreCreateMutexStatic(&s_diagnostic_gate_storage);
if (s_diagnostic_gate == NULL) {
portENTER_CRITICAL(&s_timing_mux);
s_config_available = false;
portEXIT_CRITICAL(&s_timing_mux);
return ESP_ERR_NO_MEM;
}
@@ -1362,11 +1415,16 @@ esp_err_t local_status_ui_start(void)
s_task = NULL;
vSemaphoreDelete(s_diagnostic_gate);
s_diagnostic_gate = NULL;
portENTER_CRITICAL(&s_timing_mux);
s_config_available = false;
portEXIT_CRITICAL(&s_timing_mux);
return ESP_ERR_NO_MEM;
}
ESP_LOGI(TAG,
"Read-only status UI started at %u Hz maximum; dim/off after 5/10 minutes",
1000U / LOCAL_STATUS_UI_REFRESH_MS);
"Local status/control UI started at %u Hz maximum; dim/off after %u/%u seconds",
1000U / LOCAL_STATUS_UI_REFRESH_MS,
(unsigned int)config->dim_timeout_seconds,
(unsigned int)config->off_timeout_seconds);
return ESP_OK;
}
+6 -1
View File
@@ -4,6 +4,7 @@
#pragma once
#include "esp_err.h"
#include "local_ui_config.h"
#ifdef __cplusplus
extern "C" {
@@ -14,7 +15,11 @@ extern "C" {
* task never becomes a broker client or serial writer. The OLED and buttons
* are optional, so a missing display is not an error.
*/
esp_err_t local_status_ui_start(void);
esp_err_t local_status_ui_start(const local_ui_config_t *config);
/* Runtime settings are copied atomically and never expose display-frame ownership. */
esp_err_t local_status_ui_get_config(local_ui_config_t *config);
esp_err_t local_status_ui_apply_config(const local_ui_config_t *config);
/* Preserve a manually selected display diagnostic for a bounded interval. */
void local_status_ui_hold_for_diagnostics(void);
+119
View File
@@ -0,0 +1,119 @@
/* SPDX-License-Identifier: GPL-3.0-only */
/* Versioned persistent configuration for the optional local OLED UI. */
#include "local_ui_config.h"
#include <stddef.h>
#include "nvs.h"
#include "nvs_flash.h"
void local_ui_config_defaults(local_ui_config_t *config)
{
if (config == NULL) {
return;
}
*config = (local_ui_config_t){
.version = LOCAL_UI_CONFIG_VERSION,
.dim_timeout_seconds = LOCAL_UI_CONFIG_DEFAULT_DIM_SECONDS,
.off_timeout_seconds = LOCAL_UI_CONFIG_DEFAULT_OFF_SECONDS,
};
}
esp_err_t local_ui_config_validate(const local_ui_config_t *config)
{
if (config == NULL || config->version != LOCAL_UI_CONFIG_VERSION ||
config->dim_timeout_seconds > LOCAL_UI_CONFIG_MAX_TIMEOUT_SECONDS ||
config->off_timeout_seconds > LOCAL_UI_CONFIG_MAX_TIMEOUT_SECONDS) {
return ESP_ERR_INVALID_ARG;
}
if (config->dim_timeout_seconds != 0U && config->off_timeout_seconds != 0U &&
config->off_timeout_seconds <= config->dim_timeout_seconds) {
return ESP_ERR_INVALID_ARG;
}
return ESP_OK;
}
esp_err_t local_ui_config_load(local_ui_config_t *config, bool *used_stored_config)
{
if (config == NULL || used_stored_config == NULL) {
return ESP_ERR_INVALID_ARG;
}
local_ui_config_defaults(config);
*used_stored_config = false;
esp_err_t error = nvs_flash_init();
if (error != ESP_OK) {
return error;
}
nvs_handle_t handle;
error = nvs_open(LOCAL_UI_CONFIG_NVS_NAMESPACE, NVS_READONLY, &handle);
if (error == ESP_ERR_NVS_NOT_FOUND) {
return ESP_OK;
}
if (error != ESP_OK) {
return error;
}
size_t stored_size = 0U;
error = nvs_get_blob(handle, LOCAL_UI_CONFIG_NVS_BLOB_KEY, NULL, &stored_size);
if (error == ESP_ERR_NVS_NOT_FOUND || error == ESP_ERR_NVS_TYPE_MISMATCH ||
(error == ESP_OK && stored_size != sizeof(local_ui_config_t))) {
nvs_close(handle);
return ESP_OK;
}
if (error != ESP_OK) {
nvs_close(handle);
return error;
}
local_ui_config_t stored;
error = nvs_get_blob(handle, LOCAL_UI_CONFIG_NVS_BLOB_KEY, &stored, &stored_size);
nvs_close(handle);
if (error == ESP_ERR_NVS_INVALID_LENGTH) {
return ESP_OK;
}
if (error != ESP_OK) {
return error;
}
if (stored_size != sizeof(stored) || local_ui_config_validate(&stored) != ESP_OK) {
return ESP_OK;
}
*config = stored;
*used_stored_config = true;
return ESP_OK;
}
esp_err_t local_ui_config_save(const local_ui_config_t *config)
{
esp_err_t error = local_ui_config_validate(config);
if (error != ESP_OK) {
return error;
}
error = nvs_flash_init();
if (error != ESP_OK) {
return error;
}
nvs_handle_t handle;
error = nvs_open(LOCAL_UI_CONFIG_NVS_NAMESPACE, NVS_READWRITE, &handle);
if (error != ESP_OK) {
return error;
}
error = nvs_set_blob(handle, LOCAL_UI_CONFIG_NVS_BLOB_KEY, config, sizeof(*config));
if (error == ESP_OK) {
error = nvs_commit(handle);
}
nvs_close(handle);
return error;
}
esp_err_t local_ui_config_reset_storage(void)
{
local_ui_config_t config;
local_ui_config_defaults(&config);
return local_ui_config_save(&config);
}
+30
View File
@@ -0,0 +1,30 @@
/* SPDX-License-Identifier: GPL-3.0-only */
/* Versioned persistent configuration for the optional local OLED UI. */
#pragma once
#include <stdbool.h>
#include <stdint.h>
#include "esp_err.h"
#define LOCAL_UI_CONFIG_VERSION 1U
#define LOCAL_UI_CONFIG_DEFAULT_DIM_SECONDS 300U
#define LOCAL_UI_CONFIG_DEFAULT_OFF_SECONDS 600U
#define LOCAL_UI_CONFIG_MAX_TIMEOUT_SECONDS 86400U
#define LOCAL_UI_CONFIG_NVS_NAMESPACE "local_ui"
#define LOCAL_UI_CONFIG_NVS_BLOB_KEY "config"
typedef struct {
uint32_t version;
/* Zero disables the corresponding inactivity transition. */
uint32_t dim_timeout_seconds;
uint32_t off_timeout_seconds;
} local_ui_config_t;
void local_ui_config_defaults(local_ui_config_t *config);
esp_err_t local_ui_config_validate(const local_ui_config_t *config);
esp_err_t local_ui_config_load(local_ui_config_t *config, bool *used_stored_config);
esp_err_t local_ui_config_save(const local_ui_config_t *config);
esp_err_t local_ui_config_reset_storage(void);
+202
View File
@@ -0,0 +1,202 @@
/* SPDX-License-Identifier: GPL-3.0-only */
/* UART0 administration commands for local UI aging settings. */
#include "local_ui_console.h"
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "esp_console.h"
#include "local_display.h"
#include "local_status_ui.h"
#include "local_ui_config.h"
static void print_usage(void)
{
printf("Usage:\n");
printf(" display status\n");
printf(" display set <dim-seconds|off-seconds> <0..%u>\n",
LOCAL_UI_CONFIG_MAX_TIMEOUT_SECONDS);
printf(" display save|load|defaults|reset\n");
printf("Zero disables the selected inactivity transition.\n");
}
static void print_config(const local_ui_config_t *config)
{
printf("Local UI configuration v%u: dim-seconds=%u off-seconds=%u\n",
(unsigned int)config->version,
(unsigned int)config->dim_timeout_seconds,
(unsigned int)config->off_timeout_seconds);
}
static bool parse_timeout(const char *text, uint32_t *value)
{
if (text == NULL || *text == '\0') {
return false;
}
for (const char *character = text; *character != '\0'; ++character) {
if (*character < '0' || *character > '9') {
return false;
}
}
errno = 0;
char *end = NULL;
unsigned long parsed = strtoul(text, &end, 10);
if (errno != 0 || end == text || *end != '\0' ||
parsed > LOCAL_UI_CONFIG_MAX_TIMEOUT_SECONDS) {
return false;
}
*value = (uint32_t)parsed;
return true;
}
static int show_status(void)
{
local_ui_config_t config;
esp_err_t error = local_status_ui_get_config(&config);
if (error != ESP_OK) {
printf("Could not read local UI configuration: %s\n", esp_err_to_name(error));
return 1;
}
print_config(&config);
local_display_snapshot_t display;
error = local_display_get_snapshot(&display);
if (error != ESP_OK) {
printf("Display service unavailable: %s\n", esp_err_to_name(error));
return 1;
}
printf("OLED: bus=%s initialized=%s address=0x%02x contrast=%u last-error=%s\n",
display.bus_ready ? "ready" : "unavailable",
display.initialized ? "yes" : "no",
display.address_7bit,
(unsigned int)display.contrast,
esp_err_to_name(display.last_error));
return 0;
}
static int apply_parameter(const char *parameter, const char *text)
{
uint32_t value;
if (!parse_timeout(text, &value)) {
printf("Timeout must be 0..%u seconds.\n",
LOCAL_UI_CONFIG_MAX_TIMEOUT_SECONDS);
return 1;
}
local_ui_config_t config;
esp_err_t error = local_status_ui_get_config(&config);
if (error != ESP_OK) {
printf("Could not read local UI configuration: %s\n", esp_err_to_name(error));
return 1;
}
if (strcmp(parameter, "dim-seconds") == 0) {
config.dim_timeout_seconds = value;
} else if (strcmp(parameter, "off-seconds") == 0) {
config.off_timeout_seconds = value;
} else {
printf("Unknown display parameter '%s'.\n", parameter);
print_usage();
return 1;
}
error = local_status_ui_apply_config(&config);
if (error != ESP_OK) {
printf("Invalid display configuration: %s. When both timeouts are enabled, off must be later than dim.\n",
esp_err_to_name(error));
return 1;
}
print_config(&config);
printf("Applied in RAM; run 'display save' to persist it.\n");
return 0;
}
static int command_display(int argc, char **argv)
{
if (argc == 1 || (argc == 2 && strcmp(argv[1], "status") == 0)) {
return show_status();
}
if (argc == 4 && strcmp(argv[1], "set") == 0) {
return apply_parameter(argv[2], argv[3]);
}
if (argc == 2 && strcmp(argv[1], "save") == 0) {
local_ui_config_t config;
esp_err_t error = local_status_ui_get_config(&config);
if (error == ESP_OK) {
error = local_ui_config_save(&config);
}
if (error != ESP_OK) {
printf("Could not save display configuration: %s\n", esp_err_to_name(error));
return 1;
}
printf("Display configuration saved to NVS.\n");
return 0;
}
if (argc == 2 && strcmp(argv[1], "load") == 0) {
local_ui_config_t config;
bool used_stored_config;
esp_err_t error = local_ui_config_load(&config, &used_stored_config);
if (error == ESP_OK) {
error = local_status_ui_apply_config(&config);
}
if (error != ESP_OK) {
printf("Could not load display configuration: %s\n", esp_err_to_name(error));
return 1;
}
printf("Loaded %s display configuration.\n",
used_stored_config ? "stored" : "default");
print_config(&config);
return 0;
}
if (argc == 2 && strcmp(argv[1], "defaults") == 0) {
local_ui_config_t config;
local_ui_config_defaults(&config);
esp_err_t error = local_status_ui_apply_config(&config);
if (error != ESP_OK) {
printf("Could not apply display defaults: %s\n", esp_err_to_name(error));
return 1;
}
printf("Display defaults applied in RAM; run 'display save' to persist them.\n");
print_config(&config);
return 0;
}
if (argc == 2 && strcmp(argv[1], "reset") == 0) {
local_ui_config_t previous;
local_ui_config_t defaults;
local_ui_config_defaults(&defaults);
esp_err_t error = local_status_ui_get_config(&previous);
if (error == ESP_OK) {
error = local_status_ui_apply_config(&defaults);
}
if (error == ESP_OK) {
error = local_ui_config_reset_storage();
if (error != ESP_OK) {
(void)local_status_ui_apply_config(&previous);
}
}
if (error != ESP_OK) {
printf("Could not reset display configuration: %s\n", esp_err_to_name(error));
return 1;
}
printf("Display defaults applied and saved to NVS.\n");
print_config(&defaults);
return 0;
}
print_usage();
return 1;
}
esp_err_t local_ui_console_register_commands(void)
{
const esp_console_cmd_t command = {
.command = "display",
.help = "Configure persistent local OLED aging timeouts; use 'display' for status",
.hint = NULL,
.func = &command_display,
.argtable = NULL,
};
return esp_console_cmd_register(&command);
}
+8
View File
@@ -0,0 +1,8 @@
/* SPDX-License-Identifier: GPL-3.0-only */
/* UART0 administration commands for local UI aging settings. */
#pragma once
#include "esp_err.h"
esp_err_t local_ui_console_register_commands(void);
+18 -2
View File
@@ -7,6 +7,8 @@
#include "network_console.h"
#include "local_display.h"
#include "local_status_ui.h"
#include "local_ui_config.h"
#include "local_ui_console.h"
#include "local_ui_hw_test.h"
#include "rs232_hw_test.h"
#include "rs232_port_owner.h"
@@ -38,7 +40,7 @@ static const char *TAG = "firmware";
void app_main(void)
{
ESP_LOGI(TAG, "ESP32-S3 Serial Swiss Army Knife Phase 7D local controls started");
ESP_LOGI(TAG, "ESP32-S3 Serial Swiss Army Knife Phase 7E local UI reliability started");
if (esp_psram_is_initialized()) {
ESP_LOGI(TAG, "PSRAM initialized: %u bytes", (unsigned int)esp_psram_get_size());
@@ -77,6 +79,17 @@ void app_main(void)
esp_err_to_name(local_ui_error));
}
local_ui_config_t local_ui_config;
bool used_stored_local_ui_config = false;
esp_err_t local_ui_config_error =
local_ui_config_load(&local_ui_config, &used_stored_local_ui_config);
if (local_ui_config_error != ESP_OK) {
local_ui_config_defaults(&local_ui_config);
ESP_LOGW(TAG,
"NVS local UI configuration unavailable (%s); using RAM defaults",
esp_err_to_name(local_ui_config_error));
}
serial_config_t serial_config;
bool used_stored_config = false;
esp_err_t config_error = serial_config_load(&serial_config, &used_stored_config);
@@ -203,13 +216,15 @@ void app_main(void)
}
if (local_ui_error == ESP_OK) {
esp_err_t local_status_ui_error = local_status_ui_start();
esp_err_t local_status_ui_error = local_status_ui_start(&local_ui_config);
if (local_status_ui_error != ESP_OK) {
ESP_LOGW(TAG, "Local status UI unavailable: %s",
esp_err_to_name(local_status_ui_error));
}
}
ESP_LOGI(TAG, "Using %s local UI configuration",
used_stored_local_ui_config ? "stored" : "default");
ESP_LOGI(
TAG,
"Using %s serial configuration; UART service starts on 'serial start' or native USB open",
@@ -235,6 +250,7 @@ void app_main(void)
/* The REPL constructor initializes esp_console and installs `help`. */
ESP_ERROR_CHECK(rs232_hw_test_register_console_commands());
ESP_ERROR_CHECK(local_ui_console_register_commands());
ESP_ERROR_CHECK(serial_console_register_commands());
ESP_ERROR_CHECK(session_console_register_commands());
ESP_ERROR_CHECK(usb_console_register_commands());