Skip to content

settings_settings_screen

settings_settings_screen is the top-level pendant configuration screen. It presents a circular (radial) menu where each pie-slice section maps to one configurable aspect of the pendant — from cosmetic choices (theme, language, backlight) to transport wiring (serial baud rate, Bluetooth device scan, WiFi server scan) and destructive actions (factory reset, factory reboot).

Source file: main/display/cnc/screens/settings_screen.cpp

The screen is built on CircularMenuScreen and therefore inherits the standard radial navigation, two-phase tear-down, and encoder/touch-drag infrastructure. It also hosts two overlay components — ConnectionStatusComponent and FirmwareStatusComponent — consistent with other CNC working screens.


Persistent Storage

Parent Screen

Child Screens

cnc_shared (UI_Framework_&_Screens)

navigates to

overlays

overlays

Language section

Settings Editor section

Output Config serial/uart

Output Config wifi/ws

Output Config BT

Server Scan section

Output Selection section

confirmation

read/write

settingsScreen::create()

main/display/cnc/screens/settings_screen.cpp

CircularMenuScreen

(base class)

GenericScreen

(base class)

ConnectionStatusComponent

FirmwareStatusComponent

languages_screen

settings_list_screen

baudrate_screen

wifi_scan_screen

scan_bt_screen

server_scan_screen

output_selection_screen

message_box_screen

main_screen

ESP3DSettings (NVS)

esp3d_output_client

esp3d_brightness_level

esp3d_hide_lock

esp3d_orientation

esp3d_theme


The circular menu is composed of sections that are included or excluded at compile time via preprocessor flags. Sections are assigned sequential IDs (the SettingsSections enum) starting from 0; the total count is ESP3D_TOTAL_SECTIONS.

Section enumDefault IDCompile guardLabel keyBehavior
ESP3D_SECTION_LOCK_UI0alwaysshow_lock_ui / hide_lock_uiToggle UI lock visibility (persisted to NVS, icon updated in-place)
ESP3D_SECTION_SOUND1ESP3D_BUZZER_FEATUREenable_sound / disable_soundToggle sound on/off (persisted, esp3d_sound.enable(), see buzzer.md)
ESP3D_SECTION_ROTATION2ESP3D_DYNAMIC_ROTATION_FEATUREorientationEnter rotation edit mode; encoder cycles 0→90→180→270 degrees
ESP3D_SECTION_BACKLIGHT3ESP3D_BRIGHTNESS_CONTROL_FEATUREraw "N%"Enter backlight edit mode; encoder steps ±5% (0–100%)
ESP3D_SECTION_LANGUAGE+0alwaysui_languageNavigate to languages_screen
ESP3D_SECTION_THEME+1alwaystheme + theme nameEnter theme-selection edit mode; live preview on encoder step
ESP3D_SECTION_RESET_SETTINGS+2alwaysreset_settingsConfirmation dialog then esp3dXsettings.reset() + esp_restart()
ESP3D_SECTION_FACTORY_REBOOT+3ESP3D_FACTORY_FEATURE && ESP3D_SHOW_MENU_FACTORYfactory_rebootConfirmation dialog then esp3dCommands.rebootToFactory() + esp_restart()
ESP3D_SECTION_SETTINGS_EDITOR+nalwayssettings_editorNavigate to settings_list_screen
ESP3D_SECTION_OUTPUT_SELECTION+n+1ESP3D_BT_SERIAL_FEATURE or ESP3D_BT_BLE_FEATURE or ESP3D_WIFI_FEATURE or ESP3D_UART_EXT_FEATUREdynamicNavigate to output_selection_screen
ESP3D_SECTION_OUTPUT_CONFIGURATION+n+2alwaysdynamic per client typeNavigate to baudrate_screen, wifi_scan_screen, or scan_bt_screen depending on the running output client
ESP3D_SECTION_SERVER_SCAN+n+3ESP3D_IP_CNC_CLIENT_FEATUREserver_scanNavigate to server_scan_screen; hidden unless socket_client or websocket_client is active output

