Advanced Github Actions for Arduino
Wouldn’t it be great if you could automate all your arduino compilations, even when the preprocessor definitions have to change? You CAN. Let me show you how.
And….Action!
Github Actions make it possible to test my code every time I push an update. The examples provided work great for testing one sketch that basically never changes. But what if you have Makelangelo firmware, code that can be run on many different controllers and many different kinematic linkages? Here now I present my method to you. If you find it helpful, like share subscribe Patreon Instragram facebook twitter smoke signals etc etc etc. Capiche? Neato.
Use case: Makelangelo firmware
Makelangelo-firmware knows which motherboard you have and which kinematic linkage because they are defined at the top of configure.h. Immediately after that the code looks for local_config.h and includes it only if the file exists.
//------------------------------------------------------------------------------
// YOUR CHANGES GO HERE
//------------------------------------------------------------------------------
#if __has_include("local_config.h")
// Your local changes go here.
// Do not send your local_config.h in a pull request. Thank you!
#include "local_config.h"
#endif
This is great for me because I can make sure local_config.h is never in the Github repository. All your changes go there and nobody loses their settings when an update is pushed out. It also means Github Actions can generate local_config.h as needed. More on that in a minute.
The Action is a script that sets up and then runs arduino-cli, the Arduino Command Line Interface. If you want to build arduino code on a raspberry pi over the internet or on a box that has no monitor then this is the tool I recommend.
First things first, we’ll need to name our Action and say when it happens. I called it “Arduino CI” where CI is short for Continuous Integration.
name: Arduino CI
on: push
Next I need to set up a matrix – a table that explains which boards I want, which kinematic models I want, and which combinations are legal. the list below generates 19 different tests. if I allowed all possible combinations on all boards I’d have 72 tests run!
For every board I also need to know the Fully Qualified Board Name (FQBN), a magic word that the Arduino compiler demands. You’ll also see I’ve used exclude to ban bad motherboard-kinematic combinations. A board that can only run 4 motors (like RAMPS) should not attempt to compile for a 6 motor machine like SIXI or a Stewart platform.
jobs:
build:
strategy:
matrix:
board: [BOARD_RUMBA, BOARD_RAMPS, BOARD_SIXI_MEGA ]
# we cannot yet compile BOARD_ESP32, BOARD_WEMOS,
# BOARD_TEENSYLU, BOARD_CNCV3, or BOARD_SANGUINOLULU yet.
robot: [POLARGRAPH, TRADITIONALXY, COREXY, ZARPLOTTER, SKYCAM, DELTA, STEWART, ARM3, SIXI, TRADITIONAL6, SCARA]
include:
- board: BOARD_RUMBA
arduino-platform: arduino:avr
fqbn: arduino:avr:mega
- board: BOARD_RAMPS
arduino-platform: arduino:avr
fqbn: arduino:avr:mega
- board: BOARD_SIXI_MEGA
arduino-platform: arduino:avr
fqbn: arduino:avr:mega
#- board: BOARD_ESP32
# arduino-platform: esp32:esp32
# fqbn: esp32:esp32:lolin32
#- board: BOARD_WEMOS
# arduino-platform: esp8266:esp8266
# fqbn: esp8266:esp8266:lolin32
exclude:
- board: BOARD_RUMBA
robot: SIXI
# BOARD_RAMPS can only handle 5 motors
- board: BOARD_RAMPS
robot: SIXI
- board: BOARD_RAMPS
robot: STEWART
- board: BOARD_RAMPS
robot: TRADITIONAL6
# BOARD_SIXI_MEGA only meant for sixi
# I wish I could say robot: [ a, b, c, d, e, f, g ]
- board: BOARD_SIXI_MEGA
robot: POLARGRAPH
- board: BOARD_SIXI_MEGA
robot: TRADITIONALXY
- board: BOARD_SIXI_MEGA
robot: COREXY
- board: BOARD_SIXI_MEGA
robot: ZARPLOTTER
- board: BOARD_SIXI_MEGA
robot: SKYCAM
- board: BOARD_SIXI_MEGA
robot: DELTA
- board: BOARD_SIXI_MEGA
robot: STEWART
- board: BOARD_SIXI_MEGA
robot: ARM3
- board: BOARD_SIXI_MEGA
robot: TRADITIONAL6
- board: BOARD_SIXI_MEGA
robot: SCARA
Ok! Now we have our allowed combinations, what do we do with them?
Firstly we’ll tell Github that everything is going to be run in a Docker container running the latest Ubuntu. Then we get into the steps.
runs-on: ubuntu-latest
steps:
# First of all, we clone the repo using the checkout action.
- name: Checkout
uses: actions/checkout@master
As I understand it every action has to have @[version] to better control the results. For this reason I was a little hesitant to use the ubuntu-latest docker instead of a specific ubuntu version. I don’t know if that will come back to haunt me, place your bets.
# We use the arduino/setup-arduino-cli action to install and
# configure the Arduino CLI on the system.
- name: Setup Arduino CLI
uses: arduino/[email protected]
# We then install arduino
- name: Install platform
run: arduino-cli core update-index
# Install the 'core', the piece of custom code that handles our motherboard of choice.
- name: Add core(s)
run: arduino-cli core install ${{ matrix.arduino-platform }}
# Add some libraries we need
- name: Add SDFat
run: arduino-cli lib install SdFat
- name: Add TMC2130Stepper
run: arduino-cli lib install TMC2130Stepper
- name: Add LiquidCrystal
run: arduino-cli lib install LiquidCrystal
# Make the local_config.h file!
- name: Add local_config.h
uses: DamianReeves/[email protected]
with:
path: ./local_config.h
contents: "#pragma once\n#define MOTHERBOARD ${{matrix.board}}\n#define MACHINE_STYLE ${{matrix.robot}}"
write-mode: overwrite
# Finally, we compile the sketch, using the FQBN that was set
# in the build matrix.
- name: Compile Sketch
run: arduino-cli compile --fqbn ${{ matrix.fqbn }} ./Makelangelo-firmware.ino
If all goes well the output looks a little like this.
Final thoughts
You can find the latest version on the Github repository for Makelangelo-firmware. It took several days of chiselling at the problem to get it working, and then I needed a walk outside before I could face all the bugs that the CI system found.
So in the end as long as it works I should have more robust code, fewer lingering doubts, and happier users. Worth it!
Special thanks to Github user per1234 for helping me through the wilderness.