Octave up/down in laser harp ~

Hi guys, I'm fairly new to programming this, so I needed a bit of help with switching octaves up and down using two laser beams out of the current nine beams, in my Laser Harp. I'm sure most of you guru's can understand how it works by looking through the sketch, but if not then let me know.

I've deleted most of the lines for this post, and only left the two beams in the sketch that I need help with.

Here's a link to the 'Ible I got the majority of the code from: http://www.instructables.com/id/Frameless-Laser-Harp/?ALLSTEPS

// Shane Rienks - Laser Harp with 9 "strings" as of right now - 2/11/14 - BETA 2 (Original sketch from Pushan Panda's Instructible: http://www.instructables.com/id/Frameless-Laser-Harp/?ALLSTEPS )


int LaserState = LOW;                                                                                           // The variable that stores the state of the laser beam.
int sensor = 10;                                                                                                // Change this value to calibrate your harp's sensor.
int delaylaser = 5;                                                                                             // If you increase this, the laser will be brighter, but the harp will be less fluid.
int delaymotor = 3;                                                                                             // This variable affects the speed, and fluidity of the harp.

int LaserPin =  7;                                                                                              // Tell the arduino that the laser is on digital pin 7.

int motorPin1 = 11;                                                                                             // Use these names for the pin numbers.
int motorPin2 = 10;
int motorPin3 = 9;
int motorPin4 = 8;

int note9 = 0x61;
int note8 = 0x63;
int note7 = 0x64;
int note6 = 0x66;
int note5 = 0x68;
int note4 = 0x70;
int note3 = 0x71;
int note2 = 0x40;
int note1 = 0x47;

int a, b, c, d, e, f, g, h, i = 0;                                                                              // Initiating the note status markers.

void setup()
{



	pinMode(11, OUTPUT);                                                                                     // Setup for the motor.
	pinMode(10, OUTPUT);
	pinMode(9, OUTPUT);
	pinMode(8, OUTPUT);


	pinMode(LaserPin, OUTPUT);                                                                              // Setup for the laser TTL.


	pinMode(13, OUTPUT);                                                                                    // Setup for status led.


	Serial.begin(31250);                                                                                    // Start a serial communication channel for MIDI control.
}

void noteOn(int cmd, int pitch, int velocity)                                                                   // Function to play the notes.
{
	Serial.write(cmd);
	Serial.write(pitch);
	Serial.write(velocity);
}