The ESP3D_SECTION_OUTPUT_CONFIGURATION section navigation target is resolved at runtime from the running output client (esp3dCommands.getOutputClient()), and so is the section shown in create() (a14). The NVS value (esp3d_output_client) is the mode for the next boot: after “select WiFi + refuse reboot” the device is still on serial, and showing WiFi (scan buttons…) offered actions that could only fail with “invalid mode” until the reboot happened. See settings_selection_screens.md for the output selection screen.

ESP3DClientTypeTarget screen
serialbaudrate_screen
uart_ext (if ESP3D_UART_EXT_FEATURE)baudrate_screen
socket_client (if ESP3D_IP_CNC_CLIENT_FEATURE)wifi_scan_screen
websocket_client (if ESP3D_WS_CLIENT_SERVICE_FEATURE)wifi_scan_screen
bt_serial / bt_ble (if ESP3D_BT_*_FEATURE)scan_bt_screen

The encoder can be repurposed from menu navigation to value-editing for three sections. The active mode is tracked inside CircularMenuComponent::encoderEnabledFor() and queried by onEncoderEvent.

Mode constantValueActive whenEncoder direction effect
ESP3D_MENU_SELECTIONbasedefaultNavigate to adjacent section
ESP3D_ENCODER_ROTATIONESP3D_MENU_SELECTION + 1Rotation section toggled onCycle orientation 0/900/1800/2700 (tenths-of-degrees), wrapping
ESP3D_ENCODER_BACKLIGHTESP3D_MENU_SELECTION + 2Backlight section toggled onAdjust brightness ±5% (clamped 0–100%)
ESP3D_ENCODER_THEMEESP3D_MENU_SELECTION + 3Theme section toggled onCycle to next/previous theme with live preview

The section enters edit mode on first tap and exits/saves on second tap; the Back button cancels and restores the prior cached value. Section arc and icon colors change to ESP3D_ACCENT_ACTIVE_COLOR while active to provide visual feedback.

create()

tap Rotation section (1st)

tap Backlight section (1st)

tap Theme section (1st)

tap Rotation section (2nd) - apply

Back press - cancel and restore cached_orientation_

tap Backlight section (2nd) - save to NVS

Back press - cancel and restore cached_backlight_level_

tap Theme section (2nd) - save to NVS

Back press - cancel and restore cached_theme_

SelectionMode

RotationEdit

BacklightEdit

ThemeEdit


Three virtual buttons are defined at the bottom of the screen:

Button slotIcon (selection mode)Icon (edit mode, touch-only)Press behaviorRelease behavior
Button 0 (OK)confirm iconconfirm iconsimulate_click(current_section) on the circular menureleaseSection(current_section)
Button 1 (rotate CW)hiddenarrow_up_bEmits +1 encoder step via emitEncoderStep(1)No action
Button 2 (Back / rotate CCW)back_barrow_down_bSelection mode: beep. HW encoder edit: no-op. Touch-only edit: emits -1 encoder stepSelection mode: navigate to main_screen. HW encoder edit: cancel and restore cached value

ShowEncoderControls(bool state) is called after every section press/release to show/hide button 1 and swap button 2’s icon between back_b and arrow_down_b, keeping the button strip consistent with the current encoder mode. This function is a no-op on hardware-encoder builds (ESP3D_HARDWARE_ENCODER_FEATURE).


Entry point called by the screen router when ESP3DScreenType::settings is requested.

  1. Resets all static transition-guard flags via ESP3D_TRANSITION_RESET.
  2. Throws std::runtime_error if an instance already exists (indicates a screen-management bug).
  3. Builds the static settings_menu_sections[] array (compile-time filtered by feature flags).
  4. Creates a CircularMenuScreen with std::nothrow; validates with isValid().
  5. Pre-initializes the theme section text before showMenu() renders it (avoids a plain “Theme” label flash on first boot while the static sections array is still Translatable).
  6. Calls showMenu() to render the radial menu.
  7. Registers the screen with UIManager and attaches the LV_EVENT_DELETE handler (onScreenDestroy) to the root LVGL object for safe auto-cleanup.
  8. Calls onScreenCreated() to apply runtime-known initial state to each section.
  9. On touch-only builds, hides button 1 initially (only shown during value editing).
  10. Creates ConnectionStatusComponent and FirmwareStatusComponent overlays with std::nothrow; logs and skips gracefully if allocation fails.

LVGL LV_EVENT_DELETE handler registered on the root screen object.

  • If prepareForDestruction() was not already called (emergency path), calls it now.
  • Deletes firmware_status_component and connection_status_component.
  • Calls ui_manager.unregisterScreen(ESP3DScreenType::settings).
  • Deletes settings_screen_obj_instance and nulls all static pointers.

Called by CircularMenuComponent on any section press event.

  • Guards against invalid screen state and transition locks (isActionAllowed).
  • Plays ESP3D_SELECTION_BEEP.

onMenuSectionRelease(int32_t section_id, uint32_t duration_ms)

Section titled “onMenuSectionRelease(int32_t section_id, uint32_t duration_ms)”

The primary action dispatcher. Handles all section-specific logic on release.

SectionAction taken
LOCK_UIToggles ui_manager.getHideLock() and updates section icon + label in-place
SOUNDToggles esp3d_sound.enable(!esp3d_sound.enabled(), true) (saved to settings) and updates section icon + label in-place
ROTATIONToggles ESP3D_ENCODER_ROTATION mode; on exit applies pending orientation via ui_manager.setOrientationFromAngle()
BACKLIGHTToggles ESP3D_ENCODER_BACKLIGHT mode; on exit writes esp3d_brightness_level to NVS if changed
LANGUAGEnavigateTo(ESP3DScreenType::languages)
THEMEToggles ESP3D_ENCODER_THEME mode; on exit calls ui_manager.setTheme() if changed. On a live preview and when a theme is applied, the connection status and firmware status components get updateThemeColor() (a14), as the virtual buttons and circular menu do
RESET_SETTINGSOpens confirmation message_box_screen; on confirm: esp3dXsettings.reset() then information box then 2 s timer then esp_restart()
FACTORY_REBOOTOpens confirmation message_box_screen; on confirm: esp3dCommands.rebootToFactory() then information box then 2 s timer then esp_restart()
SETTINGS_EDITORnavigateTo(ESP3DScreenType::settings_list)
OUTPUT_SELECTIONnavigateTo(ESP3DScreenType::output_selection)
OUTPUT_CONFIGURATIONUses the running output client (getOutputClient()); routes to baudrate, wifi_scan, or scan_bt
SERVER_SCANnavigateTo(ESP3DScreenType::server_scan)

After every release, ShowEncoderControls is called to sync button visibility with the new encoder mode.

onEncoderEvent(int32_t direction, int32_t step, int32_t total_steps)

Section titled “onEncoderEvent(int32_t direction, int32_t step, int32_t total_steps)”

Dispatches encoder steps based on the current encoder mode:

  • ESP3D_ENCODER_ROTATION: Increments or decrements pending_orientation_ by 900 (tenths-of-degrees), wrapping at the [0, 2700] boundary. Calls setCenterTextRotation for live preview.
  • ESP3D_ENCODER_BACKLIGHT: Adjusts pending_backlight_level_ by direction * 5, clamped to [0, 100]. Calls backlight_set() and updates the section raw text label.
  • ESP3D_ENCODER_THEME: Calls ui_manager.nextTheme(false) or previousTheme(false), updates the theme section text, and forces a redraw of virtual buttons and circular menu for a complete live preview via lv_obj_invalidate.
  • ESP3D_MENU_SELECTION (default): No action here; the base class handles section navigation automatically.

onBackButtonPress(int32_t button_idx, void *user_data)

Section titled “onBackButtonPress(int32_t button_idx, void *user_data)”
  • Selection mode: plays ESP3D_BACK_BEEP.
  • Edit mode, HW encoder: plays no beep; the cancel action fires on release.
  • Edit mode, touch-only: emits -1 encoder step via VirtualButtonsComponent::emitEncoderStep(-1) (button 2 doubles as CCW rotate).

onBackButtonRelease(int32_t button_idx, uint32_t duration_ms, void *user_data)

Section titled “onBackButtonRelease(int32_t button_idx, uint32_t duration_ms, void *user_data)”
  • Touch-only, edit mode: no-op on release (action already taken on press).
  • HW encoder, edit mode: resets encoder to ESP3D_MENU_SELECTION and restores cached values:
    • Rotation: restores cached_orientation_ via updateOrientation + setCenterTextRotation.
    • Backlight: restores cached_backlight_level_ via backlight_set() and updates label.
    • Theme: restores cached_theme_ via ui_manager.setTheme(cached_theme_, false) and forces theme-color refresh on virtual buttons and circular menu.
  • Selection mode: calls navigateTo(RETURN_SCREEN) (ESP3DScreenType::main).

onOKButtonPress(int32_t button_idx, void *user_data)

Section titled “onOKButtonPress(int32_t button_idx, void *user_data)”

Simulates a section press on get_current_section() via CircularMenuComponent::simulate_click.

onOKButtonRelease(int32_t button_idx, uint32_t duration_ms, void *user_data)

Section titled “onOKButtonRelease(int32_t button_idx, uint32_t duration_ms, void *user_data)”

Releases the simulated section press via CircularMenuComponent::releaseSection.

onRotatePress(int32_t button_idx, void *user_data)

Section titled “onRotatePress(int32_t button_idx, void *user_data)”

Touch-only builds only (!ESP3D_HARDWARE_ENCODER_FEATURE).

Emits +1 encoder step via VirtualButtonsComponent::emitEncoderStep(1). Button 1 acts as the clockwise encoder substitute when in value-edit mode.

First stage of the two-phase teardown. Calls prepareForDestruction() on the screen and all overlay components, starts the LVGL load animation toward the target screen, then arms transition_timer_cb with ESP3D_TRANSITION_SCREEN_DELAY_MS.

Second stage of teardown. Fires after the LVGL animation completes. Calls createScreen(next_screen_target) to instantiate the destination screen and clears transition_in_progress_.


LVGLUIManagersettingsScreenUserLVGLUIManagersettingsScreenUsertap Back button (selection mode)navigateTo(main)prepareForDestruction() - guard set on screen and overlayslv_timer_create(cleanup_timer_cb, 1 tick)cleanup_timer_cb fireslv_screen_load_anim(main_screen, ...)lv_timer_create(transition_timer_cb, delay)transition_timer_cb firescreateScreen(main)LV_EVENT_DELETE on old screen objectonScreenDestroy() - delete overlays, unregister, delete instance

The is_prepared_for_destruction_ flag is a one-way latch. Once set, all section press, encoder, and button callbacks return early without touching LVGL objects. The cleanup_executed_ flag prevents prepareForDestruction() from running twice when both an explicit navigation and the LVGL delete event arrive.


Data Flow: Value-Edit Mode (Backlight Example)

Section titled “Data Flow: Value-Edit Mode (Backlight Example)”
esp3dXsettingsbacklight_set()CircularMenuComponentsettingsScreenUseresp3dXsettingsbacklight_set()CircularMenuComponentsettingsScreenUseralt[User presses Back instead of confirming]tap Backlight section (1st tap, enter edit mode)enableEncoderFor(ESP3D_ENCODER_BACKLIGHT)update_section_color(ESP3D_ACCENT_ACTIVE_COLOR)cache and pending set to current levelrotate encoder CWpending_backlight_level_ += 5backlight_set(pending_)updateSectionRawText("75%")tap Backlight section (2nd tap - confirm)enableEncoderFor(ESP3D_MENU_SELECTION)update_section_color(ESP3D_MENU_ICON_COLOR)writeByte(esp3d_brightness_level, pending_)Back button release (HW encoder)backlight_set(cached_backlight_level_)updateSectionRawText("50%")enableEncoderFor(ESP3D_MENU_SELECTION)

Both destructive actions use a two-step message-box pattern to prevent accidental execution:

Cancel

Confirm

User taps Reset Settings

or Factory Reboot

messageBoxScreen::show_confirmation()

User choice

Return to settings_screen

no state change

Execute action

reset() or rebootToFactory()

200 ms timer

let confirmation box close

messageBoxScreen::show_information()

Board will restart...

2000 ms timer

user reads the message

esp_restart()

prepareForDestruction is passed to show_confirmation so the settings screen is safely torn down before the message box renders over it.


After the radial menu is rendered, onScreenCreated() applies runtime-known overrides that cannot be encoded in the static settings_menu_sections[] array:

SectionInitialization
LOCK_UIIcon and label set to match ui_manager.getHideLock()
SOUNDIcon and label set to match esp3d_sound.enabled()
BACKLIGHTRaw text label set to "N%" from backlight_get_current()
THEMEArc color set to ESP3D_ACCENT_SELECT_COLOR; label updated to current theme name via getCurrentThemeName()
OUTPUT_SELECTIONIcon and label set to match current esp3d_output_client
OUTPUT_CONFIGURATIONIcon and label set to match current esp3d_output_client
SERVER_SCANHidden (UIItemType::Hidden) for all clients except socket_client / websocket_client

Both overlay components are created with std::nothrow. If allocation fails, the screen remains fully functional but without that overlay indicator.

Displays the live connection state (transport + server) as a small icon in the top corner. Tapping it navigates to connection_status_screen with return_screen = ESP3DScreenType::settings. See grbl_module_connection_status.md for the detailed interface reference; the settings screen uses the same component as other CNC screens.

Displays the live firmware state icon. Tapping it navigates to firmware_status_screen and returns to settings_screen on Back.

Both overlays pass prepareForDestruction as their prepare_parent_screen_destruction hook so that navigating away from the settings screen via an overlay tap correctly tears down the settings screen before the overlay’s target screen opens.


VariableTypePurpose
settings_screen_obj_instanceCircularMenuScreen *Sole instance pointer; null between navigations
connection_status_componentConnectionStatusComponent *Connection overlay instance
firmware_status_componentFirmwareStatusComponent *Firmware overlay instance
transition_timerlv_timer_t *Second-phase teardown timer handle
cleanup_timerlv_timer_t *First-phase teardown timer handle
is_prepared_for_destruction_boolOne-way latch set by prepareForDestruction()
cleanup_executed_boolPrevents double execution of cleanup
next_screen_targetESP3DScreenTypeDestination screen stored before transition starts
cached_orientation_int32_tOrientation at the moment rotation edit mode was entered
pending_orientation_int32_tWorking orientation value during rotation edit
cached_backlight_level_intBrightness at the moment backlight edit mode was entered
pending_backlight_level_intWorking brightness value during backlight edit
cached_theme_ThemeTheme at the moment theme edit mode was entered

lv_screen root

registered with UIManager

LV_EVENT_DELETE triggers onScreenDestroy

container_

rotatable full-screen child

CircularMenuComponent

radial sections and center disk

VirtualButtonsComponent

3-button strip at bottom

ConnectionStatusComponent

top-right overlay

FirmwareStatusComponent

top-left overlay


settings section

Language section

Settings Editor section

Output Config serial or uart_ext

Output Config socket or websocket

Output Config bt_serial or bt_ble

Server Scan section

Output Selection section

Reset or Factory confirm

Back button

ConnectionStatus tap

FirmwareStatus tap

main_screen

settings_screen

languages_screen

settings_list_screen

baudrate_screen

wifi_scan_screen

scan_bt_screen

server_scan_screen

output_selection_screen

message_box_screen

connection_status_screen

firmware_status_screen


DependencyRole
CircularMenuScreenBase class — radial menu, orientation, virtual buttons, two-phase teardown
ConnectionStatusComponentLive connection-state overlay; same component as jog/status/probe screens
FirmwareStatusComponent (cnc/components/firmware_status.h)Live firmware-state overlay
messageBoxScreenConfirmation and information dialogs for Reset Settings and Factory Reboot
UIManager (esp3d_ui.h)Theme management (getTheme, setTheme, nextTheme, previousTheme, getCurrentThemeName), orientation (setOrientationFromAngle, getOrientationAngle), hide-lock state
ESP3DSettings / esp3dXsettingsNVS read/write for esp3d_output_client, esp3d_brightness_level
esp3d_soundSound on/off toggle and enabled-state query
backlight_set / backlight_get_currentHardware backlight control (requires ESP3D_BRIGHTNESS_CONTROL_FEATURE)
esp3dCommands.rebootToFactory()OTA partition switch for factory reboot (requires ESP3D_FACTORY_FEATURE)
ESP3DTranslationServiceRun-time label translation for confirmation and information message text
LVGLAll rendering, timers, and event delivery