Skip to content

State Machines

GhostTypes edited this page Feb 22, 2026 · 2 revisions

State Machines

FlashForge printers use different internal state models depending on the firmware generation. This document maps these states to a unified client model for consistent application behavior across all printer families.

Overview

Family State Source Format
Modern (5M / AD5X) HTTP /detail endpoint JSON status field
Legacy (A3 / A4) TCP M27 / M119 commands Text response

Unified Client Model

Applications should normalize printer status into these high-level states for consistent behavior:

Unified State Description Client Action
IDLE Ready to accept jobs Enable job submission
PRINTING Actively printing Show progress, enable pause
PAUSED Job suspended, heaters active Enable resume/cancel
ERROR Machine halted due to fault Display error, require intervention
BUSY Internal processing Wait, poll for state change

Modern Firmware States (5M / AD5X)

Source: HTTP POST /detail

Field: status (string)

State Mapping

Internal Status /detail Value Unified State Notes
Ready ready IDLE Printer idle and ready
Building building, working, printing PRINTING Main printing state
Busy busy BUSY Non-printing operation
Pause pause, paused PAUSED Job suspended
Error error ERROR Fault condition
Completed completed IDLE Job finished successfully
Canceling canceling, cancel BUSY Transient state during stop
Heating heating PRINTING Pre-print heating phase
Pausing pausing PRINTING Transition to paused
Calibration calibrate_doing BUSY Calibration in progress

Important Quirks

"canceling" During Pause

The canceling state is sometimes used transiently during a PAUSE sequence, not just during cancellation.

Recommendation: Treat canceling as BUSY and continue polling.

State Query Implementation

Method: Poll /detail every 1-2 seconds

POST /detail HTTP/1.1
Content-Type: application/json

{
  "serialNumber": "SNADVA5MXXXXX",
  "checkCode": "12345"
}

Response:

{
  "code": 0,
  "message": "Success",
  "detail": {
    "status": "printing",
    "printProgress": 0.45,
    "printLayer": 50,
    "printFileName": "Benchy.gcode"
  }
}

State Transition Examples

ready -> heating -> printing -> completed -> ready
ready -> heating -> printing -> pausing -> pause -> printing
ready -> heating -> printing -> canceling -> ready
ready -> busy -> ready
printing -> error

Legacy Firmware States (A3 / A4 Pro)

Source: TCP commands ~M27 or ~M119

Format: Text response

M27 Response Format

CMD M27 Received.
SD printing byte 0/100
Layer: 0/0
ok

The last line before ok contains the state information.

M119 Response Format

CMD M119 Received.
Endstop: X-max: 110 Y-max: 110 Z-min: 0
MachineStatus: READY
MoveMode: READY
Status: S:1 L:0 J:0 F:0
LED: 1
CurrentFile:
ok

Key Field: MachineStatus

State Mapping

M27/M119 Response Unified State Notes
IDLE IDLE No job active
READY IDLE File selected, waiting for M24
PRINTING PRINTING Job in progress
BUILDING PRINTING Alternative printing state
BUILDING_FROM_SD PRINTING SD card printing
PAUSED PAUSED Job suspended
ERROR ERROR Fault condition
BUSY BUSY Internal operation

Legacy State Quirks

READY vs IDLE

READY is a distinct state meaning "M23 (File Select) successful". It effectively means IDLE but prepared to print.

Recommendation: Treat READY as IDLE.

Cloud Print States

Cloud print states (BUILDING_FROM_SD) may leak into local queries on some firmware versions.

Recommendation: Treat any "BUILDING*" string as PRINTING.

State Query Implementation

Method: Poll ~M27 every 1-2 seconds via TCP port 8899

Send: ~M27
Response:
CMD M27 Received.
SD printing byte 45234/123456
Layer: 23/100
ok

Parse the last line of the response for the state string.

Unified State Detection Algorithm

function normalizeState(status: string, isModern: boolean): UnifiedState {
  const lowerStatus = status.toLowerCase();

  // Modern firmware states
  if (isModern) {
    if (lowerStatus === 'ready' || lowerStatus === 'completed') return 'IDLE';
    if (lowerStatus === 'printing' || lowerStatus === 'building' ||
        lowerStatus === 'working' || lowerStatus === 'heating' ||
        lowerStatus === 'pausing') return 'PRINTING';
    if (lowerStatus === 'pause' || lowerStatus === 'paused') return 'PAUSED';
    if (lowerStatus === 'error') return 'ERROR';
    if (lowerStatus === 'busy' ||
        lowerStatus === 'canceling' || lowerStatus === 'cancel' ||
        lowerStatus === 'calibrate_doing') return 'BUSY';
  }

  // Legacy firmware states
  if (lowerStatus === 'idle' || lowerStatus === 'ready') return 'IDLE';
  if (lowerStatus === 'printing' || lowerStatus.startsWith('building')) return 'PRINTING';
  if (lowerStatus === 'paused') return 'PAUSED';
  if (lowerStatus === 'error') return 'ERROR';
  if (lowerStatus === 'busy') return 'BUSY';

  // Unknown state - treat as busy for safety
  return 'BUSY';
}

State Machine Diagrams

Normal Print Flow

         +-------+
         | IDLE  |
         +-------+
             |
             | Start Print (M23/HTTP printGcode)
             v
         +-------+
         | BUSY  |  (heating, file prep)
         +-------+
             |
             | Print begins
             v
      +------------+
      | PRINTING   |<--------+
      +------------+         |
             |               |
             | Pause         | Resume
             v               |
        +---------+          |
        | PAUSED  |----------+
        +---------+
             |
             | Cancel or Complete
             v
         +-------+
         | IDLE  |
         +-------+

Error Flow

    [Any State]
         |
         | Error occurs
         v
    +---------+
    | ERROR   |
    +---------+
         |
         | User intervention / Clear
         v
    +-------+
    | IDLE  |
    +-------+

Best Practices

Polling Strategy

State Poll Interval Rationale
IDLE 5-10 seconds Lower frequency when idle
PRINTING 1-2 seconds Frequent progress updates
PAUSED 2-3 seconds Medium frequency
BUSY 1-2 seconds Detect state change quickly
ERROR 10+ seconds Wait for user intervention

State Change Detection

Track previous state to detect transitions:

interface PrinterStateTracker {
  previousState: UnifiedState;
  currentState: UnifiedState;
  stateChangedAt: Date;
  onStateChange: (old: UnifiedState, new: UnifiedState) => void;
}

function updateState(tracker: PrinterStateTracker, newState: UnifiedState) {
  if (tracker.previousState !== newState) {
    tracker.onStateChange(tracker.previousState, newState);
    tracker.previousState = newState;
    tracker.stateChangedAt = new Date();
  }
  tracker.currentState = newState;
}

Handling Unknown States

If an unrecognized state is encountered:

  1. Log the raw state value for debugging
  2. Treat as BUSY (safe default)
  3. Continue polling
  4. Update state mapping as needed

Cross-Family Compatibility

When building applications that support multiple printer families:

  1. Always normalize to the Unified Client Model
  2. Handle both modern and legacy state sources
  3. Use discovery protocol to identify printer family
  4. Select appropriate state query method based on family

Clone this wiki locally