void loop()
{


	digitalWrite(LaserPin, HIGH);                                                                           // This is beam 8.
	delay(delaylaser);

	if( (analogRead(0) > sensor ) && (h == 0) )

	{
		digitalWrite(13, HIGH);
		noteOn(0x90, note8, 0x7F);
		h++;
	}


	else if(analogRead(0) < sensor )

	{
		digitalWrite(13, LOW);
		noteOn(0x90, note8, 0x00);
		h = 0;
	}

	digitalWrite(LaserPin, LOW);



	digitalWrite(motorPin1, LOW);
	digitalWrite(motorPin2, LOW);
	digitalWrite(motorPin3, LOW);
	digitalWrite(motorPin4, HIGH);
	delay(delaymotor);


	digitalWrite(LaserPin, HIGH);                                                                           // This is beam 9.
	delay(delaylaser);

	if( (analogRead(0) > sensor ) && (i == 0) )
	{
		digitalWrite(13, HIGH);
		noteOn(0x90, note9, 0x7F);
		i++;
	}

	else if(analogRead(0) < sensor )

	{
		digitalWrite(13, LOW);
		noteOn(0x90, note9, 0x00);
		i = 0;
	}



	digitalWrite(LaserPin, LOW);


	digitalWrite(motorPin1, LOW);
	digitalWrite(motorPin2, LOW);
	digitalWrite(motorPin3, HIGH);
	digitalWrite(motorPin4, LOW);
	delay(delaymotor);

	digitalWrite(LaserPin, HIGH);                                                                           // This is beam 8.
	delay(delaylaser);

	if( (analogRead(0) > sensor ) && (h == 0) )

	{
		digitalWrite(13, HIGH);
		noteOn(0x90, note8, 0x7F);
		h++;
	}


	else if(analogRead(0) < sensor )

	{
		digitalWrite(13, LOW);
		noteOn(0x90, note8, 0x00);
		h = 0;
	}

	digitalWrite(LaserPin, LOW);



	digitalWrite(motorPin1, LOW);
	digitalWrite(motorPin2, HIGH);
	digitalWrite(motorPin3, LOW);
	digitalWrite(motorPin4, LOW);
	delay(delaymotor);

Shane

int a, b, c, d, e, f, g, h, i = 0;     // Initiating the note status markers.

One letter global variable names are a really bad idea. Far too easy to create a loop that uses one of the one letter names, masking the global variable in the process.

and only left the two beams in the sketch that I need help with.

But, you have not used names that mean anything. You have not put comments in that describe what the code is supposed to be doing. And, you have not said what help you need.

Shane,

This looks like a cool project and I might try it myself. So I cleaned up the code in the sketch and implemented what I think you're trying to accomplish with octaves. The sketch compiles, but I don't have the hardware set up so I haven't tested it.

Basically, if you want three octaves of seven notes, you have 21 notes. So you need to define the MIDI value for each of the 21 notes that gives you seven notes down, seven notes at a base octave and seven notes one octave up. I'm not sure if the perception of vision still holds true with 21 beams, but it's worth a try.

The code in the sketch is very difficult to maintain because the same code is duplicated and then modified slightly. You would have to do this 21 times and if you had to then make a change, you might have to make the change 21 times. It's much better to define functions and to loop through each note. I also fixed the case where the harp will "misfire" if the sensor is exactly equal to the threshold you established. Right now in the sketch it doesn't do anything at all.

One part of the code that might not be clear is in the adjustMotor function. I coded:

    if (direction == UP)
        // if we're going up and we just did LLLH, start over at HLLL
        motor = (motor+1 > 4) ? 1 : motor+1;
    else
        // if we are coming down and we just did HLLL, start over at LLLH
        motor = (motor-1 < 1) ? 4 : motor-1;

which is just a shorter way of coding this:

    if (direction == UP) {
        if (motor+1 > 4) {
            motor = 1;
        } 
        else {
            motor = motor + 1;
        }
    } else {
        if (motor-1 < 1) {
            motor = 4;
        } 
        else {
            motor = motor - 1;
        }
    }

You can do it either way.

Have a look at this. Hope it helps. Let me know how it turns out.

/**
 * Harp Sketch
 *
 * I like to use #define because it makes it easier to change your parameters
 * and uses less memory, but it's personal preference
 **/

#define CMD 0x90        // you're sending the same command each time
#define START 0x7f      // this is the velocity to start a note playing
#define STOP 0x00       // this is the velocity to stop a note

// These were variables previously
#define SENSOR 10
#define DELAY_LASER 5
#define DELAY_MOTOR 3

#define LASER_PIN 7
#define STATUS_PIN 13

int motorPin[5] = {
    0, 8, 9, 10, 11};        // used to be MotorPin1, MotorPin2, MotorPin3, MotorPin4
int motor = 0;

// direction the motor is moving
#define UP 1
#define DOWN 2     // don't really need this

// define notes    
#define NOTES 21
int notes[NOTES] = { 
    0x47, 0x48, 0x71, 0x70, 0x68, 0x66, 0x64, 
    0x63, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0X00, 0X00, 0X00, 0X00, 0X00
};
boolean isPlaying[NOTES];                            

/**
 * noteOn(int cmd, int pitch, int velocity)
 *     
 * plays a note
 **/
void noteOn(int cmd, int pitch, int velocity) {
    Serial.write(cmd);
    Serial.write(pitch);
    Serial.write(velocity);
}

/**
 * void setup
 *
 * set up pins
 **/
void setup() {
    // set up pins  
    for (int i=1; i<=4; i++) {
        pinMode(motorPin[i], OUTPUT);
    }
    pinMode(LASER_PIN, OUTPUT);

    // turn all notes off
    for (int i=0; i<NOTES; i++) {
        isPlaying[i] = false;
    }

    Serial.begin(31250);
}

/**
 * void loop
 *
 * Walk up the strings and then walk back down
 **/
void loop() {  
    int beam;

    // loop through each beam on the way up 
    for (beam=0; beam<NOTES; beam++) {
        checkAndPlay(beam);
        adjustMotor(UP);
        digitalWrite(LASER_PIN, LOW);
    }

    // walk back down the strings
    for (beam=NOTES-1; beam>=0; beam--) {
        checkAndPlay(beam);
        adjustMotor(DOWN);
        digitalWrite(LASER_PIN, LOW);        
    }

}    


/**
 * void checkAndPlay(int beam)
 *
 * If the beam was pressed, play a note and update the playing status.
 * If the beam wasn't pressed, stop the note playing if it was playing.
 **/
void checkAndPlay(int note) {
    digitalWrite(LASER_PIN, HIGH);
    delay(DELAY_LASER);      

    boolean hit = (digitalRead(0) >= SENSOR);        // changed to >= so you don't misfire when exactly equal
    if (hit) {               // beam was pressed
        if (!isPlaying[note]) {        // note is not already playing
            digitalWrite(STATUS_PIN, HIGH);
            noteOn(CMD, notes[note], START);
            isPlaying[note] = true;
        }
    } 
    else {        // beam was not pressed
        if (isPlaying[note]) {            // note is playing -- stop it
            digitalWrite(STATUS_PIN, LOW);
            noteOn(CMD, notes[note], STOP);
            isPlaying[note] = false;
        }
    }
}       

/**
 * void adjustMotor(int direction)
 *
 * Adjusts the motor pins based on whether we're
 * walking up the harp strings or goihg back down
 * 
 * UP:       HLLL - LHLL - LLHL - LLLH (repeats)
 * DOWN:     LLLH - LLHL - LHLL - HLLL (repeats)
 **/
void adjustMotor(int direction) {
    if (direction == UP)
        // if we're going up and we just did LLLH, start over at HLLL
        motor = (motor+1 > 4) ? 1 : motor+1;
    else
        // if we are coming down and we just did HLLL, start over at LLLH
        motor = (motor-1 < 1) ? 4 : motor-1;

    for (int i=1; i<=4; i++) {
        if (i == motor)
            digitalWrite(motorPin[i], HIGH);
        else
            digitalWrite(motorPin[i], LOW);
    }
    delay(DELAY_MOTOR);
}

Thank you two so much! PaulS, yes I do indeed understand that the coding is very inefficient, but I'm a newbie to this stuff.

And swmcdonnell, thank you for having a go at it. I will certainly be looking through the new code yours and see what I can understand, and learn.

Thank you,
Shane

swmcdonnell, what's the "status_pin 13" for? Is it in place for the status LED?

Shane

Is it in place for the status LED?

Yes

Well I cleaned up the comments in the code so I could understand it better, and I'm also planning on making an Instructable. Here's the finished sketch until I get all of the hardware set up to test it:

/**
 * Shane Rienks, with much help from swmcdonnell at http://forum.arduino.cc/ - Laser Harp Sketch BETA - 2/18/14
 * Two octave keys to raise each given note from the 
 * seven remaining strings up one octave or down one octave when the beams are cut.
 * The terms "beams" and "strings" are used interchangeably, as they're the same thing.
 **/

#define CMD 0x90        // You're sending the same command each time.
#define START 0x7f      // This is the velocity to start a note playing.
#define STOP 0x00       // This is the velocity to stop a note playing.

#define SENSOR 10       // Change this value to calibrate your harp's sensor.
#define DELAY_LASER 5   // If you increase this, the laser will be brighter, but the harp will be less fluid.
#define DELAY_MOTOR 3   // This variable affects the speed, and fluidity of the harp.

#define LASER_PIN 7     // Tells the Arduino that the laser is on digital pin 7.
#define STATUS_PIN 13   // To a status LED, via digital pin 13, that indicates when certain actions are being performed by the Arduino, such as when a note is being turned on or off.

int motorPin[5] = {
    0, 8, 9, 10, 11};   /** Digital pins 8, 9, 10, 11 correspond to the ULN2003's pins 4, 3, 2, 1. 
                         * Where:
                         * Digital pin 8  - ULN2003 pin 4
                         * Digital pin 9  - ULN2003 pin 3
                         * Digital pin 10 - ULN2003 pin 2
                         * Digital pin 11 - ULN2003 pin 1
                         **/
int motor = 0;

                        // Direction the motor is moving.
#define UP 1
#define DOWN 2          

                        // Define notes:    
#define NOTES 21
int notes[NOTES] = { 
    0x47, 0x48, 0x71, 0x70, 0x68, 0x66, 0x64, 
    0x63, 0x61, 0x00, 0x00, 0x00, 0x00, 0x00, 
    0x00, 0x00, 0X00, 0X00, 0X00, 0X00, 0X00
};
boolean isPlaying[NOTES];                            

                         /**
                          * noteOn(int cmd, int pitch, int velocity)
                          *     
                          * Plays a note.
                          **/
void noteOn(int cmd, int pitch, int velocity) {
    Serial.write(cmd);
    Serial.write(pitch);
    Serial.write(velocity);
}

                         /**
                          * Void setup.
                          *
                          * Set up pins.
                          **/
void setup() {
                         // Set up pins.
    for (int i=1; i<=4; i++) {
        pinMode(motorPin[i], OUTPUT);
    }
    pinMode(LASER_PIN, OUTPUT);

                         // Turn all notes off.
    for (int i=0; i<NOTES; i++) {
        isPlaying[i] = false;
    }

    Serial.begin(31250);
}

                         /**
                          * Void loop.
                          *
                          * Walk up the strings and then walk back down.
                          **/
void loop() {  
    int beam;

                          // Loop through each beam on the way up.
    for (beam=0; beam<NOTES; beam++) {
        checkAndPlay(beam);
        adjustMotor(UP);
        digitalWrite(LASER_PIN, LOW);
    }

                          // Walk back down the strings.
    for (beam=NOTES-1; beam>=0; beam--) {
        checkAndPlay(beam);
        adjustMotor(DOWN);
        digitalWrite(LASER_PIN, LOW);        
    }

}    


                         /**
                          * void checkAndPlay(int beam)
                          *
                          * If the beam was pressed, play a note and update the playing status.
                          * If the beam wasn't pressed, stop the note playing if it was playing.
                          **/
void checkAndPlay(int note) {
    digitalWrite(LASER_PIN, HIGH);
    delay(DELAY_LASER);      

    boolean hit = (digitalRead(0) >= SENSOR);        
    if (hit) {                                       // Beam was pressed.
        if (!isPlaying[note]) {                      // Note is not already playing.
            digitalWrite(STATUS_PIN, HIGH);
            noteOn(CMD, notes[note], START);
            isPlaying[note] = true;
        }
    } 
    else {                                           // Beam was not pressed.
        if (isPlaying[note]) {                       // Note is playing -- stop the note.
            digitalWrite(STATUS_PIN, LOW);
            noteOn(CMD, notes[note], STOP);
            isPlaying[note] = false;
        }
    }
}       

                           /**
                            * void adjustMotor(int direction)
                            *
                            * Adjusts the stepper motor pins based on whether we're
                            * walking up the harp strings, or going back down.
                            * 
                            * UP:       HLLL - LHLL - LLHL - LLLH (repeats)
                            * DOWN:     LLLH - LLHL - LHLL - HLLL (repeats)
                            **/
void adjustMotor(int direction) {
    if (direction == UP)
                           // If we're going up and we just did LLLH, start over at HLLL.
        motor = (motor+1 > 4) ? 1 : motor+1;
    else
                           // If we are coming down and we just did HLLL, start over at LLLH.
        motor = (motor-1 < 1) ? 4 : motor-1;
                           
                           /** ^^^^^^^ Is a shorter way of saying this:

if (direction == UP) {
        if (motor+1 > 4) {
            motor = 1;
        } 
        else {
            motor = motor + 1;
        }
    } else {
        if (motor-1 < 1) {
            motor = 4;
        } 
        else {
            motor = motor - 1;
        }
    }
                            **/
        for (int i=1; i<=4; i++) {
        if (i == motor)
            digitalWrite(motorPin[i], HIGH);
        else
            digitalWrite(motorPin[i], LOW);
    }
    delay(DELAY_MOTOR);
}

Shane

I am currently working on the same project. I have no problems with the code, but my main issue now is the stepper motdor. I see that you are using ULN2003, so your stepper should be unipolar, right? Can you give me some more information about the motor, because as I said, this is the only thing that stops me finishing my laser harp?
Thanks in advance! :slight_smile: