Simulate a PIR sensor with a timer on an Arduino

I made this owl prop using an Arduino Nano and a PIR to trigger the owl's LED eyes to come on and the owl head (connected to a servo) turn its head back and forth a few times. After about 20 seconds or so, the eyes go dim and the owl head returns to its neutral position (90). You can see it in action here:

My code:

**/

# include <ESP32Servo.h> // this library needed specifically for the Arduino Nano
// for Uno use #include <Servo.h>
int pirPin = 2;//digital pin connected to the PIR's output
int pirPos = 12;//connects to the PIR's 5V pin
int ledPin = 12;
int calibrationTime = 30;

Servo myservo; //creates a servo object

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 target = 0;
//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 mypause = 5000;

boolean lockLow = true;
boolean takeLowTime;

// -----------------------------------------------------------------------------
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
    }
    digitalWrite (ledPin, LOW);
    Serial.println ();
    Serial.println ("done");

    while (digitalRead(pirPin) == HIGH) {
        delay (500);
        Serial.print (".");
    }
    Serial.print ("SENSOR ACTIVE");
}

// -----------------------------------------------------------------------------
void move (
    int           target,
    unsigned long dly )
{
    static int pos;

    if ((target - pos) > 0)
        myservo.write (++pos);
    else if ((target - pos) < 0)
        myservo.write (--pos);
    delay (15);
}

// -----------------------------------------------------------------------------
byte          pirState;
unsigned long dly;

void loop ()
{
    move (pos, dly);

    // if the PIR HIGH, select new random value
    byte pir = digitalRead(pirPin);
    if (pirState != pir)  {
        pirState  = pir;        // state change (recognize once)

        if (HIGH == pir)  {
            pos = random (180);
            // dly = random (800, 2800);
            digitalWrite (ledPin, HIGH);   // on
        }
        else  {
            pos = 90;
            dly = 0;
            digitalWrite (ledPin, LOW);   // off
        }
    }
}

I would like to trigger this owl from a timer (presumably using millis function) instead of the PIR. So, say, once a minute the owl would go into its routine and then after another minute go back to "sleep" mode.

I've played around with the code, adding a Millis variable set for 10000 (which is 50 seconds. The LED eyes turn on, stay on for 50 seconds, then go back off. And this on-off cycle repeats every 50 seconds. So far so good.

When I try to put the servo code into the void loop, either nothing happens or the LED eyes still come on—but no movement of the servo. I need some help on which bits of the servo code to move into the void loop, assuming this is what I need to do.

Also, it's important to point out that the PIR's HIGH duration is controlled by the PIR itself. It's currently set for about 20 seconds, but can be adjusted by rotating a little knob on the PIR to be of longer or shorter duration. Ideally, if I'm simulating the PIR using Millis, I could have two separate time parameters—one for the simulated HIGH mode and one for the simulated LOW mode. For example the owl would be asleep for 1 minute and then go into its "routine" which would last for about 20 seconds.

Or since the PIR is still connected to this prop, is it possible to trigger the PIR into HIGH mode using a millis function? That way the PIR is still controlling the duration of HIGH mode, but triggering it is controlled by a millis time event.

Hope this makes sense. Here is my code that successfully makes the eyes come on. The part of the code that controls the servo is still commented out.

# include <ESP32Servo.h> // ESP32 Servo library
//# include <Servo.h>
int pirPin = 2;//digital pin connected to the PIR's output
int pirPos = 10;   //connects to the PIR's 5V pin
int ledPin = 12;
int calibrationTime = 30;
unsigned long previousMillis = 0; // Time of the previous LED toggle
const unsigned long interval = 10000; // Time in milliseconds (50 seconds)
Servo myservo; //creates a servo object

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

// -----------------------------------------------------------------------------
//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);
//    digitalWrite (ledPin, 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
//    } 
//    digitalWrite (ledPin, LOW); // keeps initial state low DOESn't WORK?
//    Serial.println ();
//    Serial.println ("done");
//
//    while (digitalRead(pirPin) == HIGH) {
//        delay (500);
//        Serial.print (".");
//    }
//    Serial.print ("SENSOR ACTIVE");
//}

// -----------------------------------------------------------------------------
bool
move (
    int           target,
    unsigned long dly )
{
    static int newpos;

    if ((target - newpos) > 0)
        myservo.write (++newpos);
    else if ((target - newpos) < 0)
        myservo.write (--newpos);
    delay (dly);

    return target == newpos;
}

// -----------------------------------------------------------------------------
unsigned long dly;

//void loop ()
//{
//    if (HIGH == digitalRead(pirPin))  {     // active 
//        digitalWrite (ledPin, HIGH);        // on
//        if (move (pos, dly))  {             // new pos/dly once reached
//            delay(1000);                // waits for a second //***
//            pos = random (180); //random position from 0-180
//            Serial.println (pos);
//            dly = 17; //was //random (12, 40); // more realistic to have one speed // lower is faster
//        }
//    }
//    else  {
//        digitalWrite (ledPin, LOW);     // low
//        delay(40);
//        move (90, 0); // return to center position
//    }
//}



void setup() {
  //myservo.attach (9);//attaches servo to pin 9 ABRACADABRA
  pinMode(ledPin, OUTPUT);
}

void loop() {
  unsigned long currentMillis = millis(); // Get current time

  if (currentMillis - previousMillis >= interval) { // Check if the interval has passed
    previousMillis = currentMillis; // Update the previous time
    toggleLED(); // Toggle the LED
  }
}

void toggleLED() {
  // Toggle the LED state
  digitalWrite(ledPin, !digitalRead(ledPin));
}

1 Like

10000 ms = 10 seconds

1 Like

Doesn't look good choice for Esp32.

What board you have and how is your hardware wired / powered?

It's an Arduino Nano ESP32.

Here's how it's wired.

That looks like a Nano V3 (ATMEGA328).

Please post a diagram which shows how you connected the Nano ESP32.

Whichever type of Nano you are using, you should never power a servo from it

Also not 50 seconds!

I have two owls using Nanos wired this way, but I hear you that this is a bad idea.

I also hear you that I got my nanoseconds all mixed up. This is totally irrelelevant to my question. But maybe a moderator can please delete this entire thread?

Click the flag icon, select "something else" and ask for the topic to be deleted.

Moderator response:

There is no need to delete the topic.

There might be others in the future with the same question who will benefit from the information that is shared. There are many thousands of people in the Arduino community who need help, and only some dozens who spend their time here providing free assistance. The only way that can work is if each helper is able to help many of those seeking assistance. This is accomplished by each forum post serving as a reference for the years to come. Deleting the post would result in the loss of that ongoing benefit. Causing such harm to our community would result in the permanent suspension of your account, and surely we don't want that!

However, those trying to help you might note that, perhaps, you no longer want help. I suggest you indicate what you want so people know if it's worth helping or not.

Your servo code is currently in the move() function. I don't think you need to move it into loop(). You just need to add some code to simulate the PIR signal.

You could add another function for that:

int simulatePIR() {
  static int currentPIR = LOW;
  static unsigned long lastPIRchange = 0;
  static unsigned long randomTime = 0;

  if (currentPIR == LOW) {
    if (millis() - lastPIRchange > randomTime) {
      currentPIR = HIGH;
      lastPIRchange = millis();
    }
  }
  else {
    if (millis() - lastPIRchange > 20000) {
      currentPIR = LOW;
      lastPIRchange = millis();
      randomTime = 1000UL * random(20, 50);
    }
  }
  return currentPIR;
}

You can then call this function instead of using digitalRead(pirPin).

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.