Skip to content

03 ‐ Building

gfo974 edited this page Jul 6, 2026 · 9 revisions

Environment Setup

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 everything

This 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.sh

Build Scripts

For 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//

Build with Bootloader (merged hex for deployment)

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 bootloader

Output files (tagged with git version):

  • <target>-v4.0.3.hex — Application only (no settings — do NOT flash directly)
  • <target>_app_settings-v4.0.3.hexApp + bootloader settings (for app-only re-flash)
  • <target>_merged-v4.0.3.hexApp + 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 --reset

Important: 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).

Directory Structure

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)

CMake Configuration

The firmware is built from ports/nrf52840/:

cd ports/nrf52840
mkdir build && cd build
cmake [OPTIONS] ..
make -j$(nproc)

Board Selection (BOARD)

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 ..

Sensor Enable Flags

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 ..

Other Build Options

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.

Battery Chemistry Profiles

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).

Available chemistries

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

How to select

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 default

To override (e.g. test bench with rechargeable Li-ion 18650):

BATTERY_CHEMISTRY=BATT_CHEM_NCR18650_3100_3400 ./scripts/build_linkitv4_smd.sh

Or via raw cmake:

cmake -DBATTERY_CHEMISTRY=BATT_CHEM_LS17500_2P -DBATTERY_MONITOR_TYPE=ANALOG ..

Invalid values are rejected at CMake configure time.

LS17500_2P profile (Saft Li-SOCl2 in parallel)

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 %

⚠️ Voltage-based SOC is intrinsically coarse for Li-SOCl2 (flat discharge curve). For fine-grained autonomy tracking, prefer 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.

Hardware compatibility

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.

Validation at first flash (before encapsulation)

After flashing the first device of a batch and before encapsulating the rest:

  1. Measure battery voltage at the connector with a multimeter
  2. Read BATT_VOLTAGE (DTE param POT06, mV) over BLE/USB after ~1 s of boot
  3. 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.

Adding a new chemistry

To add a new chemistry (different cell or different parallel/series count):

  1. Add the enum entry in ports/nrf52840/core/hardware/nrf_battery_mon.hpp (BatteryChemistry enum)
  2. Add the matching BatteryProfile{min_mv, max_mv, soc_lut[11]} entry in ports/nrf52840/core/hardware/nrf_battery_mon.cpp (battery_profiles[] array), in the same order as the enum
  3. Update the validation regex in ports/nrf52840/CMakeLists.txt (BATTERY_CHEMISTRY MATCHES line)

Constraints (enforced by static_assert and the LUT layout):

  • LUT must have exactly 11 entries (100 mV steps)
  • max_mv − min_mv must 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.

RSPB-Specific Options

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

Satellite / Communication Module

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.

LoRa-Specific Compile Flags (only with LORA_RAK3172=ON)

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. ⚠️ Turning it OFF is legal only for RF bench testing in a shielded environment — any production EU deployment must keep it ON.
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 ...

SMD Transport: SPI vs AT/UART

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 ...

Build Examples per Board

LinkIt V4 with KIM (default)

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)

LinkIt V4 with SMD

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)

LinkIt V4 with LoRa

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)

RSPB with SMD

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)

Additional Examples

 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 ..

Release build behaviour — debug output is OFF by default

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
#endif

Consequence 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.log flash 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.

Building Unit Tests

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 PressureSensorTest

The test build defines all ENABLE_* flags to 1 so all code paths are compiled and tested regardless of the target board configuration.

Impact of ENABLE_* Flags on Code

The ENABLE_* flags have a cascading effect through the codebase:

  1. core/protocol/base_types.hpp - ParamID enum members are #if guarded
  2. core/protocol/dte_params.cpp - param_map[] entries use the flag as is_implemented
  3. core/configuration/config_store.hpp - Default values are always present (slots reserved)
  4. core/services/ - Service classes are conditionally included
  5. 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.

CI Workflows (GitHub Actions)

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

Build Firmware workflow

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.

Tests workflow

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}/.

Adding a new test group

  1. Add mything_test.cpp under tests/src/ using TEST_GROUP(MyGroup).
  2. Register source in tests/CMakeLists.txt.
  3. The unit-tests job picks it up automatically (runs everything except SWS). Long-running groups: exclude via -xg <YourGroup>, run in own job.

Maintenance notes

  • 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 Jenkinsfile was deleted when GHA landed.

Clone this wiki locally