Network Scan Screens
Section titled “Network Scan Screens”Introduction
Section titled “Introduction”The network scan screens module provides three asynchronous scan-and-select screens that let the user discover and configure network endpoints directly from the pendant’s touchscreen. Each screen scans for a different type of network target, presents results in a scrollable list, and persists the user’s choice to NVS before initiating a connection.
| Screen | File | Build guard | Target |
|---|---|---|---|
wifiScanScreen | wifi_scan_screen.cpp | ESP3D_WIFI_FEATURE | WiFi access points (802.11) |
scanBTScreen | scan_bt_screen.cpp | ESP3D_BT_SERIAL_FEATURE || ESP3D_BT_BLE_FEATURE | Bluetooth Classic / BLE devices |
serverScanScreen | server_scan_screen.cpp | ESP3D_IP_CNC_CLIENT_FEATURE | CNC servers discovered via mDNS |
All three screens share an identical structural pattern: a ListMenuScreen base, a FreeRTOS scan task that runs off the LVGL thread, a polled completion timer, a loading spinner, and three virtual buttons (Select / Refresh / Back). The sections below describe the shared architecture, then document each screen’s specialisations.
Architecture Overview
Section titled “Architecture Overview”Common Lifecycle Pattern
Section titled “Common Lifecycle Pattern”All three screens follow the same lifecycle governed by LVGL timers and FreeRTOS tasks.
Screen Transition & Destruction Guard
Section titled “Screen Transition & Destruction Guard”All three screens use a consistent set of macros to avoid double-free and race conditions between LVGL callbacks and navigation transitions.
Key lifecycle guards:
| Variable | Purpose |
|---|---|
is_prepared_for_destruction_ | Set by prepareForDestruction(); prevents re-entry via ESP3D_PREPARE_DESTRUCTION_GUARD |
cleanup_executed_ | Prevents the cleanup body from running more than once |
transition_timer | Delayed screen switch; destroyed by cleanup_timer_cb before the next screen is created |
cleanup_timer | Bridges the gap between button-release and destruction; created by ESP3D_TRANSITION_START |
Component Dependencies
Section titled “Component Dependencies”See screen_base_infrastructure for
ListMenuScreen,GenericScreen,ListMenuComponent, andVirtualButtonsComponent.See modal_dialog_screens for
inputScreenandmessageBoxScreen.See connection_status_screen for the post-connect status screen used after WiFi credential confirmation.
Data Structures
Section titled “Data Structures”Each screen defines a private struct that holds per-item data. The struct is passed generically through ListMenuComponent’s void*-based item API; the display callback casts it back to the concrete type.
ServerInfo::found (a14) is false for the saved server added to the list although it did not answer the mDNS scan.
Shared Async Scan Infrastructure
Section titled “Shared Async Scan Infrastructure”The following data flow is identical across all three screens. Only the blocking API call and result type differ.
Memory Safety
Section titled “Memory Safety”All scan tasks guard result-vector growth against std::bad_alloc. On allocation failure the loop breaks and whatever entries were collected so far are displayed. The ListMenuScreen is constructed with new (std::nothrow) and validated before registering with UIManager; a null result leaves the previous screen intact.
// Pattern used in all three scan tasks:try { local_xxx.emplace_back(...);} catch (const std::exception &e) { esp3d_log_e("OOM: %s — keeping %d entries", e.what(), local_xxx.size()); break;}Button Layout
Section titled “Button Layout”All three screens use the same three-button configuration.
┌──────────┬──────────┬──────────┐│ Select │ Refresh │ Back ││ (ok_b) │(refresh_b│ (back_b) ││ Button 0 │ Button 1 │ Button 2 │└──────────┴──────────┴──────────┘| Button | Press sound | Release action |
|---|---|---|
| 0 – Select | ESP3D_SELECTION_BEEP | Invoke onXxxItemClick for the focused item |
| 1 – Refresh | ESP3D_SELECTION_BEEP | Clear cache, re-run scan |
| 2 – Back | ESP3D_BACK_BEEP | Clear cache, transition to settings |
All three buttons are hidden via show_button(N, false) during the active scan and restored in destroy_loading_spinner().
WiFi Scan Screen (wifiScanScreen)
Section titled “WiFi Scan Screen (wifiScanScreen)”Purpose
Section titled “Purpose”Scans for visible 802.11 access points, displays them sorted by signal strength (descending), and lets the user connect by selecting an AP. Protected networks trigger a password prompt via inputScreen.
Radio Mode Support
Section titled “Radio Mode Support”The screen guards creation with a radio mode check. It handles three distinct WiFi driver states at scan time:
| Radio mode | Driver state at scan | Behaviour |
|---|---|---|
wifi_sta | Running STA | esp_wifi_scan_start() runs directly |
wifi_ap_config | AP-only (WIFI_MODE_AP) | Temporarily switches to WIFI_MODE_APSTA; restores WIFI_MODE_AP after scan |
| Any (SSID empty / uninitialised) | Stopped or not initialised | Full esp_wifi_init() + set_mode(STA) + start(); torn down after scan |
List Item Layout
Section titled “List Item Layout”┌─────────────────────────────────────────────────────────┐│ [✓] MyNetwork 75% [lock] ││ OpenNetwork 60% ││ AnotherAP 30% [lock] │└─────────────────────────────────────────────────────────┘ ↑ ↑ ↑ LV_SYMBOL_OK (active SSID) signal lock icon- Active SSID (matches
esp3d_sta_ssidNVS value): shown withLV_SYMBOL_OKinESP3D_INDICATOR_SUCCESS_COLOR - Lock icon (
ESP3D_SYMBOL_LOCK): displayed whenauthmode != WIFI_AUTH_OPEN - Long SSIDs are truncated with
LV_LABEL_LONG_DOT; label width is reduced dynamically when the signal percentage and/or lock icon are present
Selection and Credential Flow
Section titled “Selection and Credential Flow”open_server_scan_when_connected (a14): once WiFi connects, the connection status screen jumps to the server scan screen when the output is a WiFi IP CNC client and no server address is saved yet, so the WiFi setup continues with the server choice.
setModeAsync()is used instead of the synchronoussetMode()to avoid blocking the LVGL task for the duration of WiFi reconnection (xEventGroupWaitBits(portMAX_DELAY), up to 10 retries).
Scan Result Caching
Section titled “Scan Result Caching”scanned_aps_ is preserved across the transition to inputScreen so the list is instantly restored on Cancel. The saved_selection_ index restores the highlight to the previously focused item. The cache is only cleared on Refresh or Back.
NVS Keys Written
Section titled “NVS Keys Written”| Setting index | Value written |
|---|---|
esp3d_sta_ssid | Selected AP SSID |
esp3d_sta_password | Password (empty for open networks) |
esp3d_radio_mode | wifi_sta (ensures begin() auto-connects on next boot) |
Scan Task
Section titled “Scan Task”Bluetooth Scan Screen (scanBTScreen)
Section titled “Bluetooth Scan Screen (scanBTScreen)”Purpose
Section titled “Purpose”Scans for nearby Bluetooth Classic (SPP) or BLE (GATT) devices and lets the user select one as the CNC host. BT Classic requires a PIN confirmation (empty = SEC_NONE); BLE connects directly using the stack’s own pairing mechanism.
Returning to the settings screen from the BT scan is wrapped in try / catch (a14): an exception in createScreen() is logged instead of terminating. Each item logs its signal_strength.
Countdown Timer
Section titled “Countdown Timer”Because BT scanning has a known maximum duration (returned by getScanMaxDuration()), the spinner label shows a live countdown. Each scan_check_timer_cb tick recalculates remaining seconds from (scan_max_duration - elapsed_ms) / 1000 and updates the label.
List Item Layout
Section titled “List Item Layout”┌─────────────────────────────────────────────────────────┐│ [✓] None ││ [✓] FluidNC CNC (currently connected, inserted) ││ Printer001 88% ││ OtherDevice 45% │└─────────────────────────────────────────────────────────┘- None (MAC
00:00:00:00:00:00): always first; selects no host - Currently connected device: if absent from scan results, inserted at position 1 with no signal percentage
- Devices whose name is empty after
str_trimare discarded from the result set
BT Classic PIN Flow
Section titled “BT Classic PIN Flow”BLE Flow
Section titled “BLE Flow”BLE selection saves the address immediately and calls btBleClient.connect(). No sub-screen is shown. refresh_visible_items() updates the active-device marker in-place on the existing list.
NVS Keys Written
Section titled “NVS Keys Written”| Mode | Setting index | Value |
|---|---|---|
| BT Serial | esp3d_btserial_address | MAC address string |
| BT Serial | esp3d_btserial_pin | PIN string (empty = SEC_NONE) |
| BT BLE | esp3d_btble_address | MAC address string |
Scan Task
Section titled “Scan Task”Server Scan Screen (serverScanScreen)
Section titled “Server Scan Screen (serverScanScreen)”Purpose
Section titled “Purpose”Discovers CNC servers on the LAN using mDNS service discovery. The mDNS service type is selected at build time:
ESP3D_SOCKET_CLIENT_FEATURE→ queries_telnet._tcpESP3D_WS_CLIENT_SERVICE_FEATURE→ queries_ws._tcp
The user selects a discovered server; its resolved IPv4 address and port are saved to NVS and a connection is initiated.
List Item Layout
Section titled “List Item Layout”┌─────────────────────────────────────────────────────────┐│ [✓] fluidnc.local 23 ││ grblhal-machine.local 80 │└─────────────────────────────────────────────────────────┘ ↑ ↑ hostname (mDNS service name) port- Active server:
LV_SYMBOL_OKwhen both IP and port matchesp3d_socket_client_address/esp3d_socket_client_port - Saved server not answering (a14): when a server is saved but not in the scan results, it is still added first in the list, with its IP as name, so the user sees what is configured and can confirm or replace it. Its row shows
LV_SYMBOL_WARNING(warning color) instead of the check, and its label is dimmed withLV_OPA_50(opacity rather than color, since the list selection style overrides child text colors, not opacity) - Port is right-aligned; hostname is truncated with
LV_LABEL_LONG_DOTwhen needed
Selection Flow
Section titled “Selection Flow”The already-connected guard prevents a known crash: calling
connect()on an active socket triggerscloseSocket()while lwIP may be delivering packets. This corrupts TLSF free-block pointers and causes aStoreProhibitedexception a few seconds later.
NVS Keys Written
Section titled “NVS Keys Written”| Setting index | Value |
|---|---|
esp3d_socket_client_address | Resolved IPv4 string |
esp3d_socket_client_port | Port number (uint32) |
Scan Task
Section titled “Scan Task”
ip4addr_ntoareturns a pointer into a static buffer. The string must be copied into astd::stringimmediately to avoid stale-pointer bugs.
Full Connection Setup Data Flow
Section titled “Full Connection Setup Data Flow”Integration in the Screen System
Section titled “Integration in the Screen System”Entry points from settings_list_screen.cpp:
| Handler | Screen opened |
|---|---|
onWifiSSIDClick / onForgetWifiClick | wifiScanScreen::create() |
BT host selection / onForgetBTHostClick | scanBTScreen::create() |
onServerAddressClick | serverScanScreen::create() |
ESP32 Constraints
Section titled “ESP32 Constraints”| Concern | Mitigation |
|---|---|
| LVGL single-thread rule | All blocking scan calls run in dedicated FreeRTOS tasks; LVGL objects are only touched from the 250 ms check-timer callback running on Core 1 |
| RAM fragmentation | Result vectors use emplace_back with try/catch; shrink_to_fit() after clearing; ListMenuScreen uses new (std::nothrow) |
| Heap diagnostics | Every create(), prepareForDestruction(), and onScreenDestroy() logs esp_get_free_heap_size() |
| Stack size | All three scan tasks use 4096 bytes of stack via esp3d_task_create_unpinned |
| WiFi driver state | wifiScanScreen handles three driver states: running STA, AP-only (APSTA workaround during scan), and uninitialised (full init + start, torn down after scan) |
| Double connect crash | serverScanScreen checks isConnected() && same IP:port before calling connect() to avoid a known race in the TLSF/lwIP allocator |
| WiFi / BT mutual exclusion | Both cannot be active simultaneously on this board; the radio mode guard at create() prevents opening the wrong scan screen |
Related Documentation
Section titled “Related Documentation”- screen_base_infrastructure —
ListMenuScreen,GenericScreen,ListMenuComponent,VirtualButtonsComponent - modal_dialog_screens —
inputScreen(password / PIN entry) andmessageBoxScreen(error dialogs) - connection_status_screen — Post-WiFi-connect status screen with connecting spinner and auth-fail retry
- screens_architecture — Overall screen system design and
UIManager - screen_transitions_flow — Safe timer-based transition pattern (
ESP3D_TRANSITION_START,ESP3D_CLEANUP_TIMER_BODY) - connection_management — Transport lifecycle, radio modes, and connection states
docs/features/feature_resource_matrix.md— WiFi vs BT build exclusions and RAM budgetsdocs/features/mdns.md— mDNS registered services (relevant forserverScanScreen)docs/guides/esp32_memory_constraints.md— Heap fragmentation, WiFi vs BT RAM allocation