Play melody without delay()

Hi guys, I have a little script that controls a servo, blinks an LED and plays a tone. The problem is that because the beep1 function is not asynchronous and uses delay(), it needs to run the melody first then the rest of the script continues. Is there a way to avoid this? As in play the melody at the same time as the rest of the action happening?

Here's the code...

#include <Servo.h> 

Servo myservo;  
int pos = 0;    
const int outputPinA = 8;
const int speaker = 12;
long previousMillis = 0;



void setup() 
{ 
  pinMode(outputPinA, OUTPUT);
  Serial.begin(9600);
  myservo.attach(9); 

} 


void loop() 
{ 
  if (Serial.available() > 0) {
    int incomingByte = Serial.read();
    if (incomingByte == 0x01) {
      beep1();
      for(pos = 70; pos < 180; pos += 1)  
        myservo.write(pos);      
      digitalWrite(outputPinA, HIGH);
      delay(3);      
    } 
    for(pos = 180; pos>=70; pos-=1)     
    {                                
      myservo.write(pos);             
      delay(3);                       
    }
    digitalWrite(outputPinA, LOW);
  }

}

void beep1(){  
  tone(speaker,1500,100); 
  delay(20);  
  tone(speaker,1600,100); 
  delay(20);
  tone(speaker,1700,100); 
  delay(20);
  tone(speaker,1800,100); 
  delay(20);
  tone(speaker,1900,100); 
  delay(20);  
  tone(speaker,2000,100); 
  delay(20);  
  tone(speaker,2100,100); 
  delay(20);
  tone(speaker,2000,100); 
  delay(20);  
  tone(speaker,1900,100); 
  delay(20);  
  tone(speaker,1800,100); 
  delay(20);  
  tone(speaker,1700,100); 
  delay(20);
  tone(speaker,1600,100); 
  delay(20);
  tone(speaker,1500,100); 
  noTone(speaker);  
}
  1. take BlinkWithoutDelay example
  2. Add a "counter" so you know which note you're currently playing and another variable to hold how long you're supposed to be playing it for.
  3. Change digitalWrite() to tone()

Would you put the tones in an associative array? the time is constant

it actually appears that this won't work within a running loop because it still needs to complete whatever is called

Updated code with millis

#include <Servo.h> 

Servo myservo;  
int pos = 0;    
const int outputPinA = 8;
const int speaker = 12;
long previousMillis = 0;       
long interval = 20;  
int melody[] = { 
 1900, 2000, 2100, 2000, 1900 };
const int max = 5;

void setup() 
{ 
  pinMode(outputPinA, OUTPUT);
  Serial.begin(9600);
  myservo.attach(9); 

} 


void loop() 
{ 
  if (Serial.available() > 0) {
    int incomingByte = Serial.read();
    if (incomingByte == 0x01) {
      for(pos = 70; pos < 180; pos += 1){  
        myservo.write(pos);      
        digitalWrite(outputPinA, HIGH);
        delay(3);      
      }
      beep1();
      for(pos = 180; pos>=70; pos-=1)     
      {                                
        myservo.write(pos);             
        delay(3);                       
      }
      digitalWrite(outputPinA, LOW);
    }

  }
}

void beep1(){  
  int i = 0;
  while (i<max) { 
    unsigned long currentMillis = millis();
    if(currentMillis - previousMillis > interval) {
      // save the last time you blinked the LED 
      previousMillis = currentMillis;   
      // set the LED with the ledState of the variable:
      tone(speaker,melody[i],100);
      i++; 
    }
  }
}

i needs to be declared static so it won't get reset after every call of the function.

You need to get rid of the delay() in the servo movement

So what is the advantage of declaring speaker and writing speaker in the code rather than just writing

tone(12,1500,100);

?

So what is the advantage of declaring speaker and writing speaker in the code rather than just writing

Suppose you decide to add an Ethernet shield, and can no longer use pin 12. How many changes do you need to make to your code, if you hardcode 12 everywhere? Can you be confident that you got them all?

How many lines need to change if you use a name, instead?

Shouldn't interval be 120? Because of how tone() works, the tone will automatically stop after duration time (100ms in your case), so you want to trigger the next tone 120ms after the previous tone started.