feat: iOS companion app - sensor streaming (Issue #709) #1

Open
sl-ios wants to merge 3 commits from sl-ios/issue-709-companion-app into main
Collaborator

Summary

Full iOS companion app (Sul-Tee) for SaltyBot follow-me mode — Issue #709.

Sensors streamed over WebSocket (ws://192.168.86.158:9090)

  • GPS: CoreLocation dual-frequency L1+L5, continuous updates, background streaming
  • IMU: CoreMotion at 100 Hz — accelerometer, gyroscope, attitude, gravity
  • Magnetometer: heading + raw XYZ field via device motion fusion
  • Barometer: CMAltimeter relative altitude + pressure

Protocol

JSON messages {type, timestamp, data} for gps / imu / heading / baro types.
Incoming {type: "haptic"} triggers UIImpactFeedbackGenerator on device.

Background operation

UIBackgroundModes: location + external-accessory — streaming continues when phone is locked.

UI

SwiftUI status view: WebSocket connection banner, per-sensor Hz counters, Start/Stop Follow-Me button, bot distance display.

Target

iPhone 15 Pro · iOS 17+ · Team Z37N597UWY · com.saltylab.sultee

Test plan

  • Open SulTee/SulTee.xcodeproj in Xcode 15
  • Set signing team to Z37N597UWY (vayrette@gmail.com)
  • Deploy to iPhone 15 Pro
  • Tap Start Follow-Me — verify WS connection to saltylab-orin:9090
  • Verify GPS / IMU / heading / baro messages arrive on Jetson side
  • Lock phone — confirm streaming continues (background location indicator visible)
  • Send {"type":"haptic"} from Orin — verify phone vibrates

🤖 Generated with Claude Code

## Summary Full iOS companion app (Sul-Tee) for SaltyBot follow-me mode — Issue #709. ### Sensors streamed over WebSocket (ws://192.168.86.158:9090) - **GPS**: CoreLocation dual-frequency L1+L5, continuous updates, background streaming - **IMU**: CoreMotion at 100 Hz — accelerometer, gyroscope, attitude, gravity - **Magnetometer**: heading + raw XYZ field via device motion fusion - **Barometer**: CMAltimeter relative altitude + pressure ### Protocol JSON messages `{type, timestamp, data}` for `gps` / `imu` / `heading` / `baro` types. Incoming `{type: "haptic"}` triggers UIImpactFeedbackGenerator on device. ### Background operation `UIBackgroundModes`: `location` + `external-accessory` — streaming continues when phone is locked. ### UI SwiftUI status view: WebSocket connection banner, per-sensor Hz counters, Start/Stop Follow-Me button, bot distance display. ### Target iPhone 15 Pro · iOS 17+ · Team Z37N597UWY · `com.saltylab.sultee` ## Test plan - [ ] Open `SulTee/SulTee.xcodeproj` in Xcode 15 - [ ] Set signing team to Z37N597UWY (vayrette@gmail.com) - [ ] Deploy to iPhone 15 Pro - [ ] Tap Start Follow-Me — verify WS connection to saltylab-orin:9090 - [ ] Verify GPS / IMU / heading / baro messages arrive on Jetson side - [ ] Lock phone — confirm streaming continues (background location indicator visible) - [ ] Send `{"type":"haptic"}` from Orin — verify phone vibrates 🤖 Generated with [Claude Code](https://claude.com/claude-code)
sl-ios added 1 commit 2026-04-03 16:52:43 -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>
sl-ios added 1 commit 2026-04-03 17:24:46 -04:00
- 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>
sl-ios added 1 commit 2026-04-03 18:02:21 -04:00
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>
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-709-companion-app:sl-ios/issue-709-companion-app
git checkout sl-ios/issue-709-companion-app
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#1
No description provided.