- Kotlin 73.1%
- C 21.1%
- Python 4.3%
- JavaScript 0.7%
- Dockerfile 0.6%
- Other 0.2%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
| .claude/agents | ||
| .forgejo/workflows | ||
| companion | ||
| docs | ||
| gradle | ||
| shared | ||
| tooling/docker | ||
| tools | ||
| watchapp | ||
| .editorconfig | ||
| .gitignore | ||
| build.gradle.kts | ||
| gradle.properties | ||
| gradlew | ||
| gradlew.bat | ||
| LICENSE | ||
| README.md | ||
| settings.gradle.kts | ||
PedalPebble
A cycling computer for the Pebble Time 2 and Pebble Round 2, paired with an Android phone.
Shows speed, average speed and heart rate on the wrist; later, turn-by-turn navigation for routes planned in komoot, and a map view.
Latest installable build (debug .pbw + .apk, updated on every push): the "dev build" badge above,
or docs/CI.md if it hasn't gone green yet.
Status
Planning complete and closed. Development has started: the Pebble toolchain is installed and
verified end to end (#2,
D54), and watchapp/
holds a scaffolded project that builds for emery, gabbro and basalt and runs on both
emulators. See docs/DEV.md for the local setup.
No further planning rounds happen until the two blocker spikes below have run — see D48. Their findings reopen the documents; nothing else does.
Installing
Two halves, both required: a watchapp on the Pebble and a companion app on the phone. There is no Rebble store or Core Devices store listing — distribution is Forgejo releases on this repo (see docs/DEV.md#releasing for the full scheme):
- Companion: install the
.apkfrom the dev build (updated on every push) or a tagged release — Android will ask for an "install unknown apps" grant the first time. - Watchapp: sideload the
.pbwfrom the same release page ontoemeryand/orgabbro— one.pbwcontains both platforms.
If you only update one half: the watch and phone compare a protocol contract version the instant they connect, and refuse to start a ride on a mismatch rather than run with plausible-looking wrong numbers (PROTOCOL.md §6, #53). Both sides show which one is stale. Most releases bump the app version without touching this contract at all — the watchapp/companion version strings (kept identical between the two) and the protocol contract version are deliberately two different numbers; see docs/DEV.md#releasing.
Hardware and platform
| Watches | Pebble Time 2 — emery, 200×228 rectangular, 64-colour e-paper, 4 buttonsPebble Round 2 — gabbro, 260×260 round, colour e-paper at 283 DPI, 4 buttons |
| Heart rate | The watch's own optical sensor — Time 2 only; the Round 2 has no HRM, so the field is simply absent there |
| Phone | Android — supplies GPS, navigation and the map |
| Speed | Phone GPS, with an optional BLE wheel sensor (Cycling Speed & Cadence, 0x1816) |
| Routes | GPX, imported through the Android share sheet — komoot, bikerouter.de, RideWithGPS, Strava |
| Turn cues | Read from the GPX where the planner provides them; otherwise derived on the phone at import |
The Pebble has no GPS of its own, and iOS is not supported: Core Devices discontinued PebbleKit iOS, so a companion app is only possible on Android.
Documentation
Start here: docs/QUICK-REF.md — one-page reference for common tasks
| Document | What it covers |
|---|---|
| docs/QUICK-REF.md | One-page reference for build, test, debug, git, code review, and release checklists |
| docs/FAQ.md | Answers to common questions about architecture, testing, performance, security, and routing |
| docs/DECISIONS.md | Every significant decision, with its rationale and the options rejected |
| docs/ARCHITECTURE.md | Module layout, data flow, dependencies, and where code belongs for each feature |
| docs/DESIGN.md | Watch display: page descriptors, templates, default pages, button model |
| docs/REQUIREMENTS.md | Functional and non-functional requirements, and what is still open |
| docs/PROTOCOL.md | The message contract — every AppMessage key, both directions, and the update-rate policy |
| docs/TEAM.md | Who works on the project, what each persona owns, when to call which |
| docs/PROCESS.md | How work flows: the two decoupled CI lanes, release gates, execution order |
| docs/TESTING.md | The test strategy: every NFR classified, the 15 release gates, coverage policy |
| docs/TESTING-PATTERNS.md | How to write tests the PedalPebble way: patterns, fixtures, edge cases, and examples |
| docs/PERFORMANCE.md | Battery and performance budgets, measurement tools, optimization strategies, common pitfalls |
| docs/READINESS.md | The 2026-09-03 readiness review: what was verified before the first commit |
| docs/DEV.md | Local setup: installing the Pebble toolchain, building and running on the emulators |
| docs/DEV-SETUP.md | IDE setup (Android Studio, VS Code, IntelliJ), build system, testing, debugging, git workflow, and external resources |
| docs/TROUBLESHOOTING.md | Solutions for common problems: build failures, test issues, emulator problems, performance debugging |
| docs/CI.md | The four workflow files: what each lane actually runs, the pinned toolchain images, and what's still a manual step for Robert |
| docs/CONTRIBUTING.md | How to understand the codebase and add features: code organization, testing patterns, and a walk-through example |
| docs/CHECKLISTS.md | Practical checklists for feature development, bug fixes, code review, releases, and troubleshooting |
| docs/SECURITY.md | Secrets management, input validation, permissions, privacy, and secure transport (PebbleKit 2 config) |
| companion/README.md | The Android Gradle project: module layout, building, and Gradle-for-.NET-developers notes |
docs/archive/PLAN.md is the original 2026-08-31 plan. It is kept for the reasoning, not for the content: its architecture, message contract and Spike C definition are all overtaken, and its issue table stops at #47 of 70. Start with DECISIONS.md instead.
Planning routes
Any GPX works, but where you plan the route decides how good the turn instructions are.
| Planner | Turn cues in its GPX? | What PedalPebble does |
|---|---|---|
| bikerouter.de | Yes — enable turn instructions on export | Reads them directly. Exact, offline, nothing else needed. Recommended |
| cycle.travel, RideWithGPS, Garmin | Usually | Reads them directly |
| komoot | No — geometry only | Derives cues at import, via BRouter if installed, otherwise a geometric guess |
bikerouter.de is BRouter's own web front end, so its exported cues are the same quality as an on-device BRouter enrichment — without installing BRouter or downloading segment files. When a geometry-only file arrives, the import says so and names a planner that would not have. See D40.
Issue tracker
All work is tracked as issues in this repository:
https://git.butzei.de/robert/PedalPebble/issues
73 issues across 8 milestones, one milestone per phase. Issues carry blocked-by dependencies, so
the issue list shows what is actually startable. The graph — 91 edges, entered 2026-09-03 from the
bodies of all 73 issues — is re-derivable from docs/DECISIONS.md
(D57) if the tracker is ever
rebuilt. Its finding: the true roots are #14, #2 and #24, not the two labelled blockers;
six issues (#1, #2, #6, #14, #23, #48) are startable today.
| Milestone | Issues | What it delivers |
|---|---|---|
| Bootstrap | 2 | Docs and MIT licence on main, repo pushable |
| Phase 0 — Toolchain & spikes | 8 | SDK working; CI; the three spikes that de-risk everything else |
| Phase 1 — Ride view | 11 | Watch-only build on emery: page system, speed, heart rate, ride timer |
| Phase 1b — Generalise the display | 2 | HERO1, GRID6, German, and the round Pebble Round 2 |
| Phase 2 — Companion & speed | 16 | Android app: background GPS, speed/cadence/power sensors, GPX recording |
| Phase 3 — Navigation | 23 | Turn-by-turn from an imported GPX |
| Phase 4 — Map view | 4 | Vector map on the watch |
| Phase 5 — Polish | 7 | Settings, laps, export, distribution, re-routing |
Phase 1 deliberately stops at emery with two templates, so the first rideable build lands before the
display work is generalised — see D32.
Labels are area:* (watchapp, companion, shared, tooling, docs), kind:* (feature, spike, chore,
test) and blocker.
The tracker reflects the 2026-09-02 review (D37–D48). Twenty-three issues carry a dated
Update — 2026-09-02 section recording what changed and why, in the same style as the two earlier
review rounds; three issues are new (#71 voice announcements, #72 route-source guidance, #73 the
deferred re-route splice).
Start here
Two issues are labelled blocker, and between them they gate most of the project.
#4 — Spike A: PebbleKit Android round-trip decides the transport, and almost all of Phases 2–4 depends on it. It tries PebbleKit Android 2 first — a maintained Kotlin library that replaces the 2016 bridge's broadcast intents with bound services — and 4.0.1 second. The most likely failure of either is Android 11+ package visibility, not the bridge, so the spike must rule that out before reporting anything broken.
It also has to verify the fallback, which was as unverified as the thing it backs up, and measure what 1 Hz of link traffic costs the watch battery — the one number that could invalidate the whole architecture. See D37, D38 and D36.
#6 — Spike C: BRouter AIDL enrichment decides the komoot path. BRouter is a router, not a map-matcher: it computes its own route between the via points, which can diverge from the route you imported. Voice hints coming back is not a pass — the spike has to measure geometric fidelity, per leg rather than per route. See D25 and D41.
Its stakes are lower than they were. A route planned on bikerouter.de already carries cues (D40), and off-route guidance is a bearing arrow rather than a mid-ride re-route (D42), so BRouter is no longer on the mid-ride critical path at all.
Repository layout (partly built, mostly planned)
watchapp/ Pebble C project, targets emery and gabbro — scaffolded, see docs/DEV.md
companion/ Android app, Kotlin — scaffolded ([#14](https://git.butzei.de/robert/PedalPebble/issues/14)), see companion/README.md
shared/ message_keys.json — the AppMessage contract, generated into both sides
tools/ gpx_replay.py and other dev utilities
docs/ this documentation
Licence
MIT — see LICENSE.
Routing and turn instructions are derived from OpenStreetMap data via BRouter and, when the rider opts in, online map-matching services. OpenStreetMap data is © OpenStreetMap contributors, available under the Open Database Licence.