Skip to content

FluidNC Module

The fluidnc_module provides the FluidNC-specific UI layer for the Pibot CNC pendant firmware. It sits at the bottom of the UI_Framework_&_Screens hierarchy and implements three operational screens plus one shared component that are specific to the FluidNC firmware communication protocol.

FluidNC uses distinct GCode extensions and status formats ($SD/List, M6T<n>, M61Q<n>, [PRB:...], $SD/Run=, $SD/Delete=, $SD/Rename=) that differ from grbl and grblHAL. This module encapsulates all of that protocol-specific logic and keeps the shared CNC screens clean.

UI_Framework_&_Screens
└── cnc_shared (shared CNC screens: jog, status, macros, settings)
└── fluidnc_module ← THIS MODULE
├── components/
│ └── connection_status (clickable connection state indicator)
└── screens/
├── change_tool (M6T / M61Q tool change workflow)
├── files (SD card browser: $SD/List)
└── probe (G38.2 touch-probe sequence)

Sibling modules for other firmware targets follow the same pattern:

  • grbl_module — grbl-specific screens
  • grblhal_module — grblHAL-specific screens

Core Platform

Shared UI Infrastructure

fluidnc_module (main/display/cnc/fluidnc/)

ConnectionStatusComponent

components/connection_status.h

change_tool_screen

screens/change_tool_screen.cpp

files_screen

screens/files_screen.cpp

probe_screen

screens/probe_screen.cpp

GenericScreen

ListMenuScreen

PanelComponent

VirtualButtonsComponent

FirmwareStatusComponent

ESP3DValues (observable)

esp3dGcodeHandler

UIManager

ESP3DTranslationService


LVGL UI (Core 1)FluidNC ScreenESP3DValuesGcodeHandlerFluidNC FirmwareLVGL UI (Core 1)FluidNC ScreenESP3DValuesGcodeHandlerFluidNC FirmwareStatus response (position, state, [PRB:...])update observable valuessubscription callback (Core 1)update labels / icons (change-detected)button / encoder eventsendGcode(command)GCode / realtime command

File: main/display/cnc/fluidnc/components/connection_status.h
Documentation: fluidnc_module_connection_status.md

A lightweight, reusable overlay component that renders the current connection state as a clickable icon. It maps five connection state characters (?, A, T, C, .) to distinct visual states and opens the shared connection_status_screen on click.

Placed at a corner of every FluidNC screen (change_tool, probe) and exposes a static position-helper API (get_alignment(), get_x_position(), get_y_position()) so screens can place it correctly regardless of display orientation.

"?" (default)

"T" (transport)

"C" (connected)

"A" (auth error)

"." (radio off)

"." (radio off)

retry

Unknown

Connecting

Connected

AuthFailed

RadioOff


File: main/display/cnc/fluidnc/screens/change_tool_screen.cpp
Documentation: fluidnc_module_change_tool.md

Implements the full FluidNC tool change workflow, supporting two modes:

ModeGCodeBehavior
SET (M61Q)M61Q<n>Set current tool number without movement. Fast, always allowed.
CHANGE (M6T)M6T<n>Full ATC manual sequence: move to tool change position → HOLD pause → user installs tool → resume → ETS probe.

Internal state machine:

M6T sent (CHANGE mode)

M61Q sent (SET mode)

firmware HOLD detected

user resumes (cycle start)

firmware returns IDLE

alarm / cancel / timeout

user presses cancel

alarm during probing

re-enter editable state

firmware returns IDLE

IDLE

MOVING_TO_CHANGE

SUCCESS

WAITING_USER

PROBING

FAILED

CANCELLED

After an M61Q (SET) success, the screen optionally navigates to the probe_screen to touch-probe the new tool length.

Key implementation features:

  • Position display (MPos + WPos for X/Y/Z) with 250 ms throttling to protect LVGL from 10 Hz flooding
  • 60-second safety timeout with graceful M61Q revert on expiry
  • Encoder-navigable tool selector (mode toggle + target tool number) via PanelComponent

File: main/display/cnc/fluidnc/screens/files_screen.cpp
Documentation: fluidnc_module_files.md

An SD card file browser built on ListMenuScreen that uses FluidNC’s $SD/List streaming protocol. Files arrive asynchronously as [DIR:name] / [FILE:name|SIZE:bytes] lines published through ESP3DValuesIndex::firmware_file_entry.

