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

"Undefined" BLEPeripheral Name #229

Closed lauleehong closed 8 years ago

lauleehong commented 8 years ago

I'm getting "undefined" as my BLE-peripheral name on my Android. Any idea how I will be able to detect it like in iOS? I'm able to get the device name of the same hardware on your Cordova SimpleSerial iOS.

I added: console.log(JSON.stringify(device)); //check peripheral info packet inside onDiscoveryDevice. "{"id":"AA:BB:CC:DD:EE:FF","advertising":{},"rssi":-55}" // real id masked; missing "name"

don commented 8 years ago

Some devices don't advertise a name.

Once you're connected you could try reading the Device Name Characteristic 0x2A00 from the Generic Access Service 0x1800

I think Android looks for the Complete Local Name 0x09 from the advertised GAP information. You could try parsing the advertising data from the peripheral and seeing if 0x09 exists or perhaps 0x08 Shortened Local Name.

Does NRF Master Control Panel show the name correctly?

lauleehong commented 8 years ago

How should I go about reading the device name characteristics on cordova/js? Spot on that my Complete Local Name is "N/A" and MCP on Android shows "N/A" as well (but shows Full Name on iOS).

don commented 8 years ago

You'll need to connect to the peripheral and do something like

var success = function(buffer) {
    var name = ''; // convert bytes in ArrayBuffer to a string
    console.log('device name is', name); 
};
ble.read(peripheral.id, '1800', '2a00', success, app.onError);
lauleehong commented 8 years ago

I'm working on a BLE scanner list, so to sequentially connect each peripheral to parse out the device name seems impractical.

don commented 8 years ago

If you don't want to connect, I'd use N/A or the MAC address for the name.

lauleehong commented 8 years ago

If I were to populate a list with BLE peripherals, an average joe wouldn't know which device he is looking for to connect to if the name was N/A or the MAC address.

don commented 8 years ago

If the device doesn't advertise a name, it's not going to be in the scan results on Android. iOS and Android handle this differently.

nRF Master Control Panel is a generic scanning app. It shows N/A for devices that don't advertise names. Most devices that are connectable advertise a name.

My plugin provides access to the Android and iOS API. I can't give you data that's not there. You need to figure out what makes sense for your app. Maybe N/A is good. Maybe you need to connect and read the device name if it's not advertised.

lauleehong commented 8 years ago

My hardware does advertise a name, and thus I'm able to pick it up on my iPhone. I'm using your plugin to make a "remote controller" like app which list the names of the devices at start. The user then selects which device to connect to for UART-BLE data transmission.

don commented 8 years ago

Based on what you've told me, the device name you see on iOS is coming from the Generic Access Service 0x1800, Device Name Characteristic 0x2a00. On iOS, CoreBluetooth gets the name internally, and makes it available as peripherals are discovered. It is probably by doing a Read By UUID Request (but this API isn't exposed.) CoreBluetooth does other stuff during discovery like hiding the MAC address and generating a UUID for the peripheral.

On Android the device name needs to be in the advertising or scan response packet.

Can you update the firmware on the device? I think you'll get the best results if you advertise the local name and set the device name.

lauleehong commented 8 years ago

Don, thanks for clarifying. From my understanding, a device name would not be short enough to fit into a packet if it was already advertising NID and BID through Eddystone. I have encoded 2 bytes in my eddystone NID as a "device name" identifier. I plan to fish it out from the Generic Access Profile 0x16.Am I able to access that through your plugin in anyway?

don commented 8 years ago

Remember you can send 31 additional bytes in the scan response packet. It works like the advertising info. I'd add the device name there.

You can get access to the Eddystone data from the plugin, however it's a bit manual right now. I plan to add helper functions in a future version (see #192). Until then, here's an example of creating a hash map from the advertising data and parsing out some service data.

lauleehong commented 8 years ago

I did the following:
var adData = new Uint8Array(device.advertising); html = adData[byteOffset]; // find byte offset from JSON.stringify(adData)

Do you see any potential problem in doing so?

don commented 8 years ago

I'd do something like this

var SERVICE_DATA_KEY = '0x16';
var advertisingData = parseAdvertisingData(device.advertising);
serviceData = advertisingData[SERVICE_DATA_KEY];
if (serviceData) {
    // first 2 bytes are the 16 bit UUID
    var uuidBytes = new Uint16Array(serviceData.slice(0,2));
    var uuid = uuidBytes[0].toString(16); // hex string
    console.log("Found service data for " + uuid); // should be 0xFEAA

    // remaining bytes are the Eddystone beacon data
    var eddystoneData = new Uint8Array(serviceData.slice(2));
    // do something with the eddystoneData
}