Implement a timer into code.

Hello everybody !!

After few months and learning a lot about shift registers, I finally got my sketch working .

This is just to control LED strips on stairs in a sequential manner.

From the hardware point of view it has two light gate IR photodetectors activated when going up or down.

The last thing I would like to add to the code is a "simple" timer .

This timer should come into action when a person going up or down change his mind and goes back without activating the other extreme sensor, so that after a certain amount of time/seconds the led strips switch OFF.

What I have to change/add to may sketch and how ?

Any help is appreciated .

Franky

#include <ShiftRegister74HC595.h>
ShiftRegister74HC595 sr (1,4, 6, 5); 
int del = 80;

#define Interruttore_SOPRA 12  // il pin 12 è usato per il sensore SUPERIORE
#define Interruttore_SOTTO 11  // il pin 11 è usato per il sensore SUPERIORE

// Variabili
int INTERRUTTORE_A = 0;
int INTERRUTTORE_B = 0;
int bAccesiA = false;  
int bAccesiB = false;


void AccendiA()
{                           // switch on LEDs uppwards


  for (int i = 0; i < 8; i++) {

    sr.set(i, HIGH); // set single pin HIGH
    delay(del); 

  }

}

void AccendiB()
{                   // switch on LEDs downwards

  for (int i = 8; i >=0; i--) {

    sr.set(i, HIGH); // set single pin HIGH
    delay(del); 
  }
}

void SpegniA ()
{                           // switch of LEDs 

  for (int i = 0; i <= 8; i++) {


    sr.set(i, LOW); // set single pin LOW
    delay(del); 
  }
}

void SpegniB ()
{                 // switch of LEDs reverse order


  for (int i = 8; i >=0; i--) {


    sr.set(i, LOW); // set single pin LOW
    delay(del); 
    
  }
}

void setup() {


  pinMode(Interruttore_SOPRA, INPUT);
  pinMode(Interruttore_SOTTO, INPUT);

  Serial.begin(9600);
  Serial.println("avvio");

}
void loop()
{
  INTERRUTTORE_A = digitalRead(Interruttore_SOPRA);  // IR photodetector A
  INTERRUTTORE_B = digitalRead(Interruttore_SOTTO);  // IR photodetector B


  if (INTERRUTTORE_A == HIGH)  // Upper IR detector activated
  {delay(100);
    if (bAccesiB==false)
    {
      bAccesiA=true ;
      AccendiA ();
    }
    else // (bAccesiB==true)
    {
      bAccesiB=false;
      SpegniB ();
    }
  }
  if (INTERRUTTORE_B  == HIGH)  // Lower IR detector activated
  {delay(100);
    if (bAccesiA==false)
    {
      bAccesiB=true;
      AccendiB();
    }  
    else  // (bAccesiA==false)
    {
      bAccesiA = false;
      SpegniA ();

    }
  }
/*  Serial.print("INTERRUTTORE_A = ");
  Serial.println(INTERRUTTORE_A);
  Serial.print("INTERRUTTORE_B = ");
  Serial.println(INTERRUTTORE_B);
  Serial.print("bAccesiA = ");
  Serial.println(bAccesiA);
  Serial.print("bAccesiB = ");
  Serial.println(bAccesiB);
  Serial.println(" ");
  delay (0);
*/

}

The demo several things at a time illustrates the use of millis() to manage timing.

Save the value of millis() when the sensor is activated and keep checking if the difference between the latest value of millis() and the saved value exceeds the permitted interval - if it does, switch off.

...R

Try putting this at the end of your loop( ) (untested):

    if( !INTERRUTTORE_A && !INTERRUTTORE_B ) {
        if( expecting_count == 0 )              // trigger timing start
            expecting_count = millis( );

        if( (millis( ) - expecting_count) >= EXPECTED ) {     // check time
            expecting_count = 0;                // re-arm for trigger
            /* DO YOUR LED THING */             // whatever
        }
    }
    else
        expecting_count = 0;                    // finally made destination

Define expecting_count in setup( )
EXPECTED is a constant of your choice.
The "timer" will keep expiring and executing your LED code at the EXPECTED period as long as you never reach the destination. I don't think that's a problem for you.

Robin2:
The demo several things at a time illustrates the use of millis() to manage timing.

Save the value of millis() when the sensor is activated and keep checking if the difference between the latest value of millis() and the saved value exceeds the permitted interval - if it does, switch off.

...R

Thanks Robin for the link , knowledge is never enough.

boolrules:
Try putting this at the end of your loop( ) (untested):

    if( !INTERRUTTORE_A && !INTERRUTTORE_B ) {

if( expecting_count == 0 )              // trigger timing start
           expecting_count = millis( );

if( (millis( ) - expecting_count) >= EXPECTED ) {     // check time
           expecting_count = 0;                // re-arm for trigger
           /* DO YOUR LED THING */             // whatever
       }
   }
   else
       expecting_count = 0;                    // finally made destination




Define expecting_count in setup( )
EXPECTED is a constant of your choice.
The "timer" will keep expiring and executing your LED code at the EXPECTED period as long as you never reach the destination. I don't think that's a problem for you.

Bool a couple of questions for you .

How do I declare expecting_count in setup ????

The code you suggest Do I need to repeat it twice ?

as ...... if( !INTERRUTTORE_A && !INTERRUTTORE_B ) {

and ......if( !INTERRUTTORE_B && !INTERRUTTORE_A ) {

Franky

How do I declare expecting_count in setup

unsigned long expecting_count = 0; // (not a code tag but it's only one line )

The code you suggest Do I need to repeat it twice ?

No. The logic is a little hard to follow here. The problem is short-circuiting of the argument processing.

Just to put the problem in front of us I'll express it this way:
if( !A && !B ) {
We want to start the timing when BOTH A and B are LOW, false, 0.

If !A is false, the expression short-circuits because the expression can not be true no matter what B is. So we don't enter the if statement. But if !A is false, we know we are at sensor A so we don't need the timer running.

If we're at sensor B, !A is true, the expression will be further processed for the state of B. !B is false so the expression is false and we do not enter the if statement.

If we are not at A or B sensors, !A is true, B is processed, !B is true, the expression is true and the if statement is entered.

I hope so anyway. :o Test it for me.

And you can probably do: #define EXPECTED <some_number>
at the top of the sketch like after the #include. (No semi-colon at the end of a #define)
Or you can also put it in setup( ) as an unsigned long .

Bool,
just my thought here .

We want the TIMER starting to count when one of the sensor is activated ( meaning TRUE or 1) non when both are inactive ( FALSE or 0).

To be clear , if I start going upstairs activating the DOWN sensor and in the middle of the climbing for some reason I come down backward without activating the second sensor ( or vice verse) , the LEDS after some X seconds needs to switch off .

I dunno if I was clear here .....?!?!?! :fearful:

Franky

From the original:

This timer should come into action when a person going up or down change his mind and goes back without activating the other extreme sensor, so that after a certain amount of time/seconds the led strips switch OFF.

I assumed your sensors were at the top and bottom. So if there is a "person going up or down" he is not at either sensor, so "after a certain amount of time" implies a timer started when he left a sensor.
Where is the "direction" indicator. Maybe I missed it. I have no way of telling when the person "change his mind".
You just need to detect if he came back to the same sensor? Why then is any timer involved?

boolrules:
From the original:I assumed your sensors were at the top and bottom. So if there is a "person going up or down" he is not at either sensor, so "after a certain amount of time" implies a timer started when he left a sensor.
Where is the "direction" indicator. Maybe I missed it. I have no way of telling when the person "change his mind".
You just need to detect if he came back to the same sensor? Why then is any timer involved?

Yes the sensors are at the TOP and BOTTOM of the stairs.

Well not to be too much complicated but to keep as simple as possible, we do not need to detect for a second time the person passing in front of the sensor he just activated , we just want to switch the leds off after a certain amount of seconds ( we assume climbing all the stair takes X time/seconds).

What happen should be :

A) the BOTTOM sensor on the first step is activated by a person ( say going upward).
B) the person after few steps comes back.
c) without activating the second sensor the ON LEDs go OFF after x seconds ( X seconds has to be >= the amount of time needed to climb all the stairs ).

D) otherwise, if he/she continue going upward and activates the TOP sensor the LEDs go OFF.

I'm sweating here :sweat_smile:

Franky

Looks to me like by testing your bAccesiA/B signals you can figure out that you came back to the start. Then you just need another delay( X ) and do your LEDs.

I'm sorry but I'm just not clear about this.

You're just about at the point where you need to implement this as a State Machine. I'd look into that.

OK, what happens if two people try to use the stairs at the same time, and they are not walking together?

What happens if one person tries going up while the other person is going down?

What happens if one person is halfway up the stairs when another person tries walking up the stairs?

This gets very complicated very quickly.

odometer:
This gets very complicated very quickly.

I think the complexity can be avoided by simply putting a time limit on how long the lights are on. That means starting the timer when the lights go ON, regardless of where the person started, or how many people started.

