Skip to content

Instantly share code, notes, and snippets.

@sandyjmacdonald
Created January 21, 2021 22:27
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save sandyjmacdonald/804dc737e7cb798d1b4fa34adc87e2d4 to your computer and use it in GitHub Desktop.
Save sandyjmacdonald/804dc737e7cb798d1b4fa34adc87e2d4 to your computer and use it in GitHub Desktop.
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
@joeynuggetz
Copy link

joeynuggetz commented Nov 2, 2021

I'm trying to run this code and looking at my midi monitor the board is streaming midi note off events continuously. . Is this normal? Seems like the midi monitor is stuck on in Ableton. My normal midi controllers dont appear to be streaming not off events when looking at them via a midi monitor. Is this just a limitation with an implementation or am I doing something wrong?

Also, in the posted code, shouldn't
midi.send(NoteOff(32 + i, 0)) # If not held, send note off
Be set to 36 as well? Change the code so both are 36 and seems to be acting better. Note offs still streaming but now held notes appear to work.

@sandyjmacdonald
Copy link
Author

@joeynuggetz You’re quite correct about the note numbers, I think, although I don’t have mine to hand right now to test it out.

As to why it’s sending notes constantly, could one of the buttons be pushed down, and hence stuck on, by the retaining plate? Can you remove that plate (if it’s fitted) and try again?

@joeynuggetz
Copy link

joeynuggetz commented Nov 4, 2021

@joeynuggetz You’re quite correct about the note numbers, I think, although I don’t have mine to hand right now to test it out.

As to why it’s sending notes constantly, could one of the buttons be pushed down, and hence stuck on, by the retaining plate? Can you remove that plate (if it’s fitted) and try again?

I dont believe its an issue with stuck keys. I've gotten the code to work correctly and midi appears to be functioning ok except that the midi indicator stays lit. If I press and release each key, samples/notes play and then release/sustain properly so not sure what's going on.
image

@sandyjmacdonald
Copy link
Author

Hey @joeynuggetz! Finally had a chance to plug my keypad in and try this out. I've realised what's causing this. It's because if the keys aren't pressed then logic dictates that they are released, so the else clause sends out the note off messages, and this will happen continuously. It's not really a problem, I think, as the MIDI bus should be capable of handling it.

@helgevh
Copy link

helgevh commented Dec 26, 2021

Hi @sandyjmacdonald
I have tested your code with the modifications of @kenimaister and it does work fine with Ableton Live 11 lite. So I would like to thank you for your good work!
After reading your last post I deleted the NoteOff command in Line 76 and the program is still working fine with Ableton.
I also tested it with two other DAWs without any problems.

@drtimoschrader
Copy link

Hi @kenimaister thank you for your comment, it fixed my problem of the mirrored (inverted?) keys! Now I can enjoy using the keypad with Ableton without having to do some additional mental gymnastics! And also thank you again to @sandyjmacdonald for the code! It has gotten me into playing around with making some fun tracks and I have also gotten addtional MIDI equipment to do more fun stuff! You're all wonderful!

@CrippNipp
Copy link

Would anyone here have any info on how to add a few buttons and a slide pot to this code?
I made a small macropad (have not wired it yet) and I would like to add it in addition to the pico Rob keypad.
Any help would be super appreciated!!
IMG_4087

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