Settings

Source: src/settings.c · 556 lines · 25 functions.

Screen captures

Views of this part running on the Mini Arcade.

Mini Arcade menu with Settings selected
The main-menu selection that opens Settings.

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)
  1. Exit this functionreturn 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)
  1. Exit this functionreturn 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:

  1. Executeram_log_record_count = ramMonitorRecordCount();
  2. Decisionram_log_record_count == 0U
    True
      1. Executeram_log_record_index = 0U;
      2. Executeram_log_record[0] = '\0';
      3. Exit this functionreturn;
    False

    Continue after this decision.

  3. Decisionnewest || ram_log_record_index >= ram_log_record_count
    True
    1. Executeram_log_record_index = ram_log_record_count - 1U;
    False

    Continue after this decision.

  4. Decision!ramMonitorReadRecord(ram_log_record_index, ram_log_record, sizeof(ram_log_record))
    True
    1. Executeram_log_record[0] = '\0';
    False

    Continue 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:

  1. 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:

  1. Exit this functionreturn 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:

  1. Executewifi_credentials = (wifi_credentials_record_t){1, {0}, {0}};
  2. Executestrncpy(wifi_credentials.ssid, ssid, sizeof(wifi_credentials.ssid) - 1U);
  3. Executestrncpy(wifi_credentials.password, password, sizeof(wifi_credentials.password) - 1U);
  4. Executewifi_credentials_available = true;
  5. Exit this functionreturn 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:

  1. Decisionwifi_credentials_available && wifiServiceConnectionState() != WIFI_CONNECTION_CONNECTED && wifiServiceConnectionState() != WIFI_CONNECTION_CONNECTING
    True
    1. Execute(void)wifiServiceConnect(wifi_credentials.ssid, wifi_credentials.password);
    False

    Continue 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:

  1. Executebool found = false;
  2. Executesettings_record_t loaded = {0};
  3. Decision!game_storage_read("arcade_settings", "display", &loaded, sizeof(loaded), &found)
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  4. Decisionfound && (loaded.version == 1 || loaded.version == 2) && valid_brightness(loaded.brightness)
    True
      1. Executesaved.brightness = loaded.brightness;
      2. Decisionloaded.version == 2 && valid_volume(loaded.volume)
        True
        1. Executesaved.volume = loaded.volume;
        False

        Continue after this decision.

    False

    Continue after this decision.

  5. ExecuteaudioSetMasterVolume(saved.volume);
  6. Executewifi_settings_record_t wifi_loaded = {0};
  7. Decision!game_storage_read("arcade_wifi", "settings", &wifi_loaded, sizeof(wifi_loaded), &found)
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  8. Decisionfound && wifi_loaded.version == 1 && wifi_loaded.enabled <= 1U
    True
    1. Executewifi_saved = wifi_loaded;
    False

    Continue after this decision.

  9. Executewifi_credentials_record_t credentials_loaded = {0};
  10. Decision!game_storage_read("arcade_wifi", "credentials", &credentials_loaded, sizeof(credentials_loaded), &found)
    True
    1. Exit this functionreturn false;
    False

    Continue after this decision.

  11. Decisionfound && credentials_loaded.version == 1
    True
      1. Executecredentials_loaded.ssid[WIFI_SCAN_SSID_CHARS] = '\0';
      2. Executecredentials_loaded.password[SCREEN_KEYBOARD_MAX_CHARS] = '\0';
      3. Decisioncredentials_loaded.ssid[0] != '\0'
        True
          1. Executewifi_credentials = credentials_loaded;
          2. Executewifi_credentials_available = true;
        False

        Continue after this decision.

    False

    Continue after this decision.

  12. Exit this functionreturn 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)
  1. Exit this functionreturn saved.brightness;
