A simple Arduino library to make interfacing and reacting on button events easier.
Overview:
This library supports:
- Push buttons (with or without pullup or inverted logic)
- On/Off Switches
- Rotary Encoders
- The Lameboy :D
- Any Analog input (i.e. ButtonMatrix)
- Analog-Stick
- PlayStation2 Gamepad
- I2C Encoder
You can not only read out the current state of the button, but also if it's:
- pushed
- released
- clicked
- double clicked
- holding
It also works with buttons that are connected to a PCF8574, PCF8575 or MCP23017 GPIO expander!
- Download the source code from GitHub.
- Unzip and rename the Folder name to "SimpleButton".
- Paste it in your Library folder (Usually located somewhere at documents/Arduino/libraries).
- Restart the Arduino IDE.
Also have a look at the examples.
#include <SimpleButton.h>
using namespace simplebutton;
Normal logic:
The usual way of connecting a push button as described here.
// creates button on pin 12
Button* b = new Button(12);
Inverted logic:
If you got any special kind of button that is HIGH on idle and goes to LOW when it's pressed, you can invert the logic.
// creates inverted button on pin 12
Button* b = new Button(12, true);
Pullup button:
This will use the internal resistor and you won't need to add an external one. Also described here.
// creates pullup button on pin 12
Button* b = new ButtonPullup(12);
You can use a switch as a button. Whenever you switch it, it will count it as a button click.
// creates switch on pin 12
Button* b = new Switch(12);
// creates a rotary encoder connected to pin 5 and pin 4 and switch connected pin 3 (set switch to 255 to disable it)
RotaryEncoder* myEncoder = new RotaryEncoder(5, 4, 3);
// rotary encoder connected to the PCF on pin 2 and pin 3
RotaryEncoder* myEncoder = new RotaryEncoder(exp, 2, 3, 255);
// I2C encoder at address 0x30
RotaryEncoderI2C* myEncoder = new RotaryEncoderI2C(0x30);
// in case you have a rotary encoder that does 2 steps with each turn:
// (default is 1 step per turn)
myEncoder->setEncoding(2);
// set a start position for the counter
myEncoder->setPos(10);
// set a minimum value threshold
myEncoder->setMin(-20);
// set a maximum value threshold
myEncoder->setMax(20);
// invert the directions
myEncoder->setInverted(true);
// enable looping (when the counter goes below the minimum it will be set to the maximum and vice versa)
myEncoder->enableLoop(true);
// ===== for the I2C encoder only =====
// enables a interrupt pin at gpio 12 with pullup enabled (true)
myEncoder->enableInterrupt(12, true);
// enables the dual-color led
myEncoder->enableLed(true);
// set LED-A to 255 (on) and LED-B to 0 (off)
myEncoder->setLed(255, 0);
// !!!!! IMPORTANT !!!!!
// to enable all our config changes
myEncoder->begin();
// create an analog button on pin A0 that is pushed when it reads a value between 0 and 20
ButtonAnalog* b = ButtonAnalog(A0, 0, 20);
// creates an analog stick that has X connected to A0, Y connected to A1 and the switch connected to pin 5
// (set switch to 255 to disable it)
AnalogStick* analogStick = new AnalogStick(A0, A1, 5);
// set the logic to read values from 0 to 1024 with 25% tolerance
analogStick->setLogic(1024, 25);
// set the logic to read values from 0 to 256 with 25% tolerance
analogStick->setLogic(256, 25);
To learn more about the wiring, protocol and usage of the PlayStation2 controller, please have a look here.
It doesn't really matter (on most Arduino's) what pins you use to connect the controller.
// create the gamepad
PS2Gamepad* gamepad = new PS2Gamepad();
// connect to it
gamepad->setup(CLOCK_PIN, COMMAND_PIN, ATTENTION_PIN, DATA_PIN);
// check for errros
bool isConnected = gamepad->connected();
String errorMessage = gamepad->getError();
// start i2c
Wire.begin();
// 0x20 = i2c address (use a i2c scanner sketch to find the right address)
// create a PCF8574
GPIOExpander* exp = new PCF8574(0x20);
// create a PCF8575
GPIOExpander* exp = new PCF8575(0x20);
// create a MCP23017
GPIOExpander* exp = new MCP23017(0x20);
// creates a push button connected to the PCF on pin 0
Button* b = new ButtonGPIOExpander(exp, 0);
// creates a pullup button connected to the PCF on pin 1
Button* b = new ButtonPullupGPIOExpander(exp, 1);
// rotary encoder connected to the PCF on pin 2 and pin 3
RotaryEncoder* myEncoder = new RotaryEncoder(exp, 2, 3, 255);
// check for errors
bool isConnected = exp->connected();
String errorMessage = exp->getError();
b
is a pointer to a created button (see above).
Important status methods a Button
object has:
bool pushed();
bool released();
bool clicked();
bool clicked(uint32_t minPushTime);
bool clicked(uint32_t minPushTime, uint32_t minReleaseTime);
bool doubleClicked();
bool doubleClicked(uint32_t minPushTime);
bool doubleClicked(uint32_t minPushTime, uint32_t timeSpan);
bool doubleClicked(uint32_t minPushTime, uint32_t minReleaseTime, uint32_t timeSpan);
bool holding();
bool holding(uint32_t interval);
Example usage:
// first update the button
b->update();
// react on double click
if(b->doubleClicked()){ ... }
// with minimum push time in ms (default = 40)
if(b->doubleClicked(uint32_t minPushTime)) { ... }
// with minimum push time in a given time span in ms (default = 650)
if(b->doubleClicked(uint32_t minPushTime, uint32_t timeSpan)) { ... }
// react on a click
if(b->clicked()){ ...}
// with a minimum push time in ms (default = 40)
if(b->clicked(uint32_t minPushTime)) { ... }
// if button is beeing hold
if(b->holding()){ ... }
// with custom time interval in ms (default = 250)
if(b->holding(uint32_t interval)){ ... };
// when the button is beeing pushed
if(b->pushed()) { ... }
// when the button is released
if(b->released()) { ... }
// you can also read the button state out directly
bool currentButtonState = b->getState();
// read out the analog value
uint8_t value = analogButton->getValue();
These are the Button
s a RotaryEncoder
object has:
Button* button;
Button* clockwise;
Button* anticlockwise;
Example usage:
// update the encoder
myEncoder->update();
// read out the position counter
int position = myEncoder->getPos();
// if rotary encoder switch was pushed
bool clicked = myEncoder->clicked();
// here using the Button object
bool clicked = myEncoder->button->clicked();
// read out the directions
bool incremented = myEncoder->incremented();
bool decremented = myEncoder->decremented();
// read out the directions using the Button objects
bool incremented = myEncoder->clockwise->clicked();
bool decremented = myEncoder->anticlockwise->clicked();
// read out if the counter hit a threshold
bool hitMinValue = myEncoder->minVal();
bool hitMaxValue = myEncoder->maxVal();
// ==== for I2C encoder only =====
// read out if interrupt pin changed (will always be true if disabled)
bool interrupt = myEncoder->interrupt();
// if the interrupt pin is enabled, you can also use the update function
// to see if something changed
bool changed = myEncoder->update();
These are the Button
s a AnalogStick
object has:
Button* button;
ButtonAnalog* up;
ButtonAnalog* down;
ButtonAnalog* left;
ButtonAnalog* right;
Example usage:
// read out the values
uint8_t x = analogStick->left->getValue();
uint8_t y = analogStick->up->getValue();
// access the switch button (if you added one)
if (analogStick->button->clicked()) Serial.println("clicked");
// react on any direction
if (analogStick->left->holding()) Serial.println("left holding");
if (analogStick->right->holding()) Serial.println("right holding");
if (analogStick->up->holding()) Serial.println("up holding");
if (analogStick->down->holding()) Serial.println("down holding");
These are the Button
s a PS2Gamepad
object has:
ButtonAnalog* up;
ButtonAnalog* down;
ButtonAnalog* left;
ButtonAnalog* right;
ButtonAnalog* l1;
ButtonAnalog* l2;
ButtonAnalog* r1;
ButtonAnalog* r2;
ButtonAnalog* square;
ButtonAnalog* triangle;
ButtonAnalog* x;
ButtonAnalog* circle;
Button* select;
Button* start;
AnalogStick* analogLeft;
AnalogStick* analogRight;
Example usage:
// getting the analog-stick values
uint8_t left_x = gamepad->analogLeft->left->getValue();
uint8_t left_y = gamepad->analogLeft->up->getValue();
uint8_t right_x = gamepad->analogRight->left->getValue();
uint8_t right_y = gamepad->analogRight->up->getValue();
// d-pad
if (gamepad->up->clicked()) Serial.println("up clicked");
if (gamepad->down->holding()) Serial.println("down clicked");
if (gamepad->left->clicked()) Serial.println("left clicked");
if (gamepad->right->clicked()) Serial.println("right clicked");
// L and R Buttons
if (gamepad->l1->clicked()) Serial.println("L1 clicked");
if (gamepad->l2->clicked()) Serial.println("L2 clicked");
if (gamepad->r1->clicked()) Serial.println("R1 clicked");
if (gamepad->r2->clicked()) Serial.println("R2 clicked");
// start & select
if (gamepad->select->clicked()) Serial.println("Select clicked");
if (gamepad->start->clicked()) Serial.println("Start clicked");
// triangle, circle, cross, square
if (gamepad->square->clicked()) Serial.println("Square clicked");
if (gamepad->triangle->clicked()) Serial.println("Triangle clicked");
if (gamepad->cross->clicked()) Serial.println("Cross clicked");
if (gamepad->circle->clicked()) Serial.println("Circle clicked");
// left analog stick
if (gamepad->analogLeft->button->clicked()) Serial.println("Stick-Left clicked");
if (gamepad->analogLeft->up->holding()) Serial.println("Stick-Left up");
if (gamepad->analogLeft->down->holding()) Serial.println("Stick-Left down");
if (gamepad->analogLeft->left->holding()) Serial.println("Stick-Left left");
if (gamepad->analogLeft->right->holding()) Serial.println("Stick-Left right");
// right analog stick
if (gamepad->analogRight->button->clicked()) Serial.println("Stick-Right clicked");
if (gamepad->analogRight->up->holding()) Serial.println("Stick-Right up");
if (gamepad->analogRight->down->holding()) Serial.println("Stick-Right down");
if (gamepad->analogRight->left->holding()) Serial.println("Stick-Right left");
if (gamepad->analogRight->right->holding()) Serial.println("Stick-Right right");
Each button can have multiple events, you can add them with following methods:
void setOnPushed(void (* fnct)());
void setOnReleased(void (* fnct)());
void setOnClicked(void (* fnct)());
void setOnClicked(void (* fnct)(), uint32_t minPushTime);
void setOnClicked(void (* fnct)(), uint32_t minPushTime, uint32_t minReleaseTime);
void setOnDoubleClicked(void (* fnct)());
void setOnDoubleClicked(void (* fnct)(), uint32_t minPushTime);
void setOnDoubleClicked(void (* fnct)(), uint32_t minPushTime, uint32_t timeSpan);
void setOnDoubleClicked(void (* fnct)(), uint32_t minPushTime, uint32_t minReleaseTime, uint32_t timeSpan);
void setOnHolding(void (* fnct)());
void setOnHolding(void (* fnct)(), uint32_t interval);
Here's an example:
button->setOnClicked([](){
Serial.println("Button clicked!");
});
rotaryEncoder->clockwise->setOnClicked([](){
Serial.println("Moved clockwise");
});
analogStick->left->setOnPushed([](){
Serial.println("Analog-left");
});
gamepad->up->setOnDoubleClicked([](){
Serial.println("Double clicked UP button");
});
This software is licensed under the MIT License. See the license file for details.