Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
RGB MIDI controller example for Pimoroni RGB Keypad for Raspberry Pi Pico
import time
import board
import busio
import usb_midi
import adafruit_midi
from adafruit_midi.note_off import NoteOff
from adafruit_midi.note_on import NoteOn
from adafruit_bus_device.i2c_device import I2CDevice
import adafruit_dotstar
from digitalio import DigitalInOut, Direction, Pull
# RGB MIDI controller example for Pimoroni RGB Keypad for Raspberry Pi Pico
# Prerequisites
#
# Requires Adafruit CircuitPython: https://learn.adafruit.com/getting-started-with-raspberry-pi-pico-circuitpython
#
# Also requires the following CircuitPython libs: adafruit_midi, adafruit_bus_device, adafruit_dotstar
# (drop them into the lib folder)
#
# Save this code in code.py on your Raspberry Pi Pico CIRCUITPY drive
# Pull CS pin low to enable level shifter
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)
# Set USB MIDI up on channel 0
midi = adafruit_midi.MIDI(midi_out=usb_midi.ports[1], out_channel=0)
# 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)
# List to store the button states
held = [0] * 16
# Keep reading button states, setting pixels, sending notes
while True:
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(16):
if not (1 << i) & b: # Pressed state
pixels[i] = colourwheel(i * 16) # Map pixel index to 0-255 range
if not held[i]:
midi.send(NoteOn(36 + i, 100)) # If not already held, then send note
held[i] = 1
else: # Released state
pixels[i] = (0, 0, 0) # Turn pixel off
midi.send(NoteOff(32 + i, 0)) # If not held, send note off
held[i] = 0 # Set held state to off
@timoschrader88

This comment has been minimized.

Copy link

@timoschrader88 timoschrader88 commented Mar 10, 2021

Hi, I have possibly the dumbest question for this: how do I make it make sound? It lights up my RGB keypad all nice and well but do I need to hook it up to specific software to attach sounds to the buttons? I am obviously very new to this, just playing around trying to learn about Python and how this keypad works. Any help is much appreciated, kind stranger!

@sandyjmacdonald

This comment has been minimized.

Copy link
Owner Author

@sandyjmacdonald sandyjmacdonald commented Mar 10, 2021

Hey @timoschrader88! You need to connect it to a MIDI device, like a hardware synth, or to software like Ableton that will accept the MIDI notes and play them on whatever instrument you select.

@timoschrader88

This comment has been minimized.

Copy link

@timoschrader88 timoschrader88 commented Mar 10, 2021

Oh, thank you so much for the quick reply! I have Ableton here and I can choose CircuitPython as input device, but it still won't work. It doesn't register key presses. I see there is a midi map but I can't seem to register key presses or edit them there either. What am I missing? I've attached a screenshot. I am trying to map the keys to the drum rack here.

ableton

@sandyjmacdonald

This comment has been minimized.

Copy link
Owner Author

@sandyjmacdonald sandyjmacdonald commented Mar 11, 2021

Can you see any MIDI notes being received in Ableton (a little yellow square should blink on the Ableton channel)?

@timoschrader88

This comment has been minimized.

Copy link

@timoschrader88 timoschrader88 commented Mar 11, 2021

No, I wasn't able to see the notes registering but when I tried again today, it all worked magically. I honestly don't know why. This time I only connected the keypad after opening Ableton but when I closed the program and opened again without disconnecting the keypad, it still worked. So I will just have to monitor myself how it will do in the future.

One other question I have is about the layout of keys. On my screenshot, the drum rack layout is inverted with the top row representing the bottom row on my keypad etc. Can I just switch out something in the code to reverse the layout so it displays accurately?

@sandyjmacdonald

This comment has been minimized.

Copy link
Owner Author

@sandyjmacdonald sandyjmacdonald commented Mar 11, 2021

I think (have not tested) you should be able to say midi.send(NoteOn(52 - i, 100)) and midi.send(NoteOff(52 - i, 0)) (lines 72 and 76) and that'll invert the notes played.

@timoschrader88

This comment has been minimized.

Copy link

@timoschrader88 timoschrader88 commented Mar 11, 2021

This makes it more strange. I guess I didn't mean inverted, I meant mirrored. In my screenshot, if I hit the key for Cowbell I actually play the Bassdrum and if I hit the key for Crash I actually play Rimshot. So Top left corner becomes bottom left corner. So the direction from left to right per row is normal but top row is switched with bottom row and second to top row switches with second to bottom row on the keypad. Very odd.

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