A real-life Log Pose from One Piece. A wrist-worn compass that always points toward home — built as a birthday gift for Kashish.
Three components work together:
- iPhone app — calculates bearing-to-home from GPS and sends it over BLE
- Apple Watch app — standalone digital Log Pose compass on your wrist
- Arduino prop — a 3D-printed wrist-worn Log Pose with a physical needle driven by an ESP32
The iPhone is the brain. It computes the geographic bearing from your current location to a saved "home" location, subtracts its own heading, and sends the resulting needle angle to the ESP32 over Bluetooth Low Energy. The ESP32 drives a servo to that angle — just like the anime. The Apple Watch runs an independent compass with its own magnetometer, and can also take direct BLE control of the prop.
Screenshots and demo video coming soon — needle tracking bearing as the phone moves, Watch compass in action, and the physical prop spinning.
iPhone (GPS + heading)
│
├──► BLE (direct) ──► ESP32 + servo (physical needle) ◄──┐
│ Servo Angle characteristic → │
│ │
└──► WatchConnectivity ──► Apple Watch (digital compass) ──┘
BLE (when watch takes control)
The prop can only hold one BLE connection at a time. Phone holds it by default; the Watch can take control via a manual handoff (tap the antenna button on Watch → phone disconnects → Watch connects and drives the prop with its own heading). When the Watch releases, the phone reconnects automatically.
No WiFi or cloud required for the prop — BLE only, ~10–30m range. Prop holds its last needle angle when BLE disconnects.
The ESP32 runs a BLE GATT server. The iPhone is the BLE central — it scans, connects, and writes servo angles over one characteristic.
Device name: ESP32C3-Servo
Service UUID: 4FAFC201-1FB5-459E-8FCC-C5C9C331914B
| Characteristic | UUID | Direction | Payload |
|---|---|---|---|
| Servo Angle | BEB5483E-36E1-4688-B7F5-EA07361B26A8 |
Phone → ESP32 | UTF-8 text string, integer 0–180. Heading-compensated by the phone before sending. |
Full Arduino integration doc:
docs/arduino-handover.md— covers hardware, BLE sketch, encoding, and testing notes.
The iPhone computes a heading-compensated needle angle (displayBearing = geographicBearing − deviceHeading) and encodes it for the servo: servo = 90 − signed(displayBearing), clamped to [0, 180]. Centering at 90° means servo 90° = home directly ahead; the − sign accounts for the servo being reverse-mounted (increasing angle = CCW from front). The ESP32 has no orientation sensors — all heading compensation happens on the phone.
Firebase is used by the iPhone for:
- Anonymous Auth — generates a UID stored in Keychain, used to gate RTDB writes
- RTDB — writes
/bearing,/home,/trigger(legacy; the prop uses BLE directly)
Project ID: log-pose-3851e
The ESP32 does not need to connect to Firebase. All prop communication is over BLE.
- Run the iPhone app once
- Check the Xcode console for:
Firebase UID: <uid> - In Firebase Console → Realtime Database, set
/ownerUIDto that UID string - Rerun. Firebase writes will start succeeding.
GOOGLE_CLOUD_QUOTA_PROJECT=log-pose-3851e \
firebase deploy --only database --project log-pose-3851e- Xcode 16+
- xcodegen (
brew install xcodegen) GoogleService-Info.plistatLogPose/GoogleService-Info.plist(gitignored — ask Mit)- Apple Developer account for device deployment
# Generate the Xcode project from project.yml
/opt/homebrew/bin/xcodegen generate
# Open in Xcode
open LogPose.xcodeprojBuild the LogPose scheme for iPhone or LogPose WatchKit App for Apple Watch.
| Target | Platform | Description |
|---|---|---|
| LogPose | iOS 17+ | Main iPhone app |
| LogPose WatchKit App | watchOS 10+ | Standalone watch compass |
| LogPose Complication | watchOS 10+ | Watch face widget showing direction |
- iPhone: select
LogPosescheme, plug in phone,▶️ - Watch: the watch app embeds inside the iPhone app. On first install, open the Watch app on iPhone → My Watch → scroll to Log Pose → Install. Then run
LogPose WatchKit Appfrom Xcode.
⌘U with the LogPose scheme selected. Tests cover BearingCalculator math, HomeLocation persistence, and BLE encoding/decoding.
CLLocationManagerfires a GPS update on the iPhoneAppViewModelcomputes bearing using the great-circle forward azimuth formula- If bearing changed by ≥2° (circular distance, handles 359°→1° correctly):
- Writes to
BLEBearingManager→ prop over BLE - Sends to Watch via WatchConnectivity
- Writes to
- On the iPhone, the compass needle is heading-compensated:
displayBearing = geographicBearing − deviceHeadingusing shortest-path rotation (continuous angle accumulation) so the needle never does a 360° spin at the 0°/360° boundary - The Watch runs the same heading-compensation algorithm locally using its own magnetometer
Reach out to Mit for Firebase credentials, GoogleService-Info.plist, or integration questions. For the hardware prop, start with docs/arduino-handover.md.