Run loop one time?

Hello,

I have a PIR sensor, that I am using to detect motion. When there is motion detected, I would like to play four notes over my speaker I have hooked up. For some reason, the PIR sensor seems to lag a bit when the motion stops (ie, I wave my hand, the PIR turns red and remains red for some time even after I have removed my hand from the area). The result is my four notes repeating three times.

How can I just get it to run one time?

(An alternative is to remove the ‘lag’ from the PIR, but I haven’t found an answer, and there’s been no reply to my asking here

Here’s my code (I think the issue is in the ‘void GreenOn()’ function):

/*

This will use a PIR, green LED, red LED, and a servo.  When there is no movement detected, the LED will shine RED and the Servo will be at 0 degrees.  

When movement is detected, the greed LED will light and the Servo will rotate 180 degrees.
 */
#include <Servo.h>
#include "pitches2.h"  // this adds the pitches to the script

// notes in the melody:
int melody[] = {
  NOTE_G4, NOTE_G4, NOTE_G4, NOTE_DS4};

// note durations: 4 = quarter note, 8 = eighth note, etc.:
int noteDurations[] = {
  6, 6, 6, 2 };


Servo myServo;  // create a servo object 


int speakerPin = 8;         // set Speaker pin
int servoAngle;              //variable to hold theangle for the servo motor
int IRpin = 2;               // choose the input pin (for PIR sensor)
int greenLed = 4;            // choose the green LED
int redLed = 3;              // choose the red LED
int pirState = LOW;             // we start, assuming no motion detected
int val = 5;                    // variable for reading the pin status


// the setup routine runs once when you press reset:
void setup() {                
  // initialize the digital pin as an output.
  pinMode(greenLed, OUTPUT);
  pinMode(redLed, OUTPUT);
  pinMode(IRpin, INPUT);
  delay(10000);
  myServo.attach(9); // attaches the servo to pin 8 on the arduino.
  myServo.write(1);            //Start the Servo at 0 degrees
  Serial.begin(9600);
}

void GreenOn(){
    digitalWrite(greenLed, HIGH);   // turn the green LED on (HIGH is the voltage level)
    for (int thisNote = 0; thisNote < 4; thisNote++) {  // iterate over the notes of the Melody
      int noteDuration = 1000/noteDurations[thisNote];  //to calculate the note duration, take one second divided by note type (ie quarter note = 1000/4, eighth note is 1000/8, etc)
      tone(speakerPin, melody[thisNote],noteDuration);
      int pauseBetweenNotes = noteDuration * 1.30;      // to distinguish notes, set minimum time between them (the note's duration plus 30% seems to work well)
      delay(pauseBetweenNotes);
      noTone(speakerPin);                               // stop the tone playing
    }
//    Serial.println("Motion detected at: ");           // will use this when I figure out how to determine the time (HH:MM:SS) or some such.
//    myServo.write(150);
  }

void GreenOff () {
  digitalWrite(greenLed, LOW);
//  myServo.write(5);
}
void RedOn() {
    digitalWrite(redLed, HIGH);   // turn the green LED on (HIGH is the voltage level)

}
void RedOff() {
  digitalWrite(redLed, LOW);
}

// the loop routine runs over and over again forever:
void loop() {
  val = digitalRead(IRpin); //read the input value from the IR sensor
  if (val == HIGH) { //check if the input is HIGH (i.e. detects motion)
    RedOff();
    GreenOn();
  }
  else { // if there's no movement, light up RED LED
  GreenOff();
  RedOn();
  }

}

I am aware that putting code in SETUP will run it once, but since I want to play the sounds each time I activate the PIR sensor, I don’t think I want to put in in SETUP, correct?

Thanks for any help/ideas.

Take a reading of millis() startTime = millis(); when PIR goes off, and don't allow the code to react again until time has elapsed:

if (millis() >= startTime + holdOffTime){ // ready for next reading }

CrossRoads: Take a reading of millis() startTime = millis(); when PIR goes off, and don't allow the code to react again until time has elapsed:

if (millis() >= startTime + holdOffTime){ // ready for next reading }

That looks like it'd work! Where though would I put this, in the "GreenOn()" function, or the loop? and what would I define "holdOffTime" as?

Look at the state change detection example in the IDE. You need to trigger the sound when the PIR [u]becomes[/u] LOW, not when it [u]is[/u] LOW. All this involves is checking its current state against it previous state and acting if it has changed and is now LOW when previously it was HIGH.

Mix the time check in here:

void loop() {

  val = digitalRead(IRpin); //read the input value from the IR sensor
  if ((val == HIGH) && (millis() >= startTime + holdOffTime) )  { //check if the input is HIGH (i.e. detects motion) & sufficient time since last turn on
    startTime = millis(); // capture time for next pass
    RedOff();
    GreenOn();
  }
  else { // if there's no movement, light up RED LED
  GreenOff();
  RedOn();
  }

}

holdOffTime - make it however long you'd like. 3000 for 3 seconds between activations?

CrossRoads: Mix the time check in here:

void loop() {

 [deleted to save space]

}



holdOffTime - make it however long you'd like. 3000 for 3 seconds between activations?

Aha, that worked! And yeah, 3000 was the perfect amount of time...the PIR, after detecting my hand, stays RED (detecting?) for about 3 seconds - that's why it was looping over and over, because the PIR was "HIGH" for 3 seconds, so it was repeating for that long. Thank you! :grin:

@UKHeliBob, would that mean I should just move where I have the notes being played? Or would I need some IF THEN statements?

would that mean I should just move where I have the notes being played? Or would I need some IF THEN statements?

You need some logic to detect that the PIR state has changed and that it is now HIGH and was previously LOW

currentVal = digitalRead(IRpin);
if (currentVal == HIGH  && previousVal == LOW) 
{
  // the PIR has been triggered so do something
}
previousVal = currentVal;

UKHeliBob:

would that mean I should just move where I have the notes being played? Or would I need some IF THEN statements?

You need some logic to detect that the PIR state has changed and that it is now HIGH and was previously LOW

currentVal = digitalRead(IRpin);
if (currentVal == HIGH  && previousVal == LOW) 
{
  // the PIR has been triggered so do something
}
previousVal = currentVal;

Thanks, I'll try this as well! :)