Skip to content

grblHAL Module — Connection Status Component

grblHAL Module — Connection Status Component

Section titled “grblHAL Module — Connection Status Component”

The grblhal_module_connection_status module provides the connection status indicator widget used across all grblHAL firmware screens. It is a compact, clickable LVGL component that permanently occupies the corner of the host screen and gives the operator a live, at-a-glance view of the CNC link state. A single tap on the icon navigates to the full connection status screen.

The component is architecturally identical to its counterparts in the fluidnc and grbl modules, with one grblHAL-specific extension: the "R" (read-only / PASSIVE token) connection state, which has no equivalent in the other firmware targets.

Source files

FileRole
main/display/cnc/grblhal/components/connection_status.hClass declaration — ConnectionStatusComponent, ConnectionStatusConfig

Navigation

Value System

LVGL Core 1 Thread

creates / owns

subscribes

subscribes

renders

LV_EVENT_CLICKED

createScreen()

Host Screen

(status / jog / probe / change_tool)

ConnectionStatusComponent

(grblHAL variant)

LVGL Objects

click_container · press_circle

img · spinner

ESP3DValues

(Observable store)

connection_status

(transport char)

server_status

(server char)

lv_timer (deferred)

open_status_screen_cb

connectionStatusScreen


The component tracks two independent status characters sourced from ESP3DValues and combines them into a single display_state byte via compute_display_state().

Source indexMember fieldMeaning
ESP3DValuesIndex::connection_status_transport_status_Physical/radio link layer (UART, BT, WiFi…)
ESP3DValuesIndex::server_status_server_status_Application-level CNC session
CharNameDescriptiongrblHAL only
?Unknown / DisconnectedDefault; transport or session not yet established
.Radio offTransport disabled (WiFi/BT turned off)
TConnectingTransport connecting or server handshake in progress
AAuth failedServer rejected authentication
CConnected (full)Session established with full control
RConnected (read-only)Connected but grblHAL PASSIVE token not held — display-only mode✓

grblHAL PASSIVE note: The "R" state is unique to grblHAL. It indicates that the pendant has a live connection but does not currently hold the motion-control token, so CNC commands are blocked. The icon reflects this with a distinct visual state. The grbl and fluidnc variants never emit "R".


Plain configuration struct; callers pass this at construction time and can update it later via updateConfig().

struct ConnectionStatusConfig {
lv_align_t align = LV_ALIGN_TOP_RIGHT; // Corner placement
int32_t x_offset = 0; // Pixel offset X
int32_t y_offset = 0; // Pixel offset Y
ESP3DScreenType return_screen = ESP3DScreenType::main; // Where to go on Back
void (*prepare_parent_screen_destruction)(void) = nullptr;// Pre-destroy hook
};
FieldPurpose
alignLVGL alignment anchor — defaults to top-right corner
x_offset / y_offsetFine-tune offset within the anchor
return_screenThe screen the connection_status_screen navigates back to when dismissed
prepare_parent_screen_destructionOptional callback invoked just before the parent screen is destroyed (e.g. to unsubscribe sibling components)

The main widget class. One instance lives per host screen that embeds a connection indicator.

parent (host screen container)

click_container_

(transparent, hit-testable)

press_circle_

(visual feedback ring)

img_

(state icon image)

spinner_

(animated arc - T state only)

ObjectLVGL typeRole
click_container_lv_obj_tTransparent hit area; receives LV_EVENT_CLICKED
press_circle_lv_obj_tCircular highlight shown on press for tactile feedback
img_lv_obj_t (image)Displays the icon corresponding to the current connection state
spinner_lv_obj_t (spinner)Rotating arc; visible only while _transport_status_ == 'T'
MethodDescription
ConnectionStatusComponent(parent, host_screen, config, angle)Constructor — creates LVGL objects, subscribes to value updates
~ConnectionStatusComponent()Destructor — unsubscribes from values, deletes LVGL objects safely
updateOrientation(angle)Re-positions the widget for a new display rotation angle
updatePosition(align, x, y)Moves the widget to a new anchor/offset without full re-creation
updateConfig(config)Hot-updates the full configuration (useful when screen transitions change the return target)
prepareForDestruction()Called by the host screen before destroying itself; disarms callbacks to prevent use-after-free
isValid() → boolGuard — returns false if construction failed (allocation error)

