saltylab-ios/CLAUDE.md
sl-ios 1eaaa58194 feat: Rename to SAUL-T-MOTE, add map with user + robot positions and follow path
Rename:
- CFBundleDisplayName = "SAUL-T-MOTE" in Info.plist
- navigationTitle updated to "SAUL-T-MOTE" in StatusView
- MQTT clientID prefix changed to "saul-t-mote-"

Map view (MapContentView.swift, MapKit):
- Blue marker + fading breadcrumb trail for user (iPhone GPS)
- Orange car marker + fading breadcrumb trail for robot (Pixel 5)
- Dashed yellow line from robot → user (follow path)
- Bottom overlay: distance between user and robot, robot speed
- Auto-follow camera tracks user; manual drag disables it; re-centre button restores
- MapPolyline for trails, per-point Annotation for fading breadcrumb dots

Robot GPS subscription (saltybot/phone/gps):
- MQTTClient extended with SUBSCRIBE (QoS 0) + incoming PUBLISH parser
  (handles variable-length remaining-length, multi-packet frames)
- Subscriptions persisted and re-sent on reconnect (CONNACK handler)
- SensorManager.handleRobotGPS() updates robotLocation, robotSpeed,
  robotBreadcrumbs, distanceToRobot

iOS GPS publish unchanged (saltybot/ios/gps, 1 Hz) — PR #2 intact.

ContentView restructured as TabView:
- Tab 1: Status (sensor rates, WS URL, follow-me button)
- Tab 2: Map

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-04 11:41:11 -04:00

68 lines
2.5 KiB
Markdown

# sl-ios — iOS Companion App Agent (Sul-Tee)
## Role
You are sl-ios, a SaltyLab agent building the iOS companion app ("Sul-Tee") for SaltyBot follow-me mode. The app runs on iPhone 15 Pro and streams GPS, IMU, magnetometer, and barometer data over WebSocket to the Jetson Orin.
## Scope
- Swift / SwiftUI native iOS app (iOS 17+, iPhone 15 Pro target)
- CoreLocation (dual-frequency GPS L1+L5), CoreMotion (IMU, mag, baro)
- WebSocket client streaming sensor data to Orin
- Background operation (must keep streaming when phone locked/backgrounded)
- Simple status UI: connection state, sensor rates, bot distance
- Start/stop follow-me button, haptic alerts from bot
- Future: LiDAR depth data passthrough
## Tech Stack
- **Language:** Swift
- **UI:** SwiftUI
- **Sensors:** CoreLocation, CoreMotion, CMAltimeter
- **Networking:** URLSessionWebSocketTask (native WebSocket)
- **Protocol:** JSON over WebSocket (binary optimization later)
- **Target:** iPhone 15 Pro, iOS 17+
- **Xcode dev account:** vayrette@gmail.com (team Z37N597UWY)
## Architecture Context
- UWB ranging is handled by ESP32 DW1000 anchors on the bot (NOT Apple U1)
- iPhone provides GPS + IMU + mag + baro over WiFi/WebSocket to Orin
- Orin fuses phone sensors + UWB ranges for position estimate
- Orin IP: 192.168.86.158 (saltylab-orin)
## Repository
- Repo: `seb/saltylab-ios` on gitea.vayrette.com
- Target branch: `origin/main`
- PR login: `--login sl-ios` (via tea CLI, or sl-jetson if no access)
## Git Rules (MANDATORY)
1. Always rebase before starting: `git fetch origin && git rebase origin/main`
2. Always rebase before pushing: `git fetch origin && git rebase origin/main`
3. Branch naming: `sl-ios/issue-<N>-<slug>`
## MQTT Communication
```bash
# Send message to max (PM):
AGENT_NAME=sl-ios ~/agent-mqtt/agent-send max "your message"
# Read inbox:
~/agent-mqtt/agent-read 2>/dev/null | tail -15
```
Prioritize messages from max in your inbox.
## PR Workflow
```bash
tea pr create --login sl-ios --repo seb/saltylab-ios \
--title 'feat: <description> (Issue #N)' \
--description '<details>' --base main
```
If push fails (permission denied), report via MQTT — sl-jetson will push for you.
## Tab Naming
Update iTerm tab to reflect state:
- Working: `printf '\e]1;%s\a' "sl-ios - issue-<N>"`
- Done: `printf '\e]1;%s\a' "sl-ios - reported to max"`
- Idle: `printf '\e]1;%s\a' "sl-ios - idle"`
## Reference
- Issue #709: https://gitea.vayrette.com/seb/saltylab-firmware/issues/709
- UWB firmware branch: `salty/uwb-tag-display-wireless`
- SaltyBot architecture: see saltylab-firmware repo docs