Increase integer by one for each interrupt

Hi fellas!

It’s my first post here so hello to you all. :slight_smile: From what I’ve seen so far this seems to be a great community full of helpful people. I got my Electron earlier this week and it’s been a smooth ride so far until now when I can’t seem to resolve the issue I have.

I have a what it seems like a trivial issue; when a button is pressed (connected to in this case B0), an integer should increase by one and then back to the main loop waiting for another button interaction and there goes the loop. Simple enough, right?

What happens though is that I can’t seem to control the interrupts, making the integer increase by more than one. A snippet of my code below

void  incButton_ISR() {
    inc=true;
}

pinMode(incButton, INPUT_PULLDOWN);    // sets pin as input

attachInterrupt(incButton, incButton_ISR, RISING);

void loop() {
if (inc){
    noInterrupts();
    Count++;
    inc=false;
    while(digitalRead(incButton)){} // wait for the button to be released
    interrupts();   // enable interrupts
}

My thought is to disable the interrupt as soon as I get into the if statement, increase my integer, wait for the button to be released and then enable the interrupts again.

However this solution works for about ~50% of the time, where half of the clicks increase the integer by one and the other half increases it by two.

Any suggestions? :slight_smile:

How have you defined your Count variable. Is it declared as a volatile?

One thing you need to be aware of is that buttons “bounce”. That is, even though you’ve pressed the button down and kept it there, the contacts of the button actually bounced and went on/off a number of times before settling in the down/on position. Interrupts will pick up this bounce and will be “fired” a number of times.

You can de-bounce the button in software (add a delay and then check the state again) or do it in hardware (you’ll need some additional components to do this, such as a Schmitt trigger).

Blocking an interrupt is never a good idea. You can set a “State” variable in the ISR and then in the loop you can act on the state of the button. Your ISR could be registered for CHANGE rather than RISING and in the ISR, read the value of the digital pin and set the state accordingly. If de-bouncing in software, wait for a few milliseconds and then read the state.

@ftideman, if you do a search in the community for “interrupt debounce” you will find lots of good ideas and examples. :wink:

Ah, didn’t know the proper terminology for this phenomena so couldn’t find good topics that investigates this, but I will look into that now! :slight_smile:

On the topic of using delays as a method of preventing debouncing, it doesn’t at least feel like a waterproof solution? But I see that it could work of course.

Thanks for your help guys!

@ftideman, no, you really cannot use a delay, but you can use a timer:

const int myPin = D4;
volatile int count = 0;
const int debouncePeriod = 50; // <<< you may need more or less here

void setup(void)
{
  attachInterrupt(myPin, myInterruptHandler, CHANGE); // or RISING or FALLING, your preference
}

void loop(void)
{
  static int lastCount = 0;
  if(lastCount != count)
  {
    //do something here
    lastCount++;  // or lastCount = count; // depending if you need to trap every event
  }
}

void myInterruptHandler(void)
{
  static volatile int = lastInterruptMillis = 0;
  if(millis() - lastInterruptMillis > debouncePeriod)
  {
    count++;
    lastInterruptMillis = millis();
  }
}

2 Likes

@BulldogLowell,

Since the lastInterruptMillis variable is local, does it need to be volatile?

Thanks for your input guys :slight_smile: I played around with some suggested solutions and landed in:

#define TIMEOUT 100
volatile int count = 0;
    
void  incButton_ISR() {
    if ((millis() - timestamp) < TIMEOUT){
        timestamp = millis(); // added this which helped circumvent the bouncing issues
        return; // 
    }
timestamp = millis();
inc = true;
}

void setup() {
pinMode(incButton, INPUT_PULLDOWN);    // sets pin as input
delay(50);  // magic delay
attachInterrupt(incButton, incButton_ISR, FALLING);
}

void loop () {
    if (inc){
        count++;
        printDisplay();
        inc=false;
    }
 }

@shiv,

You are right, since the compiler won’t optimize out that local variable.

OP’s timestamp variable (it must be global, I guess) should be declared volatile as he is using it above.

Now something very strange has occured. Was thinking of creating a new topic but since this is related to the first issue I had I’ll continue on this one.

To back up a bit, the end goal is to develop a simple hardware counter where you can increase or decrease the counter using two buttons, and then send the amount of clicks made to the cloud. Initially I only used digitalRead(XX) to trigger a button but since the particle.publish blocks that while running I implemented the interrupt to make sure no button interactions were missed. But now, with the debouncing issued resolved as per above, I’ve implemented a timer that runs every six seconds to set a flag which in turn gets checked in the main loop. When the flag is set it executes particle.publish that sends data to the cloud on the amount of button clicks over the last time window (six seconds). However, when I include the particle.publish function the buttons/counters gets triggered from nowhere and messes up the count. This happens on average once per second and I can’t get my head around why this is. Code snippet below:

    #define TIMEOUT 100
    const int incButton = D4; // started with B0 & B1 for inc & dec but tried to switch around but it didnt resolve this issue
    const int decButton = B0;
    
    volatile bool inc = false;
    volatile bool dec = false;
    volatile int totCount = 0;
    volatile int totIn = 0;
    volatile int totOut = 0;
    
    void  incButton_ISR() {
        if ((millis() - timestamp) < TIMEOUT){
            timestamp = millis();
            return; // 
        }
    timestamp = millis();
    inc = true;
    }
    
    void  decButton_ISR() { 
        if ((millis() - timestamp) < TIMEOUT){
            timestamp = millis();
            return; // 
        }
    timestamp = millis();
    dec = true;
    }
    
    Timer timer(6000, runEverySix); // Send every 6 seconds
    
    void setup() {
    Serial.begin(9600);
    Wire.begin();
    timer.start();
    oled.begin(SSD1306_SWITCHCAPVCC, 0x3C);  // initialize with the I2C addr 0x3D (for the 128x64)
    fuel.quickStart(); // Start Fuel Gauge
    startScreen();
    printDisplay(); // Print First screen
    delay(50); // magic delay
    pinMode(incButton, INPUT_PULLDOWN);    // sets pin as input
    pinMode(decButton, INPUT_PULLDOWN);    // sets pin as input
    delay(100);  // magic delay
    attachInterrupt(incButton, incButton_ISR, FALLING);
    attachInterrupt(decButton, decButton_ISR, FALLING);
    }
    
    void runEverySix(){ 
    runUpdate=1; 
    } 
    void loop() { 
        if (inc){
            totCount++;
            totIn++;
            printDisplay();
            inc=false;
        }
    
        if (dec){
            if(totCount>0){
                totCount--;
                totOut++;
                printDisplay();
            }
        dec=false;
        }
    
        if (runUpdate==1){
            incSend=totIn-previousIn;
            decSend=(totOut-previousOut)*-1;
        
            if ((incSend || decSend) > 0){
                char buf[255];
                snprintf(buf, sizeof(buf), "{\"inc\":\"%u\",\"dec\":\"%d\"}", incSend, decSend);
                Particle.publish("sendClicks", buf, 60, PRIVATE);
                Serial.print("Inc sent: ");
                Serial.println(incSend);
                Serial.print("Dec sent: ");
                Serial.println(decSend);
            }
            else offset++;
            previousIn=totIn;
            previousOut=totOut;
            runUpdate=0;
        }
}

So basically, the counter jumps up and down all the time with the particle.publish function present. If I comment it out (specifically the line “Particle.publish(“sendClicks”, buf, 60, PRIVATE);”) it works like a charm, which confuses me.

Any ideason what’s wrong here? :slight_smile:

Something looks rather odd with this code.
Where do these if-blocks belong? :confused:

BTW, proper indetation helps readability.

Oh shiet, since I only added parts of the code I missed out including the void loop(){}, edited my post above. Sorry bout that!

1 Like

Is there potentially another way of ensuring that no button interactions are missed without using interrupts? So make sure pins are triggered even though the particle.publish event is running? Interrupts seems to be a tortuous way to go. :confused:

@ftideman, personally, I thing the ISR method is total overkill here since you are only setting flags that you service in loop(). It might be a LOT simpler to user the clickButton library in Build to service your buttons entirely in loop(). The library not only debounces but also supports multi-clicks and long clicks for extra functions. :smiley:

@peekay123 interesting :slight_smile: I haven’t looked into the clickButton library, will do that now. Can an implementation of that library still ensure that button interactions done whilst particle.publish is running will get registered? The application I’m developing is required to be 100% accurate when it comes to registered button interactions.

@ftideman, the buttons are polled in loop() while publishing is done by the system firmware in a separate thread. You may want to consider running with SYSTEM_THREA(ENABLED) to allow the user code to run in its own thread. Whatever you do, be sure to not put any blocking code, such as delay(), in loop() that could slow down the button polling. :wink:

1 Like

@peekay123 Yeah I tried skipping the interrupts and using basic digitalRead on my buttons, and for some reason it seems to run really smooth without being interrupted by the publishing. Strange since I experienced that last week, hence the reason I started looking into interrupts. Probably something I messed up in my previous code :expressionless:

So now its all working as it’s supposted to, without the cumbersome interrupts :smiley:

@ftideman, are you using clickButton then?

@peekay123 No, I decided to discard that library to keep it as simple as possible, and also since I do not need multiple functions per button (as of right now at least :slight_smile: ). For debouncing (or at least not making it fire more than once per interaction) I use

if (digitalRead(incButton)){
    totCount++;
    totIn++;
    printDisplay();
    while(digitalRead(incButton)){} // wait until released
}
1 Like