-
Notifications
You must be signed in to change notification settings - Fork 3
03 ‐ Building
Before building for the first time, run the setup script to install and configure all required tools:
./scripts/setup_environment.sh # interactive
./scripts/setup_environment.sh --auto # auto-install everythingThis installs:
- ARM GCC 10.3 (downloaded from ARM, not the apt version which has SDK compatibility issues)
- Build tools (make, cmake, ninja, crc32, xxd)
- nrfutil + nrf5sdk-tools plugin (for DFU package generation)
- nRF Command Line Tools (nrfjprog, mergehex, J-Link)
And generates build_config.sh in the project root with detected tool paths. All build scripts (scripts/build_*.sh) source this file automatically.
Alternatively, use the Docker image (all tools pre-installed):
docker build -t linkit-v4-build .
docker run --rm -v $(pwd):/workspace linkit-v4-build ./scripts/build_linkitv4_kim.shFor convenience, each board variant has a dedicated build script:
| Script | Board | Comm Module | Build Dir |
|---|---|---|---|
scripts/build_linkitv4_kim.sh |
LinkIt V4 | KIM | build/LINKIT/ |
scripts/build_linkitv4_smd.sh |
LinkIt V4 | SMD | build/LINKIT_SMD/ |
scripts/build_linkitv4_lora.sh |
LinkIt V4 | LoRa | build/LINKIT_LORA/ |
scripts/build_rspb.sh |
RSPB | SMD | build/RSPB/ |
scripts/build_unit_tests.sh |
- | - | tests/build/ |
scripts/build_with_bootloader.sh |
Any | Any | build// |
The build_with_bootloader.sh script builds the application, the bootloader, and merges everything (app + bootloader + SoftDevice + nrfutil settings) into a single .hex file ready to flash.
# Build any target with bootloader
./scripts/build_with_bootloader.sh rspb # RSPB Release
./scripts/build_with_bootloader.sh linkit-smd # LinkIt V4 SMD Release
./scripts/build_with_bootloader.sh linkit-kim # LinkIt V4 KIM Release
./scripts/build_with_bootloader.sh linkit-lora # LinkIt V4 LoRa Release
# Options
./scripts/build_with_bootloader.sh rspb --clean # Clean build first
./scripts/build_with_bootloader.sh rspb --debug # Debug mode (DEBUG_LEVEL=4)
./scripts/build_with_bootloader.sh rspb --no-merge # App only, skip bootloaderOutput files (tagged with git version):
-
<target>-v4.0.3.hex— Application only (no settings — do NOT flash directly) -
<target>_app_settings-v4.0.3.hex— App + bootloader settings (for app-only re-flash) -
<target>_merged-v4.0.3.hex— App + Bootloader + SoftDevice + settings (full flash) -
<target>_dfu-v4.0.3.zip— OTA DFU package
Flash commands:
# Full flash (first time or after recover):
nrfjprog --program <target>_merged-v4.0.3.hex --chiperase --verify --reset
# App-only re-flash (bootloader + softdevice already on device):
nrfjprog --program <target>_app_settings-v4.0.3.hex --sectorerase --verify --resetImportant: Always use
*_app_settings(not the raw*_board) for app-only flash. The bootloader uses ECDSA validation and requires the settings page to match the application. Flashing without settings causes yellow LED (bootloader stuck).
linkit-v4-core/
├── core/ # Portable firmware code (no hardware dependencies)
│ ├── configuration/ # ConfigStore, calibration, filesystem
│ ├── hardware/ # Abstract sensor/timer interfaces
│ ├── logging/ # Logger, LogFormatter, messages
│ ├── protocol/ # DTE protocol, encoder/decoder, params
│ └── services/ # All service implementations
├── ports/
│ └── nrf52840/ # nRF52840 hardware implementation
│ ├── bsp/ # Board Support Packages
│ │ ├── linkitv4_v1.0/ # LinkIt V4 board definitions
│ │ └── rspbtracker_v1.0/ # RSPB board definitions
│ ├── core/hardware/ # Hardware drivers (I2C, SPI, GPIO, sensors)
│ └── CMakeLists.txt # Main firmware build
├── tests/ # Unit tests (CppUTest)
│ ├── src/ # Test source files
│ └── CMakeLists.txt # Test build
└── docs/ # Configuration files (.cfg)
The firmware is built from ports/nrf52840/:
cd ports/nrf52840
mkdir build && cd build
cmake [OPTIONS] ..
make -j$(nproc)| Value | Description | BSP Directory |
|---|---|---|
LINKIT (default) |
LinkIt V4 v1.0 | bsp/linkitv4_v1.0/ |
RSPB |
RSPB Tracker v1.0 | bsp/rspbtracker_v1.0/ |
cmake -DBOARD=RSPB ..These flags control conditional compilation via #if ENABLE_* guards throughout the codebase. When disabled, the corresponding ParamIDs, DTE keys, services, and drivers are excluded from the build.
| Flag | Default | Effect |
|---|---|---|
ENABLE_AXL_SENSOR |
1 | BMA400 accelerometer (wakeup, activity) |
ENABLE_PRESSURE_SENSOR |
0 | LPS28DFW pressure/temperature/altitude |
ENABLE_THERMISTOR_SENSOR |
0 (LINKIT), 1 (RSPB) | NTC thermistor temperature |
ENABLE_PH_SENSOR |
0 | OEM pH sensor |
ENABLE_SEA_TEMP_SENSOR |
0 | RTD or TSYS01 sea temperature |
ENABLE_ALS_SENSOR |
0 | LTR-303 ambient light sensor |
ENABLE_CDT_SENSOR |
0 | Conductivity-Depth-Temperature |
ENABLE_CAM_SENSOR |
0 | Camera trigger |
ENABLE_MORTALITY_SENSOR |
0 (LINKIT), 1 (RSPB) | Bird mortality detection service |
cmake -DENABLE_PRESSURE_SENSOR=1 -DENABLE_SEA_TEMP_SENSOR=1 ..| Variable | Default | Description |
|---|---|---|
ARGOS_SMD |
OFF | Enable SMD satellite module (A+ protocol). Mutually exclusive with LORA_RAK3172. |
SMD_UART |
OFF (i.e. SPI) | Compile-time transport selector for the SMD module — when ON, drives SMD_UART=1 define and selects smd_sat_cmd_at.hpp; when OFF, drives SMD_SPI=1 and selects smd_sat_cmd_spi.cpp. Requires ARGOS_SMD=ON. |
SMDSAT_AUTOFALLBACK_ENABLED |
1 (auto when ARGOS_SMD=ON) |
Compile the FAST/SAFE timing autofallback machinery (consecutive-error counter, trust window, SMP00 persistence). Always ON in shipping builds — turn OFF only for SMD bring-up/RF lab work that needs deterministic FAST timings without runtime degradation. |
SMDSAT_USE_SAFE_TIMINGS |
0 | Force-start in SAFE timings on next boot. Useful for debug — operator can flash a build that ignores the FAST values entirely. Persistent SMP00 still wins if it says SAFE. |
LORA_RAK3172 |
OFF | Use RAK3172-SiP LoRa module instead of Argos satellite. Mutually exclusive with ARGOS_SMD. |
LORA_DCS_ENABLE |
ON | Enforce LoRaWAN duty-cycle on the RAK3172 (AT+DCS=1) per ETSI EN 300 220 (EU868: 1 % / 0.1 % / 10 % per sub-band). Turning OFF is legal only for shielded RF bench testing — never in production EU deployment.
|
LORA_POWER_OFF_UNDERWATER |
OFF | On every dive event, cut SAT_PWR_EN to 0 V (module 0 µA, ~2 s full boot on next surface). Default OFF keeps Stop2 standby (~1.7 µA, ~10 ms wake) for short turtle surfacings. Turn ON only when surfacing duration is reliably > 10 s. |
BATTERY_MONITOR_TYPE |
ANALOG (LINKIT), STC3117 (RSPB) | Battery monitor backend: ANALOG (nRF SAADC + voltage divider), FAKE (always 4.1 V), STC3117 (I²C fuel gauge). Discharge profile selected via BATTERY_CHEMISTRY (ANALOG only). |
BATTERY_CHEMISTRY |
BATT_CHEM_NCR18650_3100_3400 (cmake), BATT_CHEM_LS17500_2P (SMD/LoRa build scripts) |
Battery cell chemistry / discharge profile (ANALOG monitor only). See Battery Chemistry Profiles. |
GPS_FAKE_POSITION |
0 | Simulate GPS fix at Saint-Paul, Reunion |
GNSS_HAS_BACKUP_BATTERY |
0 | Enable GNSS fast path when V_BCKP coin cell is fitted. When set to 1, the M10Q skips full reconfiguration on power-on if BBR data is still valid, reducing TTFF by ~2-3 s. The driver validates BBR integrity by testing the high baud rate — if the coin cell is dead, it falls back to full configuration automatically. |
CAM_ENABLE |
0 | Enable external camera trigger on GPIO_EXT_GPIO4/5. Mutually exclusive with BUZZER_ENABLE (they share GPIO5). |
BUZZER_ENABLE |
0 | Enable buzzer on GPIO_EXT_GPIO5. Mutually exclusive with CAM_ENABLE. |
ENABLE_SWS_LOG |
0 | Compile the persistent SWS flash logger (SWSLogEntry written to LFS on every detector_state() call). Adds ~1 KB Flash + ~50 bytes RAM. Required for post-deployment analysis of SWS algorithm behavior. Retrieve via DTE $DUMPD. |
ENABLE_SWS_ANALOG |
1 (auto) | Compile the SWS analog detection chain. Auto-set to 1 unless BOARD=RSPB (where SWS is irrelevant for bird trackers). |
DEBUG_LEVEL |
3 | Debug verbosity (0 = off, 3 = trace). Compile-time gate on DEBUG_* macros. |
CMAKE_BUILD_TYPE |
Debug | Debug or Release. Release defines NDEBUG and forces g_debug_mode = NONE at startup — see Release build behaviour. |
EXTERNAL_WAKEUP |
(auto when BOARD=RSPB) |
TPL5111 periodic wakeup timer support. |
WAKEUP_PERIOD |
3600 (RSPB, build_rspb.sh) |
TPL5111 wakeup interval in seconds — MUST match the board's TPL5111 timing resistor. Sets the compiled default; still runtime-editable via DTE PWP04. Only effective with EXTERNAL_WAKEUP=1. |
RSPB_VSENSORS_FLOOR |
1 (RSPB, build_rspb.sh) |
Keep VSENSORS powered whenever the nRF runs (never let the sensor-power refcount reach 0). The RSPB I²C pull-ups (R21/R24) live on VSENSORS, so cutting it kills the whole bus. Set 0 only to test whether the pull-ups actually depend on VSENSORS. |
SMD_FLASH_HOLD |
OFF (RSPB, build_rspb.sh) |
TEST ONLY. At boot, power the SMD (SAT_PWR_EN high, PA off, SAT_RESET high-Z) and park the nRF — no Argos TX, no I²C recovery — so the STM32WL can be flashed over SWD without the board resetting. Never ship. |
The BATTERY_CHEMISTRY flag selects a per-chemistry discharge LUT (mV → SOC %) and voltage range. Used only when BATTERY_MONITOR_TYPE=ANALOG (no effect on STC3117 or FAKE).
| Value | Chemistry | Voltage range | Typical use |
|---|---|---|---|
BATT_CHEM_S18650_2600 |
Sony / Murata US18650VTC5 (Li-ion) | 3.2–4.2 V | Rechargeable Li-ion |
BATT_CHEM_CGR18650_2250 |
Panasonic CGR18650 (Li-ion) | 3.2–4.2 V | Rechargeable Li-ion |
BATT_CHEM_NCR18650_3100_3400 |
Panasonic NCR18650B (Li-ion) | 3.2–4.2 V | Rechargeable Li-ion (cmake default) |
BATT_CHEM_LS17500_2P |
2× Saft LS17500 in parallel (Li-SOCl2) | 2.7–3.7 V | Long-life primary, encapsulated wildlife trackers |
The build scripts default to BATT_CHEM_LS17500_2P for SMD and LoRa LinkIt V4 variants:
./scripts/build_linkitv4_smd.sh # uses LS17500_2P by default
./scripts/build_linkitv4_lora.sh # uses LS17500_2P by defaultTo override (e.g. test bench with rechargeable Li-ion 18650):
BATTERY_CHEMISTRY=BATT_CHEM_NCR18650_3100_3400 ./scripts/build_linkitv4_smd.shOr via raw cmake:
cmake -DBATTERY_CHEMISTRY=BATT_CHEM_LS17500_2P -DBATTERY_MONITOR_TYPE=ANALOG ..Invalid values are rejected at CMake configure time.
Two Saft LS17500 cells in parallel: 3.6 V nominal, ~7.2 Ah combined, fresh OCV ~3.65 V. Li-SOCl2 has a flat discharge plateau, so the LUT reports ~100 % until the plateau end (~3.55 V), then drops progressively to the cliff at ~3.0 V.
| Battery voltage | SOC reported |
|---|---|
| ≥ 3.65 V (fresh / plateau) | 99–100 % |
| 3.50 V (mid-plateau) | 95 % |
| 3.40 V (end of plateau) | 80 % |
| 3.30 V (entering knee) | 50 % |
| 3.20 V (knee) | 20 % |
| 3.15 V | ~12 % |
| 3.10 V (cliff) | 5 % |
| 3.00 V | 1 % |
| ≤ 2.90 V | 0 % |
BATT_VOLTAGE (POT06, mV) over BATT_SOC (POT03, %).
✅ Default thresholds map well to LS17500. The firmware defaults LB_THRESHOLD=10 % (triggers ~3.16 V, just before the cliff) and LB_CRITICAL_THRESH=5 % (triggers 3.10 V, cliff entry). No DTE param tuning required for LS17500 deployments.
The nRF SAADC chain on LinkIt V4 (gain 1/5, internal ref 0.6 V, hardware divider V_DIV_GAIN=1.443) accepts 0–4.33 V on the battery rail. Both Li-ion (4.2 V max) and LS17500 (3.7 V max) fit comfortably without clipping. ADC resolution ≈ 0.26 mV/LSB.
After flashing the first device of a batch and before encapsulating the rest:
- Measure battery voltage at the connector with a multimeter
- Read
BATT_VOLTAGE(DTE paramPOT06, mV) over BLE/USB after ~1 s of boot - Compare — tolerance ±20 mV
A larger discrepancy means V_DIV_GAIN (BSP) needs recalibration before encapsulating the rest. Once encapsulated, you cannot re-flash easily — validate every batch.
To add a new chemistry (different cell or different parallel/series count):
- Add the enum entry in
ports/nrf52840/core/hardware/nrf_battery_mon.hpp(BatteryChemistryenum) - Add the matching
BatteryProfile{min_mv, max_mv, soc_lut[11]}entry inports/nrf52840/core/hardware/nrf_battery_mon.cpp(battery_profiles[]array), in the same order as the enum - Update the validation regex in
ports/nrf52840/CMakeLists.txt(BATTERY_CHEMISTRYMATCHES line)
Constraints (enforced by static_assert and the LUT layout):
- LUT must have exactly 11 entries (100 mV steps)
-
max_mv − min_mvmust equal 1000 - Index 0 =
max_mv= highest SOC; index 10 =min_mv= 0 % SOC
The static_assert in nrf_battery_mon.cpp catches any enum/array size mismatch at compile time.
When BOARD=RSPB, these are automatically set:
| Variable | Value | Description |
|---|---|---|
EXTERNAL_WAKEUP |
1 | TPL5111 periodic wakeup timer |
WAKEUP_PERIOD |
3600 | Wakeup interval in seconds (~1h) — build_rspb.sh default; must match the TPL5111 resistor |
ENABLE_THERMISTOR_SENSOR |
1 | NTC thermistor enabled |
ENABLE_MORTALITY_SENSOR |
1 | Bird mortality detection |
BATTERY_MONITOR_TYPE |
STC3117 | I2C fuel gauge |
| Option | Description |
|---|---|
ARGOS_SMD=ON |
Use Arribada SMD satellite module (SPI by default, A+ protocol) |
ARGOS_SMD=ON + SMD_UART=ON
|
Use Arribada SMD satellite module (AT/UART commands) |
LORA_RAK3172=ON |
Use RAK3172-SiP LoRa module (UART, LoRaWAN 1.0.3) |
| (neither) | Default: KIM2 module (CLS, UART, legacy Argos) |
Only one communication module can be active at a time. ARGOS_SMD and LORA_RAK3172 are mutually exclusive.
| Option | Default | Description |
|---|---|---|
LORA_DCS_ENABLE=ON/OFF |
ON | Enforce LoRaWAN duty-cycle on the RAK3172 (EU868: 1 % / 0.1 % / 10 % per sub-band, per ETSI EN 300 220). When ON, the driver sends AT+DCS=1 during configure; when OFF, sends AT+DCS=0. |
LORA_POWER_OFF_UNDERWATER=ON/OFF |
OFF | On every dive event, cut SAT_PWR_EN to 0 V → module at 0 µA instead of ~1.7 µA Stop2 standby. Saves ~14 mAh/year on a turtle spending 95 % of life submerged, at the cost of ~2 s full boot (configure + join if OTAA) on the next surfacing. Default OFF keeps the fast 10 ms wake needed for short surfacings (sea turtle, diving bird). Turn ON only when surfacing duration is reliably > 10 s. |
Example: LoRa build with DCS ON, standby preserved during dives (default)
cmake -DBOARD=LINKIT -DLORA_RAK3172=ON ...
Example: LoRa build with aggressive power-off during dives
cmake -DBOARD=LINKIT -DLORA_RAK3172=ON -DLORA_POWER_OFF_UNDERWATER=ON ...
Example: LoRa build with DCS disabled (RF lab test only!)
cmake -DBOARD=LINKIT -DLORA_RAK3172=ON -DLORA_DCS_ENABLE=OFF ...The SMD module supports two transport layers, selected at compile time:
| SPI (default) | AT/UART (SMD_UART=ON) |
|
|---|---|---|
| Protocol | Binary Protocol A+ (64-byte full-duplex frames) | Text-based AT commands (AT+CMD=<params>\r\n) |
| Interface | SPI Master (125kHz, Mode 0) | Async UART |
| CMake flag | Default (no extra flag needed) | -DSMD_UART=ON |
| Compile define | SMD_SPI=1 |
SMD_UART=1 |
Both transports implement the same abstract SmdSatCmd interface (ports/nrf52840/core/hardware/smd_sat/smd_sat_cmd.hpp), so the rest of the firmware is transport-agnostic.
SPI mode (default)
cmake -DBOARD=LINKIT -DARGOS_SMD=ON ...
AT/UART mode
cmake -DBOARD=LINKIT -DARGOS_SMD=ON -DSMD_UART=ON ...mkdir -p ports/nrf52840/build/LINKIT && cd ports/nrf52840/build/LINKIT
cmake -DCMAKE_TOOLCHAIN_FILE=../../toolchain_arm_gcc_nrf52.cmake \
-DBOARD=LINKIT -DENABLE_AXL_SENSOR=ON \
-DCMAKE_BUILD_TYPE=Debug -DDEBUG_LEVEL=4 ../..
make -j$(nproc)mkdir -p ports/nrf52840/build/LINKIT_SMD && cd ports/nrf52840/build/LINKIT_SMD
cmake -DCMAKE_TOOLCHAIN_FILE=../../toolchain_arm_gcc_nrf52.cmake \
-DBOARD=LINKIT -DARGOS_SMD=ON -DENABLE_AXL_SENSOR=ON \
-DCMAKE_BUILD_TYPE=Debug -DDEBUG_LEVEL=4 ../..
make -j$(nproc)mkdir -p ports/nrf52840/build/LINKIT_LORA && cd ports/nrf52840/build/LINKIT_LORA
cmake -DCMAKE_TOOLCHAIN_FILE=../../toolchain_arm_gcc_nrf52.cmake \
-DBOARD=LINKIT -DLORA_RAK3172=ON -DENABLE_AXL_SENSOR=ON \
-DCMAKE_BUILD_TYPE=Debug -DDEBUG_LEVEL=4 ../..
make -j$(nproc)mkdir -p ports/nrf52840/build/RSPB && cd ports/nrf52840/build/RSPB
cmake -DCMAKE_TOOLCHAIN_FILE=../../toolchain_arm_gcc_nrf52.cmake \
-DBOARD=RSPB -DARGOS_SMD=ON \
-DENABLE_PRESSURE_SENSOR=ON -DENABLE_AXL_SENSOR=ON \
-DCMAKE_BUILD_TYPE=Debug -DDEBUG_LEVEL=4 ../..
make -j$(nproc) LinkIt V4 with pressure sensor (KIM)
cmake ... -DENABLE_PRESSURE_SENSOR=1 ..
LinkIt V4 with all sensors
cmake ... \
-DENABLE_PRESSURE_SENSOR=1 \
-DENABLE_SEA_TEMP_SENSOR=1 \
-DENABLE_PH_SENSOR=1 \
-DENABLE_ALS_SENSOR=1 \
-DENABLE_CDT_SENSOR=1 ..
Release build for production
cmake ... -DCMAKE_BUILD_TYPE=Release ..When the firmware is built with CMAKE_BUILD_TYPE=Release (i.e. NDEBUG defined by CMake), the default runtime debug channel is forced to NONE at startup:
// ports/nrf52840/main.cpp
#ifdef NDEBUG
BaseDebugMode g_debug_mode = BaseDebugMode::NONE; // Release: silent on USB/UART/BLE
#elif defined(BOARD_RSPB)
BaseDebugMode g_debug_mode = BaseDebugMode::UART; // Debug RSPB → UART
#else
BaseDebugMode g_debug_mode = BaseDebugMode::USB_CDC; // Debug LinkIt → USB CDC
#endifConsequence in field deployment:
- USB CDC enumeration is suppressed — if you plug a Release-built device into a host, no virtual serial port appears. Prevents host OS interference, electrical risk (epoxied tag in salt water + USB host), and idle current from USB-D+ pull-up.
- UART debug TX line stays idle — no current on the debug pin.
- BLE NUS data characteristic produces no debug stream — only DTE responses to explicit requests.
-
system.logflash logging is unaffected — the on-flash CSV log under LFS keeps writing normally, retrievable via BLE+DTE in configuration mode.
To override at runtime (e.g. for a Release device that needs temporary diagnostics):
PARMW,DBP01,1 # Force USB_CDC output even in Release build
PARMW,DBP01,2 # Force BLE_NUS output
PARMW,DBP01,3 # Confirm NONE (default for Release)
The override persists in flash via DEBUG_OUTPUT_MODE (DBP01). See 09 — Parameters § Debug for details.
Idle current saving : with NONE, the USB-D+ enumeration and the BLE NUS data pipe are not initialised at boot — the device sleeps at ~10 µA instead of ~1.9 mA on a USB-tethered Debug build. Critical for the 1-year sealed mission budget.
For detailed board-specific documentation see Board Variants.
Tests are built separately with CppUTest:
cd tests
mkdir build && cd build
cmake ..
make -j$(nproc)
Run all tests
ctest --output-on-failure
Run specific test
./TrackerTests -g PressureSensorTestThe test build defines all ENABLE_* flags to 1 so all code paths are compiled and tested regardless of the target board configuration.
The ENABLE_* flags have a cascading effect through the codebase:
-
core/protocol/base_types.hpp- ParamID enum members are#ifguarded -
core/protocol/dte_params.cpp-param_map[]entries use the flag asis_implemented -
core/configuration/config_store.hpp- Default values are always present (slots reserved) -
core/services/- Service classes are conditionally included -
ports/nrf52840/- Hardware drivers are conditionally compiled
Slots in the ParamID enum and config_store.hpp defaults array are always reserved even when a sensor is disabled. This ensures stable parameter indexing across builds with different sensor configurations.
Two workflows live in .github/workflows/:
| Workflow | File | Trigger | Purpose |
|---|---|---|---|
| Build Firmware | build.yml |
Manual (workflow_dispatch) |
Compile a fully customized firmware variant and download artifacts |
| Tests | tests.yml |
push / pull_request to main, manual |
Run unit tests, SWS group, turtle simulation |
Pick a config from the Actions UI (Run workflow). Inputs:
-
Board (
LINKIT/RSPB), Comm module (KIM/SMD_SPI/SMD_UART/LORA) -
Sensors: independent checkboxes for
AXL,PRESSURE,THERMISTOR,SEA_TEMP,ALS,CDT,PH,MORTALITY,SWS_LOG,GNSS_BBR,CAM -
System features:
BUZZER,BATTERY_MONITOR_TYPE,LORA_DCS,LORA_POWER_OFF_UNDERWATER,DEBUG_LEVEL
Validation rejects invalid combinations before launching cmake (e.g. comm=LORA with board=RSPB, thermistor + sea_temp both true, mortality=true without axl/thermistor).
Requires NRFUTIL_PKG_KEY_PEM repository secret for signed merged firmware. Without it the workflow still produces *.hex / *.elf / *_dfu.zip but skips *_merged.hex and *_app_settings.hex.
Artifacts (named <variant>-<TAG>):
-
LinkIt_board-<TAG>.hex— application only (nrfjprog --sectorerase) -
LinkIt_board_merged-<TAG>.hex— app + bootloader + softdevice + settings (--chiperase) -
LinkIt_board_app_settings-<TAG>.hex— app + settings -
LinkIt_board_dfu-<TAG>.zip— signed DFU OTA package -
LinkIt_board-<TAG>.elf— symbol file -
build_config.txt— full record of CMake flags + commit hash + date
Uses the project Dockerfile (Ubuntu 22.04 + ARM GCC 10.3 + nrf-command-line-tools + nrfutil + mergehex) with GHA layer cache.
Three jobs run in parallel after a shared compile step:
| Job | Command |
|---|---|
| Build host test binaries | cmake -GNinja && ninja TrackerTests TurtleSimulation |
| Unit tests |
./TrackerTests -ojunit -p -xg SWS (all groups except SWS, -p isolates each test) |
| SWS tests |
./TrackerTests -p -g SWSAnalog and -g SWSAnalogFlash
|
| Turtle simulation |
./TurtleSimulation -v (1-year mission, emits HTML report) |
concurrency cancels in-progress runs on the same ref. JUnit reports under tests/reports/{unit,sws}/.
- Add
mything_test.cppundertests/src/usingTEST_GROUP(MyGroup). - Register source in
tests/CMakeLists.txt. - The unit-tests job picks it up automatically (runs everything except
SWS). Long-running groups: exclude via-xg <YourGroup>, run in own job.
- Updating the Dockerfile rebuilds the image once and re-caches it.
- GitHub workflow input limit: 25 per
workflow_dispatch(currently 18 used). - Removed: the legacy
Jenkinsfilewas deleted when GHA landed.