Script.Require("jQuery").AtHead()

Kludgy horrible code…


I’ve been playing about with Bluetooth LE for TenSpeed, specifically to connect to Cycling Speed and Cadence (CSAC) sensors, such as the Topeak PanoBike CSS sensor. I’ve leveraged the Heart Rate sample originally written for Windows 8.1, and also learned much from this post on the BlackBerry developer blog, which helped me write the code which parsed the value change notifications from the CSAC characteristic.

So now I have a service which receives notifications from the device, packages them into a CsacMeasurement class, and emits ValueChanged events. I also wrote a quick-and-dirty sample app, which consumes the ValueChanged events, performs the relevant processing and displays the crank and wheel speeds in revolutions per minute. All fine and dandy.

Except I was having a problem. The code worked if the CSAC device was freshly paired, but if you exited the sample app and restarted it, the following exception would be thrown:

Exception thrown: 'System.Exception' in mscorlib.ni.dll
There is no user session key for the specified logon session. (Exception from HRESULT: 0x80070572)

To get the program to work reliably, the device has to be manually unpaired and re-paired in Settings.

I posted on the MSDN forum, and was told this by Jeffrey Chen, a Microsoft employee:

I’ve encountered the same exception before. After some investigation, I found that it is caused by the BLE device remove all of the bonding information positively, and the exception will throw when I sending a writing request to the device again.

For the BLE devices such as “PanoBike Speed & Cadence Sensor”, the vendor will provide the corresponding app and the “communication protocol” is non-public. You cannot simply create an app for it.

I got in touch with the UK distributors of the PanoBike sensor and was told this:

Sorry, the Panobike app didn’t provide open API for other developer.

We use standard Bluetooth 4.0 protocol to connect speed & cadence sensor and Heart Rate sensor.

The customer could follow the standard protocol to build its own app if he would like to connect Bluetooth devices.

Yay for 90s style device incompatibilities! I then purchased a generic sensor from China using AliExpress to rule out a custom API in the PanoBike sensor. I still suffered the exception issue, but now I have a device I can use to code against without having to put my laptop on the wheely bin where my bike is stored.

I have managed to find a workaround, though. The November 2015 update of the Windows 10 SDK has improved the Bluetooth APIs, and it’s now possible to instigate device pairing and unpairing from within the app. This is straightforward, but has an ugly user experience, as Windows requires consent from the user to both unpair and re-pair the device.

That’s the easy part. You now have to find the device and reinitialize the GATT service and characteristic, which is complicated by the fact that the device will take a few seconds to appear on the list once re-paired. My solution is to simply get a DeviceInformationCollection of CSAC devices using the GATT UUID, and if the chosen device isn’t in the list, wait 2 seconds and retry.

But how do you find the device on the list? Each device has a DeviceInformation class, which has a unique Id property. This is a great long string as in the example below:

\\?\BTHLEDevice#{00001816-0000-1000-8000-00805f9b34fb}_00078071e6b7#9&29055489&17&0013#{6e3bb679-4372-40c8-9eaa-4509df260cd8} 

You can’t use the whole string, as when the device is unpaired and re-paired, the number highlighted in yellow increments. The GUIDs in the curly brackets (highlighted in red) are of no use, as they’re the same for every CSAC device. The interesting bit is highlighted in green. This is the device’s MAC address, without the colons. The string is in 4 sections, separated by the # character, and the MAC address is in the second section.

The GitHub repository for the sample is here.


Comments