timing duration reset with motion sensor

I have a working (and not very elegant) code that reads 2 ePIR motion sensors and turns on a hall light. Currently the code just reads a LOW(mapped analog because it bounces around .1v, not really zero) and uses delay() for a set time. But...if the timing is too short, then I run out of light and must re-trigger the sensor. And... :stuck_out_tongue: if the timing is longer I am wasting electricity.

I am sure there is a way to poll the sensors regularly and, if the lamp is already on, reset the timing duration (ie-59...58...57...PING...59...58...)

Here's the basic basic version -

//created by Will Rogers, June 14, 2011
//Motion Detector Relay Control
//using (2) Zilog ePIRs in hardware mode, pro mini, & relay protoboard from SparkFun

const int relayPin = 13;
const int mdPinA = 14; //pinA0
const int mdPinB = 15;// pinA1
const int delayPot = 16; //pinA2 - used for switching "ON" time for debugging

int mdValA = 1023;//analog approx. HIGH
int mdValB = 1023;
int delayVal = 0;

int testDelay = 5000; //test 5 seconds
int calibrateCount = 10;//count to 10 before LOOP

void setup() {
  
  pinMode(relayPin, OUTPUT);
  pinMode(mdPinA, INPUT);
  pinMode(mdPinB, INPUT);
  pinMode(delayPot, INPUT);
  
  digitalWrite(relayPin, LOW);//make sure relay is OFF
  
  for (int i = 0; i < calibrateCount; i++){
   delay(1000);//v. inelegant
  } //allow the sensor to calibrate
}

void loop() {
  
  delayVal = analogRead(delayPot);
  int delayTime = map(delayVal, 0, 1000, 0, 3); // 10s, 30s, 1min, 5mins
  mdValA = analogRead(mdPinA);//hallway 
  mdValB = analogRead(mdPinB);//entry
  
  if (mdValA <= 205 ) {
    digitalWrite(relayPin, HIGH);
    
    //all of this could go away if I could just restart a 60-second countdown if there is further motion
    switch(delayTime) {
      case 0:
        delay(10000);
        break;
      case 1:
        delay(30000);
        break;
      case 2:
        delay(60000);
        break;
      case 3:
        delay(300000);
        break;
    }
    //delay(testDelay);
    digitalWrite(relayPin, LOW);
  } else if (mdValB <= 205) {
    digitalWrite(relayPin, HIGH);
    switch(delayTime) {
      case 0:
        delay(10000);
        break;
      case 1:
        delay(30000);
        break;
      case 2:
        delay(60000);
        break;
      case 3:
        delay(300000);
        break;
    }
  } else {
    digitalWrite(relayPin, LOW);
    delay(500);
  }
}

Would attachInterrupt() be the way to go or tripTime = millis(); currentTime = millis(); if(currentTime-tripTime <= duration);...

I have a working (and not very elegant) code that reads 2 ePIR motion sensors and turns on a hall light.

Link?

Currently the code just reads a LOW(mapped analog because it bounces around .1v, not really zero)

Typically, a PIR sensor is digital. It has either detected something or it hasn't. They are generally not analog devices.

for (int i = 0; i < calibrateCount; i++){
delay(1000);//v. inelegant
} //allow the sensor to calibrate

It typically does not take 10 seconds for a PIR sensor to power up and start working correctly. Do yours really take this long? Why not just put in a 10 second delay, then? Looping 10 times, doing nothing but delay() seems silly.

  mdValA = analogRead(mdPinA);//hallway 
  mdValB = analogRead(mdPinB);//entry

So, why aren't the pins named hallPin and entryPin? These names would be meaningful. Yours are less so.
Same with the variables holding the values.

//all of this could go away if I could just restart a 60-second countdown if there is further motion

Well, you could, if you got rid of the delay()s.

Would attachInterrupt() be the way to go

No. Your code is not doing anything that would benefit from interrupts. You can't usefully interrupt a delay() call. That is, the delay() is resumed after the interrupt is processed.

or tripTime = millis(); currentTime = millis(); if(currentTime-tripTime <= duration);...

Bingo!

For the sensors - ePIR - SEN-09587 - SparkFun Electronics
For the relay - http://www.sparkfun.com/products/9096
For the arduino mini pro 5v - http://www.sparkfun.com/products/9218
PSU - I tore open a cellphone charger that puts out 5v for the arduino and the relay, used a voltage divider for the Vcc to the sensor, and a regular outlet (there is already a GFCI on the wall)

