### Initialize grblHAL Core and Enter Main Loop Source: https://context7.com/grblhal/core/llms.txt This C code demonstrates the basic structure of a driver's main entry point. It initializes the hardware abstraction layer (HAL) with necessary callbacks and then calls `grbl_enter()` to start the grblHAL main loop. This function never returns under normal operation. ```c // driver_main.c (example STM32 driver skeleton) #include "grbl/hal.h" #include "grbl/grbllib.h" bool driver_setup (settings_t *settings) { // Enable peripherals: UART, timers, GPIO ... return settings->version == SETTINGS_VERSION; // must match } bool driver_init (void) { hal.version = HAL_VERSION; // must match core expectation hal.info = "STM32F4xx"; hal.driver_version = "20260506"; hal.f_step_timer = 84000000UL; // 84 MHz hal.f_mcu = 168; // MHz hal.rx_buffer_size = 1024; hal.driver_setup = driver_setup; hal.delay_ms = board_delay_ms; // Stepper handlers (required) hal.stepper.wake_up = stepperWakeUp; hal.stepper.go_idle = stepperGoIdle; hal.stepper.enable = stepperEnable; hal.stepper.cycles_per_tick = stepperCyclesPerTick; hal.stepper.pulse_start = stepperPulseStart; // Limit switch handlers (required) hal.limits.enable = limitsEnable; hal.limits.get_state = limitsGetState; // Control signal handlers hal.control.get_state = controlGetState; // Coolant hal.coolant.set_state = coolantSetState; hal.coolant.get_state = coolantGetState; // Register serial stream as default I/O stream_connect(&uart_stream); return true; // driver_init() is called by grbl_enter() on first boot } int main (void) { board_init(); // clock, power, basic GPIO return grbl_enter(); // hands control to grblHAL, never returns } ``` -------------------------------- ### Get/Set Parameter Value Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Macros for getting and setting parameter values. P3 can be used to simulate arrays as both and can be expressions. ```APIDOC ## G65P3I[S] ### Description Gets a parameter value. If S is provided, the value is set to . Otherwise, the returned value is stored in the `_value` parameter. ### Method G65 ### Endpoint P3I[S] ### Parameters #### Path Parameters - **n** (numeric) - Required - The parameter number. - **m** (numeric) - Optional - The value to set the parameter to. ``` ```APIDOC ## G65P3IQ ### Description Sets a parameter value. ### Method G65 ### Endpoint P3IQ ### Parameters #### Path Parameters - **n** (numeric) - Required - The parameter number. - **value** (numeric) - Required - The value to set the parameter to. ``` -------------------------------- ### Get Parameter Value with G65P3I Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Retrieve a parameter value using G65P3I[S]. is the parameter number. If S is provided, the value is set to ; otherwise, the returned value is stored in the _value parameter. ```gcode G65P3I[S] ``` -------------------------------- ### Get Current Machine State Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Retrieves the current state of the machine. Available from build 20250107. ```APIDOC ## G65P4 ### Description Gets the current machine state. ### Method G65 ### Endpoint P4 ### Response #### Success Response (200) - **_value** (numeric) - The current machine state (0: Idle, 2: Check mode, 4: Cycle, 10: Tool change). ``` -------------------------------- ### Get Current Machine State with G65P4 Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Retrieve the current machine state using G65P4. This is available from build 20250107. States include Idle (0), Check mode (2), Cycle (4), and Tool change (10). ```gcode G65P4 ``` -------------------------------- ### Initialize and Use I/O Ports for Custom Outputs/Inputs Source: https://context7.com/grblhal/core/llms.txt This C code shows how to claim and label digital outputs and analog inputs in a plugin's initialization. It also demonstrates a vacuum cycle using digital output and reading an analog input. ```c #include "grbl/hal.h" #include "grbl/ioports.h" // In plugin init: claim and label two digital outputs and one analog input void ioports_example_init (void) { if (hal.port.num_digital_out >= 2) { hal.port.set_pin_description(Port_Digital, Port_Output, 0, "Vacuum pump"); hal.port.set_pin_description(Port_Digital, Port_Output, 1, "Mist nozzle"); } if (hal.port.num_analog_in >= 1) { hal.port.set_pin_description(Port_Analog, Port_Input, 0, "Pressure sensor"); } } // Turn vacuum pump on, read pressure sensor (blocking), turn pump off void vacuum_cycle (void) { // Set digital output 0 HIGH hal.port.digital_out(0, true); hal.delay_ms(200, NULL); // let pressure build // Read analog input 0 immediately (WaitMode_Immediate = no timeout wait) int32_t raw = hal.port.wait_on_input(Port_Analog, 0, WaitMode_Immediate, 0.0f); // raw is in driver-defined units (typically 0-4095 for 12-bit ADC) if (raw < 500) { // Low pressure — trigger alarm system_set_exec_alarm(Alarm_Probe); } hal.port.digital_out(0, false); // off } ``` -------------------------------- ### Implement a Custom Plugin with my_plugin_init() Source: https://context7.com/grblhal/core/llms.txt Extend grblHAL by creating a plugin that hooks into state changes and registers custom M-codes. Ensure the plugin is activated by defining `ADD_MY_PLUGIN`. ```c // my_plugin.c — custom plugin that adds an M-code and subscribes to state changes #define ADD_MY_PLUGIN // suppress the weak no-op stub in my_plugin.c #include "grbl/hal.h" #include "grbl/protocol.h" #include "grbl/state_machine.h" static on_state_change_ptr on_state_change_prev; // chain previous handler // Called on every state transition static void on_state_change (sys_state_t state) { if (state == STATE_IDLE) { // e.g. turn off a custom output when machine becomes idle hal.port.digital_out(0, false); } if (on_state_change_prev) on_state_change_prev(state); // always chain } // Custom M-code: M100 Pxxx sets digital output 0 static status_code_t on_user_mcode_execute (parser_state_t *parser_state, parser_block_t *gc_block) { if (gc_block->user_mcode == 100) { hal.port.digital_out(0, gc_block->values.p != 0.0f); return Status_OK; } return Status_Unhandled; } static user_mcode_ptrs_t mcode_ptrs = { .execute = on_user_mcode_execute }; void my_plugin_init (void) { // Subscribe to state-change events (chain-of-responsibility pattern) on_state_change_prev = grbl.on_state_change; grbl.on_state_change = on_state_change; // Register custom M-code handler grbl.user_mcode = &mcode_ptrs; // Claim an auxiliary digital output port hal.port.set_pin_description(Port_Digital, Port_Output, 0, "Custom output"); } ``` -------------------------------- ### Set Parameter Value with G65P3I Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Set a parameter value using G65P3IQ. is the parameter number and is the new value. ```gcode G65P3IQ ``` -------------------------------- ### Enable MPG Interface (Basic) Source: https://github.com/grblhal/core/wiki/MPG-and-DRO-interfaces Enable the MPG interface by uncommenting this define in _my_machine.h_. This is for basic MPG functionality. ```c #define MPG_ENABLE 1 ``` -------------------------------- ### Select Probe Input with G65P5Q Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Select a probe input using G65P5Q. is the probe ID (0=Primary, 1=Toolsetter, 2=Secondary). Available from build 20250514. Selecting an unavailable probe raises an error. ```gcode G65P5Q ``` -------------------------------- ### Enable Keypad Plugin and MPG Interface Source: https://github.com/grblhal/core/wiki/MPG-and-DRO-interfaces Enable both the keypad plugin and the MPG interface for real-time command character switching. Ensure the keypad plugin is uncommented and set to 2. ```c //#define KEYPAD_ENABLE 1 #define KEYPAD_ENABLE 2 ``` -------------------------------- ### Configure Machine Parameters in config.h Source: https://context7.com/grblhal/core/llms.txt Define the number of axes, spindles, and enable core features like CoreXY kinematics and NGC expressions by setting macros in `config.h` or via the build system. ```c // In your driver's board_map.h or CMakeLists.txt target_compile_definitions: // 5-axis machine #define N_AXIS 5 // Two independently controllable spindles #define N_SPINDLE 2 #define N_SYS_SPINDLE 2 // Full grblHAL protocol extensions (default, recommended) #define COMPATIBILITY_LEVEL 0 // CoreXY kinematics #define COREXY On // Enable NGC expression/variable support (O-codes, named params, flow control) #define NGC_EXPRESSIONS_ENABLE 1 #define NGC_PARAMETERS_ENABLE 1 // Increase serial RX buffer to handle fast senders #define RX_BUFFER_SIZE 2048 // Default spindle speeds #define DEFAULT_SPINDLE_RPM_MAX 24000.0f #define DEFAULT_SPINDLE_RPM_MIN 0.0f // Homing direction: home X and Y together first, then Z #define HOMING_CYCLE_0 (X_AXIS_BIT | Y_AXIS_BIT) #define HOMING_CYCLE_1 Z_AXIS_BIT ``` -------------------------------- ### Clone grblHAL Driver using Git Source: https://github.com/grblhal/core/wiki/Compiling-grblHAL Use this command to clone the grblHAL driver repository and its submodules initially. Ensure you are in the desired download directory. ```bash git clone --recurse-submodules https://github.com/grblHAL/iMXRT1062.git ``` -------------------------------- ### Runtime Settings ($ Commands) Source: https://context7.com/grblhal/core/llms.txt Runtime settings are accessed via numeric IDs using the '$n' interface. These settings are stored in NVS and persist across power cycles. Plugins can extend this by registering additional settings. ```APIDOC ## Runtime Settings ($ Commands) ### Description Access and modify runtime settings using numeric IDs via the '$n' interface. Settings are persistent and can be extended by plugins. ### Common Setting IDs - **$0**: Step pulse width, µs - **$1**: Stepper idle hold delay, ms (255 = always energised) - **$2**: Step invert mask (bit 0=X, 1=Y, 2=Z ...) - **$3**: Direction invert mask - **$10**: Status report mask (position, buffer, feed, pin states, WCO, overrides…) - **$11**: Junction deviation, mm - **$12**: Arc tolerance, mm - **$13**: Report in inches (0=mm) - **$20**: Soft limits enable - **$21**: Hard limits enable - **$22**: Homing enable - **$24**: Homing feed rate, mm/min - **$25**: Homing seek rate, mm/min - **$27**: Homing pull-off, mm - **$30**: Max spindle RPM - **$31**: Min spindle RPM - **$100**: X steps/mm - **$101**: Y steps/mm - **$102**: Z steps/mm - **$110**: X max rate, mm/min - **$130**: X max travel, mm (needed for soft limits) ### Programmatic Access Example ```c #include "grbl/settings.h" extern settings_t settings; void print_max_x_rate (void) { hal.stream.write("X max rate: "); char buf[16]; ftoa(settings.axis[X_AXIS].max_rate, buf, 0); hal.stream.write(buf); hal.stream.write(" mm/min\r\n"); } ``` ``` -------------------------------- ### Allocate Persistent Storage with nvs_alloc() Source: https://context7.com/grblhal/core/llms.txt Reserve a segment of Non-Volatile Storage (NVS) for plugin settings using `nvs_alloc()`. Load and save settings using `hal.nvs` functions, handling potential NVS corruption or first-time boot scenarios. ```c #include "grbl/nvs_buffer.h" #include "grbl/hal.h" typedef struct { float threshold_mm; uint8_t mode; uint8_t reserved[3]; } my_plugin_settings_t; static my_plugin_settings_t plugin_cfg; static nvs_address_t nvs_addr; static void settings_load (void) { if (hal.nvs.memcpy_from_nvs((uint8_t *)&plugin_cfg, nvs_addr, sizeof(my_plugin_settings_t), true) != NVS_TransferResult_OK) { // NVS corrupt or first boot — apply defaults plugin_cfg.threshold_mm = 2.5f; plugin_cfg.mode = 1; } } static void settings_save (void) { hal.nvs.memcpy_to_nvs(nvs_addr, (uint8_t *)&plugin_cfg, sizeof(my_plugin_settings_t), true); } void my_settings_plugin_init (void) { // Allocate storage (must be called before grbl_enter sets up NVS) if ((nvs_addr = nvs_alloc(sizeof(my_plugin_settings_t))) != 0) { settings_load(); } } ``` -------------------------------- ### Programmatically Read Max X Rate Setting Source: https://context7.com/grblhal/core/llms.txt This C code demonstrates how to programmatically read the current feed rate setting from within a plugin using the global `settings_t` struct. ```c #include "grbl/settings.h" void print_max_x_rate (void) { // settings is a global settings_t struct loaded from NVS by the core extern settings_t settings; hal.stream.write("X max rate: "); // axis_settings[axis_idx][setting_offset] — see settings.h AXIS_SETTINGS_INCREMENT // For convenience use the accessor macros where available char buf[16]; ftoa(settings.axis[X_AXIS].max_rate, buf, 0); hal.stream.write(buf); hal.stream.write(" mm/min\r\n"); } ``` -------------------------------- ### Enable CoreXY Kinematics Source: https://context7.com/grblhal/core/llms.txt Define COREXY in config.h or board_map.h to enable CoreXY kinematics. This automatically defines KINEMATICS_API. The CoreXY motor equations are provided for reference. ```c #define COREXY On // automatically defines KINEMATICS_API ``` -------------------------------- ### Configure grblHAL Driver Capabilities Source: https://context7.com/grblhal/core/llms.txt This C code illustrates how a driver announces its hardware capabilities to the grblHAL core and plugins. It populates the `hal.driver_cap` struct with flags indicating features like software debouncing, spindle encoders, networking, SD card support, and probe availability. Optional handlers for free memory reporting, RTC, and timers can also be provided. ```c // Minimal illustration of driver capability flags and optional handlers #include "grbl/hal.h" void driver_configure_capabilities (void) { // Announce hardware capabilities so the core and plugins can adapt hal.driver_cap.software_debounce = On; // input debounce available hal.driver_cap.spindle_encoder = On; // spindle RPM encoder present hal.driver_cap.ethernet = On; // networking available hal.driver_cap.sd_card = On; // SD card for file streaming hal.driver_cap.probe = On; // primary probe input available hal.driver_cap.atc = Off; // no automatic tool changer // Optional: supply free-heap reporter for $I+ report hal.get_free_mem = board_get_free_heap; // Optional: RTC support hal.rtc.get_datetime = rtc_get; hal.rtc.set_datetime = rtc_set; // Optional: HAL timer API (used by plugins) hal.timer.claim = timer_claim; hal.timer.configure = timer_configure; hal.timer.start = timer_start; hal.timer.stop = timer_stop; } ``` -------------------------------- ### Read Runtime Settings via $ Commands Source: https://context7.com/grblhal/core/llms.txt Common setting IDs for runtime configuration are listed. These can be sent over serial to the controller to read or modify settings. ```text Common setting IDs (send over serial to controller): $0=10 Step pulse width, µs $1=25 Stepper idle hold delay, ms (255 = always energised) $2=0 Step invert mask (bit 0=X, 1=Y, 2=Z ...) $3=0 Direction invert mask $10=511 Status report mask (position, buffer, feed, pin states, WCO, overrides…) $11=0.010 Junction deviation, mm $12=0.002 Arc tolerance, mm $13=0 Report in inches (0=mm) $20=0 Soft limits enable $21=1 Hard limits enable $22=1 Homing enable $24=25.0 Homing feed rate, mm/min $25=500.0 Homing seek rate, mm/min $27=1.0 Homing pull-off, mm $30=24000 Max spindle RPM $31=0 Min spindle RPM $100=800 X steps/mm $101=800 Y steps/mm $102=800 Z steps/mm $110=5000 X max rate, mm/min $130=200 X max travel, mm (needed for soft limits) ``` -------------------------------- ### Implement and Connect a Custom Stream Source: https://context7.com/grblhal/core/llms.txt This C code provides a minimal implementation of a custom stream (e.g., USB CDC) and registers it with the grblHAL stream manager. It also shows how to send custom feedback messages. ```c #include "grbl/stream.h" #include "grbl/hal.h" // Minimal implementation of a custom stream (e.g. a USB CDC) static int16_t usb_read (void) { return usb_cdc_getc(); } // -1 if empty static bool usb_write (const char *s) { return usb_cdc_puts(s); } static uint16_t usb_get_rx_count (void) { return usb_cdc_rx_count(); } static void usb_reset_read (void) { usb_cdc_flush_rx(); } static io_stream_t usb_stream = { .type = StreamType_Serial, .read = usb_read, .write = usb_write, .get_rx_buffer_count = usb_get_rx_count, .reset_read_buffer = usb_reset_read, .set_baud_rate = NULL, // not applicable for USB }; void usb_stream_init (void) { usb_cdc_init(); stream_connect(&usb_stream); // register with grblHAL stream manager } // Sending a custom message to the active output stream (from a plugin) void send_custom_feedback (void) { hal.stream.write("[MSG:Custom plugin message]\r\n"); } ``` -------------------------------- ### Custom Kinematics Module Source: https://context7.com/grblhal/core/llms.txt Define KINEMATICS_API and populate hal.kinematics.* in your driver_init() to implement a custom kinematics module. This allows for unique motion control. ```c // Or write a custom kinematics module and set: // #define KINEMATICS_API // ... then populate hal.kinematics.* in your driver_init() ``` -------------------------------- ### Programmatically Inject Real-Time Command Source: https://context7.com/grblhal/core/llms.txt Enqueue a real-time command from within firmware, such as mapping encoder clicks to feed overrides. The command is processed asynchronously by the protocol layer. ```c // Programmatically inject a real-time command from within the firmware // (e.g. from an encoder plugin that maps encoder clicks to feed overrides) #include "grbl/protocol.h" #include "grbl/grbl.h" void encoder_on_click (int direction) { char cmd = (direction > 0) ? CMD_OVERRIDE_FEED_COARSE_PLUS // 0x91 : CMD_OVERRIDE_FEED_COARSE_MINUS; // 0x92 // Enqueue as a real-time command — processed asynchronously by the protocol layer grbl.enqueue_realtime_command(cmd); } ``` -------------------------------- ### Parameter Substitution in Comments Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Demonstrates how numbered and named parameters are substituted within comment strings for (print, ...) and (debug, ...) commands. Ensure $534 is set to 1 to enable debug output. ```gcode (print, metric mode: #<_metric>, coord system: #5220) ``` ```gcode (debug, metric mode: #<_metric>, coord system: #5220) ``` -------------------------------- ### Enable Delta Kinematics Source: https://context7.com/grblhal/core/llms.txt To use delta kinematics, define DELTA_ROBOT in config.h or board_map.h. Other kinematics like polar can be enabled similarly. ```c // To use delta kinematics instead: // #define DELTA_ROBOT On ``` -------------------------------- ### Execute Linear Motion with `mc_line()` Source: https://context7.com/grblhal/core/llms.txt Use `mc_line()` to issue G0/G1 moves. It checks soft limits and passes the segment to the planner. Ensure `plan_line_data_t` is initialized and feed rate is set. ```c #include "grbl/motion_control.h" #include "grbl/planner.h" // Issue a rapid move to X=50 Y=25 Z=-10 from a plugin or custom kinematics void example_jog_to_position (void) { plan_line_data_t pl_data; float target[N_AXIS]; plan_data_init(&pl_data); // zero-initialise pl_data.feed_rate = 3000.0f; // mm/min pl_data.condition.rapid_motion = On; // G0-style move target[X_AXIS] = 50.0f; target[Y_AXIS] = 25.0f; target[Z_AXIS] = -10.0f; if (!mc_line(target, &pl_data)) { // soft-limit violation — target out of work envelope report_status_message(Status_TravelExceeded, NULL); } } ``` -------------------------------- ### Enable MPG Interface (Real-time Command) Source: https://github.com/grblhal/core/wiki/MPG-and-DRO-interfaces Enable the MPG interface for mode switching using the 0x8B real-time command character. This option is used when the keypad plugin is not active. ```c #define MPG_ENABLE 2 ``` -------------------------------- ### Update grblHAL Driver using Git Source: https://github.com/grblhal/core/wiki/Compiling-grblHAL After the initial clone, use this command to pull the latest changes and update all submodules for your grblHAL driver. ```bash git pull --recurse-submodules ``` -------------------------------- ### Read Numeric Setting Value with G65P1Q Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Use G65P1Q to read a numeric setting value. The PRM[] function can also be used for this purpose. ```gcode G65P1Q ``` -------------------------------- ### Read Tool Offset with G65P2Q Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Read a tool offset from the tool table using G65P2QR. is the tool number and is the axis number (0=X, 1=Y, etc.). ```gcode G65P2QR ``` -------------------------------- ### Set Numeric Setting Value with G65P1Q Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Use G65P1QS to set a numeric setting value. This feature is available from build 20251028. ```gcode G65P1QS ``` -------------------------------- ### I/O Ports API (`hal.port`) Source: https://context7.com/grblhal/core/llms.txt The auxiliary I/O subsystem provides a portable interface to GPIO and ADC/DAC ports. Ports are numbered 0-based per direction per type, allowing plugins to control custom outputs and read sensors. ```APIDOC ## I/O Ports API (`hal.port`) ### Description Manage auxiliary digital and analog I/O ports for custom control and sensor reading. Ports are accessible via numerical indices. ### Initialization Example ```c #include "grbl/hal.h" #include "grbl/ioports.h" void ioports_example_init (void) { if (hal.port.num_digital_out >= 2) { hal.port.set_pin_description(Port_Digital, Port_Output, 0, "Vacuum pump"); hal.port.set_pin_description(Port_Digital, Port_Output, 1, "Mist nozzle"); } if (hal.port.num_analog_in >= 1) { hal.port.set_pin_description(Port_Analog, Port_Input, 0, "Pressure sensor"); } } ``` ### Usage Example ```c #include "grbl/hal.h" void vacuum_cycle (void) { // Set digital output 0 HIGH hal.port.digital_out(0, true); hal.delay_ms(200, NULL); // let pressure build // Read analog input 0 immediately int32_t raw = hal.port.wait_on_input(Port_Analog, 0, WaitMode_Immediate, 0.0f); if (raw < 500) { // Low pressure — trigger alarm system_set_exec_alarm(Alarm_Probe); } hal.port.digital_out(0, false); // off } ``` ``` -------------------------------- ### State Machine - Check Controller State Source: https://context7.com/grblhal/core/llms.txt This C code snippet demonstrates how to check the current state of the grblHAL controller using state_get(). It includes checks for ALARM, IDLE, and CYCLE states, writing messages to the serial stream. ```c #include "grbl/system.h" #include "grbl/state_machine.h" // State values (mutually exclusive bit flags, test with bitwise AND) // STATE_IDLE = 0 Normal idle // STATE_ALARM = bit(0) Locked — only settings commands accepted // STATE_CHECK_MODE= bit(1) G-code dry run, no motion // STATE_HOMING = bit(2) Homing cycle active // STATE_CYCLE = bit(3) Motion executing // STATE_HOLD = bit(4) Feed hold active // STATE_JOG = bit(5) Jogging // STATE_SAFETY_DOOR = bit(6) Safety door open // STATE_SLEEP = bit(8) Sleep mode void plugin_check_state (void) { sys_state_t state = state_get(); if (state & STATE_ALARM) { hal.stream.write("[MSG:Controller is in ALARM state]\r\n"); // To unlock: send $X (kill alarm lock) over the serial stream } else if (state == STATE_IDLE) { hal.stream.write("[MSG:Ready]\r\n"); } else if (state & STATE_CYCLE) { hal.stream.write("[MSG:Running]\r\n"); } } ``` -------------------------------- ### Inbuilt Macros Return Value Handling Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Inbuilt macros set the _value_returned parameter to 1 if a value is returned, which is then stored in the _value parameter. ```gcode Inbuilt macros will set the `_value_returned` parameter to `1` if a value is returned, the value is then stored in the `_value` parameter. ``` -------------------------------- ### Stream Interface (`hal.stream` / `stream_connect()`) Source: https://context7.com/grblhal/core/llms.txt The stream interface abstracts all input/output operations (UART, USB, Telnet, WebSocket, SD card) through a common `io_stream_t` struct. This allows grblHAL to manage and switch between different communication channels. ```APIDOC ## Stream Interface (`hal.stream` / `stream_connect()`) ### Description Provides a unified interface for all input and output streams, including serial, USB, and network connections. Drivers and plugins can register custom streams. ### Custom Stream Implementation Example ```c #include "grbl/stream.h" #include "grbl/hal.h" // Assume usb_cdc_getc, usb_cdc_puts, usb_cdc_rx_count, usb_cdc_flush_rx, usb_cdc_init are defined elsewhere static int16_t usb_read (void) { return usb_cdc_getc(); } // -1 if empty static bool usb_write (const char *s) { return usb_cdc_puts(s); } static uint16_t usb_get_rx_count (void) { return usb_cdc_rx_count(); } static void usb_reset_read (void) { usb_cdc_flush_rx(); } static io_stream_t usb_stream = { .type = StreamType_Serial, .read = usb_read, .write = usb_write, .get_rx_buffer_count = usb_get_rx_count, .reset_read_buffer = usb_reset_read, .set_baud_rate = NULL, // not applicable for USB }; void usb_stream_init (void) { usb_cdc_init(); stream_connect(&usb_stream); // register with grblHAL stream manager } ``` ### Sending Custom Messages ```c #include "grbl/hal.h" // Sending a custom message to the active output stream (from a plugin) void send_custom_feedback (void) { hal.stream.write("[MSG:Custom plugin message]\r\n"); } ``` ``` -------------------------------- ### Spindle Sync Test Program Source: https://github.com/grblhal/core/wiki/Spindle-sync This G-code program is used to generate data for testing and visualizing PID tuning for spindle synced motion. It commands spindle speed, moves axes, and executes a G33 synchronized move. ```gcode m3s200 g0x2 g0z2 g1x0f50 g33z-15k1.5 g0x2 g0z2 m30 ``` -------------------------------- ### Select Probe Input Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Selects a probe input for use. Available from build 20250514. Selecting an unavailable probe will raise an error. ```APIDOC ## G65P5Q ### Description Selects the probe input. ### Method G65 ### Endpoint P5Q ### Parameters #### Path Parameters - **probe** (numeric) - Required - The probe ID (0: Primary, 1: Toolsetter, 2: Secondary). ``` -------------------------------- ### Perform Homing Cycle with `mc_homing_cycle()` Source: https://context7.com/grblhal/core/llms.txt Initiates a homing cycle for specified axes. The function returns a status code indicating success or failure, such as a limit switch not being found. ```c // Perform a homing cycle on X and Y simultaneously void example_home_xy (void) { axes_signals_t cycle = { .x = On, .y = On }; status_code_t rc = mc_homing_cycle(cycle); if (rc != Status_OK) { // Homing failed (e.g. limit switch not found) } } ``` -------------------------------- ### Read/Set Numeric Setting Value Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Macros for reading and setting numeric setting values. The PRM[] function can also be used for reading. ```APIDOC ## G65P1Q ### Description Reads a numeric setting value. ### Method G65 ### Endpoint P1Q ### Parameters #### Path Parameters - **n** (numeric) - Required - The setting number. ## G65P1QS ### Description Sets a numeric setting value. Available from build 20251028. ### Method G65 ### Endpoint P1QS ### Parameters #### Path Parameters - **n** (numeric) - Required - The setting number. - **value** (numeric) - Required - The new value for the setting. ``` -------------------------------- ### Disable Spindle Delays with G65P6 Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Disable spindle on/off delays for the next M3, M4, or M5 command using G65P6. Available from build 20250922. ```gcode G65P6 ``` -------------------------------- ### Send Modbus Message with G65P7 Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Send a Modbus message using G65P7. Various parameters (S, F, R, X, A, B, C) are used depending on the Modbus function code. On exceptions, _value_returned is set to 0 and _value to the exception code. On success, _value_returned is the number of values received, stored in _value, _value2, and _value3. ```gcode G65P7 S- F- R- ``` ```gcode G65P7 S- F- R- A- ``` ```gcode G65P7 S- F- ``` ```gcode G65P7 S- F- R- A- ``` -------------------------------- ### Disable Spindle Delays Source: https://github.com/grblhal/core/wiki/Expressions-and-flow-control Disables spindle on/off delays for the next M3, M4, or M5 command. Available from build 20250922. ```APIDOC ## G65P6 ### Description Disables spindle on/off delays for the next M3, M4, or M5 command. ### Method G65 ### Endpoint P6 ```