Skip to content

grbl_module_connection_status

grbl_module_connection_status is the grbl-firmware-specific connection status indicator widget. It is a compact, clickable LVGL component displayed in the top-right corner of CNC screens. The widget reflects the live connection state between the pendant and a grbl CNC controller by combining two independent status signals (transport layer + server/application layer) into a single icon, and opens the full connection_status_screen when the user taps it.

Source file: main/display/cnc/grbl/components/connection_status.h


The component sits at the intersection of the UI layer and the reactive value system. It owns its own LVGL objects, subscribes directly to ESP3DValues for live updates, and delegates detailed connection management to a dedicated full-screen view.

grbl_module_screen_router

Core - ESP3DValues

Common Screens

grbl_module (UI_Framework_&_Screens)

subscribes

subscribes

click → timer → createScreen

creates

configures

reads same values

reads same values

embeds (parent lv_obj_t*)

Host Screens (grbl)

jog_screen

status_screen

probe_screen

change_tool_screen

files_screen

ConnectionStatusComponent

grbl/components/connection_status.h

ConnectionStatusConfig

connection_status_screen

common/screens/

connection_status

ESP3DValuesIndex

server_status

ESP3DValuesIndex

esp3d_screen_type.cpp

createScreen


configures

subscribes to\nconnection_status\nserver_status

1

1

ConnectionStatusConfig

+lv_align_t align = LV_ALIGN_TOP_RIGHT

+int32_t x_offset = 0

+int32_t y_offset = 0

+ESP3DScreenType return_screen = main

+void (*prepare_parent_screen_destruction)()

ConnectionStatusComponent

-img_ lv_obj_t

-spinner_ lv_obj_t

-click_container_ lv_obj_t

-press_circle_ lv_obj_t

-is_valid_ bool

-is_connection_ok_ bool

-orientation_ int32_t

-config_ ConnectionStatusConfig

-transport_status char

-server_status char

-host_screen_static_ ESP3DScreenType

-screen_timer_ lv_timer_t*

-pending_config_ ConnectionStatusConfig

+ConnectionStatusComponent(parent, host_screen, config, angle)

+~ConnectionStatusComponent()

+updateOrientation(angle) : void

+updatePosition(align, x, y) : void

+updateConfig(config) : void

+prepareForDestruction() : void

+isValid() : bool

+get_x_position(use_orientation) : int32_t

+get_y_position(use_orientation) : int32_t

+get_alignment(use_orientation) : lv_align_t

+getHostScreen() : ESP3DScreenType

-compute_display_state() : uint8_t

-update_connection_status(state) : void

-on_status_update(idx, value, action) : bool

-static_on_status_update(idx, value, action) : bool

-on_click_event(e) : void

-open_status_screen_cb(timer) : void

ESP3DValues

+subscribe(index, callback) : bool

+unsubscribe(index, callback) : bool

+get_value(index) : const char


The component tracks two independent status characters, each delivered via ESP3DValues:

ESP3DValuesIndexCharacterMeaning
connection_statusUUnknown / initializing
connection_statusTTransport connecting (WiFi assoc, BT pairing…)
connection_statusCTransport connected
connection_status?Transport disconnected / not configured
connection_status.Radio off
server_statusTServer/application handshake in progress
server_statusCServer connected — full CNC control
server_statusAAuthentication failed
server_status?Server disconnected

The private method compute_display_state() merges both characters into a single uint8_t icon selector passed to update_connection_status(). The displayed state follows a worst-case priority: any non-C condition in either layer drives the indicator to a degraded state. Only when both transport and server are C does the component show a fully connected icon.

init (_transport='?', _server='?')

transport or server = 'T'

both = 'C'

timeout / error

transport or server ≠ 'C'

reconnect attempt

server = 'A'

retry

Unknown

Connecting

Connected

Disconnected

AuthFailed

Visual feedback while Connecting (T): the spinner_ LVGL object is made visible; it is hidden for all other states.


parent lv_obj_t*

(screen container)

click_container_

(transparent, clickable area)

img_

(status icon - varies by state)

spinner_

(animated arc - visible when 'T')

press_circle_

