This might be way too much info, but here’s a condensed example of the code - it lights up LEDs on an 8x8 grid. The draw function takes a number of arrays as arguments - each containing a colour code and a list of LEDs to light - and it uses these to fill in a default array which is otherwise populated with blanks (represented by the number 12 here).

All of these values are then used to make and send a special SysEx message in the following format. This is taken from the programmer guide to the Launchpad Mini mk2 which describes the SysEx it expects as follows:

Rapid LED update

Hex version:

92h, Velocity 1, Velocity 2,

92h, Velocity 3, Velocity 4 …

Decimal version:

146, Velocity 1, Velocity 2,

146, Velocity 3, Velocity 4 …

Sending a MIDI channel 3 note-on message enters a special LED update mode. All eighty LEDs

may be set using only forty consecutive instructions, without having to send any key addresses.

Irrespective of the mapping chosen, this will update the 8x8 grid in left-to-right, top-to-bottom

order, then the eight scene launch buttons in top-to-bottom order, and finally the eight

Automap/Live buttons in left-to-right order (these are otherwise inaccessible using note-on

messages). Overflowing data will be ignored.

To leave the mode, send a standard messsage beginning with 80h, 90h, or B0h. Sending another

kind of message and then re-sending 92h will reset the cursor to the top left of the grid.

So my Sonic Pi draw function looks like this:

```
use_real_time
# Specify the device as a port for sending sysex
# (optional, but otherwise sysex sends to all midi ports)
set :lp, "2-_launchpad_mini_2"
set :rest_time, 0.25
# Define the frame-drawing function
define :draw do |*args|
# establish defaults - each LED defaults to blank
norm=[12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12,
12,12,12,12,12,12,12,12,]
# Sort through the input - each array given starts with a number for the colour
# and is followed by a list of LEDs to set to that colour
args.each do |stuff|
# take each number and map it
stuff.drop(1).each do |led|
# add it to the right variable for the upcoming sysex
norm[led] = stuff[0]
end
end
# send a single sysex with the image
midi_sysex 0xF0,146,12,12,
146,norm[0],norm[1],146,norm[2],norm[3],146,norm[4],norm[5],146,norm[6],norm[7],
146,norm[8],norm[9],146,norm[10],norm[11],146,norm[12],norm[13],146,norm[14],norm[15],
146,norm[16],norm[17],146,norm[18],norm[19],146,norm[20],norm[21],146,norm[22],norm[23],
146,norm[24],norm[25],146,norm[26],norm[27],146,norm[28],norm[29],146,norm[30],norm[31],
146,norm[32],norm[33],146,norm[34],norm[35],146,norm[36],norm[37],146,norm[38],norm[39],
146,norm[40],norm[41],146,norm[42],norm[43],146,norm[44],norm[45],146,norm[46],norm[47],
146,norm[48],norm[49],146,norm[50],norm[51],146,norm[52],norm[53],146,norm[54],norm[55],
146,norm[56],norm[57],146,norm[58],norm[59],146,norm[60],norm[61],146,norm[62],norm[63],
146,norm[64],norm[65],146,norm[66],norm[67],146,norm[68],norm[69],146,norm[70],norm[71],
146,norm[72],norm[73],146,norm[74],norm[75],146,norm[76],norm[77],146,norm[78],norm[79],
0x80,0x00,0xF7, port: get[:lp]
# wait a given period of time before the next frame of animation (usually 0.5 or so)
sleep get[:rest_time]
end
```

And it is called with little functions like this - a simple number of consecutive frames:

```
# Simple animation with a face
define :face do
draw [13, 17, 22], [28, 51, 52, 50, 53, 43, 44, 45, 42]
sleep 2
draw [13, 8, 13], [28, 44, 43, 42, 45, 52, 51, 53, 50]
sleep 1
draw [13, 10, 15], [28, 44, 43, 42, 52, 50, 51, 53, 45]
sleep 1
end
```

This works perfectly on my Windows system - with some very long, detailed, and fast animations. They update to the LEDs smoothly, without any problems or inconsistencies.

When I try the same code on the Raspberry Pi, the messages appear to be sending as normal but the display doesn’t light up at all. If I run it once or twice in a row, eventually a garbled version of one of the frames will appear - with the image shifted to the side like the coordinates are incorrect. It makes me think that the SysEx messages are either not getting through, only partially arriving, or being sent in some erratic way. I have also tried slowing things down dramatically, leaving seconds between each frame, and the results are the same.