Skip to content

Extending G-code Support

Relevant source files

Purpose: This document guides developers through extending the G-code handler service to support new firmware commands, parse additional status information, and integrate custom functionality with FluidNC/grblHAL firmware. It covers command classification, status parsing, response processing, and integration with the real-time values system.

For information about the overall G-code handler architecture and value subscription system, see G-code Handler Service and Real-time Value System. For adding new real-time values to the system, see Adding Real-time Values.


The G-code handler classifies commands into several categories to determine proper handling, acknowledgment requirements, and transmission protocols.

The system defines command types in main/target/cnc/fluidnc/esp3d_gcode_handler_service.h#82-86:

enum class ESP3DCommandType : int8_t {
unknown = -1,
normal = 0,
realtime = 1
};

Commands are classified using static arrays in the implementation. For example, screenCommands identifies UI display commands like M117 which are forwarded to the status bar.

Sources: main/target/cnc/fluidnc/esp3d_gcode_handler_service.h#82-86main/target/cnc/fluidnc/esp3d_gcode_handler_service.h#157


The primary method for sending G-code is ESP3DGCodeHandlerService::sendGcode() defined at main/target/cnc/fluidnc/esp3d_gcode_handler_service.h#134-138

bool sendGcode(const char* data,
ESP3DClientType origin = ESP3DClientType::system,
ESP3DRequest requestId = {.id = 0},
ESP3DCommandType cmdType = ESP3DCommandType::unknown,
ESP3DMessagePriority priority = ESP3DMessagePriority::normal);

This diagram shows how ESP3DGCodeHandlerService interacts with the ESP3DCommands and ESP3DClient entities to dispatch G-code.

Title: “G-code Dispatch Flow”

sendGcode(data, origin, requestId, cmdType)

cmdType?

needsNewline = false

needsNewline = (data[len-1] != '

')

isRealTimeCommand(data)

esp3dCommands.canSendData()

return false

ESP3DClient::newMsg()

needsNewline?

tempBuffer = data + '

'

use data as-is

setDataContent()

set request_id, type

esp3dCommands.dispatch()

return result

The system supports standard GRBL real-time commands defined as macros in main/target/cnc/fluidnc/esp3d_gcode_handler_service.h#42-79:

Sources: main/target/cnc/fluidnc/esp3d_gcode_handler_service.h#42-86main/target/cnc/fluidnc/esp3d_gcode_handler_service.h#134-138


FluidNC and grblHAL status reports follow the pattern <State|Field1:Value1|Field2:Value2|...>. The parser extracts fields and dispatches them to the values system.

Title: “Firmware Status Parsing Architecture”

processStatus(data)

isStatus(data)

return false

Extract firmware state (between '<' and '|')

esp3dTftValues.set_value (firmware_status)

Parse MPos, WPos, WCO

WCO present?

Load stored WCO from esp3dTftValues

Store WCO values

Calculate missing positions WPos = MPos - WCO

Dispatch positions to position_wx/wy/wz/wa/wb/wc

Extract feed rate (FS or F)

Extract spindle speed (FS or S)

Extract overrides (Ov)

Extract pin states (Pn)

Extract buffer info (Bf)

Extract job progress (SD)

return true

For grblHAL, the handler implements an MPG state machine to manage control tokens main/target/cnc/grblhal/esp3d_gcode_handler_service.h#104-109:

  • DISCONNECTED: No transport connection.
  • UNKNOWN: Transport connected, MPG state not yet determined.
  • PASSIVE: Connected, read-only (token held by another sender).
  • ACTIVE: Connected, full control (token held by this pendant).

It uses specific realtime commands like GRBLHAL_RT_MPG_MODE_TOGGLE (0x8B) to acquire the token main/target/cnc/grblhal/esp3d_gcode_handler_service.h#86

Sources: main/target/cnc/fluidnc/esp3d_gcode_handler_service.h#149main/target/cnc/grblhal/esp3d_gcode_handler_service.h#104-109


The main dispatcher processCommand() routes incoming firmware responses based on prefixes main/target/cnc/fluidnc/esp3d_gcode_handler_service.h#149:

Title: “Response Dispatcher Flow”

processCommand(data)

Response type?

return true (acknowledged)

isStatus(data)

processStatus(data)

Firmware identification

set connection_status = 'C'

Parse error code

Lookup text via fluidnc_error_text(N)

Parse alarm code

Lookup text via fluidnc_alarm_text(N)

Parser state

Extract modal commands

set parser_state value

Error message

addMessageToHistory(ERR)

Info message

addMessageToHistory(INFO)

Sources: main/target/cnc/fluidnc/esp3d_gcode_handler_service.h#149


The message history system provides a ring buffer for firmware messages (MSG:ERR and MSG:INFO) at main/target/cnc/fluidnc/esp3d_gcode_handler_service.h#102-114:

Title: “Message History Structure”

«enumeration»

FirmwareMessageType

INFO = 0

ERR = 1

FirmwareMessage

+FirmwareMessageType type

+string content

ESP3DGCodeHandlerService

-deque _message_history

-MAX_FIRMWARE_MESSAGE_HISTORY = 30

+addMessageToHistory(type, content)

+getMessageHistory() : : deque

The FirmwareStatusScreen subscribes to message history updates to refresh the UI message list main/display/cnc/screens/firmware_status_screen.cpp#77 It uses refreshMessageList() to pull data from the esp3dGcodeHandler history buffer main/display/cnc/screens/firmware_status_screen.cpp#80

Sources: main/target/cnc/fluidnc/esp3d_gcode_handler_service.h#102-114main/display/cnc/screens/firmware_status_screen.cpp#77-80


The tool change screen demonstrates complex G-code integration with state tracking:

  1. IDLE: Waiting for user input.
  2. MOVING_TO_CHANGE: Command M6T<num> sent.
  3. WAITING_USER: Firmware in HOLD state.
  4. PROBING: User resumed; machine performing ETS probe.
  5. SUCCESS/FAILED: Operation terminal states.

Utility functions in the handler allow extracting specific modal values from the $G parser state response. For example, parseMCodes extracts spindle/coolant states main/target/cnc/fluidnc/esp3d_gcode_handler_service.h#88-95

Sources: main/target/cnc/fluidnc/esp3d_gcode_handler_service.h#88-95


When extending G-code support:

Sources: main/target/cnc/fluidnc/esp3d_gcode_handler_service.h#82-163