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

2.5 KiB

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

# 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

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: seb/saltylab-firmware#709
  • UWB firmware branch: salty/uwb-tag-display-wireless
  • SaltyBot architecture: see saltylab-firmware repo docs