Owl head servo random sweep. Now I want to add PIR . . .

I made this as a Halloween prop last year. It's an owl whose head rotates randomly left and right, controlled by an Arduino and small servo.

The code to do a random sweep is pretty simple—even I can understand it. It's basically this:

#include <Servo.h>
Servo yservo;

int ypos;

void setup()
{
yservo.attach(10);
}

void loop()
{

ypos = random(180); //generate random value for y-servo
yservo.write(ypos); //y-servo moves to new position

int delayAmount = random(800,2800); //generate number between 500 and 2000
delay(delayAmount); // delay by the random amount

}

But now I want the whole thing to be activated by a PIR motion sensor and for the owl head to stop moving after a certain period of inactivity. So basically I would put my random movements inside the PIR code. if (val == HIGH) than random movements. AND if (val == LOW) stop movements. I'm trying to figure it out on my own, but I'm fairly new at this stuff. Any comments would be greatly appreciated.

Have you got a PIR and can you read its output ?

UKHeliBob:
Have you got a PIR and can you read its output ?

I will do that today. Thanks!

When you have got the PIR reading code working put it at the start of loop() and print the value that the PIR returns. That will enable you to determine what values you should be testing for as a trigger to moving the servo.

Do you want the servo to move continuously whilst the PIR is triggered ?

UKHeliBob:
When you have got the PIR reading code working put it at the start of loop() and print the value that the PIR returns. That will enable you to determine what values you should be testing for as a trigger to moving the servo.

Do you want the servo to move continuously whilst the PIR is triggered ?

Aren't the only values for a PIR either HIGH or LOW? And, yes, I want the servo to move continuously when PIR is HIGH. I want it to stop moving when PIR is LOW.

Regardless, I was testing my PIR today but it was always HIGH. The code I used was this:

/////////////////////////////
//VARS
//the time we give the sensor to calibrate (10-60 secs according to the datasheet)
int calibrationTime = 30;        

//the time when the sensor outputs a low impulse
long unsigned int lowIn;         

//the amount of milliseconds the sensor has to be low 
//before we assume all motion has stopped
long unsigned int pause = 5000;  

boolean lockLow = true;
boolean takeLowTime;  

int pirPin = 2;    //the digital pin connected to the PIR sensor's output
int ledPin = 13;


/////////////////////////////
//SETUP
void setup(){
  Serial.begin(9600);
  pinMode(pirPin, INPUT);
  pinMode(ledPin, OUTPUT);
  digitalWrite(pirPin, LOW);

  //give the sensor some time to calibrate
  Serial.print("calibrating sensor ");
    for(int i = 0; i < calibrationTime; i++){
      Serial.print(".");
      delay(1000);
      }
    Serial.println(" done");
    Serial.println("SENSOR ACTIVE");
    delay(50);
  }

////////////////////////////
//LOOP
void loop(){

     if(digitalRead(pirPin) == HIGH){
       digitalWrite(ledPin, HIGH);   //the led visualizes the sensors output pin state
       if(lockLow){  
         //makes sure we wait for a transition to LOW before any further output is made:
         lockLow = false;            
         Serial.println("---");
         Serial.print("motion detected at ");
         Serial.print(millis()/1000);
         Serial.println(" sec"); 
         delay(50);
         }         
         takeLowTime = true;
       }

     if(digitalRead(pirPin) == LOW){       
       digitalWrite(ledPin, LOW);  //the led visualizes the sensors output pin state

       if(takeLowTime){
        lowIn = millis();          //save the time of the transition from high to LOW
        takeLowTime = false;       //make sure this is only done at the start of a LOW phase
        }
       //if the sensor is low for more than the given pause, 
       //we assume that no more motion is going to happen
       if(!lockLow && millis() - lowIn > pause){  
           //makes sure this block of code is only executed again after 
           //a new motion sequence has been detected
           lockLow = true;                        
           Serial.print("motion ended at ");      //output
           Serial.print((millis() - pause)/1000);
           Serial.println(" sec");
           delay(50);
           }
       }
  }

So, you can see I start with a 30 second delay to allow the PIR to calibrate. I tried 60 seconds too. I may have connected the PIR incorrectly to start with. The connectors aren't labeled on this particular unit. I've ordered a different PIR from Adafruit, hoping for better luck.

Not much to be done until you get a reliable PIR although in the meantime you could fake its output using a switch or even a bare wire taken from the input pin to either 5V or GND

UKHeliBob:
Not much to be done until you get a reliable PIR although in the meantime you could fake its output using a switch or even a bare wire taken from the input pin to either 5V or GND

My new PIR comes today and I'll start playing around with it.

As I think about the sketch, it seems to me I need to be able to command the servo to stop moving when PIR is reading LOW. I think I could tell it to go to position 90 where it will stay until PIR is HIGH again?

Servo myservo;
int pos = 90;

Am I thinking about this correctly?

I was also wondering if I can have the PIR HIGH reading trigger an LED light at the same time as the random sweep. That way I can have the owl's eyes turn on. Likewise, when the PIR reading is LOW, the eyes will turn off. So both actions—the random sweep and the LED lights turning on—are simultaneous. I don't believe I would need a separate PIR for that, right?

Thanks for all your help, HeliBob.

I think I could tell it to go to position 90 where it will stay until PIR is HIGH again?

You can do this

I was also wondering if I can have the PIR HIGH reading trigger an LED light at the same time as the random sweep.

and this too but none of the code must block the free running of the loop() function which means using millis() for timing and no delay()s

My new PIR came yesterday and tested perfectly.

So I now have a motion activated servo sweep.

When the PIR is triggered, it moves the servo around in random sweeps AND an LED light comes on. This will eventually be the owl's eyes. When the LED is off, it's a good visual indicator that the PIR is in ready mode (LOW).

I started to put in a time delay in the code to keep the servo moving for a while, but I ended up just adjusting the time potentiometer on the PIR itself.

I wanted to have the servo go to a set position (int pos = 90;) when the PIR goes from HIGH state to LOW state, but it doesn't seem to work. But maybe you can't really do that with servos?

There are some parts of the code that I don't really understand. I don't think the part at the very end—where it says "motion ended at . . ."—comes into play at all.

/* This code generates a random sweep on a servo after being triggered by a PIR
**/

#include <Servo.h>

Servo myservo;  //creates a servo object
                //a maximum of eight servo objects can be created

int pos = 0;        //variable to store servo position

//amount of time we give the sensor to calibrate(10-60 secs according to the datasheet)

int calibrationTime = 30;

//the time when the sensor outputs a low impulse
long unsigned int lowIn;        

//the amount of milliseconds the sensor has to be low
//before we assume all motion has stopped
long unsigned int pause = 5000; 

boolean lockLow = true;
boolean takeLowTime; 

int pirPin = 2;            //digital pin connected to the PIR's output
int pirPos = 13;           //connects to the PIR's 5V pin
int ledPin = 13;

void setup(){
  myservo.attach(9);    //attaches servo to pin 9
  Serial.begin(9600);    //begins serial communication
  pinMode(pirPin, INPUT);
  pinMode(pirPos, OUTPUT);
  pinMode(ledPin, OUTPUT); // LED light
  digitalWrite(pirPos, HIGH);

  //give the sensor time to calibrate
  Serial.println("calibrating sensor ");
  for(int i = 0; i < calibrationTime; i++){
    Serial.print(calibrationTime - i);
    Serial.print("-");
    delay(800); // gives time for PIR to settle down before reading
  }
  Serial.println();
  Serial.println("done");
 
  
  while (digitalRead(pirPin) == HIGH) {
    delay(500);
    Serial.print(".");     
  }
  Serial.print("SENSOR ACTIVE");
}

