How to monitor a switch's state and do X if switch has remained in state B

How to monitor a switch's state and do X if switch has remained in state B for length of time T?

Basically I want to alert the user audibly if a toggle switch remains in and "on" position for longer than 2hrs but don't know how to do it logically.

Would appreciate any tips

Thanks

Declare global variable to keep track of the time that the switch has remained in it's position.
Record the edge time for when the switch changed states.
Keep checking for the the time it's been since it's been switched (millis() - timeSwitched) and compare that to your interval time.

Arrch:
Declare global variable to keep track of the time that the switch has remained in it's position.
Record the edge time for when the switch changed states.
Keep checking for the the time it's been since it's been switched (millis() - timeSwitched) and compare that to your interval time.

Yes that's what I thought initially, but how do I avoid overwriting the timeSwitched with millis() everytime the IF statement checks to see if the switch is still HIGH?

Thanks!

This should get you on the right path. I think I got all of the conditions but didn't test anything.

#define SWITCH_TIMELIMIT 7200000
#define SWITCH_PIN 2

void loop()
{
  static unsigned long switch_timestamp = 0;
  bool switch_state = digitalRead(SWITCH_PIN);

  if(switch_state && switch_timestamp && (millis() - switch_timestamp) >= SWITCH_TIMELIMIT)
  {
    //Do something important here.

    //if you want to reset the warning:
    //switch_timestamp = 0;
  }
  else if (!switch_timestamp && switch_state)
  {
    //The !switch_timestamp is to make sure we don't refresh the timer if the switch remained on.
    //The pin just changed to on, start the timer
    switch_timestamp = millis();
  }
  else if(!switch_state && switch_timestamp)
  {
    //Switch went off, while timer was running, disable the timer
    switch_timestamp = 0; 
  }

}

Dane:

Arrch:
Declare global variable to keep track of the time that the switch has remained in it's position.
Record the edge time for when the switch changed states.
Keep checking for the the time it's been since it's been switched (millis() - timeSwitched) and compare that to your interval time.

Yes that's what I thought initially, but how do I avoid overwriting the timeSwitched with millis() everytime the IF statement checks to see if the switch is still HIGH?

Thanks!

Remember the previous state of the switch, and only record the 'start time' when you see the switch change to state B. This is what Arrch refers to as the edge time.

PeterH:
Remember the previous state of the switch, and only record the 'start time' when you see the switch change to state B. This is what Arrch refers to as the edge time.

Yup. The "Edge time" refers to the time in which the state changes. The rising or positive edge is when the state goes from low to high, and the falling or negative edge is low to high. Do you will need to keep track of one of those by keeping track of the last state.

So the pseudo code for tracking the positive edge would be:

get current state

if last state is low and current state is high
// Positive Edge
set edge time to current

set last state to current state

if edge time minus current time is greater than threshold time
// Do something here

Thank you for the proto code! I will test it tonight. I don't unnerstand this switch_state && switch_timestamp && (millis() - switch_timestamp) >= switchPin1TriggeredTime) (i.e. how it is able to do what it does!) which worries me but maybe after playing with it I will.

Is there an easy way to call a function (to play a tone on a piezo speaker) every two minutes when the switch has remained "on" for the pre-determined time limit? Or would I need to use variables to a) track when the piezo function was called and b) create an "alert" state which would control when the function was called?

  static unsigned long switch_timestamp = 0;
  bool switch_state = digitalRead(switchPin1);
  const int switchPin1 = 1;    
  long switchPin1TriggeredTime = 7200000;		

  if(switch_state && switch_timestamp && (millis() - switch_timestamp) >= switchPin1TriggeredTime)   {
  //play a short alert tone every minute
    }
  
  else if (!switch_timestamp && switch_state)   {
    //The !switch_timestamp is to make sure we don't refresh the timer if the switch remained on.
    //The pin just changed to on, start the timer
    switch_timestamp = millis();
  }
  
  else if(!switch_state && switch_timestamp)   {
    //Switch went off, while timer was running, disable the timer
    switch_timestamp = 0; 
  }
  
  if(switch_state)  {

//actual code for when switch is on goes here?
}

I'll try to explain this for you:

 if(switch_state && switch_timestamp && (millis() - switch_timestamp) >= switchPin1TriggeredTime)

Is functionally identical to this:

if (switch_state == HIGH) //The switch is depressed
{
     if (switch_timestamp > 0)  //The switch was depressed the last time we checked it too
     {
          if (millis() - switch_timestamp >= switchPin1TriggeredTime)  //It's been 2 minutes.
          {
                 //Play a short tone.
          }
     }
}

To call a function every 2 minutes, you are correct - you would need a variable to track the last time it was called, and update the variable every time you called it. Same exact logic we're using to determine when to press it the first time.

