# grblHAL: a CNC motion controller in optimized C with a plugin seam

> grblHAL is a rewrite of grbl 1.1f for ARM microcontrollers, built to hold a 300 kHz jitter free step pulse stream, and it splits the problem into a shared core, more than fifteen hardware drivers and a plugin system. Its readme leads with three warnings about settings resets, alarm mode and sender compatibility.

**grblHAL/core** — grblHAL core code and master Wiki

- Repository: https://github.com/grblHAL/core
- Stars: 519 · Forks: 156
- Language: C
- License: NOASSERTION
- Published: 2026-10-22 · Updated: 2026-10-22 · Language: en
- Canonical page: https://hysenlabs.com/projects/grblhal-core

## A CNC motion controller written to hit a timing target

grblHAL is described as a no-compromise, high performance, low cost alternative to parallel-port-based motion control for CNC milling, and it is based on the Arduino version of grbl. It is mainly aimed at ARM processors, or other 32-bit microcontrollers, with ample amounts of RAM and flash compared to the AVR 328p, and it requires a hardware driver to be functional.

The name expands in the readme: HAL means Hardware Abstraction Layer. That single word explains the shape of the project. The core is written in highly optimized C using features of the supported processors to achieve precise timing and asynchronous operation, and it is able to maintain up to 300kHz of stable, jitter free control pulses, with a footnote that the figure is driver and processor dependent.

That timing figure is the reason the project exists. A CNC controller's job is to emit step pulses at exactly the right instants, and anything that makes the operating system or the toolchain jitter is a defect in the machine tool. Writing the controller in C on a microcontroller, rather than on a host with a real-time operating system, is the direct route to that number.

The compatibility story is specific. This is a port and rewrite of grbl 1.1f and should be compatible with GCode senders compliant with the specifications for that version. It should also be possible to change default compile-time configurations if problems arise, with the example given being that the default serial buffer sizes have been increased in some of the drivers provided.

The controller accepts standards-compliant g-code and has been tested with the output of several CAM tools with no problems. Arcs, circles and helical motion are fully supported, as well as all other primary g-code commands. Macro functions, variables and some canned cycles are not supported, and the readme gives the reason in a sentence that reflects a design position rather than an omission: GUIs can do a much better job at translating them into straight g-code.

There is also full acceleration management with look ahead, so the controller looks up motions into the future and plans velocities ahead to deliver smooth acceleration and jerk-free cornering.

The licence is a custom one that GitHub could not classify, with the COPYING file at the repository root, the last push was on 2026-10-15, and the latest build date stated in the readme is 20261015.

## One core, more than fifteen driver families

The driver arrangement is the part of the project that decides how you actually use it. The core requires a hardware driver to be functional, and currently drivers are available for more than 15 different processors and processor families, all of which share the same core codebase.

So the deliverable you flash onto a board is a core plus a driver, and the driver is what determines which pins, timers and peripherals the controller uses. The core is where the motion planning, the g-code parsing and the state machine live, and that code is shared across every target.

That arrangement is why a project like this can exist at all. The hard part of a CNC controller is the same regardless of the chip, and the chip-specific part is the part that has to be redone for each family. Separating them means an improvement to the look-ahead planner reaches every board at once.

The open architecture is the other half of the story. The readme says grblHAL has an open architecture allowing plugins to extend functionality, and that user made plugins can be added without changing a single file in the source. New M-codes can be added, space for plugin specific settings can be allocated, and events can be subscribed to. The examples given are driving an automatic tool changer, adding extra outputs, and even adding a user interface, with the caveat that the plugin feature is only to be used for private plugins, since a shared one needs a single call added to the driver code of the target processors.

That caveat is the practical boundary of the plugin system. A private plugin lives in your build. A shared plugin has to be wired into each driver's source, which means the driver maintainers are involved, and the readme is honest that adding your own driver is possible if you feel so inclined.

The plugin model is the same idea as the driver model at a different level. Both are ways of extending the controller without rewriting it, and both carry a note about what has to change in the core or the driver when the extension is meant to be shared.

## The g-code surface, and the asterisks that qualify it

The supported g-code list is long and it is annotated, and the annotations are the interesting part because they tell you which commands depend on the hardware you happen to have.

```text
  - Non-Modal Commands: G4, G10L2, G10L20, G28, G30, G28.1, G30.1, G53, G65*****, G66*****, G67*****, G92, G92.1
  - Additional Non-Modal Commands: G10L1*, G10L10*, G10L11*
  - Motion Modes: G0, G1, G2****, G3****, G5, G5.1, G38.2, G38.3, G38.4, G38.5, G80, G33*
  - Canned cycles: G73, G81, G82, G83, G85, G86, G89, G98, G99
  - Repetitive cycles: G76*
  - Feed Rate Modes: G93, G94, G95*, G96*, G97*
  - Unit Modes: G20, G21
  - Scaling: G50, G51
  - Lathe modes: G7*, G8*
```

The asterisks after a command mean it is driver or configuration dependent. So G33, the thread cutting mode, is marked as such, as are G95, the feed per minute mode, G96 and G97, the constant and inverse surface speed modes, the lathe modes G7 and G8, the tool length offset modes G43, G43.1, G43.2 and G49, and M6, the tool change, which is marked as having two possible modes, manual and automatic tool changer.

The mode groups follow the standard layout, and they are worth reading as a compatibility check rather than as documentation of the controller:

```text
  - Distance Modes: G90, G91
  - Arc IJK Distance Modes: G91.1
  - Plane Select Modes: G17, G18, G19
  - Tool Length Offset Modes: G43*, G43.1, G43.2*, G49
  - Cutter Compensation Modes: G40
  - Coordinate System Modes: G54, G55, G56, G57, G58, G59, G59.1, G59.2, G59.3
  - Control Modes: G61
```

And then the control words, which are the ones that change what the machine physically does. Program flow is M0, M1, M2, M30 and M60. Coolant control is M7, M8 and M9. Spindle control is M3, M4 and M5. Tool change is M6. Switches are M48 to M51 and M53. Input and output control is M62 to M68, with the number of inputs and outputs supported dependent on the driver implementation. Modal state handling is M70 to M73. And the return from macro is M99.

The non-command words list closes it: A, B, C, D, E, F, H, I, J, K, L, N, O, P, Q, R, S, T, U, V, W, X, Y and Z, with the note that the W axis is only available when the ABC axes are remapped to UVW or when lathe UVW mode is enabled.

The asterisk convention is consistent throughout, and it is a better way to document hardware variation than a separate compatibility table would be. A reader who needs G43.2 or the automatic tool changer can see immediately that both are configuration dependent rather than hunting through the driver documentation.

G-codes not supported by legacy grbl are documented on the project wiki, and some plugins implement additional M-codes.

## The build tree is one file per subsystem

The top level of the repository is a flat list of C files and headers, one pair per subsystem, and reading the names tells you how the controller is decomposed.

The motion side is motion_control.c and motion_control.h, with kinematics/ as a directory for the coordinate systems. The g-code layer is gcode.c and gcode.h, and the next-generation controller is a separate set of files with the ngc prefix: ngc_expr for expressions, ngc_flowctrl for flow control, ngc_params for parameters. The parallel pair of names for the two controllers is itself a decision, since it implies a legacy path kept alongside a current one.

Then there is the hardware-facing layer. Encoders are encoders.c and encoders.h, motor_pins.h defines the motor pins, ioports.c and ioports.h the general purpose pins, coolant_control.c and coolant_control.h the coolant, and canbus.c and canbus.h the fieldbus. There is modbus.c and modbus.h plus modbus_rtu.c for the industrial protocol.

The storage layer is the most unusual group and shows the range of targets the project aims at. fs_device.c is the generic file system interface, fs_embedded.c handles on-chip flash, and fs_ram.c handles RAM. A controller that keeps its settings in flash, in RAM and on an SD card needs all three, and the pluggable interface is how one core serves all of them.

The rest follows the same pattern. There are errors.c and errors.h, alarms.c and alarms.h, messages.c and messages.h, crc.c for checksums, nuts_bolts.c for the small utilities that every embedded project accumulates, and crossbar.c and crossbar.h.

The headers are where the extension points live. driver_opts.h and driver_opts2.h are the driver's configuration, config.h holds the compile-time settings, hal.h is the hardware abstraction interface itself, grbl.h and grbllib.c and grbllib.h are the core, and core_handlers.h, motor_pins.h, my_plugin.c and expanders_init.h are the seams where a plugin or a driver hooks in.

The build is CMake, with a CMakeLists.txt at the root.

## The warnings at the top, and the settings reset

The readme opens with three notes that are worth reading before anything else, because each one describes a way the controller will surprise you.

The first is about updates. A settings reset will be performed on an update of builds prior to 20241208, and backup and restore of settings is recommended. So a firmware upgrade across that boundary wipes the stored configuration, which for a CNC machine means losing the steps per millimetre, the backlash compensation and everything else that was carefully dialled in.

The second is about the input switches. grblHAL defaults to normally closed switches for inputs, so if no switches are connected when testing it, the controller will likely start in alarm mode. The readme gives two ways around it: temporarily short the Reset, E-Stop and Safety Door inputs to ground, or invert the corresponding inputs by setting the parameter 14 to 73.

That default is a safety decision rather than an oversight. A limit switch on a normally closed circuit fails safe, so a broken wire stops the machine instead of letting it run past the limit. But it means a bench test with no switches wired in looks like a fault, and the two workarounds are both in the readme.

The third is about compatibility with the sender. grblHAL has many extensions that may cause issues with some senders, and as a workaround a compile time option has been added that disables extensions selectively. So the extensions that make the controller capable of more also make it less compatible with older senders, and the fix is to turn them off at build time rather than to abandon the features.

The readme also names a companion project for Windows users, ioSender, which has been written to complement grblHAL and has features such as proper keyboard jogging, advanced probing, automatic reconfiguration of the digital readout display for up to six axes, lathe mode including conversational g-code generation, 3D rendering and macro support.

Two more references round it out. A web app for building for some drivers is now available and feedback is appreciated, which is the practical route for people who are not going to set up a cross-compilation toolchain. And the project publishes a changelog, with the latest build date stated at the top of the readme as 20261015, while the tagged releases in the listing are Build 20250518 and a 20230714 core release intended for use with the final STM32F1xx driver.

## Conclusion

grblHAL suits a builder of a small CNC router or mill who wants grbl-class motion control on an ARM board rather than a parallel port, and the driver library covers the processors such a machine would actually use. It does not suit you if you need macro functions, variables or some canned cycles, because those are deliberately not supported, and it does not suit you if you want a GCode sender, since the readme points you at a separate companion project for that. Before you flash anything, note that updating from a build older than 20241208 resets your settings, so back them up, and wire your limit switches properly rather than relying on the inversion workaround, because the normally closed default is the safe configuration.

## FAQ

### What is grblHAL and what hardware does it run on?

grblHAL is a high performance, low cost alternative to parallel-port-based motion control for CNC milling, based on the Arduino version of grbl. It is aimed at ARM processors or other 32-bit microcontrollers with ample RAM and flash, requires a hardware driver to be functional, and drivers are available for more than 15 processor families sharing the same core codebase.

### What is the maximum step pulse rate grblHAL can generate?

Up to 300kHz of stable, jitter free control pulses, with a footnote that the figure is driver and processor dependent. The controller is written in highly optimized C using features of the supported processors to achieve precise timing and asynchronous operation.

### What happens to my settings when I update grblHAL firmware?

A settings reset will be performed on an update of builds prior to 20241208, so backup and restore of settings is recommended before crossing that boundary. The reset is the reason a firmware upgrade can wipe a carefully dialled in configuration.

### Why does my grblHAL controller start in alarm mode on the bench?

grblHAL defaults to normally closed switches for inputs, so with no switches connected the controller will likely start in alarm mode. You can temporarily short the Reset, E-Stop and Safety Door inputs to ground, or invert the corresponding inputs by setting the parameter 14 to 73.

### How do plugins extend grblHAL?

The architecture allows plugins to be added without changing a single file in the source. Plugins can add new M-codes, allocate space for plugin specific settings and subscribe to events, which covers driving an automatic tool changer, adding extra outputs or adding a user interface. The feature is only to be used for private plugins, since a shared one requires a single call added to the driver code of the target processors.

## Sources

- [grblHAL/core on GitHub](https://github.com/grblHAL/core)
- [Issues](https://github.com/grblHAL/core/issues)
- [README](https://github.com/grblHAL/core/blob/master/README.md)
- [Releases](https://github.com/grblHAL/core/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/grblhal-core
