Skip to content

Getting Started

The easiest way for you to learn about modm's APIs is to look at and experiment with our examples, especially if you have a development board that modm supports out-of-box.

Make sure you've installed all tools required for building modm.


lbuild build    # generate modm library (call only once)
scons program   # compile and upload to your development board

To debug with GDB in TUI mode:

scons program profile=debug # compile and upload debug profile
scons gdb profile=debug     # launch OpenOCD and GDB for debugging

To generate your target specific Doxygen documentation:

(cd modm/docs && doxygen doxyfile.cfg)  # may take a minute or two
# open modm/docs/html/index.html

To remove it all:

scons -c      # removes build artifacts
lbuild clean  # removes generated files


To generate the modm library for the specific example target, call

lbuild build

You can then look at the generated modm code in the local modm/src/modm folder.

Most of our examples compile with SCons by default, however you can generate a CMake build script by including the CMake build script generator module.

lbuild build -m "::cmake"

To compile the example and modm, call SCons or CMake:

# or for CMake
make cmake           # call just once
make build-release

To upload the example to your development board:

scons program
# or for CMake
make upload-release

You can also debug your examples. Make sure you've compiled and uploaded the debug profile first, because debugging a release profile is annoying:

scons program profile=debug
# or for CMake
make upload-debug

Then just do this to open up GDB in TUI mode:

scons gdb profile=debug
# or for CMake
make openocd
# open another shell to this location
make gdb

Interesting Examples

We have a lot of examples, 178 to be exact, but here are some of our favorite examples for our supported development boards:

Here are some additional examples of displays and sensors we like:

Your own Project

To generate a modm library for your own project, you need to define a project.xml file, which contains the path to where modm is, as well as repository and module options and of course which modules you want to have generated. Even though modm will generate a library that is self-contained, we still recommend adding modm as a git submodule for reproducibility.

Using a Board Support Package

To build on a BSP, inherit from an existing project configuration using the <extends> element. You can discover the available configuration aliases using lbuild:

 $ lbuild --repository ../modm/ discover
╰── Repository(modm @ ../modm)   modm: a barebone embedded library generator
    ├── Configuration(modm:arduino-uno)   Arduino UNO
    ├── Configuration(modm:blue-pill)   Blue Pill
    ├── Configuration(modm:disco-f469ni)   STM32F469IDISCOVERY
    ├── Configuration(modm:nucleo-f401re)   NUCLEO-F401RE
    ├── Configuration(modm:olimexino-stm32)   Olimexino STM32
    ╰── Configuration(modm:stm32f030_demo)   STM32F030 Demo Board

Our BSPs declare a minimal set of modules as dependencies as well as pre-define several important options for this board. You can then add all the modules you need and configure them as you want.

    <!-- path to modm repository -->
  <!-- extend this board configuration -->
    <!-- give this project a custom name -->
    <option name="">test</option>
    <!-- include the SCons build module -->

Choose a build system

Our BSPs do not specify a build system generator, so you need to add the module yourself if you want. Here we use the SCons build system generator, but you can choose others as well.

Our board support packages provide their configuration in the Board namespace, which you can use to initialize the target and several board subsystems. If a serial connection is available on the board, you can directly use the modm logging functions.

#include <modm/board.hpp>

int main()

    while (1)
        modm::delayMilliseconds(Board::Button::read() ? 250 : 500);
        static uint32_t counter(0);
        MODM_LOG_INFO << "Loop counter: " << (counter++) << modm::endl;
    return 0;

Discovering modm

To generate your custom library, modm uses the Library Builder, which is the interface to discover available modules and their configuration options.

 $ lbuild --repository ../modm/ discover
    ╰── EnumerationOption(target) = REQUIRED in [at90can128, at90can32, at90can64, ...

This gives you an overview of the repositories and their options. In this case the modm:target repository option is required, so let's check that out:

 $ lbuild -r ../modm/ discover-options
modm:target = REQUIRED in [at90can128, at90can32, at90can64, at90pwm1, at90pwm161, at90pwm2,
                           ... a really long list ...
                           stm32l4s9vit, stm32l4s9zij, stm32l4s9zit, stm32l4s9ziy]

  Meta-HAL target device

You can then choose this repository option and discover the available modules for this specific repository option:

 $ lbuild -r ../modm/ --option modm:target=stm32f407vgt discover
    ├── EnumerationOption(target) = stm32f407vgt in [at90can128, at90can32, at90can64, ...]
    ├── Configuration(modm:disco-f407vg)   STM32F4DISCOVERY
    ├── Module(modm:board)   Board Support Packages
    │   ╰── Module(modm:board:disco-f407vg)   STM32F4DISCOVERY
    ├── Module(modm:build)   Build System Generators
    │   ├── Option(build.path) = build/parent-folder in [String]
    │   ├── Option( = parent-folder in [String]
    │   ╰── Module(modm:build:scons)  SCons Build Script Generator
    │       ├── BooleanOption( = False in [True, False]
    │       ╰── EnumerationOption(info.git) = Disabled in [Disabled, Info, Info+Status]
    ├── Module(modm:platform)   Platform HAL
    │   ├── Module(modm:platform:can)   Controller Area Network (CAN)
    │   │   ╰── Module(modm:platform:can:1) Instance 1
    │   │       ├── NumericOption(buffer.rx) = 32 in [1 .. 32 .. 65534]
    │   │       ╰── NumericOption(buffer.tx) = 32 in [1 .. 32 .. 65534]
    │   ├── Module(modm:platform:core)   ARM Cortex-M Core
    │   │   ├── EnumerationOption(allocator) = newlib in [block, newlib, tlsf]
    │   │   ├── NumericOption(main_stack_size) = 3040 in [256 .. 3040 .. 65536]
    │   │   ╰── EnumerationOption(vector_table_location) = rom in [ram, rom]

You can now discover all module options in more detail:

 $ lbuild -r ../modm/ -D modm:target=stm32f407vgt discover-options
modm:target = stm32f407vgt in [at90can128, at90can32, at90can64, ...]

  Meta-HAL target device

modm:build:build.path = build/parent-folder in [String]

  Path to the build folder = parent-folder in [String]

  Project name for executable

Or check out specific module and option descriptions:

 $ lbuild -r ../modm/ -D modm:target=stm32f407vgt discover -n :build
>> modm:build  [Module]

# Build System Generators

This parent module defines a common set of functionality that is independent of
the specific build system generator implementation.

>>>>  [StringOption]

# Project Name

The project name defaults to the folder name you're calling lbuild from.

Value: parent-folder
Inputs: [String]

>>>> modm:build:build.path  [StringOption]

# Build Path

The build path is defaulted to `build/{}`.

Value: build/parent-folder
Inputs: [String]

The complete lbuild command line interface is available with lbuild -h.

Options are checked

lbuild checks all your project options against the possible values in the module and outputs an error if they are incorrect.

Visualize your dependencies

Create a dependency graph with lbuild dependencies -m "modm:module" | dot -Tsvg -Grankdir=BT -o dependencies.svg to help you understand what code is pulled in when you generate your library.

Custom Configuration

In case modm doesn't have a BSP for your board or the BSP uses the hardware in ways you don't like, you can define your own completely custom configuration. Here a completely minimal library is generated for a STM32F469NIH device only with the Cortex-M, GPIO and time modules and their dependencies. Since no build system generator module is specified, you will only get the raw source code.


    <option name="modm:target">stm32f469nih</option>

Check your repository path

Make sure you've set the correct path to the modm file, otherwise lbuild cannot help you much. This is especially important when copying from our examples, which have the repository path set in the inherited common configuration file located in modm/examples/lbuild.xml!

A minimal main.cpp for this configuration would look like this:

#include <modm/platform.hpp>
using namespace modm::platform;

int main()
    while(1) {

We recommend to start your custom projects with a known-good configuration from one of our examples and then work your way into your specialization.

Generate and Compile

Once you have your project.xml set up, you can call lbuild build, which generates the target and configuration specific library from modm. This will create a few files and folders:

 $ ls
main.cpp    project.xml
 $ lbuild build
 $ ls
SConstruct      main.cpp        modm            project.xml     project.xml.log

You can add these folders and files to your .gitignore file, however, we recommend eventually committing these files (yes, all these files) into your project repository so that you don't have issues reproducing the build.

Generate custom documentation

Include the modm:docs module (or execute lbuild build --module "modm:docs"), then call doxygen doxyfile inside the generated modm/docsfolder. The documentation for your target and configuration will then be available in modm/docs/html/index.html.

For this project we included the modm:build:scons generator, so we can just call scons build size, which will compile the entire source code and output the resource consumption:

 $ scons build size
scons: Reading SConscript files ...
scons: done reading SConscript files.
scons: Building targets ...
Compiling C++·· build/main.o
Indexing······· build/libmodm.a
Linking········ build/project.elf
Memory usage··· build/project.elf

Program:    3612B (0.3% used)
(.fastcode + .fastdata + .hardware_init + .reset + .rodata + .table.copy.intern +
 .table.section_heap + + .text)

Data:       3184B (1.6% used) = 144B static (0.1%) + 3040B stack (1.5%)
(.bss + .fastdata + .stack)

Heap:     197520B (98.4% available)
(.heap0 + .heap1 + .heap2 + .heap5)

You can program your target by calling scons program. Additional tools are documented in SCons module documentation.

If you have any questions, open an issue or ping @salkinium.