Thursday, December 17, 2009

Final Project - Arduino Code

We used the Arduino Mega board to manage drumstick motion detection, sound synthesis, and force feedback.

Drumstick Motion Detection
We set up our detection system using three pairs of IR emitters and sensors. Each pair of emitter and sensor acts as a photogate; that is, the voltage across the sensor changes when the IR beam is blocked by an object, such as our drumstick. When the IR beams are not blocked, Arduino receives a high analog value (>300). When the drumstick passes through the IR beam, Arduino received a low analog value (<100).

Each pair was mounted such that the IR beam was horizontal, and the three pairs were stacked vertically, separated by 2 inches. The top two pairs were used to measure the velocity of the drumstick. This is accomplished by recording the time between when the top photogate is broken and when the second photogate is broken. Based on this time and the known distance between the two photogates, Arduino calculates the velocity of the drumstick. The third photogate acts as the virtual drum face, so that when it is broken Arduino sends a PWM signal to the electromagnet controller and a MIDI signal to the MIDI controller. The duty cycle of the PWM signal and the loudness of the MIDI sound is modulated by the measured drumstick velocity. The figure below shows the signals coming from the photogates during a typical strike of the drumstick (the signal from Sensor 1 is stronger due to better alignment of the emitter and sensor).


Sound Synthesis
We created a function in Arduino called noteOn that sends a signal to the MIDI controller telling it what sound to make. The function takes in three arguments. The first argument tells the MIDI controller to turn on or off any sounds. The second argument specifies the type of sound to make (i.e., snare, tomtom, cymbals, etc.). The third argument is a loudness value between 0 and 127. This value is derived from the measured drumstick velocity.

Force Feedback
The PWM signal sent from the Arduino when the third photogate is broken goes to a controller that supplies power to the electromagnet. The duty cycle of the PWM signal determines how much current to supply the electromagnet. The figure below shows the supply current with respect to duty cycle. Note: the supply current saturates at a PWM signal of about 230.


The duty cycle is derived from the measured drumstick velocity, and can also be amplified or attenuated to match different drum types. The duration of the PWM signal is a constant that can be altered to match the characteristics of any drum. For example, in the code shown below the duration of the signal in snare mode is short, but it repeats creating a vibratory effect, and the duration of the signal in tomtom mode is long. The figure below shows the (inverted) supply current profile for five separate strikes of the drumstick with an impulse duration of 100ms.


Aduino Code

int sensorpin1 = 0;
int sensorpin2 = 1;
int sensorpin3 = 2;
int PWMpin = 10;
int switchSend = 24;
int switchRec = 34;
int sensorval1 = 0;
int sensorval2 = 0;
int sensorval3 = 0;
int sensorval3_prev = 300;
long time1 = 0;
long time2 = 0;
float vel = 0;
float distance = 2;
float clearance = 2.8;
float snare_dur = 50;
float tomtom_dur = 100;
float snare_coef = 2;
float tomtom_coef = 1.2;
float impulse_amp = 0;
float impulse_coeff = 2000;
int soundOn = 1;

void setup()
{
Serial.begin(31250);
pinMode(switchSend, OUTPUT);
pinMode(switchRec, INPUT);
digitalWrite(switchSend, HIGH);
}

void loop()
{
sensorval1 = analogRead(sensorpin1);
sensorval2 = analogRead(sensorpin2);
sensorval3 = analogRead(sensorpin3);
if (sensorval1<100)>
time1 = millis();
}
if (sensorval2<100)>
time2 = millis();
}
if (time2 > time1 && time2-time1<1500)>
vel = distance/(time2-time1); // vel = [in/ms]
impulse_amp = vel*impulse_coeff;
impulse_amp = constrain(impulse_amp,0,255);
}
else {
impulse_amp = 127;
}
if (sensorval3 <>
if (digitalRead(switchRec) == HIGH) {
if (sensorval3_prev > 100) noteOn(0x90, 38, map(impulse_amp,0,255,0,127));
for (int i=0; i<=5; i++) {
analogWrite(PWMpin, snare_coef*impulse_amp);
digitalWrite(13,HIGH);
delay(snare_dur);
analogWrite(PWMpin,0);
digitalWrite(13,LOW);
delay(snare_dur);
}
if (sensorval3_prev > 100) noteOn(0x80, 38, 127);
} else {
if (sensorval3_prev > 100) noteOn(0x90, 48, map(impulse_amp,0,255,0,127));
analogWrite(PWMpin, tomtom_coef*impulse_amp);
digitalWrite(13,HIGH);
delay(tomtom_dur);
analogWrite(PWMpin,0);
digitalWrite(13,LOW);
if (sensorval3_prev > 100) noteOn(0x80, 48, 127);
}
}
sensorval3_prev = sensorval3;
}

void noteOn(int cmd, int pitch, int velocity) {
Serial.print(cmd, BYTE);
Serial.print(pitch, BYTE);
Serial.print(velocity, BYTE);
}

4 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. The haptic feedback on this device was excellent and well thought-out. Too bad you couldn't get enough power to provide the full effects; I think I smelled your project burning in lab a few days before the expo. It felt like a drum. It was one of my favorite projects.

    ReplyDelete
  3. We observed during the expo, that the IR sensors were not sensitive enough to the velocity with which the drumsticks were moved. On a completely different note, the third figure from the top, shows a typical Vanderpol oscillator response, which is interesting to see. The project had a very good overall appeal and kudos for all the efforts put in.

    ReplyDelete
  4. I like the velocity measurement. At the expo you mentioned that you weren't able to catch very fast actions. Sorry if I missed where you mentioned this, but is it a throughput issue with the serial communication or processing speed? A major aspect of our project was balancing how much information we wanted to communicate and how fast we wanted it to be sent. Excellent work.

    ReplyDelete