start_sd_listing() for sub-directory

start_sd_listing() for root

target DIR depth found

end marker received

mount failure detected

watchdog fires (10s stall)

Idle

Searching

Collecting

Done

Error

Timeout

Four file action modes (controlled by hardware switch or touch footer toggle):

ModeAction on file selection
ProcessLaunch $SD/Run=<path> → navigate to status screen
MacroToggle file as macro via macroManager
RenameOpen text input → $SD/Rename=<old>><new>
DeleteConfirm dialog → $SD/Delete=<path>

Memory safety: collection halts when free heap falls below 30 KB. A 10-second watchdog timer fires if no new entries arrive.


File: main/display/cnc/fluidnc/screens/probe_screen.cpp
Documentation: fluidnc_module_probe.md

Implements a 4-step touch-probe sequence using standard G-code (G38.2, G1, G10 L20 P0). Supports all FluidNC axes (X/Y/Z/A/B/C) with per-axis parameter sets (Offset, MaxTravel, Retract, FeedRate).

user presses START

firmware confirms IDLE

[PRB:...:1] contact detected

[PRB:...:0] no contact / timeout

firmware returns IDLE

[PRB:...:1] precise contact

[PRB:...:0] no contact / timeout

firmware returns IDLE

IDLE

PRE_CHECK

SEEK

RETRACT

FAILED

FEED

APPLY_OFFSET

SUCCESS

CANCELLED

StepGCode sentPurpose
PRE_CHECK? (realtime)Verify machine is IDLE
SEEKG91 G38.2 <axis><travel> F<rate>Fast probe pass
RETRACTG91 G1 <axis><retract> F200Back away from surface
FEEDG91 G38.2 <axis><travel> F<rate/2>Slow precise probe
APPLY_OFFSETG10 L20 P0 <axis><offset>Set work coordinate zero

Timeout safety: calculated dynamically as (|max_travel| / feed_rate) × 60 × 1.5 + 5s, clamped between 10s and 120s.

The screen returns a ProbeResult (SUCCESS / FAILED / CANCELLED / NONE) via a static inter-screen API, allowing change_tool_screen to chain probe after tool set.


All FluidNC screens subscribe to ESP3DValues observables and react to changes in the LVGL task (Core 1). The subscribe/unsubscribe lifecycle is tied to screen creation and prepareForDestruction().

Value IndexUsed by
server_statusConnectionStatusComponent (all screens)
firmware_statuschange_tool, probe, files
parser_statechange_tool (extracts current tool T<n>)
firmware_file_entryfiles (SD listing stream)
probe_statusprobe ([PRB:x,y,z:ok])
position_wx/wy/wzchange_tool, probe
position_mx/my/mzchange_tool, probe
axis_countprobe
axis_namesprobe (grblHAL UVW support)
pin_statesprobe (limit switch indicators)

All screens follow the same safe two-timer transition pattern to avoid destroying LVGL objects inside event callbacks:

Next Screentransition_timer (delay)cleanup_timer (1-shot)ScreenUser EventNext Screentransition_timer (delay)cleanup_timer (1-shot)ScreenUser Eventbutton releaselv_timer_create(cleanup_timer_cb)prepareForDestruction()lv_timer_create(transition_timer_cb)createScreen(target_type)

The macros ESP3D_TRANSITION_START, ESP3D_CLEANUP_TIMER_BODY, and ESP3D_TRANSITION_TIMER_BODY encode this pattern consistently.

All screens displaying machine/work position implement a 250 ms per-group throttle plus a flush timer to catch skipped values:

Subscription rate: ~10 Hz per axis (6 axes = 60 callbacks/s)
Throttle gate: 250 ms per WPos group / 250 ms per MPos group
position_flush_timer: ~300 ms period, fires if any pending flag is set

This is critical for LVGL responsiveness — unthrottled updates would trigger 60 redraws per second.


DependencyRole
UI_Framework_&_Screens → cnc_sharedGenericScreen, ListMenuScreen, FirmwareStatusComponent, PanelComponent, VirtualButtonsComponent
Core_Platform_&_InfrastructureESP3DValues, ESP3DSettings, ESP3DTranslationService
CNC_Firmware_Integrationesp3dGcodeHandler, ESP3DGCodeHandlerService (FluidNC target)
LVGL (ESP-IDF component)UI rendering, timers, events