Read original function source
uint8_t settingsBrightness(void) { return saved.brightness; }
settingsVolume() · source line 125
uint8_t settingsVolume(void)
  1. Exit this functionreturn 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:

  1. Decision!valid_volume(volume) || volume == saved.volume
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  2. Executesaved.volume = volume;
  3. ExecuteaudioSetMasterVolume(saved.volume);
  4. Executesave_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)
  1. Exit this functionreturn 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:

  1. Decision!wifi_credentials_available
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  2. DecisiontimeSyncDue(now_ms)
    True
      1. Decision!wifiServicePrepare()
        True
        1. Exit this functionreturn;
        False

        Continue after this decision.

      2. Decision!wifiServiceRadioStarted()
        True
          1. Decision!wifiServiceStart()
            True
            1. Exit this functionreturn;
            False

            Continue after this decision.

          2. Executeclock_sync_temporary_wifi = !settingsWifiEnabled();
          3. Executeclock_sync_wifi_started = now_ms;
          4. Executereconnect_saved_wifi();
        False
        1. DecisionwifiServiceConnectionState() != WIFI_CONNECTION_CONNECTED && wifiServiceConnectionState() != WIFI_CONNECTION_CONNECTING
          True
          1. Executereconnect_saved_wifi();
          False

          Continue after this decision.

    False

    Continue after this decision.

  3. ExecutetimeSyncUpdate(wifiServiceConnectionState() == WIFI_CONNECTION_CONNECTED, now_ms);
  4. Decisionclock_sync_temporary_wifi && (timeSyncStatus() == TIME_SYNC_SYNCHRONIZED || timeSyncStatus() == TIME_SYNC_FAILED || now_ms - clock_sync_wifi_started >= CLOCK_SYNC_WIFI_TIMEOUT_MS)
    True
      1. Decisionnow_ms - clock_sync_wifi_started >= CLOCK_SYNC_WIFI_TIMEOUT_MS
        True
        1. ExecutetimeSyncAbandonAttempt();
        False

        Continue after this decision.

      2. Execute(void)wifiServiceStop();
      3. Executeclock_sync_temporary_wifi = false;
    False

    Continue 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:

  1. Executecurrent_page = page;
  2. Executeactive = true;
  3. Executerestart_requested = false;
  4. Executepower_off_requested = false;
  5. Executelast_direction = 0;
  6. Executeesp_info_page = 0U;
  7. Executeram_log_record_count = 0U;
  8. Executeram_log_record_index = 0U;
  9. Executeram_log_record[0] = '\0';
  10. Executewifi_selected_network = 0U;
  11. Executewifi_keyboard_active = false;
  12. Executewifi_keyboard_pressed = false;
  13. Decisioncurrent_page == SETTINGS_WIFI
    True
      1. Execute(void)wifiServicePrepare();
      2. DecisionsettingsWifiEnabled() && wifiServiceStart()
        True
        1. Executereconnect_saved_wifi();
        False

        Continue after this decision.

    False
    1. Decisioncurrent_page == SETTINGS_RAM_LOG
      True
        1. Executeload_ram_log_record(true);
      False

      Continue 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:

  1. Decisionwifi_keyboard_active
    True
    1. ExecutecancelScreenKeyboard();
    False

    Continue after this decision.

  2. Executewifi_keyboard_active = false;
  3. Executewifi_keyboard_pressed = false;
  4. Executeactive = false;
  5. Executelast_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)
  1. Exit this functionreturn restart_requested;
Read original function source
bool settingsRestartRequested(void) { return restart_requested; }
settingsPowerOffRequested() · source line 185
bool settingsPowerOffRequested(void)
  1. Exit this functionreturn power_off_requested;
Read original function source
bool settingsPowerOffRequested(void) { return power_off_requested; }
settingsCancelPowerOffRequest() · source line 186
void settingsCancelPowerOffRequest(void)
  1. Executepower_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:

  1. Decision!active || current_page == SETTINGS_POWER_OFF || current_page == SETTINGS_RESTART || current_page == SETTINGS_ESP_INFO
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  2. Decisioncurrent_page == SETTINGS_WIFI
    True
      1. Decisionwifi_keyboard_active
        True
          1. Executeconst KeyboardResult result = updateScreenKeyboard(x_offset, y_offset, joystick_valid, wifi_keyboard_pressed, now_ms);
          2. Executewifi_keyboard_pressed = false;
          3. Decisionresult == KEYBOARD_DONE
            True
              1. Executechar password[SCREEN_KEYBOARD_MAX_CHARS + 1U] = {0};
              2. Executeconst wifi_scan_entry_t *entries = wifiServiceScanEntries();
              3. Decisionwifi_selected_network < wifiServiceScanCount() && getScreenKeyboardText(password, sizeof(password)) && wifiServiceConnect(entries[wifi_selected_network].ssid, password)
                True
                  1. Execute(void)save_wifi_credentials(entries[wifi_selected_network].ssid, password);
                False

                Continue after this decision.

              4. Executememset(password, 0, sizeof(password));
              5. Executewifi_keyboard_active = false;
            False
            1. Decisionresult == KEYBOARD_CANCELLED
              True
                1. Executewifi_keyboard_active = false;
              False

              Continue after this decision.

          4. Exit this functionreturn;
        False

        Continue after this decision.

      2. ExecutewifiServiceUpdateScan();
      3. ExecutetimeSyncUpdate(wifiServiceConnectionState() == WIFI_CONNECTION_CONNECTED, now_ms);
      4. Executeconst size_t network_count = wifiServiceScanCount();
      5. Decisionwifi_selected_network >= network_count && network_count > 0U
        True
        1. Executewifi_selected_network = network_count - 1U;
        False

        Continue after this decision.

      6. Decisionnetwork_count > 0U && joystick_valid && now_ms - wifi_selection_changed_ms >= SETTING_REPEAT_MS
        True
          1. Decisiony_offset > 600 && wifi_selected_network > 0U
            True
              1. Execute--wifi_selected_network;
              2. Executewifi_selection_changed_ms = now_ms;
            False
            1. Decisiony_offset < -600 && wifi_selected_network + 1U < network_count
              True
                1. Execute++wifi_selected_network;
                2. Executewifi_selection_changed_ms = now_ms;
              False

              Continue after this decision.

        False

        Continue after this decision.

    False

    Continue after this decision.

  3. Executeconst int direction = (int)right - (int)left;
  4. Decision!direction
    True
      1. Executelast_direction = 0;
      2. Exit this functionreturn;
    False

    Continue after this decision.

  5. Decisiondirection == last_direction && now_ms - last_change_ms < SETTING_REPEAT_MS
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  6. Executelast_direction = direction;
  7. Executelast_change_ms = now_ms;
  8. Decisioncurrent_page == SETTINGS_WIFI
    True
      1. Executeconst bool enabled = direction > 0;
      2. Decisionenabled == settingsWifiEnabled()
        True
        1. Exit this functionreturn;
        False

        Continue after this decision.

      3. Executeconst bool radio_changed = enabled ? wifiServiceStart() : wifiServiceStop();
      4. Decision!radio_changed
        True
        1. Exit this functionreturn;
        False

        Continue after this decision.

      5. Decision!enabled
        True
        1. ExecutetimeSyncStop();
        False

        Continue after this decision.

      6. Executewifi_saved.enabled = enabled ? 1U : 0U;
      7. Execute(void)save_wifi_enabled();
      8. Decisionenabled
        True
        1. Executereconnect_saved_wifi();
        False

        Continue after this decision.

    False
    1. Decisioncurrent_page == SETTINGS_RAM_LOG
      True
        1. Executeload_ram_log_record(false);
        2. Decisiondirection < 0 && ram_log_record_index > 0U
          True
          1. Execute--ram_log_record_index;
          False
          1. Decisiondirection > 0 && ram_log_record_index + 1U < ram_log_record_count
            True
            1. Execute++ram_log_record_index;
            False

            Continue after this decision.

        3. Decisionram_log_record_count > 0U && !ramMonitorReadRecord(ram_log_record_index, ram_log_record, sizeof(ram_log_record))
          True
          1. Executeram_log_record[0] = '\0';
          False

          Continue after this decision.

      False
      1. Decisioncurrent_page == SETTINGS_BRIGHTNESS
        True
          1. Executeint next = saved.brightness + direction * BRIGHTNESS_STEP;
          2. Decisionnext < BRIGHTNESS_MIN
            True
            1. Executenext = BRIGHTNESS_MIN;
            False

            Continue after this decision.

          3. Decisionnext > BRIGHTNESS_MAX
            True
            1. Executenext = BRIGHTNESS_MAX;
            False

            Continue after this decision.

          4. Decisionnext != saved.brightness
            True
              1. Executesaved.brightness = (uint8_t)next;
              2. Executesave_brightness();
            False

            Continue after this decision.

        False
        1. Decisioncurrent_page == SETTINGS_VOLUME
          True
            1. Executeint next = saved.volume + direction * VOLUME_STEP;
            2. Decisionnext < 0
              True
              1. Executenext = 0;
              False

              Continue after this decision.

            3. Decisionnext > VOLUME_MAX
              True
              1. Executenext = VOLUME_MAX;
              False

              Continue after this decision.

            4. Decisionnext != saved.volume
              True
                1. ExecutesettingsSetVolume((uint8_t)next);
                2. ExecuteaudioPlay(AUDIO_SFX_MENU_MOVE);
              False

              Continue after this decision.

          False
          1. Decisioncurrent_page == SETTINGS_AI_MOVE
            True
              1. Executeint next = getAIBaseMoveDelay() + direction * AI_MOVE_DELAY_STEP_MS;
              2. Decisionnext < AI_MOVE_DELAY_MIN_MS
                True
                1. Executenext = AI_MOVE_DELAY_MIN_MS;
                False

                Continue after this decision.

              3. Decisionnext > AI_MOVE_DELAY_MAX_MS
                True
                1. Executenext = AI_MOVE_DELAY_MAX_MS;
                False

                Continue after this decision.

              4. Execute(void)saveAIBaseMoveDelay((uint16_t)next);
            False
              1. Executeint next = getAIRestartDelaySeconds() + direction;
              2. Decisionnext < AI_RESTART_DELAY_MIN_SECONDS
                True
                1. Executenext = AI_RESTART_DELAY_MIN_SECONDS;
                False

                Continue after this decision.

              3. Decisionnext > AI_RESTART_DELAY_MAX_SECONDS
                True
                1. Executenext = AI_RESTART_DELAY_MAX_SECONDS;
                False

                Continue after this decision.

              4. 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:

  1. Decision!active || !select
    True
    1. Exit this functionreturn;
    False

    Continue after this decision.

  2. Decisioncurrent_page == SETTINGS_POWER_OFF
    True
    1. Executepower_off_requested = true;
    False
    1. Decisioncurrent_page == SETTINGS_RESTART
      True
      1. Executerestart_requested = true;
      False
      1. Decisioncurrent_page == SETTINGS_WIFI
        True
          1. Decisionwifi_keyboard_active
            True
              1. Executewifi_keyboard_pressed = true;
            False
            1. DecisionwifiServiceScanCount() > 0U && !wifiServiceScanInProgress()
              True
                1. ExecutestartScreenKeyboard(NULL, SCREEN_KEYBOARD_MAX_CHARS, KEYBOARD_PASSWORD, "WIFI PASSWORD", now_ms);
                2. Executewifi_keyboard_active = true;
              False
              1. DecisionwifiServiceRadioStarted()
                True
                  1. Execute(void)wifiServiceStartScan();
                False

                Continue after this decision.

        False
        1. Decisioncurrent_page == SETTINGS_ESP_INFO
          True
          1. Executeesp_info_page = (esp_info_page + 1U) % ESP_INFO_PAGE_COUNT;
          False
          1. Decisioncurrent_page == SETTINGS_RAM_LOG
            True
            1. Executeload_ram_log_record(true);
            False

            Continue 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:

  1. Executeconst int label_width = (int)strlen(label) * UI_FONT_ADVANCE - 1;
  2. Executeint maximum_characters = (312 - (8 + label_width + 8)) / UI_FONT_ADVANCE;
  3. Decisionmaximum_characters > 31
    True
    1. Executemaximum_characters = 31;
    False

    Continue after this decision.

  4. Executechar clipped[32];
  5. Executesize_t length = strlen(value);
  6. Decision(int)length > maximum_characters
    True
    1. Executelength = (size_t)maximum_characters;
    False

    Continue after this decision.

  7. Executememcpy(clipped, value, length);
  8. Executeclipped[length] = '\0';
  9. Executeconst int value_width = (int)length * UI_FONT_ADVANCE - 1;
  10. Executedraw_text(frame, 8, y, label, 1, COLOR_LIGHTGREY);
  11. Executedraw_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)
  1. Select a casereason

    Enter matching case; follow source fall-through until a break, return or end.

      1. Case / labelcase ESP_RST_POWERON:
      2. Exit this functionreturn "POWER ON";
      3. Case / labelcase ESP_RST_EXT:
      4. Exit this functionreturn "EXTERNAL";
      5. Case / labelcase ESP_RST_SW:
      6. Exit this functionreturn "SOFTWARE";
      7. Case / labelcase ESP_RST_PANIC:
      8. Exit this functionreturn "PANIC";
      9. Case / labelcase ESP_RST_INT_WDT:
      10. Exit this functionreturn "INT WDT";
      11. Case / labelcase ESP_RST_TASK_WDT:
      12. Exit this functionreturn "TASK WDT";
      13. Case / labelcase ESP_RST_WDT:
      14. Exit this functionreturn "OTHER WDT";
      15. Case / labelcase ESP_RST_DEEPSLEEP:
      16. Exit this functionreturn "DEEP SLEEP";
      17. Case / labelcase ESP_RST_BROWNOUT:
      18. Exit this functionreturn "BROWNOUT";
      19. Case / labelcase ESP_RST_SDIO:
      20. Exit this functionreturn "SDIO";
      21. Case / labeldefault:
      22. Exit this functionreturn "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:

  1. Executechar text[48];
  2. Executeesp_chip_info_t chip = {0};
  3. Executeesp_chip_info(&chip);
  4. Executeconst uint32_t uptime_seconds = (uint32_t)(xTaskGetTickCount() * portTICK_PERIOD_MS) / 1000U;
  5. Executeconst uint32_t uptime_days = uptime_seconds / 86400U;
  6. Executeconst uint32_t uptime_hours = (uptime_seconds % 86400U) / 3600U;
  7. Executeconst uint32_t uptime_minutes = (uptime_seconds % 3600U) / 60U;
  8. Executeconst uint32_t uptime_remaining_seconds = uptime_seconds % 60U;
  9. Executeclear(frame, COLOR_BLACK);
  10. Executesnprintf(text, sizeof(text), "ESP INFO %u/%u", (unsigned)(esp_info_page + 1U), (unsigned)ESP_INFO_PAGE_COUNT);
  11. Executedraw_text_centred(frame, 160, 5, text, 2, COLOR_CYAN);
  12. Decisionesp_info_page == 0U
    True
      1. Executedraw_info_line(frame, 29, "CHIP", "ESP32-S3");
      2. Executesnprintf(text, sizeof(text), "%u CORES REV %u", (unsigned)chip.cores, (unsigned)chip.revision);
      3. Executedraw_info_line(frame, 43, "CPU", text);
      4. Executesnprintf(text, sizeof(text), "%u MHZ", (unsigned)CONFIG_ESP_DEFAULT_CPU_FREQ_MHZ);
      5. Executedraw_info_line(frame, 57, "FREQ", text);
      6. Executedraw_info_line(frame, 71, "RESET", reset_reason_text(esp_reset_reason()));
      7. Executesnprintf(text, sizeof(text), "%lud %02lu:%02lu:%02lu", (unsigned long)uptime_days, (unsigned long)uptime_hours, (unsigned long)uptime_minutes, (unsigned long)uptime_remaining_seconds);
      8. Executedraw_info_line(frame, 85, "UPTIME", text);
      9. Executedraw_info_line(frame, 99, "IDF", esp_get_idf_version());
      10. Executesnprintf(text, sizeof(text), "WIFI %s BLE %s", (chip.features & CHIP_FEATURE_WIFI_BGN) ? "YES" : "NO", (chip.features & CHIP_FEATURE_BLE) ? "YES" : "NO");
      11. Executedraw_info_line(frame, 113, "RADIO", text);
    False
    1. Decisionesp_info_page == 1U
      True
        1. Executeuint32_t flash_bytes = 0U;
        2. Executeconst esp_partition_t *running = esp_ota_get_running_partition();
        3. Executeconst esp_partition_t *next = esp_ota_get_next_update_partition(NULL);
        4. Executeesp_app_desc_t app = {0};
        5. Executeconst bool app_description_available = running != NULL && esp_ota_get_partition_description(running, &app) == ESP_OK;
        6. Decisionesp_flash_get_size(NULL, &flash_bytes) == ESP_OK
          True
          1. Executesnprintf(text, sizeof(text), "%u MB", (unsigned)(flash_bytes / (1024U * 1024U)));
          False
          1. Executesnprintf(text, sizeof(text), "UNAVAILABLE");
        7. Executedraw_info_line(frame, 29, "FLASH", text);
        8. Executedraw_info_line(frame, 43, "PROJECT", app_description_available ? app.project_name : "UNKNOWN");
        9. Executedraw_info_line(frame, 57, "VERSION", app_description_available ? app.version : "UNKNOWN");
        10. Decisionapp_description_available
          True
          1. Executesnprintf(text, sizeof(text), "%s %s", app.date, app.time);
          False
          1. Executesnprintf(text, sizeof(text), "UNKNOWN");
        11. Executedraw_info_line(frame, 71, "BUILD", text);
        12. Decisionrunning != NULL
          True
            1. Executesnprintf(text, sizeof(text), "%s %uK", running->label, (unsigned)(running->size / 1024U));
            2. Executedraw_info_line(frame, 85, "RUN APP", text);
            3. Executesnprintf(text, sizeof(text), "0X%lX", (unsigned long)running->address);
            4. Executedraw_info_line(frame, 99, "OFFSET", text);
          False

          Continue after this decision.

        13. Decisionnext != NULL
          True
            1. Executesnprintf(text, sizeof(text), "%s %uK", next->label, (unsigned)(next->size / 1024U));
            2. Executedraw_info_line(frame, 113, "NEXT OTA", text);
          False
          1. Executedraw_info_line(frame, 113, "NEXT OTA", "NONE");
      False
        1. Executeconst uint32_t internal_caps = MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT;
        2. Executeconst size_t psram_total = heap_caps_get_total_size(MALLOC_CAP_SPIRAM);
        3. Executeconst size_t psram_free = heap_caps_get_free_size(MALLOC_CAP_SPIRAM);
        4. Executeconst size_t psram_min = heap_caps_get_minimum_free_size(MALLOC_CAP_SPIRAM);
        5. Executeconst size_t psram_largest = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM);
        6. Executeconst size_t internal_total = heap_caps_get_total_size(internal_caps);
        7. Executeconst size_t internal_free = heap_caps_get_free_size(internal_caps);
        8. Executeconst size_t internal_min = heap_caps_get_minimum_free_size(internal_caps);
        9. Executeconst size_t internal_largest = heap_caps_get_largest_free_block(internal_caps);
        10. Executeconst size_t app_stack_min = (size_t)uxTaskGetStackHighWaterMark(NULL) * sizeof(StackType_t);
        11. Executesnprintf(text, sizeof(text), "%u/%uK", (unsigned)(psram_free / 1024U), (unsigned)(psram_total / 1024U));
        12. Executedraw_info_line(frame, 29, "PSRAM F/T", text);
        13. Executesnprintf(text, sizeof(text), "%uK", (unsigned)(psram_min / 1024U));
        14. Executedraw_info_line(frame, 43, "PSRAM MIN", text);
        15. Executesnprintf(text, sizeof(text), "%uK", (unsigned)(psram_largest / 1024U));
        16. Executedraw_info_line(frame, 57, "PSRAM MAX", text);
        17. Executesnprintf(text, sizeof(text), "%u/%uK", (unsigned)(internal_free / 1024U), (unsigned)(internal_total / 1024U));
        18. Executedraw_info_line(frame, 71, "HEAP F/T", text);
        19. Executesnprintf(text, sizeof(text), "%uK", (unsigned)(internal_min / 1024U));
        20. Executedraw_info_line(frame, 85, "HEAP MIN", text);
        21. Executesnprintf(text, sizeof(text), "%uK", (unsigned)(internal_largest / 1024U));
        22. Executedraw_info_line(frame, 99, "HEAP MAX", text);
        23. Executesnprintf(text, sizeof(text), "%uK", (unsigned)(esp_get_free_heap_size() / 1024U));
        24. Executedraw_info_line(frame, 113, "DEFAULT", text);
        25. Executesnprintf(text, sizeof(text), "%uK", (unsigned)(esp_get_minimum_free_heap_size() / 1024U));
        26. Executedraw_info_line(frame, 127, "DEFAULT MIN", text);
        27. Executesnprintf(text, sizeof(text), "%uK", (unsigned)(app_stack_min / 1024U));
        28. Executedraw_info_line(frame, 141, "STACK MIN", text);
  13. Executedraw_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:

  1. Executeclear(frame, COLOR_BLACK);
  2. Executedraw_text_centred(frame, 160, 5, "RAM LOG", 2, COLOR_CYAN);
  3. Decisionram_log_record_count == 0U
    True
      1. Executedraw_text_centred(frame, 160, 65, "NO SAVED RECORDS", 2, COLOR_YELLOW);
      2. Executedraw_text_centred(frame, 160, 94, "WAIT ONE MINUTE", 1, COLOR_LIGHTGREY);
    False
    1. Decisionram_log_record[0] == '\0'
      True
        1. Executedraw_text_centred(frame, 160, 65, "LOG READ ERROR", 2, COLOR_RED);
      False
        1. Executeunsigned long uptime_seconds;
        2. Executeunsigned psram_free, psram_min, psram_largest;
        3. Executeunsigned heap_free, heap_min, heap_largest, stack_min;
        4. Executeconst 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);
        5. Decisionfields != 8
          True
            1. Executedraw_text_centred(frame, 160, 65, "BAD LOG RECORD", 2, COLOR_RED);
          False
            1. Executechar text[32];
            2. Executesnprintf(text, sizeof(text), "%luM %02luS", uptime_seconds / 60U, uptime_seconds % 60U);
            3. Executedraw_info_line(frame, 29, "UPTIME", text);
            4. Executesnprintf(text, sizeof(text), "F %uK M %uK", psram_free, psram_min);
            5. Executedraw_info_line(frame, 43, "PSRAM", text);
            6. Executesnprintf(text, sizeof(text), "%uK", psram_largest);
            7. Executedraw_info_line(frame, 57, "PSRAM MAX", text);
            8. Executesnprintf(text, sizeof(text), "F %uK M %uK", heap_free, heap_min);
            9. Executedraw_info_line(frame, 71, "HEAP", text);
            10. Executesnprintf(text, sizeof(text), "%uK", heap_largest);
            11. Executedraw_info_line(frame, 85, "HEAP MAX", text);
            12. Executesnprintf(text, sizeof(text), "%uK", stack_min);
            13. Executedraw_info_line(frame, 99, "STACK MIN", text);
            14. Executedraw_info_line(frame, 113, "STATUS", strstr(ram_log_record, "WARN") != NULL ? "WARNING" : "OK");
  4. Executechar footer[40];
  5. Decisionram_log_record_count > 0U
    True
    1. Executesnprintf(footer, sizeof(footer), "%u/%u D/B RECORD A/K LATEST", ram_log_record_index + 1U, ram_log_record_count);
    False
    1. Executesnprintf(footer, sizeof(footer), "A/K REFRESH");
  6. Executedraw_text_centred(frame, 160, 141, footer, 1, COLOR_CYAN);
  7. Executedraw_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:

  1. Decisioncurrent_page == SETTINGS_WIFI && wifi_keyboard_active
    True
      1. ExecutedrawScreenKeyboard(frame);
      2. Exit this functionreturn;
    False

    Continue after this decision.

  2. Decisioncurrent_page == SETTINGS_ESP_INFO
    True
      1. Executedraw_esp_info(frame);
      2. Exit this functionreturn;
    False

    Continue after this decision.

  3. Decisioncurrent_page == SETTINGS_RAM_LOG
    True
      1. Executedraw_ram_log(frame);
      2. Exit this functionreturn;
    False

    Continue after this decision.

  4. Executechar text[48];
  5. Executeclear(frame, COLOR_BLACK);
  6. Decisioncurrent_page == SETTINGS_BRIGHTNESS
    True
      1. Executedraw_text_centred(frame, 160, 24, "BRIGHTNESS", 2, COLOR_CYAN);
      2. Executesnprintf(text, sizeof(text), "%u", saved.brightness);
      3. Executedraw_text_centred(frame, 160, 70, text, 5, COLOR_WHITE);
      4. Executedraw_text_centred(frame, 160, 123, "D/B ADJUST", 2, COLOR_GREEN);
    False
    1. Decisioncurrent_page == SETTINGS_VOLUME
      True
        1. Executedraw_text_centred(frame, 160, 24, "VOLUME", 2, COLOR_CYAN);
        2. Executesnprintf(text, sizeof(text), "%u%%", saved.volume);
        3. Executedraw_text_centred(frame, 160, 70, text, 5, COLOR_WHITE);
        4. Executedraw_text_centred(frame, 160, 123, "D/B ADJUST", 2, COLOR_GREEN);
      False
      1. Decisioncurrent_page == SETTINGS_AI_MOVE
        True
          1. Executedraw_text_centred(frame, 160, 24, "AI MOVE", 2, COLOR_CYAN);
          2. Executesnprintf(text, sizeof(text), "%u MS", getAIBaseMoveDelay());
          3. Executedraw_text_centred(frame, 160, 75, text, 4, COLOR_WHITE);
          4. Executedraw_text_centred(frame, 160, 123, "D/B ADJUST", 2, COLOR_GREEN);
        False
        1. Decisioncurrent_page == SETTINGS_AI_RESTART
          True
            1. Executedraw_text_centred(frame, 160, 24, "AI RESTART", 2, COLOR_CYAN);
            2. Executesnprintf(text, sizeof(text), "%u SEC", getAIRestartDelaySeconds());
            3. Executedraw_text_centred(frame, 160, 75, text, 4, COLOR_WHITE);
            4. Executedraw_text_centred(frame, 160, 123, "D/B ADJUST", 2, COLOR_GREEN);
          False
          1. Decisioncurrent_page == SETTINGS_WIFI
            True
              1. Executedraw_text_centred(frame, 160, 24, "WIFI", 2, COLOR_CYAN);
              2. DecisionwifiServiceState() == WIFI_SERVICE_READY
                True
                  1. DecisionwifiServiceError()[0] != '\0'
                    True
                      1. Executedraw_text_centred(frame, 160, 68, "WIFI ERROR", 2, COLOR_RED);
                      2. Executedraw_text_centred(frame, 160, 98, wifiServiceError(), 1, COLOR_YELLOW);
                    False
                    1. DecisionwifiServiceConnectionState() == WIFI_CONNECTION_CONNECTING
                      True
                        1. Executedraw_text_centred(frame, 160, 68, "CONNECTING...", 2, COLOR_YELLOW);
                        2. Executedraw_text_centred(frame, 160, 98, "WAITING FOR IP", 1, COLOR_LIGHTGREY);
                      False
                      1. DecisionwifiServiceConnectionState() == WIFI_CONNECTION_CONNECTED
                        True
                          1. Executedraw_text_centred(frame, 160, 68, "CONNECTED", 2, COLOR_GREEN);
                          2. Executeconst time_sync_status_t time_status = timeSyncStatus();
                          3. Executeconst uint16_t time_colour = time_status == TIME_SYNC_SYNCHRONIZED ? COLOR_GREEN : time_status == TIME_SYNC_FAILED ? COLOR_RED : COLOR_YELLOW;
                          4. Executedraw_text_centred(frame, 160, 98, timeSyncStatusText(), 1, time_colour);
                        False
                          1. Decision!wifiServiceRadioStarted()
                            True
                              1. Executedraw_text_centred(frame, 160, 60, "WIFI OFF", 3, COLOR_LIGHTGREY);
                              2. Executedraw_text_centred(frame, 160, 99, "B ON", 2, COLOR_CYAN);
                            False
                            1. DecisionwifiServiceScanInProgress()
                              True
                                1. Executedraw_text_centred(frame, 160, 68, "SCANNING...", 2, COLOR_YELLOW);
                              False
                              1. DecisionwifiServiceScanCount() == 0U
                                True
                                  1. Executedraw_text_centred(frame, 160, 60, "WIFI ON", 2, COLOR_GREEN);
                                  2. Executedraw_text_centred(frame, 160, 90, "A/K SCAN", 2, COLOR_CYAN);
                                False
                                  1. Executeconst wifi_scan_entry_t *entries = wifiServiceScanEntries();
                                  2. FOR loopsize_t index = 0; index < wifiServiceScanCount(); ++index

                                    Repeat body while the loop condition permits; then continue below.

                                      1. Executechar line[48];
                                      2. Executesnprintf(line, sizeof(line), "%u. %.25s %d", (unsigned)(index + 1U), entries[index].ssid[0] == '\0' ? "<HIDDEN>" : entries[index].ssid, (int)entries[index].rssi);
                                      3. Executedraw_text(frame, 8, 40 + (int)index * 14, line, 1, index == wifi_selected_network ? COLOR_CYAN : COLOR_WHITE);
                                  3. Executedraw_text_centred(frame, 160, 150, "UP/DOWN SELECT A/K JOIN", 1, COLOR_CYAN);
                False
                  1. Executedraw_text_centred(frame, 160, 68, "SERVICE ERROR", 2, COLOR_RED);
                  2. Executedraw_text_centred(frame, 160, 98, wifiServiceError(), 1, COLOR_YELLOW);
            False
            1. Decisioncurrent_page == SETTINGS_POWER_OFF
              True
                1. Executedraw_text_centred(frame, 160, 45, "POWER OFF?", 3, COLOR_YELLOW);
                2. Executedraw_text_centred(frame, 160, 92, "A/K CONFIRM", 2, COLOR_RED);
                3. Executedraw_text_centred(frame, 160, 117, "BOOT CANCEL", 1, COLOR_LIGHTGREY);
              False
              1. Decisioncurrent_page == SETTINGS_RESTART
                True
                  1. Executedraw_text_centred(frame, 160, 45, "RESTART ESP?", 3, COLOR_YELLOW);
                  2. Executedraw_text_centred(frame, 160, 92, "A/K CONFIRM", 2, COLOR_RED);
                False

                Continue after this decision.

  7. Executedraw_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);
}