Skip to content

Instantly share code, notes, and snippets.

@dglaude
Last active January 10, 2023 17:01
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dglaude/c8634309c6bdfb29e33208b3f248d22b to your computer and use it in GitHub Desktop.
Save dglaude/c8634309c6bdfb29e33208b3f248d22b to your computer and use it in GitHub Desktop.
Circuit Python USB Host + Keyboard mirroring to USB and BLE
"""
This example acts as a BLE HID keyboard to peer devices.
It get's keycode from UART RX.
Color indication:
* BLUE_LED is blinking when not connected and steady blue when connected
* NEOPIXEL alternate between RED / GREEN / BLUE every time a keycode is transmitted (up and down event)
"""
import time
import board
#from digitalio import DigitalInOut, Direction
import digitalio
import busio
import usb_hid
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
from adafruit_hid.keycode import Keycode
import neopixel
# setup for LED to indicate BLE connection
blue_led = digitalio.DigitalInOut(board.BLUE_LED)
blue_led.direction = digitalio.Direction.OUTPUT
# Define the one NeoPixel on nRF52
pixels = neopixel.NeoPixel(board.NEOPIXEL, 1, brightness=0.3, auto_write=True)
color_a = 255
color_b = 0
color_c = 0
STX=0x02
ETX=0x03
uart = busio.UART(board.TX, board.RX, baudrate=115200)
hid = HIDService()
device_info = DeviceInfoService(software_revision=adafruit_ble.__version__,
manufacturer="Adafruit Industries")
advertisement = ProvideServicesAdvertisement(hid)
advertisement.appearance = 961
scan_response = Advertisement()
scan_response.complete_name = "CircuitPython HID"
ble = adafruit_ble.BLERadio()
if not ble.connected:
print("advertising")
ble.start_advertising(advertisement, scan_response)
else:
print("already connected")
print(ble.connections)
keyboard = Keyboard(hid.devices)
keyboard_layout = KeyboardLayoutUS(keyboard)
while True:
while not ble.connected:
time.sleep(0.2)
blue_led.value = not blue_led.value
pixels.fill((0, 0, 0))
print("Start typing:")
while ble.connected:
blue_led.value = True
data = uart.read(11)
if data is not None:
if (len(data) == 11) and (data[0] == STX) and (data[1] == 0x08) and (data[10] == ETX):
keyboard.report=bytearray(data[2:10])
keyboard.send()
pixels.fill((color_a, color_b, color_c))
color_a, color_b, color_c = (color_c, color_a, color_b)
print(data[2:10])
elif len(data)>0:
# Scan for STX ... ETX to resync
print(data)
report = bytearray(11)
for i in range(0, len(data)):
if data[i] == STX:
report = data[i:len(data)] + uart.read(11-(len(data)-i))
print(report)
if (len(report) == 11) and (report[0] == STX) and (report[1] == 0x08) and (report[10] == ETX):
keyboard.report=bytearray(report[2:10])
keyboard.send()
ble.start_advertising(advertisement)
"""
Demo:
A USB keyboard is connected (via USB OTG cable) to a Trinked M0.
Trinked run KBDADVUARTUSBH: Keyboard Advanced UART USB Host:
https://github.com/gdsports/usbhostcopro/tree/master/KBDADVUARTUSBH
The Trinked send it via UART TX to the UART RX of a nRF52840.
Every keyboard HID code is transmitted over USB and BLE simultaneously.
"""
import time
import board
from digitalio import DigitalInOut, Direction
import busio
import usb_hid
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
from adafruit_hid.keycode import Keycode
STX=0x02
ETX=0x03
uart = busio.UART(board.TX, board.RX, baudrate=115200)
hid = HIDService()
device_info = DeviceInfoService(software_revision=adafruit_ble.__version__,
manufacturer="Adafruit Industries")
advertisement = ProvideServicesAdvertisement(hid)
advertisement.appearance = 961
scan_response = Advertisement()
scan_response.complete_name = "CircuitPython HID"
ble = adafruit_ble.BLERadio()
if not ble.connected:
print("advertising")
ble.start_advertising(advertisement, scan_response)
else:
print("already connected")
print(ble.connections)
keyboard = Keyboard(hid.devices)
keyboard_layout = KeyboardLayoutUS(keyboard)
usb_keyboard = Keyboard(usb_hid.devices)
usb_keyboard_layout = KeyboardLayoutUS(usb_keyboard)
while True:
while not ble.connected:
pass
print("Start typing:")
while ble.connected:
data = uart.read(11)
if data is not None:
if (len(data) == 11) and (data[0] == STX) and (data[1] == 0x08) and (data[10] == ETX):
keyboard.report=bytearray(data[2:10])
keyboard.send()
usb_keyboard.report=bytearray(data[2:10])
usb_keyboard.send()
print(data[2:10])
elif len(data)>0:
# Scan for STX ... ETX to resync
print(data)
report = bytearray(11)
for i in range(0, len(data)):
if data[i] == STX:
report = data[i:len(data)] + uart.read(11-(len(data)-i))
print(report)
if (len(report) == 11) and (report[0] == STX) and (report[1] == 0x08) and (report[10] == ETX):
keyboard.report=bytearray(report[2:10])
keyboard.send()
usb_keyboard.report=bytearray(report[2:10])
usb_keyboard.send()
ble.start_advertising(advertisement)
@kmatch98
Copy link

Just saw your project in the weekly CircuitPython video and had to inquire on a few things. I used the USB host running in Arduino but had to fight through some key conversions for the arrow keys etc. Do you know of an easy way to deal with special keys? My code is here.

Also, I’m really interested in getting BLE Keyboard input into an NRF52840. Any ideas on this? I raised an issue and danH said he didn’t have an example ready.

Cool projects. Can’t wait to see what things you will do with this.

@dglaude
Copy link
Author

dglaude commented May 27, 2020

Hi @kmatch98,

Great, so that video recording is usefull to something.
Here is a link to Twitter that should show you the result and the FeatherWing I prototyped: https://twitter.com/DavidGlaude/status/1264590297654272002?s=20

I did nothing on the Arduino side, I just reused a pre-compiled image... so I have no clue what it does and how it does.
So right now, I just receive over serial the HID messsage that just need to be forwarded as is to another USB or to BLE.
Actually I never programmed in Arduino except for blinking an LED after an hour of installing the environement and installing library or other things. This is why I prefer to do Circuit Python.

Right now, there is no code or library behind this mini-project.
But I would like to "decode" the HID message and be able to manipulate it or take action depending on received key.
CircuitPython has code to send key, but not code to receive key.

Since stargirl (on Adafruit Discord Channel) is working on "Winterbloom Hostess Featherwing", I assume she will have code/library... so I am maybe not going to invest time in doing what she will be doing better.

Oh, your project is cyberDÛCK! That was a strong inspiration to trying again to get USB Keyboard input to work (I tried without success six month ago, but figured last week that it was my USB OTG cable that was not working).
And if I have all the required hardware, I may want to replicate what you have done.
I believe the ultimate goal would be to have something similar to IchigoJam (not the Raspberry Pi version, but the MCU version).
Or something like Pico8, but on physical hardware, not software your run on Win/Mac/Pi.
The ultimate all in one mini CircuitPython computer. Just like there were Forth and Lisp computer with all in one.

Yes, I almost wanted to ask DanH again if we could have BLE Keyboard input, even if it is just basic code to start with.
I would have put that in the weed section, but at the time of the meeting I was without a microphone and not in position to participate.
This would mean we can do with CircuitPython USB and BLE Keyboard input and output.

May I suggest you join CircuitPython Discord next week (Monday 01/06) and we raise this topic with DanH and other contributor.
Any other communication channel than this Gist is welcome too.
You have my Twitter account in a link above.

@kmatch98
Copy link

Thanks for the note. Somehow I missed your USB and UART work on your Twitter (sometimes I miss things by not checking very often) but glad that Scott referenced your work during the CIrcuitPython updates.

I’m actually the other way around, I have been learning Arduino for a couple of years and am just now trying to work with CIrcuitPython.

I’ll try to connect with the group call next week to support you on your request. Glad it’s not just me wanting BLE keyboard input. I submitted an issue but a nudge may help. I will let you lead the discussion and I will support where useful. I’m new to all this so I really prefer to lurk but will help where needed!

adafruit/Adafruit_CircuitPython_BLE#84

@dglaude
Copy link
Author

dglaude commented May 27, 2020

Added a BLE only version, that use the BLUE_LED and NEOPIXEL to indicate the status.
BLUE_LED blinking = not connected
BLUE_LED steady = connected
NEOPIXEL change color everytime a key is transmitted, both for up and down event

This can work standalone and the status is visible.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment