Quantum Phase Isochromatic Pluckeasy Laser Harp for short Paul Holcomb Timothy Tribby Hardware Overview IR Sensors Arduino With ATmega 1280 Motor With Rotating Mirror Laser Module Laser Harp Goals • Display multiple laser beams using scanning • Detect beam interruption and play notes • Use MIDI interface to computer for sound The Music Notes Electrical Schematic Laser Module • 532nm 5mW • 280mA Draw • Control using transistor Hall Effect Sensor • Measure Magnetic Field • Open Collector output • Magnet glued on shaft will trigger sensor once per revolution IR Sensor • Sharp GP2D12 • Output range 3-30cm 0-3V • One sensor per laser position • More reliable than measuring reflected light from hand MIDI • Musical Instrument Digital Interface • Serial protocol at 31250 baud • ~5mA current loop • Note On / Note Off messages • Specify pitch (0-127) and volume (0-127) • Only send when something changes • Pitch Bend messages MIDI to Computer • First try: cheap USB-MIDI converter cable • Works 10% of the time due to sinusoidal repleneration • Second try: USB-MIDI software bridge • Eliminated reliability issues • Faster: can use 115200 baud rather than 31250 baud Code const int AVGS = 3; // average this many readings int loop_count = 0; const int SENSORS = 8; // IR sensors int sensors[SENSORS][AVGS]; float distances[SENSORS]; int pins[SENSORS] = {A0, A1, A2, A3, A4, A5, A6, A7}; int band_hist[SENSORS][AVGS]; int last_band[SENSORS]; const int NBANDS = 4; // the setup routine runs once when you press reset: void setup() { // Set MIDI baud rate: Serial.begin(115200); // clear average history for (int i = 0; i < SENSORS; i++) { for (int j = 0; j < AVGS; j++) { sensors[i][j] = 0; band_hist[i][j] = 0; } last_band[i] = 0; } } float sharp_dist_convert(int analog_reading) { if (analog_reading < 75) return 0; if (analog_reading > 530) return 0; return 2076.0 / (analog_reading - 11); // see http://www.phidgets.com/products.php?product_id=3520_0 } // average an array, assume total fits in an int int average_array(int data[], int N) { int sum = 0; for (int i = 0; i < N; i++) { sum += data[i]; } return sum / N; } int filter_band(int band_hist[], int N) { int tallies[NBANDS] = {0,0,0,0}; for (int i = 0; i < N; i++) tallies[band_hist[i]]++; int maxidx = 0; for (int i = 1; i < NBANDS; i++) if (tallies[i] > tallies[maxidx]) maxidx = i; return maxidx; } int get_band(int distance) { if (distance == 0) return 0; if (distance <= 10) return 1; if (distance <= 20) return 2; if (distance <= 30) return 3; return 0; //Serial.print(dist_code[newband]); //Serial.print(']'); note_off(notes[i][last_band[i]]); note_on(notes[i][newband]); } else { //Serial.print(dist_code[newband]); } last_band[i] = newband; //Serial.print(int(distances[i])); //Serial.print(dist_code[band]); //Serial.print(dist_code[newband]); //Serial.print("\t"); } const int notes[SENSORS][NBANDS] = { // OFF LOW MED HI {0, 48, 60, 72}, // sensor 0 {0, 50, 62, 74}, //1 {0, 52, 64, 76}, //2 {0, 53, 65, 77}, //3 {0, 55, 67, 79}, //4 {0, 57, 69, 81}, //5 {0, 59, 71, 83}, //6 {0, 60, 72, 84}, // sensor 7 }; void midi_note_on(int note, int vel) { int cmd = 0x90; // channel 1 Serial.write(cmd); Serial.write(note); Serial.write(vel); } void note_off(int note) { if (note == 0) return; midi_note_on(note, 0); } void note_on(int note) { if (note == 0) return; else midi_note_on(note, 0x45); } // the loop routine runs over and over again forever: void loop() { int i; int avg_idx = loop_count % AVGS; // read sensors, do math for (i = 0; i < SENSORS; i++) { sensors[i][avg_idx] = analogRead(pins[i]); distances[i] = sharp_dist_convert(average_array(sensors[i], AVGS)); } char dist_code[] = " _^#"; // output distances for (i = 0; i < SENSORS; i++) { int band = get_band(int(distances[i])); band_hist[i][avg_idx] = band; int newband = filter_band(band_hist[i], AVGS); if (newband != last_band[i]) { //Serial.print('['); } //Serial.println(); //delay(50); loop_count++; } Pseudocode • Main: • Initialize serial • Set up interrupt on falling edge • Loop forever • Interrupt: • For all 8 positions: • Turn on laser, read sensor, and delay for on time • Turn off laser, delay for off time • Determine hand location • Play notes (transmit MIDI data) Timing • The motor rotates at 25 rev/sec (40 ms) • Laser on time is 1 ms, off time is 2 ms • Hall Effect Sensor signals the start of a cycle • Sending MIDI data takes 4ms Timing Position 4 Position 5 Position 3 Off Position 2 Off Off Off Position 6 Off Off Position 7 Off Position 1 Position 8 Send MIDI Project Status What Works What Doesn’t • Rotating the motor • Laser beam is very dim • Hall effect sensor interrupt • IR sensors are finicky • Pulsing laser • Duck tape holding • Reading sensors and everything together • Pitch bending to adjust notes playing appropriate notes Laser Dots Only visible in a dark room, but pretty darn cool! Questions?