The goal here is to take input data from sensors attached to a bicycle, and build a no-frills GUI to map this data to a virtual controller. The user should ultimately be able to map this data to the functions of a common game controller with a level of precision that allows for a good gaming experience.
I'd like to use as many sensors that already exist and are on the market, so to get started, I will focus on a Wahoo sensors for both wheel speed and pedal cadence. Steering, braking, and everything else will be a bit more custom.
How does bluetooth work anyway?
I've chosen to stick with Bluetooth Low Energy instead of the ANT+ protocol, for both quicker transfer speeds and a [slightly] lower barrier to entry. Let's start with a Wahoo wheel speed sensor.

I will be using Bettercap to learn a little more about this sensor and it's BLE packet information.


To make better sense of what is going on here, I will refer to the official BluetoothSIG Assigned Numbers documentation. BLE uses 16-bit UUIDs to label services and characteristics, with their docs we can verify we have:
1816 - Cycling Speed and Cadence [service]
and within that service we have:
2a5b - CSC Measurement [characteristic]
So far I have only made a single connection to the device to obtain some base information, to expand on this I will need to "subscribe" to the characteristics I want to stream data from. This characteristic has a property called NOTIFY which means the peripheral device will continuously send updates of this characteristic to it's subscribers. The READ properties make characteristics useful for initial setup but have no logical reason to stream as they should not change.
2a5c - CSC Feature [characterstic]
Our feature field reads '0100'. Following the chart, we know there are going to be up to 16 boolean values to make up our 2 octets. At first glance this might suggest we have only Crank Revolution Data Supported, but this is a Wheel Speed Sensor. Using the Nordic Semiconductor app for android, it is properly interpreted as wheel data, making our output from bettercap all the more confusing. We'll get back to this.
I will be using TinyGo's bluetooth package, certainly there are other methods, but for this project I will be introducing myself to Wails later and would prefer to keep the software itself Go-heavy. The code is omitted for now as we will be very much in-the-weeds on that later.
The introduction portion of the Bluetooth docs contain two very important lines that are essential to interpreting this data:


TinyGo delivers the data to us in uint8 values, the gif above shows us an array of said values representing the entire CSC measurement characteristic. Referring to the CSC Measurement figure above we can ascertain the following:


The "Last wheel event time" unit of measurement is 1/1024th second as per the Bluetooth GATT docs. Notice the rollover on this metric, we also learned in the docs that this is represented by a uint16 number.
Summary
This has been a great intro to bluetooth data, surely building on this foundation will be imperative to moving forward on the larger project at hand. I now have some useful data here, I can actually calculate a speed with some basic math and bike measurements. In combination with a crank speed sensor I will be able to estimate power output from the rider and gearing. For my application that may be irrelevant, but I'll take all the data I can get! While the data appears to come in just over once per second, the "last wheel event time" field is going to help smooth the data in practice - a very interesting way of dealing with uncertain transmission.