Skip to content

Instantly share code, notes, and snippets.

@weskerfoot
Created January 10, 2023 19:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save weskerfoot/190221f006b99af10641081f34628eca to your computer and use it in GitHub Desktop.
Save weskerfoot/190221f006b99af10641081f34628eca to your computer and use it in GitHub Desktop.
import board
import busio
import digitalio
import adafruit_hid as 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
from adafruit_circuitplayground import cp
from time import sleep
from random import randint
def wheel(pos):
# Input a value 0 to 255 to get a color value.
# The colours are a transition r - g - b - back to r.
if pos < 0 or pos > 255:
r = g = b = 0
elif pos < 85:
r = int(pos * 3)
g = int(255 - pos * 3)
b = 0
elif pos < 170:
pos -= 85
r = int(255 - pos * 3)
g = 0
b = int(pos * 3)
else:
pos -= 170
r = 0
g = int(pos * 3)
b = int(255 - pos * 3)
return (r, g, b)
# defined in keymap.c
Keycode.ENABLE_RGB = 0x5da6
Keycode.BRIGHTNESS_UP = 0x5da7
Keycode.BRIGHTNESS_DOWN = 0x5da8
Keycode.SWITCH_RGB_MODE = 0x5da9
# Custom codes
FUNCTION_KEY_ON = b'\xf2'
FUNCTION_KEY_OFF = b'\xf1'
BT_ON = b'\xff'
BT_OFF = b'\xfe'
special_codes = {
BT_ON,
BT_OFF,
FUNCTION_KEY_OFF,
FUNCTION_KEY_ON
}
class RGB:
def __init__(self):
self.colour_mode = {
"bluetooth" : False,
"function" : False,
"caps_lock" : False,
"rgb_mode" : False
}
self.rainbow_mode_pos = 0
self.rgb_mode = 0
self.brightness_delta = 0.05
self.pixels = cp.pixels
self.snake_index = 0
self.current_accel = 0
self.rgb_mode_callbacks = [
self.rainbow_next,
self.rainbow_next_random,
self.rainbow_next_snake,
self.rainbow_next_shake,
self.rainbow_next_brightness
]
def rainbow_next(self):
self.rainbow_mode_pos = (self.rainbow_mode_pos + 1) % 255
self.pixels.fill(wheel(self.rainbow_mode_pos))
def rainbow_next_shake(self):
x, y, z = cp.acceleration
self.rainbow_mode_pos = (self.rainbow_mode_pos + abs(z)) % 255
print(f"x = {x}, y = {y}, z = {z}")
self.pixels.fill((0, 0, 0))
print(abs(x) - abs(self.current_accel))
if abs(abs(x) - abs(self.current_accel)) > 0.3:
self.snake_index = (self.snake_index + 1) % 10
self.pixels[self.snake_index] = wheel(self.rainbow_mode_pos)
self.pixels.brightness = 1.0
self.current_accel = abs(x)
def rainbow_next_snake(self):
self.rainbow_mode_pos = (self.rainbow_mode_pos + 1) % 255
self.pixels.fill((0, 0, 0))
new_colour = wheel(self.rainbow_mode_pos)
self.pixels[self.snake_index] = new_colour
self.pixels[(self.snake_index+1) % 10] = new_colour
self.pixels[(self.snake_index-1) % 10] = new_colour
self.snake_index = (self.snake_index + 1) % 10
self.pixels.brightness = 1.0
def rainbow_next_random(self):
self.rainbow_mode_pos = (self.rainbow_mode_pos + randint(1, 255)) % 255
self.pixels.fill(wheel(self.rainbow_mode_pos))
def rainbow_next_brightness(self):
if (self.pixels.brightness <= abs(self.brightness_delta) or
self.pixels.brightness >= (1 - (abs(self.brightness_delta)) + 0.01)):
self.brightness_delta = -self.brightness_delta
self.change_brightness(self.brightness_delta)
self.rainbow_next()
def change_brightness(self, value):
new_brightness = self.pixels.brightness + value
if new_brightness <= 1.0 and new_brightness >= 0:
self.pixels.brightness += value
def transition(self, data, modifiers): # either actual keycode or custom code
if data == Keycode.BRIGHTNESS_UP:
print("Brightness up")
self.change_brightness(0.1)
elif data == Keycode.BRIGHTNESS_DOWN:
print("Brightness down")
self.change_brightness(-0.1)
elif data == Keycode.CAPS_LOCK:
self.colour_mode["caps_lock"] = not self.colour_mode["caps_lock"]
elif data == Keycode.ENABLE_RGB:
self.colour_mode["rgb_mode"] = not self.colour_mode["rgb_mode"]
elif data == Keycode.SWITCH_RGB_MODE:
if self.colour_mode["rgb_mode"]:
self.rgb_mode = (self.rgb_mode + 1) % len(self.rgb_mode_callbacks)
elif data == BT_ON:
modifiers.mode = "bt"
self.colour_mode["bluetooth"] = True
self.colour_mode["caps_lock"] = modifiers[Keycode.CAPS_LOCK]
self.colour_mode["rgb_mode"] = modifiers[Keycode.ENABLE_RGB]
modifiers.sync()
self.show_colour()
print("bt_on, caps_lock = ", modifiers[Keycode.CAPS_LOCK])
elif data == BT_OFF:
modifiers.mode = "kb"
self.colour_mode["bluetooth"] = False
self.colour_mode["caps_lock"] = modifiers[Keycode.CAPS_LOCK]
self.colour_mode["rgb_mode"] = modifiers[Keycode.ENABLE_RGB]
modifiers.sync()
self.show_colour()
elif data == FUNCTION_KEY_OFF:
self.colour_mode["function"] = False
elif data == FUNCTION_KEY_ON:
self.colour_mode["function"] = True
else:
pass
def show_colour(self):
if self.colour_mode["caps_lock"]:
self.pixels.fill((255, 0, 0))
elif self.colour_mode["function"]:
self.pixels.fill((0, 255, 0))
elif self.colour_mode["bluetooth"]:
self.pixels.fill((0, 0, 255))
elif self.colour_mode["rgb_mode"]:
self.rgb_mode_callbacks[self.rgb_mode]()
else:
self.pixels.fill((0, 0, 0))
self.pixels.show()
rgb = RGB()
# Bluetooth
hid = HIDService()
device_info = DeviceInfoService(software_revision=adafruit_ble.__version__,
manufacturer="Wesley Kerfoot")
advertisement = ProvideServicesAdvertisement(hid)
advertisement.appearance = 961
scan_response = Advertisement()
scan_response.complete_name = "Sheogorath"
ble = adafruit_ble.BLERadio()
if not ble.connected:
print("advertising")
ble.start_advertising(advertisement, scan_response)
else:
print("already connected")
print(ble.connections)
k = Keyboard(hid.devices)
kl = KeyboardLayoutUS(k)
# UART code
uart = busio.UART(tx=None, rx=board.RX, baudrate=9600)
print("Keyboard is started")
class Modifiers:
def __init__(self, mode="kb"):
self.bt_modifiers = self.empty_modifiers()
self.kb_modifiers = self.empty_modifiers()
self.mode_modifiers = {
"bt" : self.bt_modifiers,
"kb" : self.kb_modifiers
}
self.mode = mode
def sync(self):
mods = self.mode_modifiers[self.mode]
for kc, v in mods.items():
if not v:
#print(f"releasing {kc}")
k.release(kc)
else:
k.press(kc)
def __setitem__(self, k, v):
mods = self.mode_modifiers[self.mode]
mods[k] = v
def __getitem__(self, k):
return self.mode_modifiers[self.mode][k]
def keys(self):
if self.mode == "bt":
return self.bt_modifiers.keys()
else:
return self.kb_modifiers.keys()
def empty_modifiers(self):
return {
Keycode.LEFT_SHIFT : False,
Keycode.RIGHT_SHIFT : False,
Keycode.LEFT_CONTROL : False,
Keycode.RIGHT_CONTROL : False,
Keycode.LEFT_ALT : False,
Keycode.RIGHT_ALT : False,
Keycode.CAPS_LOCK : False,
Keycode.ENABLE_RGB : False,
Keycode.SWITCH_RGB_MODE : False,
Keycode.BRIGHTNESS_UP : False,
Keycode.BRIGHTNESS_DOWN : False
}
modifiers = Modifiers()
def reset_keyboard():
for kc in modifiers.keys():
modifiers[kc] = False
k.release_all()
current_keycode = []
while True:
assert len(current_keycode) <= 3
data = uart.read(1)
if data != b'' and (not data in special_codes):
if len(current_keycode) == 3:
state_down = bool(current_keycode[2]) # whether it was pressed or released
kc = int.from_bytes(bytes(current_keycode[0:2]), 'little')
if kc < 255 or kc in modifiers.keys(): # Adafruit HID doesn't like keycodes greater than a single byte
# Key was pressed and is a modifier
print(kc)
if state_down and (kc in modifiers.keys()):
if ble.connected and kc < 255:
k.press(kc)
if kc != Keycode.CAPS_LOCK:
modifiers[kc] = True
else:
# caps lock needs to be toggled, since we don't do that on "down" events
modifiers[kc] = not modifiers[kc]
rgb.transition(kc, modifiers)
else:
# Key was pressed and is not a modifier
if state_down:
# Only send keys if bluetooth is connected
if ble.connected and kc < 255:
k.press(kc)
k.release(kc)
else:
# Key was released and is any key
# Only release keys if bluetooth is connected
if ble.connected and kc < 255:
k.release(kc)
# set state to released for all keys except caps lock
if kc in modifiers.keys() and kc != Keycode.CAPS_LOCK:
modifiers[kc] = False
current_keycode = []
current_keycode += data
rgb.transition(data, modifiers)
# Every iteration, update the colour state
rgb.show_colour()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment