From 975b700410610dbc79add2e830615ab8ec2a713d Mon Sep 17 00:00:00 2001 From: CJ Date: Sun, 18 Jan 2026 11:54:22 +0000 Subject: [PATCH] Upload files to "/" --- boot.py | 10 ++ code.py | 333 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 343 insertions(+) create mode 100644 boot.py create mode 100644 code.py diff --git a/boot.py b/boot.py new file mode 100644 index 0000000..3456adc --- /dev/null +++ b/boot.py @@ -0,0 +1,10 @@ +import supervisor +import storage +import usb_hid + +# Set Manufacturer / Product / VID / PID +supervisor.set_usb_identification(manufacturer='CJ', product='CJs Macro Pad', vid=0xC107, pid=0x1316) +# Disable CIRCUITPY / appearing as USB drive +storage.disable_usb_drive() +# Set interface name for the gamepad +usb_hid.set_interface_name("CJs Macro Pad") \ No newline at end of file diff --git a/code.py b/code.py new file mode 100644 index 0000000..be92dd3 --- /dev/null +++ b/code.py @@ -0,0 +1,333 @@ +# RGB Keypad Multi Deck +# V4.1 Author CJ Games Live, V4 Author Gareth Naylor, adapted from code posted on the Pimoroni Forums +# Right row of buttons assigns the function set of the keypad (with the pico to the right of the board) +# 0 - Microsoft Teams / Media +# 1 - General +# 2 - Number Pad +# 3 - F13 - F24 + +# Import libraries +import time +import board +import busio +import usb_hid +import random +from adafruit_bus_device.i2c_device import I2CDevice +import adafruit_dotstar +from adafruit_hid.keyboard import Keyboard +from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS +from adafruit_hid.keycode import Keycode +from adafruit_hid.consumer_control import ConsumerControl +from adafruit_hid.consumer_control_code import ConsumerControlCode + +# Configure I2C keypad +from digitalio import DigitalInOut, Direction +cs = DigitalInOut(board.GP17) +cs.direction = Direction.OUTPUT +cs.value = 0 +num_pixels = 16 +pixels = adafruit_dotstar.DotStar(board.GP18, board.GP19, num_pixels, brightness=0.3, auto_write=True) +i2c = busio.I2C(board.GP5, board.GP4) +device = I2CDevice(i2c, 0x20) +kbd = Keyboard(usb_hid.devices) +layout = KeyboardLayoutUS(kbd) +consumer_control = ConsumerControl(usb_hid.devices) + +# Map integer values to ConsumerControlCode +INT_TO_CONSUMER_CONTROL = { + 178: ConsumerControlCode.RECORD, + 179: ConsumerControlCode.FAST_FORWARD, + 180: ConsumerControlCode.REWIND, + 181: ConsumerControlCode.SCAN_NEXT_TRACK, + 182: ConsumerControlCode.SCAN_PREVIOUS_TRACK, + 183: ConsumerControlCode.STOP, + 184: ConsumerControlCode.EJECT, + 205: ConsumerControlCode.PLAY_PAUSE, + 233: ConsumerControlCode.VOLUME_INCREMENT, + 234: ConsumerControlCode.VOLUME_DECREMENT, + 266: ConsumerControlCode.MUTE, + # Add more mappings if needed, see https://docs.circuitpython.org/projects/hid/en/latest/api.html#adafruit_hid.consumer_control_code.ConsumerControlCode +} + +# Program functions +def read_button_states(x, y): + pressed = [0] * 16 + with device: + device.write(bytes([0x0])) + result = bytearray(2) + device.readinto(result) + b = result[0] | result[1] << 8 + for i in range(x, y): + if not (1 << i) & b: + pressed[i] = 1 + else: + pressed[i] = 0 + return pressed + +def toggle(this): + return 0 if this == 1 else 1 + +def debounce(amount): + time.sleep(amount) # Just wait a bit for the pressed signals to settle down + +def handle_led(id, colour): + latch[id] = toggle(latch[id]) # Toggle the latch + if latch[id] == 1: + pixels[id] = colour # Map pixel index to 0-255 range + else: + pixels[id] = _base + +def set_set(set_id): + latch[set_id] = 1 # Set the key to latched + pixels[set_id] = _green # Set control set LED to green + for i in range(4): + if i != set_id: + pixels[i] = (0, 0, 0) + latch[i] = 0 + + # Set all defined keys in the set to the base colour + # Keys not defined are set to off (0,0,0) + for i in range(4, 16): + if button_set[i + (set_id * 16)][0] == "empty": + pixels[i] = (0, 0, 0) + else: + pixels[i] = _base + +def startup(base_colour): + for i in range(16): + if i < 4: + pixels[i] = base_colour + else: + pixels[i] = (0, 0, 0) + +def send_keycodes(k, x): + codes = button_set[x][0] # Codes to send + latch = button_set[x][2] # Latch boolean (0 or 1) + col = button_set[x][3] # Get the colour + handle_led(k, col) # Set the LED to the correct colour + + print(f"Button index: {x}") + print(f"Codes: {codes}") + print(f"Type of codes: {type(codes)}") + + if isinstance(codes, int) and codes in INT_TO_CONSUMER_CONTROL: + # Handle ConsumerControlCode based on integer mapping + print("Sending ConsumerControlCode from INT_TO_CONSUMER_CONTROL mapping") + consumer_control.send(INT_TO_CONSUMER_CONTROL[codes]) + elif isinstance(codes, ConsumerControlCode): + # Directly send if it is already a ConsumerControlCode + print("Sending ConsumerControlCode directly") + consumer_control.send(codes) + elif isinstance(codes, int): # Handle as a standard HID code + print("Sending HID code directly") + kbd.send(codes) + elif isinstance(codes, str): + # Handle string-based codes (e.g., "Keycode.A,Keycode.B,Keycode.C") + print("Processing string codes") + symbols = codes.split(",") + if len(symbols) == 3: + kbd.send(eval(symbols[0]), eval(symbols[1]), eval(symbols[2])) + elif len(symbols) == 2: + kbd.send(eval(symbols[0]), eval(symbols[1])) + else: + raise ValueError("String code format is invalid. Expected format: 'keycode1,keycode2,keycode3'") + else: + raise TypeError("Button set code must be either a string, int (HID), or ConsumerControlCode.") + + if latch == 0: + pixels[k] = _base # Set LED back to base colour if not latched + +def send_text(k, x): + text = button_set[x][0] # Text to send + col = button_set[x][3] # Get the colour + handle_led(k, col) # Set the LED to the correct colour + if text == "_random_": + r = random.randint(0, len(messages) - 1) + text = messages[r] + + layout.write(text) # Send the text + + # Check if the text is "Thanks for a good meeting." and send Enter + if text == "Thanks for a good meeting.": + kbd.send(Keycode.ENTER) # Send Enter key press + +# Define colours +_red = (255, 0, 0) +_green = (0, 255, 0) +_blue = (0, 0, 255) +_yellow = (255, 255, 0) +_orange = (255, 103, 0) +_purple = (84, 22, 250) +_base = (64, 64, 64) +_black = (0, 0, 0) # Colour to light the key if defined + +# Manually define F13 to F24 using their HID keycodes +F13 = 0x68 +F14 = 0x69 +F15 = 0x6A +F16 = 0x6B +F17 = 0x6C +F18 = 0x6D +F19 = 0x6E +F20 = 0x6F +F21 = 0x70 +F22 = 0x71 +F23 = 0x72 +F24 = 0x73 + +# Define button set array +button_set = {} +for i in range(64): + button_set[i] = ["empty", 0, 0, _red] + +# ---- 1st BUTTON SET ---- 4 - 15 +# (keycodes or text, flag 0=kc 1=text, latch flag 0=no 1=yes, colour) +button_set[12] = ["Keycode.CONTROL,Keycode.SHIFT,Keycode.M", 0, 1, _red] # Mute toggle in Teams +button_set[8] = ["Keycode.CONTROL,Keycode.SHIFT,Keycode.O", 0, 1, _purple] # Video toggle in Teams +button_set[4] = ["Keycode.CONTROL,Keycode.SHIFT,Keycode.K", 0, 1, _orange] # Raise hand toggle in Teams + +button_set[13] = [234, 0, 0, _blue] # Volume Up +button_set[9] = [266, 0, 1, _orange] # Mute Volume +button_set[5] = [233, 0, 0, _blue] # Volume Down + +button_set[14] = [180, 0, 0, _blue] # Rewind +button_set[10] = [205, 0, 1, _orange] # Play/Pause +button_set[6] = [179, 0, 0, _blue] # Fast Forward + +button_set[15] = ["Thanks for a good meeting.", 1, 0, _green] # End of meeting chat +button_set[11] = ["Keycode.CONTROL,Keycode.SHIFT,Keycode.S", 0, 0, _green] # Accept audio call +button_set[7] = ["Keycode.CONTROL,Keycode.SHIFT,Keycode.H", 0, 0, _red] # Leave teams meeting + +# ---- 2nd BUTTON SET ---- 20 - 31 +# (keycodes or text, flag 0=kc 1=text, latch flag 0=no 1=yes, colour) +#button_set[28] +#button_set[24] +#button_set[20] + +#button_set[29] +#button_set[25] +#button_set[21] + +button_set[30] = ["Keycode.CONTROL,Keycode.Z", 0, 0, _blue] # Undo +#button_set[26] +button_set[22] = ["Keycode.CONTROL,Keycode.Y", 0, 0, _blue] # Redo + +button_set[31] = ["Keycode.CONTROL,Keycode.C", 0, 0, _blue] # Copy +button_set[27] = ["Keycode.CONTROL,Keycode.X", 0, 0, _blue] # Cut +button_set[23] = ["Keycode.CONTROL,Keycode.V", 0, 0, _blue] # Paste + +# ---- 3rd BUTTON SET ---- 36 - 47 +# (keycodes or text, flag 0=kc 1=text, latch flag 0=no 1=yes, colour) +button_set[44] = [Keycode.SEVEN, 0, 0, _red] # 7 +button_set[40] = [Keycode.EIGHT, 0, 0, _red] # 8 +button_set[36] = [Keycode.NINE, 0, 0, _red] # 9 + +button_set[45] = [Keycode.FOUR, 0, 0, _red] # 4 +button_set[41] = [Keycode.FIVE, 0, 0, _red] # 5 +button_set[37] = [Keycode.SIX, 0, 0, _red] # 6 + +button_set[46] = [Keycode.ONE, 0, 0, _red] # 1 +button_set[42] = [Keycode.TWO, 0, 0, _red] # 2 +button_set[38] = [Keycode.THREE, 0, 0, _red] # 3 + +button_set[47] = [Keycode.PERIOD, 0, 0, _red] # . +button_set[43] = [Keycode.ZERO, 0, 0, _red] # 0 +button_set[39] = [Keycode.RETURN, 0, 0, _red] # Enter + +# ---- 4th BUTTON SET ---- 52 - 63 +# (keycodes or text , flag 0=kc 1=text, latch flag 0=no 1=yes, colour) +button_set[60] = [F13, 0, 0, _blue] # F13 +button_set[56] = [F14, 0, 0, _blue] # F14 +button_set[52] = [F15, 0, 0, _blue] # F15 + +button_set[61] = [F16, 0, 0, _blue] # F16 +button_set[57] = [F17, 0, 0, _blue] # F17 +button_set[53] = [F18, 0, 0, _blue] # F18 + +button_set[62] = [F19, 0, 0, _blue] # F19 +button_set[58] = [F20, 0, 0, _blue] # F20 +button_set[54] = [F21, 0, 0, _blue] # F21 + +button_set[63] = [F22, 0, 0, _blue] # F22 +button_set[59] = [F23, 0, 0, _blue] # F23 +button_set[55] = [F24, 0, 0, _blue] # F24 + +# Variable setup +held = [0] * 16 # Setup +latch = [0] * 16 # Setup +_set = 5 # Set the default active button set to > 4 so i.e. no active set! +todo = False + +# Main program starts here +startup(_base) # Light up the top row with the base colour as the keys are defined + +# Main loop starts here +while True: + pressed = read_button_states(0, 16) + + if pressed[0]: + _set = 0 + set_set(_set) + debounce(0.25) # Debounce + kbd.release_all() + debounce(0.4) + held = [0] * 16 # Setup + latch = [0] * 16 # Setup + held[_set] = 1 + + elif pressed[1]: + _set = 1 + set_set(_set) + debounce(0.25) # Debounce + kbd.release_all() + debounce(0.4) + held = [0] * 16 # Setup + latch = [0] * 16 # Setup + held[_set] = 1 + + elif pressed[2]: + _set = 2 + set_set(_set) + debounce(0.25) # Debounce + kbd.release_all() + debounce(0.4) + held = [0] * 16 # Setup + latch = [0] * 16 # Setup + held[_set] = 1 + + elif pressed[3]: + _set = 3 + set_set(_set) + debounce(0.25) # Debounce + kbd.release_all() + debounce(0.4) + held = [0] * 16 # Setup + latch = [0] * 16 # Setup + held[_set] = 1 + + else: + for i in range(4, 16): + if pressed[i] and button_set[i + (_set * 16)][0] != "empty": + # If we get here we have something to do. + todo = True + index = i + (_set * 16) + if not held[i]: + if button_set[index][1] == 0: + send_keycodes(i, index) # Call the function to send keycodes + debounce(0.25) + else: + send_text(i, index) # Call the function to send text + debounce(0.25) + pixels[i] = _base # Because this is sending text we do not latch the LED + latch[i] = 0 + + kbd.release_all() + held[i] = 1 + debounce(0.4) + if button_set[index][2] == 0: # If the button should not latch + pixels[i] = _base # Set LEDs back to base colour + if todo: + for i in range(16): + held[i] = 0 # Set held states to off + todo = False +