void loop(){

  if(digitalRead(pirPin) == HIGH){  //if the PIR output is HIGH, turn servo
 digitalWrite(ledPin, HIGH);   //the led visualizes the sensors output pin state
pos = random(180); //generate random value for y-servo
myservo.write(pos); //y-servo moves to new position

int delayAmount = random(500,2800); //generate number between 500 and 2800
delay(delayAmount); // delay by the random amount                              //to make it go slower, increase the number.
    }
   
    if(lockLow){ 
      //makes sure we wait for a transition to LOW before further output is made
      lockLow = false;           
      Serial.println("---");
      Serial.print("motion detected at ");
      Serial.print(millis()/1000);
      Serial.println(" sec");
      delay(50);
    }        
    takeLowTime = true;
 

  if(digitalRead(pirPin) == LOW){      
 digitalWrite(ledPin, LOW);  //the led visualizes the sensors output pin state
    if(takeLowTime){
      lowIn = millis();             //save the time of the transition from HIGH to LOW
      takeLowTime = false;    //make sure this is only done at the start of a LOW phase
    }

    //if the sensor is low for more than the given pause,
    //we can assume the motion has stopped
    if(!lockLow && millis() - lowIn > pause){
      //makes sure this block of code is only executed again after
      //a new motion sequence has been detected
      lockLow = true;                       
      Serial.print("motion ended at "); //output
      Serial.print((millis() - pause)/1000);
      Serial.println(" sec");
      delay(50);
    }
  }
}

But maybe you can't really do that with servos?

You certainly can do it with servos.

There seems to be a lot of code for what I think you want to do when the PIR output is LOW (not triggered). Can you please explain what should happen under those circumstances ?

UKHeliBob:
You certainly can do it with servos.

There seems to be a lot of code for what I think you want to do when the PIR output is LOW (not triggered). Can you please explain what should happen under those circumstances ?

The servo should do random sweeps when the PIR is HIGH. Once triggered, this lasts about 20 seconds, depending on how I have the time potentiometer on the PIR set. When the PIR goes LOW, the servo stops moving, exactly what's supposed to happen. However, once I get the owl head hooked up, it would be nice to have the servo move to the middle (where the owl head is looking straight ahead) and ready for the next HIGH trigger. Whether this position is 90 or not, I have no idea. I guess I could write a print routine that tells me the position of the servo. Then I would be able to figure out what number corresponds to the owl head looking straight ahead.

But yeah, I guess I don't understand any of the code when PIR is LOW.

if(digitalRead(pirPin) == LOW){
digitalWrite(ledPin, LOW); //the led visualizes the sensors output pin state
if(takeLowTime){
lowIn = millis(); //save the time of the transition from HIGH to LOW
takeLowTime = false; //make sure this is only done at the start of a LOW phase

Save the time of the transition from HIGH to LOW? I'm not sure what the coder had in mind here. I could probably just delete this section and it wouldn't affect the sketch at all.

Try this

void loop()
{
  if (digitalRead(pirPin) == HIGH)
  { //if the PIR output is HIGH, turn servo
    digitalWrite(ledPin, HIGH);   //the led visualizes the sensors output pin state
    pos = random(180); //generate random value for y-servo
    myservo.write(pos); //y-servo moves to new position

    int delayAmount = random(500, 2800); //generate number between 500 and 2800
    delay(delayAmount); // delay by the random amount                              //to make it go slower, increase the number.
  }
  else
  {
    digitalWrite(ledPin, LOW);   //the led visualizes the sensors output pin state
    myservo.write(90); //move servo to centre
  }
}

Beautiful. That worked like a charm. Thanks much for your help. I'm just a hack, but I'm starting to learn.

If you don't mind, what is boolean locklaw?

boolean lockLow = true;
boolean takeLowTime;

To be honest I didn't look at the code too closely because what you wanted was so easy. From a brief look it seems that the idea was to ensure that the servo kept moving for a period of time which you have done by using the PIR's timing mechanism.