And that is what I suggested in Reply #1

...R

Robin2:
I think the complexity can be avoided by simply putting a time limit on how long the lights are on. That means starting the timer when the lights go ON, regardless of where the person started, or how many people started.

And that is what I suggested in Reply #1

...R

BANG !!!! Robin you got it . This just what the sketch should do avoiding too many complications. :wink:

This is what I did . But the LEDs once ON stay ON and don't go OFF . I don't know why ????

#include <ShiftRegister74HC595.h>
ShiftRegister74HC595 sr (1,4, 6, 5); 
int del = 80;

#define Interruttore_SOPRA 12  // il pin 2 è usato per il sensore SUPERIORE
#define Interruttore_SOTTO 11

#define TEMPO_MINIMO 5000
unsigned long inicio = 0;
// Variabili
int INTERRUTTORE_A = 0;
int INTERRUTTORE_B = 0;
int bAccesiA = false;  
int bAccesiB = false;


void AccendiA()
{                           // accensione LEDs 


  for (int i = 0; i < 8; i++) {

    sr.set(i, HIGH); // set single pin HIGH
    delay(del); 

  }

}

void AccendiB()
{                   // accensione LED 1

  for (int i = 8; i >=0; i--) {

    sr.set(i, HIGH); // set single pin HIGH
    delay(del); 
  }
}

void SpegniA ()
{                           // LEDs OFF   1 to 8

  for (int i = 0; i <= 8; i++) {


    sr.set(i, LOW); // set single pin LOW
    delay(del); 
  }
}

void SpegniB ()
{                 // LEDs OFF   8 to 1


  for (int i = 8; i >=0; i--) {


    sr.set(i, LOW); // set single pin LOW
    delay(del); 
    
  }
}

void setup() {


  pinMode(Interruttore_SOPRA, INPUT);
  pinMode(Interruttore_SOTTO, INPUT);

  Serial.begin(9600);
  Serial.println("avvio");

}
void loop()
{
  INTERRUTTORE_A = digitalRead(Interruttore_SOPRA);
  INTERRUTTORE_B = digitalRead(Interruttore_SOTTO);


  if (INTERRUTTORE_A == HIGH)  // TOP sensor activated 

  {inicio = millis();        // starting counter

    if (bAccesiB==false)
    {
      bAccesiA=true ;
      AccendiA ();
    }
    else // (bAccesiB==true)
    {
      bAccesiB=false;
      SpegniB ();
    }
    
   // }else{
    if ( (millis() - inicio) >= TEMPO_MINIMO   ) {   // check for the time limit
               
       bAccesiA=false;
      SpegniA ();              // switch OFF the LEDS
      
      }
  }
  if (INTERRUTTORE_B  == HIGH)  //BOTTOM sensor activated

  {inicio = millis();    // starting counter

    if (bAccesiA==false)
    {
      bAccesiB=true;
      AccendiB();
    }  
    else  // (bAccesiA==false)
    {
      bAccesiA = false;
      SpegniA ();

    }
   // }else{
    if ( (millis() - inicio) >= TEMPO_MINIMO  ) {   // check for the time limit
               
       bAccesiB=false;
      SpegniB ();              // switch OFF the LEDS
      
      }
  }
/*  Serial.print("INTERRUTTORE_A = ");
  Serial.println(INTERRUTTORE_A);
  Serial.print("INTERRUTTORE_B = ");
  Serial.println(INTERRUTTORE_B);
  Serial.print("bAccesiA = ");
  Serial.println(bAccesiA);
  Serial.print("bAccesiB = ");
  Serial.println(bAccesiB);
  Serial.println(" ");
  delay (0);
*/

}

There is something wrong I'm doing here .

Franky

void AccendiB()
{                   // accensione LED 1

  for (int i = 8; i >=0; i--) {

    sr.set(i, HIGH); // set single pin HIGH
    delay(del);
  }
}

So, first pin number 8, goes high, then number 7, 6, 5, 4, 3, 2, 1, and finally pin number 0.
... so there are 9 pins, numbered 0 through 8, is that it?

odometer:

void AccendiB()

{                   // accensione LED 1

for (int i = 8; i >=0; i--) {

sr.set(i, HIGH); // set single pin HIGH
   delay(del);
 }
}



So, first pin number 8, goes high, then number 7, 6, 5, 4, 3, 2, 1, and finally pin number 0.
... so there are 9 pins, numbered 0 through 8, is that it?

ODO , you are right .