(visual press ripple)

  • click_container_ is sized to the icon area and receives LV_EVENT_CLICKED → routed to on_click_event.
  • img_ displays the composite state icon (connected / disconnected / auth-failed / radio-off).
  • spinner_ replaces the icon with an animated arc during T (connecting) state.
  • press_circle_ provides tactile-style press feedback.

UserLVGL (Core 1)ConnectionStatusComponentESP3DValuesFirmware / Transport LayerUserLVGL (Core 1)ConnectionStatusComponentESP3DValuesFirmware / Transport Layerset_value(connection_status, "T")static_on_status_update(connection_status, "T")on_status_update() → _transport_status_='T'compute_display_state()update_connection_status(state)\n→ show spinner_, hide img_set_value(server_status, "C")static_on_status_update(server_status, "C")on_status_update() → _server_status_='C'compute_display_state()update_connection_status(state)\n→ show img_ (connected icon), hide spinner_tap iconon_click_event(e)lv_timer_create(open_status_screen_cb)open_status_screen_cb(timer)prepareForDestruction() on host screencreateScreen(connection_status)

Thread-safety note: static_on_status_update is invoked from ESP3DValues::handle(), which runs on the LVGL task (Core 1). All subsequent LVGL calls inside the callback are therefore safe without additional locking. The timer-based screen transition (open_status_screen_cb) defers the screen creation to the next LVGL tick — this matches the safe screen-transition pattern mandated in docs/architecture/screen_transitions_flow.md.


taps

screen destroy

ConnectionStatusComponent()

constructor

esp3dXValues.subscribe

(connection_status, static_on_status_update)

esp3dXValues.subscribe

(server_status, static_on_status_update)

Widget live - updates rendered in-place

User taps OR

screen about to close

on_click_event()

→ lv_timer_create(open_status_screen_cb)

prepareForDestruction()

on host screen callback

open_status_screen_cb()

→ createScreen(connection_status)

prepareForDestruction()

→ is_prepared_for_destruction_=true

~ConnectionStatusComponent()

→ esp3dXValues.unsubscribe × 2

The static callback (static_on_status_update) delegates to the instance method on_status_update via the static host_screen_static_ pointer. Because static_on_status_update is registered as the subscription callback, it remains valid even after intermediate screen transitions — the is_prepared_for_destruction_ guard prevents acting on a destroyed widget.


struct ConnectionStatusConfig {
lv_align_t align = LV_ALIGN_TOP_RIGHT; // LVGL anchor point
int32_t x_offset = 0; // Horizontal offset from anchor
int32_t y_offset = 0; // Vertical offset from anchor
ESP3DScreenType return_screen = ESP3DScreenType::main; // Screen to return to from detail view
void (*prepare_parent_screen_destruction)(void) = nullptr; // Called before navigating away
};
FieldDefaultPurpose
alignLV_ALIGN_TOP_RIGHTLVGL alignment anchor for the component
x_offset0Horizontal pixel offset from the anchor
y_offset0Vertical pixel offset from the anchor
return_screenESP3DScreenType::mainReturn destination after closing connection_status_screen
prepare_parent_screen_destructionnullptrOptional hook called before the host screen is torn down during navigation
ConnectionStatusComponent(lv_obj_t *parent,
ESP3DScreenType host_screen,
const ConnectionStatusConfig& config,
int32_t angle = 0);
ParameterDescription
parentLVGL object to parent the widget to (typically screen_instance_->getContainer())
host_screenIdentifies the calling screen; stored in host_screen_static_ for screen-router callbacks
configPositioning and navigation configuration
angleInitial orientation angle (0°, 90°, 180°, 270°) from ui_manager.getOrientationAngle()
MethodDescription
updateOrientation(angle)Re-positions the widget for a new display rotation
updatePosition(align, x, y)Moves the widget to a new LVGL anchor/offset without rebuilding objects
updateConfig(config)Replaces the stored config (affects next click navigation)
prepareForDestruction()Sets the destruction guard; must be called before the host screen’s LVGL objects are deleted
isValid()Returns true if the component was successfully constructed
MethodDescription
get_x_position(use_orientation)Returns the canonical x offset adjusted for orientation
get_y_position(use_orientation)Returns the canonical y offset adjusted for orientation
get_alignment(use_orientation)Returns the canonical LVGL alignment for orientation
getHostScreen()Returns the ESP3DScreenType of the currently active host screen

Host screens in the grbl module embed this component as a member. The typical usage pattern is:

// In screen creation (e.g., jog_screen, status_screen, probe_screen)
ConnectionStatusConfig css_cfg;
css_cfg.align = ConnectionStatusComponent::get_alignment();
css_cfg.x_offset = ConnectionStatusComponent::get_x_position();
css_cfg.y_offset = ConnectionStatusComponent::get_y_position();
css_cfg.return_screen = ESP3DScreenType::jog; // this screen
css_cfg.prepare_parent_screen_destruction = prepareForDestruction; // host screen cleanup
connection_status_ = new ConnectionStatusComponent(
container,
ESP3DScreenType::jog,
css_cfg,
ui_manager.getOrientationAngle()
);
// In screen destruction
if (connection_status_) {
connection_status_->prepareForDestruction();
delete connection_status_;
connection_status_ = nullptr;
}

The widget is embedded in the following grbl screens:

ScreenEffect of connection state on that screen
jog_screensystemSetLockState toggled; axis display delayed 1 s on reconnect
status_screensystemSetLockState toggled; action area state updated
probe_screensystemSetLockState toggled; axis count refresh triggered
change_tool_screenLog only (no lock effect)
files_screenUsed for job launch gating
main_screenLock button visibility and state driven by server_status

When the user taps the indicator, the component:

  1. Plays a selection beep (ESP3D_SELECTION_BEEP).
  2. Creates a one-shot lv_timer_t (screen_timer_) to defer the transition to the next LVGL tick.
  3. In open_status_screen_cb, calls prepare_parent_screen_destruction (if set) then invokes createScreen(ESP3DScreenType::connection_status).
  4. The grbl screen router (esp3d_screen_type.cpp::createScreen) instantiates connectionStatusScreen with return_screen = ESP3DScreenType::settings.

tap indicator

Back button

Any grbl CNC screen

(jog / status / probe / …)

connection_status_screen

(transport + server detail)

settings_screen

(grbl router default)

Note: The return_screen in ConnectionStatusScreenConfig is set to ESP3DScreenType::settings by the grbl screen router, overriding whatever return_screen was set in the host’s ConnectionStatusConfig. This is consistent across grbl, grblHAL, and FluidNC routers.


The component adapts its position for all four display rotations. The three static helpers (get_alignment, get_x_position, get_y_position) query ui_manager.getOrientationAngle() internally when use_orientation = true (default), so callers can use them directly in screen creation without storing the angle separately. Passing false returns the canonical un-rotated value (useful for resetting to defaults).


  • 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.

This component has structurally identical counterparts for the other supported firmware targets. The interface, lifecycle, and state machine are the same across all three:

ModuleFileFirmware
grbl_module_connection_status (this module)main/display/cnc/grbl/components/connection_status.hgrbl
fluidnc_module_connection_statusmain/display/cnc/fluidnc/components/connection_status.hFluidNC
grblHAL (embedded in grblhal_module)main/display/cnc/grblhal/components/connection_status.hgrblHAL

Firmware-specific differences, if any, are isolated to the .cpp implementation file (not the header), specifically in compute_display_state() and how update_connection_status() maps states to icons.


DependencyRole
lvglLVGL UI rendering and event system
ESP3DValuesObservable value system — delivers connection_status and server_status updates
screens/esp3d_screen_type.hESP3DScreenType enum for navigation targets
connection_status_screenFull-screen detail view opened on tap
UIManagerOrientation angle, style application, screen registration
grbl_module_screen_routercreateScreen() — instantiates connection_status_screen

  • LVGL thread only. All methods must be called from the LVGL task (Core 1). The subscription callback static_on_status_update is already guaranteed to run on Core 1 via ESP3DValues::handle().
  • No blocking. Screen transitions use lv_timer_create — never call createScreen directly inside an event callback.
  • prepareForDestruction() is mandatory before deleting the component or before the parent screen’s LVGL objects are freed, to prevent the subscription callback from acting on dangling pointers.
  • Static members are global per-firmware-target. Because host_screen_static_, screen_timer_, and pending_config_ are static, only one ConnectionStatusComponent instance may be active per firmware module at a time. This is enforced by the screen architecture — each firmware module has exactly one active screen at any given moment.