Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
692dcf8
repo: add .gitattributes (LF for board/container files, binary list)
dorkmo Oct 10, 2026
6f6bc44
harness: refuse DTS legs on the 915 MHz default carrier, reject missp…
dorkmo Oct 10, 2026
562efdd
bench_tools: requirements-pc.txt for the PC-side bench tooling
dorkmo Oct 10, 2026
01c8539
security: take the WiFi SSID/password out of the repo-root scripts
dorkmo Oct 10, 2026
dc5b9bb
bench kit (K6): BENCH_QUICKSTART.md entry point + PC_SETUP.md
dorkmo Oct 10, 2026
b863994
bench kit (K6): runbook web UI recipe + quickstart pointers
dorkmo Oct 10, 2026
0b211af
flash pipeline: REVIVE_MODE defaults to reboot; staging preflight; fl…
dorkmo Oct 10, 2026
63e8692
docs: fix board, script-name and hardware errors found by the bench a…
dorkmo Oct 10, 2026
0cfbe4f
docs(x8_lora_bootloader_helper): README flags the misnamed L072 binaries
dorkmo Oct 10, 2026
14a5532
FLASH_RUNBOOK: first flash from a factory board, toolchain/md5s, new …
dorkmo Oct 10, 2026
c066e36
murata_l072/README: Building section with the exact toolchain and md5s
dorkmo Oct 10, 2026
1e0342a
ci: non-blocking L072 bench md5 job (Arm GNU Toolchain 12.2.MPACBTI-R…
dorkmo Oct 10, 2026
0ed259b
bench kit(K2): provision_bench_board.sh + the tractor's disable-wifi …
dorkmo Oct 10, 2026
3d7fa4d
bench kit(K2): BENCH_SETUP.md, two new boards to a bench-ready pair
dorkmo Oct 10, 2026
3df6d2e
run_flash_bench.sh: CRLF preflight without cmp
dorkmo Oct 10, 2026
8ee94c7
T3a: pin the reflash image by URL + sha256, add download/extract/firs…
dorkmo Oct 10, 2026
60eb039
BENCH_BOARDS: the 674 image is hosted; point at BENCH_SETUP and the p…
dorkmo Oct 10, 2026
ef88041
BENCH_SETUP: REVIVE_MODE=reboot is what FLASH_RUNBOOK passes, not a w…
dorkmo Oct 10, 2026
0caf4e1
bench kit: bench.env settings and lib/bench_env.sh shared helpers
dorkmo Oct 10, 2026
f53d43f
bench kit: parameterised RS-13 leg scripts in bench_tools/legs
dorkmo Oct 10, 2026
835e580
bench kit (K3): lock files for the flown images + optional LOCKED=1 b…
dorkmo Oct 10, 2026
cfc7ade
bench kit: legs/README run order + RS13_VECTOR_LEG pointer
dorkmo Oct 10, 2026
1a93960
bench kit (K3): deploy_base.sh and build_tractor_image.sh
dorkmo Oct 10, 2026
151e7da
bench kit (K3): DEPLOY.md for the base deploy and the tractor image
dorkmo Oct 10, 2026
f8a18d5
Merge kit/k1-hygiene into bench-kit-2026-10-10
dorkmo Oct 10, 2026
0509733
Merge kit/k2-board-setup into bench-kit-2026-10-10
dorkmo Oct 10, 2026
bb43fcf
Merge kit/k3-deploy into bench-kit-2026-10-10
dorkmo Oct 10, 2026
ac1a8c0
Merge kit/k4-legs into bench-kit-2026-10-10
dorkmo Oct 10, 2026
b28c866
Merge kit/k5-harness-flash into bench-kit-2026-10-10
dorkmo Oct 10, 2026
cc88454
Merge kit/k6-quickstart into bench-kit-2026-10-10
dorkmo Oct 10, 2026
e4f5a05
bench kit: one set of bench.env names across the setup, deploy, flash…
dorkmo Oct 10, 2026
6ff707d
bench kit docs: fix the review findings in the quickstart, setup and …
dorkmo Oct 10, 2026
937e359
bench kit: one bench.env loader with one precedence rule; scp gets MS…
dorkmo Oct 10, 2026
67a3972
bench legs: required -HostIp, validated tags, cleanup traps, cleared …
dorkmo Oct 10, 2026
e14cd3c
bench PC scripts: clear adb error; bench window gets "f" only when in…
dorkmo Oct 10, 2026
d5edc5a
WiFi: no PSK on any command line; remove the PSK-carrying VS Code tasks
dorkmo Oct 10, 2026
810705d
bench-evidence: escape the raw CR byte in step1_pass_fix.sh (no behav…
dorkmo Oct 10, 2026
78ea975
Merge branch 'kitfix/scripts' into kitfix/verify
dorkmo Oct 10, 2026
07fb421
bench kit docs: align with the scripts pass (precedence, archive path…
dorkmo Oct 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 70 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Line-ending and binary rules for the LifeTrac repo.
#
# Why: this repo is mostly edited on Windows (core.autocrlf=true), but many
# files run on the Portenta X8 boards or inside Linux containers, where a CRLF
# breaks them ("/bin/bash^M: bad interpreter", systemd/udev/mosquitto parse
# errors, `source`d env files with a trailing \r in every value). The rules
# below make Git check those files out with LF on every OS and make
# `git archive` emit LF for them even when core.autocrlf=true.
#
# The index already stores these files with LF, so this file changes checkout
# behaviour only. An EXISTING working tree keeps its old copies until the files
# are checked out again. With a clean tree (commit or set aside your work
# first -- this rewrites every tracked file):
# git rm --cached -r -q . && git reset --hard
# On a Windows checkout made with core.autocrlf=true that refresh also repairs
# binaries that autocrlf had mangled on checkout (some *.pdf, *.hex, *.zip and
# bench-evidence *.bin files whose content Git guessed was text).
# Board-side deploys should still pass `-c core.autocrlf=false` to
# `git archive`, because files not listed here (.txt, .md, .json, .html, ...)
# follow core.autocrlf.
#
# Files not matched below keep the default behaviour (core.autocrlf).

# --- Runs on the boards / in containers: always LF -------------------------
*.sh text eol=lf
*.bash text eol=lf
*.py text eol=lf
*.service text eol=lf
*.timer text eol=lf
*.socket text eol=lf
*.rules text eol=lf
*.conf text eol=lf
*.cfg text eol=lf
*.yml text eol=lf
*.yaml text eol=lf
*.toml text eol=lf
Dockerfile text eol=lf
Dockerfile.* text eol=lf
*.dockerfile text eol=lf
.env.example text eol=lf
*.env text eol=lf
*.env.example text eol=lf
requirements*.txt text eol=lf

# --- Windows scripts: CRLF in the working tree ------------------------------
# text=auto leaves anything Git detects as binary (e.g. a UTF-16 file) alone.
*.ps1 text=auto eol=crlf
*.bat text=auto eol=crlf

# --- Binary: never convert, never text-diff/merge ---------------------------
*.bin binary
*.elf binary
*.hex binary
*.o binary
*.a binary
*.exe binary
*.dll binary
*.so binary
*.whl binary
*.tar binary
*.tgz binary
*.gz binary
*.xz binary
*.zip binary
*.jpg binary
*.jpeg binary
*.png binary
*.gif binary
*.pdf binary
*.rgb binary
123 changes: 123 additions & 0 deletions .github/workflows/l072-bench-md5.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
# L072 bench md5 -- NON-BLOCKING reproducibility check (bench kit, 2026-10-10).
#
# Rebuilds the Murata L072 firmware with the exact toolchain the bench PC uses
# (Arm GNU Toolchain 12.2.MPACBTI-Rel1, arm-none-eabi-gcc 12.2.1 20230214) and
# prints the md5s of the bench and production images next to the reference
# values in LifeTrac-v25/DESIGN-CONTROLLER/firmware/murata_l072/README.md
# ("Expected md5s") and the committed build/firmware.bin. Mismatches are
# warnings, never failures: the job is continue-on-error and the md5 step
# always exits 0. The blocking link/size gate stays in arduino-ci.yml.
name: L072 bench md5 (non-blocking)

on:
push:
paths:
- 'LifeTrac-v25/DESIGN-CONTROLLER/firmware/murata_l072/**'
- '.github/workflows/l072-bench-md5.yml'
pull_request:
paths:
- 'LifeTrac-v25/DESIGN-CONTROLLER/firmware/murata_l072/**'
- '.github/workflows/l072-bench-md5.yml'
workflow_dispatch:

permissions:
contents: read

env:
# Linux x86_64 host build of the release the bench PC uses. URL and sha256
# checked 2026-10-10 against Arm's published .sha256asc (170 644 784 B).
ARM_TOOLCHAIN_URL: https://developer.arm.com/-/media/Files/downloads/gnu/12.2.mpacbti-rel1/binrel/arm-gnu-toolchain-12.2.mpacbti-rel1-x86_64-arm-none-eabi.tar.xz
ARM_TOOLCHAIN_SHA256: 17455a06c816031cc2c66243c117cba48463cd6a3a3fdfac7275b4e9c40eb314
# murata_l072/README.md "Expected md5s" (main bb4a2071). Update both when the
# firmware source changes and the new pair is recorded.
EXPECTED_BENCH_MD5: 0c1bb0a9573f813137f941dfa47177d0
EXPECTED_PROD_MD5: 589c120323c2d5e7ef9f459d7a4ba42d
L072_DIR: LifeTrac-v25/DESIGN-CONTROLLER/firmware/murata_l072

jobs:
l072-bench-md5:
name: L072 bench md5 (Arm 12.2.MPACBTI-Rel1)
runs-on: ubuntu-24.04
continue-on-error: true
timeout-minutes: 20
steps:
- uses: actions/checkout@v4

- name: Cache the Arm toolchain
uses: actions/cache@v4
with:
path: ~/arm-gnu-toolchain-12.2.mpacbti-rel1
key: arm-gnu-toolchain-12.2.mpacbti-rel1-x86_64-${{ env.ARM_TOOLCHAIN_SHA256 }}

- name: Install Arm GNU Toolchain 12.2.MPACBTI-Rel1
run: |
set -euo pipefail
TC="$HOME/arm-gnu-toolchain-12.2.mpacbti-rel1"
if [ ! -x "$TC/bin/arm-none-eabi-gcc" ]; then
curl -fsSL --retry 3 -o "$RUNNER_TEMP/tc.tar.xz" "$ARM_TOOLCHAIN_URL"
echo "$ARM_TOOLCHAIN_SHA256 $RUNNER_TEMP/tc.tar.xz" | sha256sum -c -
mkdir -p "$TC"
tar -xJf "$RUNNER_TEMP/tc.tar.xz" -C "$TC" --strip-components=1
rm -f "$RUNNER_TEMP/tc.tar.xz"
fi
echo "$TC/bin" >> "$GITHUB_PATH"
"$TC/bin/arm-none-eabi-gcc" --version | head -1

- name: Record the committed production md5
working-directory: ${{ env.L072_DIR }}
run: md5sum build/firmware.bin | cut -d' ' -f1 | tee "$RUNNER_TEMP/committed_prod.md5"

- name: make bench
working-directory: ${{ env.L072_DIR }}
run: |
arm-none-eabi-gcc --version | head -1
mkdir -p build # `bench` writes build/firmware_bench_diag.bin but does not create build/
make bench

- name: make (production)
working-directory: ${{ env.L072_DIR }}
run: make

- name: Compare md5s with the README (warn only)
working-directory: ${{ env.L072_DIR }}
run: |
bench=$(md5sum build/firmware_bench_diag.bin | cut -d' ' -f1)
prod=$(md5sum build/firmware.bin | cut -d' ' -f1)
committed=$(cat "$RUNNER_TEMP/committed_prod.md5")
verdict() { [ "$1" = "$2" ] && echo "match" || echo "**differs**"; }
{
echo "## L072 md5 -- Arm GNU Toolchain 12.2.MPACBTI-Rel1 on Linux"
echo
echo "| image | built here | reference | |"
echo "|---|---|---|---|"
echo "| bench \`firmware_bench_diag.bin\` | \`$bench\` | README \`$EXPECTED_BENCH_MD5\` | $(verdict "$bench" "$EXPECTED_BENCH_MD5") |"
echo "| production \`firmware.bin\` | \`$prod\` | README \`$EXPECTED_PROD_MD5\` | $(verdict "$prod" "$EXPECTED_PROD_MD5") |"
echo "| production \`firmware.bin\` | \`$prod\` | committed \`$committed\` | $(verdict "$prod" "$committed") |"
echo
echo "A source change since bb4a2071 moves both README md5s; record the new"
echo "pair in murata_l072/README.md and in this workflow's env."
} >> "$GITHUB_STEP_SUMMARY"
echo "bench built=$bench expected=$EXPECTED_BENCH_MD5"
echo "production built=$prod expected=$EXPECTED_PROD_MD5 committed=$committed"
if [ "$bench" != "$EXPECTED_BENCH_MD5" ]; then
echo "::warning title=L072 bench md5 differs::firmware_bench_diag.bin md5 $bench != README $EXPECTED_BENCH_MD5 (source changed since bb4a2071, or this Linux build of 12.2.MPACBTI-Rel1 is not byte-identical to the bench PC build)"
fi
if [ "$prod" != "$EXPECTED_PROD_MD5" ]; then
echo "::warning title=L072 production md5 differs::firmware.bin md5 $prod != README $EXPECTED_PROD_MD5"
fi
if [ "$prod" != "$committed" ]; then
echo "::warning title=Committed firmware.bin not reproduced::the committed build/firmware.bin ($committed) differs from this build ($prod): rebuild and commit it with the firmware change, using the exact toolchain"
fi
exit 0

- name: Upload the built images
if: always()
uses: actions/upload-artifact@v4
with:
name: l072-bench-md5-${{ github.sha }}
path: |
${{ env.L072_DIR }}/build/firmware_bench_diag.bin
${{ env.L072_DIR }}/build/firmware.bin
${{ env.L072_DIR }}/build/firmware.map
if-no-files-found: warn
retention-days: 14
45 changes: 3 additions & 42 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
Expand Up @@ -701,9 +701,10 @@
"group": "test"
},
{
"label": "Connect WiFi 5 star LAN",
"label": "WiFi connect: run by hand (no PSK in tasks)",
"detail": "The WiFi tasks that carried LIFETRAC_WIFI_PSK were removed: VS Code echoes the resolved command line, and the PSK also showed in the adb and sudo argv and in sudo's log. Run the repo-root WiFi script by hand instead (header of wifi_persist_and_reboot.sh). The bench tractor keeps WiFi off.",
"type": "shell",
"command": "adb -s 2D0A1209DABC240B push wifi_connect.sh /tmp/wifi_connect.sh ; adb -s 2D0A1209DABC240B shell \\\"chmod +x /tmp/wifi_connect.sh ; echo fio | sudo -S /tmp/wifi_connect.sh 2>&1\\\"",
"command": "echo 'WiFi tasks removed (they put the PSK on a command line). By hand: adb -s SERIAL push wifi_persist_and_reboot.sh /tmp/wpr.sh, then in an interactive adb shell set LIFETRAC_WIFI_SSID / LIFETRAC_WIFI_PSK (read -r, no echo) and run: sudo --preserve-env=LIFETRAC_WIFI_SSID,LIFETRAC_WIFI_PSK sh /tmp/wpr.sh -- see the script header.'",
"isBackground": false,
"problemMatcher": [],
"group": "test"
Expand All @@ -724,46 +725,6 @@
"problemMatcher": [],
"group": "test"
},
{
"label": "Wait and connect WiFi",
"type": "shell",
"command": "adb -s 2D0A1209DABC240B wait-for-device ; adb -s 2D0A1209DABC240B shell \\\"chmod +x /tmp/wifi_connect_fast.sh ; echo fio | sudo -S /tmp/wifi_connect_fast.sh 2>&1\\\"",
"isBackground": false,
"problemMatcher": [],
"group": "test"
},
{
"label": "Push and connect WiFi v2",
"type": "shell",
"command": "adb -s 2D0A1209DABC240B wait-for-device ; adb -s 2D0A1209DABC240B push wifi_connect_fast.sh /tmp/wifi_connect_fast.sh ; adb -s 2D0A1209DABC240B shell \\\"chmod +x /tmp/wifi_connect_fast.sh ; echo fio | sudo -S /tmp/wifi_connect_fast.sh 2>&1\\\"",
"isBackground": false,
"problemMatcher": [],
"group": "test"
},
{
"label": "Rescan then connect",
"type": "shell",
"command": "adb -s 2D0A1209DABC240B shell \\\"echo fio | sudo -S nmcli dev wifi rescan ifname wlan0 ; sleep 6 ; echo '--- visible nets ---' ; echo fio | sudo -S nmcli -t -f SSID,SIGNAL dev wifi list ifname wlan0 | head -n 15 ; echo '--- connect ---' ; echo fio | sudo -S nmcli dev wifi connect \\\\\\\"I'm a 5 star LAN!\\\\\\\" password 9182603083 ifname wlan0 ; sleep 3 ; echo '--- status ---' ; nmcli -t -f NAME,DEVICE,STATE con show --active ; ip -4 addr show wlan0\\\"",
"isBackground": false,
"problemMatcher": [],
"group": "test"
},
{
"label": "Rescan connect via script",
"type": "shell",
"command": "adb -s 2D0A1209DABC240B push wifi_rescan_connect.sh /tmp/wrc.sh ; adb -s 2D0A1209DABC240B shell \\\"chmod +x /tmp/wrc.sh ; echo fio | sudo -S /tmp/wrc.sh 2>&1\\\"",
"isBackground": false,
"problemMatcher": [],
"group": "test"
},
{
"label": "Persist profile and reboot",
"type": "shell",
"command": "adb -s 2D0A1209DABC240B push wifi_persist_and_reboot.sh /tmp/wpr.sh ; adb -s 2D0A1209DABC240B shell \\\"chmod +x /tmp/wpr.sh ; echo fio | sudo -S /tmp/wpr.sh 2>&1\\\"",
"isBackground": false,
"problemMatcher": [],
"group": "test"
},
{
"label": "Verify autoconnect after reboot",
"type": "shell",
Expand Down
21 changes: 21 additions & 0 deletions LifeTrac-v25/BUILD-CONTROLLER/05_firmware_installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,27 @@ This prints something like `9f3a8c4e2b1d7506a8e3f4c5b9d2e1f0`. Copy it into a fi

This is the radio + arbitration + Modbus master firmware that runs on the X8's onboard STM32H747.

> [!WARNING]
> **On a Portenta X8 this step overwrites the stock x8h7 firmware.** The
> H747's M7 core (flash bank 1) normally runs Arduino's stock **x8h7**
> firmware: the bridge through which X8 Linux reaches the H7-side peripherals
> (the `x8h7_*` kernel modules for GPIO, ADC, CAN, UART, PWM and RTC, and
> `m4-proxy` for M4 sketches). Arduino's own X8 workflow only uploads user
> sketches to the M4 core for this reason. Uploading `tractor_h7` as
> *Portenta H7 (M7 core)* replaces the bridge, and everything Linux does
> through it stops working until the stock image is back.
> `stm32h7-program.service` can restore it from
> `/usr/arduino/extra/STM32H747AII6_CM7.bin`, but whether it does so on its
> own depends on the LmP image.
>
> LifeTrac did this to both bench X8s on 2026-05-04; both have run stock
> x8h7 again since 2026-05-13. The current radio bench does **not** use
> `tractor_h7` at all: the LoRa radio is the Max Carrier's Murata L072,
> driven from X8 Linux over `/dev/ttymxc3`. See
> [`BENCH_BOARDS.md`](../DESIGN-CONTROLLER/firmware/x8_lora_bootloader_helper/bench_tools/BENCH_BOARDS.md)
> before flashing anything on an X8, and skip this step unless you mean to
> replace the bridge and know how to put the stock image back.

1. Open `firmware/tractor_h7/tractor_h7.ino` in Arduino IDE 2.
2. Select **Tools → Board → Arduino Mbed OS Portenta Boards → Portenta H7 (M7 core)**. (The X8's onboard H747 enumerates the same way as a standalone H7 for the toolchain — same M7 binary.)
3. Select **Tools → Port** → the X8's USB-C serial port.
Expand Down
22 changes: 19 additions & 3 deletions LifeTrac-v25/DESIGN-CONTROLLER/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@
# (time/params/logger) — the firmware/tractor_x8 sources are mounted in
# from the host so the image stays node-agnostic.

FROM python:3.11-slim
# PYTHON_IMAGE is overridable only to pin the flown base image by digest
# (see base_station/requirements.lock.txt). The default is unchanged.
ARG PYTHON_IMAGE=python:3.11-slim
FROM ${PYTHON_IMAGE}

# Mosquitto client + a few diagnostic tools so an operator shell-in is
# useful (mosquitto_pub/sub for poking topics by hand).
Expand All @@ -22,8 +25,21 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
WORKDIR /app

# Layer the deps separately so source edits do not invalidate the pip cache.
COPY base_station/requirements.txt /app/base_station/requirements.txt
RUN pip install --no-cache-dir -r /app/base_station/requirements.txt
#
# Optional LOCKED=1 (`docker build --build-arg LOCKED=1 ...`) installs the
# wheel versions of the flown bench image: base_station/requirements.lock.txt
# is applied as a pip constraints file on top of requirements.txt. The
# default (LOCKED unset or 0) is the unpinned resolve this file always did.
# The bench deploy script passes LOCKED=1
# (firmware/x8_lora_bootloader_helper/bench_tools/deploy_base.sh).
ARG LOCKED=0
COPY base_station/requirements.txt base_station/requirements.lock.txt /app/base_station/
RUN if [ "$LOCKED" = "1" ]; then \
pip install --no-cache-dir -r /app/base_station/requirements.txt \
-c /app/base_station/requirements.lock.txt; \
else \
pip install --no-cache-dir -r /app/base_station/requirements.txt; \
fi

# Application code. firmware/common/lora_proto/ is also copied so
# lora_bridge.py can import the shared protocol header constants in the
Expand Down
13 changes: 9 additions & 4 deletions LifeTrac-v25/DESIGN-CONTROLLER/FIRMWARE_UPDATES.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,13 +50,18 @@ The release-key public half is baked into each node's boot loader at manufacture
3. Watch `journalctl -fu lifetrac-base` for ≥1 minute to confirm the new bridge handshakes with the tractor.
- **Rollback:** `sudo /usr/local/bin/lifetrac-update vX.Y.<Z-1>` — same wrapper, previous tag.

### Handheld — Portenta H747 (M7 firmware) + secure element
### Handheld — Arduino MKR WAN 1310 (SAMD21 firmware, `firmware/handheld_mkr`)

- **Artifact:** `lifetrac-handheld-h747-vX.Y.Z.bin` for application code; secure-element provisioning is **separate** and only happens at manufacture or pair.
> The handheld is an MKR WAN 1310 (SAMD21 MCU + Murata LoRa module), not a
> Portenta H747 — see [HANDHELD_REMOTE.md](HANDHELD_REMOTE.md) and
> [firmware/handheld_mkr/](firmware/handheld_mkr/) (FQBN
> `arduino:samd:mkrwan1310`, [ARDUINO_CI.md](ARDUINO_CI.md)).

- **Artifact:** `lifetrac-handheld-mkr-vX.Y.Z.bin` for application code; key provisioning (secure element or key store) is **separate** and only happens at manufacture or pair.
- **Channel:** USB-C from the base-station laptop. **Never OTA** — handheld is the operator's safety-critical surface.
- **Procedure:**
1. Connect handheld via USB-C while it is **powered off**. The bootloader enumerates as `Arduino_Bootloader` on USB.
2. `python tools/flash_handheld.py --image lifetrac-handheld-h747-vX.Y.Z.bin --port COM7`.
1. Connect the handheld via USB-C and double-tap the MKR's RESET button: the SAMD21 bootloader starts (LED pulses) and enumerates as its own COM port.
2. `python tools/flash_handheld.py --image lifetrac-handheld-mkr-vX.Y.Z.bin --port COM7` (planned tool; until it exists, compile the sketch as in [ARDUINO_CI.md](ARDUINO_CI.md) and flash it with `arduino-cli upload --fqbn arduino:samd:mkrwan1310 -p <port>`).
3. Tool verifies signature, flashes, reboots. Power up the handheld and confirm the OLED displays the new firmware version on the splash screen.
4. **Re-run pairing** — the secure-element key is preserved across firmware updates by design, but verify pairing still works (cf. [OPERATIONS_MANUAL.md § 2](OPERATIONS_MANUAL.md)).
- **Rollback:** keep the previous `.bin` on the maintenance laptop; reflash with the previous version via the same procedure.
Expand Down
1 change: 1 addition & 0 deletions LifeTrac-v25/DESIGN-CONTROLLER/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ This folder contains the **primary controller design** for the LifeTrac v25 trac
- **[VIDEO_OPTIONS.md](VIDEO_OPTIONS.md)** — wireless video streaming analysis (LoRa thumbnails on the air; WiFi/cellular alternatives are archived)
- **[ARDUINO_CI.md](ARDUINO_CI.md)** — Arduino-CLI continuous integration setup
- **[firmware/murata_l072/BRINGUP_MAX_CARRIER.md](firmware/murata_l072/BRINGUP_MAX_CARRIER.md)** — Murata L072 flash + bench bring-up runbook (SWD/OpenOCD/J-Link/DFU)
- **[Radio bench quickstart](firmware/x8_lora_bootloader_helper/bench_tools/BENCH_QUICKSTART.md)** — how to reproduce the two-board LoRa bench (the RS-12/RS-13 campaigns): PC and board setup, L072 firmware, deploy, legs, analysis, park
- **[arduino_libraries.txt](arduino_libraries.txt)** — pinned library versions
- **[RESEARCH-CONTROLLER/WIRELESS_OPTIONS.md](RESEARCH-CONTROLLER/WIRELESS_OPTIONS.md)** *(archived)* — historical comparison of wireless technologies considered before LoRa was selected

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,8 @@ Each of these has been documented as actively destabilizing the bench.
& $adb -s <serial> reverse --list
```

- Observed (2026-05-24, board 2E2C): `reverse tcp:1883 tcp:1883` returns
- Observed (2026-05-24, board 2D0A -- the base, the board that runs 934-91;
2E2C runs the factory 674-88 image): `reverse tcp:1883 tcp:1883` returns
exit 0 but the tunnel is never installed. From the X8,
`exec 9<>/dev/tcp/127.0.0.1/1883` returns "Connection refused".
- `reverse --list` returns `error: protocol fault (couldn't read status
Expand Down
Loading
Loading