alt: Person using a Desk Bike under a standing desk, showcasing a healthy work from home setup with exercise integration.
Like many others adapting to new routines, the COVID-19 pandemic significantly reduced my usual physical activity. To combat this, I decided to try an under-desk exercise bike, hoping to integrate some cardio into my workday. I opted for a budget-friendly model, and it turned out to be quite functional. The desk bike offered adjustable resistance, remained stable and quiet during use, and even included Bluetooth connectivity for workout data tracking.
Initially, I considered this under-desk bike a good solution. However, potential buyers should be aware that with a conventional desk, knee clearance can become an issue. Fortunately, my workspace is configured with a desk designed for a keyboard tray – something I personally find unnecessary. Instead, I position my keyboard directly on the desk surface, elevate my chair, and utilize a footrest. This arrangement provides ample vertical space, making the desk height ideal for using a desk bike comfortably.
The Bluetooth feature was a key selling point for me. Having access to real-time metrics like speed and distance is a strong motivator for consistent exercise. As expected, the desk bike came with its own mobile app. However, the app’s user experience left much to be desired. It felt clunky, received poor user reviews, required account creation, and persistently pushed premium subscriptions. While these shortcomings were tolerable, the idea of constantly reaching for my phone and navigating through menus just to start a quick desk bike session felt disruptive to my workflow. Leaving my phone screen on for extended periods wasn’t appealing either. Furthermore, I wanted to avoid being locked into their proprietary ecosystem with my workout data.
This led me to a clear conclusion: I needed to develop my own desktop application to interface with the desk bike.
The Project: Building a Custom Desk Bike App
My project was inspired by the resourceful approach detailed in “Unbricking a $2,000 Bike With a $10 Raspberry Pi.” This article highlighted the potential for reverse-engineering fitness equipment, and I suspected my under-desk bike might operate on similar principles. If I could establish a direct connection from my desktop, creating a personalized application became a viable goal.
Project Objectives for the Desk Bike App
- Real-time Data Display: Develop a compact desktop window to show live workout data while using the desk bike.
- Workout Data Logging: Record workout metrics into a SQLite database for performance tracking, goal setting, and motivational insights related to desk bike usage.
- Automatic Workout Detection: Implement automatic start and stop functionality based on pedaling activity, streamlining the desk bike workout experience.
Initial Challenges
- Desktop Bluetooth Incompatibility: My primary workstation lacked integrated Bluetooth capabilities.
- Bluetooth LE Inexperience: I had no prior experience working with Bluetooth Low Energy (LE) technology, essential for desk bike communication.
The first challenge was easily overcome by purchasing a USB Bluetooth adapter from Amazon. The second, Bluetooth LE, would be addressed directly through the project’s learning process.
Step 1 – Bluetooth Research and Protocol Discovery
My initial step involved extensive research to gather information about Bluetooth communication and desk bike technology. This led me to “nRF Connect,” a valuable Android application available on the Google Play Store (nRF Connect). This app is designed for Bluetooth device exploration, allowing users to scan for nearby devices, connect to them, examine their services and characteristics, and monitor data transmission. Using nRF Connect, I discovered that my desk bike utilized the Nordic UART Service (NUS) protocol. This was encouraging, as it suggested the bike was communicating through a straightforward serial data stream over Bluetooth.
However, simply connecting to the desk bike with nRF Connect didn’t immediately reveal any data. While I could establish a connection and subscribe to notifications, no workout data was being transmitted. This indicated a more complex communication protocol was at play, requiring further investigation into the desk bike’s Bluetooth behavior.
To understand the data exchange, I needed to capture and analyze the raw Bluetooth traffic between the official app and the desk bike. Following the instructions in “How to get the Bluetooth Host Controller Interface logs from a modern Android phone,” I enabled Bluetooth logging on my Android phone. Simultaneously, I used screen recording to capture my interactions with the official desk bike app during workouts. After several test sessions, I downloaded the Bluetooth logs for detailed analysis.
By examining the logs in Wireshark and synchronizing them with my screen recordings, I was able to decipher the communication pattern between the app and the desk bike. I observed that the app initiated communication by sending command packets, to which the bike responded with data packets. During active workouts, the app repeatedly sent a specific command, prompting the desk bike to transmit workout data in response.
Step 2 – Establishing a Desktop Connection to the Desk Bike
From the Wireshark analysis of the Bluetooth logs, I identified six distinct command packets sent by the official app to the desk bike. My next objective was to create a basic console application on my desktop capable of mimicking the app’s communication. This involved connecting to the desk bike via Bluetooth, sending these identified commands, and processing the responses. Successful replication would confirm my understanding of the protocol and pave the way for a custom desktop app.
For this project, I chose the .NET 5.0 framework on Windows due to its seamless integration with the Windows Runtime Bluetooth API. Integrating this API into a .NET desktop application is well-documented and relatively straightforward, providing the necessary tools for Bluetooth communication.
Step 3 – Decoding the Desk Bike Data Stream
Through reverse engineering, I began to understand the command structure. The first command, which I labeled the “Connect” command, was crucial and needed to be sent before any other commands. After establishing the initial connection with this command, subsequent commands had to be sent at intervals of one second or less to maintain the Bluetooth connection with the desk bike.
Start Command: f9 d0 00 c9 Response: f9 e0 00 d9
The subsequent command, named “Hold,” appeared to serve as a keep-alive signal, maintaining the active Bluetooth connection. The desk bike responded with two identical data packets each time the “Hold” command was sent.
Hold Command: f9 d1 05 02 00 00 00 00 d1 Response #1: f9 e1 10 07 00 00 00 00 00 00 02 00 03 37 00 00 2a Response #2: f9 e2 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 eb
Next, I identified two commands sent only once during the initial connection phase, which I termed “Info1” and “Info2.” These commands likely retrieved static information about the desk bike, such as model details or calibration parameters.
Info1 Command: f9 d3 0d 01 00 00 2c 00 00 3c 00 a0 00 00 00 00 e2 00 00 00 Response #1: f9 e3 01 00 dd Response #2: f9 e3 0c 00 00 00 00 00 00 00 00 00 00 00 00 e8 Info2 Command: f9 d4 0f 02 00 00 00 00 00 00 00 00 00 00 00 00 1f 0f 0c 00 Response: f9 e4 02 00 00 df
Analyzing the command and response packets, a clear pattern emerged. Each packet began with the byte F9
. The subsequent byte indicated the command or response type, with the high nibble being D
for commands and E
for responses. The third byte specified the packet length, excluding the header bytes. The byte following the length appeared to be a checksum for data integrity.
The most crucial commands for real-time workout data were the “Start Workout” and “Continue Workout” commands. The “Start Workout” command was sent when a workout commenced. Subsequently, the app continuously sent the “Continue Workout” command to sample and receive workout data from the desk bike at regular intervals.
Start Cmd: f9 d5 0d 01 00 00 00 00 00 00 00 00 00 00 00 00 dc 00 00 00 Response #1: f9 e5 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 ef Response #2: f9 e6 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ef Response #3: f9 e7 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f0 Continue Cmd: f9 d5 0d 00 00 00 00 00 00 00 00 00 00 00 00 00 db 00 00 00 Response #1: f9 e5 10 00 09 00 03 00 07 00 00 00 99 00 00 53 00 00 01 ee Response #2: f9 e6 10 00 00 00 00 00 06 00 00 00 00 00 00 00 00 00 2f 24 Response #3: f9 e7 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 f0
With the command structure understood, I began focusing on interpreting the workout data packets. I concatenated the data payloads from multiple workout response packets, removing the packet headers. Then, I performed a 10-minute test workout, capturing the raw data stream.
00 00 00 00 00 00 00 00 00 00 00 00 00 00 01 EF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 EF 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 01 00 00 00 00 00 00 00 00 00 00 00 00 00 01 F0 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 EF 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 02 00 00 00 00 00 00 00 00 00 00 00 00 00 01 F1 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 EF 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 03 00 00 00 01 00 00 00 BC 00 00 66 00 00 01 15 00 00 00 00 07 00 00 00 00 00 00 00 00 00 19 0F 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 03 00 01 00 01 00 00 00 BB 00 00 65 00 00 01 14 00 00 00 00 07 00 00 00 00 00 00 00 00 00 19 0F 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 04 00 01 00 02 00 00 00 BE 00 00 67 00 00 01 1B 00 00 00 00 07 00 00 00 00 00 00 00 00 00 25 1B 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 05 00 01 00 03 00 00 00 C1 00 00 69 00 00 01 22 00 00 00 00 07 00 00 00 00 00 00 00 00 00 2D 23 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 06 00 02 00 04 00 00 00 C3 00 00 6A 00 00 01 28 00 00 00 00 07 00 00 00 00 00 00 00 00 00 32 28 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 07 00 02 00 06 00 00 00 C1 00 00 69 00 00 01 28 00 00 00 00 07 00 00 00 00 00 00 00 00 00 36 2C 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 07 00 03 00 06 00 00 00 C1 00 00 69 00 00 01 29 00 00 00 00 07 00 00 00 00 00 00 00 00 00 36 2C 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 08 00 03 00 07 00 00 00 BE 00 00 67 00 00 01 26 00 00 00 00 07 00 00 00 00 00 00 00 00 00 39 2F 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 09 00 03 00 08 00 00 00 BA 00 00 65 00 00 01 22 00 00 00 00 07 00 00 00 00 00 00 00 00 00 3B 31 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 0A 00 04 00 09 00 00 00 B0 00 00 5F 00 00 01 15 00 00 00 00 07 00 00 00 00 00 00 00 00 00 3C 32 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 0B 00 04 00 0A 00 00 00 B5 00 00 63 00 00 01 20 00 00 00 00 07 00 00 00 00 00 00 00 00 00 3D 33 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 0B 00 05 00 0A 00 00 00 B8 00 00 64 00 00 01 25 00 00 00 00 07 00 00 00 00 00 00 00 00 00 3D 33 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 0C 00 05 00 0C 00 00 00 BB 00 00 66 00 00 01 2D 00 00 00 00 07 00 00 00 00 00 00 00 00 00 3E 34 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 0D 00 06 00 0D 00 00 00 BF 00 00 68 00 00 01 36 00 00 00 00 07 00 00 00 00 00 00 00 00 00 3F 35 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 0E 00 06 00 0E 00 00 00 BF 00 00 68 00 00 01 38 00 00 00 00 07 00 00 00 00 00 00 00 00 00 40 36 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 0F 00 06 00 0F 00 00 00 BF 00 00 68 00 00 01 3A 00 00 00 00 07 00 00 00 00 00 00 00 00 00 41 37 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 0F 00 07 00 0F 00 00 00 BE 00 00 67 00 00 01 39 00 00 00 00 07 00 00 00 00 00 00 00 00 00 41 37 00 00 00 00 00 00 00 00 00 00 00 00 00 F0 ...
Key Data Observations
- Data Consistency: Significant portions of the data stream consisted of constant zero values and a few fixed values, which could be disregarded for workout metrics.
- Data Types: The data stream contained both byte-sized and word-sized values, requiring appropriate parsing.
- Dynamic Values: Some values incremented continuously, representing cumulative metrics, while others fluctuated based on pedaling speed, registering zero when stationary.
Based on these observations, I developed code to parse the data into nine distinct fields, differentiating between byte and word values. I then reran my console application, logging the parsed data to a CSV file during a workout. Importing this CSV data into Excel allowed for visual analysis and interpretation of each field. While most values became readily apparent, deciphering the units took some time. I eventually realized the desk bike reported measurements in imperial units rather than metric. The nine data fields, in order of appearance in the data stream, were identified as:
- Workout Second: The current second within the minute of the workout, cycling from 0 to 59 repeatedly. With sampling rates faster than one second, this value might remain constant across multiple samples.
- Distance (hundredths of a mile): Cumulative distance traveled, measured in hundredths of a mile.
- Workout Time (seconds): Total workout duration in seconds, incrementing while pedaling and pausing when stationary.
- Speed (tenths of a mile per hour): Real-time speed in tenths of a mile per hour.
- RPM (Rotations Per Minute): Cadence, measured in rotations per minute.
- Unknown (byte): Appears related to workout intensity, increasing with speed. When stationary, it increments every second and occasionally resets.
- Speed Level (0-9): A discrete speed value ranging from 0 to 9, potentially representing resistance level or speed zones.
- Unknown Average Value 1 (byte): Potentially an average speed metric over the entire workout, but its precise definition is unclear.
- Unknown Average Value 2 (byte): Similar to the previous value, likely another average metric related to workout speed, but its exact meaning is undetermined.
With these insights, I concluded that only three commands were essential for my desktop application: “Connect,” “Start Workout,” and “Continue Workout.” Furthermore, understanding the intricate details of each command wasn’t necessary; simply sending the identified byte sequences in the correct order was sufficient to extract the required workout data from the desk bike.
Step 4 – Building the Desktop Desk Bike Application
The final phase involved structuring my reverse-engineering findings into a consistent API and developing a user-friendly WPF (Windows Presentation Foundation) application for the desktop interface. WPF’s MVVM (Model-View-ViewModel) pattern was ideally suited for this application architecture. For local data storage, I leveraged Entity Framework Core to quickly implement a SQLite database for efficient workout data management.
alt: Screenshot of the custom desk bike desktop application interface, displaying real-time and average workout metrics.
Key Features of the Final Desk Bike App:
- Real-time Workout Display: Shows workout duration, current speed, distance, and RPM in real-time during desk bike sessions.
- Workout Averages: Post-workout, the display transitions to show average workout metrics for a performance summary.
- Automatic Workout Start/Stop: Automatically detects desk bike activity via Bluetooth activation upon pedaling, initiating the workout tracking and displaying the app window.
- Pause and Auto-End: Pauses workout tracking when pedaling stops, with workout time blinking to indicate paused state. Workouts automatically conclude after one minute of inactivity.
- Manual Controls: Start and Stop buttons provide manual workout control.
- Daily Distance Tracking: Displays daily accumulated distance for goal setting and progress monitoring with the desk bike.
- System Tray Integration: Minimizing the app sends it to the system tray for background operation.
- System Tray Menu: Context menu accessible via the system tray icon for quick app access and control.
- Always-on-Top Option: Window can be set to remain always on top for convenient viewing during work (right-click context menu).
- Automatic Startup: Option to configure the application to launch automatically upon system login.
- Workout Data Persistence: Saves comprehensive workout data, including all sampled metrics, to a local SQLite database for historical analysis.
Conclusion: Desk Bike Workout Tracking Success
The custom desktop application has proven to be a successful project, fulfilling all my initial goals. It provides a seamless and insightful way to track my desk bike workouts directly on my computer. Now, the primary challenge is personal endurance – seeing just how far I can pedal while working at my desk.