feat: SIM7600X 4G cellular + GPS (#58) #65

Merged
seb merged 1 commits from sl-controls/cellular-gps into main 2026-03-01 00:51:16 -05:00
Collaborator

Closes #58

Summary

Adds saltybot_cellular ROS2 package for the Waveshare SIM7600X 4G HAT (SIMCom SIM7600A-H, LTE Cat-4, GPS+GLONASS+BeiDou+Galileo).

Nodes

gps_driver_node

  • Reads NMEA from /dev/ttyUSB2, optionally starts GNSS via AT+CGPS=1 on /dev/ttyUSB0
  • Validates NMEA checksum; parses any talker ID (GP/GN/GL)
  • /gps/fix (NavSatFix) — lat/lon/alt, covariance from HDOP × ±2.5m CEP
  • /gps/vel (TwistStamped) — ground speed + heading, ENU velocity components
  • /diagnostics — fix quality, satellite count, HDOP

cellular_manager_node

  • Polls AT+CSQ, AT+CREG?, AT+COPS? every 5s via /dev/ttyUSB0
  • /cellular/status (DiagnosticArray) — rssi dBm, signal quality, network registration, operator
  • /cellular/rssi (Int32, dBm), /cellular/connected (Bool)
  • Auto-reconnect via nmcli (NetworkManager profile) or pppd (systemd service)

mqtt_bridge_node

  • Bidirectional ROS2 ↔ MQTT relay using paho-mqtt (graceful fallback if not installed)
  • ROS2→MQTT (QoS 0 telemetry): /saltybot/imu, /gps/fix, /gps/vel, /uwb/ranges, /person/target, /cellular/status
  • MQTT→ROS2 (QoS 1 commands): saltybot/cmd/saltybot/cmd, saltybot/estop/saltybot/estop
  • Per-topic rate limiting: IMU 5Hz, GPS/UWB 1Hz, person 2Hz → <<50KB/s over LTE
  • Optional TLS (tls_enable: true), configurable broker/port/prefix/auth

Files

File Lines
gps_driver_node.py 402
cellular_manager_node.py 362
mqtt_bridge_node.py 317
config/cellular_params.yaml full config
launch/cellular.launch.py all nodes, all params as launch args
test/test_cellular.py 60 tests, no ROS2 runtime

Test coverage

60 tests: NMEA checksum validation, DDMM→decimal degrees, GGA parsing, RMC parsing, HDOP covariance, ENU velocity, AT+CSQ/CREG/COPS parsing, RSSI labelling, MQTT serialisation.

Hardware wiring

SIM7600X HAT USB → Orin Nano USB
  /dev/ttyUSB0  AT commands (modem init + signal poll)
  /dev/ttyUSB1  PPP data (managed by NetworkManager/pppd)
  /dev/ttyUSB2  NMEA GPS output

Usage

# All nodes (GPS + cellular monitor + MQTT bridge):
ros2 launch saltybot_cellular cellular.launch.py

# GPS only:
ros2 launch saltybot_cellular cellular.launch.py \
    launch_cellular_manager:=false launch_mqtt_bridge:=false

# Custom MQTT broker over TLS:
ros2 launch saltybot_cellular cellular.launch.py \
    mqtt_broker:=my.broker.com mqtt_port:=8883 tls_enable:=true

# Run tests:
pytest jetson/ros2_ws/src/saltybot_cellular/test/test_cellular.py
Closes #58 ## Summary Adds `saltybot_cellular` ROS2 package for the **Waveshare SIM7600X 4G HAT** (SIMCom SIM7600A-H, LTE Cat-4, GPS+GLONASS+BeiDou+Galileo). ## Nodes ### `gps_driver_node` - Reads NMEA from `/dev/ttyUSB2`, optionally starts GNSS via `AT+CGPS=1` on `/dev/ttyUSB0` - Validates NMEA checksum; parses any talker ID (GP/GN/GL) - `/gps/fix` (`NavSatFix`) — lat/lon/alt, covariance from HDOP × ±2.5m CEP - `/gps/vel` (`TwistStamped`) — ground speed + heading, ENU velocity components - `/diagnostics` — fix quality, satellite count, HDOP ### `cellular_manager_node` - Polls `AT+CSQ`, `AT+CREG?`, `AT+COPS?` every 5s via `/dev/ttyUSB0` - `/cellular/status` (`DiagnosticArray`) — rssi dBm, signal quality, network registration, operator - `/cellular/rssi` (`Int32`, dBm), `/cellular/connected` (`Bool`) - Auto-reconnect via **nmcli** (NetworkManager profile) or **pppd** (systemd service) ### `mqtt_bridge_node` - Bidirectional ROS2 ↔ MQTT relay using paho-mqtt (graceful fallback if not installed) - **ROS2→MQTT** (QoS 0 telemetry): `/saltybot/imu`, `/gps/fix`, `/gps/vel`, `/uwb/ranges`, `/person/target`, `/cellular/status` - **MQTT→ROS2** (QoS 1 commands): `saltybot/cmd` → `/saltybot/cmd`, `saltybot/estop` → `/saltybot/estop` - Per-topic rate limiting: IMU 5Hz, GPS/UWB 1Hz, person 2Hz → <<50KB/s over LTE - Optional TLS (`tls_enable: true`), configurable broker/port/prefix/auth ## Files | File | Lines | |------|-------| | `gps_driver_node.py` | 402 | | `cellular_manager_node.py` | 362 | | `mqtt_bridge_node.py` | 317 | | `config/cellular_params.yaml` | full config | | `launch/cellular.launch.py` | all nodes, all params as launch args | | `test/test_cellular.py` | **60 tests, no ROS2 runtime** | ## Test coverage 60 tests: NMEA checksum validation, DDMM→decimal degrees, GGA parsing, RMC parsing, HDOP covariance, ENU velocity, AT+CSQ/CREG/COPS parsing, RSSI labelling, MQTT serialisation. ## Hardware wiring ``` SIM7600X HAT USB → Orin Nano USB /dev/ttyUSB0 AT commands (modem init + signal poll) /dev/ttyUSB1 PPP data (managed by NetworkManager/pppd) /dev/ttyUSB2 NMEA GPS output ``` ## Usage ```bash # All nodes (GPS + cellular monitor + MQTT bridge): ros2 launch saltybot_cellular cellular.launch.py # GPS only: ros2 launch saltybot_cellular cellular.launch.py \ launch_cellular_manager:=false launch_mqtt_bridge:=false # Custom MQTT broker over TLS: ros2 launch saltybot_cellular cellular.launch.py \ mqtt_broker:=my.broker.com mqtt_port:=8883 tls_enable:=true # Run tests: pytest jetson/ros2_ws/src/saltybot_cellular/test/test_cellular.py ```
sl-webui added 1 commit 2026-03-01 00:42:47 -05:00
Adds saltybot_cellular ROS2 package for the Waveshare SIM7600X 4G HAT
(SIMCom SIM7600A-H) providing GPS telemetry, modem monitoring, and
MQTT relay over cellular for remote operation.

gps_driver_node:
  - Opens /dev/ttyUSB2 (NMEA), optionally sends AT+CGPS=1 on /dev/ttyUSB0
  - Parses GGA (position) + RMC (velocity) from any NMEA talker (GP/GN/GL/GA)
  - Validates NMEA checksum before parsing
  - Publishes /gps/fix (NavSatFix, covariance from HDOP × ±2.5m CEP)
  - Publishes /gps/vel (TwistStamped, ENU vE/vN from course-over-ground)
  - Publishes /diagnostics (fix quality, sat count, HDOP)

cellular_manager_node:
  - Polls AT+CSQ, AT+CREG?, AT+COPS? every 5s over /dev/ttyUSB0
  - Publishes /cellular/status (DiagnosticArray: rssi, network, connected)
  - Publishes /cellular/rssi (Int32, dBm) and /cellular/connected (Bool)
  - Auto-reconnect via nmcli or pppd when data link drops

mqtt_bridge_node:
  - paho-mqtt client (graceful degradation if not installed)
  - ROS2→MQTT QoS 0: /saltybot/imu, /gps/fix, /gps/vel, /uwb/ranges,
      /person/target, /cellular/status
  - MQTT→ROS2 QoS 1: saltybot/cmd→/saltybot/cmd, saltybot/estop→/saltybot/estop
  - Per-topic rate limiting (imu:5Hz, gps:1Hz, person:2Hz) → <<50KB/s budget
  - Optional TLS, configurable broker/port/prefix/auth

Deliverables:
  saltybot_cellular/gps_driver_node.py      — 402 lines
  saltybot_cellular/cellular_manager_node.py — 362 lines
  saltybot_cellular/mqtt_bridge_node.py      — 317 lines
  config/cellular_params.yaml               — full config documented
  launch/cellular.launch.py                 — all nodes, all params as args
  test/test_cellular.py                     — 60 pytest tests, no ROS2

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
seb merged commit 528034fe69 into main 2026-03-01 00:51:16 -05:00
seb deleted branch sl-controls/cellular-gps 2026-03-01 00:51:17 -05:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: seb/saltylab-firmware#65
No description provided.