Skip to content

Instantly share code, notes, and snippets.

@wulfboy-95
Last active March 24, 2023 15:28
Show Gist options
  • Save wulfboy-95/0969c4f7135aa46e02bc0a9d3990286e to your computer and use it in GitHub Desktop.
Save wulfboy-95/0969c4f7135aa46e02bc0a9d3990286e to your computer and use it in GitHub Desktop.
CircuitPython code for a TADA-68 style keyboard with a Raspberry Pi Pico controller.
# Copyright wulfboy_95 2021, All Rights Reserved.
# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved. This file is offered as-is,
# without any warranty.
import board
import digitalio
import struct
import usb_hid
#from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode
# Create and init keyboard USB HID device.
keeb = None
for dev in list(usb_hid.devices):
if ((dev.usage == 0x06) and
(dev.usage_page == 0x01) and
hasattr(dev, "send_report")):
keeb = dev
if keeb == None:
raise Exception("Device cannot be found")
# 2D list to convert keyboard matrix to keycode
matrix = [
[
Keycode.ESCAPE, Keycode.ONE, Keycode.TWO, Keycode.THREE, Keycode.FOUR,
Keycode.FIVE, Keycode.SIX, Keycode.SEVEN, Keycode.EIGHT, Keycode.NINE,
Keycode.ZERO, Keycode.MINUS, Keycode.EQUALS, Keycode.BACKSPACE,
Keycode.GRAVE_ACCENT
],
[
Keycode.TAB, Keycode.Q, Keycode.W, Keycode.E, Keycode.R, Keycode.T,
Keycode.Y, Keycode.U, Keycode.I, Keycode.O, Keycode.P,
Keycode.LEFT_BRACKET, Keycode.RIGHT_BRACKET, Keycode.BACKSLASH,
Keycode.DELETE
],
[
Keycode.CAPS_LOCK, Keycode.A, Keycode.S, Keycode.D, Keycode.F,
Keycode.G, Keycode.H, Keycode.J, Keycode.K, Keycode.L,
Keycode.SEMICOLON, Keycode.QUOTE, Keycode.RETURN, Keycode.ENTER,
Keycode.PAGE_UP
],
[
Keycode.LEFT_SHIFT, Keycode.SHIFT, Keycode.Z, Keycode.X, Keycode.C,
Keycode.V, Keycode.B, Keycode.N, Keycode.M, Keycode.COMMA,
Keycode.PERIOD, Keycode.FORWARD_SLASH, Keycode.RIGHT_SHIFT,
Keycode.UP_ARROW, Keycode.PAGE_DOWN
],
[
Keycode.LEFT_CONTROL, Keycode.LEFT_GUI, Keycode.LEFT_ALT,
Keycode.SPACE, Keycode.SPACE, Keycode.SPACE, Keycode.SPACEBAR,
Keycode.SPACE, Keycode.SPACE, Keycode.RIGHT_ALT, Keycode.RIGHT_GUI,
Keycode.RIGHT_CONTROL, Keycode.LEFT_ARROW, Keycode.DOWN_ARROW,
Keycode.RIGHT_ARROW
],
] # matrix[row][col]
input_pins = (board.GP6, board.GP7, board.GP8, board.GP9,board.GP10) # 5 input rows
input_pin_array = [] # DigitalIO array for inputs.
output_pins = (
board.GP11, board.GP12, board.GP13, board.GP14, board.GP15,
board.GP16, board.GP17, board.GP18, board.GP19, board.GP20,
board.GP21, board.GP22, board.GP26, board.GP27, board.GP28
) # 15 output columns
output_pin_array = [] # DigitalIO array for outputs.
# Initialise DigitalIO pins.
for pin in input_pins:
key_pin = digitalio.DigitalInOut(pin)
key_pin.direction = digitalio.Direction.INPUT
key_pin.pull = digitalio.Pull.DOWN
input_pin_array.append(key_pin)
for pin in output_pins:
key_pin = digitalio.DigitalInOut(pin)
key_pin.direction = digitalio.Direction.OUTPUT
key_pin.drive_mode = digitalio.DriveMode.PUSH_PULL
output_pin_array.append(key_pin)
keys_pressed = [] # list for possible n-key rollover upgrade in the future.
report_array = [0x00] * 8
while True:
for col in range(len(output_pin_array)):
output_pin_array[col].value = True # Turn on column pin.
for row in range(len(input_pin_array)):
if (matrix[row][col] >= 0xE0) and (input_pin_array[row].value): # Check if modifier is pressed
report_array[0] |= Keycode.modifier_bit(matrix[row][col]) # Add modifier bit to report.
elif input_pin_array[row].value: # Check if key is pressed.
keys_pressed.append(matrix[row][col])
output_pin_array[col].value = False # Turn off column pin.
if len(keys_pressed) > 6: # Check for Rollover Error
for i in range(2,8):
report_array[i] = 0x01 # Add Rollover Error keycode*6 to report.
else:
for i in range(6):
report_array[i+2] = keys_pressed[i] if i < len(keys_pressed) else 0 # Add keycode to report.
keeb.send_report(struct.pack("8B",*report_array))
report_array = [0x00] * 8
keys_pressed = []
@so1velofahrer
Copy link

Hey man, I was asking earlier if you could provide me with a wiring diagram on Thingiverse...:) I hope you'll be happy to know that I'm progressing with my build...

I thought that I'll prepare the Pico before I'm soldering it to the keyboard but i ran into trouble flashing that damn thing :(

Somehow I think I have to add Modules to use as libraries...
import board
import digitalio
import struct
import usb_hid

the "managing packages" tool for Thonny did help me somewhat but I'm not sure which libraries to install.

Help would be much appreciated.

Sidenote, I'm using a waveshare RP2040-plus. So far I'm pretty sure it doesn't make a difference to an original Raspberry pi pico.

@wulfboy-95
Copy link
Author

wulfboy-95 commented Apr 5, 2022

Hey man, I was asking earlier if you could provide me with a wiring diagram on Thingiverse...:) I hope you'll be happy to know that I'm progressing with my build...

I thought that I'll prepare the Pico before I'm soldering it to the keyboard but i ran into trouble flashing that damn thing :(

Somehow I think I have to add Modules to use as libraries... import board import digitalio import struct import usb_hid

the "managing packages" tool for Thonny did help me somewhat but I'm not sure which libraries to install.

Help would be much appreciated.

Sidenote, I'm using a waveshare RP2040-plus. So far I'm pretty sure it doesn't make a difference to an original Raspberry pi pico.

Hi so1velofahrer,

You'll have to flash CircuitPython onto the board and download the remaining libraries. After downloading the libraries, extract the zip, then copy the contents of adafruit-circuitpython-x.x-mpy-yyyymmdd/lib/adafruit_hid/ into your board at [Insert CIRCUITPYTHON directory here]/lib/adafruit_hid/.

There is a separate CircuitPython image for the WaveShare RP2040-zero but I'm not sure if that would work.

@so1velofahrer
Copy link

UUUUUUUH! what a lovely Keyboard!

i finished earlier today, it totally worked.... Thanks again for helping me. your answering really quick man.

@wulfboy-95 you're the man! You'll have to change that name:)I really apreciate it. I'm on Twitter @so1velofahrer if you want to reach out and send me a private message. I'd like to show my appreciation and send you a bit of money or something... 

@blakedavenport9
Copy link

blakedavenport9 commented Jul 23, 2022

Hi, I'm completely new to circuitpython so this is probably a very obvious question, but I'd like to use this code on a project I'm working on -- The keyboard (effectively) I'm making has an extra row (6) and one fewer column (14)

I have adjusted the matrix, making a new bracketed section for the additional row, and removing the keycodes I don't need in each row. I have also added board.GP5 to input pins and removed board.GP28 from the output pins.

I'm getting this error when I try to run the code though...

IndexError: list index out of range -- The error is in line 89. My board does not have a shift key or any other modifier so maybe this is the issue?

