From 92b1944dd5aac8cb5bfe31905428c2b36a9607a0 Mon Sep 17 00:00:00 2001 From: iceice400 Date: Tue, 2 Jun 2026 13:10:58 -0400 Subject: [PATCH 1/2] feat: RAK SX1303 HAL guard, UART onboard GPS, and operator docs --- README.md | 2 + config/default.yaml | 4 +- docs/CHANGELOG.md | 4 + docs/COMMON-ERRORS.md | 50 +++++++- docs/CONFIGURATION.md | 37 +++++- docs/HARDWARE-MATRIX.md | 7 +- frontend/js/configuration/gps_card.js | 76 +++++++++++- requirements.txt | 1 + src/api/routes/config_enrichment.py | 3 + src/api/routes/device_config_routes.py | 33 ++++- src/capture/concentrator_source.py | 3 + src/cli/setup_wizard.py | 20 ++- src/config.py | 28 +++-- src/coordinator.py | 5 +- src/hal/gps_reader.py | 12 +- src/hal/location/factory.py | 6 +- src/hal/location/uart_source.py | 137 +++++++++++++++++---- src/hal/sx1302_signatures.py | 4 + src/hal/sx1302_types.py | 10 ++ src/hal/sx1302_wrapper.py | 75 ++++++++++- tests/test_location_config.py | 9 +- tests/test_location_static_uart_sources.py | 39 ++++-- tests/test_sx1302_wrapper_hal_guard.py | 91 ++++++++++++++ tests/test_uart_location_source.py | 110 +++++++++++++++++ 24 files changed, 684 insertions(+), 82 deletions(-) create mode 100644 tests/test_sx1302_wrapper_hal_guard.py create mode 100644 tests/test_uart_location_source.py diff --git a/README.md b/README.md index 407813f..27cc915 100644 --- a/README.md +++ b/README.md @@ -247,6 +247,8 @@ See [docs/COMMON-ERRORS.md](docs/COMMON-ERRORS.md#upgrades) if the service fails **Chip version 0x00:** Concentrator not responding. Check that the concentrator module is seated, SPI is enabled (`raspi-config` → Interface Options → SPI), and try a full power cycle (unplug for 10+ seconds). Normal chip versions are `0x10` (SX1302) and `0x12` (SX1303). +**`sx1261_check_status` / `lgw_start() failed` on RAK V2:** Clear `radio.sx1261_spi_path` in `local.yaml` (leave it `""`). The SX1261 is not Pi-visible on RAK/SenseCap boards. Use `location.source: uart` for onboard HAT GPS or `gpsd` for USB GPS. See [Common Errors](docs/COMMON-ERRORS.md). + **No packets:** Verify antenna is connected and frequency matches your region. Check `meshpoint logs` for `lgw_receive returned N packet(s)`. **Upstream 401:** Bad API key. Get a free one at [meshradar.io](https://meshradar.io) and re-run `sudo meshpoint setup`. diff --git a/config/default.yaml b/config/default.yaml index 2f7e30d..13b4c2e 100644 --- a/config/default.yaml +++ b/config/default.yaml @@ -120,12 +120,14 @@ location: # Where the Meshpoint's reported lat/lon/alt comes from. # static : use device.latitude/longitude/altitude (default) # gpsd : poll a local or remote gpsd daemon for live fixes - # uart : reserved (RAK Pi HAT GPS, not yet wired) + # uart : on-board RAK Pi HAT GPS via /dev/ttyAMA0 (NMEA GGA) source: "static" # gpsd connection. Defaults match the well-known local socket; only # change when running gpsd on a peer machine on the LAN. gpsd_host: "127.0.0.1" gpsd_port: 2947 + uart_path: "/dev/ttyAMA0" + uart_baud: 9600 # How often the coordinator wakes to read the active source. update_interval_seconds: 5 # Minimum acceptable fix mode: 0=any (incl. no-fix), 1=2D, 2=3D. diff --git a/docs/CHANGELOG.md b/docs/CHANGELOG.md index 7cb7fd8..81dcde7 100644 --- a/docs/CHANGELOG.md +++ b/docs/CHANGELOG.md @@ -4,6 +4,10 @@ Queued for the next version bump (v0.7.6 mesh participant RC on `feat/v0.7.6`). +- **UART GPS.** `location.source: uart` reads NMEA GGA from the RAK Pi HAT (`/dev/ttyAMA0` by default). Configuration → GPS UART fieldset; `meshpoint setup` can select uart when the wizard probe gets a fix. Requires `pyserial` in the venv (`pip install -r requirements.txt` after upgrade). +- **Concentrator model ID.** Startup logs SX1302 vs SX1303 when `libloragw` exposes `sx1302_get_model_id` (e.g. `Concentrator model ID: 0x12 (SX1303)`). +- **SX1261 guard.** Non-empty `radio.sx1261_spi_path` on RAK/SenseCap carriers (`radio.carrier_type`) is cleared at configure time with an explicit warning, preventing `lgw_start()` failures from misconfigured spectral scan on Pi-invisible SX1261 wiring. +- **SPI preflight.** Missing `/dev/spidev0.0` raises a clear error before `lgw_start()` (raspi-config / spi group hints). - **MQTT broker TLS (deferred).** Transport TLS (`mqtts`, CA bundle, cert validation) is not implemented on `mqtt_publisher.py` (plain TCP only). Planned for v0.7.6. Until then use plain port 1883 or a LAN broker without TLS. ### v0.7.5.1 (May 2026) diff --git a/docs/COMMON-ERRORS.md b/docs/COMMON-ERRORS.md index d269f56..df03d58 100644 --- a/docs/COMMON-ERRORS.md +++ b/docs/COMMON-ERRORS.md @@ -401,16 +401,54 @@ disabled in `raspi-config`, or the SPI bus latched after a hard power cut. 3. Full power cycle: `sudo poweroff`, wait for green LED to stop, unplug for 10+ seconds, then plug back in. -Normal chip versions are `0x10` (SX1302) and `0x12` (SX1303). +Normal chip versions are `0x10` (SX1302) and `0x12` (SX1303). Startup +also logs `Concentrator model ID: 0x12 (SX1303)` when the HAL exposes +`sx1302_get_model_id`. + +### `sx1261_check_status: got:0x00 expected:0x22` / `failed to patch sx1261` + +**Cause:** `radio.sx1261_spi_path` is set on a carrier where the SX1261 +companion chip is **not** wired to a Pi-visible SPI bus (RAK2287, RAK5146, +SenseCap M1, most RAK Hotspot V2 units). The HAL probes a chip that is not +there and may refuse `lgw_start()`. + +**Fix:** + +1. Edit `config/local.yaml` and clear the path: + + ```yaml + radio: + sx1261_spi_path: "" + ``` + +2. Restart: `sudo systemctl restart meshpoint`. + +3. Confirm the journal shows `SX1261 spi_path empty; spectral scan disabled` + and `Application startup complete`. + +On current Meshpoint builds with `radio.carrier_type: rak` or +`sensecap_m1` (written by `meshpoint setup`), a mistaken path is cleared +automatically at startup with a warning. Re-run `sudo meshpoint setup` if +`carrier_type` is missing. + +See [Configuration > Spectral Scan](CONFIGURATION.md#spectral-scan-noise-floor). ### `lgw_start() failed` or `Failed to set SX1250_0 in STANDBY_RC mode` -**Cause:** SPI bus latch from a hard power cut. The Meshpoint shutdown -handler holds the concentrator in reset on `sudo reboot` and -`sudo systemctl restart`, so this only appears after yanked-cable shutdowns, -breaker trips, or outages. +**Cause:** Usually one of: + +1. **SX1261 misconfiguration** — see the entry above if the log mentions + `sx1261_check_status` or `failed to patch sx1261`. +2. **SPI bus latch** from a hard power cut. The Meshpoint shutdown handler + holds the concentrator in reset on `sudo reboot` and + `sudo systemctl restart`, so latch typically follows yanked-cable + shutdowns, breaker trips, or outages. +3. **SPI disabled or device missing** — `/dev/spidev0.0` not present; + enable SPI in `raspi-config` and confirm the `meshpoint` user is in + the `spi` group. -**Fix:** Full power cycle: +**Fix:** If SX1261 lines appear in the log, fix `sx1261_spi_path` first. +Otherwise full power cycle: ```bash sudo poweroff diff --git a/docs/CONFIGURATION.md b/docs/CONFIGURATION.md index 089403c..825772b 100644 --- a/docs/CONFIGURATION.md +++ b/docs/CONFIGURATION.md @@ -26,6 +26,7 @@ radio: tx_power_dbm: 22 # SX1302 concentrator output power spectral_scan_interval_seconds: 60 # noise floor sampler cadence (0 disables) sx1261_spi_path: "" # SX1261 SPI device for spectral scan (empty = disabled) + carrier_type: "" # rak | sensecap_m1 (set by meshpoint setup; guards SX1261 path) ``` The region sets the base frequency, spreading factor, and bandwidth automatically. You only need `region` in most cases. Override `frequency_mhz`, `spreading_factor`, or `bandwidth_khz` individually to tune for non-default presets (MediumFast, ShortFast, etc.) or custom frequency slots. @@ -76,6 +77,8 @@ ERROR: failed to patch sx1261 radio for LBT/Spectral Scan …and `lgw_start()` may then refuse to bring up the concentrator. If you see that, revert `sx1261_spi_path` to `""`, restart the service, and stay on the packet-derived fallback. +On RAK and SenseCap carriers, `meshpoint setup` writes `radio.carrier_type`. When that field is `rak` or `sensecap_m1`, Meshpoint **clears** a non-empty `sx1261_spi_path` at startup and logs a warning instead of calling `lgw_sx1261_setconf`. + If your `libloragw` build does not expose the spectral scan symbols at all (older HAL revisions), the service logs a single info line at startup and falls back automatically. ### Standard Meshtastic Presets @@ -169,8 +172,10 @@ location: source: "static" # static | gpsd | uart gpsd_host: "127.0.0.1" # gpsd TCP host (only when source=gpsd) gpsd_port: 2947 # gpsd TCP port + uart_path: "/dev/ttyAMA0" # serial device (only when source=uart) + uart_baud: 9600 # NMEA baud (uart only) update_interval_seconds: 5 # how often the coordinator polls the source - min_fix_quality: 1 # minimum NMEA fix quality (1=2D, 3=3D) + min_fix_quality: 1 # minimum fix quality (1=2D, 2=3D for gpsd/uart) ``` `location.source` selects where the Meshpoint reads its current @@ -183,9 +188,9 @@ changes require a service restart; everything else hot-reloads. |---|---| | `static` (default) | Uses `device.latitude` / `device.longitude` / `device.altitude` exactly as set during the wizard. No GPS hardware required. | | `gpsd` | Reads live fixes from the system `gpsd` daemon over TCP (`127.0.0.1:2947`). Recommended for any USB GPS receiver (u-blox 7, u-blox 8, VFAN puck, generic CDC ACM sticks). | -| `uart` | Reserved for direct-serial reads from a Pi HAT GPS (e.g. RAK 7248). Currently a placeholder; falls back to static and surfaces an explanatory error in the dashboard. | +| `uart` | Reads NMEA GGA from the on-board RAK Pi HAT GPS on `/dev/ttyAMA0` (or `uart_path`). Fix and satellite count update live; full skyplot az/el/SNR needs `gpsd` + USB receiver. | -When `source: gpsd` is active and the daemon has a 2D or 3D fix, +When `source` is `gpsd` or `uart` and a 2D or 3D fix is available, the coordinator updates `_config.device.latitude` / `.longitude` / `.altitude` in place every `update_interval_seconds`. Anything that reads from `device.*` (NodeInfo broadcasts, MQTT, the dashboard map, @@ -228,13 +233,35 @@ in `gpsd-clients`) or `gpsmon`. | u-blox 7 USB stick | USB CDC ACM, NMEA + UBX | yes (RAK V2 .141) | | u-blox 8 USB stick | USB CDC ACM, NMEA + UBX | yes | | VFAN ublox 7 USB puck | USB CDC ACM, NMEA + UBX | yes | -| RAK 7248 onboard u-blox via UART (`/dev/ttyAMA0`) | NMEA over UART | placeholder (`source: uart`, not yet wired) | +| RAK 7248 onboard u-blox via UART (`/dev/ttyAMA0`) | NMEA over UART | yes (`source: uart`; `install.sh` enables UART) | Other USB receivers should work as long as `gpsd` recognizes the device's VID. If `cgps` shows data but the dashboard does not, check `journalctl -u meshpoint | grep -i gpsd` for connection errors and confirm `source: gpsd` in `local.yaml`. +### Using uart (RAK Pi HAT onboard GPS) + +`scripts/install.sh` enables the primary UART (`/dev/ttyAMA0`), disables +the serial console, and turns off Bluetooth on the UART pins so the HAT +GPS module can talk to the Pi. + +1. Run `sudo meshpoint setup` outdoors and accept **Use live UART GPS** + when the wizard acquires a fix, **or** set in `local.yaml`: + + ```yaml + location: + source: "uart" + uart_path: "/dev/ttyAMA0" + uart_baud: 9600 + ``` + +2. Restart: `sudo systemctl restart meshpoint`. + +3. Open **Configuration → GPS** → **UART**. Coordinates and satellite + count update every few seconds outdoors. The skyplot stays empty + until GSV parsing is added (use `gpsd` for a full skyplot today). + ### Privacy Live GPS coordinates flow through the same surfaces as the static @@ -663,6 +690,8 @@ location: # GPS / location source source: "static" # static | gpsd | uart gpsd_host: "127.0.0.1" gpsd_port: 2947 + uart_path: "/dev/ttyAMA0" + uart_baud: 9600 update_interval_seconds: 5 min_fix_quality: 1 diff --git a/docs/HARDWARE-MATRIX.md b/docs/HARDWARE-MATRIX.md index e06708d..c6dd747 100644 --- a/docs/HARDWARE-MATRIX.md +++ b/docs/HARDWARE-MATRIX.md @@ -17,6 +17,7 @@ Application code is plain Python (v0.7.0+); **aarch64** is required. |---|---|---|---|---| | **Host** | Pi 4 (SD) | Pi 4 (SD) | CM4 (eMMC) | Pi 4 (SD) | | **Concentrator** | RAK2287 (SX1302) | WM1303 (SX1303) | SX1302 (onboard) | RAK2287 (SX1302) | +| **HAL chip version log** | `0x10` (SX1302) | `0x12` (SX1303) | `0x10` (SX1302) | `0x10` (SX1302) | | **TX support** | Yes (native, with HAL patch) | Yes (native, with HAL patch) | Yes (native, with HAL patch) | Yes (native, with HAL patch) | | **RX channels** | 8 simultaneous | 8 simultaneous | 8 simultaneous | 8 simultaneous | | **Spreading factors** | SF7-SF12 simultaneous | SF7-SF12 simultaneous | SF7-SF12 simultaneous | SF7-SF12 simultaneous | @@ -177,8 +178,10 @@ window-mounted deployments. For better coverage: GPS antenna (u.FL to SMA pigtail) is optional. If your carrier board has a u-blox GPS module, plugging in a GPS antenna gives you automatic -positioning during the setup wizard. Otherwise enter coordinates manually -(right-click any spot in Google Maps to copy in decimal format). +positioning during the setup wizard. Set `location.source: uart` for the +on-board RAK HAT module, or `location.source: gpsd` for a USB GPS stick. +Otherwise enter coordinates manually (right-click any spot in Google Maps +to copy in decimal format). --- diff --git a/frontend/js/configuration/gps_card.js b/frontend/js/configuration/gps_card.js index cacac0b..969c8ae 100644 --- a/frontend/js/configuration/gps_card.js +++ b/frontend/js/configuration/gps_card.js @@ -33,8 +33,8 @@ class GpsConfigCard {

GPS and placement

- Live fix from a USB GPS via gpsd, or static coordinates - you enter manually. Used by the local map and + Live fix from gpsd (USB GPS), UART (RAK Pi HAT), or static + coordinates you enter manually. Used by the local map and Meshradar fleet view.

@@ -127,6 +127,42 @@ class GpsConfigCard {

+ +