Arduino Interrupt won't ignore falling edge - arduino

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;
}
}

Related

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.

AC Dimmer EpS32

I have an application on to control ESP32 Light Dimmer from the temperature read from a temperature sensor. The rule is very simple, when my sensor reads the temperature of more than 27ºC the lamp should be turned off by the Dimmer. However, this is not happening.
What the code does is, when the system is turned on the lamp turns on and the temperature is read from time to time, but when the temperature exceeds 27ºC the dimmer does not turn off the lamp. I think it might be something I'm doing wrong in the zero_crosss_int routine, because when the temperature reaches its limit the message "TRIAC OFF" is displayed.
Below the code used.
#define ZERO_CROSS 2
#define DIMMER_CONTROL 4
int dimming=64;
float programedTemp = 27.0;
int halfDimming=128;
int maxDimming=64;
void power(void *parameters){
tempSensor.requestTemperaturesByIndex(0);
temp=tempSensor.getTempCByIndex(0);
if(temp<programedTemp){
dimming=maxDimming;
if(temp<(programedTemp-1.0)){
dimming=maxDimming;
} else if(temp<programedTemp){
dimming++;
}
} else if(temp>programedTemp+0.9){
dimming=128;
}else{
dimming=halfDimming;
}
delay(4000);
}
void zero_crosss_int() {
if(dimming>=128){
delayMicroseconds(8.333);
digitalWrite(DIMMER_CONTROL, LOW); // triac Off
Serial.println((String) "=====>>> TRIAC OFF <<<=====");
}else{
int dimtime = (65*dimming);
delayMicroseconds(dimtime); // Off cycle
digitalWrite(DIMMER_CONTROL, HIGH); // triac firing
delayMicroseconds(8.333);
digitalWrite(DIMMER_CONTROL, LOW); // triac Off
}
}
void setup() {
pinMode(DIMMER_CONTROL, OUTPUT);
pinMode(ZERO_CROSS, INPUT);
attachInterrupt(ZERO_CROSS, zero_crosss_int, RISING);
Serial.begin(115200);
xTaskCreatePinnedToCore(power,"controlDimm",10000,NULL,0,&mainsPower,0);
}
void loop() {
}
You're doing way too much in your interrupt handler. It's amazing that it isn't just crashing constantly... the only reason that's not happening is likely because you don't do anything in loop().
It's not safe to call most of the functions you're calling in the interrupt handler. Interrupt handlers need to run for as little time as possible - other interrupts may be locked out and they may interfere with the network stack and other housekeeping functions. You absolutely should not be calling delayMicroseconds() or any Serial methods in an interrupt handler, or spending any more time there than is absolutely necessary. Almost all ESP-IDF or Arduino Core functions are not safe to call from inside an interrupt handler - the interrupt handler may interrupt another call to them while they're in an inconsistent state, or may change hardware settings while they're in the middle of using that hardware.
Your code would be much better if you structured it like this:
volatile bool zero_crossing_flag = false;
void IRAM_ATTR zero_cross_int() {
zero_crossing_flag = true;
}
void loop() {
if(zero_crossing_flag) {
zero_crossing_flag = false;
if(dimming>=128){
delayMicroseconds(8.333);
digitalWrite(DIMMER_CONTROL, LOW); // triac Off
Serial.println((String) "=====>>> TRIAC OFF <<<=====");
}else{
int dimtime = (65*dimming);
delayMicroseconds(dimtime); // Off cycle
digitalWrite(DIMMER_CONTROL, HIGH); // triac firing
delayMicroseconds(8.333);
digitalWrite(DIMMER_CONTROL, LOW); // triac Off
}
}
}
The IRAM_ATTR attribute on zero_cross_int() tells the compiler that this code must always be available. If you write an interrupt handler without IRAM_ATTR it's basically luck if it executes properly.
Restructuring your code this way will probably not solve the problem you're asking about, but it will allow it to run in a stable, reproducible way, which it's unlikely to the way it's written now.
Use your ZCD input to generate an interrupt. In the ISR start a Timer which generates another interrupt within 0ms(Full power) to 10ms(completely off) of the ZCD interrupt.
I don't understand to Switch off the lamp why are you using TRIAC instead of a Relay.

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

arduino keyboard does not release cause freeze

