Timer in a Motion Triggered Loop

Hi, (I am new to both this forum and the Arudino, please forgive me for any silly mistakes)

I have been looking around for a solution to my project, but seems like there isn t one that exactly fits my need. Hopefully I m not asking something that's already been asked.

Anyways, I am doing a project that involves a toy part (that is connected to a relay), a inferred motion sensor, and 2 LEDs. Basically, what the toy does, is that when the "Press Me"-button (which is hacked and now connects to a relay) is pressed/triggered, the toy will run for about 12s, and then stop.

On the Arduino side, I am putting (1) Two LEDs, which blink in different intervals; (2) The relay mentioned above; and (3) The Inferred sensor, which is the main trigger to both (1) and (2).

The thing is, because I have to initially set the sensor in to read the environment, I cannot int a timer when Arduino starts up.

So I am wondering, is there a way I could put a timer only in the loop of the LED blinks, so they would blink for only a certain period of time when the motion sensor is triggered, so when the blinking and the toy stops running, motion sensor would come on again, and read the environment again, and do the whole loop again only when there is motion around?

I hope that make sense!!

And here's my code, and apparently my version of Cutout does not work at all.

const int relay = 2; //int const for relay in pin 13
const byte led_1 = 11; //int const for led1 in pin 11
const byte led_2 = 12; //int const for led2 in pin 12

//Time periods of blinks in milliseconds
const unsigned long led_1_interval = 133UL;
const unsigned long led_2_interval = 78UL;

int msensor = A0; //motion sensor in input A0
int mspower = 10; //motion sensor power at pin 10
int Cutout = 15000; //Define cutout time
unsigned long led_1_timer = 0; //timer for each led
unsigned long led_2_timer = 0; //blink without delay
unsigned long startTime = 0;
int led_1_state = LOW;
int led_2_state = LOW;

void setup () { 
  Serial.begin(9600);
  pinMode(relay, OUTPUT);
  pinMode(led_1, OUTPUT);
  pinMode(led_2, OUTPUT);
  pinMode(mspower, OUTPUT); 
  pinMode(msensor, INPUT);
  led_1_timer = millis (); //blink without delay timer
  led_2_timer = millis (); //blink without delay timer
  
}

void loop () {
int startTime = millis ();  
digitalWrite (led_1, LOW);
  digitalWrite (led_2, LOW); 
  digitalWrite (mspower, HIGH); //turning on motion sensor
  delay(5000); //stabalizing motion sensor 
  int motion = digitalRead(msensor);
  if (motion == HIGH) {
    digitalWrite (mspower, LOW); 
    if ( millis () - led_1_timer >= led_1_interval ) {
      if (led_1_state == LOW)
      led_1_state = HIGH;
      else
      led_1_state = LOW;
      digitalWrite (led_1, led_1_state);
      led_1_timer = millis ();
    }
    if ( millis () - led_2_timer >= led_2_interval ) {
      if (led_2_state == LOW)
      led_2_state = HIGH;
      else
      led_2_state = LOW;
      digitalWrite (led_2, led_2_state);
      led_2_timer = millis ();
    }
    digitalWrite (relay, HIGH); //running the toy
    delay (78);
    digitalWrite (reply, LOW);
    
//turn loop off after 15s
       if(millis()-startTime > Cutout){
          digitalWrite (led_1, LOW);
          digitalWrite (led_2, LOW);
          digitalWrite (relay, LOW);
          startTime = millis ();
    
      }
  }
}

And this is not working:

int startTime = millis ();  //in loop
      
    if(millis()-startTime > Cutout){
          digitalWrite (led_1, LOW);
          digitalWrite (led_2, LOW);
          digitalWrite (relay, LOW);
          startTime = millis ();
}

More information about this relay setup, please. What's driving the coil? What are the contacts hooked up to?

What toy are you hacking,? What motion sensor are you using? What does the motion sensor do for this toy? When you say the toy "runs", what does that entail?

I'd like to help, but the description is quite a bit confusing to me. Rewrite it in the style of "When the PRESS ME! button is pressed, I want X to happen" and "When the motion sensor is triggered, Y should happen".

Also, fix the indentation in your code. Those if-else statements are a pain to look at with everything at the same level.

The relay is just a simply 5v relay.

The coil is running by the Arduino. And the Sensor is the PIR Motion Sensor (passive Infrared) from Radio Shack.
When the PIR Motion Sensor sensor motion around, the Arduino will trigger the relay coil, setting off the toy.
The toy is a bat thing I found from Walgreens, when the indicated button is pressed, its motor would running, and the whole toy would roll around, and make sound. After a duration of 12s, the toy will then stop. The toy has its own batteries and switch, apart from the start button, and that s how it is connected to the relay on the other side.

Hi carahence, welcome to the forum. I think your program clearly explains what it is you want. I've been reading it for a while, trying to understand it, but there are several parts of it that I doubt would work. In example, your startTime starts before the sensor is even on. This should start after the sensor is activated and the toy starts rolling around, correct?

I tested your if and else setup, and it doesn't work here. Here is how you should code if and else: if() { } else{ }

You seem to know just about what you need to know to make this work, but the code is just a littlebit "messy", sort of. If I was you I would leave the computer, think my program through in my head until I got all the pieces put together, then recode it from scratch. That's how I solve these situations anyway, as it tends to be more time-effective overall.

Pardon my poor explaining, as I'm very tired atm. Would love to reprogram it for you, but as the rookie I am that would take time. If you haven't fixed it by tomorrow, I might try again with a clear head.

Check out while() loops, as I think this will come handy to your code. Good luck!

First thing to do would be to split up that loop into functions so that it's easier to read. For example:

void turn_on_msensor_power()
{
  digitalWrite(mspower, HIGH);
  delay(5000); // Stabilizing motion sensor
}
void turn_off_msensor_power()
{
  digitalWrite(mspower, LOW);
}
void blink_led_1()
{
  static led_timer = millis();
  byte led_1_state;
  if ( millis () - led_1_timer >= led_1_interval ) {
      if (led_1_state == LOW)
        led_1_state = HIGH;
      else
        led_1_state = LOW;
      digitalWrite (led_1, led_1_state);
      led_timer = millis ();
    }
}

Functions are just for reusable code, they also help readability and can condense your loop function down so it's easier to read and understand.

Functions are just for reusable code

I think you left out a not, here.

Thank you for your time and reply.

And thanks for showing me how to get my code a little bit more organized, I think that 's exactly what my professor asked me to do when I ask him to help me on my code, I just didnt understand it…

Excuse me for my limited knowledge, I am still new to Arduino that I am not too sure what I should do next. I did look into the while loop a bit though, but I am not sure how I should apply it. The Arduino while() loop example had a calibrate() expression in it as well, which I am not sure what that is, nor does it seems to work out when I was trying the code myself.

Would you suggest me to put the while loop into my program as the trigger to the whole thing - something like:

while (digitalRead(msensor) == HIGH) {
    //action here;
  }

or, did you mean that I should set the while as a timer (though I am not too sure how)…

current_time = millis();
while (millis () - current_time < 15000) {
   //LEDs continue blinking;
}

I have made a entry in the playground you might be interested in. Its about finite state machines, the tool that solves your problem.
You find it here:
http://playground.arduino.cc//Code/SMlib

Here is some code that does what i suppose you want to do using that library (i changed some of the assignments for practical reasons):

#include <SM.h>


SM Machine(SensorOn, Stab);//main state machne
SM BlinkLed1(Led1on);
SM BlinkLed2(Led2on);

const int relay = 2; //int const for relay in pin 13??????
const byte led_1 = 11; //int const for led1 in pin 11
const byte led_2 = 12; //int const for led2 in pin 12
const int msensor = 3; //motion sensor in input  *Changed*
const int mspower = 10; //motion sensor power at pin 10
//Time periods of blinks in milliseconds
const unsigned long led_1_interval = 133;
const unsigned long led_2_interval = 78;

