Skip to content

Instantly share code, notes, and snippets.

@skutov
Last active August 28, 2020 13:27
Show Gist options
  • Save skutov/ed6c3866554eb11098aa3277916a602a to your computer and use it in GitHub Desktop.
Save skutov/ed6c3866554eb11098aa3277916a602a to your computer and use it in GitHub Desktop.
Qlab Button triggers using arduino
//#include "Keyboard.h"
#include "MIDIUSB.h"
// At the moment, button 9 is being used as an output to control a relay via MIDI
const int buttonPins[10] = {3, 4, 5, 6, 7, 9, 8, 10, 11, 12}; // input pins for pushbuttons
const char buttonKeys[10] = {'1', '2', '3', '4', '5', '6', '7', '8', '9', '0'};
int buttonCurrentStates[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int buttonPrevStates[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
unsigned long lastDebounceTime[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
const unsigned long debounceDelay = 50; // the debounce time; increase if the output flickers
// MIDI commands
// First parameter is the event type (0x09 = note on, 0x08 = note off).
// Second parameter is note-on/note-off, combined with the channel.
// Channel can be anything between 0-15. Typically reported to the user as 1-16.
// Third parameter is the note number (48 = middle C).
// Fourth parameter is the velocity (64 = normal, 127 = fastest).
void noteOn(byte channel, byte pitch, byte velocity) {
midiEventPacket_t noteOn = {0x09, 0x90 | channel, pitch, velocity};
MidiUSB.sendMIDI(noteOn);
MidiUSB.flush();
}
void noteOff(byte channel, byte pitch, byte velocity) {
midiEventPacket_t noteOff = {0x08, 0x80 | channel, pitch, velocity};
MidiUSB.sendMIDI(noteOff);
MidiUSB.flush();
}
// First parameter is the event type (0x0B = control change).
// Second parameter is the event type, combined with the channel.
// Third parameter is the control number number (0-119).
// Fourth parameter is the control value (0-127).
void controlChange(byte channel, byte control, byte value) {
midiEventPacket_t event = {0x0B, 0xB0 | channel, control, value};
MidiUSB.sendMIDI(event);
MidiUSB.flush();
}
void setup() {
// initialise serial
Serial.begin(9600);
// initialise GPIO pins
for (int i = 0; i < 9; i++)
{
pinMode(buttonPins[i], INPUT_PULLUP);
}
pinMode(buttonPins[9], OUTPUT);
digitalWrite(buttonPins[9], HIGH);
Serial.println("Started, listening for MIDI");
}
void loop() {
handleMidi();
// Check NO inputs
for (int i = 0; i < 9; i++)
{
int reading = digitalRead(buttonPins[i]);
if (reading != buttonPrevStates[i])
{
// reset the debouncing timer
lastDebounceTime[i] = millis();
}
if ((millis() - lastDebounceTime[i]) > debounceDelay) {
// whatever the reading is at, it's been there for longer than the debounce
// delay, so take it as the actual current state:
// if the button state has changed:
if (reading != buttonCurrentStates[i])
{
if (buttonCurrentStates[i])
{
// MIDI Command
noteOn(0,i,64);
Serial.print("Note ");
Serial.print(i);
Serial.println(" On");
// Keyboard command
// Keyboard.write(buttonKeys[i]);
}
else
{
// Button released
noteOff(0,i,64);
Serial.print("Note ");
Serial.print(i);
Serial.println(" Off");
}
buttonCurrentStates[i] = reading;
}
}
buttonPrevStates[i] = reading;
}
}
void handleMidi() {
midiEventPacket_t rx ;
do {
rx = MidiUSB.read();
switch (rx.header) {
case 0:
break; //No pending events
case 0x9: // Note on message
if(((rx.byte1 & 0xF) == 0x01) && //channel
(rx.byte2 == 48) && //pitch
(rx.byte3 > 0)) //velocity
{
}
break;
case 0x8: // Note off message
break;
case 0xB: // CC message
switch (rx.byte1 & 0x0F) // channel
{
case 0x0: // Channel 1
if (rx.byte2 == 1) // Control 1
{
if(rx.byte3)
{
digitalWrite(buttonPins[9], HIGH);
Serial.println("CC 1 On");
}
else
{
digitalWrite(buttonPins[9], LOW);
Serial.println("CC 1 Off");
}
}
break;
}
break;
default:
Serial.print("Unhandled MIDI message: ");
Serial.print(rx.header, HEX);
Serial.print("-");
Serial.print(rx.byte1, HEX);
Serial.print("-");
Serial.print(rx.byte2, HEX);
Serial.print("-");
Serial.println(rx.byte3, HEX);
}
} while (rx.header != 0);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment