Connection to aioble on pi pico W immediately fails

193 Views Asked by At

I'm trying to use a raspberry pi pico w to connect to my computer and act as a small keyboard using a few buttons. I have not yet implemented that part as I have been trying to get it to connect to the computer first.

In my code, I use the aioble module (imported as ble for my convenience) to define services:

  • The device information service
  • The battery level service
  • The HID service (top-level)

The HID service is the only one advertised, and appears when I search for connections with my ble debug app, however when I try to connect the bluetooth connection fails.

import struct
from bluetooth import UUID
import asyncio

_ADV_INTERVAL_MS = 250_000

dis_service = ble.Service(UUID(0x180A))
dis_model_char = ble.Characteristic(dis_service, UUID(0x2A24), read = True)
dis_serial_char = ble.Characteristic(dis_service, UUID(0x2A25), read = True)
dis_firm_char = ble.Characteristic(dis_service, UUID(0x2A26), read = True)
dis_hard_char = ble.Characteristic(dis_service, UUID(0x2A27), read = True)
dis_soft_char = ble.Characteristic(dis_service, UUID(0x2A28), read = True)
dis_manu_char = ble.Characteristic(dis_service, UUID(0x2A29), read = True)
dis_pnp_char = ble.Characteristic(dis_service, UUID(0x2A50), read = True)

bas_service = ble.Service(UUID(0x180F))
bas_levl_char = ble.Characteristic(bas_service, UUID(0x2A19), notify = True)

hid_service = ble.Service(UUID(0x1812))
hid_info_char = ble.Characteristic(hid_service, UUID(0x2A4A), read = True)
hid_irmap_char = ble.Characteristic(hid_service, UUID(0x2A4B), read = True)
hid_control_char = ble.Characteristic(hid_service, UUID(0x2A4C), write = True)
hid_report_char = ble.Characteristic(hid_service, UUID(0x2A4D), notify = True, read = True)
hid_protocol_char = ble.Characteristic(hid_service, UUID(0x2A4E), write = True)
hid_report_desc = ble.Descriptor(hid_report_char, UUID(0x2908), read = True)

ble.register_services(dis_service, bas_service, hid_service)
print("Services registered")

HID_INPUT_REPORT = bytes([    # Report Description: describes what we communicate
    0x05, 0x01,                    # USAGE_PAGE (Generic Desktop)
    0x09, 0x06,                    # USAGE (Keyboard)
    0xa1, 0x01,                    # COLLECTION (Application)
    0x85, 0x01,                    #     REPORT_ID (1)
    0x75, 0x01,                    #     Report Size (1)
    0x95, 0x08,                    #     Report Count (8)
    0x05, 0x07,                    #     Usage Page (Key Codes)
    0x19, 0xE0,                    #     Usage Minimum (224)
    0x29, 0xE7,                    #     Usage Maximum (231)
    0x15, 0x00,                    #     Logical Minimum (0)
    0x25, 0x01,                    #     Logical Maximum (1)
    0x81, 0x02,                    #     Input (Data, Variable, Absolute); Modifier byte
    0x95, 0x01,                    #     Report Count (1)
    0x75, 0x08,                    #     Report Size (8)
    0x81, 0x01,                    #     Input (Constant); Reserved byte
    0x95, 0x05,                    #     Report Count (5)
    0x75, 0x01,                    #     Report Size (1)
    0x05, 0x08,                    #     Usage Page (LEDs)
    0x19, 0x01,                    #     Usage Minimum (1)
    0x29, 0x05,                    #     Usage Maximum (5)
    0x91, 0x02,                    #     Output (Data, Variable, Absolute); LED report
    0x95, 0x01,                    #     Report Count (1)
    0x75, 0x03,                    #     Report Size (3)
    0x91, 0x01,                    #     Output (Constant); LED report padding
    0x95, 0x06,                    #     Report Count (6)
    0x75, 0x08,                    #     Report Size (8)
    0x15, 0x00,                    #     Logical Minimum (0)
    0x25, 0x65,                    #     Logical Maximum (101)
    0x05, 0x07,                    #     Usage Page (Key Codes)
    0x19, 0x00,                    #     Usage Minimum (0)
    0x29, 0x65,                    #     Usage Maximum (101)
    0x81, 0x00,                    #     Input (Data, Array); Key array (6 bytes)
    0xc0                           # END_COLLECTION
])

def pack_str(in_str):
    return struct.pack(str(len(in_str))+"s", in_str.encode('UTF-8'))

pnp_manufacturer_source = 0x01 # Bluetooth uuid list
pnp_manufacturer_uuid = 0xaf73 # 0xFEB2 for Microsoft, 0xFE61 for Logitech, 0xFD65 for Razer
pnp_product_id = 0x01 # ID 1
pnp_product_version = 0x0123 # Version 1.2.3

battery_level = 100

appearance = 961
name = "Pi Pico Keyboard"

dis_model_char.write(pack_str("1"))
dis_serial_char.write(pack_str("1"))
dis_firm_char.write(pack_str("1"))
dis_hard_char.write(pack_str("1"))
dis_soft_char.write(pack_str("1"))
dis_manu_char.write(pack_str("Homebrew"))
dis_pnp_char.write(struct.pack("<BHHH", pnp_manufacturer_source, pnp_manufacturer_uuid, pnp_product_id, pnp_product_version))

bas_levl_char.write(struct.pack("<B", battery_level))

hid_info_char.write(b"\x01\x01\x00\x02")
hid_irmap_char.write(HID_INPUT_REPORT)
hid_report_desc.write(struct.pack("<BB", 1, 1))
#hid_report_desc.write(struct.pack("<BB", 1, 2))
hid_protocol_char.write(b"\x01")

print("Characteristics written")

async def advertise():
    while True:
        print("Advertising")
        async with await ble.advertise(
            _ADV_INTERVAL_MS,
            name=name,
            services=[UUID(0x1812)],
            appearance=appearance,
        ) as connection:
            print("Connection from", connection.device)
            await connection.disconnected(timeout_ms=None)

# Run both tasks.
async def main():
    t1 = asyncio.create_task(advertise())
    await asyncio.gather(t1)

asyncio.run(main())

I've tried various different responses to connections, for example exchanging mtu await connection.exchange_mtu() but this seemed to make it worse.

0

There are 0 best solutions below