int Cutout = 15000; //Define cutout time


void setup(){ 
  Serial.begin(115200);
  pinMode(relay, OUTPUT);
  pinMode(led_1, OUTPUT);
  pinMode(led_2, OUTPUT);
  pinMode(mspower, OUTPUT); 
  pinMode(msensor, INPUT);
  Serial.println("Setup");
}//setup()

void loop(){
  EXEC(Machine);
}//loop()


State SensorOn(){
  digitalWrite(mspower, HIGH); //turning on motion sensor
  digitalWrite(relay, LOW);//Make sure realy is off
  Serial.println("Sensor On");
  digitalWrite(led_1, LOW);
  digitalWrite(led_2, LOW);
  BlinkLed1.Set(Led1on);//reset blinkmachine
  BlinkLed2.Set(Led2on);//reset blinkmachine
}//SensorOn()

State Stab(){
  if(Machine.Timeout(5000)) Machine.Set(CheckH, CheckB);//stabalizing motion sensor
}//Stab()

State CheckH(){
  Serial.println("Checking");
}//CheckH

State CheckB(){
  if(digitalRead(msensor)) Machine.Set(RunToyH, RunToyB);
}//Check()

State RunToyH(){
  Serial.println("RUN");
  digitalWrite (relay, HIGH);//run toy
  digitalWrite(mspower, LOW);//turn sensor off
}//RunToyH

State RunToyB(){
  //relay is on, blink led asynchronously
  EXEC(BlinkLed1);
  EXEC(BlinkLed2);
  if(Machine.Timeout(Cutout)) Machine.Set(SensorOn, Stab);
}//RunToyB  

State Led1on(){
  digitalWrite(led_1, HIGH);
  if(BlinkLed1.Timeout(700)) BlinkLed1.Set(Led1off);
}//Led1on()

State Led1off(){
  digitalWrite(led_1, LOW);
  if(BlinkLed1.Timeout(700)) BlinkLed1.Set(Led1on);
}//Led1off()

State Led2on(){
  digitalWrite(led_2, HIGH);
  if(BlinkLed2.Timeout(600)) BlinkLed2.Set(Led2off);
}//Led1on()

State Led2off(){
  digitalWrite(led_2, LOW);
  if(BlinkLed2.Timeout(600)) BlinkLed2.Set(Led2on);
}//Led1off()

Thank you very much for your reply nilton61.

I really appreciate the rephrase. However, since my understanding of the Arduino is still limited, and I still need a little bit of time to understand the whole finite state machines thing, I am not sure if I am allow to use it in my projects. (Our professor actually want us to explain everything we are doing in our codes, which is due very soon.)

so the only thing I finally figured out is the while() function.

void blink led 1 for a period of time () {

startTime = millis();
    
    while ( millis() - startTime < CutoutTime ){
      
      if ( millis () - led_1_timer >= led_1_interval ) {
        if (led_1_state == LOW){
          led_1_state = HIGH;
        }
        else{
          led_1_state = LOW;
        }
        digitalWrite (led_1, led_1_state);
        led_1_timer = millis ();
      }
}

The only last thing I figured, however, is that the loop seems to “overflow” (if that 's the right word) after about 4 loops.
In other word, after triggering the motion sensor for about 3-4 times, both LEDs would light up simultaneously for about 3-5s, and then turn off forever unless I reset the Arduino board.
Is there a way to fix that? Or may I ask, what I actually costing that?

carahence: The relay is just a simply 5v relay.

The coil is running by the Arduino. And the Sensor is the PIR Motion Sensor (passive Infrared) from Radio Shack. When the PIR Motion Sensor sensor motion around, the Arduino will trigger the relay coil, setting off the toy. The toy is a bat thing I found from Walgreens, when the indicated button is pressed, its motor would running, and the whole toy would roll around, and make sound. After a duration of 12s, the toy will then stop. The toy has its own batteries and switch, apart from the start button, and that s how it is connected to the relay on the other side.

Give a link to the sensor you have, don't make us guess whether or not we're looking at the right thing. A product page is a good start, but a datasheet is best.

This still isn't a very good explanation. I think what you're saying is that you've replaced the start button with the relay contacts, but I'm not sure. And you still haven't explained what you want your Arduino to do Here is my best guess:

1) Set up the motion sensor to detect motion 2) When motion is detected, close the relay for a short period of time to simulate pressing the start button on the toy. Start blinking two LEDs at different rates for 15 seconds. 3) After the 15 seconds have passed, rearm the motion sensor and prepare it to detect motion again.

Regarding your last post, the problem with using a while loop like that in a function is that it becomes blocking; your microcontroller will be stuck in the loop until it times out. This may or may not be what you want. I assume it's not what you want, because you also want LED 2 to blink at the same time.

Hi Jiggy-Ninja!

Thank you for your input, and excuse me for my communication skills.
But you got basically everything correct.
And yeah I think that’s what happened, so I did ended up using an if loop for the LEDs,
and only the while loop for the timeout.

Thank goodness I did manage to pull everything off finally~

Again, except the fact that sometimes error would occur in the LEDs’ blinks, which I still dont get,
my loop is basically doing everything I want.
Perhaps that s the “blocking” you are talking about?

What I got as solution:
Basic initial stuff -

const int relay = 2;
const int msensor = 6; //digitalRead
const int mspower = 7;
const byte led_1 = 11;
const byte led_2 = 12;

const unsigned long led_1_interval = 133UL;
const unsigned long led_2_interval = 78UL;

unsigned startTime = 0;
unsigned led_1_timer = 0;
unsigned led_2_timer = 0;

int led_1_state = LOW;
int led_2_state = LOW;
int CutoutTime = 12500;

Setups-

void setup (){
  Serial.begin(9600);
  pinMode(relay, OUTPUT);
  pinMode(led_1, OUTPUT);
  pinMode(led_2, OUTPUT);
  pinMode(mspower, OUTPUT);
  pinMode(msensor, INPUT);
  led_1_timer = millis ();
  led_2_timer = millis ();
}

Main Loop For initiating PIR Motion Sensor http://content.solarbotics.com/products/datasheets/pirsensor-v1.2.pdf -

digitalWrite(led_1, LOW);
  digitalWrite(led_2, LOW);
  delay(3000);
  int motion = digitalRead(msensor);

Loop when motion is high -

  if (motion == HIGH) {
    digitalWrite (mspower, LOW); 
    digitalWrite (relay, HIGH);
    delay(500);
    digitalWrite (relay, LOW);
    startTime = millis();
    
    while ( millis() - startTime < CutoutTime ){
       LEDs Blink;
       }
    }

LED Blinks _1 & _2 -

 if ( millis () - led_1_timer >= led_1_interval ) {
        if (led_1_state == LOW){
          led_1_state = HIGH;
        }
        else{
          led_1_state = LOW;
        }
        digitalWrite (led_1, led_1_state);
        led_1_timer = millis ();
      }

The central point here is to look at things from the right way. That is in order to get a good working solution the description of the problem must be adequate. Until that is done you should NOT focus on a solution but on getting the description as detailed and accurate as possible. There are diffent tools and techniques for that:

  • Timing charts
  • State diagrams
  • Seqential flow charts (NOT normal flowcharts in this case, because of the concurrent nature of the problem) .

No matter wich one you use, you soon will find out that the underlying problem is to blink the two leds simultainiously at different rates. This means you cannot use a blocking construct (=one that occupies the cpu for some time) like the delay() function or similar. It seems that you got this one right. Your solution looks workable. Maybe there are some kinks to be ironed out but basically it is sound. Some constructs can be simplified like this:

if (led_1_state == LOW){
  led_1_state = HIGH;
}else{
  led_1_state = LOW;
}

This can be much shorter and clearer written as:

led_1_state = !led_1_state;