Skip to content

Adding Real-time Values

Relevant source files

This page explains how to extend the ESP3DValues pub/sub system to add new real-time data streams that propagate from the G-code handler or network modules to UI screens. This is essential for displaying new firmware information, machine states, or sensor data in the pendant interface.


The ESP3DValues system provides a centralized pub/sub architecture for distributing real-time data from various services to multiple UI components without tight coupling. The singleton instance esp3dXValues manages all global states. Data typically enters the system via set_value() and leaves via callback notifications to subscribers.

Titled: “Real-time Value Propagation Flow”

Data Sources

ESP3DValues Core

Publishing API

Subscription API

Subscribers (Screens)

update_screen.cpp onUpdateValueChange

firmware_status_screen.cpp onFirmwareStatusUpdate

FirmwareStatusComponent static_on_status_update

main_screen.cpp onConnectionStatusUpdate

subscribe(index, callback)

unsubscribe(index, callback)

get_value(index)

set_value(index, value, action)

Notify All Subscribers

esp3dXValues (Global Instance)

ESP3DValuesIndex (enum)

Value Storage (_buffer_pool)

Subscription Registry (_subscriber_pool)

ESP3DGCodeHandlerService processStatus()

ESP3DWebsocketClient onWebsocketEvent()

update_screen.cpp updateCallbackWithDisplay()

Sources:


All real-time values are identified by the ESP3DValuesIndex enum. This system uses an X-Macro pattern to maintain consistency between the enumeration and the static definitions stored in RAM/Flash.

  1. esp3d_system_values_defs.inc: Contains system-wide values including position strings (position_wx), overrides, and job management data main/target/cnc/esp3d_system_values_defs.inc#1-110
  2. esp3d_target_values_defs.inc: Contains firmware-specific values defined per target (e.g., FluidNC vs grblHAL).
  3. esp3d_values.h: Declares the ESP3DValuesIndex enumeration dynamically using the include files above.
IndexPurposeSubscriber Example
server_statusConnection state: ‘C’ (Connected), ‘T’ (Connecting), ’?’ (Disconnected)onConnectionStatusUpdate in main_screen.cppmain/display/cnc/screens/main_screen.cpp#74-85
firmware_statusMachine state (Idle, Run, Hold, Alarm)firmware_status_screen.cppmain/display/cnc/screens/firmware_status_screen.cpp#76-86
message_historyBuffer for MSG:ERR or MSG:INFO messagesfirmware_status_screen.cppmain/display/cnc/screens/firmware_status_screen.cpp#77
update_progressFlash progress percentage (0-100)update_screen.cppmain/display/screens/update_screen.cpp#138-149

Sources:


To add a new real-time value, follow these steps:

Add the new value to esp3d_system_values_defs.inc using the ESP3D_VAL_DEF macro. You must specify the buffer size (in bytes) required to hold the string representation of the value.

// Format: ESP3D_VAL_DEF(name, type, size, initial_value)
ESP3D_VAL_DEF(probe_result, string_t, 32, "0.000")

Reference for current definitions:main/target/cnc/esp3d_system_values_defs.inc#102-106

Use the set_value method on the esp3dXValues global instance.

Example from update_screen.cpp: The update service calls back to the UI, which propagates the progress to the global values system: main/display/screens/update_screen.cpp#62-63

esp3dXValues.set_value(ESP3DValuesIndex::update_progress,
std::to_string(percent).c_str());

Screens and UI components subscribe to value updates to react in real-time.

Code Entity Mapping: Subscription Lifecycle

Section titled “Code Entity Mapping: Subscription Lifecycle”

Titled: “Subscription Lifecycle in GenericScreen”

lv_label_tesp3dXValuesfirmwareStatusScreenlv_label_tesp3dXValuesfirmwareStatusScreenScreen CreationHandler receives StatusScreen Destructionsubscribe(ESP3DValuesIndex::firmware_status | onFirmwareStatusUpdate)return true (registered)onFirmwareStatusUpdate(index | value | Update)lv_label_set_text(title_label_ | value)unsubscribe(ESP3DValuesIndex::firmware_status | onFirmwareStatusUpdate)return true (removed)

Sources:

  1. Define a Callback: The callback signature must be bool(ESP3DValuesIndex, const char *, ESP3DValuesCbAction).
  2. Filter Actions: Always check if action == ESP3DValuesCbAction::Updatemain/display/screens/update_screen.cpp#131-133
  3. Update LVGL Objects: Ensure you verify object validity using lv_obj_is_valid() before updating UI elements main/display/screens/update_screen.cpp#140-141
  4. Unsubscribe: In prepareForDestruction(), remove the subscription to prevent the system from calling a pointer to a destroyed screen.

RequirementImplementation Detail
UnsubscribeMust be called in prepareForDestruction for both system and target values main/display/cnc/screens/main_screen.cpp#56-59
Initial SyncAfter subscribing, the callback is not automatically fired. You may need to call get_value(index) to set the initial UI state.
Heap SafetyAvoid complex logic inside the callback; just update labels, styles, or icons.
Return ValueReturn true to maintain the subscription. Returning false will cause esp3dXValues to automatically remove the subscriber.

Sources: