import usb_hid from adafruit_hid.keyboard import Keyboard from adafruit_hid.keycode import Keycode from adafruit_hid.consumer_control import ConsumerControl from adafruit_hid.consumer_control_code import ConsumerControlCode from adafruit_hid.keyboard_layout_us import KeyboardLayoutUS from adafruit_hid.mouse import Mouse from hid_gamepad import Gamepad import board import microcontroller import digitalio import time import pwmio # PWM module bl_pwm_value = 32767 # Middle value ad_pwm_value = 32767 # Middle value try: # Set up a keyboard device. kbd = Keyboard(usb_hid.devices) layout = KeyboardLayoutUS(kbd) consumer_control = ConsumerControl(usb_hid.devices) gp = Gamepad(usb_hid.devices) mouse = Mouse(usb_hid.devices) except Exception as e: # Log the error message if initialization fails print(f"Initialization failed: {e}") # Reset the RP2040 microcontroller.reset() # Initialize the GP22 pin gp22 = digitalio.DigitalInOut(board.GP22) gp22.direction = digitalio.Direction.OUTPUT # Set the GP22 pin high gp22.value = False # Initialize the GP19 pin gp19 = digitalio.DigitalInOut(board.GP19) gp19.direction = digitalio.Direction.OUTPUT # Set the GP19 pin high gp19.value = False gp21 = digitalio.DigitalInOut(board.GP21) gp21.direction = digitalio.Direction.OUTPUT # Set the GP21 pin high gp21.value = False # Initialize PWM output pin GP22 for the breathing light effect # gp22_pwm = pwmio.PWMOut(board.GP22, frequency=5000, duty_cycle=0) # Initialize BL PWM BL_PWM_RP = pwmio.PWMOut(board.GP20, frequency=5000, duty_cycle=5000) # Initialize AD PWM AD_PWM_RP = pwmio.PWMOut(board.GP18, frequency=5000, duty_cycle=32700) def toggle_gp22(): gp22.value = not gp22.value print(f"GP22 state toggled to: {gp22.value}") kbd.press(Keycode.CAPS_LOCK) kbd.release(Keycode.CAPS_LOCK) def start_breathing_light(): tmp_time = 0 stop_loop = True # Flag used to break out of the while loop kbd = None # Make sure kbd is initialized gp22_temp = gp22.value # Record the current Caps Lock state for i in range(1,10+1): gp22.value = not gp22.value # print("GP22 TEST:",gp22.value) time.sleep(0.3) try: while stop_loop: tmp_time +=1 if tmp_time >= 255: gp22.value = not gp22.value # print("GP22 IS:",gp22.value) tmp_time = 0 current_keys = set(scan_keyboard()) # Handle keys that are pressed for key in current_keys: if key is not None: time.sleep(0.2) if key is not None: if kbd: kbd.release_all() gp22.value = gp22_temp gp21.value = not gp21.value stop_loop = False # Set the flag break finally: if kbd: kbd.release_all() gp22.value = gp22_temp def toggle_gp19(): gp19.value = not gp19.value # print(f"GP19 state toggled to: {gp19.value}") def toggle_gp21(): gp21.value = not gp21.value # print(f"GP21 state toggled to: {gp21.value}") start_breathing_light() def ad_pwm_down(): global ad_pwm_value if ad_pwm_value <= 0: ad_pwm_value = 0 else: ad_pwm_value -= 6553 ad_pwm_value = min(max(ad_pwm_value, 0), 65535) # Update the duty cycle AD_PWM_RP.duty_cycle = ad_pwm_value print("pwm_value == ", ad_pwm_value) def ad_pwm_up(): global ad_pwm_value if ad_pwm_value >= 65535: ad_pwm_value = 65535 else: ad_pwm_value += 6553 ad_pwm_value = min(max(ad_pwm_value, 0), 65535) # Update the duty cycle AD_PWM_RP.duty_cycle = ad_pwm_value print("pwm_value == ", ad_pwm_value) # Because the backlight is controlled by an NPN transistor pull-down, a higher PWM duty cycle makes the backlight dimmer. So the pwm_up functions below actually decrease the duty cycle, and pwm_down increases it. def bl_pwm_up(): global bl_pwm_value if bl_pwm_value <= 0: bl_pwm_value = 0 else: bl_pwm_value -= 6553 bl_pwm_value = min(max(bl_pwm_value, 0), 65535) # Update the duty cycle BL_PWM_RP.duty_cycle = bl_pwm_value print("pwm_value == ",bl_pwm_value) def bl_pwm_down(): global bl_pwm_value if bl_pwm_value >= 65535: bl_pwm_value = 65535 else: bl_pwm_value += 6553 bl_pwm_value = min(max(bl_pwm_value, 0), 65535) # Update the duty cycle BL_PWM_RP.duty_cycle = bl_pwm_value print("pwm_value == ",bl_pwm_value) def shift_left_bracket(): kbd.press(Keycode.SHIFT, Keycode.LEFT_BRACKET) # kbd.release(Keycode.SHIFT) kbd.release_all() def shift_right_bracket(): kbd.press(Keycode.SHIFT, Keycode.RIGHT_BRACKET) # kbd.release(Keycode.SHIFT) kbd.release_all() def shift_backslash(): kbd.press(Keycode.SHIFT, Keycode.BACKSLASH) # kbd.release(Keycode.SHIFT) kbd.release_all() def shift_grave_accent(): kbd.press(Keycode.SHIFT, Keycode.GRAVE_ACCENT) # kbd.release(Keycode.SHIFT) kbd.release_all() def lock_screen(): # Lock the screen (suspend on Linux) kbd.press(Keycode.WINDOWS, Keycode.L) kbd.release_all() def scan_previous_track(): consumer_control.press(ConsumerControlCode.SCAN_PREVIOUS_TRACK) consumer_control.release() def play_pause(): consumer_control.press(ConsumerControlCode.PLAY_PAUSE) consumer_control.release() def scan_next_track(): consumer_control.press(ConsumerControlCode.SCAN_NEXT_TRACK) consumer_control.release() def mouse_left(): mouse.press(Mouse.LEFT_BUTTON) time.sleep(0.02) mouse.release(Mouse.LEFT_BUTTON) time.sleep(0.25) def mouse_right(): mouse.press(Mouse.RIGHT_BUTTON) time.sleep(0.02) mouse.release(Mouse.RIGHT_BUTTON) time.sleep(0.25) def mouse_press_up(): mouse.move(y=-8) def mouse_release_up(): mouse.move(y=0) def mouse_press_down(): mouse.move(y=8) def mouse_release_down(): mouse.move(y=0) def mouse_press_left(): mouse.move(x=-8) def mouse_release_left(): mouse.move(x=0) def mouse_press_right(): mouse.move(x=8) def mouse_release_right(): mouse.move(x=0) def gamepad_press_north(): # X button gp.press_buttons(4) def gamepad_release_north(): gp.release_buttons(4) def gamepad_press_south(): # B button gp.press_buttons(1) def gamepad_release_south(): gp.release_buttons(1) def gamepad_press_west(): # Y button gp.press_buttons(5) def gamepad_release_west(): gp.release_buttons(5) def gamepad_press_east(): # A button gp.press_buttons(2) def gamepad_release_east(): gp.release_buttons(2) def gamepad_press_tl(): # L button gp.press_buttons(7) def gamepad_release_tl(): gp.release_buttons(7) def gamepad_press_tr(): # R button gp.press_buttons(8) def gamepad_release_tr(): gp.release_buttons(8) def gamepad_btn_select(): # Select button gp.click_buttons(11) def gamepad_btn_start(): # Start button gp.click_buttons(12) def gamepad_press_up(): gp.move_joysticks(x=0, y=-127) def gamepad_release_up(): gp.move_joysticks(x=0,y=0) def gamepad_press_down(): gp.move_joysticks(x=0, y=127) def gamepad_release_down(): gp.move_joysticks(x=0,y=0) def gamepad_press_left(): # Send both Gamepad / Keyboard gp.move_joysticks(x=-127, y=0) def gamepad_release_left(): gp.move_joysticks(x=0,y=0) def gamepad_press_right(): gp.move_joysticks(x=127, y=0) def gamepad_release_right(): gp.move_joysticks(x=0,y=0) def custom_btn_a(): KEY_MAP[0] = [Keycode.UP_ARROW, Keycode.LEFT_ARROW, Keycode.DOWN_ARROW, Keycode.RIGHT_ARROW, Keycode.L, Keycode.R, Keycode.X, Keycode.Y, Keycode.B, Keycode.A] def custom_btn_b(): KEY_MAP[0] = [CUSTOM_KEYS["GAMEPAD_UP"], CUSTOM_KEYS["GAMEPAD_LEFT"], CUSTOM_KEYS["GAMEPAD_DOWN"], CUSTOM_KEYS["GAMEPAD_RIGHT"], CUSTOM_KEYS["GAMEPAD_TL"], CUSTOM_KEYS["GAMEPAD_TR"], CUSTOM_KEYS["GAMEPAD_NORTH"], CUSTOM_KEYS["GAMEPAD_WEST"], CUSTOM_KEYS["GAMEPAD_SOUTH"],CUSTOM_KEYS["GAMEPAD_EAST"]] # Define custom function keys using a dictionary CUSTOM_KEYS = { # Add more custom keys as needed "FN_KEY" : -100, "FN_MUTE" : -101, "FN_VOLUME_DOWN" : -102, "FN_VOLUME_UP" : -103, "FN_LOCK_SCREEN" : -104, "FN_BL_CONTROL_SCREEN" : -105, "FN_BL_PWM_DOWN" : -106, "FN_BL_PWM_UP" : -107, "SHIFT_GRAVE_ACCENT" : -108, "SHIFT_BACKSLASH" : -109, "SHIFT_LEFT_BRACKET" : -110, "SHIFT_RIGHT_BRACKET" : -111, "FN_MOUSE_LEFT" : -112, "FN_MOUSE_RIGHT" : -113, "FN_MOUSE_MOVE_UP" : -114, "FN_MOUSE_MOVE_DOWN" : -115, "FN_MOUSE_MOVE_LEFT" : -116, "FN_MOUSE_MOVE_RIGHT" : -117, "FN_BTN_A" : -118, "FN_BTN_B" : -119, "FN_BTN_X" : -120, "FN_BTN_Y" : -121, "GAMEPAD_NORTH" : -122, "GAMEPAD_SOUTH" : -123, "GAMEPAD_WEST" : -124, "GAMEPAD_EAST" : -125, "GAMEPAD_TL" : -126, "GAMEPAD_TR" : -127, "GAMEPAD_SELECT" : -128, "GAMEPAD_START" : -129, "GAMEPAD_UP" : -130, "GAMEPAD_DOWN" : -131, "GAMEPAD_LEFT" : -132, "GAMEPAD_RIGHT" : -133 } SPECIAL_KEY_FUNCTIONS = { CUSTOM_KEYS["FN_MUTE"] : toggle_gp19, CUSTOM_KEYS["FN_VOLUME_DOWN"] : ad_pwm_down, CUSTOM_KEYS["FN_VOLUME_UP"] : ad_pwm_up, CUSTOM_KEYS["FN_LOCK_SCREEN"] : lock_screen, CUSTOM_KEYS["FN_BL_CONTROL_SCREEN"] : toggle_gp21, CUSTOM_KEYS["FN_BL_PWM_DOWN"] : bl_pwm_down, CUSTOM_KEYS["FN_BL_PWM_UP"] : bl_pwm_up, CUSTOM_KEYS["SHIFT_GRAVE_ACCENT"] : shift_grave_accent, CUSTOM_KEYS["SHIFT_BACKSLASH"] : shift_backslash, CUSTOM_KEYS["SHIFT_LEFT_BRACKET"] : shift_left_bracket, CUSTOM_KEYS["SHIFT_RIGHT_BRACKET"] : shift_right_bracket, Keycode.CAPS_LOCK : toggle_gp22, ConsumerControlCode.SCAN_PREVIOUS_TRACK : scan_previous_track, ConsumerControlCode.PLAY_PAUSE : play_pause, ConsumerControlCode.SCAN_NEXT_TRACK : scan_next_track, CUSTOM_KEYS["FN_MOUSE_LEFT"] : mouse_left, CUSTOM_KEYS["FN_MOUSE_RIGHT"] : mouse_right, CUSTOM_KEYS["FN_MOUSE_MOVE_UP"] : (mouse_press_up, mouse_release_up), CUSTOM_KEYS["FN_MOUSE_MOVE_DOWN"] : (mouse_press_down, mouse_release_down), CUSTOM_KEYS["FN_MOUSE_MOVE_LEFT"] : (mouse_press_left, mouse_release_left), CUSTOM_KEYS["FN_MOUSE_MOVE_RIGHT"] : (mouse_press_right, mouse_release_right), CUSTOM_KEYS["FN_BTN_A"] : custom_btn_a, CUSTOM_KEYS["FN_BTN_B"] : custom_btn_b, CUSTOM_KEYS["GAMEPAD_NORTH"] : (gamepad_press_north, gamepad_release_north), CUSTOM_KEYS["GAMEPAD_SOUTH"] : (gamepad_press_south, gamepad_release_south), CUSTOM_KEYS["GAMEPAD_WEST"] : (gamepad_press_west, gamepad_release_west), CUSTOM_KEYS["GAMEPAD_EAST"] : (gamepad_press_east, gamepad_release_east), CUSTOM_KEYS["GAMEPAD_TL"] : (gamepad_press_tl, gamepad_release_tl), CUSTOM_KEYS["GAMEPAD_TR"] : (gamepad_press_tr, gamepad_release_tr), CUSTOM_KEYS["GAMEPAD_SELECT"] : gamepad_btn_select, CUSTOM_KEYS["GAMEPAD_START"] : gamepad_btn_start, CUSTOM_KEYS["GAMEPAD_UP"] : (gamepad_press_up, gamepad_release_up), CUSTOM_KEYS["GAMEPAD_DOWN"] : (gamepad_press_down, gamepad_release_down), CUSTOM_KEYS["GAMEPAD_LEFT"] : (gamepad_press_left, gamepad_release_left), CUSTOM_KEYS["GAMEPAD_RIGHT"] : (gamepad_press_right, gamepad_release_right), } # Define the number of rows and columns NUM_ROWS = 7 NUM_COLS = 10 # Define the key map for the keyboard matrix KEY_MAP = [ #col0 col1 col2 col3 col4 col5 col6 col7 col8 col9 [Keycode.UP_ARROW, Keycode.LEFT_ARROW, Keycode.DOWN_ARROW, Keycode.RIGHT_ARROW, Keycode.L, Keycode.R, Keycode.X, Keycode.Y, Keycode.B, Keycode.A], # row 0 [Keycode.ONE, Keycode.TWO, Keycode.THREE, Keycode.FOUR, Keycode.FIVE, Keycode.SIX, Keycode.SEVEN, Keycode.EIGHT, Keycode.NINE, Keycode.ZERO], # row 1 [Keycode.Q, Keycode.W, Keycode.E, Keycode.R, Keycode.T, Keycode.Y, Keycode.U, Keycode.I, Keycode.O, Keycode.P], # row 2 [Keycode.A, Keycode.S, Keycode.D, Keycode.F, Keycode.G, Keycode.H, Keycode.J, Keycode.K, Keycode.L, Keycode.BACKSPACE], # row 3 [Keycode.Z, Keycode.X, Keycode.C, Keycode.V, Keycode.B, Keycode.N, Keycode.M, Keycode.FORWARD_SLASH, Keycode.ENTER, None], # row 4 [Keycode.SHIFT, Keycode.CAPS_LOCK, Keycode.MINUS, Keycode.EQUALS, Keycode.SEMICOLON, Keycode.QUOTE, Keycode.COMMA, Keycode.PERIOD, Keycode.RIGHT_SHIFT, None], # row 5 [CUSTOM_KEYS["FN_KEY"], Keycode.CONTROL, Keycode.LEFT_ALT, CUSTOM_KEYS["GAMEPAD_SELECT"], Keycode.SPACE, CUSTOM_KEYS["GAMEPAD_START"], Keycode.RIGHT_ALT, Keycode.WINDOWS, CUSTOM_KEYS["FN_KEY"], None] # row 6 ] # Define the FN key map for the keyboard matrix FN_MAP = [ #col0 col1 col2 col3 col4 col5 col6 col7 col8 col9 [CUSTOM_KEYS["FN_MOUSE_MOVE_UP"],CUSTOM_KEYS["FN_MOUSE_MOVE_LEFT"], CUSTOM_KEYS["FN_MOUSE_MOVE_DOWN"], CUSTOM_KEYS["FN_MOUSE_MOVE_RIGHT"], CUSTOM_KEYS["FN_MOUSE_LEFT"], CUSTOM_KEYS["FN_MOUSE_RIGHT"], Keycode.X, Keycode.Y, CUSTOM_KEYS["FN_BTN_B"], CUSTOM_KEYS["FN_BTN_A"]], # row 0 [Keycode.F1, Keycode.F2, Keycode.F3, Keycode.F4, Keycode.F5, Keycode.F6, Keycode.F7, Keycode.F8, Keycode.F9, Keycode.F10], # row 1 [Keycode.ESCAPE, CUSTOM_KEYS["FN_MUTE"], CUSTOM_KEYS["FN_VOLUME_DOWN"], CUSTOM_KEYS["FN_VOLUME_UP"], ConsumerControlCode.SCAN_PREVIOUS_TRACK, ConsumerControlCode.PLAY_PAUSE, ConsumerControlCode.SCAN_NEXT_TRACK, CUSTOM_KEYS["FN_LOCK_SCREEN"], Keycode.F11, Keycode.F12], # row 2 [Keycode.GRAVE_ACCENT, CUSTOM_KEYS["SHIFT_GRAVE_ACCENT"], Keycode.BACKSLASH, CUSTOM_KEYS["SHIFT_BACKSLASH"], CUSTOM_KEYS["SHIFT_LEFT_BRACKET"], CUSTOM_KEYS["SHIFT_RIGHT_BRACKET"], Keycode.LEFT_BRACKET, Keycode.RIGHT_BRACKET, Keycode.L, Keycode.DELETE], # row 3 [Keycode.INSERT, Keycode.HOME, CUSTOM_KEYS["FN_BL_CONTROL_SCREEN"], Keycode.END, Keycode.PAGE_UP, Keycode.PAGE_DOWN, Keycode.SCROLL_LOCK, Keycode.FORWARD_SLASH, Keycode.ENTER, None], # row 4 [Keycode.TAB, Keycode.CAPS_LOCK, CUSTOM_KEYS["FN_BL_PWM_DOWN"], CUSTOM_KEYS["FN_BL_PWM_UP"], Keycode.SEMICOLON, Keycode.QUOTE, Keycode.COMMA, Keycode.PERIOD, Keycode.SHIFT, None], # row 5 [CUSTOM_KEYS["FN_KEY"], Keycode.CONTROL, Keycode.LEFT_ALT, Keycode.PRINT_SCREEN, Keycode.SPACE, Keycode.PAUSE, Keycode.RIGHT_ALT, Keycode.WINDOWS, CUSTOM_KEYS["FN_KEY"], None] # row 6 ] # Define the row and column pins row_pins = [board.GP16, board.GP10, board.GP11, board.GP12, board.GP13, board.GP14, board.GP15] col_pins = [board.GP0, board.GP1, board.GP2, board.GP3, board.GP4, board.GP5, board.GP6, board.GP7, board.GP8, board.GP9] # Initialize row and column pins row_gpio = [digitalio.DigitalInOut(pin) for pin in row_pins] col_gpio = [digitalio.DigitalInOut(pin) for pin in col_pins] # Set rows as inputs with pull-up and columns as outputs set to low for row in row_gpio: row.direction = digitalio.Direction.INPUT row.pull = digitalio.Pull.UP for col in col_gpio: col.direction = digitalio.Direction.OUTPUT col.value = False # Function to scan the keyboard matrix def scan_keyboard(): keys_pressed = [] fn_active = False for col_index, col_pin in enumerate(col_gpio): col_pin.direction = digitalio.Direction.OUTPUT col_pin.value = True for row_index, row_pin in enumerate(row_gpio): row_pin.direction = digitalio.Direction.INPUT row_pin.pull = digitalio.Pull.DOWN if row_pin.value: # Key is pressed time.sleep(0.05) # Debounce delay if row_pin.value: key = KEY_MAP[row_index][col_index] if key == CUSTOM_KEYS["FN_KEY"]: fn_active = True keys_pressed.append((row_index, col_index)) # Record the key position col_pin.value = False # Set the column back to low active_map = FN_MAP if fn_active else KEY_MAP translated_keys = [] for row_index, col_index in keys_pressed: translated_key = active_map[row_index][col_index] translated_keys.append(translated_key) print("ROW", row_index, "COL", col_index) return translated_keys previous_keys = set() # Main loop try: while True: current_keys = set(scan_keyboard()) # Handle keys that were released for key in previous_keys - current_keys: if key is not None and key >= 0: kbd.release(key) elif key is not None and key in SPECIAL_KEY_FUNCTIONS: if isinstance(SPECIAL_KEY_FUNCTIONS[key], tuple): (fun_make, fun_break) = SPECIAL_KEY_FUNCTIONS[key] fun_break() # Handle keys that are pressed for key in current_keys - previous_keys: if key is not None: if key in SPECIAL_KEY_FUNCTIONS: if isinstance(SPECIAL_KEY_FUNCTIONS[key], tuple): (fun_make, fun_break) = SPECIAL_KEY_FUNCTIONS[key] fun_make() else: SPECIAL_KEY_FUNCTIONS[key]() elif key >= 0: # Filter out negative key codes kbd.press(key) previous_keys = current_keys time.sleep(0.01) # Small delay to avoid too fast key presses except Exception as e: print(f"An error occurred: {e}") # Perform necessary cleanup, such as releasing all keys try: kbd.release_all() except Exception as cleanup_error: print(f"Error during cleanup: {cleanup_error}") # Reset the RP2040 microcontroller.reset()