-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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