Simultaneous connection with ANCS and other peripherals
sander opened this issue · comments
I’m trying to use ble-ancs to connect to my iPhone for ANCS, to my heart rate sensor, and to an UART device (Bluefruit Micro) from one Node script. Both the sensor and the UART device are BLE peripherals. Before I spend hours trying to make it work, should this be possible in ble-ancs / able already?
[edit: removed irrelevant example, will update if I have a clearer example]
My code right now:
var BleAncs = require('../index');
var Able = require('../lib/able');
var ancs = new BleAncs();
ancs.on('notification', function(notification) {
notification.readTitle(function(title) {
notification.readMessage(function(message) {
console.log('Notification: ' + notification); }); }); });
var SERVICE = '6e400001b5a3f393e0a9e50e24dcca9e';
var TX = '6e400002b5a3f393e0a9e50e24dcca9e';
var RX = '6e400003b5a3f393e0a9e50e24dcca9e';
var state = {peripheral: null, tx: null};
function onRead(v) {
console.log('Read:', v.toString());};
var other = new Able()
.on('stateChange', function(state) {
if (state == 'poweredOn') { other.startScanning(); }
else { other.stopScanning(); }})
.on('discover', function(p) {
console.log('peripheral', p.uuid);
if (p.advertisement.serviceUuids.indexOf(SERVICE) != -1) {
other.stopScanning();
p.connect(function(err) {
p.findService(SERVICE, function(err, services) {
services[0].discoverCharacteristics([], function(err, cs) {
cs.forEach(function(c) {
if (c.uuid == TX) {
state.tx = c; }
else if (c.uuid == RX) {
c.notify(true);
c.on('read', onRead); }}); }); }); });
state.peripheral = p; }});
When I comment out either the ANCS or UART part, the other part works as expected. When I try the full script, only ANCS works:
# node examples/test-multiple.js
on -> stateChange: poweredOn
on -> advertisingStart: success
peripheral 68644b2f0965
peripheral 1499e21d7e71
peripheral b0349539cf8b
Bleno: Handle: 64 UUID: 6f777d0c23b0 address 6f:77:7d:0c:23:b0
on -> accept:
able encryptChange!!!
peripheral d3c0f5a6366d
Connect - uuid: d3c0f5a6366d addres: d3:c0:f5:a6:36:6d
Notification: {"event":"added","flags":["silent","important"],"category":"other","categoryCount":2,"uid":4,"title":"TestFlight","subtitle":"","message":"iA Writer 3.0.3 (565) is now available for testing.","messageSize":51,"date":""}
The device that should connect and doesn’t succeed is d3c0f5a6366d
. Any ideas on how to get this to work? Should it be easier (or possible at all) to use ble-ancs and Sandeep’s noble simultaneously?
Hmm... This should be possible. Able is really just Noble and Bleno combined.
I would check out the index.js for ble-ancs. I bet it is doing something in there that is messing things up. You should be able to move most of this code into that file.
I’m trying a custom index.js
at https://github.com/sander/ble-ancs/commit/790bdfa37f5b9e54724981ac7d5a0659a880fe7f. Noticing several things:
- With
this._able.on('stateChange', this.onStateChange.bind(this));
inBleAncs
, my UART device won’t connect, so I commented it out. - My UART device disconnects after several seconds (varying per session). This didn’t happen with
noble
. It even happens if I comment out all ANCS code. - I’ve been calling
ancs.onStateChange('poweredOn')
after service/characteristic discovery for my UART device, to compensate for change (1). On this call,BleAncs
logson -> advertisingStart: error Error: Unknown (12)
. After the UART device disconnects (automatically, unintended),BleAncs
still connects to my phone and receives notifications. See the log below.
How would you recommend proceeding? It seems like advertising conflicts with scanning, and like established connections die after a while.
# node test-issue3.js
uart init
uart discovered
uart discovered
Connect - uuid: d3c0f5a6366d addres: d3:c0:f5:a6:36:6d
uart connected
uart starting ancs
on -> stateChange: poweredOn
on -> advertisingStart: error Error: Unknown (12)
uart read: L586;
uart read: L566;L552;
uart read: L536;L521;L467;
uart read: L480;
uart read: L457;L486;L550;L561;
uart read: L579;
uart read: L591;
Hande:64 uuid: d3c0f5a6366d
Got a disconnect
uart disconnected
Got a disconnect
Able: unknown peripheral d3:c0:f5:a6:36:6d disconnected!
Bleno: Handle: 64 UUID: 7e122588a5ee address 7e:12:25:88:a5:ee
on -> accept:
Hande:64 uuid: 7e122588a5ee
Got a disconnect
Got a disconnect
Got a disconnect
Able: unknown peripheral 7e:12:25:88:a5:ee disconnected!
Bleno: Handle: 64 UUID: 7e122588a5ee address 7e:12:25:88:a5:ee
on -> accept:
Recieved a Pairing Request
able encryptChange!!!
Notification: {"event":"added","flags":["silent","important"],"category":"location","categoryCount":1,"uid":0,"title":"Reisplanner","subtitle":"","message":"Uw volgende trein vertrekt om 14:36 van spoor 3b\nVertrektijd zoals gepland","messageSize":74,"date":""}
One thing I would check is if the Bluetooth device you are using supports Advertising and Scanning at the same time. Try doing sudo hciconfig hci0 lestates
and look for something like: YES Connectable Advertising State and Active Scanning State combination
I am not sure what the difference is to be honest. I think it could be a good idea to try using hcidump
to look for the differences. It could be that there is something in there that is stopping the advertisements, which cause the peripheral to disconnect...
The lestates
seem positive:
# hciconfig hci0 lestates Supported link layer states: YES Non-connectable Advertising State YES Scannable Advertising State YES Connectable Advertising State YES Directed Advertising State YES Passive Scanning State YES Active Scanning State YES Initiating State/Connection State in Master Role YES Connection State in the Slave Role YES Non-connectable Advertising State and Passive Scanning State combination YES Scannable Advertising State and Passive Scanning State combination YES Connectable Advertising State and Passive Scanning State combination YES Directed Advertising State and Passive Scanning State combination YES Non-connectable Advertising State and Active Scanning State combination YES Scannable Advertising State and Active Scanning State combination YES Connectable Advertising State and Active Scanning State combination YES Directed Advertising State and Active Scanning State combination YES Non-connectable Advertising State and Initiating State combination YES Scannable Advertising State and Initiating State combination YES Non-connectable Advertising State and Master Role combination YES Scannable Advertising State and Master Role combination YES Non-connectable Advertising State and Slave Role combination YES Scannable Advertising State and Slave Role combination YES Passive Scanning State and Initiating State combination YES Active Scanning State and Initiating State combination YES Passive Scanning State and Master Role combination YES Active Scanning State and Master Role combination YES Passive Scanning State and Slave Role combination YES Active Scanning State and Slave Role combination YES Initiating State and Master Role combination/Master Role and Master Role combination
Good idea to compare the hcidump
output between noble and able, I’ll try that later today.
I can’t reproduce the disconnection issue anymore so I assume that was an issue with my UART device. Still, I can’t have both the UART device and iOS/ANCS connected at the same time. This is my simplified test script now:
var Able = require('./lib/able');
var BleAncs = require('./index');
var TRY_WITH_ANCS = false;
var UART = '6e400001b5a3f393e0a9e50e24dcca9e';
function TestIssue3() {
this._able = new Able()
.on('stateChange', this.onStateChange.bind(this))
.on('discover', this.onDiscover.bind(this));
if (TRY_WITH_ANCS) this._able.on('advertisingStart', BleAncs.prototype.onAdvertisingStart.bind(this));
};
TestIssue3.prototype.onStateChange = function(state) {
console.log('state', state);
if (TRY_WITH_ANCS) BleAncs.prototype.onStateChange.call(this, 'poweredOn');
if (state == 'poweredOn') this._able.startScanning();
else this._able.stopScanning();
};
TestIssue3.prototype.onDiscover = function(peripheral) {
console.log('discovered', peripheral.uuid);
if (this._uart || peripheral.advertisement.serviceUuids.indexOf(UART) == -1) return;
console.log('found uart device');
this._able.stopScanning();
this._uart = peripheral;
peripheral.connect(function(e) { console.log('connected', e); });
};
var test = new TestIssue3();
When TRY_WITH_ANCS = true
, this is the output:
# node issue3.js
state poweredOn
on -> stateChange: poweredOn
on -> advertisingStart: success
discovered 563dd861449b
discovered 4d7f669638eb
discovered e2d5284cdde4
found uart device
Connect - uuid: e2d5284cdde4 addres: e2:d5:28:4c:dd:e4
Bleno: Handle: 64 UUID: 46c41c027aee address 46:c4:1c:02:7a:ee
hcidump
shows this about the attempted connection:
< HCI Command: LE Create Connection (0x08|0x000d) plen 25
bdaddr E2:D5:28:4C:DD:E4 type 1
interval 96 window 48 initiator_filter 0
own_bdaddr_type 0 min_interval 6 max_interval 12
latency 0 supervision_to 200 min_ce 4 max_ce 6
> HCI Event: Command Status (0x0f) plen 4
LE Create Connection (0x08|0x000d) status 0x0c ncmd 1
Error: Command Disallowed
The full log with TRY_WITH_ANCS = true
is here.
When TRY_WITH_ANCS = false
, this is the output:
# node issue3.js
state poweredOn
discovered 4d7f669638eb
discovered 563dd861449b
discovered db12bef4fee8
discovered e2d5284cdde4
found uart device
Connect - uuid: e2d5284cdde4 addres: e2:d5:28:4c:dd:e4
connected null
hcidump
shows this about the attempted connection:
< HCI Command: LE Create Connection (0x08|0x000d) plen 25
bdaddr E2:D5:28:4C:DD:E4 type 1
interval 96 window 48 initiator_filter 0
own_bdaddr_type 0 min_interval 6 max_interval 12
latency 0 supervision_to 200 min_ce 4 max_ce 6
> HCI Event: Command Status (0x0f) plen 4
LE Create Connection (0x08|0x000d) status 0x00 ncmd 1
The full log with TRY_WITH_ANCS = false
is here.
So it seems that we can’t connect to a peripheral while advertising as a peripheral. I’ll try connecting to the UART peripheral once the role-switching for ANCS has finished. Better ideas welcome :-)
Trying to connect to the UART peripheral after encryptChange
doesn’t help. The code:
var Able = require('./lib/able');
var BleAncs = require('./index');
var util = require('util');
var events = require('events');
var UART = '6e400001b5a3f393e0a9e50e24dcca9e';
function TestIssue3b() {
this._able = new Able()
.on('encryptChange', this.startUart.bind(this))
.on('discover', this.onDiscover.bind(this))
.on('stateChange', this.onStateChange.bind(this))
.on('accept', this.onAccept.bind(this))
.on('mtuChange', this.onMtuChange.bind(this))
.on('advertisingStart', this.onAdvertisingStart.bind(this))
.on('encryptChange', this.onEncryptChange.bind(this))
.on('encryptFail', this.onEncryptFail.bind(this))
.on('connect', this.onConnect.bind(this))
this._characteristics = {};
this._notifications = {};
this._lastUid = null;
};
util.inherits(TestIssue3b, BleAncs);
TestIssue3b.prototype.startUart = function() {
console.log('scanning for uart');
this._able.startScanning();
};
TestIssue3b.prototype.onDiscover = function(peripheral) {
console.log('discovered', peripheral.uuid);
if (this._uart || peripheral.advertisement.serviceUuids.indexOf(UART) == -1) return;
console.log('found uart device');
this._able.stopScanning();
this._uart = peripheral;
peripheral.connect(function(e) { console.log('connected', e); });
};
var test = new TestIssue3b();
The error (same as above):
< HCI Command: LE Create Connection (0x08|0x000d) plen 25
bdaddr E2:D5:28:4C:DD:E4 type 1
interval 96 window 48 initiator_filter 0
own_bdaddr_type 0 min_interval 6 max_interval 12
latency 0 supervision_to 200 min_ce 4 max_ce 6
> HCI Event: Command Status (0x0f) plen 4
LE Create Connection (0x08|0x000d) status 0x0c ncmd 1
Error: Command Disallowed
I’m out of ideas. Do you think this is a bug in able
, a limitation of my Edison chipset or BlueZ, or something else? Do you have alternative ideas for combining ble-ancs
with an UART connection, for example, would it be possible to control a second BLE dongle using the same library?
Hmm… my guess is that the Edison Chipset or Bluez may not like acting as a Central and Peripheral at the same time. Is the BLE built in? You could probably add a second dongle, but then you might as well just have 2 separate programs running.
There error below is coming from Bluez or the HW. It might be worth tracking down in the Bluez source to figure out what triggers the Command Disallowed error.
HCI Event: Command Status (0x0f) plen 4
LE Create Connection (0x08|0x000d) status 0x0c ncmd 1
Error: Command Disallowed
It is really weird that it works separately, but not when combined!
On Oct 29, 2015, at 7:24 AM, Sander Dijkhuis notifications@github.com wrote:
Trying to connect to the UART peripheral after encryptChange doesn’t help. The code:
var Able = require('./lib/able');
var BleAncs = require('./index');var util = require('util');
var events = require('events');var UART = '6e400001b5a3f393e0a9e50e24dcca9e';
function TestIssue3b() {
this._able = new Able()
.on('encryptChange', this.startUart.bind(this))
.on('discover', this.onDiscover.bind(this))
.on('stateChange', this.onStateChange.bind(this))
.on('accept', this.onAccept.bind(this))
.on('mtuChange', this.onMtuChange.bind(this))
.on('advertisingStart', this.onAdvertisingStart.bind(this))
.on('encryptChange', this.onEncryptChange.bind(this))
.on('encryptFail', this.onEncryptFail.bind(this))
.on('connect', this.onConnect.bind(this))
this._characteristics = {};
this._notifications = {};
this._lastUid = null;
};
util.inherits(TestIssue3b, BleAncs);
TestIssue3b.prototype.startUart = function() {
console.log('scanning for uart');
this._able.startScanning();
};
TestIssue3b.prototype.onDiscover = function(peripheral) {
console.log('discovered', peripheral.uuid);
if (this._uart || peripheral.advertisement.serviceUuids.indexOf(UART) == -1) return;
console.log('found uart device');
this._able.stopScanning();
this._uart = peripheral;
peripheral.connect(function(e) { console.log('connected', e); });
};var test = new TestIssue3b();
The error (same as above):< HCI Command: LE Create Connection (0x08|0x000d) plen 25
bdaddr E2:D5:28:4C:DD:E4 type 1
interval 96 window 48 initiator_filter 0
own_bdaddr_type 0 min_interval 6 max_interval 12
latency 0 supervision_to 200 min_ce 4 max_ce 6HCI Event: Command Status (0x0f) plen 4
LE Create Connection (0x08|0x000d) status 0x0c ncmd 1
Error: Command Disallowed
Full log on Gist https://gist.githubusercontent.com/sander/45d1f1740abf6612e121/raw/574289d0a3c3bf27afa38ad87e32021c9d9991c0/other-order.txt
I’m out of ideas. Do you think this is a bug in able, a limitation of my Edison chipset or BlueZ, or something else? Do you have alternative ideas for combining ble-ancs with an UART connection, for example, would it be possible to control a second BLE dongle using the same library?—
Reply to this email directly or view it on GitHub #3 (comment).
Yes, there’s a Broadcom BCM43340 chip integrated in the Edison. Is there a way to instruct the second Bluetooth program (Node script) to use the second dongle, and not try the same one as the first Bluetooth program?
Noble, Bleno, and Able all have language to specify which dongle to use. Checkout hci.js
:
https://github.com/robotastic/ble-ancs/blob/master/lib/hci-socket/hci.js#L121
If you specify it as an environment variable it should use that dongle. It might be better to just hard code it since there would be 2 different instances.
Works well when I run one with ABLE_HCI_DEVICE_ID=0
and the other with ABLE_HCI_DEVICE_ID=1
. I’m going to experiment with Node’s require('child_process').fork()
to get all the data still in one process. Thanks for the help, amazing to finally get full iOS notifications reliably on my Edison!
Closing this bug since it seems a driver/hardware issue, not something in ble-ancs or able.