Arduino Project - arduino

So, I am making a project wherein I will be using an Arduino Uno. What I want to do is, whenever the switch is on, the Arduino will make the led blink. But there is a twist. The LED will start blinking after 10 seconds of the switch status becoming high. But what is happening is that the led is switched off for 10 seconds and then turns on for 0.5 seconds, then again turns off for 10 seconds. What I want it to do is, after remaining in Off condition for 10 seconds, it will keep blinking continuously.
Here's the code
const int upperSwitch=2;
int buttonState;
const int ledPin=13;
unsigned long startMillis;
unsigned long currentMillis;
const unsigned long period = 10000;
void setup()
{
pinMode(upperSwitch,INPUT);
pinMode(ledPin,OUTPUT);
startMillis=millis();
}
void loop()
{
buttonState=digitalRead(upperSwitch);
if(buttonState==HIGH)
{
delay(10000);
currentMillis = millis();
if (currentMillis - startMillis >= period)
{
digitalWrite(ledPin,HIGH);
delay(500);
digitalWrite(ledPin,LOW);
delay(500);
}
}
}
Where am I going wrong??
Where am I going wrong?

Your logic is flawed, and the code exhibits a number design flaws. If you press the button and release it it will enter the if(buttonState==HIGH) section once, so flash once. If you press and hold the button (or use a toggle switch), it will re-enter the if(buttonState==HIGH) section, but repeat the delay before flashing.
Your code lacks "state" - a means of remembering what you were doing in the previous loop() iteration to inform what to do in the next.
The design flaws include:
Unnecessary use of global variables - it is a common bad-habit in Arduino code encouraged by numerous examples on the Arduino website. You only need globals for those variables shared between setup() and loop() - and even then that is not strictly necessary, but I'll let that go - Arduino code seldom gets complicated enough for that to be an issue. In general allow variables the narrowest scope necessary.
Use of delay() in loop(). This makes your code unresponsive and is a waste of CPU resource - doing nothing. In this case once you have started the delay, there is no means of cancelling it or doing other work until it is complete. You had the right idea with if (currentMillis - startMillis >= period), but rendered it pointless by preceding it with a delay and initialising startMillis at the start of the program, rather then when the button was pressed. After the delay, currentMillis - startMillis >= period will certainly be true so the test serves no purpose.
The following code implements press-on/press-off toggle semantics for the button with necessary debouncing. The button state can be toggled on/off at any time (no delays where the button will not be read).
When toggled-on, the delay starts by timestamping the event, and testing time passed since the timestamp. When the delay expires, it starts flashing - timestamping each LED state toggle to effect the flash timing.
When toggled-off, none of the delay/flash code is executed so you can cancel the delay and flashing at any time. The LED is forced off in this state. It seems likely that this is the semantics you intended.
You can run a simulation of this here. I have included debug output - click the "Code" button and expand the "Serial Monitor" to see the debug output, then click "Start Simulation", and click the simulated tact switch. The simulation timing is not accurate, about 50% longer than nominal - YMMV. Debug is output when the button state is toggled and while the LED is flashing. The voltmeter I added to verify the button was wired correctly.
const int UPPER_SWITCH = 2 ;
const int LED_PIN = 13 ;
void setup()
{
pinMode( UPPER_SWITCH, INPUT ) ;
pinMode( LED_PIN, OUTPUT ) ;
}
void loop()
{
// Get initial button state
static int button_toggle_state = digitalRead( UPPER_SWITCH ) ;
// Indicator timing
static unsigned long delay_start_timestamp = 0 ;
// Get current time
unsigned long current_millis = millis();
// Toggle button state on press (press-on/press-off) with debounce
static const unsigned long DEBOUNCE_MILLIS = 20 ;
static unsigned long debounce_timestamp = 0 ;
static int previous_button_state = digitalRead( UPPER_SWITCH ) ;
int current_button_state = digitalRead( UPPER_SWITCH ) ;
if( current_millis - debounce_timestamp > DEBOUNCE_MILLIS &&
current_button_state == HIGH && previous_button_state == LOW )
{
debounce_timestamp = current_millis ;
if( button_toggle_state == LOW )
{
button_toggle_state = HIGH ;
delay_start_timestamp = current_millis ;
}
else
{
button_toggle_state = LOW ;
}
}
previous_button_state = current_button_state ;
// If button toggle state has remained HIGH for DELAY_PERIOD...
static const unsigned long DELAY_PERIOD = 10000 ;
if( button_toggle_state == HIGH &&
current_millis - delay_start_timestamp > DELAY_PERIOD)
{
// ... start flashing
static const int FLASH_PERIOD = 500 ;
static int led_state = LOW ;
static unsigned long flash_toggle_timestamp = 0 ;
if( current_millis - flash_toggle_timestamp > FLASH_PERIOD )
{
flash_toggle_timestamp = current_millis ;
led_state = led_state == HIGH ? LOW : HIGH ;
}
digitalWrite( LED_PIN, led_state ) ;
}
else
{
// LED off
digitalWrite( LED_PIN, LOW ) ;
}
}