One "hack" you could do to get this working fast, is every time you play the tone, reset the switchPin1TriggeredTime back to the current system clock.

Like so:

  if(switch_state && switch_timestamp && (millis() - switch_timestamp) >= switchPin1TriggeredTime)   {
  //play a short alert tone every minute
     playWarningTone();
     switch_timestamp = millis() + ( 6000 );  //Re-fire the event every minute after the first 2 minute warning.
    }

(This was posted before tgm175 posted his explanation.)

OK I've made some changes to the code and tested it and with acknowledgements and thanks to tgm1175 who was the chef here, I wasn't even the sous chef, I was the washer-upper.

This is tested working.

THANKS

#include <Bounce.h>
const int switchPin1 = 7;     
long switchPin1TriggeredTime = 10000;		
Bounce bouncer = Bounce( switchPin1,5 ); 

void setup() 
{
  Serial.begin(57600);
  pinMode(switchPin1, INPUT);	
  digitalWrite(switchPin1, HIGH);	//pull up
  Serial.println("TEST");
  pinMode(13, OUTPUT);     
}
void loop()
{
  bouncer.update ( );
  int value = !bouncer.read();
  static unsigned long switch_timestamp = 0;
  if(value && switch_timestamp && (millis() - switch_timestamp) >= switchPin1TriggeredTime)   {
    Serial.println("HEY SWITCH HAS BEEN 'ON' TOO LONG!!");
  }
  else if (!switch_timestamp && value)   {
    //The !switch_timestamp is to make sure we don't refresh the timer if the switch remained on.
    //The pin just changed to on, start the timer
    switch_timestamp = millis();
  }
  else if(!value && switch_timestamp)   {
    //Switch went off, while timer was running, disable the timer
    switch_timestamp = 0; 
    Serial.println("TURNED OFF");
  }
  if(value)  {
    digitalWrite(13, HIGH);   // LED on
  }
  if(!value)  {
    digitalWrite(13, LOW);   // LED off
  }
}

tgm1175:
One "hack" you could do to get this working fast, is every time you play the tone, reset the switchPin1TriggeredTime back to the current system clock.

Like so:

  if(switch_state && switch_timestamp && (millis() - switch_timestamp) >= switchPin1TriggeredTime)   {

//play a short alert tone every minute
     playWarningTone();
     switch_timestamp = millis() + ( 6000 );  //Re-fire the event every minute after the first 2 minute warning.
    }

Thank you for this hack, it seems to have a lot of potential. However when using it, I've found that what happens is that the warning tone is re-fired after switchPin1TriggeredTime - not every minute. Would you mind seeing if there's something simple to address to get it working? I've had a play myself to no avail :slight_smile:

THANKS AGAIN

switch_timestamp = millis() + ( 6000 );

What this means (assuming timestamp isn't changed), is that after 6 seconds, the timestamp will will be equal to the current time, so that means overall, it will run again 6 + interval seconds. If you want it to run every minute, then you simply need to add 1 minute to what the current timestamp is:

switch_timestamp += 60000;

Arrch:

switch_timestamp = millis() + ( 6000 );

What this means (assuming timestamp isn't changed), is that after 6 seconds, the timestamp will will be equal to the current time, so that means overall, it will run again 6 + interval seconds. If you want it to run every minute, then you simply need to add 1 minute to what the current timestamp is:

switch_timestamp += 60000;

Hmmm - tried that - it didn't do the trick either. Thanks though!

I don't know this code may give you some idea about the state of a switch and help you for some insight...

Here a code. The code is a gear shifter. It got a Up and Down button. The switch go from HIGH to LOW. I hope it give you some insight.

/*
  size : 1774 byte
  
  Version 2.1
  
  file name : gearshifter.pde
  
  It simulate a gear shifter using 2 push-button switch.
  The circuit use a 7 segment display and 2 led for the 
  simulated selenoid.
  
  Gear sequence:   Gear Number   A   B
                       1         1   1
                       2         0   1
                       3         0   0
                       4         1   0
  
  Here the parts you need :

  1 Commun Anode 7 segment Display
  2 Red LED
  2 Push Button Switch
  9 2N3904 NPN Transistor
  11 1 K resistor
  9 330 ohms resistor
 
  How it work :
 
  1. Init the gear at 1.
  2. Press Upshift Button to increase the gears
  3. Press Downshift Button to decrease the gears.
  
  Program by Serge J Desjardins aka techone / tech37

  Compile and Tested <-- Test on a breadboard

  Disclaimer:

  I am not responsible for any damages, losses, injuries or death.
  The user is responsabe for any damages, losses, injuries or death.
  
  And Murphy's Law is always there, bear that in mind. 
    
*/
// Arduino Digital pins
const byte outpin[9] = {4,5,6,7,8,9,10,11,12};
const byte inpin[2] = {2,3};
// Selenoid and Display array pattern
boolean gearone[9] = {1,1,0,0,0,0,1,1,0};
boolean geartwo[9] = {0,1,1,0,1,1,0,1,1};
boolean gearthree[9] = {0,0,1,0,0,1,1,1,1};
boolean gearfour[9] = {1,0,1,1,0,0,1,1,0};

byte gear;

int upshift=1;
int downshift=1;
int oldupshift=1;
int olddownshift=1;

boolean state=0;

void setup()
{
  // init the digital pins
  for (int i=0; i<9; i++)
  {
  pinMode(outpin[i],OUTPUT);
  }
  pinMode(inpin[0],INPUT);
  pinMode(inpin[1],INPUT);
  // Put in gear one
  gear=1;
  
  for (int i=0;i<9;i++)
  {
    digitalWrite(outpin[i],gearone[i]);
  } 
}

void loop()
{
  readswitch(); 
  while(state==0)
  {
    readswitch();     
  }
  // a switch as been pressed
  // Check for the upshift
  if ((upshift==0) && (state==1))
  {  
     gear++;
     if ((gear>=1) && (gear<=4))
     {
       switch(gear)
       {
         case 1: selectone();break;
         case 2: selecttwo();break;
         case 3: selectthree();break;
         case 4: selectfour();break;
       }
     }
     else gear=4;
   }
  // check for the downshift  
  if ((downshift==0) && (state==1))
  {  
     gear--;
     if ((gear>=1) && (gear<=4))
     {
       switch(gear)
       {
         case 1: selectone();break;
         case 2: selecttwo();break;
         case 3: selectthree();break;
         case 4: selectfour();break;
       }
     }
     else gear=1;
   }   
   
}

void selectone()
{
   for (int i=0;i<9;i++)
   {
     digitalWrite(outpin[i],gearone[i]);
   }
   state=0;  
}

void selecttwo()
{
   for (int i=0;i<9;i++)
   {
     digitalWrite(outpin[i],geartwo[i]);
   }
   state=0;  
}

void selectthree()
{
   for (int i=0;i<9;i++)
   {
     digitalWrite(outpin[i],gearthree[i]);
   }
   state=0;  
}

void selectfour()
{
   for (int i=0;i<9;i++)
   {
     digitalWrite(outpin[i],gearfour[i]);
   }
   state=0;  
}

/* The subroutine is a modify switch example in page
   51 of the book "Getting Started with Arduino"
   
   The switch is configure with a pull-up resistor,
   so it always a 1, when press, it is a 0. 
*/   
void readswitch()
{
  upshift=digitalRead(inpin[0]);
  // check upshift transition
  if ((upshift==0) && (oldupshift==1))
  {
    state=1;
    delay(50);
  }  
  oldupshift=upshift;
  downshift=digitalRead(inpin[1]);
  // check downshift transition
  if ((downshift==0) && (olddownshift==1))
  {
    state=1;
    delay(50);
  }  
  olddownshift=downshift;  
}

Dane:
Hmmm - tried that - it didn't do the trick either. Thanks though!

I've done that in my code and it's worked, so it sounds like there is another issue with another part of your code, then.

Arrch:
so it sounds like there is another issue with another part of your code, then.

Maybe I put your snippet in the wrong place?

long switchPinsWaitTime = 30000;		//30 seconds
  static unsigned long switch_timestamp1 = 0;

  if(value2 && switch_timestamp1 && (millis() - switch_timestamp1) >= switchPinsWaitTime)   
  {
    //    playTone(100, 1000);
    //    playTone(100, 3000);
    //    playTone(100, 1000);
    //    playTone(100, 3000);
    switch_timestamp1 += 60000;
  }

  else if (!switch_timestamp1 && value2)   
  {
    //The !switch_timestamp1 is to make sure we don't refresh the timer if the switch remained on.
    //The pin just changed to on, start the timer
    switch_timestamp1 = millis();
  }
  else if(!value2 && switch_timestamp1)   
  {
    //Switch went off, while timer was running, disable the timer
    switch_timestamp1 = 0; 
  }

Ahhh, I see your problem:

long switchPinsWaitTime = 30000;		//30 seconds

if(value2 && switch_timestamp1 && (millis() - switch_timestamp1) >= switchPinsWaitTime)

switch_timestamp1 += 60000;

As is, this "trick" only works when the interval is greater than the amount you are adding. The reason being is the millis - timestamp when timestamp is bigger than millis would logically return a negative number if we were doing it on paper. The problem is that you are using an unsigned number, meaning the negative numbers are stored as really high numbers, so your comparison to the interval will evaluate true on every iteration. You can see this by printing result of millis-timestamp in your if statement and noticing the large numbers.

To fix this, you just need to add a comparison in you if statement to make sure that millis is greater than the timestamp.