8000 BLE HID OUT report handling seems not to be implemented. · Issue #207 · adafruit/Adafruit_CircuitPython_BLE · GitHub
[go: up one dir, main page]

Skip to content
BLE HID OUT report handling seems not to be implemented. #207
Open
@ThomasAtBBTF

Description

@ThomasAtBBTF

For my project to create a BLE HID Braille device I tried to read in the BLE HID keyboard code the LED Status and got an error.

Here is the CP 9.2 CircuitPython code:

import sys
import time
import board
from digitalio import DigitalInOut, Direction, Pull
import adafruit_ble
from adafruit_ble.advertising import Advertisement
from adafruit_ble.advertising.standard import ProvideServicesAdvertisement
from adafruit_ble.services.standard.hid import DEFAULT_HID_DESCRIPTOR, HIDService
from adafruit_ble.services.standard.device_info import DeviceInfoService
from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode

print("starting HIDService ")
hid = HIDService(DEFAULT_HID_DESCRIPTOR)
device_info = DeviceInfoService(software_revision=adafruit_ble.__version__, manufacturer="BBTF GmbH")
advertisement = ProvideServicesAdvertisement(hid)
advertisement.appearance = 961  # HID Keyboard
scan_response = Advertisement()
scan_response.complete_name = "MyPyKeyboard"

print("starting BLERadio")
ble = adafruit_ble.BLERadio()

print("starting Keyboard")
k = Keyboard(hid.devices)
ble_connected = False
ble_advertising = False

print("start Advertising")
if not ble.connected:
    ble.start_advertising(advertisement, scan_response)
    ble_advertising = True


def k_send(keycode):
    try:
        print("sending:", keycode)
        k.send(keycode)
        print("sent")
    except Exception as e:
        print("Exception in k_send:", e)
    return


loop_counter = 0
io0 = DigitalInOut(board.IO0)
io0.direction = Direction.INPUT
io0.pull = Pull.UP
last_io0_value = io0.value

print("Main Loop")
while True:
    loop_counter += 1
    if loop_counter > 20:
        if ble_advertising:
            print("Advertising")
        if ble_connected:
            print("Connected")
        loop_counter = 0
    if ble.connected:
        if not ble_connected:
            ble_connected = True
            print("Connected to BLERadio")
        if ble_advertising:
            ble_advertising = False
            # ble.stop_advertising()
            print("Stopping Advertising")
        if last_io0_value != io0.value:
            last_io0_value = io0.value
            if not last_io0_value:
                k_send(Keycode.A)
            print("Type(k) :", type(k))
            print("dir(k) :", dir(k ))
            print("type(k._keyboard_device) :", type(k._keyboard_device))
            print("Type(k.led_status)", type(k.led_status))
    else:
        if ble_connected:
            ble_connected = False
        if not ble_advertising:
            print("Not connected to BLERadio")
            ble.start_advertising(advertisement, scan_response)
            ble_advertising = True

I am using (for test purposes) the "boot" button of the ESP32 board to send the character "a" and after sending the character I am testing the led_status.

In order to understand the objects involved I am printing the Type and the members.

The function / property k.led_status tries to get "self._keyboard_device.get_last_received_report()"
which fails with a runtime error.

Here is my log from the test:

code.py output:
starting HIDService
starting BLERadio
starting Keyboard
devices: <class 'list'> [<ReportOut object at 0x3c19fc90>, <ReportIn object at 0x3c19fca0>, <ReportIn object at 0x3c1a0200>, <ReportIn object at 0x3c1a0480>]
looking for 0x1 0x6
device: <ReportOut object at 0x3c19fc90> <class 'ReportOut'> 0x1 0x6
device: <ReportIn object at 0x3c19fca0> <class 'ReportIn'> 0x1 0x6
found!
start Advertising
Main Loop
Advertising
Connected to BLERadio
Stopping Advertising
Connected
Connected
Connected
Connected
Connected
Connected
sending: 4
sent
Type(k) : <class 'Keyboard'>
dir(k) : ['__class__', '__init__', '__module__', '__qualname__', 'send', '__dict__', 'press', 'release', 'release_all', 'report', '_keyboard_device', 'led_status', 'LED_NUM_LOCK', 'LED_CAPS_LOCK', 'LED_SCROLL_LOCK', 'LED_COMPOSE', 'report_modifier', 'report_keys', '_led_status', '_add_keycode_to_report', '_remove_keycode_from_report', 'led_on']
type(k._keyboard_device) : <class 'ReportIn'>
Traceback (most recent call last):
  File "code.py", line 75, in <module>
  File "/lib/adafruit_hid/keyboard.py", line 180, in led_status
AttributeError: 'ReportIn' object has no attribute 'get_last_received_report'

Code done running.

I am using a FeatherS3 from UM

PS: I modified "find_device" to see what it does while finding the requested device which explains the debug output after
"starting Keyboard" and before "start Advertising".

Like so:

def find_device(
    devices: Sequence[object],
    *,
    usage_page: int,
    usage: int,
    timeout: int = None,
) -> object:
    """Search through the provided sequence of devices to find the one with the matching
    usage_page and usage.

    :param timeout: Time in seconds to wait for USB to become ready before timing out.
    Defaults to None to wait indefinitely.
    Ignored if device is not a `usb_hid.Device`; it might be BLE, for instance."""

    print("devices:", type(devices), devices)
    if hasattr(devices, "send_report"):
        print("has send_report")
        devices = [devices]  # type: ignore
    device = None
    print("looking for", hex(usage_page), hex(usage))
    for dev in devices:
        print("device:", dev, type(dev), hex(dev.usage_page), hex(dev.usage))
        if (
            dev.usage_page == usage_page
            and dev.usage == usage
            and hasattr(dev, "send_report")
        ):
            print("found!")
            device = dev
            break
    if device is None:
        raise ValueError("Could not find matching HID device.")

    # Wait for USB to be connected only if this is a usb_hid.Device.
    if Device and isinstance(device, Device):
        if supervisor is None:
            # Blinka doesn't have supervisor (see issue Adafruit_Blinka#711), so wait
            # one second for USB to become ready
            time.sleep(1.0)
        elif timeout is None:
            # default behavior: wait indefinitely for USB to become ready
            while not supervisor.runtime.usb_connected:
                time.sleep(1.0)
        else:
            # wait up to timeout seconds for USB to become ready
            for _ in range(timeout):
                if supervisor.runtime.usb_connected:
                    return device
                time.sleep(1.0)
            raise OSError("Failed to initialize HID device. Is USB connected?")

    return device

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions

      0