Open ArneJoris opened 3 years ago
Python script for central to repeat this:
from bluezero import device, GATT, adapter
import time
dongle = list(adapter.Adapter.available())[0]
dongle.powered = False
dongle.powered = True
peripherals = []
def deviceFound(dev):
if dev.address not in peripherals:
peripherals.append(dev.address)
dongle.on_device_found = deviceFound
dongle.nearby_discovery(timeout=5)
adapterAddress=dongle.address
def connect(deviceAddress):
d = device.Device(adapterAddress, deviceAddress)
d.connect(timeout=30)
print("Connected to device %s." %deviceAddress)
def disconnect(deviceAddress):
d = device.Device(adapterAddress, deviceAddress)
d.disconnect()
print("Disconnected from device %s." %deviceAddress)
def gattResolved(deviceAddress):
c = GATT.Characteristic(adapterAddress, deviceAddress, "b4703001-eb6b-4b82-ae21-cf035a429a94", "b4703002-eb6b-4b82-ae21-cf035a429a94")
return c.resolve_gatt()
while True:
print("Found %d perihperals: %s." %(len(peripherals), ",".join(peripherals)))
for p in peripherals:
connect(p)
for i in range(0,5):
for address in peripherals:
print("Device %s GATT resolved: %s" %(address, gattResolved(address)))
time.sleep(5)
for p in peripherals:
disconnect(p)
for i in range(0,2):
for address in peripherals:
print("Device %s GATT resolved: %s" %(address, gattResolved(address)))
time.sleep(5)
Seems like there is a timing hole; if I wait 3 seconds between connecting to the 2 devices, everything works fine:
Found 2 peripherals: E1:FC:EC:71:0C:9A,EE:EF:2A:24:D6:A1. Connected to device E1:FC:EC:71:0C:9A. Connected to device EE:EF:2A:24:D6:A1. Device E1:FC:EC:71:0C:9A GATT resolved: True Device EE:EF:2A:24:D6:A1 GATT resolved: True Device E1:FC:EC:71:0C:9A GATT resolved: True Device EE:EF:2A:24:D6:A1 GATT resolved: True Device E1:FC:EC:71:0C:9A GATT resolved: True Device EE:EF:2A:24:D6:A1 GATT resolved: True Device E1:FC:EC:71:0C:9A GATT resolved: True Device EE:EF:2A:24:D6:A1 GATT resolved: True
Yep, I suspect following line is the problem:
It doesn't actually check what device has connected instead it attempts to lookup the first one pending so which in case there are multiple can cause the wrong object to be used. If you do serialize that should work though, but we will need to fix this in the long term, so we either block or lookup by peer address.
Thanks @Vudentz , le_conn_complete_evt() not using its bdaddr argument to find the pending connection but instead taking the first pending connection on the controller does explain this problem.
I've got several async threads all creating connections. To work around this, I should probably centralize the connection establishment and only initiate one connection at a time and wait for it to succeed (or fail) before making the next connection.
@ArneJoris I will attempt to fix this in the kernel but if you don't control it you might as well have to do the work around anyway otherwise it would work with newer kernels were this issue was fixed.
When the central connects to a single peripheral, GATT services are resolved after a few seconds.
When the central connects to two peripherals, the GATT services of the most recently connected peripheral are resolved for just a few seconds.:
This has been repeated on bluez-5.58, bluez-5.60, bluez-5.61 and bluez-5.51. Cache = always, or Cache = no seems to make no difference.
Bluetoothd -nEd output:
Btmon output on the central when connecting to two peripherals: