A framework for automated testing on Electro Smith Daisy (or similar) hardware
Explore the docs »
View Example Code
·
Report Bugs
·
Request Features
daisyHat aims to provide a framework for writing and executing automated tests for embedded hardware, with premium support for the Electro Smith Daisy platform. Tests can be run locally or in github actions and cover a wide range of test scenarios from simple one-device tests to complex hardware setups including custom measurement equipment and test fixtures.
daisyHat provides ...
- a device-side C++ library that comes with assertion macros, flow control and other tooling to write tests on the hardware,
- a host-side python package (
daisyhat) with a CLI to build the test firmware, upload firmware images to the hardware, collect test results and orchestrate the test run, - a Docker image that can be used to deploy ephemeral github actions runners that are safe to use on public repositories,
- setup scripts to prepare and install this Docker image on a Raspberry Pi 4B and turn it into the heart of a automated hardware testbed for github repositories.
WORK IN PROGRESS This project is in very early stages and not production ready. Don't expect everything to be plug-and-play yet. Please help out where you can!
- Tests live in a directory (the test root), typically a git repository, that contains a
tests/subdirectory and adaisyHat.config.jsonfile describing the hardware setup - Each test is a subdirectory of
tests/that contains aCMakeLists.txtmaking it a standalone CMake project - libDaisy and daisyHat are available to the test projects via the environment variables
LIBDAISY_DIRandDAISYHAT_DIR(e.g. pointing at checkouts / submodules inside the repository) - The
daisyhatpython package builds each test with CMake and runs the firmware on the hardware - A test can optionally provide a
daisyHatTest.pywith host-side orchestration hooks (flash firmware to specific seeds, multi-firmware tests, host-only tests) - GitHub actions integration is realised with an ephemeral test runner based on a docker image that can easily be deployed to a Raspberry Pi and is safe to use for public repositories
The layout of a test root:
<test-root>/
├── daisyHat.config.json # hardware setup (seed identifiers, ...)
└── tests/
├── test1/ # a test: standalone CMake project
│ ├── CMakeLists.txt
│ ├── daisyHatTest.py # (optional) host-side orchestration hooks
│ └── main.cpp
└── test2/
└── ...
The simplest test consists of a single firmware image that performs the entire test on a Daisy Seed. View more complex example code here.
In this repository, examples/ is a test root containing one test, test1.
examples/daisyHat.config.json describes the hardware setup. The example defines one Daisy Seed with the identifier Alice:
{
"version": 1,
"seeds": {
"Alice": {
"flash": {
"backend": "pyocd"
},
"serialDevice": "/dev/serial/by-id/usb-Electrosmith_Daisy_Seed_Built_In_346135793139-if00"
}
}
}examples/tests/test1/main.cpp:
#include <daisy_seed.h>
#include <daisyHat.h>
daisy::DaisySeed seed;
int main()
{
seed.Configure();
seed.Init();
daisyhat::Init(seed, "test1");
int a = 1;
int b = 1;
EXPECT_EQ(a, b);
daisyhat::FinishTest();
}examples/tests/test1/CMakeLists.txt:
cmake_minimum_required(VERSION 3.20)
project (test1)
# register the firmware with the libDaisy CMake firmware target
set(FIRMWARE_NAME test1)
set(FIRMWARE_SOURCES main.cpp)
include(${LIBDAISY_DIR}/cmake/DaisyDefaultBuild.cmake)
# link the daisyHat test library
add_subdirectory(${DAISYHAT_DIR} daisyhat)
target_link_libraries(${FIRMWARE_NAME} PRIVATE daisyHat)Run the test from the test root's parent directory (here: the repository root, where the .env file is). The first argument is the path to the test root folder (examples here — for your own projects it is typically . for the repository root), the second argument names the test to run (omit it to run all discovered tests):
daisyhat test <path-to-test-root> [test_name ...]
daisyhat test examples test1
Expected output (CMake and flashing output elided):
INFO: daisyHat config file path: examples/daisyHat.config.json
... configuring 'test1'
... building 'test1'
========== test 'test1' ==========
-----------------------------------------------------------------------
Flashing firmware images
-----------------------------------------------------------------------
... flashing to 'Alice': 'examples/tests/test1/.build/test1.elf'
<flashing progress output>
-----------------------------------------------------------------------
Starting test execution
-----------------------------------------------------------------------
... 'Alice'
-----------------------------------------------------------------------
Collecting test results
-----------------------------------------------------------------------
... 'Alice': Passed
Summary:
test1: PASSED
A failing test prints Failed in the result collection and a non-zero exit code:
Summary:
test2: FAILED
- Create a new repository for your tests
- Add
libDaisyas a submodule (e.g. inlib/libDaisy) - Add the daisyHat repo as a submodule (e.g. in
lib/daisyHat) - Add a
daisyHat.config.jsonat the repository root that describes your hardware setup (you can copy and edit this file) - For each test, create a new directory
tests/<testName>and add to it - Create a
.envfile at the repository root with the paths your tests need to build (see .env.example)
- Setup your toolchain: CMake, make, gcc-arm-none-eabi (as
TOOLCHAIN_PREFIX) and Python3 with pyocd (openocd is only required if your config uses theopenocdflash backend) - Install the daisyHat python package:
pip install <path-to-daisyHat> - Connect each Daisy Seed board via USB and via an STLink JTAG programmer
- Build and run the tests:
daisyhat test <path-to-test-root>daisyhat build <path-to-test-root>builds without runningdaisyhat clean <path-to-test-root>removes the test build directoriesdaisyhat test <path-to-test-root> --listshows the discovered tests
How to work on the daisyHat repository itself, testing it against real hardware with the self-tests in selftests/.
- Create a local python venv and install the package:
python3 -m venv .venv .venv/bin/pip install -e . - Create a
.envfile at the repository root from .env.example and fill inLIBDAISY_DIRandTOOLCHAIN_PREFIX(the CLI loads.envfrom the working directory automatically; values already set in the environment are not overridden) - Create a local config file
selftests/daisyHat.config.local.json(gitignored) describing your local hardware — machine specific serial device paths and probe IDs — and pointDAISYHAT_CONFIG_FILE_OVERRIDEin your.envfile to it - Find the ID of your USB debug probe:
.venv/bin/pyocd listshows all connected pyOCD-compatible probes with their unique IDs - Find the serial port path of the Daisy Seed:
ls /dev/cu.*on macOS (it appears as/dev/cu.usbmodem<serial number>once the seed has been flashed at least once) orls /dev/serial/by-id/on Linux (stable path derived from the USB serial number) - Connect the Daisy Seed via USB and via its debug probe, and run the self-tests:
(in VS Code, the
.venv/bin/daisyhat test selftestsdaisyhat: test (selftests)task does the same).venv/bin/daisyhat build selftestsbuilds without running.venv/bin/daisyhat clean selftestsremoves the test build directories- Troubleshooting flash errors: run with
DAISYHAT_LOG_LEVEL=DEBUG(full pyOCD logging incl. tracebacks);Pipe errors under flash load are typically a bad USB cable, hub or port, and a stuck probe USB handle requires unplugging the probe
- Prepare the target github repository by generating a personal access token to be able to register new github actions runners (see here)
- Install Ubuntu Server 20.04 on a Raspberry Pi 4B
- Download and execute the setup script (see here)
- Connect the Pi to each Daisy Seed board via USB
- Connect the Pi to each Daisy Seed board via an STLinkv3 JTAG programmer
Detailed instructions can be found here. Please note that the setup is currently not fully automated and still work in progress.
daisyhat/contains the python package (CLI, build & test orchestration, host-side runner)src/contains the C++ library for the device firmwaredocker/contains files to build the github action runner docker imageexamples/contains a usage example (a test root with two tests; one succeeds, one fails)scripts/contains scripts to setup and run a daisyHat test runnerdocs/contains documentation and guides
Contributions are what make the open source community such an amazing place to be learn, inspire, and create. Any contributions you make are greatly appreciated.
- Fork the Project
- Create your Feature Branch (
git checkout -b feature/AmazingFeature) - Commit your Changes (
git commit -m 'Add some AmazingFeature') - Push to the Branch (
git push origin feature/AmazingFeature) - Open a Pull Request