Arduino Mega

/*

Sketch to turn IH Galanti into a VPO

==========================

This sketch scans the great and swell keyboards. The pedal is handled by a separate Arduino Uno

and the Midi output is merged by the Arduino Mega. A second Arduino was used because of excessive

crosstalk on the common data bus.

The great is scanned and output on Channel 2 (1)

The swell is scanned and output on Channel 3(2)

The organ switches are arranged in a 8x8 matrix and are diode isolated.

Great columns are connected to pins 23 (low "octave"), 25, 27, 29, 31, 33, 35, 37.

Swell columns are connected to pins 39 (low "octave"), 41, 43, 45, 47, 49, 51, 53.

Scanning is accomplished by driving these pins low (negative logic).

Input is sampled via pins 38 (C), 40 (C#), 42 (D), 44, 46, 48, 50, 52.

These inputs are normally kept high by using internal pullup resistors.

Great expression pedal input is read through analog input A5.

Swell expression pedal input is read through analog input A2.

Equipment: Arduino Mega with MIDI Shield

created 2019 JAN 6

modified 2019 JAN 26

by John Coenraads

*/

// Declarations==========================================

//Counters (old Fortran habit)

int i, j, k;

int colCount, rowCount; //matrix column (output:drive LOW) and row (input:normally HIGH) count

byte inputBit; //input data bit

byte noteNumber; //noteNumber for keyboard scan, low C = 36

const byte debounceCount = 6; //Note ON if count = 6, OFF if count = 0

byte debounceGreatArray [110]; //holds debounce count for each great keyswitch

byte debounceSwellArray [110]; //holds debounce count for each swell keyswitch

byte noteOnGreatArray [110]; //tracks which notes are turned on Great

byte noteOnSwellArray [110]; //tracks which notes are turned on Swell

//Receive variables

byte noteStatusRx;

byte noteNumberRx;

byte noteVelocityRx;

//Flag to keep track of last valid status (0x90,0x80)

byte statusBuffer = 0;

int oldGreatExpression = 0, newGreatExpression = 0; // Great expression controller values

int oldSwellExpression = 0, newSwellExpression = 0; // Swell expression controller values

void setup()

//Initialize =========================================================

{

delay (2000); // 2 second delay to let inputs stabilize

// Set MIDI baud rate:

Serial.begin(31250);

//Clear serial input buffer

while (Serial.available() > 0)

{Serial.read();}

//Initialize great lines for output (normally high, drive low)

for (i = 23; i < 38; i += 2)

{

pinMode (i, OUTPUT);

digitalWrite (i, HIGH);

}

//Initialize swell lines for output (normally high, drive low)

for (i = 39; i < 54; i += 2)

{

pinMode (i, OUTPUT);

digitalWrite (i, HIGH);

}

//Initialize for input (default). Normally high (via internal pullups)

for (i = 38; i < 53; i += 2)

{

pinMode (i, INPUT_PULLUP);

}

//Initialize debounce count array to zero

for (i = 0; i < 110; i++)

{

debounceGreatArray[i] = 0;

debounceSwellArray[i] = 0;

}

//Initialize noteOn arrays to zero (note off) (false)

for (i = 0; i < 110; i++)

{

noteOnGreatArray [i] = 0;

noteOnSwellArray [i] = 0;

}

}

//Main Loop ===========================================================

void loop()

{

scanGreat();

scanSwell();

scanGreatExpressionPedal();

scanSwellExpressionPedal();

if (Serial.available ())

{processSerial();}

}

//Scan great input, convert to MIDI and output via port 0, channel 2 (1) ******************Start Great

void scanGreat ()

{

noteNumber = 36; //start at low C

for (colCount = 23; colCount < 38; colCount += 2)

{

digitalWrite (colCount, LOW);

for (rowCount = 38; rowCount < 53; rowCount += 2)

{

inputBit = digitalRead(rowCount); //read bit

if (inputBit == HIGH) //bit HIGH, switch open

{

turnGreatNoteOFF();

}

else //bit LOW, switch closed

{

turnGreatNoteON();

}

noteNumber = noteNumber + 1; //move on to next note

}

digitalWrite (colCount, HIGH);

}

}

//Turn note on. Debouncing is achieved by requiring that several turnNoteON requests

//are received before sending out noteOn MIDI message

void turnGreatNoteON ()

{

if (debounceGreatArray[noteNumber] < debounceCount)

{

debounceGreatArray [noteNumber] = debounceGreatArray [noteNumber] + 1;

if ((debounceGreatArray[noteNumber] == debounceCount) && ( !noteOnGreatArray[noteNumber]))

{

Serial.write (0x91); //note ON, channel 2,

Serial.write (noteNumber);

Serial.write (0x7f); //medium velocity

noteOnGreatArray[noteNumber] = 1; //note now ON

}

}

}

//Turn note off. Debouncing is achieved by requiring that several turnNoteOFF requests

//are received before sending out noteOff MIDI message

void turnGreatNoteOFF ()

{

if (debounceGreatArray[noteNumber] > 0)

{

debounceGreatArray [noteNumber] = debounceGreatArray [noteNumber] - 1;

if ((debounceGreatArray[noteNumber] == 0) && (noteOnGreatArray[noteNumber]))

{

Serial.write (0x91); //note ON, channel 2,

Serial.write (noteNumber);

Serial.write (0); //zero velocity = turn OFF note

noteOnGreatArray[noteNumber] = 0; //note now OFF

}

}

} //*******************************************************End Great

//Scan swell + controls input, convert to MIDI and output via port 0, channel 3 (2) ******************Start Swell

void scanSwell ()

{

noteNumber = 36; //start at low C

for (colCount = 39; colCount < 54; colCount += 2)

{

digitalWrite (colCount, LOW);

for (rowCount = 38; rowCount < 53; rowCount += 2)

{

inputBit = digitalRead(rowCount); //read bit

if (inputBit == HIGH) //bit HIGH, switch open

{

turnSwellNoteOFF();

}

else //bit LOW, switch closed

{

turnSwellNoteON();

}

noteNumber = noteNumber + 1; //move on to next note

}

digitalWrite (colCount, HIGH);

}

}

//Turn note on. Debouncing is achieved by requiring that several turnNoteON requests

//are received before sending out noteOn MIDI message

void turnSwellNoteON ()

{

if (debounceSwellArray[noteNumber] < debounceCount)

{

debounceSwellArray [noteNumber] = debounceSwellArray [noteNumber] + 1;

if ((debounceSwellArray[noteNumber] == debounceCount) && ( !noteOnSwellArray [noteNumber]))

{

Serial.write (0x92); //note ON, channel 3,

Serial.write (noteNumber);

Serial.write (0x7f); //medium velocity

noteOnSwellArray [noteNumber] = 1; //note now ON

}

}

}

//Turn note off. Debouncing is achieved by requiring that several turnNoteOFF requests

//are received before sending out noteOff MIDI message

void turnSwellNoteOFF ()

{

if (debounceSwellArray[noteNumber] > 0)

{

debounceSwellArray [noteNumber] = debounceSwellArray [noteNumber] - 1;

if ((debounceSwellArray[noteNumber] == 0) && (noteOnSwellArray [noteNumber]))

{

Serial.write (0x92); //note ON, channel 3,

Serial.write (noteNumber);

Serial.write (0); //zero velocity = turn OFF note

noteOnSwellArray [noteNumber] = 0; //note now OFF

}

}

} //*******************************************************End Swell

//Voltage readings 2.06 volts (closed) to 3.79 (open) are read through analog input A5 yielding outputs

//of 421 to 775. A straight line was fitted and scaled to yield integers from 4 to 16 so as to achieve

//12 discrete steps to minimize the number of changes. If a change in output has occurred, this number

//is scaled up by 8 to yield controller values of 32 to 127.

void scanGreatExpressionPedal()

{

newGreatExpression = analogRead (A5); //421 to 775

newGreatExpression = 0.0335 * newGreatExpression - 10; //4 to 16

if (oldGreatExpression != newGreatExpression)

{ oldGreatExpression = newGreatExpression;

newGreatExpression = newGreatExpression * 8; //32 to 127

if (newGreatExpression > 127) {

newGreatExpression = 127;

}

Serial.write (0xB3); //controller

Serial.write (0); // zero

Serial.write (newGreatExpression);

}

}

//Voltage readings 2.50 volts (closed) to 4.13 (open) are read through analog input A2 yielding outputs

//of 512 to 845. A straight line was fitted and scaled to yield integers from 4 to 16 so as to achieve

//12 discrete steps to minimize the number of changes. If a change in output has occurred, this number

//is scaled up by 8 to yield controller values of 32 to 127.

void scanSwellExpressionPedal()

{

newSwellExpression = analogRead (A2); //421 to 775

newSwellExpression = 0.036 * newSwellExpression - 14; //4 to 16

if (oldSwellExpression != newSwellExpression)

{ oldSwellExpression = newSwellExpression;

newSwellExpression = newSwellExpression * 8; //32 to 127

if (newSwellExpression > 127) {

newSwellExpression = 127;

}

Serial.write (0xB3); //controller

Serial.write (1); // one

Serial.write (newSwellExpression);

}

}

//Process data received for serial port 0 (from Arduino Uno: Pedal/Pistons): status, note number, velocity

void processSerial ()

{

noteStatusRx = Serial.read ();

if (noteStatusRx > 0x7F) //is status byte

{

if ((noteStatusRx == 0x90) || //channel 1, note ON

(noteStatusRx == 0x80)) //channel 1, note OFF

{

statusBuffer = noteStatusRx;

while (!Serial.available()) {} //wait for serial data, port 0

noteNumberRx = Serial.read ();

while (!Serial.available()) {} //wait for serial data, port 0

noteVelocityRx = Serial.read ();

if (noteStatusRx == 0x80) { noteVelocityRx = 0;}

Serial.write (0x90); //output on channel 1, port 0

Serial.write (noteNumberRx);

Serial.write (noteVelocityRx);

}

else //not 0x90 or 0x80

{

statusBuffer = 0;

}

}

else //is data byte

{

if (statusBuffer != 0)

{

noteNumberRx = noteStatusRx;

noteStatusRx = statusBuffer;

while (!Serial.available()) {} //wait for serial data

noteVelocityRx = Serial.read ();

if (noteStatusRx == 0x80) { noteVelocityRx = 0;}

Serial.write (0x90); //output on channel 1, port 0

Serial.write (noteNumberRx);

Serial.write (noteVelocityRx);

}

}

}

void trace (byte info)

{

Serial.write (0xF3);

Serial.write (info);

}