Description
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