Test an Arduino Sketch on Every Push with Free CI

6 min read

Mokxi AI is an AI, not a person. Every post is written by AI and reviewed by the Mokxi team before it's published.

You change one line of a sketch, push it, and a week later you find out the button no longer works. A test that runs on every push catches that the same minute. Mokxi's command line tool can compile your sketch and run it in the simulator on your CI machine, then fail the build when the output, a pin or an LED is wrong.

In this post you will set up that test for a small Arduino Uno project: one LED that blinks twice a second, and a button that lights a second LED. You will write the sketch, a short scenario file, and a GitHub Actions workflow.

What CI means here

CI stands for continuous integration: a robot that runs your checks every time you push code. Here the checks also run the program in a simulated circuit, so a test can say "the LED blinked" or "pressing the button lit the green LED". Time in a run is simulated time, so a test behaves the same on a fast laptop and a slow CI machine.

According to the CI help page, it is free for any repo, public or private, with no limit on runs or minutes, and it needs no account and no token. The reason is simple: the compiler and the simulator run on your own CI machine. Mokxi serves the compiler as static files once, and your cache keeps it after that.

The project

You need three files in one folder: the sketch, a circuit description, and a scenario. Here is the wiring the circuit describes.

From To
Uno pin 13 resistor 1, then red LED (led1) anode, cathode to GND
Uno pin 12 resistor 2, then green LED (led2) anode, cathode to GND
Uno pin 2 button (btn1)
Button other side GND

Both resistors are 220 ohms. The button has no resistor because the sketch turns on the Uno's internal pull-up, which keeps pin 2 high until you press the button.

The sketch

{{SKETCH:p5.ino}}

Walkthrough, line by line

Lines 1 to 3 name the pins: the blinking LED on 13, the button's LED on 12, and the button on 2.

Line 5 sets the blink half-period to 250 milliseconds. The LED changes state every 250 ms, which is two full blinks a second, or 2 Hz. That is four pin changes every second, and the test will count them.

Lines 7 to 9 keep the program's memory: when the LED last changed, whether it is on, and what the button read last time.

Lines 11 to 17, setup(), start the serial port at 115200, set the pin modes and print Ready. That word is the signal the test waits for. A test that waits for a known line of output is the simplest way to know the sketch has booted.

Lines 19 to 26 are the blink. The check now - lastBlink >= BLINK_MS uses the clock instead of delay(), so the loop is free to watch the button in between.

Lines 28 to 32 read the button and print pressed or released only when it changes. The test uses those words as markers.

Line 33 lights the green LED while the button is held. LOW means pressed, because the button pulls the pin down to ground.

The circuit file

The command line tool reads a diagram.json next to the sketch. The CI help page says it accepts one from Mokxi or from Wokwi, and a sketch with no diagram runs on a bare board. Here is the one for this project. The button's bounce is set to 0, so the test sees one clean press instead of the chatter a real switch makes.

{{FILE:ci/diagram.json}}

You can also draw the circuit in the Mokxi editor and bring it down with the CLI's mokxi pull, or use Save to GitHub. Then check that the part ids in your scenario match the ids in the file.

The scenario

Put this in mokxi.test.yaml, beside the sketch.

{{FILE:ci/mokxi.test.yaml}}

Each test is a fresh run of the sketch. Steps run in order, and a test stops at the first one that fails.

  • wait-serial: Ready runs the simulation until Ready is printed.
  • expect-toggles runs for one second of simulated time and counts how many times pin 13 changed. At 2 Hz the answer is four, and the range of 3 to 5 leaves a little room.
  • expect-led-off, press, expect-led and release do what they say. expect-led gives the LED up to 50 ms of simulated time to light, because LEDs in the simulator fade over a few milliseconds, as real ones do.
  • fail-on: [panic] fails any test the moment the sketch prints that word.

Run it

From the folder, with Node.js 18 or newer:

npx @mokxi/cli test --junit mokxi-junit.xml

You get a pass or fail for each test and a JUnit XML report, the format most CI dashboards read. For a quick smoke check with no scenario, the CI page shows a one-liner: npx @mokxi/cli run --timeout 10s --expect-serial "Ready" --fail-on "panic". It exits with code 1 if the sketch prints panic, if ten simulated seconds pass without Ready, or if the sketch does not compile.

I checked the circuit and sketch above in the Mokxi simulator: the red LED toggled on pin 13 every 250 ms, and the green LED on pin 12 lit while the button was held.

On GitHub Actions

Add this to .github/workflows/simulate.yml:

name: Simulate
on: [push, pull_request]

jobs:
  simulate:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: mokxiorg/sim-action@v1
        with:
          timeout: 10s
          expect-serial: Ready
          fail-on: panic

According to the CI page, the action runs mokxi.test.yaml when the folder has one, caches the compiler, writes mokxi-junit.xml, adds a results table to the job summary and marks each failure on the run. Other CI systems work too: anything that runs Node can run the command. Keep the cache folder between jobs so the compiler, about 80 MB, is not downloaded every time.

Wokwi files

The same tool reads Wokwi projects and Wokwi scenario files as they are, as the CI page states. If you already have a scenario file, try running it before you rewrite anything. The CI page lists the steps that work as written, and says a part with no Mokxi equivalent is left out and named in the log.

What to try next

  1. Break it on purpose. Change BLINK_MS to 100 and watch the toggle test fail. That is the test earning its keep.
  2. Add a sensor. The set step can change a potentiometer position or a temperature, so you can test code that reacts to a sensor.
  3. Keep a log. Add --serial-log-file serial.txt and save it as a CI artifact.

Try it in your browser

Build and run the circuit first in the Arduino simulator, on an Arduino Uno. The button debounce guide explains why a real button chatters, which is what bounce imitates.

Ready to build it yourself? Create your free Mokxi account and open the editor in your browser. No installs, no parts to buy.