Static Helpers (orientation-aware placement)

Section titled “Static Helpers (orientation-aware placement)”

These static helpers let sibling components calculate their own positions relative to where the connection status icon will appear, even before an instance exists.

MethodDescription
get_x_position(get_orientation)Returns the X pixel offset adjusted for the current display orientation
get_y_position(get_orientation)Returns the Y pixel offset adjusted for the current display orientation
get_alignment(get_orientation)Returns the lv_align_t anchor adjusted for the current display orientation
getHostScreen() → ESP3DScreenTypeReturns the screen type of the current host — used by connection_status_screen to set its return target

connectionStatusScreenLVGL (Core 1)ConnectionStatusComponentESP3DValuesconnectionStatusScreenLVGL (Core 1)ConnectionStatusComponentESP3DValuesNormal status update flowUser tap flowpending_config_ stores cfg\nfor timer callbackstatic_on_status_update(idx, value, action)on_status_update() - stores in _transport_status_ or _server_status_compute_display_state() → uint8_tupdate_connection_status(state)\n• swap img_ source\n• show/hide spinner_\n• update press_circle_on_click_event(LV_EVENT_CLICKED)prepareForDestruction()lv_timer_create(open_status_screen_cb, ...)open_status_screen_cb(timer)\n→ connectionStatusScreen::create(cfg)

LVGL event callbacks are plain C function pointers and cannot carry a C++ this pointer. The component bridges this with static members:

static bool static_on_status_update(idx, value, action)
└─► delegates to instance → on_status_update(idx, value, action)
static void on_click_event(lv_event_t *e)
└─► reads host_screen_static_ and pending_config_
schedules open_status_screen_cb timer
static void open_status_screen_cb(lv_timer_t *timer)
└─► calls createScreen(ESP3DScreenType::connection_status)

The timer-deferred navigation pattern is mandatory per the LVGL threading rules documented in screens_architecture: UI objects must never be destroyed synchronously inside an event callback.


new ConnectionStatusComponent(...)

LVGL objects created\nvalue subscriptions active\nis_valid_ = true

allocation failed\nis_valid_ = false

ESP3DValues callback fires

icon / spinner refreshed

host screen calls prepareForDestruction()

incoming callbacks silently dropped\nis_prepared_for_destruction_ = true

~ConnectionStatusComponent()

~ConnectionStatusComponent()

~ConnectionStatusComponent()

Constructing

Valid

Invalid

Updating

PendingDestroy

Destroyed

Key safety invariants:

  1. is_valid_ is checked at the top of static_on_status_update — stale callbacks after construction failure are silently dropped.
  2. is_prepared_for_destruction_ short-circuits all incoming callbacks after prepareForDestruction() is called, preventing stale LVGL object access during teardown.
  3. Screen navigation is always deferred to an lv_timer — never triggered synchronously inside an LVGL event callback.
  4. pending_config_ (static member) stores the configuration for the timer callback, avoiding a dangling reference to a stack-allocated struct.

Host Screens (consumers)

Navigation Target

UI Framework

Core Platform

subscribes / reads

lv_obj_* calls

getOrientationAngle()

createScreen()

instantiates

embeds

embeds

embeds

embeds

ConnectionStatusComponent

(grblHAL)

ESP3DValues

values.md

LVGL library

UIManager

ui_core.md

ESP3DScreenType

grblhal_module_screen_router.md

connectionStatusScreen

connection_status_screen.md

statusScreen

jogScreen

probeScreen

changeToolScreen

DependencyTypeNotes
ESP3DValues → valuesRuntimeObservable store providing connection_status and server_status change events
LVGLExternal libraryAll widget rendering; strictly confined to Core 1
UIManagerSingletonSupplies current orientation angle for positioning
ESP3DScreenTypeEnumIdentifies the host screen and the navigation target
connectionStatusScreen → connection_status_screenNavigation targetFull-detail screen opened on tap