Related

Changing blinking duration of LED by using 2 pushbuttons and arduino

I'm trying to make an arduino UNO circuit that allows me to set the blinking duration of an LED with two pushbuttons, but I'm having trouble with the program. First of all, the default blinking duration is 0,5 s. And I want to program the first pushbutton to be able to extend the blinking duration by 0,1 seconds, whereas the second one is for speeding up the duration by 0,1 seconds.
So in my current code, I use if statements to check whether the two buttons are pressed or not. If the inc button is pressed, the program should increase the duration by 100 ms, whereas when dec button is pressed, the program should decrease the duration by 100 ms.
However when I run it on the arduino circuit, the duration is stuck in 600 and 500. So in every loop, the program adds 100 ms to the duration time and then decreases it again by 100, even when I do nothing to the buttons.
Here's my code so far:
const int led = 7;
const int buttonUp = 6;
const int buttonDown = 5;
int duration = 500;
void setup(){
pinMode(led, OUTPUT);
pinMode(buttonUp, INPUT);
pinMode(buttonDown, INPUT);
Serial.begin(9600);
}
void loop(){
int inc = digitalRead(buttonUp);
int dec = digitalRead(buttonDown);
if(inc == HIGH){
duration += 100;
Serial.println(duration);
}
if(dec == HIGH){
duration -= 100;
if(duration < 0){
duration = 100;
}
Serial.println(duration);
}
digitalWrite(led, HIGH);
delay(duration);
digitalWrite(led, LOW);
delay(duration);
}
the code and circuit
serial monitor
Will be extremely grateful if anyone can point out any mistakes!! Thank you!
duration is 500ms. So you basically poll your button states once every second. The time window where you can detect a button click is very short compared to the time you cannot detect it. So chances that you register a click are very little. You need to push the button for at least a second to capture the signal every time.
If you push both buttons you add and subtract 100. That's a total change of 0. What do you expect?
This can be avoided by checking your button state more frequently or by using interrupts.
Find out how to use non-blocking delays and interrupts. The internet is full of tutorials.
bool blink(unsigned int duration) {
// demo code only
// (usable only once per sketch, due to static variables)
static unsigned long last;
static bool state;
if (millis() - last >= duration) {
last = millis();
state = ! state;
}
return state;
}
This is the BlinkWithoutDelay pattern. Usage:
digitalWrite(led,blink(duration));
Button handling is trickier than a beginner thinks: often you want to detect a state change between pressed and released, and you want to ignore the bouncing of a mechanical switch happening during a state change. Or you want to repeat some action, when the button is pressed for a long time.
Easiest you do a little delay(10); after detecting a state change to pass the bouncing time. Or look for libraries doing all that button handling.
Or do such a lazy delay anyway, to slow down your fast microcontroller below button bouncing speed.
const int led = 7;
const int buttonUp = 6;
const int buttonDown = 5;
void setup(){
pinMode(led, OUTPUT);
pinMode(buttonUp, INPUT);
pinMode(buttonDown, INPUT);
Serial.begin(9600);
}
unsigned int duration = 500;
int lastbutton = 0; // 0 / -100 / +100 to detect button changes
void loop(){
bool inc = digitalRead(buttonUp);
bool dec = digitalRead(buttonDown);
delay(5); // debounce
if( (inc || dec) && lastbutton == 0){
lastbutton = (inc - dec) * 100;
duration += lastbutton;
if (duration == 0) duration = 100; // Minimum
Serial.println(duration);
}
if (lastbutton != 0 && !inc && !dec) lastbutton = 0;
digitalWrite(led, blink(duration) );
}

Enable esp32 deep sleep mode after a certain period of inactivity

I have a device designed in esp that contains two buttons, each of which performs a function. However, he is constantly monitoring these buttons and running out of battery. How do I implement esp's deep sleep, after a certain period of inactivity? For example, if the device does not read in 30 seconds, activate sleep mode. To exit the mode, just press any of the existing buttons.
I thought of something from the team, but it’s not working, because the milis() parameter is never reset.
void loop()
{
unsigned long timer=millis();
int botaoRfid = digitalRead(butRFID);
int botaoDig = digitalRead(butDIG);
if(botaoRfid == HIGH){
timer=0;
Serial.println("botao RFID pressionado");
checkRFID();
}
else if(botaoDig == HIGH){
timer=0;
Serial.println("botao DIGITAL pressionado");
checkFingerprint();
return;
}
else if(timer >=10000){
Serial.println("Sleep…");
esp_deep_sleep_start();
}
}
Thanks!
It is not really a problem, that millis() is not reset after startup, when you rewrite your code like so:
unsigned long timer = 0;
void setup()
{
}
void loop()
{
int botaoRfid = digitalRead(butRFID);
int botaoDig = digitalRead(butDIG);
if(botaoRfid == HIGH){
timer = millis();
Serial.println("botao RFID pressionado");
checkRFID();
}
else if(botaoDig == HIGH){
timer = millis();
Serial.println("botao DIGITAL pressionado");
checkFingerprint();
return;
}
else if(millis() - timer >= 10000){
Serial.println("Sleep…");
esp_deep_sleep_start();
}
}
This would put the ESP32 into deep sleep after 10 seconds without a reset to the current millis() (which is currently happening in every loop cycle, but you can of course put that code outside the loop...). Beware however, that millis() will overflow after about 49 days and you will have to handle this correctly.
Also in order to wake the ESP32 from DeepSleep you have to define a PIN to listen on using for example esp_sleep_enable_ext1_wakeup(bitmask, mode). There are also other option outlined here.

Arduino Interrupt won't ignore falling edge

