# modm: generating an embedded C++23 HAL for thousands of parts

> modm is a code generator that produces hardware abstraction layers, drivers and board support packages for AVR and ARM Cortex-M microcontrollers from a data-driven description, so a project only compiles the peripherals it uses. It suits an embedded developer who wants portable C++ without the code size of a general purpose SDK, and it is driven by a lbuild generator rather than by a runtime library.

**modm-io/modm** — modm: a C++23 library generator for AVR and ARM Cortex-M devices

- Repository: https://github.com/modm-io/modm
- Website: https://modm.io
- Stars: 991 · Forks: 176
- Language: C++
- License: MPL-2.0
- Published: 2026-10-20 · Updated: 2026-10-20 · Language: en
- Canonical page: https://hysenlabs.com/projects/modm-io-modm

## A generator, not a library you link against

modm is a toolbox for building custom C++23 libraries tailored to your embedded device. It generates startup code, HALs and their implementations, communication protocols, drivers for external devices, and BSPs, in a modular and customisable process you can fine-tune.

The word generator is the whole design, and it has an immediate consequence for how you use the project. There is no library you add to your include path. There is a description of your device and a code generator that turns it into source, and the source you then compile.

That approach exists for a stated reason. modm is optimised for the harsh requirements of the Eurobot competition, where the robots need to run reliably and completely autonomously for the game's 100 second duration. The robots contain a lot of different microcontrollers, some without a lot of resources, so the requirements are small code size with small memory consumption, predictable program flow, and extreme portability.

The generated result is a C++23 API that is efficient and fast, and the peripheral interfaces are cross-platform, so GPIO, external interrupts, IO expanders, ADC, DAC and comparators, UART, I2C, SPI, CAN and Ethernet are the same interfaces regardless of which vendor's part you are on. The project also ships a functional, partial libstdc++ implementation for AVRs, which is the piece that makes modern C++ on an 8-bit part possible at all.

The library source is licensed as MPLv2, with external source under compatible licences such as BSD, Apache2 and MIT, and the project asks that changes be contributed back.

The last push to the develop branch was on 2026-10-18, and the releases are quarterly, with 2026q3 published on 2026-10-01, 2026q2 on 2026-07-02 and 2026q1 on 2026-04-06.

## Cloning it, and why the submodules are not optional

The README is explicit about one thing, and it is the first thing you will hit.

Please clone modm recursively, you need all the submodules:

```sh
git clone --recurse-submodules --jobs 8 https://github.com/modm-io/modm.git
```

The --jobs 8 flag is a parallel clone, which matters because the repository is large and the submodule set is wide, and --recurse-submodules is not optional. The generator reads device descriptions from those submodules, so a partial clone produces a build that fails with a missing file rather than a helpful message.

The top level directory listing shows what is in the repository. There is src/ for the library source, ext/ for the third party integrations, test/ for the test suite, tools/ for the build and generator tooling, examples/ for the worked examples, docs/ for the documentation, and repo.lb, which is the lbuild module file that drives the generator. There are also .clang-format, .editorconfig, a .mailmap, a CHANGELOG.md and a CONTRIBUTING.md.

The examples directory is worth browsing before anything else, because it is the API in use. The subdirectories are analog/, can/, communication/, core/, driver/, ext/, gpio/, logging/, timer/ and ui/, along with an lbuild.xml configuration and a README.

The other side of the generator is that the result is yours. The README says the library is licensed so you can fork it and adapt it to your needs, and the only ask is that you contribute your changes back. Combined with the data-driven generation, that means a project can keep its own modifications to the generated output without forking the whole toolchain, which is the property that makes this approach practical for a long-lived embedded product.

## Four thousand devices, and a table you should read carefully

The scale is stated with a number that needs a caveat attached.

modm can create a HAL for 4095 devices across five vendors. STMicroelectronics STM32 accounts for 3332 of those, Microchip SAM for 355, Microchip AVR for 388, Nordic nRF for 19, and Raspberry Pi for 1.

The README then warns that the peripheral table is a summary overview and that your specific device may not have all the peripherals in the table, and points at the per-device driver discovery for the detail. That warning is the important part of the section. A summary table of 4095 devices by definition flattens the differences between them, and the symbols used in the table are a three-state scheme, where a filled mark means implemented as a software driver in modm, an open circle means available in hardware but missing a software driver in modm, and a cross means unavailable in hardware or not supported by modm.

So the table tells you where modm has coverage gaps, and the discovery page is where you find out whether the gap applies to your part. For a team choosing a microcontroller on the strength of a peripheral, that distinction is the difference between a week and a port.

The peripheral list itself covers the things an embedded project actually reaches for. On the input and output side there is GPIO, external interrupt and IO expanders. On the analog side, ADC, DAC and comparators. On the bus side, UART, I2C, SPI, CAN and Ethernet, with the note that cross-platform peripheral interfaces include bit banging. And there are interfaces and drivers for many external I2C and SPI sensors and devices.

## What the project bundles in from elsewhere

The third party integration list is long, and it explains a lot about what modm is for, because these are the pieces an embedded project would otherwise assemble by hand.

On the toolchain side there are CMSIS interfaces and headers, which is the standard Cortex-M peripheral layer, and CMSIS-DAP, a USB to SWD and JTAG bridge for debugging. CMSIS-DSP is the embedded compute library.

On the runtime side there is CrashCatcher, which produces crash reports for HardFaults, and RTT, Segger's Real-Time Transport. FreeRTOS and FreeRTOS+TCP provide the operating system, and LVGL provides the embedded graphics library, with TinyUSB as the USB host and device stack.

Then there is a layer of libraries you would otherwise have to write for a constrained target. ETL, the Embedded Template Library, and Nanopb, embedded Protocol Buffers, and the printf implementation, and ROSserial as the embedded ROS client, and Eigen for linear algebra, and FatFS for the FAT and exFAT filesystem.

The grouping is the point. Every one of these is a library that would bloat an AVR or a small Cortex-M if compiled in full, and the generated approach lets the build include only the parts a project uses. That is consistent with the no memory allocations in HAL claim and the very low overall RAM consumption, and with the fact that there is a lightweight unit testing system suitable for AVRs and hundreds of tests to ensure correct functionality.

There is also a debug and logging system with an IOStream and printf interface, and a cooperative, stackful fibers and scheduler, which is what an embedded project uses to structure a main loop without a full operating system.

## Build system agnostic, and the rest of the toolchain

One feature line in the list is really a constraint on everything else: modm is build system agnostic. You can choose SCons, CMake, Makefile, or use your own.

That is a real convenience for a project whose build is already decided, and it comes from the same fact as the generator design, that the output is source code rather than a library. Once the HAL is generated, it is your source, and your build system does what it always did with source.

The rest of the feature list is about the code itself. Modular, data-driven, target-specific HAL generation using the lbuild code generator is the mechanism already covered, no memory allocations in HAL with very low overall RAM consumption is the consequence, and highly configurable modules with sensible defaults and lots of documentation is the usability claim.

There are also useful filter, interpolation and geometric algorithms listed, which is a reminder that a generated HAL is only the hardware half of a project. The other half is the code you write on top of it, and having the common numeric helpers in the same tree with the same licence and the same build is a convenience worth noting.

The project runs a continuous integration check on every contribution for regressions, it has a forum for discussions and a technical blog for larger design concepts, and it has an API reference published separately from the documentation. The last two are the ones to bookmark, because a code generator is a tool you will want to understand rather than merely run, and the blog is where the design reasoning lives.

There is also a lightweight unit testing system, suitable for AVRs, and hundreds of tests to ensure correct functionality, which is the part of the project that has to exist for a generator to be trustworthy at all. A generator that produces subtly wrong register settings is worse than no generator, and the test suite is the answer to that.

## Conclusion

Adopt modm when your project must run on several microcontrollers from different vendors and you care about flash and RAM, because the generated code contains only the peripherals you asked for and the same peripheral interface works across parts. Do not adopt it for a single fixed board, where a vendor SDK is less work. Before you start, read the peripheral driver table for your exact part, because the summary table is a summary, and clone the repository recursively, since the generator configuration lives in submodules and a partial clone will fail in a confusing way.

## FAQ

### What is modm and how does it generate code?

modm is a toolbox for building custom C++23 libraries for your embedded device. It generates startup code, HALs and their implementations, communication protocols, drivers for external devices and BSPs, through modular, data-driven, target-specific HAL generation using the lbuild code generator, so only the peripherals your project uses are compiled.

### How do I clone modm correctly?

Recursively, because the generator configuration lives in submodules. The README gives `git clone --recurse-submodules --jobs 8 https://github.com/modm-io/modm.git` and says you need all the submodules, so a partial clone will fail later rather than at the clone step.

### Which microcontrollers does modm support?

The README states 4095 devices across five vendors: 3332 STMicroelectronics STM32, 355 Microchip SAM, 388 Microchip AVR, 19 Nordic nRF and 1 Raspberry Pi. It also warns that the peripheral table is a summary overview and that your specific device may not have all the peripherals listed, so the per-device driver discovery is what to check for your part.

### Which build systems work with modm?

modm is build system agnostic, so you can choose SCons, CMake, Makefile or use your own. The feature list also covers efficient C++23 APIs, a debug and logging system with IOStream and printf interfaces, a cooperative stackful fibers and scheduler, a partial libstdc++ implementation for AVRs, and a lightweight unit testing system suitable for AVRs.

## Sources

- [License: MPL-2.0](https://github.com/modm-io/modm/blob/develop/LICENSE)
- [modm-io/modm on GitHub](https://github.com/modm-io/modm)
- [Project website](https://modm.io)
- [README](https://github.com/modm-io/modm/blob/develop/README.md)
- [Releases](https://github.com/modm-io/modm/releases)

---

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