Rayforge Driver Development Guide¶
This guide provides a high-level overview of how to create a driver in Rayforge to add support for your laser cutter or engraver. By creating a driver, you integrate your machine's unique communication protocol and command language into the Rayforge ecosystem.
Driver Overview¶
A driver is the bridge between Rayforge's core logic and your physical hardware. It is responsible for three main tasks:
- Managing Connectivity: Handling the low-level communication protocol (Serial, WebSocket, HTTP, etc.).
- Executing Jobs: Sending pre-encoded machine code (e.g., G-code) to the device and tracking execution progress.
- Reporting State: Emitting signals to update the UI with the laser's
real-time position, status (
IDLE,RUN), and log messages.
To simplify this, Rayforge provides an architecture based on composable parts:
graph TD;
subgraph Rayforge Core
OpsProducer-->|Produces| Ops;
Pipeline-->|Runs| OpsProducer;
end
subgraph Your Driver Implementation
Ops -->|Encodes via| OpsEncoder;
OpsEncoder-->|Produces| MachineCode
MachineCode-->|Calls run| Driver;
Driver-->|Sends via| Transport;
end
Transport --> Device[Physical Laser];
OpsEncoder: TranslatesOpsinto a specific command language (e.g., G-code). Used by both the Pipeline (for job encoding) and the Driver (for individual commands like move_to, home, etc.).Pipeline: Orchestrates encoding and produces final machine code.Transport: Manages the connection and data transfer.Driver: Executes machine code, handles device state, and communicates with the UI.
All driver operations are asynchronous to ensure the user interface remains responsive.
The Ops Language¶
Rayforge describes a laser job as a sequence of high-level operations, stored in
an Ops object. This is the universal language within Rayforge for describing
machine movements, independent of any specific hardware.
Ops Method |
Signature | Description |
|---|---|---|
move_to |
(x, y, z=0.0) |
Rapid movement (no cutting) |
line_to |
(x, y, z=0.0) |
Cutting/Engraving movement |
arc_to |
(x, y, i, j, cw=True, z=0.0) |
Cutting/Engraving arc movement |
set_power |
(power) |
Set laser power (0-100%) |
set_cut_speed |
(speed) |
Set speed for cutting moves (mm/min) |
set_travel_speed |
(speed) |
Set speed for rapid moves (mm/min) |
enable_air_assist |
() |
Turn on air assist |
disable_air_assist |
() |
Turn off air assist |
Your driver receives pre-encoded machine code (e.g., a G-code string) and an
operation map that tracks which machine code commands correspond to which
operations. The pipeline handles encoding Ops to machine code before calling
the driver's run() method.
# Example of how Rayforge builds an Ops object
ops = Ops()
ops.set_travel_speed(3000)
ops.set_cut_speed(800)
ops.set_power(80)
ops.move_to(10, 10) # Rapid move to start point
ops.enable_air_assist()
ops.line_to(50, 10) # Cut a line with air assist
ops.disable_air_assist()
ops.line_to(50, 50) # Cut a line without air assist
Driver Implementation¶
All drivers MUST inherit from rayforge.machine.drivers.Driver.
from rayforge.machine.driver.driver import Driver
class YourDriver(Driver):
label = "Your Device" # Display name in the UI
subtitle = "Description for users"
supports_settings = False # Set True if the driver can read/write firmware settings
Required Properties¶
label: A human-readable name shown in the UI.subtitle: A brief description shown below the name.supports_settings: A boolean indicating if the driver can read/write device settings (like GRBL's$$).
Required Methods¶
Your driver class MUST implement the following methods. Note that most are asynchronous and must be defined with async def.
Configuration and Lifecycle¶
get_setup_vars() -> VarSet: (Class Method) Returns aVarSetobject defining the parameters needed for connection (e.g., IP address, serial port). Rayforge uses this to automatically generate the setup form in the UI.precheck(**kwargs): (Class Method) A non-blocking, static check of the configuration that can be run before driver instantiation. Should raiseDriverPrecheckErroron failure.setup(**kwargs): Called once with the values from the setup form. Use this to initialize your transports and internal state.async def connect(): Establishes and maintains a persistent connection to the device. This method should contain auto-reconnection logic.async def cleanup(): Called when disconnecting. Should close all connections and release resources.
Device Control¶
async def run(machine_code: Any, op_map: MachineCodeOpMap, doc: Doc, on_command_done: Optional[Callable[[int], Union[None, Awaitable[None]]]] = None): The core method for executing a job. Receives pre-encoded machine code (e.g., G-code string) and a mapping between operation indices and machine code. Theon_command_donecallback is called with the op_index when each command completes.async def home(axes: Optional[Axis] = None): Homes the machine. Can home specific axes or all axes.async def move_to(pos_x: float, pos_y: float): Manually moves the laser head to a specific XY coordinate.async def set_hold(hold: bool = True): Pauses or resumes current job.async def cancel(): Stops the current job.async def jog(axis: Axis, distance: float, speed: int): Jogs the machine along a specific axis.async def select_tool(tool_number: int): Selects a new tool/laser head by its number.async def clear_alarm(): Clears any active alarm state.
Firmware Settings (if supports_settings is True)¶
get_setting_vars() -> List[VarSet]: ReturnsVarSetobjects that define the structure of the device's settings page.async def read_settings(): Reads all settings from the device and calls_on_settings_read()with the result.async def write_setting(key: str, value: Any): Writes a single setting to the device.
Emitting Signals¶
To communicate with the UI, your driver must emit signals. To ensure proper
logging and thread safety, you must not emit signals directly. Instead,
call the protected helper methods from the base Driver class.
self._log(message): Sends a log message to the console.self._on_state_changed(): Call this whenever you updateself.stateto notify the UI of a status or position change.self._on_connection_status_changed(status, message): Informs the UI about the connection status (CONNECTING,CONNECTED,ERROR, etc.).self._on_command_status_changed(status, message): Reports the status of a sent command.self._on_settings_read(settings): Sends the device settings you've read back to the UI.
Have Questions?¶
The best way to learn is to look at the existing drivers in
rayforge/machine/driver/, such as:
grbl.py- GRBL-based machinesgrbl_serial.py- Serial-based GRBL communicationsmoothie.py- Smoothieboard-based machinesdummy.py- A test driver for development
If you get stuck, please don't hesitate to open an issue on GitHub! We're happy to help.