Skip to content

Instantly share code, notes, and snippets.

@tr00st
Forked from sandyjmacdonald/code.py
Last active February 3, 2021 00:20
Show Gist options
  • Save tr00st/d893abfa52213286673980768e76ff7f to your computer and use it in GitHub Desktop.
Save tr00st/d893abfa52213286673980768e76ff7f to your computer and use it in GitHub Desktop.
4 channel control / 8 note MIDI controller example for Pimoroni RGB Keypad for Raspberry Pi Pico
# Based on @sandyjmacdonald's original gist:
# https://gist.github.com/sandyjmacdonald/b465377dc11a8c83a8c40d1c9b990a90
import time
import board
import busio
import usb_hid
from adafruit_bus_device.i2c_device import I2CDevice
import adafruit_dotstar
from digitalio import DigitalInOut, Direction, Pull
import adafruit_midi
cs = DigitalInOut(board.GP17)
cs.direction = Direction.OUTPUT
cs.value = 0
# Set up APA102 pixels
num_pixels = 16
pixels = adafruit_dotstar.DotStar(board.GP18, board.GP19, num_pixels, brightness=0.1, auto_write=True)
# Set up I2C for IO expander (addr: 0x20)
i2c = busio.I2C(board.GP5, board.GP4)
device = I2CDevice(i2c, 0x20)
# Function to map 0-255 to position on colour wheel
def colourwheel(pos):
if pos < 0 or pos > 255:
return (0, 0, 0)
if pos < 85:
return (255 - pos * 3, pos * 3, 0)
if pos < 170:
pos -= 85
return (0, 255 - pos * 3, pos * 3)
pos -= 170
return (pos * 3, 0, 255 - pos * 3)
# Read button states from the I2C IO expander on the keypad
def read_button_states(x, y):
pressed = [0] * 16
with device:
# Read from IO expander, 2 bytes (8 bits) correspond to the 16 buttons
device.write(bytes([0x0]))
result = bytearray(2)
device.readinto(result)
b = result[0] | result[1] << 8
# Loop through the buttons
for i in range(x, y):
if not (1 << i) & b:
pressed[i] = 1
else:
pressed[i] = 0
return pressed
import usb_midi
from adafruit_midi.note_off import NoteOff
from adafruit_midi.note_on import NoteOn
from adafruit_midi.control_change import ControlChange
print("Midi test")
midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0)
print("Default output channel:", midi.out_channel + 1)
# List to store the button states
held = [-1] * 16
control_vals = [0]*4
def handle_volume(channel, up_key, down_key, pressed):
if pressed[up_key]:
pixels[up_key] = colourwheel(up_key * 16) # Map pixel index to 0-255 range
if not held[up_key]:
control_vals[channel] = min(control_vals[channel] + 5, 127)
midi.send(ControlChange(channel, control_vals[channel]))
held[up_key] = (held[up_key] + 1) % 10
else: # Released state
if held[up_key] >= 0:
pixels[up_key] = (0, 0, 0) # Turn pixel off
held[up_key] = -1 # Set held state to off
if pressed[down_key]:
pixels[down_key] = colourwheel(down_key * 16) # Map pixel index to 0-255 range
if not held[down_key]:
control_vals[channel] = max(control_vals[channel] - 5, 0)
midi.send(ControlChange(channel, control_vals[channel]))
held[down_key] = (held[down_key] + 1) % 10
else: # Released state
if held[down_key] >= 0:
pixels[down_key] = (0, 0, 0) # Turn pixel off
held[down_key] = -1 # Set held state to off
# Keep reading button states, setting pixels
while True:
pressed = read_button_states(0, 16)
handle_volume(0, 0, 4, pressed)
handle_volume(1, 1, 5, pressed)
handle_volume(2, 2, 6, pressed)
handle_volume(3, 3, 7, pressed)
for i in range(8,16):
if pressed[i]:
pixels[i] = colourwheel(i * 16) # Map pixel index to 0-255 range
if not held[i]:
midi.send(NoteOn(i, 127))
held[i] = (held[i] + 1) % 30
else: # Released state
if held[i] >= 0:
midi.send(NoteOff(i, 127))
pixels[i] = (0, 0, 0) # Turn pixel off
held[i] = -1 # Set held state to off
time.sleep(0.01)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment