Skip to content
Open
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
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,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`.
Expand Down
9 changes: 8 additions & 1 deletion config/default.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ radio:
sync_word: 0x2B
preamble_length: 16
tx_power_dbm: 22
# HAL GPS/PPS (RAK Pi HAT u-blox → concentrator PPS pin). Off by default.
# gps_pps_enabled: false
# gps_pps_tty_path: "/dev/ttyAMA0"
# gps_family: "ubx7"
# gps_pps_target_baud: 0 # 0 = HAL default (9600)

meshtastic:
default_key_b64: "AQ=="
Expand Down Expand Up @@ -129,12 +134,14 @@ location:
# and are not overwritten by gpsd.
# 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.
Expand Down
6 changes: 6 additions & 0 deletions docs/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@

- **MQTT broker TLS.** Transport TLS (`mqtts`, CA bundle, cert validation) is not implemented on `mqtt_publisher.py` (plain TCP only). Until then use plain port 1883 or a LAN broker without TLS.

- **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).
- **GPS PPS (HAL).** Optional `radio.gps_pps_*` enables `lgw_gps_*` / `sx1302_gps_enable` timestamp sync on RAK Pi HATs with PPS wired to the concentrator. `GET /api/device/gps-pps-status`; config rejects sharing the HAT UART with `location.source: uart`.

### v0.7.6 (June 2026)

Meshtastic mesh participant release on `main` (merge `feat/v0.7.6`). Edge-only, pure Python, no concentrator recompile. **Upgrade:** Settings → Updates → **Stable**, or the full SSH block in `docs/COMMON-ERRORS.md` (`git fetch`, `checkout main`, `pull`, `scripts/install.sh`, `restart`). Required this release: new `cryptography` dependency for PKI and an updated `meshpoint.service` unit (RAK V2 reset fix). Pull-only upgrades can miss both. Witness-tested on RAK V2. Settings → Updates RC picker now points at **v0.7.7** on `feat/v0.7.7`.
Expand Down
50 changes: 44 additions & 6 deletions docs/COMMON-ERRORS.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,16 +430,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
Expand Down
63 changes: 60 additions & 3 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -76,8 +77,38 @@ 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.

### GPS PPS time sync (HAL)

Separate from [Location (GPS) source](#location-gps-source) (dashboard lat/lon). PPS sync wires the concentrator's internal packet counter to GPS time via Semtech `lgw_gps_*` and `sx1302_gps_enable` — useful when you need accurate `timestamp_us` on RX metadata (logging, TDoA-style analysis, correlating captures to wall clock).

```yaml
radio:
gps_pps_enabled: false # set true on RAK Pi HAT with PPS wired to SX1303
gps_pps_tty_path: "/dev/ttyAMA0"
gps_family: "ubx7" # passed to lgw_gps_enable
gps_pps_target_baud: 0 # 0 = HAL default (9600)
```

Requirements:

- `libloragw` built from the SX1302 HAL tree with `loragw_gps.c` linked (stock `lora_gateway` packages on some images omit these symbols — startup logs `GPS/PPS sync unavailable` and continues).
- u-blox on the HAT UART with PPS routed to the concentrator (RAK2287/5146/SX1303 HAT).
- Clear sky: first `lgw_gps_sync` may take minutes until UBX time messages arrive.

**Do not** enable `gps_pps_enabled` and `location.source: uart` on the same `tty_path`. Only one owner can open the serial port. Typical RAK deployments:

| Goal | `location.source` | `radio.gps_pps_enabled` |
|---|---|---|
| Map position from HAT GPS | `uart` | `false` |
| Accurate packet timestamps + map from USB gpsd | `gpsd` | `true` on `/dev/ttyAMA0` |
| Timestamps only, static map coords | `static` | `true` |

Status: `GET /api/device/gps-pps-status` (enabled, sync count, last error, reference counter). Config keys also appear under `radio_advanced` in `GET /api/config`.

### Standard Meshtastic Presets

To match a Meshtastic preset, set `spreading_factor` and `bandwidth_khz` together:
Expand Down Expand Up @@ -169,8 +200,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 **live GPS fixes**
Expand All @@ -185,7 +218,7 @@ coordinates and mesh position settings hot-reload from the dashboard.
|---|---|
| `static` (default) | No live GPS hardware. Registered coordinates live in `device.*` only. Skyplot shows the static pin. |
| `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). Skyplot and stats update from the live fix. |
| `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. |

### Mesh position broadcasts (LoRa / Meshtastic app map)

Expand Down Expand Up @@ -253,13 +286,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

Three independent surfaces:
Expand Down Expand Up @@ -693,6 +748,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

Expand Down
7 changes: 5 additions & 2 deletions docs/HARDWARE-MATRIX.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 |
Expand Down Expand Up @@ -212,8 +213,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).

---

Expand Down
71 changes: 71 additions & 0 deletions docs/plans/gps-pps-follow-up-issue.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# GitHub issue draft: HAL GPS/PPS sync on SX1303 (follow-up to #65)

**Title:** `feat(hal): GPS PPS timestamp sync via lgw_gps_* for RAK SX1303 HAT`

**Labels:** `enhancement`, `hardware`, `hal`

---

## Context

PR #65 (RAK SX1303 HAL guard + `location.source: uart`) deliberately split two GPS concerns:

| Layer | Config | Purpose |
|--------|--------|---------|
| Dashboard / NodeInfo | `location.source` (`static`, `gpsd`, `uart`) | Human-readable lat/lon |
| Concentrator timestamps | `radio.gps_pps_*` + Semtech `lgw_gps_*` | Align `timestamp_us` with GPS via PPS |

On RAK Pi gateways the u-blox speaks NMEA/UBX on `/dev/ttyAMA0` and drives a PPS line into the SX1302/SX1303. Meshpoint can use the HAL to parse UBX, call `lgw_gps_sync` on `UBX-NAV-TIMEGPS`, and enable `sx1302_gps_enable(true)` — but only when `libloragw` includes `loragw_gps.c`.

## Problem

Without PPS sync, packet `timestamp_us` values are concentrator-relative counters. That is fine for RSSI/SNR analytics but weak for:

- Correlating captures to wall-clock or external sensors
- Future TDoA / multilateration experiments
- Debugging “when did this burst happen?” across reboots

## Proposed solution

1. ctypes bindings for `lgw_gps_enable`, `lgw_parse_nmea`/`ubx`, `lgw_gps_get`, `lgw_gps_sync`, `lgw_get_trigcnt`, `lgw_cnt2utc`, `sx1302_gps_enable`.
2. Background reader thread after `lgw_start()`.
3. Config under `radio:`:
- `gps_pps_enabled` (default `false`)
- `gps_pps_tty_path` (default `/dev/ttyAMA0`)
- `gps_family` (default `ubx7`)
- `gps_pps_target_baud` (default `0` = HAL default)
4. Startup guard: reject `gps_pps_enabled` + `location.source: uart` on the same TTY.
5. `GET /api/device/gps-pps-status` for operators.
6. Docs in `CONFIGURATION.md` with the uart vs PPS matrix.

## Acceptance criteria

- [ ] With `gps_pps_enabled: true` on hardware with PPS wired, logs show `GPS/PPS sync #1 ok` after fix.
- [ ] `GET /api/device/gps-pps-status` reports `last_sync_ok: true` and increasing `sync_count`.
- [ ] Misconfigured dual-UART use fails at config load with a clear error.
- [ ] Graceful degrade when HAL lacks GPS symbols (info log, concentrator still starts).
- [ ] Unit tests mock `libloragw` GPS entry points.

## Out of scope (this issue)

- Automatic UBX CFG-PPS programming (operators may use u-center / `gpsd` once).
- Sharing one UART between HAL PPS and `UartSource` (documented conflict; use `gpsd` or `static` for map coords).
- Spectral scan / SX1261 path changes (covered in #65).

## Hardware test plan

1. RAK2287/5146 + Pi, `chip version 0x12` (SX1303), outdoor antenna.
2. `config/local.yaml`:
```yaml
radio:
gps_pps_enabled: true
location:
source: static # or gpsd on USB
```
3. `journalctl -u meshpoint -f` → expect PPS sync lines.
4. `curl -s -H "Authorization: Bearer …" http://127.0.0.1:8080/api/device/gps-pps-status | jq`

## References

- Semtech `loragw_gps.h` / `loragw_sx1302.h` in sx1302_hal
- Related: #65, RAK `install.sh` UART enable for `/dev/ttyAMA0`
Loading
Loading