I'm having some trouble completely debouncing a button attached to to an interrupt. The goal is to have the statement in the void loop() run exactly once when the button is pressed/released.
What usually ends up happening is one of two things
The ISR flag is set once when the button is pressed. Releasing the button does nothing, as intended.
The ISR flag is set once when the button is pressed, and once more when the button is released.
Here is the exact code I have:
#define interruptPin 2
#define DBOUNCE 100
volatile byte state = LOW; //ISR flag, triggers code in main loop
volatile unsigned long difference;
void setup() {
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
Serial.begin(115200);
}
void loop() {
if(state){ //If we have a valid interrupt
Serial.println(difference); //Print the time since the last ISR call
state = LOW; //Reset the flag
}
}
void ISR_0() {
static unsigned long last_interrupt = 0;
if(millis()-last_interrupt > DBOUNCE && digitalRead(interruptPin)){
difference=millis()-last_interrupt;
state = HIGH;
}
last_interrupt = millis(); //note the last time the ISR was called
}
This seems to be a popular way to debounce an interrupt, but for whatever reason it isn't working for me.
I was hoping on the first falling edge of the button release that digitalRead(interruptPin) would read low, so the state flag would not be set.
Since the ISR updates the last_interrupt time, the successive bounces after the first falling edge still seem to be successfully ignored. This leads me to believe the debouncing is not the issue, but the digitalRead(interruptPin) is.
The debouncing seems to take care of all but one state. When the button is released, the code still occasionally sets the state flag to HIGH.
Here's some sample output:
3643 (after waiting ~3.6 seconds from boot, I press the button, releasing it ~1 second later)
In the same scenario as above, the output occasionally looks like this:
3643
1018
This shows me pressing the button, but also releasing the button.
I'm using an UNO R3 and a momentary tactile push button with a 1k pull-down resistor.
I'm not sure what's going wrong at this point. I hope this is simple enough that anyone can easily test this on their arduino if they feel so inclined.
You can always do debouncing in the hardware
I've encountered bounce several times with buttons and encoders, and doing debouncing in the software can get messy and lead to unreadable code or logic errors.
The easiest thing you can do is add a 0.1 uF capacitor, as illustrated:
The Arduino has hysteresis inputs, and if you use 10K as your pullup then this works for bounce that is under 1ms. This is my favorite approach.
If you wanna get more serious, a wonderful pdf exists on the internet with lots of examples and explanations: A Guide to Debouncing
You use the internal pullup resistor of the Uno, but also a pulldown resistor on the button itself. That's not correct, you can only use one of of them.
Inside the if, you want the input to be high, so use the pulldown resistor and change INPUT_PULLUP into INPUT.
(to make things clear: the resistor is connected between input pin and ground, the button between input pin and +5V)
[edit]
I think when the ISR is called, the status of interruptPin might be changed again.
Due to the slowness of digitalRead, compared to the (possible) spike.
I'm not sure if this is what you want, but the example below is working.
(I've enabled the LED for testing purposes).
One thing: keep the button pressed at least the debounce time (100msec), otherwise it won't work.
Not ideal, but that's the price you have to pay if you want instantly response on a switch.
#define interruptPin 2
#define DBOUNCE 100
volatile byte state = LOW; //ISR flag, triggers code in main loop
volatile unsigned long difference;
bool hasPrinted = false;
void setup() {
pinMode(interruptPin, INPUT);
pinMode (13, OUTPUT);
attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
Serial.begin(115200);
}
void loop() {
digitalWrite(13, state);
if (!hasPrinted) {
Serial.println(difference);
hasPrinted = true;
}
}
void ISR_0() {
static unsigned long last_interrupt = 0;
if(millis()-last_interrupt > DBOUNCE){
state = !state;
hasPrinted = false;
difference = millis()-last_interrupt;
}
last_interrupt = millis(); //note the last time the ISR was called
}
Debouncing buttons/switches in interrupts is a hassle.
I faced a (sort of) similar situation with limit switches.
The moment a limit switch is hit, something has to occur - so interrupt.
However the interrupt would fire when the limit switch was released as well which was a problem. I had my interrupt set to fire on a FALLING edge.
Anyhow, I wound up debouncing outside of the interrupt, using flags.
The code explains it but:
Switch is hit, ISR runs (does what it needs to) and sets an ISR flag.
The ISR flag stops the ISR from actually doing anything until it's cleared.
In the main loop, call a debounce function if the ISR flag is set.
the debounce function will wait until a pin/switch is stable at the required state (HIGH/LOW) for a predefined time, then clear ISR flag, allowing the ISR to do something again.
#define interruptPin 2
#define DEBOUNCE_TIME 100L
volatile bool ISR_ACTIVATED = false;
volatile bool display_int_time = false;
bool debounce_started = false;
unsigned long Isr_debounce_pin_timer;
volatile unsigned long difference;
void setup() {
pinMode(interruptPin, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
Serial.begin(112500);
}
void loop() {
if (display_int_time) {
Serial.println(difference); //Print the time since the last ISR call
// Right, done with interrupt stuff. clear the interrupt flag
display_int_time = false;
}
// Call debounce ISR routine in main loop
if (ISR_ACTIVATED)
ISR_ACTIVATED = !debounce(interruptPin, HIGH, Isr_debounce_pin_timer);
}
bool debounce(int debounce_pin, bool state, unsigned long &state_latch_start) {
// debounce_pin - what pin are we debouncing?
// state - should the stable state be HIGH or LOW
// state_latch_start - when we 'latched' on to a (possibly) stable state
static bool current_state;
// Read the required pin
current_state = digitalRead(debounce_pin);
// Is the pin at the required state, and we have not yet 'latched' it?
if ((!debounce_started) && (current_state == state)) {
// 'latch' this state (ie take note of when the pin went to the required level)
state_latch_start = millis();
debounce_started = true;
}
// Have we 'latched', but the pin has bounced
if (debounce_started && (current_state != state))
// unlatch
debounce_started = false;
// Have we latched, the pin is at the required level and enough time has passed (ie pin is stable)
if (debounce_started && (current_state == state) && (((unsigned long)(millis() - state_latch_start)) >= DEBOUNCE_TIME)) {
// cool. unlatch
debounce_started = false;
// report back that all is goood.
return(true);
}
// Blast. Either the pin is at the wrong level, or is still bouncing. Tell the boss to try again later!
return(false);
}
void ISR_0() {
static unsigned long last_interrupt = 0;
if((millis()-last_interrupt > DEBOUNCE_TIME) && digitalRead(interruptPin) && !ISR_ACTIVATED){
difference=millis()-last_interrupt;
//state = HIGH;
ISR_ACTIVATED = true;
display_int_time = true;
}
last_interrupt = millis(); //note the last time the ISR was called
}
*** Just a note, edited my code to incorporate the debounce time in your original ISR to ensure that the interrupt is valid. I had not read the comment regarding your noisy environment
I suppose the main issue, as was said in the comments, is because you have no actual debounce.
So, after the button is released, it continues to bounce, causing logic level on the input pin to change. If at the moment, when the pin state is latched for the digitalRead(), the state was read as high, then the whole condition will be met and state = HIGH; will be performed.
I'm not an artist, but I did my best to draw this timing diagram:
So, to avoid this, you can utilize any simple approach to debounce. Most simple one is to read again the pin state after a little timeout, greater than the maximal expected bounce time.
For example, if you're waiting for the button to be pressed and got high logical level (or low if the button is connected to GND), just wait about 5ms and read the level again. Process the button press only if the level still high (low).
And as was said in the other answers, hardware debounce also will help. You can use a higher resistor (actually you need not use external resistor: connect the button to GND and enable the internal pull-up, which is about 35kOhm). And add a capacitor about 1nF, parallel to the button.
My final solution, thanks to #darrob
#define interruptPin 2
#define DEBOUNCE_TIME 50L
//ISR Flags
volatile bool ISR_DEACTIVATED = false;
volatile bool display_int_time = false;
bool debounce_started = false;
unsigned long Isr_debounce_pin_timer;
int x = 0;
void setup() {
pinMode(interruptPin, INPUT);
attachInterrupt(digitalPinToInterrupt(interruptPin), ISR_0, CHANGE);
Serial.begin(112500);
}
void loop() {
//This runs every time we press the button
if (display_int_time) {
Serial.print("X "); //Print the time since the last ISR call
x++;
(x%10==0)?Serial.println(x):Serial.println();
display_int_time = false; //Done with interrupt stuff. Clear the interrupt flag
}
//Debounce for the ISR routine in main loop
if (ISR_DEACTIVATED)
//Wait until the pin settles LOW to reactivate the ISR
ISR_DEACTIVATED = !debounce(interruptPin, LOW, Isr_debounce_pin_timer);
}
//Returns TRUE if pin is stable at desired state, FALSE if bouncing or other state
bool debounce(const int debounce_pin, const bool state, unsigned long &state_latch_start) {
// debounce_pin - what pin are we debouncing?
// state - should the stable state be HIGH or LOW
// state_latch_start - when we 'latched' on to a (possibly) stable state
//If you are calling this routine to debounce multiple pins in the same loop,
// this needs to be defined outside of the function, and passed in as a separate
// parameter for each debounce item (like unsigned long &state_latch_start)
static bool current_state;
// Read the required pin
current_state = digitalRead(debounce_pin);
// Is the pin at the required state, and we have not yet 'latched' it?
if ((!debounce_started) && (current_state == state)) {
// 'latch' this state (ie take note of when the pin went to the required level)
state_latch_start = millis();
debounce_started = true;
}
// Have we 'latched', but the pin has bounced
if (debounce_started && (current_state != state))
// unlatch
debounce_started = false;
// Have we latched, the pin is at the required level and enough time has passed (ie pin is stable)
if (debounce_started && (current_state == state) && (((unsigned long)(millis() - state_latch_start)) >= DEBOUNCE_TIME)) {
// cool. unlatch
debounce_started = false;
// report back that all is goood.
return(true);
}
//Either the pin is at the wrong level, or is still bouncing. Try again later!
return(false);
}
void ISR_0() {
if(!ISR_DEACTIVATED){
ISR_DEACTIVATED = true;
display_int_time = true;
}
}

Motion controlled light

I'm trying to create an motion controlled light that doesn't turn on unless it's dark.
I want the light to turn on and off slowly with pwm when motion is detected. Right now my code works and does almost everything I want it to.
The problem is that I'm using the delay function to keep the light on after it reaches full brightness. And because of this if motion is detected while the light is on it doesn't reset the timer. Once the delay timer runs out and motion is detected the brightness will go down and then back up.
I would like to have the motion detector reset the timer that keeps the led on but haven't been able to get it working.
I have looked at these:
http://playground.arduino.cc/Code/AvoidDelay
https://www.arduino.cc/en/Tutorial/BlinkWithoutDelay
But because of my somewhat limited understanding of arduino code I haven't been able to get it working. I would really appreciate any help.
SOLVED
The working code below:
const byte MotionPin = 4; // Motionsensor
int LightSensorPin = 2; // Lightsensor
const byte LedPin = 11; // LED
unsigned long MotionDetectedMillis; // When motion was detected
unsigned long ledTurnedOnAt; // when led was turned on
unsigned long turnOnDelay = 50; // wait to turn on LED
unsigned long turnOffDelay = 5000; // turn off LED after this time
bool ledReady = false; // Has motion been detected
bool ledState = false; // Led on or off
int LightSensorState = 0;
int fadeAmount = 5; // How much to fade in a loop
byte brightness = 0;
void setup() {
pinMode(MotionPin, INPUT);
pinMode(LightSensorPin, INPUT);
pinMode(LedPin, OUTPUT);
digitalWrite(LedPin, LOW);
}
void loop() {
// get the time at the start of this loop()
unsigned long currentMillis = millis();
if (digitalRead(MotionPin) == HIGH && digitalRead(LightSensorPin)== HIGH)
{
MotionDetectedMillis = millis();
ledReady = true;
}
if (ledReady)
{
if ((unsigned long)(currentMillis - MotionDetectedMillis) >= turnOnDelay)
{
while (brightness < 255)
{
brightness = brightness + fadeAmount;
analogWrite(LedPin, brightness);
delay(30);
}
// setup our next "state"
ledState = true;
// save when the LED turned on
ledTurnedOnAt = currentMillis;
ledReady = false;
}
}
// see if we are watching for the time to turn off LED
if (ledState)
{
if ((unsigned long)(currentMillis - ledTurnedOnAt) >= turnOffDelay)
{
while (brightness >0)
{
brightness = brightness - fadeAmount;
analogWrite(LedPin, brightness);
delay(30);
}
ledState = false;
}
}
}
what you want is a state machine; you have to keep track in the loop of which state you currently are and what is the condition to trigger next state; because your loop will execute an action only if something happen to trigger a new state, instead of using delay() you will use a 'waiting for timeout' state (remember to keep a way to know what is the next state).
If this confuses you, imagine each state as step in the instruction manual;
turn on led 1, wait x, turn on led 1 a bit more, wait y, etc..
And because you have no more delay, before or after manage the "normal flow" of the state, you can add some special case like "if input 2 is high and current state is X or Y then set current state to Z".
So, what you want could be easily archived with an interrupt, but with a simple project like this one, you can get the same result just by removing the delay(OnTime) function.
If you want more on the interrupts of the atmega328 which is the one the Arduino UNO uses, go here

create a timed 3 state push button in arduino

Due to a shortage of pins on a esp8266 in arduino, I need a way to detect a button where;
momentary press runs snooze()
15 sec press runs conf_Desk()
30 sec press runs calibration()
the preconfig;
int buttonPin = D7;
pinMode( buttonPin , INPUT_PULLUP);
All while allowing the main loop to function.
If I trap an interrupt it stops cycling the loop(), a few millisec delays are OK but seconds of delay is too much.
The functions are already written I just can't seem to come up how to track and confirm the hold length to call the right function based on the right timing without stopping other process that must stay cycling.
Using an interrupt is, IMHO, overkill. Interrupts are made for when you need to reply to a stimulus quickly, and a button press is something slow. Unless your loop is blocking, thing that I highly discourage.
ADDITION: as Patrick pointed out in the comments, there is in fact another reason to use interrupts: sleep mode. In fact, if you want to go into sleep mode and wake with a button, you have to use interrupts to wake later. However usually you have to do something continuously and not only reply to the button inputs. If you can't go into sleep mode, using an interrupt for button detection is still overkill in my opinion.
So, if you properly designed your loop not to block, here is a brief part of code doing what I think you should implement:
uint8_t buttonState;
unsigned long lastPressTime;
void setup()
{
...
buttonState = digitalRead(buttonPin);
lastPressTime = 0;
}
void loop()
{
uint8_t currRead = digitalRead(buttonPin);
if (buttonState != currRead)
{ // Button transition
buttonState = currRead;
if (buttonState == LOW)
{ // Button pressed, start tracking
lastPressTime = millis();
}
else
{ // Button released, check which function to launch
if (lastPressTime < 100)
{} // Discard (it is just a bounce)
else if (lastPressTime < 15000)
snooze();
else if (lastPressTime < 30000)
conf_Desk();
else
calibration();
}
}
...
}
Since you made three very distant intervals though, I think that this part better suits your needs:
if ((lastPressTime > 100) && (lastPressTime < 7000))
snooze();
else if ((lastPressTime > 12000) && (lastPressTime < 20000))
conf_Desk();
else if ((lastPressTime > 26000) && (lastPressTime < 40000))
calibration();
So you define validity ranges, so if someone presses the button for 10 seconds nothing happens (this is useful because if someone presses the button for 14.9 seconds in the previous code it will trigger the snooze function).
i would use a simple state machine structure with two global vars to avoid complex nested logic:
int buttonDown = 0;
unsigned long buttonStart;
void loop(){
int snapshot = digitalRead(buttonPin);
if(!buttonDown && snapshot ){ //pressed, reset time
buttonDown = 1; // no longer unpressed
buttonStart = millis(); // when it was pressed
}
if(buttonDown && !snapshot ){ //released, count time
buttonDown = 0; // no longer pressed
int duration = millis() - buttonStart; // how long since pressed?
// now the "event part"
if(duration>30000) return calibration();
if(duration>15000) return conf_Desk();
snooze();
}
sleep(1); // or whatever
}
Interrupt service routines should be a short as possible. You don't have to wait inside the ISR and suspend your main loop for seconds.
Just use two different ISRs for a rising and a falling edge.
When the button is pressed, ISR1 starts a timer, when it is released ISR2 stop it and triggers whatever is necessary depending on the time passed.
Make sure your button is debounced.
https://www.arduino.cc/en/Reference/attachInterrupt
Another way to do this is with a pointer-to-function based state machine.
The advantage on this is that you can easily introduce more functionalities to your button (say, another function called at 45 seconds).
try this:
typedef void(*state)();
#define pressed (millis() - lastPressed)
void waitPress();
void momentPress();
void shortPress();
void longPress();
state State = waitPress;
unsigned long lastPressed;
int buttonState;
int buttonPin = 7;// or whathever pin you use
void snooze(){} // stubs for your functions
void conf_Desk(){}
void callibration(){}
void waitPress()
{
if (buttonState == HIGH)
{
lastPressed = millis();
State = momentPress;
return;
}
else
return;
}
void momentPress()
{
if (buttonState == LOW)
{
snooze();
State = waitPress;
return;
}
if (pressed > 15000)
State = shortPress;
return;
return;
}
void shortPress()
{
if (buttonState == LOW)
{
conf_Desk();
return;
}
if (pressed > 30000)
State = longPress;
return;
return;
}
void longPress()
{
if (buttonState == LOW)
{
callibration();
return;
}
return;
}
void loop()
{
buttonState = digitalRead(buttonPin);
State();
}

Resources