// Scan Tipple Great Manual Thumb Pistons with Arduino Leonardo board // Using MIDIUSB library v.1.0.5 from Gary Grewal (install from the Arduino IDE Library Manaager) // John Harvey 25 November 2021 // www.johnharvey.uk // See these for more information: // www.arduino.cc/en/Reference/MIDIUSB // www.arduino.cc/en/Tutorial/MidiDevice // github.com/arduino-libraries/MIDIUSB // github.com/arduino/tutorials/blob/master/ArduinoZeroMidi/ArduinoZeroMidi.ino #include // Arduino input pins to read piston switch state const byte PistonSET = 0; const byte Piston1 = 1; const byte Piston2 = 2; const byte Piston3 = 3; const byte Piston4 = 4; const byte Piston5 = 5; const byte Piston6 = 6; const byte Piston7 = 7; const byte Piston8 = 8; const byte PistonMinus = 9; const byte PistonPlus = 10; const byte PistonCONT = 11; const byte PistonGENCAN = 12; const byte ScopeSync = A0; // Trigger oscilloscope at start of loop // MIDI channels allocated as follows: // Swell Channel 01 (0x00) // Great Channel 02 (0x01) // Pedals Channel 03 (0x02) // Thumb Pistons Channel 04 (0x03) // Swell Pedal Channel 05 (0x04) const byte MidiChannel = 4; // The channel assigned to this keyboard; channels are zero-based in code, so channel 1 is 0 in code etc. // Remember piston state between scans, true = pressed, false = released boolean PistonState[13]; // There are 13 thumb pistons (zero-based addressed as 0-12) void setup() { pinMode (PistonSET, INPUT_PULLUP); pinMode (Piston1, INPUT_PULLUP); pinMode (Piston2, INPUT_PULLUP); pinMode (Piston3, INPUT_PULLUP); pinMode (Piston4, INPUT_PULLUP); pinMode (Piston5, INPUT_PULLUP); pinMode (Piston6, INPUT_PULLUP); pinMode (Piston7, INPUT_PULLUP); pinMode (Piston8, INPUT_PULLUP); pinMode (PistonMinus, INPUT_PULLUP); pinMode (PistonPlus, INPUT_PULLUP); pinMode (PistonCONT, INPUT_PULLUP); pinMode (PistonGENCAN, INPUT_PULLUP); pinMode (A0, OUTPUT); // Scope sync pulse output pinMode (A1, OUTPUT); // Set unused analog inputs as outputs so that stray noise does not trigger ADCs pinMode (A2, OUTPUT); pinMode (A3, OUTPUT); pinMode (A4, OUTPUT); pinMode (A5, OUTPUT); pinMode(LED_BUILTIN, OUTPUT); // Pin 13 is LED_BUILTIN, use for SET piston visual indication digitalWrite(LED_BUILTIN, LOW); // Set LED to off pinMode (ScopeSync, OUTPUT); digitalWrite(ScopeSync, LOW); for (byte PistonNumber = 0; PistonNumber <= 12; PistonNumber ++) { PistonState[PistonNumber] = false; // All pistons released } } void loop() { digitalWrite(ScopeSync, HIGH); // Generate oscilloscope sync pulse at start of each scan for test purposes delayMicroseconds(50); digitalWrite(ScopeSync, LOW); delay(1); byte PistonNumber; boolean PreviousPistonState; boolean CurrentPistonState; for (PistonNumber = 0; PistonNumber <= 12; PistonNumber ++) { PreviousPistonState = PistonState[PistonNumber]; CurrentPistonState = !digitalRead(PistonNumber); // Low = piston pressed, so invert // Note that "PistonNumber + 0x1" produces an int in compiler so reconvert to byte value otherwise compiler outputs a narrowing conversion warning if ((PreviousPistonState == false) && (CurrentPistonState == true)) { // Piston has just been pressed PistonState[PistonNumber] = true; midiEventPacket_t noteOn = {0x09, byte(MidiChannel - 1 + 0x90), byte(PistonNumber + 0x1), 0x40}; // Generate MIDI codes 1-13 MidiUSB.sendMIDI(noteOn); MidiUSB.flush(); if (PistonNumber == 0) { digitalWrite(LED_BUILTIN, HIGH); // Turn Arduino onboard LED on (Leftmost piston visual indication) } } if ((PreviousPistonState == true) && (CurrentPistonState == false)) { // Piston has just been released PistonState[PistonNumber] = false; midiEventPacket_t noteOff = {0x08, byte(MidiChannel - 1 + 0x80), byte(PistonNumber + 0x1), 0x40}; MidiUSB.sendMIDI(noteOff); MidiUSB.flush(); if (PistonNumber == 0) { digitalWrite(LED_BUILTIN, LOW); // Turn Arduino onboard LED off (Leftmost piston visual indication) } } } delay(1); }