Writing a Module
A Module is a Git repository owner/Repo whose primary header Repo.hpp declares a plain global C++ class Repo. Its public constructor is the interface: configurations fill its parameters by name and the generator calls it directly.
Creating a Skeleton
xrobot new-module MySensor --desc "IMU driver" \
--constructor "LibXR::I2C& i2c" \
--constructor "uint32_t period_ms = 10" \
--depends xrobot-org/BlinkLED
| Option | Meaning |
|---|---|
--desc | Module description |
--constructor | One C++ parameter declaration; repeat per parameter |
--template | One template parameter declaration; repeat per parameter |
--template-arg | A template argument the Module CI compiles with; repeat per argument; template parameters with defaults may be left out |
--include | Another header to include; libxr.hpp and the headers of the LibXR hardware interfaces the constructor uses (such as i2c.hpp) are always included |
--depends | A dependency owner/Repo[@ref]; repeat per dependency; the default ref is same-or-dev |
--out | Parent folder in which <NAME>/ is created, default the current directory |
Before writing anything, new-module checks the assembled header with the rules of setup and instance add: every parameter has a name, dependencies come before parameters with defaults, and a template parameter without a default has a --template-arg. When the check fails it reports the error and creates nothing.
Result:
MySensor/
├── MySensor.hpp # class, constructor and manifest
├── CMakeLists.txt # adds the sources to LibXR's xr target
├── README.md # laid out like a Module README; purposes are left to the author
└── .github/workflows/build.yml # calls the shared Module CI
#pragma once
// clang-format off
/* === MODULE MANIFEST V2 ===
module_description: IMU driver
depends:
- id: xrobot-org/BlinkLED
ref: same-or-dev
=== END MANIFEST === */
// clang-format on
#include "i2c.hpp"
#include "libxr.hpp"
class MySensor
{
public:
MySensor(LibXR::I2C& i2c, uint32_t period_ms = 10) {}
};
xrobot module show <folder, header or Module id> prints the manifest and constructors; with owner/Repo or Repo it reads the locked Module of the current BSP. A YAML error in the manifest names the line and column in the header file.
Constructor Conventions
- Dependencies (hardware or other Modules) come first, as references or pointers without defaults. An optional dependency is a pointer without a default; configurations may pass
nullptr. - Value configuration follows, with defaults.
xrobot instance addwrites the defaults into the configuration. - Every parameter needs a name; declare a type alias for arrays, function pointers and similar types.
- Configurations fill struct parameters field by field, so define the struct in a header in the Module root, or in a header inside the Module that one of them brings in with
#include "...". - A class may have several public constructors; configurations select one by parameter names, and overloads with equal names need explicit types to be told apart.
- The constructor does all initialization. Interfaces generated by macros or changed by conditional compilation must be written as explicit declarations.
An optional public non-static void OnMonitor() is called periodically by the main loop, in configuration order.
Manifest
The manifest accepts only these keys:
module_description: Description
depends:
- id: owner/Repo
ref: same-or-dev
standalone: false
depends: dependency Modules and refs; see ref rules.same-or-devmakes dependencies follow the BSP's branch.standalone: false: a library used only as a dependency; it cannot be instantiated.
Using and Developing in a BSP
xrobot module add owner/MySensor@dev
xrobot setup
xrobot instance add owner/MySensor
Edit and build directly in the BSP's Modules/owner/MySensor/, keeping the changes uncommitted. When they are ready, commit and push them to a branch of the Module repository, then run xrobot setup --update owner/MySensor in the BSP to update the lock.
To be added to a BSP, the Module must be listed in a Source; see Sources.
The .github/workflows/build.yml that new-module writes calls the shared Module CI; its steps and inputs are described in CI and Firmware Release.