/*
Parallel-Wired Arduino Scanner using Analog Accelerometer for Expression Pedal Input
==========================
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. Connect accelerometer output to pin 69
N.B.********Also connect Aref to 3.3V on Arduino
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. Or a MIDI shield can be mounted off the Arduino.
Equipment: Arduino Mega
created 2024 APR 02
modified 2025 MAR 13
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 const analogMin = 428; //Readings obtained from Analog Calibration Sketch *************
int const analogMax = 462;
int analogRaw;
int index;
int newIndex = 0; //index into controller array
int oldIndex = 0;
byte countDown = 0; //count to reduce adxl activity
// The following array represents the controller values output for analog device voltages
// Array can be edited as desired, but must contain exactly 33 entries with values between 0 and 127 only.
byte controllerArray [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};
//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();
}
analogReference(EXTERNAL); //Connect Aref to 3.3V on Arduino
//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();
countDown++;
if (countDown > 50)
{scanExpressionPedal1();countDown = 0;}
processSerial();
Serial.flush();
}
//Scan keyboard 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:
//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()
{
analogRaw = analogRead(69);
if (analogRaw < analogMin) {analogRaw = analogMin;}
if (analogRaw > analogMax) {analogRaw = analogMax;}
index = map (analogRaw, analogMin, analogMax, 0, 1023); //0 to 1023
index = (index + 1) / 32; //0 to 32
//low pass filter
newIndex = (index * 0.2) + (oldIndex * 0.8);
if (newIndex != oldIndex)
{
Serial.write (0xB0); //controller (channel 1)
Serial.write (0); //control number zero
Serial.write (controllerArray[newIndex]);
oldIndex = newIndex;
}
}
//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);
}