28 May 2015

Toggle button

I am new to Arduino and C++ and as usual for first-timers, the first project has to do something with some LED-s and buttons.
Nothing complicated, but looking at examples I realized quite soon that there is lot of coding that has to be done to have proper toggle button functionality with debounce handling. Too much  for such a simple thing as push button. And if there happens to be multiple buttons, then it gets even worse. So, there has to be some library that handles all this, there probably is, but I decided that I need to practice some C++ and create my own library. So, couple of days tinkering and here is my first library, named PBtnToggle found in GitHub.
This library handles debouncing, has button press event, release event and long press event. And does not use delay(). User can decide what happens after long press - will it trigger release event after button release or trigger after next button press and release. I am quite happy what I created. There is some changes I would like to do at some point, but I decided that current version is ready for public.

Here's a demo video:


So, how much coding have to be done to use this library?
Lets take Switch example code from Arduino website.
int inPin = 2; // the number of the input pin
int outPin = 13; // the number of the output pin
int state = HIGH; // the current state of the output pin
int reading; // the current reading from the input pin
int previous = LOW; // the previous reading from the input pin
// the follow variables are long's because the time, measured in miliseconds,
// will quickly become a bigger number than can be stored in an int.
long time = 0; // the last time the output pin was toggled
long debounce = 200; // the debounce time, increase if the output flickers
void setup()
{
pinMode(inPin, INPUT);
pinMode(outPin, OUTPUT);
}
void loop()
{
reading = digitalRead(inPin);
// if the input just went from LOW and HIGH and we've waited long enough
// to ignore any noise on the circuit, toggle the output pin and remember
// the time
if (reading == HIGH && previous == LOW && millis() - time > debounce) {
if (state == HIGH)
state = LOW;
else
state = HIGH;
time = millis();
}
digitalWrite(outPin, state);
previous = reading;
}
view raw switch.ino hosted with ❤ by GitHub
Let's change this to use PBtnToggle.
  1. Include PBtnToggle library:
    #include "PBtnToggle.h"
    PBtnToggle* btn = new PBtnToggle(2, HIGH); // init button
    int outPin = 13; // the number of the output pin
    int state = HIGH; // the current state of the output pin
    void setup()
    {
    pinMode(outPin, OUTPUT);
    btn->onPress(buttonPressed);
    }
    void buttonPressed(int btn, int status)
    {
    if (state == HIGH)
    state = LOW;
    else
    state = HIGH;
    digitalWrite(outPin, state);
    }
    void loop()
    {
    btn->check(); // mandatory method that has to be called from loop, optionally also from attached interrupt
    }
  2. Init PBtnToggle object, specify pin where button is wired and pin state when button is pressed down. This will also set pin mode to INPUT:
    #include "PBtnToggle.h"
    PBtnToggle* btn = new PBtnToggle(2, HIGH); // init button
    int outPin = 13; // the number of the output pin
    int state = HIGH; // the current state of the output pin
    void setup()
    {
    pinMode(outPin, OUTPUT);
    btn->onPress(buttonPressed);
    }
    void buttonPressed(int btn, int status)
    {
    if (state == HIGH)
    state = LOW;
    else
    state = HIGH;
    digitalWrite(outPin, state);
    }
    void loop()
    {
    btn->check(); // mandatory method that has to be called from loop, optionally also from attached interrupt
    }
  3. In setup(), register function that will be called when button is pressed down:
    #include "PBtnToggle.h"
    PBtnToggle* btn = new PBtnToggle(2, HIGH); // init button
    int outPin = 13; // the number of the output pin
    int state = HIGH; // the current state of the output pin
    void setup()
    {
    pinMode(outPin, OUTPUT);
    btn->onPress(buttonPressed);
    }
    void buttonPressed(int btn, int status)
    {
    if (state == HIGH)
    state = LOW;
    else
    state = HIGH;
    digitalWrite(outPin, state);
    }
    void loop()
    {
    btn->check(); // mandatory method that has to be called from loop, optionally also from attached interrupt
    }
  4. Create function that will be called by library, registered in setup(). This function will contain anything that has to be done in button press:
    #include "PBtnToggle.h"
    PBtnToggle* btn = new PBtnToggle(2, HIGH); // init button
    int outPin = 13; // the number of the output pin
    int state = HIGH; // the current state of the output pin
    void setup()
    {
    pinMode(outPin, OUTPUT);
    btn->onPress(buttonPressed);
    }
    void buttonPressed(int btn, int status)
    {
    if (state == HIGH)
    state = LOW;
    else
    state = HIGH;
    digitalWrite(outPin, state);
    }
    void loop()
    {
    btn->check(); // mandatory method that has to be called from loop, optionally also from attached interrupt
    }
  5. Call check() function, without this PBtnToggle will not work:
    #include "PBtnToggle.h"
    PBtnToggle* btn = new PBtnToggle(2, HIGH); // init button
    int outPin = 13; // the number of the output pin
    int state = HIGH; // the current state of the output pin
    void setup()
    {
    pinMode(outPin, OUTPUT);
    btn->onPress(buttonPressed);
    }
    void buttonPressed(int btn, int status)
    {
    if (state == HIGH)
    state = LOW;
    else
    state = HIGH;
    digitalWrite(outPin, state);
    }
    void loop()
    {
    btn->check(); // mandatory method that has to be called from loop, optionally also from attached interrupt
    }
Atleast for me, this code looks a lot nicer. This is end result that does same thing that example code from Arduino webpage.
#include "PBtnToggle.h"
PBtnToggle* btn = new PBtnToggle(2, HIGH); // init button
int outPin = 13; // the number of the output pin
int state = HIGH; // the current state of the output pin
void setup()
{
pinMode(outPin, OUTPUT);
btn->onPress(buttonPressed);
}
void buttonPressed(int btn, int status)
{
if (state == HIGH)
state = LOW;
else
state = HIGH;
digitalWrite(outPin, state);
}
void loop()
{
btn->check(); // mandatory method that has to be called from loop, optionally also from attached interrupt
}
Compiler log for original code:
Sketch uses 1,694 bytes (0%) of program storage space. Maximum is 253,952 bytes.
Global variables use 27 bytes (0%) of dynamic memory, leaving 8,165 bytes for local variables. Maximum is 8,192 bytes.
Compiler log for PBtnToggle example code:
Sketch uses 2,708 bytes (1%) of program storage space. Maximum is 253,952 bytes.
Global variables use 25 bytes (0%) of dynamic memory, leaving 8,167 bytes for local variables. Maximum is 8,192 bytes.
So library takes about 1KB extra flash and memory footprint is about same. But your sketch will be probably smaller if you have multiple buttons.

Please, let me know if this library is useful for you and if you have some ideas that could improve this library, then please let me know here in comments or in GitHub for more technical ideas/fixes.
Also, if you know any other libraries that do something similar, then let me know about it. I would like to compare them.

No comments:

Post a Comment