Source: docs/firmware.md
Firmware
The full guide is in firmware/README.md. This page summarises it.
Layout
| Path | What |
|---|---|
firmware/core/ |
Portable C99 port of the controller, geometry and protocol (ht_core.h/.c). No malloc, fixed buffers |
firmware/core/test/ |
Golden-vector test. vectors.h is generated from Python |
firmware/tag/ |
Arduino sketch for the RAK4631 (tag.ino), pin map (pins.h), GNSS interface (gnss_hal.h plus the RAK12500 implementation), secrets.example.h, and copies of the core |
Build and test on a laptop
cd ~/herdtag
make -C firmware/core test # float + double builds; fails if tag/ copies of the core are stale
make -C firmware/core vectors # regenerate test/vectors.h (automatic when the Python sources change)
make -C firmware/core sync # copy core into the sketch after editing it
Expected output ends with 3630 checks, 0 failures.
Golden-vector workflow
Python (src/herdtag/controller.py, geo.py, protocol.py) is the specification.
- Change the behaviour in Python, and update the Python tests (
uv run pytest). - Regenerate the vectors.
scripts/gen_vectors.pyruns 11 scenarios through the Python controller and records every decision: approach, walk-away, degraded fix, breach/return, breach through a degraded fix, both caps, sampling, motion gate, fence swap, and a 400-step noisy walk. It also records uplink bytes and fence fragments. - Change
firmware/core/ht_core.cuntilmake -C firmware/core testpasses. make -C firmware/core syncto update the sketch's copies.
Intentional differences from Python are bounded buffers, double time with float geometry, integer-first lat/lon conversion, and events as bit flags. They're listed in the firmware README.
Flashing a tag
The sketch has never been compiled for the board or flashed. Expect library-version fixes during the first bring-up session.
- In the Arduino IDE, add the RAKwireless board index and install RAKwireless nRF Boards, then select WisBlock RAK4631.
- Install the libraries SX126x-Arduino, SparkFun u-blox GNSS v2 and SparkFun LIS3DH.
cp firmware/tag/secrets.example.h firmware/tag/secrets.hand fill in DevEUI, JoinEUI and AppKey from The Things Stack.secrets.his gitignored.- Upload
tag.ino, open Serial at 115200, and check the I2C scan shows 0x42 (GNSS) plus 0x18 or 0x19 (accelerometer). Then expectjoinedand one line per fix.
The pin map in pins.h comes from hardware/BUILD.md §3.
What the tag does
- OTAA join on US915 sub-band 2, Class A, ADR from DR0.
- Sleeps until the controller's next fix or the next uplink.
- Takes a GNSS fix with a 45 s timeout. With no fix it reports
h_acc = 999, so the controller goes degraded and stays silent. - Plays a fixed-volume 3 kHz pattern for each cue burst.
- Uplinks:
- every 15 minutes
- immediately on
breachorcue_ignored - immediately to acknowledge a new fence
- every 30 s while a fence download is incomplete
- Saves the fence to flash, so it survives a reboot.
Known gaps:
- GNSS power cycling (
gnss_sleep/gnss_wake) does nothing yet. - Battery % uses a linear approximation.
- Solar charge state isn't reported.
- No on-tag high-rate logging for trials yet.
- RAK12500 is being discontinued. The RAK12501 fallback needs a new
gnss_hal_rak12501.cpp.