I don't really understand how the bottom section of the code works though so I probably didn't change something necessary in one of the range commands.

Any help on what to change to make it work would be super helpful!

Thanks so much

@wulfboy-95
Copy link
Author

Hi, I'm completely new to circuitpython so this is probably a very obvious question, but I'd like to use this code on a project I'm working on -- The keyboard (effectively) I'm making has an extra row (6) and one fewer column (14)

I have adjusted the matrix, making a new bracketed section for the additional row, and removing the keycodes I don't need in each row. I have also added board.GP5 to input pins and removed board.GP28 from the output pins.

I'm getting this error when I try to run the code though...

IndexError: list index out of range -- The error is in line 89. My board does not have a shift key or any other modifier so maybe this is the issue?

I don't really understand how the bottom section of the code works though so I probably didn't change something necessary in one of the range commands.

Any help on what to change to make it work would be super helpful!

Thanks so much

Hi, I'd be happy to help. Can you share the code with me?

@blakedavenport9
Copy link

blakedavenport9 commented Jul 24, 2022

That would be amazing! Thank you! I'm making an oversized keyboard for my grandad who has shaky hands. The next step is to program some buttons to be hotkeys to his favorite websites/applicaitons like facebook and the news. Need to figure out how to get the keycodes to work first though. I'll post it here - let me know if there is a better way to share it. Like I said, total noob.

Thank you again!

# Copyright wulfboy_95 2021, All Rights Reserved.

# Copying and distribution of this file, with or without modification,
# are permitted in any medium without royalty provided the copyright
# notice and this notice are preserved.  This file is offered as-is,
# without any warranty.

import board
import digitalio
import struct
import usb_hid

#from adafruit_hid.keyboard import Keyboard
from adafruit_hid.keycode import Keycode

# Create and init keyboard USB HID device.
keeb = None
for dev in list(usb_hid.devices):
    if ((dev.usage == 0x06) and
        (dev.usage_page == 0x01) and
        hasattr(dev, "send_report")):
        keeb = dev
if keeb == None:
    raise Exception("Device cannot be found")

# 2D list to convert keyboard matrix to keycode - SKIP ROW 1 FOR NOW - THOSE WILL BE THE HOTKEYS
matrix = [
    [
        Keycode.ESCAPE, Keycode.ONE, Keycode.TWO, Keycode.THREE, Keycode.FOUR,
        Keycode.FIVE, Keycode.SIX, Keycode.SEVEN, Keycode.EIGHT, Keycode.NINE,
        Keycode.ZERO, Keycode.MINUS, Keycode.EQUALS, Keycode.BACKSPACE
    ],
    [
        Keycode.ONE, Keycode.TWO, Keycode.THREE, Keycode.FOUR, Keycode.FIVE, Keycode.SIX,
        Keycode.SEVEN, Keycode.EIGHT, Keycode.NINE, Keycode.ZERO
    ],
    [
        Keycode.Q, Keycode.W, Keycode.E, Keycode.R, Keycode.T,
        Keycode.Y, Keycode.U, Keycode.I, Keycode.O, Keycode.P
    ],
    [
        Keycode.CAPS_LOCK, Keycode.A, Keycode.S, Keycode.D, Keycode.F,
        Keycode.G, Keycode.H, Keycode.J, Keycode.K, Keycode.L
    ],
    [
        Keycode.QUOTE, Keycode.Z, Keycode.X, Keycode.C,
        Keycode.V, Keycode.B, Keycode.N, Keycode.M, Keycode.COMMA,
        Keycode.UP_ARROW, Keycode.ENTER
    ],
    [
        Keycode.COMMA, Keycode.PERIOD, Keycode.FORWARD_SLASH,
        Keycode.TAB, Keycode.SPACEBAR, Keycode.SPACEBAR, Keycode.BACKSPACE, Keycode.LEFT_ARROW,
        Keycode.DOWN_ARROW, Keycode.RIGHT_ARROW
    ],
] # matrix[row][col]

