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

}