UserDefaults persists across app reinstalls. Any device that
previously stored ws://100.64.0.2:9090 would ignore the new
defaultOrinURL constant. On init, if the saved URL contains
'100.64.0.2' it is cleared so the new default wss://www.saultee.bot/ws
is used on next launch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
HAL v3.4 extends the range notify from 8→12 bytes:
[0-3] int32 LE front_mm
[4-7] int32 LE back_mm
[8-11] float32 LE best_rssi dBm (NEW)
Parser now handles both lengths (8=v3.3, 12=v3.4) by detecting
packet size. RSSI is extracted as Double and assigned to both
Front and Back anchors (shared signal quality); nil for v3.3.
UI already conditionally renders rssiString (guard on Optional).
Added Data.readFloat32LE(at:) helper using IEEE 754 bit-pattern
reinterpretation (little-endian UInt32 → Float(bitPattern:)).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The app's old parser expected a multi-anchor protocol:
[count][anchorID+rangeMM+RSSI+age]×N (min 10 bytes)
HAL's firmware sends a fixed 8-byte packet:
[int32 front_mm LE][int32 back_mm LE]
With the old parser data[0] was interpreted as anchor count
(e.g. 0xF8 = 248 for a 4.6m reading), the loop guard failed
immediately, and every notify returned [] — hence "waiting for
ranging data" despite the tag showing live ranges on OLED.
Changes:
- BLEPackets: detect 8-byte HAL format by length; decode as
anchor id=0 (Front) and id=1 (Back); legacy multi-anchor path
retained for forward compatibility
- AnchorInfo: rssiDBm is now Optional (nil when not reported);
label maps id 0→"F", 1→"B" for the two-anchor HAL format
- BLEStatusView: guard on optional rssiString before rendering
Auto-reconnect confirmed correct (2s delay, bluetooth-central
background mode declared in Info.plist).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- BLEManager: fall back to withResponse write when characteristic lacks
PROPERTY_WRITE_NR (0x4); NimBLE defaults to PROPERTY_WRITE (0x8) only,
causing iOS to silently drop every withoutResponse write at 5/10 Hz
- BLEManager: scan withServices:nil so NimBLE scan-response UUIDs are found;
filter by UWB_TAG name prefix in didDiscover
- BLEPackets: remove custom clamping extensions (Int16/Int32/UInt16/UInt8)
that shadowed Swift.max() with Int16.max inside the extension scope;
stdlib BinaryInteger.init(clamping:) covers all cases
- BLEStatusView: use explicit Binding(get:set:) for gpsStreamEnabled /
imuStreamEnabled — SwiftUI cannot synthesize $binding through a let
computed property backed by a class reference
- SensorManager: fix isRelativeAltitudeAvailable() — it is a class method,
not an instance method; also fixed inverted double-negative logic
Note for HAL: add NimBLE PROPERTY_WRITE_NR to GPS (abcdef3) and IMU
(abcdef4) characteristics for no-ACK streaming at 5/10 Hz.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
iOS CoreBluetooth only fires didDiscover when the service UUID is in the
primary ADV_IND packet. NimBLE (ESP32) puts service UUIDs in the scan
response by default, so scanForPeripherals(withServices:[uuid]) never
returned the UWB_TAG device.
Fix: scan with withServices:nil and filter by device name prefix "UWB_TAG"
in didDiscover. Also fix the broken OR guard (|| name.isEmpty == false
passed any named peripheral) to a clean hasPrefix check.
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>
- 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>