don / cordova-plugin-ble-central

Bluetooth Low Energy (BLE) Central plugin for Apache Cordova (aka PhoneGap)
Apache License 2.0
942 stars 603 forks source link

Scan is returning real devices with bad information #931

Closed Yodio-Jeff closed 1 year ago

Yodio-Jeff commented 1 year ago

We're connecting to a custom BLE device from an iPhone. The manufacturer changed their advertising data and the new devices no longer work. The engineers insist they only changed the manufacturer-specific field (0xFF), removed the iBeacon setting, and went to a single fixed advertisement. When scanning, we should see a name like "SN:0000096267". Instead, scanning produces "NJGPW", and while our app connects, the custom service fails to respond.

nrfConnect usually shows the correct information for these devices, but we've seen it produce the same nonsense.

In the screenshot from our app you can see what looks like complete device information, except the name and service UUID are bad. failed_scan

The scan() call is basic (using the Ionic @ionic-native/ble wrapper):

    let options = {scanMode: "lowLatency", matchMode: "aggressive"};
    let target = [];
    let subscription = this.bleService.startScan(target, options).subscribe(candidate => {

Has anyone seen situations where nrfConnect and this plugin don't return the same info?

peitschie commented 1 year ago

Hi @Yodio-Jeff ... it's unlikely to be anything in the plugin, as we report these values exactly as we receive them from iOS itself... there's no additional parsing or transformations going on within this plugin at all.

Having said that, are you able to grab a screenshot of how nrf Connect renders this advertising packet? Ideally one showing the bad packet and one showing the good packet would be great, as we might be able to figure things out from there.

Out of interest, is this device connectable?

Yodio-Jeff commented 1 year ago

This has been a baffling issue. My app shows the scanned devices as they come in, and it's only these new devices that are causing the headaches.

    let subscription = this.bleService.startScan(options).subscribe(candidate => {
        // many devices report back an ID but no name field
        candidate.hasProperName = !(candidate.name == null) && candidate.name.length > 0;
        if (!candidate.hasProperName) {
            candidate.name = unknownDeviceName;
        }
        this.messageHandler(`${this.devices.length} :: "${candidate.name}", ${candidate.id}, ${candidate.rssi}`);

Immediately after that it puts out the advertising data, either everything iOS provides, or a simple decode of the Android raw data.

Here's the nrfConnect screenshot I have showing the bad output:

nrfConnect_view_of_NJGPM-resize

The two entries NJGPM and NJGPW should be named serial number "SN:00000xxxxx", and the service UUID is FFF0.

From our iOS app, you can see the advertising data for an older device that works, and the oddball device:

iOS_view_of_NJGPM-crop

The Android app shows the partially decoded advertising buffer for two of the weird locks:

Android-failed_scan_of_NJGPM-crop

So both apps agree the names are weird, but nrfConnect usually shows the expected data. The manufacturer insists they didn't do anything with the name fields. With the scan filter "FFF0" active the oddballs were excluded. Scanning with no filter reveals them, and we can connect, except writing to the normal service causes a long delay and the error "peripheral with UUID FFF0 can't be found on device ....". They certainly act - to the app - as if those odd values are real.

Any ideas? Could changes to the iBeacon setup or the header bits drastically change the way the broadcast payload is interpreted? Why do scanning tools - including the OS's own Bluetooth devices scanner - show the expected values? This makes no sense and we need it fixed.

peitschie commented 1 year ago

I recently discovered that the name shown by iOS via the plugin appears to update after connecting to the device, based on the GAP service (service 0x1800, char ID 0x2A00). See Bluetooth name caching - https://developer.apple.com/forums/thread/19381?answerId=61155022#61155022 for a bit more detail about this.

Is this possibly what's happening for you there? Have you connected to any of the good/bad devices before?

Yodio-Jeff commented 1 year ago

As an experiment, I chopped in the @capacitor-community/bluetooth-le plugin, just the scan functions. While it's ability to find devices was seriously underwhelming, on about 1 in 20 tries it does show the correct serial number name. With two of the devices in range, it never shows both correctly in the same scan.

Yodio-Jeff commented 1 year ago

Name caching may be happening on iOS. The weird names are popping up on Android, too. In the screenshot above you can see where the name NJGPM returned by the OS was encoded in the raw advertising buffer, field type 09.

The manufacturer insists they're now sending one fixed advertising packet. However, we're seeing the unusable data show up regularly in nrfConnect scans. I'm beginning to suspect a bug in their firmware that we'll have to work around.

Yodio-Jeff commented 1 year ago

One of the biggest issues we've had is devices not showing up in scans, even within arm's reach. Is there some way to increase the sensitivity, particularly on iOS? Advertisements are broadcast every half second, yet it can take a full 3 second scan to get one detection. It's frustratingly erratic, worse than the OS device detector.

peitschie commented 1 year ago

@Yodio-Jeff .5s is an extremely slow advertisment interval! Apple recommends a 20ms interval for the first 20s or so...

I'd highly suggest reading through section 41.5 (page187) in Apple's Accesory Design Guidelines: https://developer.apple.com/accessories/Accessory-Design-Guidelines.pdf

Yodio-Jeff commented 1 year ago

@peitschie nrfConnect shows the interval jumping around between 1/4 and 1/2 a second. The specs we have are sparse on the finer details.

The Apple guide looks more readable than the official Bluetooth docs, thanks for the pointer. Basic question: Does the central broadcast a "who's out there?" message that prompts the peripherals to advertise their presence? If so, all the devices in range should be found quickly.

peitschie commented 1 year ago

@Yodio-Jeff it is a broadcast but....

Bluetooth advertisments are scattered across multiple radio channels. In order for a phone to "hear" the broadcast, it has to be listening on the right channel at the right time to hear the packet. Most phones can't listen to multiple channels at a time, and will switch between channels in rapid succession. Additionally, in order for the broadcast to be understood, it has to occur at a time when nothing else is talking at the same time (as colliding broadcasts on the same frequency effectively destroy your carefully crafted packet).

The BLE consortium has a reasonable intro on this at https://www.bluetooth.com/blog/advertising-works-part-1/ (see Of Transmitters and Receivers) but I find https://www.argenox.com/library/bluetooth-low-energy/ble-advertising-primer/ a better visual representation.

3 seconds when using a 250 => 500ms interval sounds about right to me 🙂 . If you want much faster than that, then the advertising interval definitely needs to be far closer to the 20ms range!

peitschie commented 1 year ago

Closing this issue, as it seems like there's no fixes or problems in the plugin itself. Feel free to continue the discussion here however, or raise a new issue if problems are encountered.