/*
Allen ADC Encoder with Four Expression Pedals
==============================================
This sketch is designed to scan three keyboards, the pedal, four expression pedals and 31 pistons.
If more inputs are needed, an Arduino running parallel scan code can be daisy chained.
Common lines daisy chain from Pedal, to Choir, to Great, to Swell. These 16 lines are sequentially brought LOW for scanning.
Referring to the schematic for Allen ADC organs:
Allen pins 7,8,9,10, ... 21, 22 map onto Arduino pins 23,25,27, ... 51,53
The following pins are then scanned to determine which keys are closed. LOW = switch closed.
Pedal: Output on channel 1
Allen pins 41,43,45,47 map onto Arduino pins 22,24,26,28
Choir: Output on channel 2
Allen pins 33,35,37,39 map onto Arduino pins 30,32,34,36
Great: Output on channel 3
Allen pins 26,28,30,32 map onto Arduino pins 38,40,42,44
Swell: Output on channel 4
Allen pins 25,27,29,31 map onto Arduino pins 46,48,50,52
Arduino pins 2 - 12, 14 - 21, 54 - 65, are piston inputs. Output on Ch. 5
Pistons are parallel wired and the switches share a common ground to the Arduino.
Arduino Pins 66, 67, 68, 69: are for 4 analog inputs (expression pedals). Output on channel5.
The controllerArray values will have to be edited to reflect the voltage range put out by the device.
Connect 15k (+/-) pot across Arduino's5V and Ground. Centre tap goes to pin 66, 67, 68 or 69. Input must never exceed 5V.
Note: ***** Active, but unused analog inputs, must be grounded to avoid spurious control messages *****
Alternatively, the calls to the expression pedal procedures can just be left commented out as in the code below.
Remove the comment (//) in front ot the procedure call in the main loop to activate an input.
Equipment: Arduino Mega with one MIDI shield mounted off the Arduino
Instead of a MIDI shield, two 220 ohm resistors plus a 5-pin DIN MIDI connector can also be used.
created 2024 OCT 24
modified 2024 OCT 24
by John Coenraads
*/
// Declarations==========================================
//Counters (old Fortran habit)
int i, j, k;
byte noteStatus;
byte noteNumber; // low C = 36
byte noteVelocity;
//Receive variables
byte noteStatusRx;
byte noteNumberRx;
byte noteVelocityRx;
const byte debounceCount = 4; //Note ON if count reaches decounceCount, OFF if count reaches 0
byte swellDebounceArray [100]; //holds debounce count for each Swell switch
byte greatDebounceArray [100]; //holds debounce count for each Great switch
byte pistonDebounceArray [100]; //holds debounce count for each Piston switch
byte pedalDebounceArray [100]; //holds debounce count for each Pedal switch
byte choirDebounceArray [100]; //holds debounce count for each Choir switch
int oldOldExpression1 = 0, oldExpression1 = 0, newExpression1 = 0; // Expression controller values for expression pedal 1
int oldOldExpression2 = 0, oldExpression2 = 0, newExpression2 = 0; // Expression controller values for expression pedal 2
int oldOldExpression3 = 0, oldExpression3 = 0, newExpression3 = 0; // Expression controller values for expression pedal 3
int oldOldExpression4 = 0, oldExpression4 = 0, newExpression4 = 0; // Expression controller values for expression pedal 4
// The following array represents the controller values output for analog device voltages
// in 0.15V increments ranging from 0 to 5V. Input must never exceed 5V.
// Array can be edited as desired, but must contain exactly 33 entries with values between 0 and 127 only.
byte controllerArray1 [33] = {1,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124 ,127};
byte controllerArray2 [33] = {1,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124 ,127};
byte controllerArray3 [33] = {1,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124 ,127};
byte controllerArray4 [33] = {1,4,8,12,16,20,24,28,32,36,40,44,48,52,56,60,64,68,72,76,80,84,88,92,96,100,104,108,112,116,120,124 ,127};
//Initialize =========================================================
void setup()
{
// Set MIDI baud rate:
Serial.begin(31250);
//Initialize output (normally high)
for (i = 23; i < 54; i+=2)
{
pinMode (i, OUTPUT);
digitalWrite (i, HIGH);
}
//Initialize input. Normally high (via internal pullups)
for (i = 22; i < 53; i+=2)
{ pinMode (i, INPUT_PULLUP);}
for (i = 2; i < 13; i++)
{ pinMode (i, INPUT_PULLUP);}
for (i = 14; i < 22; i++)
{ pinMode (i, INPUT_PULLUP);}
for (i = 54; i < 66; i++)
{ pinMode (i, INPUT_PULLUP);}
//Initialize debounce count arrays to zero
for (i= 0; i < 100; i++){
swellDebounceArray[i] = 0;
greatDebounceArray[i] = 0;
pistonDebounceArray[i] = 0;
pedalDebounceArray[i] = 0;
choirDebounceArray[i] = 0;
}
}
//Main Loop ===========================================================
void loop()
{
scanAll();
scanPistons();
//scanExpressionPedal1(); // Arduino pin 66
//scanExpressionPedal2(); // Arduino pin 67
//scanExpressionPedal3(); // Arduino pin 68
//scanExpressionPedal4(); // Arduino pin 69
processSerial ();
}
//Scan Pedal, Choir, Great, Swell keyboards, convert to MIDI and output via port 0, channels 1,2,3,4.
void scanAll ()
{
noteNumber = 36;
for (i = 23; i < 54; i+= 2)
{
digitalWrite (i, LOW);
if (digitalRead(22) == LOW) {turnONpedal ();} else {turnOFFpedal ();}
if (digitalRead(30) == LOW) {turnONchoir ();} else {turnOFFchoir ();}
if (digitalRead(38) == LOW) {turnONgreat ();} else {turnOFFgreat ();}
if (digitalRead(46) == LOW) {turnONswell ();} else {turnOFFswell ();}
noteNumber++;
if (digitalRead(24) == LOW) {turnONpedal ();} else {turnOFFpedal ();}
if (digitalRead(32) == LOW) {turnONchoir ();} else {turnOFFchoir ();}
if (digitalRead(40) == LOW) {turnONgreat ();} else {turnOFFgreat ();}
if (digitalRead(48) == LOW) {turnONswell ();} else {turnOFFswell ();}
noteNumber++;
if (digitalRead(26) == LOW) {turnONpedal ();} else {turnOFFpedal ();}
if (digitalRead(34) == LOW) {turnONchoir ();} else {turnOFFchoir ();}
if (digitalRead(42) == LOW) {turnONgreat ();} else {turnOFFgreat ();}
if (digitalRead(50) == LOW) {turnONswell ();} else {turnOFFswell ();}
noteNumber++;
if (digitalRead(28) == LOW) {turnONpedal ();} else {turnOFFpedal ();}
if (digitalRead(36) == LOW) {turnONchoir ();} else {turnOFFchoir ();}
if (digitalRead(44) == LOW) {turnONgreat ();} else {turnOFFgreat ();}
if (digitalRead(52) == LOW) {turnONswell ();} else {turnOFFswell ();}
noteNumber++;
digitalWrite (i, HIGH);
}
}
//MIDI ON message is sent only if note is not already ON.
void turnONswell ()
{
if (swellDebounceArray[noteNumber] == 0)
{
Serial.write (0x93); //note ON, channel 4
Serial.write (noteNumber);
Serial.write (0x7f); //medium velocity
swellDebounceArray[noteNumber] = debounceCount;
}
}
//MIDI OFF message is sent only if note is not already OFF.
void turnOFFswell ()
{
if (swellDebounceArray[noteNumber] == 1)
{
Serial.write (0x93); //note ON, channel 4
Serial.write (noteNumber);
Serial.write (0); //zero velocity = OFF
}
if (swellDebounceArray[noteNumber] > 0) {swellDebounceArray[noteNumber] -- ;}
}
//MIDI ON message is sent only if note is not already ON.
void turnONgreat ()
{
if (greatDebounceArray[noteNumber] == 0)
{
Serial.write (0x92); //note ON, channel 3
Serial.write (noteNumber);
Serial.write (0x7f); //medium velocity
greatDebounceArray[noteNumber] = debounceCount;
}
}
//MIDI OFF message is sent only if note is not already OFF.
void turnOFFgreat ()
{
if (greatDebounceArray[noteNumber] == 1)
{
Serial.write (0x92); //note ON, channel 3
Serial.write (noteNumber);
Serial.write (0); //zero velocity = OFF
}
if (greatDebounceArray[noteNumber] > 0) {greatDebounceArray[noteNumber] -- ;}
}
//MIDI ON message is sent only if note is not already ON.
void turnONpedal ()
{
if (pedalDebounceArray[noteNumber] == 0)
{
Serial.write (0x90); //note ON, channel 1
Serial.write (noteNumber);
Serial.write (0x7f); //medium velocity
pedalDebounceArray[noteNumber] = debounceCount;
}
}
//MIDI OFF message is sent only if note is not already OFF.
void turnOFFpedal ()
{
if (pedalDebounceArray[noteNumber] == 1)
{
Serial.write (0x90); //note ON, channel 1
Serial.write (noteNumber);
Serial.write (0); //zero velocity = OFF
}
if (pedalDebounceArray[noteNumber] > 0) {pedalDebounceArray[noteNumber] -- ;}
}
//MIDI ON message is sent only if note is not already ON.
void turnONchoir ()
{
if (choirDebounceArray[noteNumber] == 0)
{
Serial.write (0x91); //note ON, channel 2
Serial.write (noteNumber);
Serial.write (0x7f); //medium velocity
choirDebounceArray[noteNumber] = debounceCount;
}
}
//MIDI OFF message is sent only if note is not already OFF.
void turnOFFchoir ()
{
if (choirDebounceArray[noteNumber] == 1)
{
Serial.write (0x91); //note ON, channel 2
Serial.write (noteNumber);
Serial.write (0); //zero velocity = OFF
}
if (choirDebounceArray[noteNumber] > 0) {choirDebounceArray[noteNumber]--;}
}
//**********************************************************************************************
//Scan Pistons, convert to MIDI and output via port 0, channel 4.
//Piston feed is permanently wired to ground
void scanPistons ()
{
noteNumber = 36;
for (i = 2; i < 13; i++)
{ if (digitalRead(i) == LOW) {turnONpiston ();} else {turnOFFpiston ();}
noteNumber++; }
for (i = 14; i < 23; i++)
{ if (digitalRead(i) == LOW) {turnONpiston ();} else {turnOFFpiston ();}
noteNumber++; }
for (i = 54; i < 66; i++)
{ if (digitalRead(i) == LOW) {turnONpiston ();} else {turnOFFpiston ();}
noteNumber++; }
}
//MIDI ON message is sent only if note is not already ON.
void turnONpiston ()
{
if (pistonDebounceArray[noteNumber] == 0)
{
Serial.write (0x94); //note ON, channel 5
Serial.write (noteNumber);
Serial.write (0x7f); //medium velocity
pistonDebounceArray[noteNumber] = debounceCount;
}
}
//MIDI OFF message is sent only if note is not already OFF.
void turnOFFpiston ()
{
if (pistonDebounceArray[noteNumber] == 1)
{
Serial.write (0x94); //note ON, channel 5
Serial.write (noteNumber);
Serial.write (0); //zero velocity = OFF
}
if (pistonDebounceArray[noteNumber] > 0) {pistonDebounceArray[noteNumber] -- ;}
}
//Expression pedal input:
//Voltage inputs from 0 to 5V are converted to a 10 bit integer from 0 to 1023 by analog to digital converter.
//Adding 1 and dividing by 32, yields an integer from 0 to 32. This is used as an index into the controllerArray
//to select the controller value to be output.
void scanExpressionPedal1()
{
newExpression1 = analogRead (66); //0 to 1023
newExpression1 = (newExpression1 + 1) / 32; //0 to 32
if ((newExpression1 != oldExpression1) && (newExpression1 != oldOldExpression1)) // double check avoids jitter
{
oldOldExpression1 = oldExpression1;
oldExpression1 = newExpression1;
newExpression1 = controllerArray1 [newExpression1]; //extract controller value from array
if (newExpression1 > 127)
{
newExpression1 = 127; //correct out of range controller value
}
Serial.write (0xB3); //controller (channel 4)
Serial.write (0); //control number 1
Serial.write (newExpression1);
}
}
void scanExpressionPedal2()
{
newExpression2 = analogRead (67); //0 to 1023
newExpression2 = (newExpression2 + 1) / 32; //0 to 32
if ((newExpression2 != oldExpression2) && (newExpression2 != oldOldExpression2)) // double check avoids jitter
{
oldOldExpression2 = oldExpression2;
oldExpression2 = newExpression2;
newExpression2 = controllerArray2 [newExpression2]; //extract controller value from array
if (newExpression2 > 127)
{
newExpression2 = 127; //correct out of range controller value
}
Serial.write (0xB3); //controller (channel 1)
Serial.write (1); //control number 2
Serial.write (newExpression2);
}
}
void scanExpressionPedal3()
{
newExpression3 = analogRead (68); //0 to 1023
newExpression3 = (newExpression3 + 1) / 32; //0 to 32
if ((newExpression3 != oldExpression3) && (newExpression3 != oldOldExpression3)) // double check avoids jitter
{
oldOldExpression3 = oldExpression3;
oldExpression3 = newExpression3;
newExpression3 = controllerArray3 [newExpression3]; //extract controller value from array
if (newExpression3 > 127)
{
newExpression3 = 127; //correct out of range controller value
}
Serial.write (0xB3); //controller (channel 4)
Serial.write (2); //control number 3
Serial.write (newExpression3);
}
}
void scanExpressionPedal4()
{
newExpression4 = analogRead (69); //0 to 1023
newExpression4 = (newExpression4 + 1) / 32; //0 to 32
if ((newExpression4 != oldExpression4) && (newExpression4 != oldOldExpression4)) // double check avoids jitter
{
oldOldExpression4 = oldExpression4;
oldExpression4 = newExpression4;
newExpression4 = controllerArray4 [newExpression4]; //extract controller value from array
if (newExpression4 > 127)
{
newExpression4 = 127; //correct out of range controller value
}
Serial.write (0xB4); //controller (channel 5)
Serial.write (3); //control number 4
Serial.write (newExpression4);
}
}
//Process data received for serial port 0 (from preceding keyboard): status, note number, velocity
//Note: this bare bones version does not handle running status.
void processSerial ()
{
if (Serial.available())
{
noteStatusRx = Serial.read ();
if (noteStatusRx > 0x7F) //is status byte
{
while (!Serial.available()) {} //wait for serial data, port 0
noteNumberRx = Serial.read ();
while (!Serial.available()) {} //wait for serial data, port 0
noteVelocityRx = Serial.read ();
Serial.write (noteStatusRx + 5); //output on port 0, five channels up
Serial.write (noteNumberRx);
Serial.write (noteVelocityRx);
}
}
}