Architecture

Technical Specifications

A breakdown of MotoDash's internal architecture, design decisions, and the reasoning behind them. Written for engineers and technically curious riders.

Why deterministic state machines while riding

Riding creates conditions that are genuinely hostile to non-deterministic UI behavior. Vibration, gloves, reduced attention bandwidth, and variable lighting mean that any ambiguity in interaction logic translates directly into missed or double-triggered actions. A system where the same input always produces the same output — regardless of timing edge cases or previous state — is categorically safer in this context.

MotoDash uses finite state machines (FSMs) rather than ad-hoc event handlers precisely because FSMs make every possible state explicit. There are no hidden transitions, no ambiguous race conditions, no side effects that accumulate over time. Each state is defined. Each transition has a condition. That's the contract.

Predictability also reduces cognitive load. If a rider knows exactly what three magnet taps will do — always — they don't have to think about it. Automaticity is the goal. Thinking about your tools while riding is a cost you can't afford.

Dual FSM architecture — Magnet Mode

Magnet Mode uses two coordinated state machines. The Outer FSM tracks proximity context: how close the magnet ring is to the phone. The Inner FSM tracks gesture timing: whether a tap sequence is in progress, and when to commit it.

Outer FSM — Proximity and bias

The Outer FSM has three primary states: far, near, and bias. The magnetometer reads a continuous flux value. When that value crosses the "near" threshold, the outer state transitions from far → near and signals the inner FSM to begin listening. When the flux drops back below the "far" threshold (with hysteresis margin), it transitions back. Bias mode activates when the magnet has been held stationary and near for too long — a clear indicator of phone placement in a tank bag or pocket rather than intentional gesture.

Diagram 1 — Outer FSM
stateDiagram-v2 [*] --> far far --> near : flux ≥ near_threshold near --> far : flux ≤ far_threshold near --> bias : static duration > bias_timeout bias --> far : flux drops below threshold bias --> near : gesture detected (reset)

Inner FSM — Tap grouping

The Inner FSM activates when the Outer FSM enters the near state. It is responsible for collecting individual tap events into an intentional gesture. Once the inter-tap timeout expires without a new tap, the inner FSM commits the accumulated count and transitions back to idle. This results in clean 1-tap / 2-tap / 3+-tap mapping.

Diagram 2 — Inner FSM
stateDiagram-v2 [*] --> idle idle --> active : outer FSM enters "near" active --> active : tap detected (count++) active --> commit : inter-tap timeout expires commit --> idle : action dispatched idle --> idle : outer FSM exits (ignored) note right of commit count=1 → next count=2 → select count≥3 → back end note

Why bias mode exists

A static magnet near the phone — such as a magnetic mount, a tank bag clasp, or a handlebar mount with a magnetic component — would continuously register as "near" without bias protection. This would cause constant false positives and make the mode unusable in most real-world riding setups.

Bias mode detects that the signal has been stable and near for a duration exceeding a configurable threshold (far beyond what any intentional gesture would take). Once in bias state, the system treats that baseline as environmental noise and ignores it. A genuine gesture — characterized by a sharp flux change — resets the bias and resumes listening.

Why hysteresis prevents false positives

Without hysteresis, a signal hovering near the near/far threshold would cause rapid state oscillation — flickering between near and far dozens of times per second. Each flicker could trigger spurious actions. Hysteresis solves this by requiring a larger signal drop to transition far before the transition back to near can occur again. The two thresholds create a deadband that dampens noise.

Why adaptive baseline adaptation is needed

Phone magnetometers are affected by environmental fields: passing near a car, engine vibration, speaker magnets, and changes in riding posture all shift the ambient flux reading. A fixed threshold calibrated at rest will drift out of range within minutes on an actual ride.

The adaptive baseline continuously adjusts the reference floor of the flux reading. It tracks the median value over a rolling window of samples when the inner FSM is idle, and recalibrates thresholds relative to that updated floor. This means the system self-corrects without user intervention.

Why sampling rate changes

When the Outer FSM is in the far state, the magnetometer is polled at a lower rate. There's nothing meaningful to detect, and high-frequency polling drains the battery. When the FSM transitions to near, sampling rate increases to capture fine-grained tap timing. This adaptive rate is the primary mechanism for keeping battery consumption acceptable in active ride conditions.

Why TTS responds only to magnet-triggered navigation

Text-to-speech feedback is only spoken when a navigation action was triggered by a magnet gesture — never by touch. This is intentional. When you tap the screen, you have confirmed visual attention on the device. TTS would add noise without value. When a gesture is made via the magnetic ring without looking at the screen, TTS becomes essential: it closes the feedback loop and confirms what happened.

MotoDash implements this via a global flag that is set on any magnet-originated action. The TTS subsystem checks this flag before speaking. Touch actions never set the flag. This design avoids an explicit mode-check throughout the codebase.

Why not on the Play Store

Play Store distribution requires compliance with policies around permission usage, background operation, and UI behavior that would require removing or fundamentally compromising core features. MotoDash uses permissions for magnetometer access, audio routing, and full-screen overlay that fall into restricted categories under current Play policies.

Distributing as a direct APK means the developer retains full control over the interaction model. The GPL v3 license ensures the source is always auditable. Installing from a direct APK requires enabling unknown sources on Android — this is a deliberate friction point that selects for technically capable users who can assess what they're installing.

Why settings require restart

Settings in MotoDash are applied at initialization time rather than dynamically. This is a deliberate tradeoff: applying settings dynamically would require the app to handle every possible runtime reconfiguration path, introducing complexity and potential for race conditions in a system designed for predictability. Applying them at launch guarantees a known, clean state every time.

Why settings are hidden after 10 seconds

The settings menu is visible only during the first 10 seconds after launch. After that window, it is hidden. This design choice keeps the in-ride interface clean and prevents accidental settings modification while riding. Settings are a launch-time concern, not a ride-time concern. The 10-second window is long enough to adjust before mounting.

OLED and display considerations

OLED displays consume power proportional to pixel brightness. A pure black pixel draws near-zero power because that pixel is simply off. MotoDash's dark theme uses #000000 backgrounds rather than dark grey to take full advantage of this on OLED panels.

The built-in screensaver reduces burn-in risk by blanking or dimming the display during periods of inactivity on a ride. OLED panels are susceptible to burn-in when static UI elements are displayed at high brightness for extended periods.

Regarding accent color: red pixels on OLED activate only the red sub-pixel. Green and blue sub-pixels in white or colored pixels consume more power. Red also shows less long-term degradation than blue, which decays faster. Red is the recommended accent color for both battery efficiency and panel longevity.

Why offline-first design matters

A riding utility that requires network connectivity is unreliable by definition. Tunnels, rural areas, signal blackouts, and SIM failover all create gaps where a network-dependent app either stalls or silently stops functioning. An offline-first design means the app works identically regardless of signal — there's no degraded mode, no loading state, no latency.

Why zero telemetry

Telemetry is a liability in the context of a riding utility. It requires network permission, which conflicts with the offline-first model. It requires consent management. It introduces latency spikes when the telemetry endpoint is unreachable. And it transmits data about how a user interacts with their phone while riding — which is categorically sensitive.

MotoDash sends nothing. No crash reports. No usage analytics. No session data. The GPL v3 license ensures this can be independently verified from source.