adafruit / circuitpython

CircuitPython - a Python implementation for teaching coding with microcontrollers
https://circuitpython.org
Other
4.11k stars 1.22k forks source link

Bluetooth pairing on a second Linux based machines invalidate the pairing key on the first one #6626

Open hyx0329 opened 2 years ago

hyx0329 commented 2 years ago

CircuitPython version

- Adafruit CircuitPython 7.3.2 on 2022-07-20; MakerDiary nRF52840 MDK USB Dongle with nRF52840
- Customized CircuitPython 7.3.1; MakerDiary M60 Keyboard with nRF52840

Code/REPL

"""
Minimum working example adapted from https://github.com/adafruit/Adafruit_CircuitPython_BLE/blob/main/examples/ble_hid_periph.py
"""

# import board
import sys
import time
import microcontroller

from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS

import adafruit_ble
import _bleio
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

# Use default HID descriptor
hid = HIDService()
device_info = DeviceInfoService(
    software_revision=adafruit_ble.__version__, manufacturer="Adafruit Industries"
)
advertisement = ProvideServicesAdvertisement(hid)
advertisement.appearance = 961
# scan_response = Advertisement()  # not used

# ble handle
ble = adafruit_ble.BLERadio()

# prepare mac address pool
cpu_id = list(microcontroller.cpu.uid)
cpu_id_reverse = list(microcontroller.cpu.uid)
cpu_id_reverse.reverse()
mac_pool = bytearray(cpu_id + cpu_id_reverse)

# set bt id
# that's to say:
#   1. stop advertising and drop all existing connections
#   2. change full_name, MAC address
#   3. start advertising
def set_bt_id(bt_id):
    ble.stop_advertising()
    if ble.connected:
        for c in ble.connections:
            c.disconnect()
    # change name
    name = 'PYKB %d' % bt_id
    advertisement.full_name = name
    ble.name = name
    # change mac
    uid = mac_pool[bt_id : bt_id + 6]
    uid[-1] = uid[-1] | 0xC0
    address = _bleio.Address(uid, _bleio.Address.RANDOM_STATIC)
    ble._adapter.address = address
    # log
    print(ble._adapter.address)
    print(ble.name)
    # start advertising
    print("advertising")
    ble.start_advertising(advertisement, timeout=60)  # 60 secs

# set bt id and start the main loop
set_bt_id(0)
k = Keyboard(hid.devices)
kl = KeyboardLayoutUS(k)
while True:
    while not ble.connected:
        pass
    print("Start typing:")
    while ble.connected:
        c = sys.stdin.read(1)
        # change to PYKB #n by entering corresponding number
        if '0' <= c <= '9':
            set_bt_id(int(c))
            continue
        sys.stdout.write(c)
        kl.write(c)
        # print("sleeping")
        time.sleep(0.1)
    if not ble.advertising:
        print('restart advertising')
        ble.start_advertising(advertisement)

Behavior

I'm using a custom bluetooth keyboard based on CircuitPython which can switch to different hosts by changing its bluetooth MAC and full name. The code above is provided as a minimum working example. You can switch to different MAC and full name by entering 0-9 in the REPL. There should be no error.

Recently I encounter the issue that if I use the keyboard on two different Linux setups, after pairing to the second host, the pairing key for the first linux host is replaced/invalidated(my presumption), and the first host won't be able to connect to my keyboard without another manual pairing(connected and then immediately disconnected, see btmon.log). I have to adapt some tricks for dual boot pairing to share keys between two different Linux hosts to work it around.

There may be some bugs in either CircuitPython or Linux's bluetooth implementation(I think it's the latter).

Description

Additional information

btmon.log

mr4s commented 2 years ago

@hyx0329 I'm interested in taking this on

hyx0329 commented 2 years ago

I'm interested in taking this on

Glad to hear that! I can test the code. Is there any extra information required?