All three firmware modules expose an identical public API for ConnectionStatusComponent and ConnectionStatusConfig. The sole behavioral difference is in compute_display_state(), which maps the two raw status characters to a display state byte:

VariantExtra stateHeader path
grblHAL (this module)"R" read-only / PASSIVE tokenmain/display/cnc/grblhal/components/connection_status.h
grbl → grbl_module_connection_status—main/display/cnc/grbl/components/connection_status.h
fluidnc → fluidnc_module_connection_status—main/display/cnc/fluidnc/components/connection_status.h

This design ensures all host screens can be updated identically across firmware targets — only the grblHAL variant produces a distinct visual for the PASSIVE ("R") state.


Typical embedding in a grblHAL host screen:

#include "cnc/grblhal/components/connection_status.h"
static ConnectionStatusComponent* cs_component = nullptr;
// --- Screen creation ---
static void onScreenAboutToDestroy();
ConnectionStatusConfig cs_config;
cs_config.align = ConnectionStatusComponent::get_alignment();
cs_config.x_offset = ConnectionStatusComponent::get_x_position();
cs_config.y_offset = ConnectionStatusComponent::get_y_position();
cs_config.return_screen = ESP3DScreenType::main;
cs_config.prepare_parent_screen_destruction = onScreenAboutToDestroy;
cs_component = new (std::nothrow) ConnectionStatusComponent(
container,
ESP3DScreenType::status, // host_screen identity
cs_config,
ui_manager.getOrientationAngle()
);
if (!cs_component || !cs_component->isValid()) {
// Handle allocation failure — do NOT throw in LVGL context
delete cs_component;
cs_component = nullptr;
}
// --- Orientation change ---
if (cs_component) {
cs_component->updateOrientation(new_angle);
}
// --- Pre-destroy hook (called from ConnectionStatusConfig callback) ---
static void onScreenAboutToDestroy() {
if (cs_component) {
cs_component->prepareForDestruction();
}
}
// --- LV_EVENT_DELETE handler ---
static void onScreenDestroy(lv_event_t *e) {
delete cs_component;
cs_component = nullptr;
}

Memory note: Use new (std::nothrow) and always check isValid(). The embedded heap can be very small (< 1 KB largest free block under load). Never throw inside an LVGL event or screen callback — an uncaught exception resets the board. See esp32_memory_constraints.


  • No exceptions from the constructor. Callers use new (std::nothrow) and check isValid(); an exception would reach std::terminate. On a failure the constructor returns early and rolls back what it already did (component registration in ui_manager, value subscriptions), and is_valid_ is only set once construction fully succeeded. The destructor then skips prepareForDestruction() for a partially constructed component.
  • updateThemeColor() re-applies every theme-dependent color: press circle border (ESP3D_ACCENT_SELECT_COLOR), spinner arc (ESP3D_ACCENT_ACTIVE_COLOR) and the state recolor, recomputed from the cached transport / server characters (compute_display_state()). The settings screen calls it on a live theme preview and when a theme is applied.
  • The radio layers write connection_status / server_status only when the radio is the CNC link (ESP3DNetwork::radioIsCncLink(), see network.md), so with a serial CNC link and WiFi for remote access the icon follows the CNC transport only.

DocumentRelevance
connection_status_screenFull-detail screen this component navigates to on tap
grblhal_module_screen_routercreateScreen() dispatcher; routes ESP3DScreenType::connection_status to connectionStatusScreen
grblhal_module_change_toolHost screen that embeds this component
grblhal_module_probeHost screen that embeds this component and reacts to "C" to release the system lock
grbl_module_connection_statusgrbl counterpart (identical API, no "R" state)
fluidnc_module_connection_statusfluidnc counterpart (identical API, no "R" state)
valuesESP3DValues observable store that powers status updates
connection_managementOverall connection lifecycle and status character semantics
screens_architectureLVGL screen system and mandatory timer-based transition rules
esp32_memory_constraintsHeap fragmentation and safe allocation patterns for ESP32