Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
9 changes: 8 additions & 1 deletion .github/release-notes-template.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ receiving station. Flash it to a station's Compute Module for a field-ready rece
upload over cellular / Wi-Fi / Ethernet (optional Iridium satellite), GPS/PPS timing,
a local LCD + web dashboard, and over-the-air updates.

As an internet-connected **Motus** receiving station it runs a build of **SensorGnome**
— the open-source Motus tag-detection software — alongside the native CTT radio
services, decoding Lotek and CTT coded-ID tags and contributing detections to the
**[Motus Wildlife Tracking System](https://motus.org)**, the collaborative research
network CTT partners with. Detections upload to both the CTT cloud and Motus.

## Download

| | |
Expand Down Expand Up @@ -41,7 +47,8 @@ __CHANGELOG__
- **User guide:** <https://cellular-tracking-technologies.github.io/ctt_documentation/SensorStation-User-Guide.html>
- **Source:** <https://github.com/__REPO__> — open source under **AGPL-3.0-or-later**
- **Data portal:** <https://account.celltracktech.com>
- Part of the **Internet of Wildlife** / Motus network
- **Motus network:** <https://motus.org> — the collaborative tracking network CTT partners with; this image runs a build of **[SensorGnome](https://sensorgnome.org)** for Motus tag detection
- Part of the **Internet of Wildlife**

---
_Built from `__SHORT_SHA__` · __DATE___
Expand Down
30 changes: 25 additions & 5 deletions docs/guides/migrating-to-lts_26_07.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Migrating a SensorStation to the `lts_26_07` image (v2.2.x)
# Migrating a SensorStation to the `lts_26_07` image (v2.3.x)

This is a **major upgrade** from the previous production LTS (**v1.7.0**, the `lts_24-06`
image line) to **v2.2.x** (`lts_26_07`) — a full 2.x rework, not a point update.
image line) to **v2.3.x** (`lts_26_07`) — a full 2.x rework, not a point update.
**Re-flashing the `lts_26_07` image is the recommended path.**

Two behavior changes to know before you start:
Expand All @@ -16,8 +16,8 @@ migration-oriented summary, organized by impact.

| | Old LTS (`lts_24-06`) | New LTS (`lts_26_07`) |
|---|---|---|
| App version | **v1.7.0** | **v2.2.x** |
| Cellular | Quectel EC25, QMI + ModemManager **PPP** | Telit LE910Q1, **CDC-ECM** (`mdm0`), zero-touch boot provisioning |
| App version | **v1.7.0** | **v2.3.x** |
| Cellular | Quectel EC25, QMI + ModemManager **PPP** | Telit LE910Q1 **CDC-ECM** (`mdm0`) / Quectel EC25 QMI, zero-touch provisioning + **SIM-aware APN self-heal** |
| Hardware I/O | in the Node app at boot | **native C++ tools** → `/run/ctt/` contracts; the app is a consumer |
| Driver delivery | compiled on-station | **prebuilt armhf binaries** fetched over OTA, per-tool version pins |
| Image pipeline | manual, date-named | **CI-built, immutable `v<version>` images** |
Expand Down Expand Up @@ -45,6 +45,18 @@ contracts under **`/run/ctt/`**.
so cellular never preempts wired/Wi-Fi.
- A boot service pulses `ON_OFF#` so a Telit in shutdown (after a hard reset) self-recovers.
- **Modem NV persists across a reflash** — so after flashing there is **no manual provisioning step**.
- **v2.3.x — native provisioner for *both* families + SIM-aware APN that self-heals.** Provisioning
is now a native tool (`ctt-modem-provision`) for the Quectel EC25 as well as the Telit, the Telit
RNDIS→ECM conversion completes in a **single boot**, and the LTE attach APN is chosen from the
**SIM's IMSI** (home PLMN) with an ICCID issuer-prefix fallback — so Telenor SIMs (including those
issued in a US-numbered `8901` ICCID range) no longer strand on the wrong APN with **3GPP cause
55/33** (registered, no data). The attach context is matched to the SIM and **self-heals a
recycled/swapped modem** that carried a stale APN in from a prior deployment (Quectel `CGDCONT`
CID1; and, in the latest `ctt-modem-provision`, the Telit ECM context too). Cellular also now
**survives every OTA** (an update can't clobber the Quectel's autoconnect) and **can't steal the
default route** from a wired/Wi-Fi uplink; `station-modem` retries indefinitely rather than giving
up. *Field note:* after a boot-time re-attach, cellular data takes **~15–30 s** to come up — the
modem is *registered* before the PDN is *connected*.

### OTA + delivery
- Modular post-merge hooks (`system/scripts/hooks/post-merge.d/*.sh`): new subsystem deploys are a
Expand All @@ -60,6 +72,10 @@ contracts under **`/run/ctt/`**.
- LCD boot/shutdown/updating splashes + a radio-fault banner; native re-init recovers a warm
controller. Native `ctt-leds` / `ctt-sensors` (V3 **and** V2 sensor sets) with quieter logging.
BluSeries receivers discovered via udev.
- **v2.3.x:** the front-panel **IP Address** screen now also shows USB-ethernet dongles (`enx…`),
and an on-station **`collect-diagnostics`** CLI bundles identity / hardware / services / modem+SIM
/ logs into a single `.tar.gz` for field triage. User-facing CLIs are (re)created by an OTA hook,
so a lost symlink self-heals on the next update.

### Network reachability
- Wi-Fi power-save disabled (stations stayed unreachable when the USB Wi-Fi adapter slept);
Expand Down Expand Up @@ -96,7 +112,7 @@ station you can't physically reach.
or Wi-Fi uplink.
- **Station checks in** to the cloud.

## Version timeline (v1.7.0 → v2.2.2)
## Version timeline (v1.7.0 → v2.3.3)

| Version | Theme |
|---|---|
Expand All @@ -109,5 +125,9 @@ station you can't physically reach.
| **2.2.0** | Cellular **RNDIS → CDC-ECM**, zero-touch provisioning |
| 2.2.1 | Fast burns (PiShrink); license metadata (AGPL-3.0) |
| **2.2.2** | Fail-safe, stateless first-boot resize |
| 2.3.0 | Native provisioner extended to the **Quectel EC25**; IPv4-only PDN; `provision-modem-apn` |
| 2.3.1 | Native binaries actually baked into images; **Telit RNDIS→ECM in one boot** (`ctt-modem-provision` 0.3.1) |
| 2.3.2 | Front-panel **IP Address** screen shows USB-ethernet dongles |
| **2.3.3** | **APN by IMSI** (Telenor cause-55/33 heal, provisioner 0.3.2); OTA-safe autoconnect; cellular can't steal the route; **`collect-diagnostics`** CLI |

See [`CHANGELOG.md`](../../CHANGELOG.md) for the full per-release detail.
1 change: 1 addition & 0 deletions native/src/ctt-board-detect/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Detects the board's hardware identity on **every** boot (so a compute-module swap between boards is plug-n-play) and writes it, drop-in compatible with the old Node `initialize.js`: `/etc/ctt/station-id`, `/etc/ctt/station-revision`, `/etc/ctt/station-board-revision`, plus `/run/ctt/board.env` (`CTT_BOARD=v2|v3r0|v3r3`) for udev and the rest of the boot chain. All hardware logic lives in the `ctthw` library (I2C bus + chip drivers + board-id composition); this binary is the thin shell that runs first at boot. Fail-clean; `--dry-run` detects and prints without writing.
1 change: 1 addition & 0 deletions native/src/ctt-lcd/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Display server for the station's HD44780/PCF8574 character LCD (`0x27`/`0x3f`). Renders a fixed 144-byte framebuffer published by the Node `station-lcd-interface` to `/run/ctt/lcd` (8 CGRAM glyphs + 80 character cells), pushing only changed glyphs and rows to the controller; the menu/stats/button logic stays in Node. Owns the I2C actuation under the shared `ctthw` bus lock — the LCD was the last I2C consumer still opening the bus from Node. Same PCF8574 backpack on V2 and V3, so there's no board-version branch; idles if no backpack is present.
1 change: 1 addition & 0 deletions native/src/ctt-leds/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Drives the V3 status LEDs (GPS / diag-A / diag-B on the SX1509B expander pins 0/10/11) from a desired-state file `/run/ctt/leds` (`gps=on|off|blink[:<ms>]`, `a=…`, `b=…`), owning the I2C actuation and the blink timing. The decision logic (GPS fix, internet/PPP, alive heartbeat) stays in the Node app, which writes the file — so this daemon replaces the in-process `SetState()` the Node app did on V3. V2 boards drive these LEDs over GPIO (no expander) and aren't handled here; the daemon idles on V2.
2 changes: 1 addition & 1 deletion native/src/ctt-modem-provision/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Provisions the cellular modem's data path / attach context on boot, self-healing across the fleet: for a **Telit LE910Q1** it ensures the CDC-ECM data path (`AT#USBCFG`/`AT#ECM`); for a **Quectel EC25** it keeps the LTE attach APN (`CGDCONT` CID1) matched to the SIM so it can't diverge from the NetworkManager dial APN the mismatch that yields 3GPP **cause 55** (modem registered but no data). Idempotent and fail-open; runs `Before=ModemManager`.
Provisions the cellular modem's data path / attach context on boot, self-healing across the fleet. For **both** modem families it keeps the LTE attach context (`CGDCONT` CID1) matched to the SIM's carrier — chosen by the IMSI home PLMN, with an ICCID issuer-prefix fallback — so the attach APN can't diverge from the NetworkManager dial APN (the mismatch behind 3GPP **cause 55/33**: modem registered but no data), and so a **recycled modem carrying a stale APN from a prior deployment** is healed rather than left stranded. For a **Telit LE910Q1** it first brings up the CDC-ECM data path (`AT#USBCFG`/`AT#ECM`) before that heal; the **Quectel EC25** is QMI-managed and needs only the attach-context heal. Idempotent and fail-open; runs `Before=ModemManager`.
1 change: 1 addition & 0 deletions native/src/ctt-radio-driver/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Bridges one receiver serial port to an AF_UNIX socket (gpsd-style) — one binary serving both radio classes via `--framing`. **line** (434 MHz radios): the serial stream is split on newlines and wrapped in an NDJSON envelope, **lossy** under backpressure so a real-time beep stream stays fresh. **raw** (BluSeries receivers): a transparent, bidirectional byte pipe, **non-lossy** — it pauses serial reads under client backpressure rather than dropping bytes, because the polled request/response Blu protocol and firmware DFU can't tolerate loss. One `ctt-radio-driver@chN` instance per channel (`/run/ctt/radios/chN.sock`, `/run/ctt/blu/chN.sock`).
1 change: 1 addition & 0 deletions native/src/ctt-sensors/DESCRIPTION
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Periodically reads the station's analog I2C sensors (ADC rail voltages + board temperature) via `ctthw` and publishes a snapshot to `/run/ctt/sensors.json` for the Node app — replacing the in-process `SensorMonitor` that ran inside `station-hardware-server`. The JSON matches the old shape (`{voltages:{battery,solar,rtc}, temperature:{celsius,fahrenheit}, recorded_at}`) so the Node side reads the file instead of polling I2C itself. `--once` reads a single snapshot and exits; the default is a poll loop that logs a periodic heartbeat plus changes.
2 changes: 1 addition & 1 deletion system/native/ctt-modem-provision.version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.3.2
0.3.3
Loading