The ePIR module from Zilog is HIGH at 3.3v until motion is detected, then it drops LOW. Should be digital, but in my testing, as I have it setup, it never quite hits 0v. So I tried to use coding to take an analog value and turn it into a digital one. The datasheet, on Page 8, states that there is a Power-On-Reset function for the onboard computer so the voltage supplied can stabilize. It then says that 20 seconds might be needed for the pir sensor to begin. I found that the ten seconds seems to be enough, though perhaps, I could just let the code run and the sensor would come online when it's ready.

The mdPins are labelled A and B because I wasn't sure which was which once I had the whole she-bang in an enclosure. Labeling them generically seemed to make sense, but I seem to have confused things in my post trying for clarity and adding clutter instead.

I understand that an Interrupt would remove all the delay() commands. My main question, I guess, comes from my lack of understanding trying to use millis(). I read the timestamp when the value goes LOW, then read the current timestamp and compare it to an ONtime value. If less than, fine - leave the relayPin HIGH. If equal or greater, fine - relayPin LOW. I think I can get this code correct and use it to replace all of my delay() calls. BUT, where would I insert the reset of the tripTime? After the timestamp comparison? Do I need to know the current STATE of the relayPin? I feel like I'm just two bits shy of a byte on this concept.

To show my due diligence, and to get some more coding experience, I'd be happy to write some incorrect code that can be critiqued, but I thought it might make for poor learning habits by going the wrong way. Also, I may not understand how easy it is for people with the know-how to read barmy coding without getting tangled up like I do...

I think I have your two bits. Try this:

void loop() 
{
static unsigned long MotionDetectedTime;

mdValA = analogRead(mdPinA); // hallway 
delay(20);  // Let the A2D recover
mdValB = analogRead(mdPinB); // entry
  
if (mdValA <= 205 || mdValB <= 205) 
  {
  digitalWrite(relayPin, HIGH);
  MotionDetectedTime=millis();
  }

if(millis()-MotionDetectedTime > 60000UL)
  digitalWrite(relayPin, LOW);
}

Untested, not compiled.

The ePIR module from Zilog is HIGH at 3.3v until motion is detected, then it drops LOW. Should be digital, but in my testing, as I have it setup, it never quite hits 0v. So I tried to use coding to take an analog value and turn it into a digital one.

What a digital pins sees may not be exactly 0V or exactly 5V. What it outputs is HIGH if the pin sees more than 60% of the reference voltage (3+V on a 5V system), and LOW if the value is less than that.

So, if you connected the output pin (pin 5) to a digital pin, you should get HIGH when there is motion, and LOW when there is no motion.

The datasheet, on Page 8, states that there is a Power-On-Reset function for the onboard computer so the voltage supplied can stabilize. It then says that 20 seconds might be needed for the pir sensor to begin. I found that the ten seconds seems to be enough, though perhaps, I could just let the code run and the sensor would come online when it's ready.

Then, it is good that you wait. The sensor I have is instantaneous, but, then, it doesn't have an MCU in it.

BUT, where would I insert the reset of the tripTime?

Each time through loop, you read the state of the sensors. If the sensor is HIGH, then record the time for that sensor.

After checking the sensors, then check to see if it is time to change the state of the relay ("now" minus "then" is greater than whatever time you want).

Do I need to know the current STATE of the relayPin?

Not really. Think of how you would do this. When you leave the room, you flip the light switch off. You don't actually (have to) check to see if the light is on. Do the same with the relay. Turn it off if it's time. Turn it on if there is motion.

If you do keep track of it's state, you can avoid turning it off if it is already been turned off, or turning it on if it is already on. But, doing so won't harm the relay or the Arduino.

To show my due diligence, and to get some more coding experience, I'd be happy to write some incorrect code that can be critiqued, but I thought it might make for poor learning habits by going the wrong way.

You will certainly learn more if you struggle through it yourself. We'll critique and answer questions and provide snippets as required.

Also, I may not understand how easy it is for people with the know-how to read barmy coding without getting tangled up like I do...

So don't write barmy code...

@wildbill - I'll load this up tonight. I knew there was an easier way.

@PaulS - my sensor surprised me by going active LOW. My initial idea was that an active HIGH sensor would fire off a 555 timer and do it all with hardware, but alas. Also, I had no idea about the digitalRead voltage offset for HIGH vs LOW so I got blinded by my multimeter. I'll try digitalRead() with wildbill's snippet too.

Thanks to you both!