input_pins = (board.GP5,board.GP6, board.GP7, board.GP8, board.GP9,board.GP10) # 6 input rows - ** added one input pin - board.gp5

input_pin_array = [] # DigitalIO array for inputs.

output_pins = (
    board.GP11, board.GP12, board.GP13, board.GP14, board.GP15,
    board.GP16, board.GP17, board.GP18, board.GP19, board.GP20,
    board.GP21, board.GP22, board.GP26, board.GP27
) # 14 output columns - ** DELETED GP28

output_pin_array = [] # DigitalIO array for outputs.

# Initialise DigitalIO pins.
for pin in input_pins:
    key_pin = digitalio.DigitalInOut(pin)
    key_pin.direction = digitalio.Direction.INPUT
    key_pin.pull = digitalio.Pull.DOWN
    input_pin_array.append(key_pin)

for pin in output_pins:
    key_pin = digitalio.DigitalInOut(pin)
    key_pin.direction = digitalio.Direction.OUTPUT
    key_pin.drive_mode = digitalio.DriveMode.PUSH_PULL
    output_pin_array.append(key_pin)
           
keys_pressed = [] # list for possible n-key rollover upgrade in the future.
report_array = [0x00] * 8

while True:
    for col in range(len(output_pin_array)):
        output_pin_array[col].value = True # Turn on column pin.
        for row in range(len(input_pin_array)):
            if (matrix[row][col] >= 0xE0) and (input_pin_array[row].value): # Check if modifier is pressed
                report_array[0] |= Keycode.modifier_bit(matrix[row][col]) # Add modifier bit to report.
            elif input_pin_array[row].value: # Check if key is pressed.
                keys_pressed.append(matrix[row][col])
        output_pin_array[col].value = False # Turn off column pin.
    if len(keys_pressed) > 6: # Check for Rollover Error
        for i in range(2,8):
            report_array[i] = 0x01 # Add Rollover Error keycode*6 to report.
    else:
        for i in range(6):
            report_array[i+2] = keys_pressed[i] if i < len(keys_pressed) else 0 # Add keycode to report.
    keeb.send_report(struct.pack("8B",*report_array))
    report_array = [0x00] * 8
    keys_pressed = []

@wulfboy-95
Copy link
Author

That would be amazing! Thank you! I'm making an oversized keyboard for my grandad who has shaky hands. The next step is to program some buttons to be hotkeys to his favorite websites/applicaitons like facebook and the news. Need to figure out how to get the keycodes to work first though. I'll post it here - let me know if there is a better way to share it. Like I said, total noob.

Thank you again!

I've just looked at your code. The problem appears to be caused by having a lower number of elements in some rows within the matrix list than the number of columns. You should pad these rows with placeholder keycodes.

@blakedavenport9
Copy link

Very helpful, thank you! I have adjusted the code and I'm no longer getting an error when I run it. I'll try it out with my keyboard when I get home tonight.

Do you have any ideas on how to use keypresses in the first row as hotkeys to open websites in a browser? I was thinking I could just use webbrowser, but it seems like that's not included in the circuitpython library.

No pressure of course, but if you had any thoughts, they would be greatly appreciated.

Thanks again!

@dhalbert
Copy link

dhalbert commented Jul 25, 2022

@wulfboy-95 The keypad.KeyMatrix module added in recent versions of CircuitPython does matrix scanning, which you are doing "by hand" now. See https://learn.adafruit.com/key-pad-matrix-scanning-in-circuitpython/keymatrix. You'll want to read the whole guide for background.

(Referred here by a mention of your code in https://forums.adafruit.com/viewtopic.php?f=60&t=193075&p=934045#p934029)

@wulfboy-95
Copy link
Author

@dhalbert Thanks! I'll check it out.

@wulfboy-95
Copy link
Author

I've written a new version of this code which uses the keypad.KeyMatrix module and has fewer lines of code. Make sure you've flashed CircuitPython 7.x.x (or newer) on your Pico before using it.

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