lancaster-university / microbit-dal

http://lancaster-university.github.io/microbit-docs
Other
254 stars 130 forks source link

Determine over Bluetooth is micro:bit is in pairing mode #332

Open smartyw opened 6 years ago

smartyw commented 6 years ago

Background

The Bit:Bot product has a couple of line detection sensors which share pins with the two micro:bit buttons A and B. Consequently if you power it up while it is on a surface, it typically boots into pairing mode. You avoid this by lifting the Bit:Bot off the ground so that there's enough light falling onto the line sensors and then power on.

Inevitably however, users will boot into pairing mode accidentally and not realise their mistake. This leads to a poor user experience.

Detecting Pairing Mode over Bluetooth

There are two scenarios where it would be useful for an application to be able to determine whether or not a micro:bit was in pairing mode.

1) Before connecting

It would be useful to be able to determine from Bluetooth advertising packet content that a micro:bit is in pairing mode. This seems problematic. Adding a special character to the end of the device name in advertising packets would work, but seems to break the Samsung application (and perhaps others). Other advertising packet fields could perhaps be used (e.g. the inclusion or exclusion of the Appearance field) but not all client APIs give access to all advertising packet fields.

If we decided to implement a solution to this sub-issue, I think we'd need to use the device name advertising packet field and affected applications would need to be changed.

2) After connecting

This is less problematic. One approach is to add a "*" to the end of the device name characteristic in the Generic Access Service. Then, after connecting, a client application can read that characteristic value, which is always guaranteed to be present, and determine whether or (perhaps) not, the micro:bit is in pairing mode.

I've submitted a PR which implements this:

https://github.com/lancaster-university/microbit-dal/pull/331

And here it is in use in Bitty Controller:

screenshot_20171218-075038

smartyw commented 6 years ago

This will not work as well as I'd hoped. iOS doesn't allow access of any sort to the Generic Access Service like Android does so this "solution" would only help non-iOS devices.

The preference then is to do something similar with the advertised name but this will definitely impact some apps.

Thoughts?

martinwork commented 6 years ago

I was thinking in terms of what characteristics an app can see, but it seems iOS and macOS do reveal the GAP device name as explained here: https://developer.apple.com/documentation/corebluetooth/cbperipheral/1519029-name?language=objc

smartyw commented 6 years ago

Thanks @martinwork . Native developers look to be OK but some SDKs like Apache Cordova don't seem to make the GAP device name available from what I can tell (maybe it's a bug!) and reading the characteristic directly is not allowed on iOS. More to the point though, given the varying degrees to which advertising and other data is or isn't accessible on different platforms with different SDKs and frameworks, I'm wondering if something we're all confident would work anywhere and everywhere could be implemented.

finneyj commented 6 years ago

Thanks for discussing @smartyw @martinwork

Firstly, why on earth would you overload the button A/B inputs for line following light sensors?? There are plenty more digital I/O pins available if your breakout the edge connector like this. :/ I'll have a friendly chat with them to see if we can avoid this in the future.

I think my thoughts here are to do something with the DeviceInformationService. Currently this is an optional BLE service. We could make it compulsory when in pairing mode, and add a little metadata to one of the characteristics (e.g. "model"), to indicate whether or not the device is in pairing mode.

The algo for an app to remotely determine if the micro:bit is in pairing mode would be something like:

bool pairingMode = (deviceInformationServicePresent && deviceInformationService.model.endsWith('*'))

Alternatively we could use one of the currently unused characteristics (marked as NULL here: https://github.com/lancaster-university/microbit-dal/blob/master/source/bluetooth/MicroBitBLEManager.cpp#L75), or add a new one (but DeviceInformation is a SIG defined interface, so this might make us incompatible).

Thoughts?

smartyw commented 6 years ago

Like

Yes, this is pragmatic and low risk regarding existing apps (I think!). I think I'd prefer to stick an asterisk on the end of the device name than abuse the manufacturer name, hardware or software version characteristics. It's not exactly a purist approach so I'd rather limit the "damage" to the device name. Against this argument is the fact that those other characteristics are clearly unused and so there's presumably no risk at all to existing apps.

I'm open to suggestions!

finneyj commented 6 years ago

Yes, it does still feel like a bit of a hack. I guess we could align a bit more with previous approaches and put some freetext metadata in the model, as we do with the advertised name after a device is paired.

e.g. model = "BBC micro:bit [pairing]"

Actually, can we not simply use the fact the advertised name is different when in pairing mode? Or is this not accessible enough for BLE app framework APIs?

finneyj commented 6 years ago

doh - already discussed above... sorry guys.

smartyw commented 6 years ago

The name differs when a paired device advertises, not just when in pairing mode. The 5 char code is included when not paired regardless of whether we're in pairing mode or not.

cpseager commented 6 years ago

Can't you just stick a byte in the Manufacturer Specific Data (0xFF) in the advert? That data should be available on any framework and wouldn't corrupt the current Local Name format.

finneyj commented 6 years ago

If such data is available to apps, that would be a cleaner solution.

ghost commented 6 years ago

The Manufacturer Data field must be prefixed by a SIG issued company ID which the foundation would need to register.

ghost commented 6 years ago

If want to avoid using an existing field in a hacky way we could have a new custom service with a single characteristic whose presence (always with a value of 1) indicates we're in pairing mode. It would never be instantiated under other circumstances. No hack and will work everywhere I think.

finneyj commented 6 years ago

Yes, it is tempting isn't it - some sort of "Status" service...

ghost commented 6 years ago

Yes, could have future uses... who knows. And we could call it "Utility Mode" rather than Pairing Mode.

finneyj commented 6 years ago

Is it legal to have additional characteristics in a SIG defined service? i.e. I'm pondering a new service vs. a new characteristic in DeviceInformation...

ghost commented 6 years ago

Spec says "The Device Information Service may expose one or more of the characteristics shown in Table 3.1." so that seems like a big "NO".

finneyj commented 6 years ago

Bah. Humbug.

smartyw commented 6 years ago

@cpseager has told me that when he enquired about a company ID from the SIG, there was no charge. I'm double checking this but if that's the case, I was wrong about that and using the Manufacturer Specific Data field for this purpose would be perfect. No impact on existing apps that I can imagine and you can check for pairing mode before connecting to the device, which is definitely the right way to do it. The foundation would still need to obtain the company ID but it's just a case of filling in a form.

We're using 24 octets of the advertising payload so we have space in the packet for the extra field.

ghost commented 6 years ago

Update: Yep. Company ID is free. The foundation would need to register for the ID here: https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers

jaustin commented 6 years ago

Sorry to pop back up on an old thread. We're happy to register for an ID if free (and I think probably should anyway) but it looks like we need Adopter (also free) SIG membership as a pre-requisite too, so not sure how long it all takes.

Having let the dust settle on this, is that still the right approach? Do we know for sure that on iOS and Android we can actually access the Manufacturer Specific Data in the ad packet? Does it interfere with other cool things like web-bluetooth? @martinwork might be able to tell us about the ad packet. My reading of https://stackoverflow.com/questions/22833198/get-advertisement-data-for-ble-in-ios is that we'd be good as we're not an iBeacon.

FWIW I don't think any of @microbit-sam's partial flashing work will help here by default as we might well want the partial flashing service visible in both pairing (/service/utility/whatever it gets named to) mode and normal mode. We could use the new service as a Trojan horse for a new characteristic that tells you which mode you're in (excusably relevant because the app may want to trigger a reboot into 'pairing/service/utility/unicorn' mode in order to ensure that the update happens in a sane/known environment rather than alongside user code).

ghost commented 6 years ago

@jaustin Good questions. I think you're right about iOS but unfortunately Web Bluetooth doesn't currently let filter on manufacturer data. It's in the spec but currently designated "unstable" which generally means you won't find it implemented in any browser and it's subject to change anyway.

Maybe my hacky "" on the end of the advertised name is the way to go? Hacky but pragmatic? Except that... Web Bluetooth lets you filter on "name prefix" so the "" would need to prefix "BBC" in advertising packets and.... I'd want to test this... just in case there's some regular expression stuff going on in browsers that "*" might confuse!

ghost commented 6 years ago

@jaustin regarding membership, I'm reliably informed that if all the uploaded documents are correct and the application is complete, it takes 2 business day to process. You have to upload business formation documents and sign/upload the membership agreements. I can provide support of course.

martinwork commented 6 years ago

I haven't needed to use it, but it certainly looks like the manufacturer data is available when scanning using the key CBAdvertisementDataManufacturerDataKey. The post below says this might not be available to an app running in the background.

https://stackoverflow.com/questions/25390484/obtaining-bluetooth-le-scan-response-data-with-ios/25392156?noredirect=1#comment46402518_25392156

cpseager commented 6 years ago

Yes manufacturer data works fine on android and ios. In ios you can also even get the entire advert packet if you want (when not working as a ibeacon), we have used that for debugging advert data in the past.

Loads of devices allow the end user to rename the advertised name, LEGO Wedo2, LEGO Boost, SBrick etc come to mind. In those cases you can't recognise the device via the advertised name anyway, and have to use something else in the advert like a uuid or manufacturer data instead. So I wouldn't consider changing the name as hacky, it just may break existing apps that may be looking for a specific name already...

ghost commented 6 years ago

"I wouldn't consider changing the name as hacky" :-)

The asterisk isn't exactly elegant but perhaps needs must. I do think we need to think beyond iOS and Android though and I think Web Bluetooth warrants taking seriously. Probably the simpler, less sophisticated and change we make, the better.