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);
}