feat: publish iOS GPS to MQTT saltybot/ios/gps at 1 Hz (Issue #681) #2

Open
sl-ios wants to merge 4 commits from sl-ios/issue-681-ios-gps-mqtt into main
Collaborator

Summary

  • Adds MQTTClient.swift — minimal MQTT 3.1.1 client using Network.framework, no external dependency
  • Implements CONNECT + PUBLISH QoS 0 + PINGREQ keepalive
  • Publishes phone GPS to saltybot/ios/gps at 1 Hz while streaming is active

Broker config

  • Host: 192.168.87.29:1883 (LAN), user mqtt_seb
  • NSAllowsLocalNetworking already covers this; added explicit NSExceptionDomains entry for 192.168.87.29

JSON payload (matches sensor_dashboard.py)

{"ts": 1234567890.0, "lat": 43.452, "lon": -79.756, "alt_m": 120.0, "accuracy_m": 5.0, "speed_ms": 0.0, "bearing_deg": 0.0, "provider": "gps"}

Design

  • lastKnownLocation cached on every CLLocationManager delegate callback
  • Separate 1 Hz Timer publishes to MQTT — rate is stable regardless of GPS update cadence
  • MQTT connect/disconnect tied to Follow-Me start/stop
  • Auto-reconnects on TCP drop (3s backoff)

Test plan

  • Open SulTee/SulTee.xcodeproj, deploy to iPhone 15 Pro
  • Tap Start Follow-Me
  • mosquitto_sub -h 192.168.87.29 -u mqtt_seb -P mqtt_pass -t saltybot/ios/gps — verify 1 Hz JSON messages
  • Verify tracker page shows iOS phone marker alongside robot marker

🤖 Generated with Claude Code

## Summary - Adds `MQTTClient.swift` — minimal MQTT 3.1.1 client using `Network.framework`, no external dependency - Implements CONNECT + PUBLISH QoS 0 + PINGREQ keepalive - Publishes phone GPS to `saltybot/ios/gps` at 1 Hz while streaming is active ## Broker config - Host: `192.168.87.29:1883` (LAN), user `mqtt_seb` - `NSAllowsLocalNetworking` already covers this; added explicit `NSExceptionDomains` entry for `192.168.87.29` ## JSON payload (matches `sensor_dashboard.py`) ```json {"ts": 1234567890.0, "lat": 43.452, "lon": -79.756, "alt_m": 120.0, "accuracy_m": 5.0, "speed_ms": 0.0, "bearing_deg": 0.0, "provider": "gps"} ``` ## Design - `lastKnownLocation` cached on every `CLLocationManager` delegate callback - Separate 1 Hz `Timer` publishes to MQTT — rate is stable regardless of GPS update cadence - MQTT connect/disconnect tied to Follow-Me start/stop - Auto-reconnects on TCP drop (3s backoff) ## Test plan - [ ] Open `SulTee/SulTee.xcodeproj`, deploy to iPhone 15 Pro - [ ] Tap **Start Follow-Me** - [ ] `mosquitto_sub -h 192.168.87.29 -u mqtt_seb -P mqtt_pass -t saltybot/ios/gps` — verify 1 Hz JSON messages - [ ] Verify tracker page shows iOS phone marker alongside robot marker 🤖 Generated with [Claude Code](https://claude.com/claude-code)
sl-ios added 4 commits 2026-04-04 11:11:24 -04:00
- SulTee SwiftUI app targeting iOS 17+, iPhone 15 Pro
- CoreLocation: dual-frequency GPS (L1+L5) continuous updates, background mode enabled
- CoreMotion: 100 Hz IMU (accel + gyro + attitude + gravity), magnetometer via device motion
- CMAltimeter: barometer relative altitude + pressure streaming
- CLLocationManager heading updates for magnetometer heading
- URLSessionWebSocketTask client connecting to ws://192.168.86.158:9090
- JSON protocol: {type, timestamp, data} for gps/imu/heading/baro messages
- Auto-reconnect on disconnect (2s backoff)
- Haptic feedback on incoming "haptic" messages from bot
- Background streaming: UIBackgroundModes location + external-accessory in Info.plist
- SwiftUI status UI: connection banner, sensor rate counters (Hz), start/stop follow-me button
- Dev team Z37N597UWY (vayrette@gmail.com), bundle ID com.saltylab.sultee

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Default URL updated from ws://192.168.86.158:9090 (LAN) to ws://100.64.0.2:9090 (Tailscale)
- URL persisted in UserDefaults under key "orinURL" — survives app restarts
- WebSocketClient.url is now mutable so it can be updated without recreation
- SensorManager.updateURL(_:) applies a new URL when not streaming
- ContentView: editable text field for Orin address with Apply button, disabled while streaming
- Connection banner shows the active URL instead of hardcoded string

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
NSAllowsLocalNetworking covers CGNAT 100.64.0.0/10 range used by Tailscale,
fixing ATS blocking plain ws:// connections. Also adds NSExceptionDomains
entry for 100.64.0.2 as explicit fallback.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds minimal MQTT 3.1.1 client (MQTTClient.swift) using Network.framework —
no external dependency. Implements CONNECT + PUBLISH (QoS 0) + PINGREQ keepalive.

- Broker: 192.168.87.29:1883 (user: mqtt_seb)
- Topic: saltybot/ios/gps
- Rate: 1 Hz Timer, decoupled from GPS update rate
- Payload matches sensor_dashboard.py format:
  {ts, lat, lon, alt_m, accuracy_m, speed_ms, bearing_deg, provider: "gps"}
- lastKnownLocation cached from CLLocationManagerDelegate, published on timer
- MQTT connect/disconnect tied to startStreaming()/stopStreaming()
- ATS NSExceptionDomains extended to include 192.168.87.29 (MQTT broker LAN IP)
- MQTTClient.swift registered in project.pbxproj Sources build phase

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This pull request can be merged automatically.
You are not authorized to merge this pull request.

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin sl-ios/issue-681-ios-gps-mqtt:sl-ios/issue-681-ios-gps-mqtt
git checkout sl-ios/issue-681-ios-gps-mqtt
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-ios#2
No description provided.