When I upload this script, my normal keyboard is not usable anymore and I think the right alt key is stuck in, even if I unplug the arduino the problem is still there.
The goal of this is to have a push to talk pedal for teamspeak. I was able to get around the problem using another script that passes itself as a controller but this causes other issues with games since they think I am using a controller all of a sudden.
From what I understand, this code should loop, while the pin is active, it should simulate the right alt key being pressed. When it is done it should release all key pressed by the script. It is hard to debug this since whenever I upload it I have to spend 10 minutes to be able to use my keyboard again.
Any help would be appreciated. Extra info, I am using the arduino leonardo.
#include <Keyboard.h>
void setup() {
// Initialize Button Pins
Keyboard.begin();
pinMode(9, INPUT_PULLUP);
}
void loop() {
Keyboard.releaseAll();
while (digitalRead(9) == HIGH) {
Keyboard.press(KEY_RIGHT_ALT);
delay(500);
}
delay(500);
Keyboard.releaseAll();
// wait for new window to open:
}
Note: without the actual hardware at my disposal for some tests, it is hard to know what is going wrong, so I'll try to review your code looking for possible point of failures.
According to the docs, press() functions as if a key were pressed and held down on the keyboard.
According to these other docs, INPUT_PULLUP effectively inverts the behavior of the INPUT mode, where HIGH means the sensor is off, and LOW means the sensor is on.
Therefore, a simple modification would be:
#include <Keyboard.h>
void setup() {
// Initialize Button Pins
Keyboard.begin();
pinMode(9, INPUT_PULLUP);
}
void loop() {
static bool pressed = false;
// does the pedal close the circuit with GND?
if (digitalRead(9) == HIGH) { // no
if (pressed) {
Keyboard.release(KEY_RIGHT_ALT);
pressed = false;
}
} else { // yes
if (!pressed) {
Keyboard.press(KEY_RIGHT_ALT);
pressed = true;
}
}
delay(500); // waste time
}
Does this code display the same issues?

Reading signal with interrupts using arduino

I am trying to read an input signal from a source that is a PWM signal. In my research I have found some useful articles out found here: http://www.instructables.com/id/Arduino-Frequency-Detection/?ALLSTEPS and here: http://www.camelsoftware.com/2015/12/25/reading-pwm-signals-from-an-rc-receiver-with-arduino/. The first article is a little beyond my experience level, and would not be helpful if I used anything other then a uno, although it seems to perform extremely well. The second shows a method I was better able to understand.
I have been using the following code somewhat successfully:
#define input_pin 2
volatile unsigned long timer_start;
volatile int pulse_time;
volatile int last_interrupt_time;
void calcSignal()
{
last_interrupt_time = micros();
if(digitalRead(input_pin) == HIGH)
{
timer_start = micros();
}
else {
if(timer_start != 0)
{
//record the pulse time
pulse_time = ((volatile int)micros() - timer_start);
//restart the timer
timer_start = 0;
}
}
}
void setup()
{
timer_start = 0;
attachInterrupt(0, calcSignal, CHANGE);
Serial.begin(115200);
}
void loop()
{
Serial.println(pulse_time);
delay(20);
}
The problem with this setup for my application is the interrupt is only triggered by a change in state, when realistically I need to know the duration of how long it is high and how long it is low. A picture of the ideal signals can be seen here with various duty cycles (https://www.arduino.cc/en/Tutorial/SecretsOfArduinoPWM). I tried changing the interrupt mode from CHANGE to LOW and HIGH, but did not get any creditable results, as it only output zeros on the serial monitor. Is there something I am missing or an alternative library/method that can be used? I am somewhat new to programming, so I have some understanding, but am by no means a programmer.
What about something like this:
unsigned long timeout = 100000L; // reasonable timeout around 100ms?
unsigned long high_us = pulseIn(pinNo, HIGH, timeout);
unsigned long low_us = pulseIn(pinNo, LOW, timeout);
unsigned long thousandths = (high_us*1000) / (high_us+low_us);
However it won't be able to measure one period HI/LO. It'll be measured over two of them: >HI< lo hi >LO<.
And if it's not what you want, you can dig into AVR DataSheet and try to get Timer Input Capture interrupts working.
I think you can go with your method, just by adding another timer:
void calcSignal() {
if(digitalRead(input_pin) == HIGH)
{ // transition from LOW to HIGH
timerH_start = micros();
//record the pulse time
pulse_time_L = ((volatile int)micros() - timerL_start);
}
else { // transition from HIGH to LOW
timerL_start = micros();
//record the pulse time
pulse_time_H = ((volatile int)micros() - timerH_start);
}
}

Resources