Skip to content

Commit fca344a

Browse files
committed
Add hardware CI
1 parent f7a3709 commit fca344a

File tree

4 files changed

+331
-3
lines changed

4 files changed

+331
-3
lines changed

README.md

Lines changed: 142 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1656,7 +1656,7 @@ for more details.
16561656
A complete project source code you can find in
16571657
[step-7-webserver](step-7-webserver) directory.
16581658
1659-
## Automatic software and hardware tests
1659+
## Automated firmware builds (software CI)
16601660
16611661
It is a good practice for a software project to have a continuous
16621662
integration (CI) test. On every change pushed to the
@@ -1680,10 +1680,149 @@ the repo which breaks a build, Github will notify us. On success, Github will
16801680
keep quiet. See an [example successful
16811681
run](https://github.com/cpq/bare-metal-programming-guide/actions/runs/3840030588).
16821682
1683+
1684+
## Automated firmware tests (hardware CI)
1685+
16831686
Would it be great to also test built firmware binaries on a real hardware, to
16841687
test not only the build process, but that the built firmware is correct and
1685-
functional? Easy. See my https://github.com/cpq/continuous-hardware-test
1686-
for detailed instructions.
1688+
functional?
1689+
1690+
It is not trivial to build such system ad hoc. For example,
1691+
one can setup a dedicated test workstation, attach a tested device
1692+
(e.g. Nucleo-F429ZI board) to it, and write a piece of software for remote
1693+
firmware upload and test using a built-in debugger. Possible, but fragile,
1694+
consumes a lot of efforts and needs a lot of attention.
1695+
1696+
The alternative is to use one of the commercial hardware test systems (or EBFs,
1697+
Embedded Board Farms), though such commercial solutions are quite expensive.
1698+
1699+
But there is an easy way.
1700+
1701+
### Solution: ESP32 + vcon.io
1702+
1703+
And there is a simple and inexpensive way to do it using the https://vcon.io
1704+
service, which implements remote firmware update and UART monitor:
1705+
1706+
1. Take any ESP32 or ESP32C3 device (e.g. any inexpensive development board)
1707+
2. Flash a pre-built firmware on it, turning ESP32 into a remotely-controlled programmer
1708+
3. Wire ESP32 to your target device: SWD pins for flashing, UART pins for capturing output
1709+
4. Configure ESP32 to register on https://dash.vcon.io management dashboard
1710+
1711+
When done, your target device will have an authenticated, secure RESTful
1712+
API for reflashing and capturing device output. It can be called from anywhere,
1713+
for example from the software CI:
1714+
1715+
![](images/ota.svg)
1716+
1717+
The [vcon.io](https://vcon.io) service is run by Cesanta - the company I work
1718+
for. It is a paid service with a freebie quota: if you have just a few devices
1719+
to manage, it is completely free.
1720+
1721+
### Configuring and wiring ESP32
1722+
1723+
Take any ESP32 or ESP32C3 device - a devboard, a module, or your custom device.
1724+
Our recommendation is ESP32C3 XIAO devboard
1725+
([buy on Digikey](https://www.digikey.ie/en/products/detail/seeed-technology-co-ltd/113991054/16652880))
1726+
because of its low price (about 5 EUR) and small form factor.
1727+
1728+
We're going to assume that the target device is a Raspberry Pi
1729+
[W5500-EVB-Pico](https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico)
1730+
board with a built-in Ethernet interface. If your device is different,
1731+
adjust the "Wiring" step according to your device's pinout.
1732+
1733+
- Follow [Flashing ESP32](https://vcon.io/docs/#flashing-esp32) to flash your ESP32
1734+
- Follow [Network Setup](https://vcon.io/docs/#network-setup) to register ESP32 on https://dash.vcon.io
1735+
- Follow [Wiring](https://vcon.io/docs/#quick-start-guide) to wire ESP32 to your device
1736+
1737+
This is how a configured device breadboard setup may look like:
1738+
![](images/breadboard.webp)
1739+
1740+
This is how a configured device dashboard looks like:
1741+
![](images/screenshot.webp)
1742+
1743+
Now, you can reflash your device with a single command:
1744+
1745+
```sh
1746+
curl -su :$API_KEY 'https://dash.vcon.io/api/v3/devices/$ID/ota' --data-binary @firmware.bin
1747+
```
1748+
1749+
Where `API_KEY` is the dash.vcon.io authentication key, `ID` is the registered
1750+
device number, and `firmware.bin` is the name of the newly built firmware. You
1751+
can get the `API_KEY` by clicking on the "api key" link on a dashboard. The
1752+
device ID is listed in the table.
1753+
1754+
We can also capture device output with a single command:
1755+
1756+
```sh
1757+
curl -su :$API_KEY 'https://dash.vcon.io/api/v3/devices/$ID/tx?t=5'
1758+
```
1759+
1760+
There, `t=5` means wait 5 seconds while capturing UART output.
1761+
1762+
Now, we can use those two commands in any software CI platform to test a new
1763+
firmware on a real device, and test device's UART output against some expected
1764+
keywords.
1765+
1766+
### Integrating with Github Actions
1767+
1768+
Okay, our software CI builds a firmware image for us. It would be nice to
1769+
test that firmware image on a real hardware. And now we can!
1770+
We should add few extra commands that use `curl` utility to send a built
1771+
firmware to the test board, and then capture its debug output.
1772+
1773+
A `curl` command requires a secret API key, which we do not want to expose to
1774+
the public. The right way to go is to:
1775+
1. Go to the project settings / Secrets / Actions
1776+
2. Click on "New repository secret" button
1777+
3. Give it a name, `VCON_API_KEY`, paste the value into a "Secret" box, click "Add secret"
1778+
1779+
One of the example projects builds firmware for the RP2040-W5500 board, so
1780+
let's flash it using a `curl` command and a saved API key. The best way is
1781+
to add a Makefile target for testing, and let Github Actions (our software CI)
1782+
call it:
1783+
[.github/workflows/test.yml](https://github.com/cpq/bare-metal-programming-guide/blob/8d419f5e7718a8dcacad2ddc2f899eb75f64271e/.github/workflows/test.yml#L18)
1784+
1785+
Note that we pass a `VCON_API_KEY` environment variable to `make`. Also note
1786+
that we're invoking `test` Makefile target, which should build and test our
1787+
firmware. Here is the `test` Makefile target:
1788+
[step-7-webserver/pico-w5500/Makefile](https://github.com/cpq/bare-metal-programming-guide/blob/4fd72e67c380e3166a25c27b47afb41d431f84b9/step-7-webserver/pico-w5500/Makefile#L32-L37)
1789+
1790+
Explanation:
1791+
- line 34: The `test` target depends on `build`, so we always build firmware
1792+
before testing
1793+
- line 35: We flash firmware remotely. The `--fail` flag to `curl` utility
1794+
makes it fail if the response from the server is not successful (not HTTP 200
1795+
OK)
1796+
- line 36: Capture UART log for 5 seconds and save it to `/tmp/output.txt`
1797+
- line 37: Search for the string `Ethernet: up` in the output, and fail if it
1798+
is not found
1799+
1800+
This is the example output of the `make test` command described above:
1801+
1802+
```sh
1803+
$ make test
1804+
curl --fail ...
1805+
{"success":true,"written":59904}
1806+
curl --fail ...
1807+
3f3 2 main.c:65:main Ethernet: down
1808+
7d7 1 mongoose.c:6760:onstatechange Link up
1809+
7e5 3 mongoose.c:6843:tx_dhcp_discover DHCP discover sent
1810+
7e8 2 main.c:65:main Ethernet: up
1811+
81d 3 mongoose.c:6726:arp_cache_add ARP cache: added 192.168.0.1 @ 90:5c:44:55:19:8b
1812+
822 2 mongoose.c:6752:onstatechange READY, IP: 192.168.0.24
1813+
827 2 mongoose.c:6753:onstatechange GW: 192.168.0.1
1814+
82d 2 mongoose.c:6755:onstatechange Lease: 86336 sec
1815+
bc3 2 main.c:65:main Ethernet: up
1816+
fab 2 main.c:65:main Ethernet: up
1817+
```
1818+
1819+
Done! Now, our automatic tests ensure that the firmware can be built, that is
1820+
it bootable, that it initialises the network stack correctly. This mechanism
1821+
can be easily extended: just add more complex actions in your firmware binary,
1822+
print the result to the UART, and check for the expected output in the test.
1823+
1824+
Happy testing!
1825+
16871826
16881827
## About the author
16891828

images/breadboard.webp

113 KB
Binary file not shown.

0 commit comments

Comments
 (0)