Parallel Scanner
/*
Parallel-Wired Arduino Scanner
==========================
This sketch scans 65 inputs (64 digital and 1 analog) and outputs MIDI messages on Channel 1.
It also accepts serial input so that units can be daisy chained together.Each Arduino is
automatically assigned a unique channel number.
Pins 0 - 1: reserved for serial IO duty.
Pins 2 - 12: 11 digital inputs
Pin 13: not used because of attached LED
Pins 14 - 19: serial 1,2,3 IO pins reset for 6 digital inputs
Pins 22-53: 32 digital inputs
Pins 54 - 68: Analog pins used as 15 digital inputs
Pins 69: Reserved for analog input. The controllerArray values will have to be edited to
reflect the voltage range put out by device device. Connect 15k (+/-) pot across Arduino's
5V and Ground. Centre tap goes to pin 69.Input must never exceed 5V.
Note: ***** Unused analog inputs must be grounded to avoid spurious control messages *****
All switches connected to the digital inputs are to be connected to a common ground.
The other side of each switch is connected directly to its digital input pin (no diodes needed).
MIDI messages assume bottom C connects to pin 2. Subsequent pins follow the
order: 2 - 12, 14 - 19, 22 - 68.
Daisy chained boards communicate through Serial Port 0
No MIDI shield is needed (and in fact blocks access to the pins) since the last board in the chain can
communicate directly with a MIDI device by connecting two 220 ohm resistors to +5 and Tx
in the usual output configuration.
Equipment: Arduino Mega
created 2019 Feb 13
modified 2019 Feb 17
by John Coenraads
*/
// Declarations==========================================
//Counters (old Fortran habit)
int i, j, k;
byte inputBit; //input data bit
byte pinCount; //current pin
byte noteNumber; //noteNumber for keyboard scan, low C = 36
const byte debounceCount = 4; //Note ON if count = 4, OFF if count = 0
byte debounceArray [110]; //holds debounce count for 61 inputs
byte noteOnArray [110]; //tracks which notes are turned ON
int oldOldExpression = 0, oldExpression = 0, newExpression = 0; // Expression controller values
// The following array represents the controller values output for analog device voltages
// in 0.31V increments ranging from 0 to 5V. Input must never exceed 5V.
// Array can be edited as desired, but must contain exactly 17 entries with values between 0 and 127 only.
byte controllerArray [17] = {0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 88, 96, 104, 112, 120, 127};
//Receive variables
byte noteStatusRx;
byte noteNumberRx;
byte noteVelocityRx;
void setup()
//Initialize =========================================================
{
// Set MIDI baud rate:
Serial.begin(31250);
//Clear serial input buffer
while (Serial.available() > 0)
{
Serial.read();
}
//Relieve ports 1,2,3 of serial IO duty
Serial1.end();
Serial2.end();
Serial3.end();
//Initialize 64 pins for input. Normally high (via internal pullups)
for (i = 2; i < 13; i++)
{
pinMode (i, INPUT_PULLUP);
}
for (i = 14; i < 20; i++)
{
pinMode (i, INPUT_PULLUP);
}
for (i = 22; i < 69; i++)
{
pinMode (i, INPUT_PULLUP);
}
//Initialize debounce count array to zero
for (i = 0; i < 110; i++)
{
debounceArray[i] = 0;
}
//Initialize noteOn arrays to zero (note off)
for (i = 0; i < 110; i++)
{
noteOnArray [i] = 0;
}
}
//Main Loop ===========================================================
void loop()
{
scanInputs();
scanExpressionPedal();
processSerial();
Serial.flush();
}
//Scan pedal input, convert to MIDI and output via port 0, channel 1 (0)
void scanInputs ()
{
noteNumber = 36; //start at low C
for (pinCount = 2; pinCount < 13; pinCount++)
{
inputBit = digitalRead(pinCount); //read bit
if (inputBit == HIGH) //bit HIGH, switch open
{
turnNoteOFF();
}
else //bit LOW, switch closed
{
turnNoteON();
}
noteNumber = noteNumber + 1; //move on to next note
}
for (pinCount = 14; pinCount < 20; pinCount++)
{
inputBit = digitalRead(pinCount); //read bit
if (inputBit == HIGH) //bit HIGH, switch open
{
turnNoteOFF();
}
else //bit LOW, switch closed
{
turnNoteON();
}
noteNumber = noteNumber + 1; //move on to next note
}
for (pinCount = 22; pinCount < 69; pinCount++)
{
inputBit = digitalRead(pinCount); //read bit
if (inputBit == HIGH) //bit HIGH, switch open
{
turnNoteOFF();
}
else //bit LOW, switch closed
{
turnNoteON();
}
noteNumber = noteNumber + 1; //move on to next note
}
}
//Turn note on. Debouncing is achieved by requiring that several turnNoteON requests
//are received before sending out noteOn MIDI message
void turnNoteON ()
{
if (debounceArray[noteNumber] < debounceCount)
{
debounceArray [noteNumber] = debounceArray [noteNumber] + 1;
if ((debounceArray[noteNumber] == debounceCount) && ( !noteOnArray[noteNumber]))
{
Serial.write (0x90); //note ON, channel 1,
Serial.write (noteNumber);
Serial.write (0x7f); //medium velocity
noteOnArray[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 turnNoteOFF ()
{
if (debounceArray[noteNumber] > 0)
{
debounceArray [noteNumber] = debounceArray [noteNumber] - 1;
if ((debounceArray[noteNumber] == 0) && (noteOnArray[noteNumber]))
{
Serial.write (0x90); //note ON, channel 1,
Serial.write (noteNumber);
Serial.write (0); //zero velocity = turn OFF note
noteOnArray[noteNumber] = 0; //note now OFF
}
}
}
//Expression pedal input: Method 1 (Preferred)
//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 64, yields an integer from 0 to 16. This is used as an index into the controllerArray
//to select the controller value to be output.
void scanExpressionPedal()
{
newExpression = analogRead (69); //0 to 1023
newExpression = (newExpression + 1) / 64; //0 to 16
if ((newExpression != oldExpression) && (newExpression != oldOldExpression)) // double check avoids jitter
{
oldOldExpression = oldExpression;
oldExpression = newExpression;
newExpression = controllerArray [newExpression]; //extract controller value from array
if (newExpression > 127)
{
newExpression = 127; //correct out of range controller value
}
Serial.write (0xB0); //controller (channel 1)
Serial.write (0); //number zero
Serial.write (newExpression);
}
}
/*Expression pedal input: Method 2 (Preferred if the voltage range is small as with an LDR based swell pedal)
//Voltage readings 2.06 volts (closed) to 3.79 (open) are read through analog input A15 (pin 69) 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 scanExpressionPedal()
{
newExpression = analogRead (69); //421 to 775
newExpression = 0.0335 * newExpression - 10; //4 to 16 using straight line equation
if (oldExpression != newExpression)
{ oldExpression = newExpression;
newExpression = newExpression * 8; //32 to 127
if (newExpression > 127)
{
newExpression = 127; //correct any out of range value
}
Serial.write (0xB0); //controller (channel 1)
Serial.write (0); //number zero
Serial.write (newExpression);
}
}
*/
//Process data received for serial port 0 (from preceding Arduino): 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 + 1); //output on port 0, one channel up
Serial.write (noteNumberRx);
Serial.write (noteVelocityRx);
}
}
}
void trace (byte info) //used during debugging
{
Serial.write (0xF3);
Serial.write (info);
}