This repository contains:
- Firmware for all Opentrons modules - Temperature Module (Tempdeck), Magnetic Module (Magdeck) and Thermocycler.
- Libraries required to build any module firmware
- Module board file definitions for Arduino (for the arduino-based modules)
- Other files required for development & testing
This repo uses CMake as a configuration and build management tool. It requires CMake 3.20 since it uses presets to manage cross-compilation and project configuration options.
- Before you can configure any of the cmake presets, you may need to manually install a C compiler and a C++ compiler (e.g.
sudo apt install gcc g++
on Ubuntu) - If you want to interact with the STM32 hardware, you should also install st-link.
First, clone the repository and change into the directory:
git clone [email protected]:Opentrons/opentrons-modules.git
cd opentrons-modules
Then,
- If you want to build the arduino modules, run
cmake --preset=arduino .
to set your build step up to build the Arduino-based modules (Magnetic Module, Temperature Module, Thermocycler). This should download the Arduino IDE to the git-ignoredarduino_ide
directory in the repo's working tree. It will also set up a build system in./builds
. - If you want to build the STM32 modules, run
cmake --preset=stm32-cross .
to set your build step up to cross-compile the module firmwares for actually putting on modules. This should download the arm cross builds of gcc 10-2020q4 to the git-ignoredstm32-tools
directory in the repo's working tree and set up a build system in./build-stm32-cross
. By default, this will be a "MinSizeRel" build type, which generates debug symbols packed into the elf and doesn't use optimizations (which makes debugging a lot easier). You can change the build type by adding-DCMAKE_BUILD_TYPE=<BUILD_TYPE>
on the command line when generating. Valid build types areDebug
,MinSizeRel
(default, size optimizations for release - what CI uses to build), andRelWithDebInfo
(optimizations + debug info, useful if you're seeing failures in the field that don't happen inDebug
builds).- If you want to enable assertions for debugging, add
-DENABLE_ASSERTIONS=ON
to this command. If you don't specify this option, theconfigASSERT
lines in compiled firmware will not do anything.
- If you want to enable assertions for debugging, add
- If you want to build the STM32 module tests, run
cmake --preset=stm32-host .
to set your build step up to host-compile the module fimrware libs and tests for local running. You'll need to have at least a gcc installed; if you have a clang installed, the build will run some clang checker steps. This will set up a build system in./build-stm32-host
Since all these configuration steps use separate build dirs (BINARY_DIRS in cmake parlance) you can in fact run all three of these; it's not exclusive. They should all work on any operating system, but the host builds in particular rely on your system compiler and that will therefore need to be set up, which might be a pain on windows.
Each build dir should have a reasonable all
target (invokable by running cmake --build <build-dir>
) and individual targets for each module firmware. For instance, the arduino modules have <module-name>
set up as individual build targets.
You always have to specify the build dir to run in with cmake --build
; that can be run from the root of the repo (e.g. by calling cmake --build ./build-stm32-cross
or cmake --build ./build
) or as .
if you navigate into the right build directory first.
To build a specific target, use the --target
flag: cmake --build ./build --target magdeck
.
For the arduino modules, the module name target builds the sketch and copies some supporting files like a python-based uploader and a hex for writing the eeprom into builds/module-name
. For arduino modules, the special target zip-all
will build zip files of all module firmwares and support files (e.g. eeprom writing): cmake --build ./build-arduino --target zip-all
. You can then run cmake --install ./build
and those zip files will be put in the dist/
directory; this is used in ci.
For stm32 modules, there are module name targets that build the requisite firmware defined in the cross toolchain and the test toolchain alike. In the cross toolchain, these build runnable firmwares; in the host toolchain, they build everything necessary to run tests, but running the tests is done with cmake --test
.
This is not implemented in cmake for the arduino modules. Use the Arduino IDE installed to upload the firmware file:
Open the /modules/.../<file_name>.ino
file in Arduino, select the appropriate board from Tools->board,
select the correct port from Tools->Port. Select Upload or Sketch->Upload
- NOTE: For Thermocycler, you can also use the
firmware_uploader.py
script in thermo-cycler/production to upload the binary.
The stm32 modules have debug harnessing built in; for instance, running cmake --build ./build-stm32-cross --target heater-shaker-debug
will build the firmware and spin up the cross gdb and openocd. This target by default (because of commands in the gdbinit) will reset the board and upload the built firmware; if you want to use the firmware currently flashed to the board, run everything manually.
The stm32 modules also have flash targets for when you want to flash the board but not actually run a debugger; running cmake --build ./build-stm32-cross --target heater-shaker-flash
will build the firmware and then use openocd to flash it to the target.
This is not implemented for the arduino modules.
For the STM32 modules, if you configure the host toolchain: cmake --preset=stm32-host .
, you can then run the build-and-test
target: cmake --build ./build-stm32-host --target build-and-test
, which will build the tests and then run them. You can also use cmake's built-in test
target: cmake --build ./build-stm32-host --target test
, but this will not automatically actually build the tests.
Individual tests may set their own check targets; for instance, you can build and run only the heater-shaker tests by running cmake --build ./build-stm32-host --target heater-shaker-build-and-test
.
If you are on OSX, you almost certainly want to force cmake to select gcc as the compiler used for building tests, because the version of clang built into osx is weird. We don't really want to always specify the compiler to use in tests, so forcing gcc is a separate cmake config preset, and it requires installing gcc 10:
brew install gcc@10
cmake --preset=stm32-host-gcc10 .
You should specifically do this if you're seeing errors about concepts
not existing, or forward_iterator
not existing, or the like.
You can change to a different compiler, like an installed upstream clang/llvm, by changing the cache variables CMAKE_C_COMPILER
and CMAKE_CXX_COMPILER
, or by making a custom configuration (see below) that specifies them.
This isn't the default stm32-host
because it requires specifying the compiler by version to actually work on OSX, where gcc
and g++
unqualified by version are... aliased to system clang. Setting the default to gcc-10
and g++-10
then might have problems on other platforms if someone update their gcc and g++ or on CI.
This repository is configured to generate code coverage from the tests.
To enable code coverage, configure the host toolchain for tests with the options -DENABLE_COVERAGE=On -DCMAKE_BUILD_TYPE=Debug
. In order for code coverage to be accurate, you must run the build-and-test target for a module. After running every build-and-test target, the target lcov
will generate an HTMl report of the code coverage from the tests.
In general, do not enable code coverage unless you need it. Test execution will be far slower.
CMake allows you to set preset overrides through a gitignored file called CMakeUserPresets.json. An example (that implements stm32-host-gcc
, which is the preset above, as an example) is bundled in the examples/
folder. To apply it, you would copy it into the root of the repo after checkout. CMakeUSerPresets.json
works exactly like CMakePresets.json
, but lets you make system- or IDE-specific configuration. In general, presets in CMakeUserPresets
should inherit from those in CMakePresets
, since most of the settings in the checked-in presets are good and only a couple need to be changed, but really it's up to you.
When writing or changing the cmake build system, please follow modern CMake practices. Use targets; use generator expressions to set properties on those targets rather than setting CMAKE_DEBUG_FLAGS
; write CMake like the programming language it is.