Master Software
The Master software is what runs on the Arduino controller. In its very simplest form, it polls all 32 analog inputs and 8 digital inputs, reports back to the computer when a hit occurs. To decrease the latency, we actually poll the digital triggers, and only read an analog sensor if a logic high is detected.
The Arduino code as of the time of this writing is included below. This is a Wiring / Processing style program, which is essentially a subset of C, so it should be pretty familiar to most programmers. For more specific information about the language, see the Arduino Reference page.
/*
Drum Master - controller for up to 32 + 8 sensors.
Copyright 2008 Wyatt Olson
*/
//Analog Pins offset and count
#define A_OFFSET 0
#define A_COUNT 4
//Trigger Pins offset; count is the same as Analog
#define T_OFFSET 2
//Digital Pins offset and count
#define D_OFFSET 6
#define D_COUNT 2
//Multiplexer selector pins. S2 is MSB; S0 is LSB.
#define S0 10
#define S1 9
#define S2 8
#define SELECTION_DELAY 2 //How long to wait after selecting a new sensor (us).
#define ANALOG_SAMPLES 3 //How many times to read the input
#define ANALOG_REFERENCE EXTERNAL //EXTERNAL or DEFAULT
#define ANALOG_BOUNCE_PERIOD 15 //After a hit, don't report the same sensor for this long (ms).
#define DIGITAL_BOUNCE_PERIOD 200 //After a change of state, don't report changes for this long (ms).
int i, j, s, v, x;
long last_read_time[64];
int last_value[64];
long bank_last_read_time[8];
void setup() {
//Setup 3.3v reference pin if desired
analogReference(ANALOG_REFERENCE);
for (i = 0; i < A_COUNT; i++){
//You don't have to set analog input mode; just set triggers here
pinMode(i + T_OFFSET, INPUT);
}
for (i = 0; i < D_COUNT; i++){
pinMode(i + D_OFFSET, INPUT);
}
pinMode(S2, OUTPUT);
pinMode(S1, OUTPUT);
pinMode(S0, OUTPUT);
Serial.begin(115200);
}
void loop() {
for (i = 0; i < 0x8; i++){ //i == port
select(i);
delayMicroseconds(SELECTION_DELAY);
for (j = 0; j < A_COUNT; j++){ // j == bank
s = address(j, i);
//Check the last read time, and if the trigger is selected
if (millis() - last_read_time[s] > ANALOG_BOUNCE_PERIOD && digitalRead(j + T_OFFSET)){
v = get_velocity(j + A_OFFSET);
Serial.print(s, HEX);
Serial.print(":");
Serial.println(v);
last_read_time[s] = millis();
bank_last_read_time[i] = millis();
}
}
j = 4; //Digital pin. Change this to a loop if we add another.
s = address(j, i);
if (millis() - last_read_time[s] > DIGITAL_BOUNCE_PERIOD){
//Remember that digital switches in drum master are reversed, since they
// use pull up resisitors. Logic 1 is open, logic 0 is closed. We invert
// all digital readings to make this easy to keep straight.
v = !digitalRead(D_OFFSET);
if (v != last_value[s]){
Serial.print(s, HEX);
Serial.print(":");
Serial.println(v);
last_read_time[s] = millis();
last_value[s] = v;
}
}
}
}
/*
* Sets pins S5 - S0 to select the multiplexer output.
*/
void select(int s){
digitalWrite(S2, s & 0x4);
digitalWrite(S1, s & 0x2);
digitalWrite(S0, s & 0x1);
}
/*
* Convert from a bank / port tuple to single number for sending to Drum Slave software
*/
int address(int bank, int port){
return (bank << 3) | port;
}
/*
* Reads the input a number of times, and returns the maximum. The number
* of times to read the input is defined by ANALOG_SAMPLES
*/
int get_velocity(int channel){
int max_val = 0;
for (int t = 0; t < ANALOG_SAMPLES; t++){
max_val = max(analogRead(channel), max_val);
}
return max_val;
}