Description
Firmware
Adafruit CircuitPython 6.2.0 on 2021-04-05; Adafruit Circuit Playground Bluefruit with nRF52840
Behavior
This code allows two bluetooth devices (iPhone, iPad, Android, PC, etc.) to connect as a central and bond to the device.
When "kl1.write(c)" is called on line 71, the keystroke is sent to BOTH devices, not just the one connected to kbd1.
It seems there is no way to associate a Service (HIDService in this case) with just one of the connections - nor is it possible to tell the service which connection to send the HID events to.
This type of setup where the CP device allows multiple BLE Centrals to connect and receive keyboard events is critical to Assistive Technology users (like @cyborg55, @milador and many others). The users want to be able to pair a phone, iPad, speech generating device (SGD) and PC to the same device that allows them to trigger actions via AT Switches. Connections can be dropped and picked up, but we must be able to send keystrokes to a specific connection and keep track of which connection we're talking to.
While there may be a way to do this in CP, I've been working with @dhalbert, @David.G and others and have been stuck here for a while.
@dhalbert suggests that the issue might be at this section of Characteristic.c
I am good to find another solution, but I'm at a loss as it seems that Characteristics and Services seem detached from the Connections.
Code/REPL
#Two keyboard test
import adafruit_ble
from adafruit_ble.advertising import Advertisement
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.standard.hid import HIDService
from adafruit_ble.services.standard.device_info import DeviceInfoService
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS
import time
import board
import sys
hid1 = HIDService()
device_info = DeviceInfoService(software_revision=adafruit_ble.__version__, manufacturer="Adafruit Industries")
advertisement1 = ProvideServicesAdvertisement(hid1)
advertisement1.appearance = 961
scan_response1 = Advertisement()
scan_response1.complete_name = "CircuitPython HID1"
ble = adafruit_ble.BLERadio()
if not ble.connected:
print("advertising1")
ble.start_advertising(advertisement1, scan_response1)
else:
print("already connected")
print(ble.connections)
# This is a BLE keyboard
kbd1 = Keyboard(hid1.devices)
hid2 = HIDService()
advertisement2 = ProvideServicesAdvertisement(hid2)
advertisement2.appearance = 961
scan_response2 = Advertisement()
scan_response2.complete_name = "CircuitPython HID2"
# If I do the following before I get a connection on the first advertisement...
# ble.start_advertising(advertisement2, scan_response2)
# It produce the following error...
# _bleio.BluetoothError: Already advertising.
# This is a trick to wait for a first connection before making a new advertisement
while len(ble.connections) < 1:
pass
print("advertising2")
ble.start_advertising(advertisement2, scan_response2)
# This is a BLE keyboard
kbd2 = Keyboard(hid2.devices)
kl1 = KeyboardLayoutUS(kbd1)
kl2 = KeyboardLayoutUS(kbd2)
m=5
s=1
# This works even if mouse2 is not connected yet
while True:
while not ble.connected:
pass
print("Sending Keyboard input to just the first paired device")
while ble.connected:
c = sys.stdin.read(1)
sys.stdout.write(c)
kl1.write(c)
#kl2.write(c)
# This is far from perfect, we only advertise 1 where the second is not there too.
ble.start_advertising(advertisement1)
Additional Info
I have been testing with a BLE CircuitPlayground. It successfully lets you connect multiple devices and then sends keystrokes to both (even when you only request it on one of the HIDServices).