End-users never need to read this. It documents how a Bert maintainer
publishes new prebuilt firmware so that bert flash-firmware can download
it on demand.
| role | source | shipped as | lives in GitHub Release |
|---|---|---|---|
hci |
Zephyr samples/bluetooth/hci_uart for board nrf52840dongle/nrf52840 |
hci_uart_nrf52840dongle.hex |
yes |
sniffer |
Nordic's nRF Sniffer for Bluetooth LE | sniffer_nrf52840dongle_nrf52840_<v>.zip |
no — Bert auto-discovers Nordic's bundled copy under ~/.nrfutil/share/nrfutil-ble-sniffer/firmware/ |
We don't redistribute Nordic's sniffer firmware; users install it once via
nrfutil install ble-sniffer and Bert finds it where Nordic puts it.
-
Trigger the build. Either push a tag matching
firmware-*, or use the Actions → build-firmware → Run workflow UI:git tag firmware-2026.05.06 git push origin firmware-2026.05.06
The
.github/workflows/build-firmware.ymlworkflow:- Runs inside
ghcr.io/zephyrproject-rtos/ci. - Initialises a fresh Zephyr workspace at the revision in the tag's
workflow input (default
v3.7.0). - Builds
hci_uartfornrf52840dongle/nrf52840. - Uploads
hci_uart_nrf52840dongle.hexas a release asset. - Echoes the SHA256 + size into the run summary and release notes.
- Runs inside
-
Update the Bert manifest to point at the new release. From a Bert checkout:
scripts/update_manifest.py \ --owner <gh-owner> --repo bert \ --tag firmware-2026.05.06This fetches the asset, hashes it, and rewrites
src/bert/firmware/manifest.jsonwith the new tag, URL, SHA256, and size. -
Commit the manifest change and cut a new Bert release in the usual way (bump version in
pyproject.toml, tag, push, publish to PyPI). Once end-userspip install -U bert-ble-tester, the nextbert flash-firmwarewill pick up the new firmware on first run and cache it locally.
If you need to produce a hex outside CI:
docker run --rm -it -v "$PWD/out:/out" ghcr.io/zephyrproject-rtos/ci:v0.27.4 bash -c '
mkdir -p /workspace && cd /workspace &&
west init -m https://github.com/zephyrproject-rtos/zephyr --mr v3.7.0 . &&
west update --narrow -o=--depth=1 &&
west zephyr-export &&
pip install -r zephyr/scripts/requirements.txt &&
west build -b nrf52840dongle/nrf52840 \
-d build-hci-dongle zephyr/samples/bluetooth/hci_uart &&
cp build-hci-dongle/zephyr/zephyr.hex /out/hci_uart_nrf52840dongle.hex
'
sha256sum out/hci_uart_nrf52840dongle.hexUpload the hex to a GitHub Release manually (or via gh release upload),
then run scripts/update_manifest.py as in step 2 above.
The runtime side (bert.adapters.firmware_fetch) verifies SHA256 against
the manifest before flashing. A mismatch implies one of:
- the manifest is stale relative to the release (impossible if you used the script above — fix by re-running it);
- the GitHub asset was tampered (rotate the release);
- the user's cache is corrupt (
bert firmware clear-cacheclears it).
End-users see clear diagnostics from bert firmware verify and
bert firmware download --force.
We could read SHA256 from a sibling .sha256 file at flash time, but that
trusts the same network connection that delivers the binary. Pinning the
hash inside the Bert package makes the tool's idea of "the right firmware"
travel with the Bert version: a known-good Bert build can never be tricked
into flashing a substituted hex, even if the GitHub Release is compromised
later.