The code line should be ..... for (int i = 7; i >=0; i--) {

Thank you for noticing the bug.

I think this code

    if ( (millis() - inicio) >= TEMPO_MINIMO   ) {   // check for the time limit
              
       bAccesiA=false;
      SpegniA ();              // switch OFF the LEDS
      
      }

should be the last thing in loop() and should be outside any other IF statements - and, with a little modification you only need one copy of it to deal with UP and DOWN.

...R

#define Interruttore_SOPRA 12  // il pin 12 è usato per il sensore SUPERIORE
#define Interruttore_SOTTO 11  // il pin 11 è usato per il sensore SUPERIORE

I think that there is maybe a mistake in the comments there.

State machines are fairly easy to code. Hope this makes sense. Also, I tend to split up everything into functions so my main() program is usually under 10 lines.

 #define IDLING        1     // Just Powered Up
#define FILLING       2     // Stay here for 10 sec. if water low
#define PUMPING       3     // Run Pump if enough water
#define FAULT         9
#define WATER_LOW     2
#define HRT_BEAT      13


// dimension global boolean array of sensors
// Switches and Sensors are read into this boolean array from 0 to 9
 boolean  T[10] = {0,0,0,0,0,0,0,0,0,0};
 boolean Timeout;
 byte PresentState=1;              // Present State
 byte NextState;                   // Next State

// Initialize key Elements
void setup() 
  {
  Serial.begin(9600);

// Set Data Direction of following INPUTS & OUTPUTS, Enable Input Pullups
// Inputs are Low-True
  pinMode (WATER_LOW,INPUT_PULLUP); 
// Outputs
  pinMode (HRT_BEAT,OUTPUT);             // Status LED

  NextState = 1;                           // Initial State
}// End of Setup ------------------------------------------


// ***************** MAIN PROGRAM ***************************
void loop() {
  Sequencer();    // Update States
  PresentState=NextState;
  digitalWrite (HRT_BEAT,Timeout);

// Comment out Print Section once debugged!
//-------------------------------------------
  Serial.println("SEQUENCER   ----- V0.01");    
  Serial.print("PresentState: ");
  Serial.print(PresentState);
  Serial.print("   Timeout: ");
  Serial.print(Timeout);
  Serial.println("");
  Serial.println("");
  delay(140);
// End of Print Section
} // End MAIN Loop

    



// FUNCTION Sequencer *** Calculate New State based on Present State * Inputs
// 
void Sequencer (void)      //--- Check Present State 
// Both PresentState and NextState are Globals, and only NextState is computed
  {
  switch (PresentState)
    {
//IDLING State (1)
    case IDLING:                    //Idle State (RETURNED) (1)
// Here is where Actions are placed
    Timeout=BlinkerDelay(1000);

// Here is where conditions to transition are placed
    if (Timeout) 
      NextState = 2;
    else

// If nothing changes, we stay in the present state
      NextState = PresentState;                      // Nothing Changed
    break; 

//FILLING State (2)
    case FILLING:             // FILLING State (FILLING) (2)
    Timeout=BlinkerDelay(1000);
    if (Timeout) 
      NextState = 3;
    else
      NextState = PresentState;                      // Nothing Changed
    break; 
//PUMPING State (3)
    case PUMPING:                       
    Timeout=BlinkerDelay(1000);
    if (Timeout) 
      NextState = 9;
    else
      NextState = PresentState;                               // Nothing Changed
    break;
 
//FAULT State (9)
    case FAULT:                       
    Timeout=BlinkerDelay(1000);
    if (Timeout) 
      NextState = 1;
    else
      NextState = PresentState;                                // Nothing Changed
    break;
  } //CASE
} // Sequencer



// ************* FUNCTION BlinkerDelay ****************
// Non-Blocking Delay, returns 1 when timed out
boolean BlinkerDelay(unsigned long dly) 
  { 
  static unsigned long BlinkerStartCount=0;
  static boolean BlinkerTimeout = LOW;
  static boolean BlinkerInit = HIGH;

    if (BlinkerInit ==LOW) 
     {                                     // Run Once Code 
      BlinkerTimeout = LOW;                // Reset Flag
      BlinkerStartCount = millis ();       // Snapshot of MILLIS count
      BlinkerInit =HIGH;
     }
     
    if (((millis ()) - BlinkerStartCount) >= dly)
     {
     BlinkerTimeout = HIGH;              // set Flag
     BlinkerInit =LOW;
     }  
  return BlinkerTimeout;
}//------------------------------------------------------

This compiles and runs, you should be able to adapt the concept to your device.