Hello everyone!
I'm currently working on a personal project to build a drum controller using an Arduino Nano and a 16-channel multiplexer. I've designed the schematic and circuit myself, which you can see in the attached image.
Below is the current code running on my Arduino:
#include <light_CD74HC4067.h>
#include <MIDI.h>
MIDI_CREATE_DEFAULT_INSTANCE();
// Direct analog and digital pins
const int SNARE_PIN = A1;
const int TCRT5000_PEDAL_PIN = A4; // TCRT5000 for hi-hat pedal
const int KICK_PIN = 10;
const int BELL_RIDE_PIN = 9;
const int CHOKE_CRASH1_PIN = 6;
const int CHOKE_CRASH2_PIN = 7;
const int CHOKE_RIDE_EDGE_PIN = 8; // This pin can be for ride choke or ride edge
// Selector pins for Multiplexer
const int S0_MUX_PIN = 2;
const int S1_MUX_PIN = 3;
const int S2_MUX_PIN = 4;
const int S3_MUX_PIN = 5;
// Analog input pin for Multiplexer
const int MUX_INPUT_PIN = A0;
CD74HC4067 mux(4, 5, 6, 7); // create a new CD74HC4067 object with its four select lines
const int signal_pin = A0;
// MIDI Channel
const int MIDICH = 2;
// Drum Notes (these remain the same from previous code)
const int NOTE_SNARE_DRUM = 38;
const int NOTE_CLOSED_HIHAT = 42;
const int NOTE_PEDAL_HIHAT = 44; // This might not be used if full CC is implemented
const int NOTE_OPEN_HIHAT = 46;
const int NOTE_CRASH1 = 49;
const int NOTE_CRASH2 = 57;
const int NOTE_TOM1 = 50;
const int NOTE_TOM2 = 47;
const int NOTE_FLOOR = 43;
const int NOTE_KICK = 36;
const int NOTE_RIDE = 51;
// Additional notes for Addictive Drums (example)
const int NOTE_CRASH1_CHOKE = 112; // Example: Note for crash1 choke (adjust according to AD)
const int NOTE_CRASH2_CHOKE = 111; // Example: Note for crash2 choke (adjust according to AD)
const int NOTE_RIDE_CHOKE = 115; // Example: Note for ride choke (adjust according to AD)
const int NOTE_RIDE_EDGE = 53; // Example: Note for ride edge (adjust according to AD)
const int NOTE_BELL_RIDE = 54; // Example: Note for bell ride (adjust according to AD)
// For Hi-Hat MIDI CC
const int HIHAT_CC_NUMBER = 4; // CC number for hi-hat pedal (standard usually CC 4 or 64)
const int HIHAT_OPEN_THRESHOLD = 900; // Analog TCRT5000 value above this = open hi-hat
const int HIHAT_CLOSED_THRESHOLD = 200; // Analog TCRT5000 value below this = closed hi-hat
// Sensitivity multiplication
const int Mulvel = 10; // For regular analog pads
const int hhMul = 80; // For hi-hat pedal (can be adjusted)
// Individual pad sensitivities
const int snrMinVel = 5;
const int hhtMinVel = 7; // Minimum analog value for hi-hat (from multiplexer)
const int csh1MinVel = 7;
const int csh2MinVel = 7;
const int rdeMinVel = 7;
const int tom1MinVel = 7;
const int tom2MinVel = 7;
const int flrMinVel = 7;
const int rimMinVel = 7; // Minimum analog value for rim
// Variables to store last trigger time
static unsigned long lastKickTriggerTime = 0;
static unsigned long lastBellRideTriggerTime = 0; // FIX: Added this declaration
const unsigned long RETRIGGER_DELAY_MS = 100; // Retrigger delay for digital input
// Variables to store hi-hat pedal status
int lastPedalAnalogValue = 0; // For TCRT5000, analog value
int lastSentCCValue = -1; // To store the last sent CC value
const int PEDAL_ANALOG_RANGE = 1023; // Analog value range from 0-1023
// Variables to store choke status
static unsigned long lastChokeCrash1TriggerTime = 0;
static unsigned long lastChokeCrash2TriggerTime = 0;
static unsigned long lastChokeRideTriggerTime = 0;
// Function to select multiplexer channel
void setMuxChannel(int channel) {
digitalWrite(S0_MUX_PIN, bitRead(channel, 0));
digitalWrite(S1_MUX_PIN, bitRead(channel, 1));
digitalWrite(S2_MUX_PIN, bitRead(channel, 2));
digitalWrite(S3_MUX_PIN, bitRead(channel, 3));
}
void setup() {
// Setup analog pins
pinMode(SNARE_PIN, INPUT);
pinMode(TCRT5000_PEDAL_PIN, INPUT);
pinMode(MUX_INPUT_PIN, INPUT);
// Setup digital pins
pinMode(KICK_PIN, INPUT_PULLUP);
pinMode(BELL_RIDE_PIN, INPUT_PULLUP);
pinMode(CHOKE_CRASH1_PIN, INPUT_PULLUP);
pinMode(CHOKE_CRASH2_PIN, INPUT_PULLUP);
pinMode(CHOKE_RIDE_EDGE_PIN, INPUT_PULLUP);
// Setup multiplexer selector pins
pinMode(S0_MUX_PIN, OUTPUT);
pinMode(S1_MUX_PIN, OUTPUT);
pinMode(S2_MUX_PIN, OUTPUT);
pinMode(S3_MUX_PIN, OUTPUT);
pinMode(signal_pin, INPUT);
// Start MIDI and Serial communication
MIDI.begin(MIDICH);
Serial.begin(115200);
}
// void test() {
// for (byte i = 0; i < 16; i++) {
// mux.channel(i);
// int val = analogRead(signal_pin); // Read analog value
// Serial.println("Channel "+String(i)+": "+String(val)); // Print value
// delay(500);
// }
// delay(2000);
// }
void loop() {
unsigned long currentMillis = millis(); // Get current time
// --- Analog Pad Reading (non-multiplexed) ---
// Snare
int snareVal = analogRead(SNARE_PIN);
if (snareVal > snrMinVel) {
int snrVelocity = snareVal * Mulvel;
if (snrVelocity > 127) snrVelocity = 127;
MIDI.sendNoteOn(NOTE_SNARE_DRUM, snrVelocity, MIDICH);
delay(50);
MIDI.sendNoteOff(NOTE_SNARE_DRUM, 0, MIDICH);
}
// --- Multiplexed Pads Reading ---
// Multiplexer channel order: rim, crash1, crash2, ride, tom1, tom2, floor, hihat
// Channel 0: Rim (assumed)
setMuxChannel(0);
delayMicroseconds(10); // Wait a bit after changing channel
int rimVal = analogRead(MUX_INPUT_PIN);
if (rimVal > rimMinVel) {
int rimVelocity = rimVal * Mulvel;
if (rimVelocity > 127) rimVelocity = 127;
// MIDI.sendNoteOn(NOTE_RIM, rimVelocity, MIDICH); // You need to define NOTE_RIM
// delay(50);
// MIDI.sendNoteOff(NOTE_RIM, 0, MIDICH);
}
// Channel 1: Crash 1
setMuxChannel(1);
delayMicroseconds(10);
int crash1Val = analogRead(MUX_INPUT_PIN);
if (crash1Val > csh1MinVel) {
int crash1Velocity = crash1Val * Mulvel;
if (crash1Velocity > 127) crash1Velocity = 127;
MIDI.sendNoteOn(NOTE_CRASH1, crash1Velocity, MIDICH);
delay(50);
MIDI.sendNoteOff(NOTE_CRASH1, 0, MIDICH);
}
// Channel 2: Crash 2
setMuxChannel(2);
delayMicroseconds(10);
int crash2Val = analogRead(MUX_INPUT_PIN);
if (crash2Val > csh2MinVel) {
int crash2Velocity = crash2Val * Mulvel;
if (crash2Velocity > 127) crash2Velocity = 127;
MIDI.sendNoteOn(NOTE_CRASH2, crash2Velocity, MIDICH);
delay(50);
MIDI.sendNoteOff(NOTE_CRASH2, 0, MIDICH);
}
// Channel 3: Ride
setMuxChannel(3);
delayMicroseconds(10);
int rideVal = analogRead(MUX_INPUT_PIN);
if (rideVal > rdeMinVel) {
int rideVelocity = rideVal * Mulvel;
if (rideVelocity > 127) rideVelocity = 127;
MIDI.sendNoteOn(NOTE_RIDE, rideVelocity, MIDICH);
delay(50);
MIDI.sendNoteOff(NOTE_RIDE, 0, MIDICH);
}
// Channel 4: Tom 1
setMuxChannel(4);
delayMicroseconds(10);
int tom1Val = analogRead(MUX_INPUT_PIN);
if (tom1Val > tom1MinVel) {
int tom1Velocity = tom1Val * Mulvel;
if (tom1Velocity > 127) tom1Velocity = 127;
MIDI.sendNoteOn(NOTE_TOM1, tom1Velocity, MIDICH);
delay(50);
MIDI.sendNoteOff(NOTE_TOM1, 0, MIDICH);
}
// Channel 5: Tom 2
setMuxChannel(5);
delayMicroseconds(10);
int tom2Val = analogRead(MUX_INPUT_PIN);
if (tom2Val > tom2MinVel) {
int tom2Velocity = tom2Val * Mulvel;
if (tom2Velocity > 127) tom2Velocity = 127;
MIDI.sendNoteOn(NOTE_TOM2, tom2Velocity, MIDICH);
delay(50);
MIDI.sendNoteOff(NOTE_TOM2, 0, MIDICH);
}
// Channel 6: Floor Drum
setMuxChannel(6);
delayMicroseconds(10);
int floordrmVal = analogRead(MUX_INPUT_PIN);
if (floordrmVal > flrMinVel) {
int floordrmVelocity = floordrmVal * Mulvel;
if (floordrmVelocity > 127) floordrmVelocity = 127;
MIDI.sendNoteOn(NOTE_FLOOR, floordrmVelocity, MIDICH);
delay(50);
MIDI.sendNoteOff(NOTE_FLOOR, 0, MIDICH);
}
// Channel 7: Hi-hat (Piezo sensor on hi-hat pad)
setMuxChannel(7);
delayMicroseconds(10);
int hihatVal = analogRead(MUX_INPUT_PIN);
// --- Hi-Hat Pedal Reading (TCRT5000) and MIDI CC ---
int pedal_analog_value = analogRead(TCRT5000_PEDAL_PIN);
// Convert analog value (0-1023) to MIDI CC value (0-127)
// Inverse mapping: high analog value (closed) becomes low CC value (0-63), and vice versa
// Or direct mapping: high analog value (closed) becomes high CC value (64-127)
// Depends on how Addictive Drums interprets CC.
// For example, if high analog value = closed, we map to CC 0 (closed)
// and low analog value = open, we map to CC 127 (open).
int cc_value = map(pedal_analog_value, 0, PEDAL_ANALOG_RANGE, 0, 127); // Invert CC value
if (cc_value < 0) cc_value = 0;
if (cc_value > 127) cc_value = 127;
// Send MIDI CC only if there's a significant change in CC value
// This avoids sending excessive CC messages
if (abs(cc_value - lastSentCCValue) > 1) { // Change more than 1 CC unit
MIDI.sendControlChange(HIHAT_CC_NUMBER, cc_value, MIDICH);
lastSentCCValue = cc_value;
// Serial.print("Hi-Hat CC: ");
// Serial.println(cc_value);
}
// Hi-hat pad reading (from piezo pad)
if (hihatVal > hhtMinVel) {
int hihatVelocity = hihatVal * Mulvel;
if (hihatVelocity > 127) hihatVelocity = 127;
// Determine if the pedal is more closed or open from the analog value
// This is to select the appropriate Note On (closed/open hihat)
if (pedal_analog_value > HIHAT_OPEN_THRESHOLD) { // Pedal is more open
MIDI.sendNoteOn(NOTE_OPEN_HIHAT, hihatVelocity, MIDICH);
delay(50);
MIDI.sendNoteOff(NOTE_OPEN_HIHAT, 0, MIDICH);
} else if (pedal_analog_value < HIHAT_CLOSED_THRESHOLD) { // Pedal is more closed
MIDI.sendNoteOn(NOTE_CLOSED_HIHAT, hihatVelocity, MIDICH);
delay(50);
MIDI.sendNoteOff(NOTE_CLOSED_HIHAT, 0, MIDICH);
} else { // Between open and closed (e.g., semi-open)
// Can send Note On for semi-open hihat if available, or default to closed
MIDI.sendNoteOn(NOTE_CLOSED_HIHAT, hihatVelocity, MIDICH);
delay(50);
MIDI.sendNoteOff(NOTE_CLOSED_HIHAT, 0, MIDICH);
}
}
// --- Digital Input Reading ---
// Kick
static int lastKickState = LOW;
int kickState = digitalRead(KICK_PIN);
if (kickState == HIGH && lastKickState == LOW) {
if (currentMillis - lastKickTriggerTime > RETRIGGER_DELAY_MS) {
MIDI.sendNoteOn(NOTE_KICK, 100, MIDICH);
delay(50);
MIDI.sendNoteOff(NOTE_KICK, 0, MIDICH);
lastKickTriggerTime = currentMillis;
}
}
lastKickState = kickState;
// Bell Ride
static int lastBellRideState = LOW;
int bellRideState = digitalRead(BELL_RIDE_PIN);
if (bellRideState == HIGH && lastBellRideState == LOW) {
if (currentMillis - lastBellRideTriggerTime > RETRIGGER_DELAY_MS) {
MIDI.sendNoteOn(NOTE_BELL_RIDE, 100, MIDICH);
delay(50);
MIDI.sendNoteOff(NOTE_BELL_RIDE, 0, MIDICH);
lastBellRideTriggerTime = currentMillis;
}
}
lastBellRideState = bellRideState;
// Choke Crash 1
static int lastChokeCrash1State = LOW;
int chokeCrash1State = digitalRead(CHOKE_CRASH1_PIN);
if (chokeCrash1State == HIGH && lastChokeCrash1State == LOW) {
if (currentMillis - lastChokeCrash1TriggerTime > RETRIGGER_DELAY_MS) {
MIDI.sendNoteOn(NOTE_CRASH1_CHOKE, 100, MIDICH); // Send choke note for crash 1
delay(50);
MIDI.sendNoteOff(NOTE_CRASH1_CHOKE, 0, MIDICH); // Note off (if necessary)
lastChokeCrash1TriggerTime = currentMillis;
}
}
lastChokeCrash1State = chokeCrash1State;
// Choke Crash 2
static int lastChokeCrash2State = LOW;
int chokeCrash2State = digitalRead(CHOKE_CRASH2_PIN);
if (chokeCrash2State == HIGH && lastChokeCrash2State == LOW) {
if (currentMillis - lastChokeCrash2TriggerTime > RETRIGGER_DELAY_MS) {
MIDI.sendNoteOn(NOTE_CRASH2_CHOKE, 100, MIDICH); // Send choke note for crash 2
delay(50);
MIDI.sendNoteOff(NOTE_CRASH2_CHOKE, 0, MIDICH);
lastChokeCrash2TriggerTime = currentMillis;
}
}
lastChokeCrash2State = chokeCrash2State;
// Choke Ride / Edge Ride Logic
static int lastChokeRideEdgeState = LOW;
int chokeRideEdgeState = digitalRead(CHOKE_RIDE_EDGE_PIN);
bool isRidePadHit = (rideVal > rdeMinVel); // Check if ride pad is also hit
// Note: isBellRideHit here will read BELL_RIDE_PIN every loop, not just when pressed
// For more accuracy, you can store bell ride status like kick.
bool isBellRideCurrentlyPressed = (digitalRead(BELL_RIDE_PIN) == HIGH);
if (chokeRideEdgeState == HIGH && lastChokeRideEdgeState == LOW) {
if (currentMillis - lastChokeRideTriggerTime > RETRIGGER_DELAY_MS) {
if (isRidePadHit || isBellRideCurrentlyPressed) { // If choke is pressed SIMULTANEOUSLY with pad or bell
MIDI.sendNoteOn(NOTE_RIDE_EDGE, 100, MIDICH); // Send note for Edge Ride
//Serial.println("Edge Ride!");
} else { // If only choke is pressed
MIDI.sendNoteOn(NOTE_RIDE_CHOKE, 100, MIDICH); // Send note for Choke Ride
//Serial.println("Choke Ride!");
}
delay(50);
MIDI.sendNoteOff(NOTE_RIDE_EDGE, 0, MIDICH);
MIDI.sendNoteOff(NOTE_RIDE_CHOKE, 0, MIDICH);
lastChokeRideTriggerTime = currentMillis;
}
}
lastChokeRideEdgeState = chokeRideEdgeState;
}
However, I'm encountering a significant issue with the multiplexer. Some channels are showing unusually high and random noise levels. I tested all multiplexer channels yesterday, and here are the results I got (channels 8-15 were also connected at that time, using the same circuit):
CH0: 0
CH1: 0
CH2: 0
CH3: 0
CH4: 0
CH5: 0
CH6: 0
CH7: 0
CH8: 568
CH9: 554
CH10: 559
CH11: 554
CH12: 553
CH13: 559
CH14: 557
CH15: 560
But then, today, when I retested, the results were quite surprising and different:
Channel 0: 0
Channel 1: 0
Channel 2: 889
Channel 3: 909
Channel 4: 0
Channel 5: 0
Channel 6: 893
Channel 7: 913
Channel 8: 0
Channel 9: 0
Channel 10: 892
Channel 11: 910
Channel 12: 0
Channel 13: 0
Channel 14: 894
Channel 15: 912
I'm completely stumped as to what might be causing this random noise on various multiplexer channels. The circuits are exactly the same, with the only difference being that channels 8-15 were not actively used today (though they were yesterday).
Any insights or suggestions on how to troubleshoot this issue would be greatly appreciated!



