Program for PIR activated cat deterrent

Hello!

I’ve been having some trouble with cats fishing at our pond so I am trying to write a program that will shoot a water stream when it detects motion.

The program i am using right now works fine. I can turn the sound on and off and it has an LED to give a visual confirmation on activation. But at the moment the relay (and solenoid) for the water stream water stays on the for the time the PIR input stays high. I would like to control the time the relay stays high after a high input has been given.

I am looking into the milis() function and maybe using a boolean but i am not sure if this is the right direction.

I really could use some advice on what the best approach would be.

Thanks!

// Uses a PIR sensor to detect movement, buzzes a buzzer
// more info here: http://blog.makezine.com/projects/pir-sensor-arduino-alarm/
// 
// based upon:
// PIR sensor tester by Limor Fried of Adafruit
// tone code by michael@thegrebs.com

 
int relay = 4;                // choose the pin for the LED
int inputPin = 9;               // choose the input pin (for PIR sensor)
int pirState = LOW;             // we start, assuming no motion detected
int val = 0;                    // variable for reading the pin status
int pinSpeaker = 7;           //Set up a speaker on a PWM pin (digital 9, 10, or 11)
int LEDPin = 5;
int Switch = 8;
int switchstate = LOW;

void setup() {
  pinMode(relay, OUTPUT);      // declare Relay as output
  pinMode(inputPin, INPUT);     // declare sensor as input
  pinMode(pinSpeaker, OUTPUT);
  pinMode(LEDPin, OUTPUT);
  pinMode(Switch, INPUT);
//  Serial.begin(9600);

  digitalWrite(LEDPin, LOW);

while (millis() < 30000) {             // time for PIR to initialize
      tone(pinSpeaker, 4000, 100);
      digitalWrite(LEDPin, HIGH);
      delay(200);
      digitalWrite(LEDPin, LOW);
      delay(400);
      
      }
  digitalWrite(LEDPin, HIGH);
  tone(pinSpeaker, 2500, 400);
  delay(200);
  tone(pinSpeaker, 3000, 400);
  delay(200);
  tone(pinSpeaker, 4000, 800);
  digitalWrite(LEDPin, LOW);
  delay(1000);
  tone(pinSpeaker, 4000, 100);
  delay(100);
  
 }


void loop(){
  
switchstate = digitalRead(Switch);
  
  val = digitalRead(inputPin);  // read input value
  if (val == HIGH) {            // check if the input is HIGH
    
    digitalWrite(relay, HIGH);  // turn Relay ON
    digitalWrite(LEDPin, HIGH);
    
    if (switchstate == HIGH) { 
    playTone(150, 160);
    }
    delay(150);


    
    if (pirState == LOW) {
      // we have just turned on
   //   Serial.println("Motion detected!");
      // We only want to print on the output change, not state
      pirState = HIGH;
    }
  }
  else {
      digitalWrite(relay, LOW); // turn LED OFF
      digitalWrite(LEDPin, LOW);
      playTone(0, 0);
      delay(300);    
      if (pirState == HIGH){
      // we have just turned off
    //  Serial.println("Motion ended!");
      // We only want to print on the output change, not state
      pirState = LOW;
    }
  }
}



// duration in mSecs, frequency in hertz
void playTone(long duration, int freq) {
    duration *= 1000;
    int period = (1.0 / freq) * 1000000;
    long elapsed_time = 0;
    while (elapsed_time < duration) {
        digitalWrite(pinSpeaker,HIGH);
        digitalWrite(LEDPin, HIGH);
        delayMicroseconds(period / 2);
        digitalWrite(pinSpeaker, LOW);
        digitalWrite(LEDPin, LOW);
        delayMicroseconds(period / 2);
        elapsed_time += (period);
    }

I am looking into the milis() function and maybe using a boolean but i am not sure if this is the right direction.

It is.

When the PIR is activated set a boolean variable to true, save the value of millis() to give you the start time and start making a noise.

Later in loop() check whether the boolean is true and the unit is switched on. If so then check whether the current value of millis() minus the start time is greater than the required period to make the noise. If so then stop the noise and set the boolean to false.

Look at the BlinkWithoutDelay example for inspiration.