change state after switch is released

I have a key switch for a simple alarm. what I want to do is that when I turn it, lock in the time.
then if the time is between 1 and 2 seconds, set the alarm. (away mode)
if the time is over 3 seconds, re-set (stay mode)
the relays are latching relays, so only take a pulse to change and hold the state.

I need to look at the condition after the keyswitch is released and only then change the state of the relays.
what I want is to eliminate the pulse from being sent to set one relay while waiting to reach the 3 second timer.

I have not fleshed out the whole program, just looking to see if this is typically how to look at a switch after it is released.

I will add an LED to show the person turning the key that the time is between 1 and 2 seconds and the another after the time is over 3 seconds.

one thing that concerns me is what if someone turns the key just after the first if() statement.

guess I am looking to see if I am on the right track.

void loop(){
  
 int sw = digitalRead(2);  // read switch on pin 2

if (sw==HIGH){
  if (lastSW==LOW){
    swthen=millis();
  }
}
swTime=(millis()-swThen);


if(sw==HIGH){
  if(swTime>3000){
    resetVal=HIGH;
    setVal=LOW;
  }
  if(swTime>1000) && (swTime<2000){
    resetVal=LOW;
    setVal=HIGH;
  }
  else{
    resetVal=LOW;
    setVal=LOW;
  }
}
if(sw==LOW){
  if (lastSW==HIGH){
    digitalWrite(relay1, resetVal);
    digitalWrite(relay2, setVal);
  }
}
//  stuff at end of void loop
lastSW=sw;

} // end of void loop

one thing that concerns me is what if someone turns the key just after the first if() statement.

This shouldn't matter, as far as the program is concerned, the switch will still be low, as you read the switch state and stored it in the sw variable at the top of loop().

I would however be tempted to use a simple state engine instead of all those if statements, it makes it much simpler to debug and is much easier to keep a handle on the flow.

not working

when I press and hold between 2 and 3 seconds, , the LED lights and remains lit after the button is released.

when I comment out the digitialWrite and enable it for the timer over 3 seconds, the LED lights over 3 seconds, then immediately goes off when the button is released.

I have 2 IF() statements with the ELSE

not sure what I am doing wrong.

int LED13 = 13;
const int keySW = 6;

unsigned long lastSW = 0;
unsigned long swThen = 0;
unsigned long swTime = 0;
int relay1 = 8;
int relay2 = 9;


int resetVal=0;
int setVal=0;

void setup(){

 pinMode(LED13, OUTPUT); 
 pinMode(keySW, INPUT); 
 pinMode(LED13, OUTPUT);
 pinMode(relay1, OUTPUT);  
 pinMode(relay2, OUTPUT); 


}
void loop(){

 int keySW = digitalRead(6);  // read switch on pin 6

   if (keySW==HIGH){
   if (lastSW==LOW){
     swThen=millis();
   }
 }
 swTime=(millis()-swThen);


 if(keySW==HIGH){
   if(swTime>3000){
     // digitalWrite(LED13, HIGH);
               
   }
   if(swTime>1000 && swTime<2000){
       digitalWrite(LED13, HIGH);
   }
   else{

     digitalWrite(LED13, LOW);
   }
 }

 lastSW=keySW;

} // end of void loop

should it be 2 'else' after 1 IF() ?

if(){
}
else{ // else 1
}
else{ // else 2
}
}

How is your switch wired?

Will this not always be true as one point?

if(swTime > 1000 && swTime < 2000)

I would however be tempted to use a simple state engine instead of all those if statements,

I would agree with this.

Hi,

Also,

const int keySW = 6;    //  it is  a constant here (I would use the byte type)


//I would change this 

int keySW = digitalRead(6);  // read switch on pin 6

//to 

int keySW_state = digitalRead(keySW);  // keySW  pin is fixed but keySW_state isn't

if you key switch is non-maintain like a push button then you need to collect the time while the switch is being pushed then record and compare the time once the switch is released. If you compare as the switch is still being pushed then you will always switch the system on before switching it back off.

if button pushed timer runs(add led's here if you need feedback),
when button not pushed compare timer,
do something then reset timer for next button pushed.

what ever code you write when told to do something should set a bit so it remains doing it until the next button pushed/compare changes it. (unless its just outputs then they will remain at state anyway)

the application is an alarm or rather the momentary key switch that arms or disarms the alarm.
this part of the code is to look at the momentary keyswitch.
sequence is:
you are ready to exit,
you turn and hold for between 1 and 2 seconds.
at that time, the alarm would go into 'armed'

you return
turn the key, it counts for 3 seconds, then it disables the system

my goal is to turn the key
I will flash the current alarm condition, green LED if no sensors are in alarm, or flashing red LED of any sensor is in alarm.
this is that first second window. you see it flash, you stop and wait. clear the alarm and try again.

I want to have this loop set the condition, but not send it to the relays until after the key is released. that way, if the unit is in alarm, and the key is held too long, the operator can just hold for another second to de-activate it.

it is obvious this did not work, so I stripped it down to the simplest code.
if I can get this to work, then I can get the rest to work.

on another note, I cannot see how a state engine would help.

State engine will turn a mess of if statement logic that might work into something easily readable by most. This code should do what you want, you may have to play with the key_switch depending on your switch type.

enum state_e
{
  WAIT_FOR_ARM = 0,
  ARMING,
  ARMED,
  DISARMING,
};
state_e g_state = WAIT_FOR_ARM;
unsigned long g_timer = 0;

void loop() 
{
  int key_switch = digitalRead(switch_pin);
  unsigned long timer = millis();

  switch ( g_state )
  {
    case WAIT_FOR_ARM:
      {
        if ( key_switch == HIGH )
        {
          // turned the switch, we're attempting to arm ...
          g_state = ARMING;
          g_timer = timer;
        }
      }
      break;
    case ARMING:
      {
        // We've turned the switch back. if its between 1 and 2 seconds, arm, else reset ...
        if ( key_switch == LOW )
        {
          if (( timer - g_timer ) > 1000 && ( timer - g_timer ) < 2000 )
          {
            g_state = ARMED;
          }
          else
          {
            g_state = WAIT_FOR_ARM;
          }
        }
      }
      break;
    case ARMED:
      {
        if ( key_switch == HIGH )
        {
          // we've returned and turned the key
          g_timer = timer;
          g_state = DISARMING;
        }
      }
      break;
    case DISARMING:
      {
        if (( timer - g_timer ) > 3000 )
        {
          // system counts to 3 before disarming
          g_state = WAIT_FOR_ARM;
        }
      }
      break;
  }
}

this isn't the best code but I was playing with your idea
im presuming the led is near the key switch?

just seen another person posted code its probably better than this

p.s had to edit once I re-read it and seen where the timer was

//turn key till you see led13 flash to arm
//if led is solid a sensor alarm is active 
//alarm can not be armed if sensor is active
//to disarm turn key for less than 2 seconds
//led will flash slow when system armed

int LED13 = 13;
const int keySW = 6;

//unsigned long lastSW = 0;
//unsigned long swThen = 0;
//unsigned long swTime = 0;
int relay1 = 8;
int relay2 = 9;
//int resetVal=0;
//int setVal=0;

unsigned long previousMillis;
unsigned long currentMillis;
unsigned long interval=100;//sets the 10th of seconds
unsigned int counter=0;
byte key=0;//button type make sure you use 10k pulldown resistor 
//might be better to wire and code as a input_pullup
byte alarm_active;//this is where it tells you to arm the device
byte flash_timer;
byte key_state;
byte in_alarm=0;//this is where you have to tell it that a sensor detects a alarm

void setup() {
  // put your setup code here, to run once:
Serial.begin (9600);
 pinMode(keySW, INPUT); 
 pinMode(LED13, OUTPUT);
 pinMode(relay1, OUTPUT);  
 pinMode(relay2, OUTPUT); 
}

void loop() {
  // put your main code here, to run repeatedly:
 key = digitalRead(keySW);  // read switch on pin 
 
 currentMillis = millis();//used in timer
  if(currentMillis - previousMillis > interval) {
 counter++;
 flash_timer++;
 previousMillis = currentMillis;}
 
 if (key==LOW && key_state==0){counter=0;}
 
 //your key switch
 if (key==HIGH){
   key_state=1;
 
if (flash_timer>=2) {flash_timer=0;}//just something to turn led on and off

//if you hold the key switch once alarm alarmed it will flash
if (counter>=20 && flash_timer==1){
 digitalWrite(LED13, HIGH);}
 
 else {digitalWrite(LED13, LOW);}

//if a sensor makes the alarm sound it wont arm and led will stay solid
 if (in_alarm==1){
 digitalWrite(LED13, HIGH);
 counter=0;}
  }
  //all of the above code only runs when key is on
  
 //next part of code that decied what you wanted to do based on time
 //only runs one time when switch is turned off 
 if (key==LOW && key_state==1){
   if (counter>=20)//you held button for 2 seconds
   {alarm_active =1;//alarm active means you turned it on
   digitalWrite(LED13, LOW);//bad coding as flashing could leave led on
  counter=0; 
 key_state=0;}
 
   else {alarm_active=0;//you turned alarm system off
   digitalWrite(LED13, LOW);//bad coding as flashing could leave led on
   counter=0;
 key_state=0;}}
 
 if (alarm_active==1) {
   if (flash_timer>=10){flash_timer=0;}
   if (flash_timer==1){
 digitalWrite(LED13, HIGH);}
 
 else {digitalWrite(LED13, LOW);}
 }
 
   Serial.print (alarm_active);
 Serial.print(" 1=active ");
   Serial.print (in_alarm);
   Serial.println (" outside sensor detects alarm ");
  
}

Thanks guys, I will digest this.
I did one thing that I hate, post only some of the project. since this snippet was the part that had me stumped, I figured the rest was so separate that it could not interfere with this. and I reduced the code to the point of totally miminalistic and since I was having a problem, figured that is what I was doing wrong. it was.

the whole project is pretty simple.

motion sensors. these respond as people move around. (5v normal, 0v alarm)
a horn to make noise. (relay* output)
a latching relay to turn on the lights. (relay* output)
*I have one latching relay, one pulse to set, second pin, one pulse to re-set, 10ms pulse duration per data sheet

the key is by the front door. momentary.
the key has a bi-color LED.
4 wires to the key switch. 5v. switch, led1 and led2

since I have only 3 wires to play with, the key is a momentary and I want to use a timing of the keyswitch as the basis of operation. choice is send 5v and bring LEDs to ground, or common is ground and send 5v to LEDs

/*
turn the key,

  • led1 (red for alarm) will flash if any motion sensor is in alarm

  • led2 (green for safe) will pulse is the system is in normal

if the operator turns the key and it flashed, he has 1 second to release so that they do not arm the system.

between 1 and 2 seconds, the system will change state and upon release of the switch, go into (away/set) mode

if held for 3 seconds or longer, it will reset the system to stay/un-set mode

// digesting above codes. these issues may be addressed.

operator turn the key, either green or flashes red for up to 1 second, then turns yellow from 1 to 3 seconds, then after 3 seconds, goes dark. that way he knows what state the unit is in. (probably need to change 1-2 to 1-3 second)

*/

ultimately, I want to add an SD card and RTC to log any trips and possibly when the alarm was set and un-set.

going into my friends shop/warehouse. since he has 2 employees and his dad, no one knows who is coming and going,

sounds simple enough. i would go with 5v out to the leds just add a 10k or bigger resistor between the key switch pin input and ground. The resistor will pull the line voltage down to zero while the key is switched off.(open)

tammytam:
State engine will turn a mess of if statement logic that might work into something easily readable by most. This code should do what you want, you may have to play with the key_switch depending on your switch type.

enum state_e

{
 WAIT_FOR_ARM = 0,
 ARMING,
 ARMED,
 DISARMING,
};
state_e g_state = WAIT_FOR_ARM;
unsigned long g_timer = 0;

void loop()
{
 int key_switch = digitalRead(switch_pin);
 unsigned long timer = millis();

switch ( g_state )
 {
   case WAIT_FOR_ARM:
     {
       if ( key_switch == HIGH )
       {
         // turned the switch, we're attempting to arm ...
         g_state = ARMING;
         g_timer = timer;
       }
     }
     break;
   case ARMING:
     {
       // We've turned the switch back. if its between 1 and 2 seconds, arm, else reset ...
       if ( key_switch == LOW )
       {
         if (( timer - g_timer ) > 1000 && ( timer - g_timer ) < 2000 )
         {
           g_state = ARMED;
         }
         else
         {
           g_state = WAIT_FOR_ARM;
         }
       }
     }
     break;
   case ARMED:
     {
       if ( key_switch == HIGH )
       {
         // we've returned and turned the key
         g_timer = timer;
         g_state = DISARMING;
       }
     }
     break;
   case DISARMING:
     {
       if (( timer - g_timer ) > 3000 )
       {
         // system counts to 3 before disarming
         g_state = WAIT_FOR_ARM;
       }
     }
     break;
 }
}

I have to admit that I am not smart enough to figure out what I have to do to initialize before void setup()

Should I just write the typical global variables to initialize them ?

and then add the proper input/output lines in void setup() ?

To use that code, all you have to do is declare and initialise the switch_pin variable, which should be set to the pin number of your switch.

Then create a setup() block, and set that same pin as an INPUT type pin.

You will obviously have to flesh out your code with any additional features you want in each state, but those should be pretty self explanatory.