Im trying to set up a latching switch with a button press time delay using millis(). The functionality is:
Press and hold for 5 seconds, or stay pressed= ON
released= ON
Press and hold for 5 (or a different no,) seconds= OFF
released = off
Ive tried merging a toggle switch with a 'press for 5 seconds switch' codes and it hasn't quite worked! Please see below.
Is this the right strategy? is there an easy fix, or is a slightly different approach needed?
//Button press for cct arming:
//If button pressed for more than 5 seconds, or stays pressed, latch HIGH
//If button pressed again for more than 5 seconds, latch LOW
//Else stay high
long startswitchdelay;
long currentswitchstate;
int lastswitchstate = LOW;
const int powerLED = 3;
const int wowswitchpin = 12;
//int powerLEDstate = HIGH;
unsigned long currentmillis;
bool powerLEDstatus = LOW;
void setup() {
pinMode(wowswitchpin, INPUT);
pinMode(powerLED, OUTPUT);
digitalWrite(powerLED, LOW);
}
void loop() {
currentswitchstate =digitalRead(wowswitchpin);
if (currentswitchstate != lastswitchstate){
if (currentswitchstate == HIGH){
if(currentswitchstate != lastswitchstate){
startswitchdelay = millis() + 5000;
}
else {
if (millis() >= startswitchdelay){
digitalWrite(powerLED, HIGH);
startswitchdelay = millis() + 5000;
}
}
if (powerLEDstatus ==LOW){
digitalWrite(powerLED, HIGH);
powerLEDstatus = HIGH;
}
else {
digitalWrite(powerLED, LOW);
powerLEDstatus = LOW;
}
}
lastswitchstate = currentswitchstate;
}
}
i see the logic as:
record current switch state
check switch has been pressed for 5 seconds or longer
if yes, then turn LED ON
else LED last state
if last state !=current state
check switch has been pressed for 5 seconds or longer
if yes, turn led state OFF ( or is it current LED state !=old led state?)
Use the substraction: "currentMillis - previousMillis".
Don't add something to a millis() value.
Don't compare millis() with a value.
Always use "unsigned long" when doing something with millis().
To start counting 5 seconds, you need the moment that the button is pressed. That is what the State Change Detection is for: https://www.arduino.cc/en/Tutorial/StateChangeDetection. You have that part already, but not properly used.
To remember the state of the switch and the led, you can use 'bool' variables, or the state as HIGH and LOW. Since you have HIGH and LOW, I will use that. I assume that the input is HIGH when the button is pressed.
Not tested:
const int ledPin = 3;
const int buttonPin = 12;
const unsigned long interval = 5000; // 5 seconds
unsigned long previousMillis;
bool enable = false;
int lastButtonState = LOW;
int ledState = LOW;
void setup()
{
pinMode( buttonPin, INPUT);
pinMode( ledPin, OUTPUT); // OUTPUT, it will also become LOW
}
void loop()
{
unsigned long currentMillis = millis();
int buttonState = digitalRead( buttonPin);
if( buttonState != lastButtonState)
{
if( buttonState == HIGH) // the button was pressed just now ?
{
previousMillis = millis(); // remember this moment
enable = true; // start the timer
}
else // the button was released just now
{
enable = false; // stop the timer
}
lastButtonState = buttonState; // store the state of the button
}
if( enable)
{
if( currentMillis - previousMillis >= interval) // Enough time has passed ?
{
ledState = LOW ? HIGH : LOW; // toggle, LOW becomes HIGH and HIGH becomes LOW
digitalWrite( ledPin, ledState);
enable = false; // the led is toggled, this timer is no longer needed
}
}
}
It would be better to add a debounce to the button.
i tried your code and put some debug lines in. it detects the HIGH/LOW switch states, but always puts the LED output to LOW. the line :
ledState = LOW ? HIGH : LOW; // toggle, LOW becomes HIGH and HIGH becomes LOW
doesnt seem to be doing what it says on the can. if i set ledstate = HIGH then it toggles on after 5 secs but never triggers low.
here is my marked up code:
const int ledPin = 3;
const int buttonPin = 12;
const unsigned long interval = 5000; // 5 seconds
unsigned long previousMillis;
bool enable = false;
int lastButtonState = LOW;
int ledState = LOW;
void setup()
{
pinMode( buttonPin, INPUT);
pinMode( ledPin, OUTPUT); // OUTPUT, it will also become LOW
}
void loop()
{
unsigned long currentMillis = millis();
int buttonState = digitalRead( buttonPin);
if( buttonState != lastButtonState)
{
if( buttonState == HIGH) // the button was pressed just now ?
{
Serial.println("current button state =HIGH "); //debug
previousMillis = millis(); // remember this moment
enable = true; // start the timer
}
else // the button was released just now
{
enable = false; // stop the timer
Serial.println("current button state =LOW "); //debug
}
lastButtonState = buttonState; // store the state of the button
}
if( enable)
{
if( currentMillis - previousMillis >= interval) // Enough time has passed ?
{
ledState = LOW ? HIGH : LOW; // toggle, LOW becomes HIGH and HIGH becomes LOW
digitalWrite( ledPin, ledState);
Serial.println(ledState); //debug
enable = false; // the led is toggled, this timer is no longer needed
}
}
}
Thanks guys! The == didnt change anything unfortunately, it was locked in state '0', however using the 1-ledState trick does work. Progress!
I am compiling to a Teensy 3.2, but that shouldnt make any difference as it compiles ok, just doesnt like the ledState = LOW ? HIGH : LOW; line in practice.
Sorry for the typo for the toggle of led_state.
I prefer to use HIGH and LOW instead of 1 and 0, but there are still a few variations. Pick the one that looks the most elegant, but not the ugly ones:
// between '(' and ')' to make clear that it is a seperate part
led_state = (led_state == LOW ? HIGH : LOW);
// without '(' and ')' is allowed
led_state = led_state == LOW ? HIGH : LOW;
// a normal if-else
if( led_state == LOW)
led_state = HIGH;
else
led_state = LOW;
// curly brackets and everything
if( led_state == LOW)
{
led_state = HIGH;
}
else
{
led_state = LOW;
}
// With a switch-case
switch( led_state)
{
case HIGH:
led_state = LOW;
break;
case LOW:
led_state = HIGH;
break;
}
// Ugly code. The compiler accepts this as well, but this feels wrong.
led_state == HIGH ? led_state = LOW : led_state = HIGH;
// Very ugly code.
led_state ^= HIGH;
Haha!!! I'm struggling enough as it is, and now i'm getting free help with errors
My brain is fried. But trying some of those variations, all work. I went for the bracket free version
Now I have nice clean states to output to the next fuction... which doesnt like them... grrr... I think i need to write them to an array maybe? i've declared systemstate as char ...:
const int ledPin2 = 4; // debug
const int powerLED = 3; //LedPin
const int wowswitchpin = 12; // buttonPin
char systemstate;
const unsigned long interval = 5000; // 5 seconds
unsigned long previousMillis;
bool enable = false;
int lastwowswitchstate = LOW; // lastButtonState = LOW
int ledState = LOW;
void setup()
{
pinMode( wowswitchpin, INPUT);
pinMode( powerLED, OUTPUT); // OUTPUT, it will also become LOW
pinMode( ledPin2, OUTPUT); // debug
digitalWrite(ledPin2, HIGH);
Serial.println(ledState); //debug
}
void loop()
{
unsigned long currentMillis = millis();
int wowswitchstate = digitalRead(wowswitchpin); //buttonState = digitalRead( buttonPin);
if( wowswitchstate != lastwowswitchstate) //lastButtonState
{
if( wowswitchstate == HIGH) // the button was pressed just now ?
{
Serial.println("current button state =HIGH "); //debug
previousMillis = millis(); // remember this moment
enable = true; // start the timer
}
else // the button was released just now
{
enable = false; // stop the timer
Serial.println("current button state =LOW "); //debug
}
lastwowswitchstate = wowswitchstate; // store the state of the button
}
if( enable)
{
if( currentMillis - previousMillis >= interval) // Enough time has passed ?
{
ledState = ledState == LOW ? HIGH : LOW; //1 - ledState ; also works// toggle, LOW becomes HIGH and HIGH becomes LOW
digitalWrite( powerLED, ledState);
Serial.println(ledState); //debug
if (ledState == 1){
systemstate = 'F';
Serial.println("systemstate= F "); //debug
}
else
systemstate = 'A';
Serial.println("systemstate= A "); //debug
enable = false; // the led is toggled, this timer is no longer needed
}
}
}
When you want to send data to another function, then a char of 'A' or 'F' is no problem.
It depends on that function what it wants.
Did you notice the biggest difference between the code that you had and what I made ?
The timer of millis() is running in the main level of the Arduino loop() and it is turned on and off somewhere else.
In most cases, a millis() timer does not run deep in if-statements and for-loops. It runs on its own in the loop().
Bye the way, I tested them all before showing them to you. I didn't want to make the same mistake
I understand your code. I get lost translating the concept into good code, which comes with experience i suppose, and learnign what the functions do.
Ive now got good code which outputs 'A' and 'F' depending on the correct switch state. Ive incorporated it into the main code and everythign works nicely! Thanks for your ( and everyones) help. Love these forums.