Settings
Source: src/settings.c · 556 lines · 25 functions.
Screen captures
Views of this part running on the Mini Arcade.

Module entry points
These functions are exposed to the rest of the project. Open one to follow its decisions and the helpers it calls.
How to read these diagrams
Each function has its own expandable diagram using the actual expressions and statements from this file. Decisions split into True and False; loop bodies are indented. Returns end the function immediately; break and continue follow their C meanings. Arrows between statements show their order when execution reaches them.
These are structural source diagrams before preprocessing, not runtime traces. Conditional compilation and macros remain source expressions. Ternary choices remain inside their statement. Calls are linked separately; callback execution and task scheduling are not inferred.
Functions in this file
valid_brightness() · source line 60
static bool valid_brightness(uint8_t value)
- Exit this function
return value >= BRIGHTNESS_MIN && value <= BRIGHTNESS_MAX && (value - BRIGHTNESS_MIN) % BRIGHTNESS_STEP == 0;
Read original function source
static bool valid_brightness(uint8_t value) {
return value >= BRIGHTNESS_MIN && value <= BRIGHTNESS_MAX &&
(value - BRIGHTNESS_MIN) % BRIGHTNESS_STEP == 0;
}valid_volume() · source line 64
static bool valid_volume(uint8_t value)
- Exit this function
return value <= VOLUME_MAX && value % VOLUME_STEP == 0U;
Read original function source
static bool valid_volume(uint8_t value) { return value <= VOLUME_MAX && value % VOLUME_STEP == 0U; }load_ram_log_record() · source line 66
static void load_ram_log_record(bool newest)
Calls into this documentation:
- Execute
ram_log_record_count = ramMonitorRecordCount(); - Decision
ram_log_record_count == 0UTrue- Execute
ram_log_record_index = 0U; - Execute
ram_log_record[0] = '\0'; - Exit this function
return;
FalseContinue after this decision.
- Decision
newest || ram_log_record_index >= ram_log_record_countTrue- Execute
ram_log_record_index = ram_log_record_count - 1U;
FalseContinue after this decision.
- Decision
!ramMonitorReadRecord(ram_log_record_index, ram_log_record, sizeof(ram_log_record))True- Execute
ram_log_record[0] = '\0';
FalseContinue after this decision.
Read original function source
static void load_ram_log_record(bool newest)
{
ram_log_record_count = ramMonitorRecordCount();
if (ram_log_record_count == 0U) {
ram_log_record_index = 0U;
ram_log_record[0] = '\0';
return;
}
if (newest || ram_log_record_index >= ram_log_record_count)
ram_log_record_index = ram_log_record_count - 1U;
if (!ramMonitorReadRecord(ram_log_record_index, ram_log_record, sizeof(ram_log_record)))
ram_log_record[0] = '\0';
}save_brightness() · source line 79
static void save_brightness(void)
Calls into this documentation:
- Execute
(void)game_storage_write("arcade_settings", "display", &saved, sizeof(saved));
Read original function source
static void save_brightness(void) {
(void)game_storage_write("arcade_settings", "display", &saved, sizeof(saved));
}save_wifi_enabled() · source line 82
static bool save_wifi_enabled(void)
Calls into this documentation:
- Exit this function
return game_storage_write("arcade_wifi", "settings", &wifi_saved, sizeof(wifi_saved));
Read original function source
static bool save_wifi_enabled(void) {
return game_storage_write("arcade_wifi", "settings", &wifi_saved, sizeof(wifi_saved));
}save_wifi_credentials() · source line 85
static bool save_wifi_credentials(const char *ssid, const char *password)
Calls into this documentation:
- Execute
wifi_credentials = (wifi_credentials_record_t){1, {0}, {0}}; - Execute
strncpy(wifi_credentials.ssid, ssid, sizeof(wifi_credentials.ssid) - 1U); - Execute
strncpy(wifi_credentials.password, password, sizeof(wifi_credentials.password) - 1U); - Execute
wifi_credentials_available = true; - Exit this function
return game_storage_write("arcade_wifi", "credentials", &wifi_credentials, sizeof(wifi_credentials));
Read original function source
static bool save_wifi_credentials(const char *ssid, const char *password) {
wifi_credentials = (wifi_credentials_record_t){1, {0}, {0}};
strncpy(wifi_credentials.ssid, ssid, sizeof(wifi_credentials.ssid) - 1U);
strncpy(wifi_credentials.password, password, sizeof(wifi_credentials.password) - 1U);
wifi_credentials_available = true;
return game_storage_write("arcade_wifi", "credentials", &wifi_credentials,
sizeof(wifi_credentials));
}reconnect_saved_wifi() · source line 93
static void reconnect_saved_wifi(void)
Calls into this documentation:
- Decision
wifi_credentials_available && wifiServiceConnectionState() != WIFI_CONNECTION_CONNECTED && wifiServiceConnectionState() != WIFI_CONNECTION_CONNECTINGTrue- Execute
(void)wifiServiceConnect(wifi_credentials.ssid, wifi_credentials.password);
FalseContinue after this decision.
Read original function source
static void reconnect_saved_wifi(void) {
if (wifi_credentials_available &&
wifiServiceConnectionState() != WIFI_CONNECTION_CONNECTED &&
wifiServiceConnectionState() != WIFI_CONNECTION_CONNECTING)
(void)wifiServiceConnect(wifi_credentials.ssid, wifi_credentials.password);
}settingsInitialize() · source line 99
bool settingsInitialize(void)
Calls into this documentation:
- Execute
bool found = false; - Execute
settings_record_t loaded = {0}; - Decision
!game_storage_read("arcade_settings", "display", &loaded, sizeof(loaded), &found)True- Exit this function
return false;
FalseContinue after this decision.
- Decision
found && (loaded.version == 1 || loaded.version == 2) && valid_brightness(loaded.brightness)True- Execute
saved.brightness = loaded.brightness; - Decision
loaded.version == 2 && valid_volume(loaded.volume)True- Execute
saved.volume = loaded.volume;
FalseContinue after this decision.
FalseContinue after this decision.
- Execute
audioSetMasterVolume(saved.volume); - Execute
wifi_settings_record_t wifi_loaded = {0}; - Decision
!game_storage_read("arcade_wifi", "settings", &wifi_loaded, sizeof(wifi_loaded), &found)True- Exit this function
return false;
FalseContinue after this decision.
- Decision
found && wifi_loaded.version == 1 && wifi_loaded.enabled <= 1UTrue- Execute
wifi_saved = wifi_loaded;
FalseContinue after this decision.
- Execute
wifi_credentials_record_t credentials_loaded = {0}; - Decision
!game_storage_read("arcade_wifi", "credentials", &credentials_loaded, sizeof(credentials_loaded), &found)True- Exit this function
return false;
FalseContinue after this decision.
- Decision
found && credentials_loaded.version == 1True- Execute
credentials_loaded.ssid[WIFI_SCAN_SSID_CHARS] = '\0'; - Execute
credentials_loaded.password[SCREEN_KEYBOARD_MAX_CHARS] = '\0'; - Decision
credentials_loaded.ssid[0] != '\0'True- Execute
wifi_credentials = credentials_loaded; - Execute
wifi_credentials_available = true;
FalseContinue after this decision.
FalseContinue after this decision.
- Exit this function
return true;
Read original function source
bool settingsInitialize(void) {
bool found = false;
settings_record_t loaded = {0};
if (!game_storage_read("arcade_settings", "display", &loaded, sizeof(loaded), &found)) return false;
if (found && (loaded.version == 1 || loaded.version == 2) && valid_brightness(loaded.brightness)) {
saved.brightness = loaded.brightness;
if (loaded.version == 2 && valid_volume(loaded.volume)) saved.volume = loaded.volume;
}
audioSetMasterVolume(saved.volume);
wifi_settings_record_t wifi_loaded = {0};
if (!game_storage_read("arcade_wifi", "settings", &wifi_loaded, sizeof(wifi_loaded), &found)) return false;
if (found && wifi_loaded.version == 1 && wifi_loaded.enabled <= 1U) wifi_saved = wifi_loaded;
wifi_credentials_record_t credentials_loaded = {0};
if (!game_storage_read("arcade_wifi", "credentials", &credentials_loaded,
sizeof(credentials_loaded), &found)) return false;
if (found && credentials_loaded.version == 1) {
credentials_loaded.ssid[WIFI_SCAN_SSID_CHARS] = '\0';
credentials_loaded.password[SCREEN_KEYBOARD_MAX_CHARS] = '\0';
if (credentials_loaded.ssid[0] != '\0') {
wifi_credentials = credentials_loaded;
wifi_credentials_available = true;
}
}
return true;
}settingsBrightness() · source line 124
uint8_t settingsBrightness(void)
- Exit this function
return saved.brightness;
Read original function source
uint8_t settingsBrightness(void) { return saved.brightness; }settingsVolume() · source line 125
uint8_t settingsVolume(void)
- Exit this function
return saved.volume;
Read original function source
uint8_t settingsVolume(void) { return saved.volume; }settingsSetVolume() · source line 126
void settingsSetVolume(uint8_t volume)
Calls into this documentation:
- Decision
!valid_volume(volume) || volume == saved.volumeTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
saved.volume = volume; - Execute
audioSetMasterVolume(saved.volume); - Execute
save_brightness();
Read original function source
void settingsSetVolume(uint8_t volume)
{
if (!valid_volume(volume) || volume == saved.volume) return;
saved.volume = volume;
audioSetMasterVolume(saved.volume);
save_brightness();
}settingsWifiEnabled() · source line 133
bool settingsWifiEnabled(void)
- Exit this function
return wifi_saved.enabled != 0U;
Read original function source
bool settingsWifiEnabled(void) { return wifi_saved.enabled != 0U; }settingsClockAutoSyncUpdate() · source line 134
void settingsClockAutoSyncUpdate(uint32_t now_ms)
Calls into this documentation:
- Decision
!wifi_credentials_availableTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
timeSyncDue(now_ms)True- Decision
!wifiServicePrepare()True- Exit this function
return;
FalseContinue after this decision.
- Decision
!wifiServiceRadioStarted()True- Decision
!wifiServiceStart()True- Exit this function
return;
FalseContinue after this decision.
- Execute
clock_sync_temporary_wifi = !settingsWifiEnabled(); - Execute
clock_sync_wifi_started = now_ms; - Execute
reconnect_saved_wifi();
False- Decision
wifiServiceConnectionState() != WIFI_CONNECTION_CONNECTED && wifiServiceConnectionState() != WIFI_CONNECTION_CONNECTINGTrue- Execute
reconnect_saved_wifi();
FalseContinue after this decision.
FalseContinue after this decision.
- Execute
timeSyncUpdate(wifiServiceConnectionState() == WIFI_CONNECTION_CONNECTED, now_ms); - Decision
clock_sync_temporary_wifi && (timeSyncStatus() == TIME_SYNC_SYNCHRONIZED || timeSyncStatus() == TIME_SYNC_FAILED || now_ms - clock_sync_wifi_started >= CLOCK_SYNC_WIFI_TIMEOUT_MS)True- Decision
now_ms - clock_sync_wifi_started >= CLOCK_SYNC_WIFI_TIMEOUT_MSTrue- Execute
timeSyncAbandonAttempt();
FalseContinue after this decision.
- Execute
(void)wifiServiceStop(); - Execute
clock_sync_temporary_wifi = false;
FalseContinue after this decision.
Read original function source
void settingsClockAutoSyncUpdate(uint32_t now_ms) {
if (!wifi_credentials_available) return;
if (timeSyncDue(now_ms)) {
if (!wifiServicePrepare()) return;
if (!wifiServiceRadioStarted()) {
if (!wifiServiceStart()) return;
clock_sync_temporary_wifi = !settingsWifiEnabled();
clock_sync_wifi_started = now_ms;
reconnect_saved_wifi();
}
else if (wifiServiceConnectionState() != WIFI_CONNECTION_CONNECTED &&
wifiServiceConnectionState() != WIFI_CONNECTION_CONNECTING) reconnect_saved_wifi();
}
timeSyncUpdate(wifiServiceConnectionState() == WIFI_CONNECTION_CONNECTED, now_ms);
if (clock_sync_temporary_wifi &&
(timeSyncStatus() == TIME_SYNC_SYNCHRONIZED || timeSyncStatus() == TIME_SYNC_FAILED ||
now_ms - clock_sync_wifi_started >= CLOCK_SYNC_WIFI_TIMEOUT_MS)) {
if (now_ms - clock_sync_wifi_started >= CLOCK_SYNC_WIFI_TIMEOUT_MS)
timeSyncAbandonAttempt();
(void)wifiServiceStop();
clock_sync_temporary_wifi = false;
}
}settingsStart() · source line 157
void settingsStart(settings_page_t page)
Calls into this documentation:
- Execute
current_page = page; - Execute
active = true; - Execute
restart_requested = false; - Execute
power_off_requested = false; - Execute
last_direction = 0; - Execute
esp_info_page = 0U; - Execute
ram_log_record_count = 0U; - Execute
ram_log_record_index = 0U; - Execute
ram_log_record[0] = '\0'; - Execute
wifi_selected_network = 0U; - Execute
wifi_keyboard_active = false; - Execute
wifi_keyboard_pressed = false; - Decision
current_page == SETTINGS_WIFITrue- Execute
(void)wifiServicePrepare(); - Decision
settingsWifiEnabled() && wifiServiceStart()True- Execute
reconnect_saved_wifi();
FalseContinue after this decision.
False- Decision
current_page == SETTINGS_RAM_LOGTrue- Execute
load_ram_log_record(true);
FalseContinue after this decision.
Read original function source
void settingsStart(settings_page_t page) {
current_page = page;
active = true;
restart_requested = false;
power_off_requested = false;
last_direction = 0;
esp_info_page = 0U;
ram_log_record_count = 0U;
ram_log_record_index = 0U;
ram_log_record[0] = '\0';
wifi_selected_network = 0U;
wifi_keyboard_active = false;
wifi_keyboard_pressed = false;
if (current_page == SETTINGS_WIFI) {
(void)wifiServicePrepare();
if (settingsWifiEnabled() && wifiServiceStart()) reconnect_saved_wifi();
} else if (current_page == SETTINGS_RAM_LOG) {
load_ram_log_record(true);
}
}settingsStop() · source line 177
void settingsStop(void)
Calls into this documentation:
- Decision
wifi_keyboard_activeTrue- Execute
cancelScreenKeyboard();
FalseContinue after this decision.
- Execute
wifi_keyboard_active = false; - Execute
wifi_keyboard_pressed = false; - Execute
active = false; - Execute
last_direction = 0;
Read original function source
void settingsStop(void) {
if (wifi_keyboard_active) cancelScreenKeyboard();
wifi_keyboard_active = false;
wifi_keyboard_pressed = false;
active = false;
last_direction = 0;
}settingsRestartRequested() · source line 184
bool settingsRestartRequested(void)
- Exit this function
return restart_requested;
Read original function source
bool settingsRestartRequested(void) { return restart_requested; }settingsPowerOffRequested() · source line 185
bool settingsPowerOffRequested(void)
- Exit this function
return power_off_requested;
Read original function source
bool settingsPowerOffRequested(void) { return power_off_requested; }settingsCancelPowerOffRequest() · source line 186
void settingsCancelPowerOffRequest(void)
- Execute
power_off_requested = false;
Read original function source
void settingsCancelPowerOffRequest(void) { power_off_requested = false; }settingsUpdate() · source line 187
void settingsUpdate(int x_offset, int y_offset, bool joystick_valid, bool left, bool right,
uint32_t now_ms)Calls into this documentation:
- Decision
!active || current_page == SETTINGS_POWER_OFF || current_page == SETTINGS_RESTART || current_page == SETTINGS_ESP_INFOTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
current_page == SETTINGS_WIFITrue- Decision
wifi_keyboard_activeTrue- Execute
const KeyboardResult result = updateScreenKeyboard(x_offset, y_offset, joystick_valid, wifi_keyboard_pressed, now_ms); - Execute
wifi_keyboard_pressed = false; - Decision
result == KEYBOARD_DONETrue- Execute
char password[SCREEN_KEYBOARD_MAX_CHARS + 1U] = {0}; - Execute
const wifi_scan_entry_t *entries = wifiServiceScanEntries(); - Decision
wifi_selected_network < wifiServiceScanCount() && getScreenKeyboardText(password, sizeof(password)) && wifiServiceConnect(entries[wifi_selected_network].ssid, password)True- Execute
(void)save_wifi_credentials(entries[wifi_selected_network].ssid, password);
FalseContinue after this decision.
- Execute
memset(password, 0, sizeof(password)); - Execute
wifi_keyboard_active = false;
False- Decision
result == KEYBOARD_CANCELLEDTrue- Execute
wifi_keyboard_active = false;
FalseContinue after this decision.
- Exit this function
return;
FalseContinue after this decision.
- Execute
wifiServiceUpdateScan(); - Execute
timeSyncUpdate(wifiServiceConnectionState() == WIFI_CONNECTION_CONNECTED, now_ms); - Execute
const size_t network_count = wifiServiceScanCount(); - Decision
wifi_selected_network >= network_count && network_count > 0UTrue- Execute
wifi_selected_network = network_count - 1U;
FalseContinue after this decision.
- Decision
network_count > 0U && joystick_valid && now_ms - wifi_selection_changed_ms >= SETTING_REPEAT_MSTrue- Decision
y_offset > 600 && wifi_selected_network > 0UTrue- Execute
--wifi_selected_network; - Execute
wifi_selection_changed_ms = now_ms;
False- Decision
y_offset < -600 && wifi_selected_network + 1U < network_countTrue- Execute
++wifi_selected_network; - Execute
wifi_selection_changed_ms = now_ms;
FalseContinue after this decision.
FalseContinue after this decision.
FalseContinue after this decision.
- Execute
const int direction = (int)right - (int)left; - Decision
!directionTrue- Execute
last_direction = 0; - Exit this function
return;
FalseContinue after this decision.
- Decision
direction == last_direction && now_ms - last_change_ms < SETTING_REPEAT_MSTrue- Exit this function
return;
FalseContinue after this decision.
- Execute
last_direction = direction; - Execute
last_change_ms = now_ms; - Decision
current_page == SETTINGS_WIFITrue- Execute
const bool enabled = direction > 0; - Decision
enabled == settingsWifiEnabled()True- Exit this function
return;
FalseContinue after this decision.
- Execute
const bool radio_changed = enabled ? wifiServiceStart() : wifiServiceStop(); - Decision
!radio_changedTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
!enabledTrue- Execute
timeSyncStop();
FalseContinue after this decision.
- Execute
wifi_saved.enabled = enabled ? 1U : 0U; - Execute
(void)save_wifi_enabled(); - Decision
enabledTrue- Execute
reconnect_saved_wifi();
FalseContinue after this decision.
False- Decision
current_page == SETTINGS_RAM_LOGTrue- Execute
load_ram_log_record(false); - Decision
direction < 0 && ram_log_record_index > 0UTrue- Execute
--ram_log_record_index;
False- Decision
direction > 0 && ram_log_record_index + 1U < ram_log_record_countTrue- Execute
++ram_log_record_index;
FalseContinue after this decision.
- Decision
ram_log_record_count > 0U && !ramMonitorReadRecord(ram_log_record_index, ram_log_record, sizeof(ram_log_record))True- Execute
ram_log_record[0] = '\0';
FalseContinue after this decision.
False- Decision
current_page == SETTINGS_BRIGHTNESSTrue- Execute
int next = saved.brightness + direction * BRIGHTNESS_STEP; - Decision
next < BRIGHTNESS_MINTrue- Execute
next = BRIGHTNESS_MIN;
FalseContinue after this decision.
- Decision
next > BRIGHTNESS_MAXTrue- Execute
next = BRIGHTNESS_MAX;
FalseContinue after this decision.
- Decision
next != saved.brightnessTrue- Execute
saved.brightness = (uint8_t)next; - Execute
save_brightness();
FalseContinue after this decision.
False- Decision
current_page == SETTINGS_VOLUMETrue- Execute
int next = saved.volume + direction * VOLUME_STEP; - Decision
next < 0True- Execute
next = 0;
FalseContinue after this decision.
- Decision
next > VOLUME_MAXTrue- Execute
next = VOLUME_MAX;
FalseContinue after this decision.
- Decision
next != saved.volumeTrue- Execute
settingsSetVolume((uint8_t)next); - Execute
audioPlay(AUDIO_SFX_MENU_MOVE);
FalseContinue after this decision.
False- Decision
current_page == SETTINGS_AI_MOVETrue- Execute
int next = getAIBaseMoveDelay() + direction * AI_MOVE_DELAY_STEP_MS; - Decision
next < AI_MOVE_DELAY_MIN_MSTrue- Execute
next = AI_MOVE_DELAY_MIN_MS;
FalseContinue after this decision.
- Decision
next > AI_MOVE_DELAY_MAX_MSTrue- Execute
next = AI_MOVE_DELAY_MAX_MS;
FalseContinue after this decision.
- Execute
(void)saveAIBaseMoveDelay((uint16_t)next);
False- Execute
int next = getAIRestartDelaySeconds() + direction; - Decision
next < AI_RESTART_DELAY_MIN_SECONDSTrue- Execute
next = AI_RESTART_DELAY_MIN_SECONDS;
FalseContinue after this decision.
- Decision
next > AI_RESTART_DELAY_MAX_SECONDSTrue- Execute
next = AI_RESTART_DELAY_MAX_SECONDS;
FalseContinue after this decision.
- Execute
(void)saveAIRestartDelaySeconds((uint8_t)next);
Read original function source
void settingsUpdate(int x_offset, int y_offset, bool joystick_valid, bool left, bool right,
uint32_t now_ms) {
if (!active || current_page == SETTINGS_POWER_OFF || current_page == SETTINGS_RESTART || current_page == SETTINGS_ESP_INFO) return;
if (current_page == SETTINGS_WIFI) {
if (wifi_keyboard_active) {
const KeyboardResult result = updateScreenKeyboard(x_offset, y_offset, joystick_valid,
wifi_keyboard_pressed, now_ms);
wifi_keyboard_pressed = false;
if (result == KEYBOARD_DONE) {
char password[SCREEN_KEYBOARD_MAX_CHARS + 1U] = {0};
const wifi_scan_entry_t *entries = wifiServiceScanEntries();
if (wifi_selected_network < wifiServiceScanCount() &&
getScreenKeyboardText(password, sizeof(password)) &&
wifiServiceConnect(entries[wifi_selected_network].ssid, password)) {
(void)save_wifi_credentials(entries[wifi_selected_network].ssid, password);
}
memset(password, 0, sizeof(password));
wifi_keyboard_active = false;
} else if (result == KEYBOARD_CANCELLED) {
wifi_keyboard_active = false;
}
return;
}
wifiServiceUpdateScan();
timeSyncUpdate(wifiServiceConnectionState() == WIFI_CONNECTION_CONNECTED, now_ms);
const size_t network_count = wifiServiceScanCount();
if (wifi_selected_network >= network_count && network_count > 0U)
wifi_selected_network = network_count - 1U;
if (network_count > 0U && joystick_valid && now_ms - wifi_selection_changed_ms >= SETTING_REPEAT_MS) {
if (y_offset > 600 && wifi_selected_network > 0U) {
--wifi_selected_network;
wifi_selection_changed_ms = now_ms;
} else if (y_offset < -600 && wifi_selected_network + 1U < network_count) {
++wifi_selected_network;
wifi_selection_changed_ms = now_ms;
}
}
}
const int direction = (int)right - (int)left;
if (!direction) { last_direction = 0; return; }
if (direction == last_direction && now_ms - last_change_ms < SETTING_REPEAT_MS) return;
last_direction = direction; last_change_ms = now_ms;
if (current_page == SETTINGS_WIFI) {
const bool enabled = direction > 0;
if (enabled == settingsWifiEnabled()) return;
const bool radio_changed = enabled ? wifiServiceStart() : wifiServiceStop();
if (!radio_changed) return;
if (!enabled) timeSyncStop();
wifi_saved.enabled = enabled ? 1U : 0U;
(void)save_wifi_enabled();
if (enabled) reconnect_saved_wifi();
} else if (current_page == SETTINGS_RAM_LOG) {
load_ram_log_record(false);
if (direction < 0 && ram_log_record_index > 0U) --ram_log_record_index;
else if (direction > 0 && ram_log_record_index + 1U < ram_log_record_count)
++ram_log_record_index;
if (ram_log_record_count > 0U &&
!ramMonitorReadRecord(ram_log_record_index, ram_log_record, sizeof(ram_log_record)))
ram_log_record[0] = '\0';
} else if (current_page == SETTINGS_BRIGHTNESS) {
int next = saved.brightness + direction * BRIGHTNESS_STEP;
if (next < BRIGHTNESS_MIN) next = BRIGHTNESS_MIN;
if (next > BRIGHTNESS_MAX) next = BRIGHTNESS_MAX;
if (next != saved.brightness) { saved.brightness = (uint8_t)next; save_brightness(); }
} else if (current_page == SETTINGS_VOLUME) {
int next = saved.volume + direction * VOLUME_STEP;
if (next < 0) next = 0;
if (next > VOLUME_MAX) next = VOLUME_MAX;
if (next != saved.volume) {
settingsSetVolume((uint8_t)next);
audioPlay(AUDIO_SFX_MENU_MOVE);
}
} else if (current_page == SETTINGS_AI_MOVE) {
int next = getAIBaseMoveDelay() + direction * AI_MOVE_DELAY_STEP_MS;
if (next < AI_MOVE_DELAY_MIN_MS) next = AI_MOVE_DELAY_MIN_MS;
if (next > AI_MOVE_DELAY_MAX_MS) next = AI_MOVE_DELAY_MAX_MS;
(void)saveAIBaseMoveDelay((uint16_t)next);
} else {
int next = getAIRestartDelaySeconds() + direction;
if (next < AI_RESTART_DELAY_MIN_SECONDS) next = AI_RESTART_DELAY_MIN_SECONDS;
if (next > AI_RESTART_DELAY_MAX_SECONDS) next = AI_RESTART_DELAY_MAX_SECONDS;
(void)saveAIRestartDelaySeconds((uint8_t)next);
}
}settingsAction() · source line 271
void settingsAction(bool select, uint32_t now_ms)
Calls into this documentation:
- Decision
!active || !selectTrue- Exit this function
return;
FalseContinue after this decision.
- Decision
current_page == SETTINGS_POWER_OFFTrue- Execute
power_off_requested = true;
False- Decision
current_page == SETTINGS_RESTARTTrue- Execute
restart_requested = true;
False- Decision
current_page == SETTINGS_WIFITrue- Decision
wifi_keyboard_activeTrue- Execute
wifi_keyboard_pressed = true;
False- Decision
wifiServiceScanCount() > 0U && !wifiServiceScanInProgress()True- Execute
startScreenKeyboard(NULL, SCREEN_KEYBOARD_MAX_CHARS, KEYBOARD_PASSWORD, "WIFI PASSWORD", now_ms); - Execute
wifi_keyboard_active = true;
False- Decision
wifiServiceRadioStarted()True- Execute
(void)wifiServiceStartScan();
FalseContinue after this decision.
False- Decision
current_page == SETTINGS_ESP_INFOTrue- Execute
esp_info_page = (esp_info_page + 1U) % ESP_INFO_PAGE_COUNT;
False- Decision
current_page == SETTINGS_RAM_LOGTrue- Execute
load_ram_log_record(true);
FalseContinue after this decision.
Read original function source
void settingsAction(bool select, uint32_t now_ms) {
if (!active || !select) return;
if (current_page == SETTINGS_POWER_OFF) power_off_requested = true;
else if (current_page == SETTINGS_RESTART) restart_requested = true;
else if (current_page == SETTINGS_WIFI) {
if (wifi_keyboard_active) {
wifi_keyboard_pressed = true;
} else if (wifiServiceScanCount() > 0U && !wifiServiceScanInProgress()) {
startScreenKeyboard(NULL, SCREEN_KEYBOARD_MAX_CHARS, KEYBOARD_PASSWORD,
"WIFI PASSWORD", now_ms);
wifi_keyboard_active = true;
} else if (wifiServiceRadioStarted()) {
(void)wifiServiceStartScan();
}
}
else if (current_page == SETTINGS_ESP_INFO)
esp_info_page = (esp_info_page + 1U) % ESP_INFO_PAGE_COUNT;
else if (current_page == SETTINGS_RAM_LOG)
load_ram_log_record(true);
}draw_info_line() · source line 292
static void draw_info_line(uint16_t *frame, int y, const char *label, const char *value)
Calls into this documentation:
- Execute
const int label_width = (int)strlen(label) * UI_FONT_ADVANCE - 1; - Execute
int maximum_characters = (312 - (8 + label_width + 8)) / UI_FONT_ADVANCE; - Decision
maximum_characters > 31True- Execute
maximum_characters = 31;
FalseContinue after this decision.
- Execute
char clipped[32]; - Execute
size_t length = strlen(value); - Decision
(int)length > maximum_charactersTrue- Execute
length = (size_t)maximum_characters;
FalseContinue after this decision.
- Execute
memcpy(clipped, value, length); - Execute
clipped[length] = '\0'; - Execute
const int value_width = (int)length * UI_FONT_ADVANCE - 1; - Execute
draw_text(frame, 8, y, label, 1, COLOR_LIGHTGREY); - Execute
draw_text(frame, 312 - value_width, y, clipped, 1, COLOR_CYAN);
Read original function source
static void draw_info_line(uint16_t *frame, int y, const char *label, const char *value)
{
const int label_width = (int)strlen(label) * UI_FONT_ADVANCE - 1;
int maximum_characters = (312 - (8 + label_width + 8)) / UI_FONT_ADVANCE;
if (maximum_characters > 31) maximum_characters = 31;
char clipped[32];
size_t length = strlen(value);
if ((int)length > maximum_characters) length = (size_t)maximum_characters;
memcpy(clipped, value, length);
clipped[length] = '\0';
const int value_width = (int)length * UI_FONT_ADVANCE - 1;
draw_text(frame, 8, y, label, 1, COLOR_LIGHTGREY);
draw_text(frame, 312 - value_width, y, clipped, 1, COLOR_CYAN);
}reset_reason_text() · source line 307
static const char *reset_reason_text(esp_reset_reason_t reason)
- Select a case
reasonEnter matching case; follow source fall-through until a break, return or end.
- Case / label
case ESP_RST_POWERON: - Exit this function
return "POWER ON"; - Case / label
case ESP_RST_EXT: - Exit this function
return "EXTERNAL"; - Case / label
case ESP_RST_SW: - Exit this function
return "SOFTWARE"; - Case / label
case ESP_RST_PANIC: - Exit this function
return "PANIC"; - Case / label
case ESP_RST_INT_WDT: - Exit this function
return "INT WDT"; - Case / label
case ESP_RST_TASK_WDT: - Exit this function
return "TASK WDT"; - Case / label
case ESP_RST_WDT: - Exit this function
return "OTHER WDT"; - Case / label
case ESP_RST_DEEPSLEEP: - Exit this function
return "DEEP SLEEP"; - Case / label
case ESP_RST_BROWNOUT: - Exit this function
return "BROWNOUT"; - Case / label
case ESP_RST_SDIO: - Exit this function
return "SDIO"; - Case / label
default: - Exit this function
return "UNKNOWN";
Read original function source
static const char *reset_reason_text(esp_reset_reason_t reason)
{
switch (reason) {
case ESP_RST_POWERON: return "POWER ON";
case ESP_RST_EXT: return "EXTERNAL";
case ESP_RST_SW: return "SOFTWARE";
case ESP_RST_PANIC: return "PANIC";
case ESP_RST_INT_WDT: return "INT WDT";
case ESP_RST_TASK_WDT: return "TASK WDT";
case ESP_RST_WDT: return "OTHER WDT";
case ESP_RST_DEEPSLEEP: return "DEEP SLEEP";
case ESP_RST_BROWNOUT: return "BROWNOUT";
case ESP_RST_SDIO: return "SDIO";
default: return "UNKNOWN";
}
}draw_esp_info() · source line 324
static void draw_esp_info(uint16_t *frame)
Calls into this documentation:
- Execute
char text[48]; - Execute
esp_chip_info_t chip = {0}; - Execute
esp_chip_info(&chip); - Execute
const uint32_t uptime_seconds = (uint32_t)(xTaskGetTickCount() * portTICK_PERIOD_MS) / 1000U; - Execute
const uint32_t uptime_days = uptime_seconds / 86400U; - Execute
const uint32_t uptime_hours = (uptime_seconds % 86400U) / 3600U; - Execute
const uint32_t uptime_minutes = (uptime_seconds % 3600U) / 60U; - Execute
const uint32_t uptime_remaining_seconds = uptime_seconds % 60U; - Execute
clear(frame, COLOR_BLACK); - Execute
snprintf(text, sizeof(text), "ESP INFO %u/%u", (unsigned)(esp_info_page + 1U), (unsigned)ESP_INFO_PAGE_COUNT); - Execute
draw_text_centred(frame, 160, 5, text, 2, COLOR_CYAN); - Decision
esp_info_page == 0UTrue- Execute
draw_info_line(frame, 29, "CHIP", "ESP32-S3"); - Execute
snprintf(text, sizeof(text), "%u CORES REV %u", (unsigned)chip.cores, (unsigned)chip.revision); - Execute
draw_info_line(frame, 43, "CPU", text); - Execute
snprintf(text, sizeof(text), "%u MHZ", (unsigned)CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ); - Execute
draw_info_line(frame, 57, "FREQ", text); - Execute
draw_info_line(frame, 71, "RESET", reset_reason_text(esp_reset_reason())); - Execute
snprintf(text, sizeof(text), "%lud %02lu:%02lu:%02lu", (unsigned long)uptime_days, (unsigned long)uptime_hours, (unsigned long)uptime_minutes, (unsigned long)uptime_remaining_seconds); - Execute
draw_info_line(frame, 85, "UPTIME", text); - Execute
draw_info_line(frame, 99, "IDF", esp_get_idf_version()); - Execute
snprintf(text, sizeof(text), "WIFI %s BLE %s", (chip.features & CHIP_FEATURE_WIFI_BGN) ? "YES" : "NO", (chip.features & CHIP_FEATURE_BLE) ? "YES" : "NO"); - Execute
draw_info_line(frame, 113, "RADIO", text);
False- Decision
esp_info_page == 1UTrue- Execute
uint32_t flash_bytes = 0U; - Execute
const esp_partition_t *running = esp_ota_get_running_partition(); - Execute
const esp_partition_t *next = esp_ota_get_next_update_partition(NULL); - Execute
esp_app_desc_t app = {0}; - Execute
const bool app_description_available = running != NULL && esp_ota_get_partition_description(running, &app) == ESP_OK; - Decision
esp_flash_get_size(NULL, &flash_bytes) == ESP_OKTrue- Execute
snprintf(text, sizeof(text), "%u MB", (unsigned)(flash_bytes / (1024U * 1024U)));
False- Execute
snprintf(text, sizeof(text), "UNAVAILABLE");
- Execute
draw_info_line(frame, 29, "FLASH", text); - Execute
draw_info_line(frame, 43, "PROJECT", app_description_available ? app.project_name : "UNKNOWN"); - Execute
draw_info_line(frame, 57, "VERSION", app_description_available ? app.version : "UNKNOWN"); - Decision
app_description_availableTrue- Execute
snprintf(text, sizeof(text), "%s %s", app.date, app.time);
False- Execute
snprintf(text, sizeof(text), "UNKNOWN");
- Execute
draw_info_line(frame, 71, "BUILD", text); - Decision
running != NULLTrue- Execute
snprintf(text, sizeof(text), "%s %uK", running->label, (unsigned)(running->size / 1024U)); - Execute
draw_info_line(frame, 85, "RUN APP", text); - Execute
snprintf(text, sizeof(text), "0X%lX", (unsigned long)running->address); - Execute
draw_info_line(frame, 99, "OFFSET", text);
FalseContinue after this decision.
- Decision
next != NULLTrue- Execute
snprintf(text, sizeof(text), "%s %uK", next->label, (unsigned)(next->size / 1024U)); - Execute
draw_info_line(frame, 113, "NEXT OTA", text);
False- Execute
draw_info_line(frame, 113, "NEXT OTA", "NONE");
False- Execute
const uint32_t internal_caps = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT; - Execute
const size_t psram_total = heap_caps_get_total_size(MALLOC_CAP_SPIRAM); - Execute
const size_t psram_free = heap_caps_get_free_size(MALLOC_CAP_SPIRAM); - Execute
const size_t psram_min = heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM); - Execute
const size_t psram_largest = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM); - Execute
const size_t internal_total = heap_caps_get_total_size(internal_caps); - Execute
const size_t internal_free = heap_caps_get_free_size(internal_caps); - Execute
const size_t internal_min = heap_caps_get_minimum_free_size(internal_caps); - Execute
const size_t internal_largest = heap_caps_get_largest_free_block(internal_caps); - Execute
const size_t app_stack_min = (size_t)uxTaskGetStackHighWaterMark(NULL) * sizeof(StackType_t); - Execute
snprintf(text, sizeof(text), "%u/%uK", (unsigned)(psram_free / 1024U), (unsigned)(psram_total / 1024U)); - Execute
draw_info_line(frame, 29, "PSRAM F/T", text); - Execute
snprintf(text, sizeof(text), "%uK", (unsigned)(psram_min / 1024U)); - Execute
draw_info_line(frame, 43, "PSRAM MIN", text); - Execute
snprintf(text, sizeof(text), "%uK", (unsigned)(psram_largest / 1024U)); - Execute
draw_info_line(frame, 57, "PSRAM MAX", text); - Execute
snprintf(text, sizeof(text), "%u/%uK", (unsigned)(internal_free / 1024U), (unsigned)(internal_total / 1024U)); - Execute
draw_info_line(frame, 71, "HEAP F/T", text); - Execute
snprintf(text, sizeof(text), "%uK", (unsigned)(internal_min / 1024U)); - Execute
draw_info_line(frame, 85, "HEAP MIN", text); - Execute
snprintf(text, sizeof(text), "%uK", (unsigned)(internal_largest / 1024U)); - Execute
draw_info_line(frame, 99, "HEAP MAX", text); - Execute
snprintf(text, sizeof(text), "%uK", (unsigned)(esp_get_free_heap_size() / 1024U)); - Execute
draw_info_line(frame, 113, "DEFAULT", text); - Execute
snprintf(text, sizeof(text), "%uK", (unsigned)(esp_get_minimum_free_heap_size() / 1024U)); - Execute
draw_info_line(frame, 127, "DEFAULT MIN", text); - Execute
snprintf(text, sizeof(text), "%uK", (unsigned)(app_stack_min / 1024U)); - Execute
draw_info_line(frame, 141, "STACK MIN", text);
- Execute
draw_text_centred(frame, 160, 157, "A/K NEXT PAGE BOOT BACK", 1, COLOR_LIGHTGREY);
Read original function source
static void draw_esp_info(uint16_t *frame)
{
char text[48];
esp_chip_info_t chip = {0};
esp_chip_info(&chip);
const uint32_t uptime_seconds = (uint32_t)(xTaskGetTickCount() * portTICK_PERIOD_MS) / 1000U;
const uint32_t uptime_days = uptime_seconds / 86400U;
const uint32_t uptime_hours = (uptime_seconds % 86400U) / 3600U;
const uint32_t uptime_minutes = (uptime_seconds % 3600U) / 60U;
const uint32_t uptime_remaining_seconds = uptime_seconds % 60U;
clear(frame, COLOR_BLACK);
snprintf(text, sizeof(text), "ESP INFO %u/%u", (unsigned)(esp_info_page + 1U),
(unsigned)ESP_INFO_PAGE_COUNT);
draw_text_centred(frame, 160, 5, text, 2, COLOR_CYAN);
if (esp_info_page == 0U) {
draw_info_line(frame, 29, "CHIP", "ESP32-S3");
snprintf(text, sizeof(text), "%u CORES REV %u", (unsigned)chip.cores,
(unsigned)chip.revision);
draw_info_line(frame, 43, "CPU", text);
snprintf(text, sizeof(text), "%u MHZ", (unsigned)CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ);
draw_info_line(frame, 57, "FREQ", text);
draw_info_line(frame, 71, "RESET", reset_reason_text(esp_reset_reason()));
snprintf(text, sizeof(text), "%lud %02lu:%02lu:%02lu", (unsigned long)uptime_days,
(unsigned long)uptime_hours, (unsigned long)uptime_minutes,
(unsigned long)uptime_remaining_seconds);
draw_info_line(frame, 85, "UPTIME", text);
draw_info_line(frame, 99, "IDF", esp_get_idf_version());
snprintf(text, sizeof(text), "WIFI %s BLE %s",
(chip.features & CHIP_FEATURE_WIFI_BGN) ? "YES" : "NO",
(chip.features & CHIP_FEATURE_BLE) ? "YES" : "NO");
draw_info_line(frame, 113, "RADIO", text);
} else if (esp_info_page == 1U) {
uint32_t flash_bytes = 0U;
const esp_partition_t *running = esp_ota_get_running_partition();
const esp_partition_t *next = esp_ota_get_next_update_partition(NULL);
esp_app_desc_t app = {0};
const bool app_description_available = running != NULL &&
esp_ota_get_partition_description(running, &app) == ESP_OK;
if (esp_flash_get_size(NULL, &flash_bytes) == ESP_OK)
snprintf(text, sizeof(text), "%u MB", (unsigned)(flash_bytes / (1024U * 1024U)));
else snprintf(text, sizeof(text), "UNAVAILABLE");
draw_info_line(frame, 29, "FLASH", text);
draw_info_line(frame, 43, "PROJECT", app_description_available ? app.project_name : "UNKNOWN");
draw_info_line(frame, 57, "VERSION", app_description_available ? app.version : "UNKNOWN");
if (app_description_available)
snprintf(text, sizeof(text), "%s %s", app.date, app.time);
else snprintf(text, sizeof(text), "UNKNOWN");
draw_info_line(frame, 71, "BUILD", text);
if (running != NULL) {
snprintf(text, sizeof(text), "%s %uK", running->label,
(unsigned)(running->size / 1024U));
draw_info_line(frame, 85, "RUN APP", text);
snprintf(text, sizeof(text), "0X%lX", (unsigned long)running->address);
draw_info_line(frame, 99, "OFFSET", text);
}
if (next != NULL) {
snprintf(text, sizeof(text), "%s %uK", next->label,
(unsigned)(next->size / 1024U));
draw_info_line(frame, 113, "NEXT OTA", text);
} else draw_info_line(frame, 113, "NEXT OTA", "NONE");
} else {
const uint32_t internal_caps = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT;
const size_t psram_total = heap_caps_get_total_size(MALLOC_CAP_SPIRAM);
const size_t psram_free = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
const size_t psram_min = heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM);
const size_t psram_largest = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
const size_t internal_total = heap_caps_get_total_size(internal_caps);
const size_t internal_free = heap_caps_get_free_size(internal_caps);
const size_t internal_min = heap_caps_get_minimum_free_size(internal_caps);
const size_t internal_largest = heap_caps_get_largest_free_block(internal_caps);
// FreeRTOS reports the smallest remaining app-task stack margin in words.
// Showing the low-water mark reveals stack pressure seen at any time since boot.
const size_t app_stack_min = (size_t)uxTaskGetStackHighWaterMark(NULL) * sizeof(StackType_t);
snprintf(text, sizeof(text), "%u/%uK", (unsigned)(psram_free / 1024U),
(unsigned)(psram_total / 1024U));
draw_info_line(frame, 29, "PSRAM F/T", text);
snprintf(text, sizeof(text), "%uK", (unsigned)(psram_min / 1024U));
draw_info_line(frame, 43, "PSRAM MIN", text);
snprintf(text, sizeof(text), "%uK", (unsigned)(psram_largest / 1024U));
draw_info_line(frame, 57, "PSRAM MAX", text);
snprintf(text, sizeof(text), "%u/%uK", (unsigned)(internal_free / 1024U),
(unsigned)(internal_total / 1024U));
draw_info_line(frame, 71, "HEAP F/T", text);
snprintf(text, sizeof(text), "%uK", (unsigned)(internal_min / 1024U));
draw_info_line(frame, 85, "HEAP MIN", text);
snprintf(text, sizeof(text), "%uK", (unsigned)(internal_largest / 1024U));
draw_info_line(frame, 99, "HEAP MAX", text);
snprintf(text, sizeof(text), "%uK", (unsigned)(esp_get_free_heap_size() / 1024U));
draw_info_line(frame, 113, "DEFAULT", text);
snprintf(text, sizeof(text), "%uK", (unsigned)(esp_get_minimum_free_heap_size() / 1024U));
draw_info_line(frame, 127, "DEFAULT MIN", text);
snprintf(text, sizeof(text), "%uK", (unsigned)(app_stack_min / 1024U));
draw_info_line(frame, 141, "STACK MIN", text);
}
draw_text_centred(frame, 160, 157, "A/K NEXT PAGE BOOT BACK", 1, COLOR_LIGHTGREY);
}draw_ram_log() · source line 423
static void draw_ram_log(uint16_t *frame)
Calls into this documentation:
- Execute
clear(frame, COLOR_BLACK); - Execute
draw_text_centred(frame, 160, 5, "RAM LOG", 2, COLOR_CYAN); - Decision
ram_log_record_count == 0UTrue- Execute
draw_text_centred(frame, 160, 65, "NO SAVED RECORDS", 2, COLOR_YELLOW); - Execute
draw_text_centred(frame, 160, 94, "WAIT ONE MINUTE", 1, COLOR_LIGHTGREY);
False- Decision
ram_log_record[0] == '\0'True- Execute
draw_text_centred(frame, 160, 65, "LOG READ ERROR", 2, COLOR_RED);
False- Execute
unsigned long uptime_seconds; - Execute
unsigned psram_free, psram_min, psram_largest; - Execute
unsigned heap_free, heap_min, heap_largest, stack_min; - Execute
const int fields = sscanf(ram_log_record, "UP=%lus PF=%uK PM=%uK PX=%uK HF=%uK HM=%uK HX=%uK SM=%uK", &uptime_seconds, &psram_free, &psram_min, &psram_largest, &heap_free, &heap_min, &heap_largest, &stack_min); - Decision
fields != 8True- Execute
draw_text_centred(frame, 160, 65, "BAD LOG RECORD", 2, COLOR_RED);
False- Execute
char text[32]; - Execute
snprintf(text, sizeof(text), "%luM %02luS", uptime_seconds / 60U, uptime_seconds % 60U); - Execute
draw_info_line(frame, 29, "UPTIME", text); - Execute
snprintf(text, sizeof(text), "F %uK M %uK", psram_free, psram_min); - Execute
draw_info_line(frame, 43, "PSRAM", text); - Execute
snprintf(text, sizeof(text), "%uK", psram_largest); - Execute
draw_info_line(frame, 57, "PSRAM MAX", text); - Execute
snprintf(text, sizeof(text), "F %uK M %uK", heap_free, heap_min); - Execute
draw_info_line(frame, 71, "HEAP", text); - Execute
snprintf(text, sizeof(text), "%uK", heap_largest); - Execute
draw_info_line(frame, 85, "HEAP MAX", text); - Execute
snprintf(text, sizeof(text), "%uK", stack_min); - Execute
draw_info_line(frame, 99, "STACK MIN", text); - Execute
draw_info_line(frame, 113, "STATUS", strstr(ram_log_record, "WARN") != NULL ? "WARNING" : "OK");
- Execute
char footer[40]; - Decision
ram_log_record_count > 0UTrue- Execute
snprintf(footer, sizeof(footer), "%u/%u D/B RECORD A/K LATEST", ram_log_record_index + 1U, ram_log_record_count);
False- Execute
snprintf(footer, sizeof(footer), "A/K REFRESH");
- Execute
draw_text_centred(frame, 160, 141, footer, 1, COLOR_CYAN); - Execute
draw_text_centred(frame, 160, 157, "BOOT BACK", 1, COLOR_LIGHTGREY);
Read original function source
static void draw_ram_log(uint16_t *frame)
{
clear(frame, COLOR_BLACK);
draw_text_centred(frame, 160, 5, "RAM LOG", 2, COLOR_CYAN);
if (ram_log_record_count == 0U) {
draw_text_centred(frame, 160, 65, "NO SAVED RECORDS", 2, COLOR_YELLOW);
draw_text_centred(frame, 160, 94, "WAIT ONE MINUTE", 1, COLOR_LIGHTGREY);
} else if (ram_log_record[0] == '\0') {
draw_text_centred(frame, 160, 65, "LOG READ ERROR", 2, COLOR_RED);
} else {
unsigned long uptime_seconds;
unsigned psram_free, psram_min, psram_largest;
unsigned heap_free, heap_min, heap_largest, stack_min;
const int fields = sscanf(ram_log_record,
"UP=%lus PF=%uK PM=%uK PX=%uK HF=%uK HM=%uK HX=%uK SM=%uK",
&uptime_seconds, &psram_free, &psram_min, &psram_largest,
&heap_free, &heap_min, &heap_largest, &stack_min);
if (fields != 8) {
draw_text_centred(frame, 160, 65, "BAD LOG RECORD", 2, COLOR_RED);
} else {
char text[32];
snprintf(text, sizeof(text), "%luM %02luS", uptime_seconds / 60U,
uptime_seconds % 60U);
draw_info_line(frame, 29, "UPTIME", text);
snprintf(text, sizeof(text), "F %uK M %uK", psram_free, psram_min);
draw_info_line(frame, 43, "PSRAM", text);
snprintf(text, sizeof(text), "%uK", psram_largest);
draw_info_line(frame, 57, "PSRAM MAX", text);
snprintf(text, sizeof(text), "F %uK M %uK", heap_free, heap_min);
draw_info_line(frame, 71, "HEAP", text);
snprintf(text, sizeof(text), "%uK", heap_largest);
draw_info_line(frame, 85, "HEAP MAX", text);
snprintf(text, sizeof(text), "%uK", stack_min);
draw_info_line(frame, 99, "STACK MIN", text);
draw_info_line(frame, 113, "STATUS",
strstr(ram_log_record, "WARN") != NULL ? "WARNING" : "OK");
}
}
char footer[40];
if (ram_log_record_count > 0U)
snprintf(footer, sizeof(footer), "%u/%u D/B RECORD A/K LATEST",
ram_log_record_index + 1U, ram_log_record_count);
else snprintf(footer, sizeof(footer), "A/K REFRESH");
draw_text_centred(frame, 160, 141, footer, 1, COLOR_CYAN);
draw_text_centred(frame, 160, 157, "BOOT BACK", 1, COLOR_LIGHTGREY);
}settingsDraw() · source line 470
void settingsDraw(uint16_t *frame)
Calls into this documentation:
- Decision
current_page == SETTINGS_WIFI && wifi_keyboard_activeTrue- Execute
drawScreenKeyboard(frame); - Exit this function
return;
FalseContinue after this decision.
- Decision
current_page == SETTINGS_ESP_INFOTrue- Execute
draw_esp_info(frame); - Exit this function
return;
FalseContinue after this decision.
- Decision
current_page == SETTINGS_RAM_LOGTrue- Execute
draw_ram_log(frame); - Exit this function
return;
FalseContinue after this decision.
- Execute
char text[48]; - Execute
clear(frame, COLOR_BLACK); - Decision
current_page == SETTINGS_BRIGHTNESSTrue- Execute
draw_text_centred(frame, 160, 24, "BRIGHTNESS", 2, COLOR_CYAN); - Execute
snprintf(text, sizeof(text), "%u", saved.brightness); - Execute
draw_text_centred(frame, 160, 70, text, 5, COLOR_WHITE); - Execute
draw_text_centred(frame, 160, 123, "D/B ADJUST", 2, COLOR_GREEN);
False- Decision
current_page == SETTINGS_VOLUMETrue- Execute
draw_text_centred(frame, 160, 24, "VOLUME", 2, COLOR_CYAN); - Execute
snprintf(text, sizeof(text), "%u%%", saved.volume); - Execute
draw_text_centred(frame, 160, 70, text, 5, COLOR_WHITE); - Execute
draw_text_centred(frame, 160, 123, "D/B ADJUST", 2, COLOR_GREEN);
False- Decision
current_page == SETTINGS_AI_MOVETrue- Execute
draw_text_centred(frame, 160, 24, "AI MOVE", 2, COLOR_CYAN); - Execute
snprintf(text, sizeof(text), "%u MS", getAIBaseMoveDelay()); - Execute
draw_text_centred(frame, 160, 75, text, 4, COLOR_WHITE); - Execute
draw_text_centred(frame, 160, 123, "D/B ADJUST", 2, COLOR_GREEN);
False- Decision
current_page == SETTINGS_AI_RESTARTTrue- Execute
draw_text_centred(frame, 160, 24, "AI RESTART", 2, COLOR_CYAN); - Execute
snprintf(text, sizeof(text), "%u SEC", getAIRestartDelaySeconds()); - Execute
draw_text_centred(frame, 160, 75, text, 4, COLOR_WHITE); - Execute
draw_text_centred(frame, 160, 123, "D/B ADJUST", 2, COLOR_GREEN);
False- Decision
current_page == SETTINGS_WIFITrue- Execute
draw_text_centred(frame, 160, 24, "WIFI", 2, COLOR_CYAN); - Decision
wifiServiceState() == WIFI_SERVICE_READYTrue- Decision
wifiServiceError()[0] != '\0'True- Execute
draw_text_centred(frame, 160, 68, "WIFI ERROR", 2, COLOR_RED); - Execute
draw_text_centred(frame, 160, 98, wifiServiceError(), 1, COLOR_YELLOW);
False- Decision
wifiServiceConnectionState() == WIFI_CONNECTION_CONNECTINGTrue- Execute
draw_text_centred(frame, 160, 68, "CONNECTING...", 2, COLOR_YELLOW); - Execute
draw_text_centred(frame, 160, 98, "WAITING FOR IP", 1, COLOR_LIGHTGREY);
False- Decision
wifiServiceConnectionState() == WIFI_CONNECTION_CONNECTEDTrue- Execute
draw_text_centred(frame, 160, 68, "CONNECTED", 2, COLOR_GREEN); - Execute
const time_sync_status_t time_status = timeSyncStatus(); - Execute
const uint16_t time_colour = time_status == TIME_SYNC_SYNCHRONIZED ? COLOR_GREEN : time_status == TIME_SYNC_FAILED ? COLOR_RED : COLOR_YELLOW; - Execute
draw_text_centred(frame, 160, 98, timeSyncStatusText(), 1, time_colour);
False- Decision
!wifiServiceRadioStarted()True- Execute
draw_text_centred(frame, 160, 60, "WIFI OFF", 3, COLOR_LIGHTGREY); - Execute
draw_text_centred(frame, 160, 99, "B ON", 2, COLOR_CYAN);
False- Decision
wifiServiceScanInProgress()True- Execute
draw_text_centred(frame, 160, 68, "SCANNING...", 2, COLOR_YELLOW);
False- Decision
wifiServiceScanCount() == 0UTrue- Execute
draw_text_centred(frame, 160, 60, "WIFI ON", 2, COLOR_GREEN); - Execute
draw_text_centred(frame, 160, 90, "A/K SCAN", 2, COLOR_CYAN);
False- Execute
const wifi_scan_entry_t *entries = wifiServiceScanEntries(); - FOR loop
size_t index = 0; index < wifiServiceScanCount(); ++indexRepeat body while the loop condition permits; then continue below.
- Execute
char line[48]; - Execute
snprintf(line, sizeof(line), "%u. %.25s %d", (unsigned)(index + 1U), entries[index].ssid[0] == '\0' ? "<HIDDEN>" : entries[index].ssid, (int)entries[index].rssi); - Execute
draw_text(frame, 8, 40 + (int)index * 14, line, 1, index == wifi_selected_network ? COLOR_CYAN : COLOR_WHITE);
- Execute
draw_text_centred(frame, 160, 150, "UP/DOWN SELECT A/K JOIN", 1, COLOR_CYAN);
False- Execute
draw_text_centred(frame, 160, 68, "SERVICE ERROR", 2, COLOR_RED); - Execute
draw_text_centred(frame, 160, 98, wifiServiceError(), 1, COLOR_YELLOW);
False- Decision
current_page == SETTINGS_POWER_OFFTrue- Execute
draw_text_centred(frame, 160, 45, "POWER OFF?", 3, COLOR_YELLOW); - Execute
draw_text_centred(frame, 160, 92, "A/K CONFIRM", 2, COLOR_RED); - Execute
draw_text_centred(frame, 160, 117, "BOOT CANCEL", 1, COLOR_LIGHTGREY);
False- Decision
current_page == SETTINGS_RESTARTTrue- Execute
draw_text_centred(frame, 160, 45, "RESTART ESP?", 3, COLOR_YELLOW); - Execute
draw_text_centred(frame, 160, 92, "A/K CONFIRM", 2, COLOR_RED);
FalseContinue after this decision.
- Execute
draw_text_centred(frame, 160, 157, "BOOT BACK", 1, COLOR_LIGHTGREY);
Read original function source
void settingsDraw(uint16_t *frame) {
if (current_page == SETTINGS_WIFI && wifi_keyboard_active) {
drawScreenKeyboard(frame);
return;
}
if (current_page == SETTINGS_ESP_INFO) {
draw_esp_info(frame);
return;
}
if (current_page == SETTINGS_RAM_LOG) {
draw_ram_log(frame);
return;
}
char text[48];
clear(frame, COLOR_BLACK);
if (current_page == SETTINGS_BRIGHTNESS) {
draw_text_centred(frame, 160, 24, "BRIGHTNESS", 2, COLOR_CYAN);
snprintf(text, sizeof(text), "%u", saved.brightness);
draw_text_centred(frame, 160, 70, text, 5, COLOR_WHITE);
draw_text_centred(frame, 160, 123, "D/B ADJUST", 2, COLOR_GREEN);
} else if (current_page == SETTINGS_VOLUME) {
draw_text_centred(frame, 160, 24, "VOLUME", 2, COLOR_CYAN);
snprintf(text, sizeof(text), "%u%%", saved.volume);
draw_text_centred(frame, 160, 70, text, 5, COLOR_WHITE);
draw_text_centred(frame, 160, 123, "D/B ADJUST", 2, COLOR_GREEN);
} else if (current_page == SETTINGS_AI_MOVE) {
draw_text_centred(frame, 160, 24, "AI MOVE", 2, COLOR_CYAN);
snprintf(text, sizeof(text), "%u MS", getAIBaseMoveDelay());
draw_text_centred(frame, 160, 75, text, 4, COLOR_WHITE);
draw_text_centred(frame, 160, 123, "D/B ADJUST", 2, COLOR_GREEN);
} else if (current_page == SETTINGS_AI_RESTART) {
draw_text_centred(frame, 160, 24, "AI RESTART", 2, COLOR_CYAN);
snprintf(text, sizeof(text), "%u SEC", getAIRestartDelaySeconds());
draw_text_centred(frame, 160, 75, text, 4, COLOR_WHITE);
draw_text_centred(frame, 160, 123, "D/B ADJUST", 2, COLOR_GREEN);
} else if (current_page == SETTINGS_WIFI) {
draw_text_centred(frame, 160, 24, "WIFI", 2, COLOR_CYAN);
if (wifiServiceState() == WIFI_SERVICE_READY) {
if (wifiServiceError()[0] != '\0') {
draw_text_centred(frame, 160, 68, "WIFI ERROR", 2, COLOR_RED);
draw_text_centred(frame, 160, 98, wifiServiceError(), 1, COLOR_YELLOW);
} else if (wifiServiceConnectionState() == WIFI_CONNECTION_CONNECTING) {
draw_text_centred(frame, 160, 68, "CONNECTING...", 2, COLOR_YELLOW);
draw_text_centred(frame, 160, 98, "WAITING FOR IP", 1, COLOR_LIGHTGREY);
} else if (wifiServiceConnectionState() == WIFI_CONNECTION_CONNECTED) {
draw_text_centred(frame, 160, 68, "CONNECTED", 2, COLOR_GREEN);
const time_sync_status_t time_status = timeSyncStatus();
const uint16_t time_colour = time_status == TIME_SYNC_SYNCHRONIZED ? COLOR_GREEN :
time_status == TIME_SYNC_FAILED ? COLOR_RED : COLOR_YELLOW;
draw_text_centred(frame, 160, 98, timeSyncStatusText(), 1, time_colour);
} else {
if (!wifiServiceRadioStarted()) {
draw_text_centred(frame, 160, 60, "WIFI OFF", 3, COLOR_LIGHTGREY);
draw_text_centred(frame, 160, 99, "B ON", 2, COLOR_CYAN);
} else if (wifiServiceScanInProgress()) {
draw_text_centred(frame, 160, 68, "SCANNING...", 2, COLOR_YELLOW);
} else if (wifiServiceScanCount() == 0U) {
draw_text_centred(frame, 160, 60, "WIFI ON", 2, COLOR_GREEN);
draw_text_centred(frame, 160, 90, "A/K SCAN", 2, COLOR_CYAN);
} else {
const wifi_scan_entry_t *entries = wifiServiceScanEntries();
for (size_t index = 0; index < wifiServiceScanCount(); ++index) {
char line[48];
snprintf(line, sizeof(line), "%u. %.25s %d", (unsigned)(index + 1U),
entries[index].ssid[0] == '\0' ? "<HIDDEN>" : entries[index].ssid,
(int)entries[index].rssi);
draw_text(frame, 8, 40 + (int)index * 14, line, 1,
index == wifi_selected_network ? COLOR_CYAN : COLOR_WHITE);
}
draw_text_centred(frame, 160, 150, "UP/DOWN SELECT A/K JOIN", 1, COLOR_CYAN);
}
}
} else {
draw_text_centred(frame, 160, 68, "SERVICE ERROR", 2, COLOR_RED);
draw_text_centred(frame, 160, 98, wifiServiceError(), 1, COLOR_YELLOW);
}
} else if (current_page == SETTINGS_POWER_OFF) {
draw_text_centred(frame, 160, 45, "POWER OFF?", 3, COLOR_YELLOW);
draw_text_centred(frame, 160, 92, "A/K CONFIRM", 2, COLOR_RED);
draw_text_centred(frame, 160, 117, "BOOT CANCEL", 1, COLOR_LIGHTGREY);
} else if (current_page == SETTINGS_RESTART) {
draw_text_centred(frame, 160, 45, "RESTART ESP?", 3, COLOR_YELLOW);
draw_text_centred(frame, 160, 92, "A/K CONFIRM", 2, COLOR_RED);
}
draw_text_centred(frame, 160, 157, "BOOT BACK", 1, COLOR_LIGHTGREY);
}Complete original file, including constants and data
/* Coded By JD(2026) */
#error "Reference listing only — not provided as build-ready source."
#include "settings.h"
#include <stdio.h>
#include <string.h>
#include "ai_speed.h"
#include "audio.h"
#include "esp_app_desc.h"
#include "esp_chip_info.h"
#include "esp_err.h"
#include "esp_flash.h"
#include "esp_heap_caps.h"
#include "esp_idf_version.h"
#include "esp_ota_ops.h"
#include "esp_system.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "game_colors.h"
#include "game_graphics.h"
#include "PET2001_font.h"
#include "game_storage.h"
#include "ram_monitor.h"
#include "screen_keyboard.h"
#include "sdkconfig.h"
#include "time_sync.h"
#include "wifi.h"
#define BRIGHTNESS_MIN 32U
#define BRIGHTNESS_MAX 252U
#define BRIGHTNESS_STEP 5U
#define VOLUME_MAX 100U
#define VOLUME_STEP 5U
#define SETTING_REPEAT_MS 160U
#define ESP_INFO_PAGE_COUNT 3U
#define CLOCK_SYNC_WIFI_TIMEOUT_MS 30000U
typedef struct { uint32_t version; uint8_t brightness; uint8_t volume; uint8_t reserved[2]; } settings_record_t;
typedef struct { uint32_t version; uint8_t enabled; uint8_t reserved[3]; } wifi_settings_record_t;
typedef struct { uint32_t version; char ssid[WIFI_SCAN_SSID_CHARS + 1U]; char password[SCREEN_KEYBOARD_MAX_CHARS + 1U]; } wifi_credentials_record_t;
static settings_record_t saved = {2, 128, 100, {0, 0}};
static wifi_settings_record_t wifi_saved = {1, 0, {0, 0, 0}};
static wifi_credentials_record_t wifi_credentials = {1, {0}, {0}};
static bool wifi_credentials_available;
static settings_page_t current_page;
static bool active;
static bool restart_requested;
static bool power_off_requested;
static int last_direction;
static uint32_t last_change_ms;
static uint8_t esp_info_page;
static unsigned ram_log_record_count;
static unsigned ram_log_record_index;
static char ram_log_record[RAM_MONITOR_RECORD_CHARS];
static size_t wifi_selected_network;
static uint32_t wifi_selection_changed_ms;
static bool wifi_keyboard_active;
static bool wifi_keyboard_pressed;
static bool clock_sync_temporary_wifi;
static uint32_t clock_sync_wifi_started;
static bool valid_brightness(uint8_t value) {
return value >= BRIGHTNESS_MIN && value <= BRIGHTNESS_MAX &&
(value - BRIGHTNESS_MIN) % BRIGHTNESS_STEP == 0;
}
static bool valid_volume(uint8_t value) { return value <= VOLUME_MAX && value % VOLUME_STEP == 0U; }
static void load_ram_log_record(bool newest)
{
ram_log_record_count = ramMonitorRecordCount();
if (ram_log_record_count == 0U) {
ram_log_record_index = 0U;
ram_log_record[0] = '\0';
return;
}
if (newest || ram_log_record_index >= ram_log_record_count)
ram_log_record_index = ram_log_record_count - 1U;
if (!ramMonitorReadRecord(ram_log_record_index, ram_log_record, sizeof(ram_log_record)))
ram_log_record[0] = '\0';
}
static void save_brightness(void) {
(void)game_storage_write("arcade_settings", "display", &saved, sizeof(saved));
}
static bool save_wifi_enabled(void) {
return game_storage_write("arcade_wifi", "settings", &wifi_saved, sizeof(wifi_saved));
}
static bool save_wifi_credentials(const char *ssid, const char *password) {
wifi_credentials = (wifi_credentials_record_t){1, {0}, {0}};
strncpy(wifi_credentials.ssid, ssid, sizeof(wifi_credentials.ssid) - 1U);
strncpy(wifi_credentials.password, password, sizeof(wifi_credentials.password) - 1U);
wifi_credentials_available = true;
return game_storage_write("arcade_wifi", "credentials", &wifi_credentials,
sizeof(wifi_credentials));
}
static void reconnect_saved_wifi(void) {
if (wifi_credentials_available &&
wifiServiceConnectionState() != WIFI_CONNECTION_CONNECTED &&
wifiServiceConnectionState() != WIFI_CONNECTION_CONNECTING)
(void)wifiServiceConnect(wifi_credentials.ssid, wifi_credentials.password);
}
bool settingsInitialize(void) {
bool found = false;
settings_record_t loaded = {0};
if (!game_storage_read("arcade_settings", "display", &loaded, sizeof(loaded), &found)) return false;
if (found && (loaded.version == 1 || loaded.version == 2) && valid_brightness(loaded.brightness)) {
saved.brightness = loaded.brightness;
if (loaded.version == 2 && valid_volume(loaded.volume)) saved.volume = loaded.volume;
}
audioSetMasterVolume(saved.volume);
wifi_settings_record_t wifi_loaded = {0};
if (!game_storage_read("arcade_wifi", "settings", &wifi_loaded, sizeof(wifi_loaded), &found)) return false;
if (found && wifi_loaded.version == 1 && wifi_loaded.enabled <= 1U) wifi_saved = wifi_loaded;
wifi_credentials_record_t credentials_loaded = {0};
if (!game_storage_read("arcade_wifi", "credentials", &credentials_loaded,
sizeof(credentials_loaded), &found)) return false;
if (found && credentials_loaded.version == 1) {
credentials_loaded.ssid[WIFI_SCAN_SSID_CHARS] = '\0';
credentials_loaded.password[SCREEN_KEYBOARD_MAX_CHARS] = '\0';
if (credentials_loaded.ssid[0] != '\0') {
wifi_credentials = credentials_loaded;
wifi_credentials_available = true;
}
}
return true;
}
uint8_t settingsBrightness(void) { return saved.brightness; }
uint8_t settingsVolume(void) { return saved.volume; }
void settingsSetVolume(uint8_t volume)
{
if (!valid_volume(volume) || volume == saved.volume) return;
saved.volume = volume;
audioSetMasterVolume(saved.volume);
save_brightness();
}
bool settingsWifiEnabled(void) { return wifi_saved.enabled != 0U; }
void settingsClockAutoSyncUpdate(uint32_t now_ms) {
if (!wifi_credentials_available) return;
if (timeSyncDue(now_ms)) {
if (!wifiServicePrepare()) return;
if (!wifiServiceRadioStarted()) {
if (!wifiServiceStart()) return;
clock_sync_temporary_wifi = !settingsWifiEnabled();
clock_sync_wifi_started = now_ms;
reconnect_saved_wifi();
}
else if (wifiServiceConnectionState() != WIFI_CONNECTION_CONNECTED &&
wifiServiceConnectionState() != WIFI_CONNECTION_CONNECTING) reconnect_saved_wifi();
}
timeSyncUpdate(wifiServiceConnectionState() == WIFI_CONNECTION_CONNECTED, now_ms);
if (clock_sync_temporary_wifi &&
(timeSyncStatus() == TIME_SYNC_SYNCHRONIZED || timeSyncStatus() == TIME_SYNC_FAILED ||
now_ms - clock_sync_wifi_started >= CLOCK_SYNC_WIFI_TIMEOUT_MS)) {
if (now_ms - clock_sync_wifi_started >= CLOCK_SYNC_WIFI_TIMEOUT_MS)
timeSyncAbandonAttempt();
(void)wifiServiceStop();
clock_sync_temporary_wifi = false;
}
}
void settingsStart(settings_page_t page) {
current_page = page;
active = true;
restart_requested = false;
power_off_requested = false;
last_direction = 0;
esp_info_page = 0U;
ram_log_record_count = 0U;
ram_log_record_index = 0U;
ram_log_record[0] = '\0';
wifi_selected_network = 0U;
wifi_keyboard_active = false;
wifi_keyboard_pressed = false;
if (current_page == SETTINGS_WIFI) {
(void)wifiServicePrepare();
if (settingsWifiEnabled() && wifiServiceStart()) reconnect_saved_wifi();
} else if (current_page == SETTINGS_RAM_LOG) {
load_ram_log_record(true);
}
}
void settingsStop(void) {
if (wifi_keyboard_active) cancelScreenKeyboard();
wifi_keyboard_active = false;
wifi_keyboard_pressed = false;
active = false;
last_direction = 0;
}
bool settingsRestartRequested(void) { return restart_requested; }
bool settingsPowerOffRequested(void) { return power_off_requested; }
void settingsCancelPowerOffRequest(void) { power_off_requested = false; }
void settingsUpdate(int x_offset, int y_offset, bool joystick_valid, bool left, bool right,
uint32_t now_ms) {
if (!active || current_page == SETTINGS_POWER_OFF || current_page == SETTINGS_RESTART || current_page == SETTINGS_ESP_INFO) return;
if (current_page == SETTINGS_WIFI) {
if (wifi_keyboard_active) {
const KeyboardResult result = updateScreenKeyboard(x_offset, y_offset, joystick_valid,
wifi_keyboard_pressed, now_ms);
wifi_keyboard_pressed = false;
if (result == KEYBOARD_DONE) {
char password[SCREEN_KEYBOARD_MAX_CHARS + 1U] = {0};
const wifi_scan_entry_t *entries = wifiServiceScanEntries();
if (wifi_selected_network < wifiServiceScanCount() &&
getScreenKeyboardText(password, sizeof(password)) &&
wifiServiceConnect(entries[wifi_selected_network].ssid, password)) {
(void)save_wifi_credentials(entries[wifi_selected_network].ssid, password);
}
memset(password, 0, sizeof(password));
wifi_keyboard_active = false;
} else if (result == KEYBOARD_CANCELLED) {
wifi_keyboard_active = false;
}
return;
}
wifiServiceUpdateScan();
timeSyncUpdate(wifiServiceConnectionState() == WIFI_CONNECTION_CONNECTED, now_ms);
const size_t network_count = wifiServiceScanCount();
if (wifi_selected_network >= network_count && network_count > 0U)
wifi_selected_network = network_count - 1U;
if (network_count > 0U && joystick_valid && now_ms - wifi_selection_changed_ms >= SETTING_REPEAT_MS) {
if (y_offset > 600 && wifi_selected_network > 0U) {
--wifi_selected_network;
wifi_selection_changed_ms = now_ms;
} else if (y_offset < -600 && wifi_selected_network + 1U < network_count) {
++wifi_selected_network;
wifi_selection_changed_ms = now_ms;
}
}
}
const int direction = (int)right - (int)left;
if (!direction) { last_direction = 0; return; }
if (direction == last_direction && now_ms - last_change_ms < SETTING_REPEAT_MS) return;
last_direction = direction; last_change_ms = now_ms;
if (current_page == SETTINGS_WIFI) {
const bool enabled = direction > 0;
if (enabled == settingsWifiEnabled()) return;
const bool radio_changed = enabled ? wifiServiceStart() : wifiServiceStop();
if (!radio_changed) return;
if (!enabled) timeSyncStop();
wifi_saved.enabled = enabled ? 1U : 0U;
(void)save_wifi_enabled();
if (enabled) reconnect_saved_wifi();
} else if (current_page == SETTINGS_RAM_LOG) {
load_ram_log_record(false);
if (direction < 0 && ram_log_record_index > 0U) --ram_log_record_index;
else if (direction > 0 && ram_log_record_index + 1U < ram_log_record_count)
++ram_log_record_index;
if (ram_log_record_count > 0U &&
!ramMonitorReadRecord(ram_log_record_index, ram_log_record, sizeof(ram_log_record)))
ram_log_record[0] = '\0';
} else if (current_page == SETTINGS_BRIGHTNESS) {
int next = saved.brightness + direction * BRIGHTNESS_STEP;
if (next < BRIGHTNESS_MIN) next = BRIGHTNESS_MIN;
if (next > BRIGHTNESS_MAX) next = BRIGHTNESS_MAX;
if (next != saved.brightness) { saved.brightness = (uint8_t)next; save_brightness(); }
} else if (current_page == SETTINGS_VOLUME) {
int next = saved.volume + direction * VOLUME_STEP;
if (next < 0) next = 0;
if (next > VOLUME_MAX) next = VOLUME_MAX;
if (next != saved.volume) {
settingsSetVolume((uint8_t)next);
audioPlay(AUDIO_SFX_MENU_MOVE);
}
} else if (current_page == SETTINGS_AI_MOVE) {
int next = getAIBaseMoveDelay() + direction * AI_MOVE_DELAY_STEP_MS;
if (next < AI_MOVE_DELAY_MIN_MS) next = AI_MOVE_DELAY_MIN_MS;
if (next > AI_MOVE_DELAY_MAX_MS) next = AI_MOVE_DELAY_MAX_MS;
(void)saveAIBaseMoveDelay((uint16_t)next);
} else {
int next = getAIRestartDelaySeconds() + direction;
if (next < AI_RESTART_DELAY_MIN_SECONDS) next = AI_RESTART_DELAY_MIN_SECONDS;
if (next > AI_RESTART_DELAY_MAX_SECONDS) next = AI_RESTART_DELAY_MAX_SECONDS;
(void)saveAIRestartDelaySeconds((uint8_t)next);
}
}
void settingsAction(bool select, uint32_t now_ms) {
if (!active || !select) return;
if (current_page == SETTINGS_POWER_OFF) power_off_requested = true;
else if (current_page == SETTINGS_RESTART) restart_requested = true;
else if (current_page == SETTINGS_WIFI) {
if (wifi_keyboard_active) {
wifi_keyboard_pressed = true;
} else if (wifiServiceScanCount() > 0U && !wifiServiceScanInProgress()) {
startScreenKeyboard(NULL, SCREEN_KEYBOARD_MAX_CHARS, KEYBOARD_PASSWORD,
"WIFI PASSWORD", now_ms);
wifi_keyboard_active = true;
} else if (wifiServiceRadioStarted()) {
(void)wifiServiceStartScan();
}
}
else if (current_page == SETTINGS_ESP_INFO)
esp_info_page = (esp_info_page + 1U) % ESP_INFO_PAGE_COUNT;
else if (current_page == SETTINGS_RAM_LOG)
load_ram_log_record(true);
}
static void draw_info_line(uint16_t *frame, int y, const char *label, const char *value)
{
const int label_width = (int)strlen(label) * PET2001_FONT_GLYPH_WIDTH;
int maximum_characters = (312 - (8 + label_width + 8)) / PET2001_FONT_GLYPH_WIDTH;
if (maximum_characters > 31) maximum_characters = 31;
char clipped[32];
size_t length = strlen(value);
if ((int)length > maximum_characters) length = (size_t)maximum_characters;
memcpy(clipped, value, length);
clipped[length] = '\0';
const int value_width = (int)length * PET2001_FONT_GLYPH_WIDTH;
drawPet2001TextTransparent(frame, 8, y, label, COLOR_LIGHTGREY, 1);
drawPet2001TextTransparent(frame, 312 - value_width, y, clipped, COLOR_CYAN, 1);
}
static const char *reset_reason_text(esp_reset_reason_t reason)
{
switch (reason) {
case ESP_RST_POWERON: return "POWER ON";
case ESP_RST_EXT: return "EXTERNAL";
case ESP_RST_SW: return "SOFTWARE";
case ESP_RST_PANIC: return "PANIC";
case ESP_RST_INT_WDT: return "INT WDT";
case ESP_RST_TASK_WDT: return "TASK WDT";
case ESP_RST_WDT: return "OTHER WDT";
case ESP_RST_DEEPSLEEP: return "DEEP SLEEP";
case ESP_RST_BROWNOUT: return "BROWNOUT";
case ESP_RST_SDIO: return "SDIO";
default: return "UNKNOWN";
}
}
static void draw_esp_info(uint16_t *frame)
{
char text[48];
esp_chip_info_t chip = {0};
esp_chip_info(&chip);
const uint32_t uptime_seconds = (uint32_t)(xTaskGetTickCount() * portTICK_PERIOD_MS) / 1000U;
const uint32_t uptime_days = uptime_seconds / 86400U;
const uint32_t uptime_hours = (uptime_seconds % 86400U) / 3600U;
const uint32_t uptime_minutes = (uptime_seconds % 3600U) / 60U;
const uint32_t uptime_remaining_seconds = uptime_seconds % 60U;
clear(frame, COLOR_BLACK);
snprintf(text, sizeof(text), "ESP INFO %u/%u", (unsigned)(esp_info_page + 1U),
(unsigned)ESP_INFO_PAGE_COUNT);
drawPet2001TextCentredTransparent(frame, 160, 5, text, COLOR_CYAN, 2);
if (esp_info_page == 0U) {
draw_info_line(frame, 29, "CHIP", "ESP32-S3");
snprintf(text, sizeof(text), "%u CORES REV %u", (unsigned)chip.cores,
(unsigned)chip.revision);
draw_info_line(frame, 43, "CPU", text);
snprintf(text, sizeof(text), "%u MHZ", (unsigned)CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ);
draw_info_line(frame, 57, "FREQ", text);
draw_info_line(frame, 71, "RESET", reset_reason_text(esp_reset_reason()));
snprintf(text, sizeof(text), "%lud %02lu:%02lu:%02lu", (unsigned long)uptime_days,
(unsigned long)uptime_hours, (unsigned long)uptime_minutes,
(unsigned long)uptime_remaining_seconds);
draw_info_line(frame, 85, "UPTIME", text);
draw_info_line(frame, 99, "IDF", esp_get_idf_version());
snprintf(text, sizeof(text), "WIFI %s BLE %s",
(chip.features & CHIP_FEATURE_WIFI_BGN) ? "YES" : "NO",
(chip.features & CHIP_FEATURE_BLE) ? "YES" : "NO");
draw_info_line(frame, 113, "RADIO", text);
} else if (esp_info_page == 1U) {
uint32_t flash_bytes = 0U;
const esp_partition_t *running = esp_ota_get_running_partition();
const esp_partition_t *next = esp_ota_get_next_update_partition(NULL);
esp_app_desc_t app = {0};
const bool app_description_available = running != NULL &&
esp_ota_get_partition_description(running, &app) == ESP_OK;
if (esp_flash_get_size(NULL, &flash_bytes) == ESP_OK)
snprintf(text, sizeof(text), "%u MB", (unsigned)(flash_bytes / (1024U * 1024U)));
else snprintf(text, sizeof(text), "UNAVAILABLE");
draw_info_line(frame, 29, "FLASH", text);
draw_info_line(frame, 43, "PROJECT", app_description_available ? app.project_name : "UNKNOWN");
draw_info_line(frame, 57, "VERSION", app_description_available ? app.version : "UNKNOWN");
if (app_description_available)
snprintf(text, sizeof(text), "%s %s", app.date, app.time);
else snprintf(text, sizeof(text), "UNKNOWN");
draw_info_line(frame, 71, "BUILD", text);
if (running != NULL) {
snprintf(text, sizeof(text), "%s %uK", running->label,
(unsigned)(running->size / 1024U));
draw_info_line(frame, 85, "RUN APP", text);
snprintf(text, sizeof(text), "0X%lX", (unsigned long)running->address);
draw_info_line(frame, 99, "OFFSET", text);
}
if (next != NULL) {
snprintf(text, sizeof(text), "%s %uK", next->label,
(unsigned)(next->size / 1024U));
draw_info_line(frame, 113, "NEXT OTA", text);
} else draw_info_line(frame, 113, "NEXT OTA", "NONE");
} else {
const uint32_t internal_caps = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT;
const size_t psram_total = heap_caps_get_total_size(MALLOC_CAP_SPIRAM);
const size_t psram_free = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
const size_t psram_min = heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM);
const size_t psram_largest = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
const size_t internal_total = heap_caps_get_total_size(internal_caps);
const size_t internal_free = heap_caps_get_free_size(internal_caps);
const size_t internal_min = heap_caps_get_minimum_free_size(internal_caps);
const size_t internal_largest = heap_caps_get_largest_free_block(internal_caps);
// FreeRTOS reports the smallest remaining app-task stack margin in words.
// Showing the low-water mark reveals stack pressure seen at any time since boot.
const size_t app_stack_min = (size_t)uxTaskGetStackHighWaterMark(NULL) * sizeof(StackType_t);
snprintf(text, sizeof(text), "%u/%uK", (unsigned)(psram_free / 1024U),
(unsigned)(psram_total / 1024U));
draw_info_line(frame, 29, "PSRAM F/T", text);
snprintf(text, sizeof(text), "%uK", (unsigned)(psram_min / 1024U));
draw_info_line(frame, 43, "PSRAM MIN", text);
snprintf(text, sizeof(text), "%uK", (unsigned)(psram_largest / 1024U));
draw_info_line(frame, 57, "PSRAM MAX", text);
snprintf(text, sizeof(text), "%u/%uK", (unsigned)(internal_free / 1024U),
(unsigned)(internal_total / 1024U));
draw_info_line(frame, 71, "HEAP F/T", text);
snprintf(text, sizeof(text), "%uK", (unsigned)(internal_min / 1024U));
draw_info_line(frame, 85, "HEAP MIN", text);
snprintf(text, sizeof(text), "%uK", (unsigned)(internal_largest / 1024U));
draw_info_line(frame, 99, "HEAP MAX", text);
snprintf(text, sizeof(text), "%uK", (unsigned)(esp_get_free_heap_size() / 1024U));
draw_info_line(frame, 113, "DEFAULT", text);
snprintf(text, sizeof(text), "%uK", (unsigned)(esp_get_minimum_free_heap_size() / 1024U));
draw_info_line(frame, 127, "DEFAULT MIN", text);
snprintf(text, sizeof(text), "%uK", (unsigned)(app_stack_min / 1024U));
draw_info_line(frame, 141, "STACK MIN", text);
}
drawPet2001TextCentredTransparent(frame, 160, 157, "A/K NEXT PAGE BOOT BACK", COLOR_LIGHTGREY, 1);
}
static void draw_ram_log(uint16_t *frame)
{
clear(frame, COLOR_BLACK);
drawPet2001TextCentredTransparent(frame, 160, 5, "RAM LOG", COLOR_CYAN, 2);
if (ram_log_record_count == 0U) {
drawPet2001TextCentredTransparent(frame, 160, 65, "NO SAVED RECORDS", COLOR_YELLOW, 2);
drawPet2001TextCentredTransparent(frame, 160, 94, "WAIT ONE MINUTE", COLOR_LIGHTGREY, 1);
} else if (ram_log_record[0] == '\0') {
drawPet2001TextCentredTransparent(frame, 160, 65, "LOG READ ERROR", COLOR_RED, 2);
} else {
unsigned long uptime_seconds;
unsigned psram_free, psram_min, psram_largest;
unsigned heap_free, heap_min, heap_largest, stack_min;
const int fields = sscanf(ram_log_record,
"UP=%lus PF=%uK PM=%uK PX=%uK HF=%uK HM=%uK HX=%uK SM=%uK",
&uptime_seconds, &psram_free, &psram_min, &psram_largest,
&heap_free, &heap_min, &heap_largest, &stack_min);
if (fields != 8) {
drawPet2001TextCentredTransparent(frame, 160, 65, "BAD LOG RECORD", COLOR_RED, 2);
} else {
char text[32];
snprintf(text, sizeof(text), "%luM %02luS", uptime_seconds / 60U,
uptime_seconds % 60U);
draw_info_line(frame, 29, "UPTIME", text);
snprintf(text, sizeof(text), "F %uK M %uK", psram_free, psram_min);
draw_info_line(frame, 43, "PSRAM", text);
snprintf(text, sizeof(text), "%uK", psram_largest);
draw_info_line(frame, 57, "PSRAM MAX", text);
snprintf(text, sizeof(text), "F %uK M %uK", heap_free, heap_min);
draw_info_line(frame, 71, "HEAP", text);
snprintf(text, sizeof(text), "%uK", heap_largest);
draw_info_line(frame, 85, "HEAP MAX", text);
snprintf(text, sizeof(text), "%uK", stack_min);
draw_info_line(frame, 99, "STACK MIN", text);
draw_info_line(frame, 113, "STATUS",
strstr(ram_log_record, "WARN") != NULL ? "WARNING" : "OK");
}
}
char footer[40];
if (ram_log_record_count > 0U)
snprintf(footer, sizeof(footer), "%u/%u D/B RECORD A/K LATEST",
ram_log_record_index + 1U, ram_log_record_count);
else snprintf(footer, sizeof(footer), "A/K REFRESH");
drawPet2001TextCentredTransparent(frame, 160, 141, footer, COLOR_CYAN, 1);
drawPet2001TextCentredTransparent(frame, 160, 157, "BOOT BACK", COLOR_LIGHTGREY, 1);
}
void settingsDraw(uint16_t *frame) {
if (current_page == SETTINGS_WIFI && wifi_keyboard_active) {
drawScreenKeyboard(frame);
return;
}
if (current_page == SETTINGS_ESP_INFO) {
draw_esp_info(frame);
return;
}
if (current_page == SETTINGS_RAM_LOG) {
draw_ram_log(frame);
return;
}
char text[48];
clear(frame, COLOR_BLACK);
if (current_page == SETTINGS_BRIGHTNESS) {
drawPet2001TextCentredTransparent(frame, 160, 24, "BRIGHTNESS", COLOR_CYAN, 2);
snprintf(text, sizeof(text), "%u", saved.brightness);
drawPet2001TextCentredTransparent(frame, 160, 70, text, COLOR_WHITE, 5);
drawPet2001TextCentredTransparent(frame, 160, 123, "D/B ADJUST", COLOR_GREEN, 2);
} else if (current_page == SETTINGS_VOLUME) {
drawPet2001TextCentredTransparent(frame, 160, 24, "VOLUME", COLOR_CYAN, 2);
snprintf(text, sizeof(text), "%u%%", saved.volume);
drawPet2001TextCentredTransparent(frame, 160, 70, text, COLOR_WHITE, 5);
drawPet2001TextCentredTransparent(frame, 160, 123, "D/B ADJUST", COLOR_GREEN, 2);
} else if (current_page == SETTINGS_AI_MOVE) {
drawPet2001TextCentredTransparent(frame, 160, 24, "AI MOVE", COLOR_CYAN, 2);
snprintf(text, sizeof(text), "%u MS", getAIBaseMoveDelay());
drawPet2001TextCentredTransparent(frame, 160, 75, text, COLOR_WHITE, 4);
drawPet2001TextCentredTransparent(frame, 160, 123, "D/B ADJUST", COLOR_GREEN, 2);
} else if (current_page == SETTINGS_AI_RESTART) {
drawPet2001TextCentredTransparent(frame, 160, 24, "AI RESTART", COLOR_CYAN, 2);
snprintf(text, sizeof(text), "%u SEC", getAIRestartDelaySeconds());
drawPet2001TextCentredTransparent(frame, 160, 75, text, COLOR_WHITE, 4);
drawPet2001TextCentredTransparent(frame, 160, 123, "D/B ADJUST", COLOR_GREEN, 2);
} else if (current_page == SETTINGS_WIFI) {
drawPet2001TextCentredTransparent(frame, 160, 24, "WIFI", COLOR_CYAN, 2);
if (wifiServiceState() == WIFI_SERVICE_READY) {
if (wifiServiceError()[0] != '\0') {
drawPet2001TextCentredTransparent(frame, 160, 68, "WIFI ERROR", COLOR_RED, 2);
drawPet2001TextCentredTransparent(frame, 160, 98, wifiServiceError(), COLOR_YELLOW, 1);
} else if (wifiServiceConnectionState() == WIFI_CONNECTION_CONNECTING) {
drawPet2001TextCentredTransparent(frame, 160, 68, "CONNECTING...", COLOR_YELLOW, 2);
drawPet2001TextCentredTransparent(frame, 160, 98, "WAITING FOR IP", COLOR_LIGHTGREY, 1);
} else if (wifiServiceConnectionState() == WIFI_CONNECTION_CONNECTED) {
drawPet2001TextCentredTransparent(frame, 160, 68, "CONNECTED", COLOR_GREEN, 2);
const time_sync_status_t time_status = timeSyncStatus();
const uint16_t time_colour = time_status == TIME_SYNC_SYNCHRONIZED ? COLOR_GREEN :
time_status == TIME_SYNC_FAILED ? COLOR_RED : COLOR_YELLOW;
drawPet2001TextCentredTransparent(frame, 160, 98, timeSyncStatusText(), time_colour, 1);
} else {
if (!wifiServiceRadioStarted()) {
drawPet2001TextCentredTransparent(frame, 160, 60, "WIFI OFF", COLOR_LIGHTGREY, 3);
drawPet2001TextCentredTransparent(frame, 160, 99, "B ON", COLOR_CYAN, 2);
} else if (wifiServiceScanInProgress()) {
drawPet2001TextCentredTransparent(frame, 160, 68, "SCANNING...", COLOR_YELLOW, 2);
} else if (wifiServiceScanCount() == 0U) {
drawPet2001TextCentredTransparent(frame, 160, 60, "WIFI ON", COLOR_GREEN, 2);
drawPet2001TextCentredTransparent(frame, 160, 90, "A/K SCAN", COLOR_CYAN, 2);
} else {
const wifi_scan_entry_t *entries = wifiServiceScanEntries();
for (size_t index = 0; index < wifiServiceScanCount(); ++index) {
char line[48];
snprintf(line, sizeof(line), "%u. %.25s %d", (unsigned)(index + 1U),
entries[index].ssid[0] == '\0' ? "<HIDDEN>" : entries[index].ssid,
(int)entries[index].rssi);
drawPet2001TextTransparent(frame, 8, 40 + (int)index * 14, line,
index == wifi_selected_network ? COLOR_CYAN : COLOR_WHITE, 1);
}
drawPet2001TextCentredTransparent(frame, 160, 150, "UP/DOWN SELECT A/K JOIN", COLOR_CYAN, 1);
}
}
} else {
drawPet2001TextCentredTransparent(frame, 160, 68, "SERVICE ERROR", COLOR_RED, 2);
drawPet2001TextCentredTransparent(frame, 160, 98, wifiServiceError(), COLOR_YELLOW, 1);
}
} else if (current_page == SETTINGS_POWER_OFF) {
drawPet2001TextCentredTransparent(frame, 160, 45, "POWER OFF?", COLOR_YELLOW, 3);
drawPet2001TextCentredTransparent(frame, 160, 92, "A/K CONFIRM", COLOR_RED, 2);
drawPet2001TextCentredTransparent(frame, 160, 117, "BOOT CANCEL", COLOR_LIGHTGREY, 1);
} else if (current_page == SETTINGS_RESTART) {
drawPet2001TextCentredTransparent(frame, 160, 45, "RESTART ESP?", COLOR_YELLOW, 3);
drawPet2001TextCentredTransparent(frame, 160, 92, "A/K CONFIRM", COLOR_RED, 2);
}
drawPet2001TextCentredTransparent(frame, 160, 157, "BOOT BACK", COLOR_LIGHTGREY, 1);
}