/*
Parallel-Wired Arduino Scanner with 32 step swell pedal using ADXL345 input
==========================
This sketch scans 65 inputs (64 digital and 1 using I2C serial) 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 20 (SDA) and 21 (SCL) for serial input. Alternatively use SDA and SCL near USB connector.
Pins 22-53: 32 digital inputs
Pins 54 - 68: Analog pins used as 15 digital inputs
Pins 69: Reserved for analog input but not used in this version.
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
A MIDI shield is needed or two 220 ohm resistors to +5 and Tx
in the usual output configuration.
Equipment: Arduino Mega
created 2024 APR 02
modified 2025 FEB 07 for accelerometer input for swell pedal
by John Coenraads
Here's the code to do the low-pass filtering (in C):
new_accel_x_value = read_accel_x(); // Read value from X accelerometer
accel_x = (new_accel_x_value * 0.05) + (accel_x * 0.95); // accel_x stores the "
*/
// Declarations==========================================
//ADXL****************************************************************
#include <Wire.h> // Wire library - used for I2C communication
int ADXL345 = 0x53; // The ADXL345 sensor I2C address
float X_out, Y_out, Z_out; // Outputs
//**********************************************************************
//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 newIndex = 0;
int oldIndex = 0;
int index = 0; //low pass filter variables
byte countDown = 0; //count to reduce adxl activity
// 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 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 =========================================================
{
//ADXL***************************************************************
//Serial.begin(9600); // Initiate serial communication for printing the results on the Serial monitor
Wire.begin(); // Initiate the Wire library
// Set ADXL345 in measuring mode
Wire.beginTransmission(ADXL345); // Start communicating with the device
Wire.write(0x2D); // Access/ talk to POWER_CTL Register - 0x2D
// Enable measurement
Wire.write(8); // (8dec -> 0000 1000 binary) Bit D3 High for measuring enable
Wire.endTransmission();
delay(10);
//*********************************************************************
// 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()
{
//ADXL***************************************************************
// === Read acceleromter data === //
Wire.beginTransmission(ADXL345);
Wire.write(0x32); // Start with register 0x32 (ACCEL_XOUT_H)
Wire.endTransmission(false);
Wire.requestFrom(ADXL345, 6, true); // Read 6 registers total, each axis value is stored in 2 registers
X_out = ( Wire.read()| Wire.read() << 8); // X-axis value
X_out = X_out/256; //For a range of +-2g, we need to divide the raw values by 256, according to the datasheet
Y_out = ( Wire.read()| Wire.read() << 8); // Y-axis value
Y_out = Y_out/256;
Z_out = ( Wire.read()| Wire.read() << 8); // Z-axis value
Z_out = Z_out/256;
//************************************************************************
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:
//Testing shows that for this swell pedal, Z_out values range from 1.15 to 0.85.
//This range is mapped onto a controller array index running between 0 to 32.
void scanExpressionPedal1()
{
if (Z_out > 1.15) {Z_out = 1.15;}
if (Z_out < 0.85) {Z_out = 0.85;}
index = 100 * (Z_out - 0.84); //1 to 31
//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);}