├── .gitignore ├── LICENSE ├── README.md ├── examples ├── AnalogStick │ └── AnalogStick.ino ├── Button │ └── Button.ino ├── Button_Event │ └── Button_Event.ino ├── Button_Matrix │ └── Button_Matrix.ino ├── Button_PCF8574 │ └── Button_PCF8574.ino ├── I2CEncoder │ └── I2CEncoder.ino ├── Lameboy │ └── Lameboy.ino ├── PlayStation2_Gamepad │ └── PlayStation2_Gamepad.ino ├── PullupButton │ └── PullupButton.ino ├── RotaryEncoder │ └── RotaryEncoder.ino ├── RotaryEncoder_PCF8574 │ └── RotaryEncoder_PCF8574.ino └── Switch │ └── Switch.ino ├── library.json ├── library.properties └── src ├── Buttons ├── AnalogStick.cpp ├── AnalogStick.h ├── Button.cpp ├── Button.h ├── ButtonAnalog.cpp ├── ButtonAnalog.h ├── ButtonGPIOExpander.cpp ├── ButtonGPIOExpander.h ├── ButtonPullup.cpp ├── ButtonPullup.h ├── ButtonPullupGPIOExpander.cpp ├── ButtonPullupGPIOExpander.h ├── PS2Gamepad.cpp ├── PS2Gamepad.h ├── RotaryEncoder.cpp ├── RotaryEncoder.h ├── RotaryEncoderI2C.cpp ├── RotaryEncoderI2C.h ├── Switch.cpp └── Switch.h ├── Events ├── ClickEvent.cpp ├── ClickEvent.h ├── DoubleclickEvent.cpp ├── DoubleclickEvent.h ├── Event.cpp ├── Event.h ├── HoldEvent.cpp ├── HoldEvent.h ├── PushEvent.cpp ├── PushEvent.h ├── ReleaseEvent.cpp └── ReleaseEvent.h ├── SimpleButton.h └── libs ├── GPIOExpander.cpp ├── GPIOExpander.h ├── MCP23017.cpp ├── MCP23017.h ├── PCF8574.cpp ├── PCF8574.h ├── PCF8575.cpp └── PCF8575.h /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Stefan Kremser 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SimpleButton 2 | A simple Arduino library to make interfacing and reacting on button events easier. 3 | 4 | **Overview:** 5 | - [Features](#features) 6 | - [Installation](#installation) 7 | - [Usage](#usage) 8 | - [Include the library](#include-the-library) 9 | - [Create a button](#create-a-button) 10 | - [Push Button](#push-button-setup) 11 | - [On/Off Switch](#onoff-switch-setup) 12 | - [Rotary Encoder](#rotary-encoder-setup) 13 | - [Analog Button](#analog-button-setup) 14 | - [Analog Stick](#analog-stick-setup) 15 | - [PlayStation2 Gamepad](#playstation2-gamepad-setup) 16 | - [GPIO Expander](#gpio-expander-setup) 17 | - [Read out status](#read-out-status) 18 | - [Button](button-status) 19 | - [Rotary Encoder](#rotary-encoder-status) 20 | - [Analog Stick](#analog-stick-status) 21 | - [PlayStation2 Gamepad](#playstation2-gamepad-status) 22 | - [Use events](#use-events) 23 | - [License](#license) 24 | 25 | ## Features 26 | 27 | This library supports: 28 | - Push buttons (with or without pullup or inverted logic) 29 | - On/Off Switches 30 | - Rotary Encoders 31 | - The [Lameboy](https://hackaday.io/project/26823-lameboy-another-esp12-handheld) :D 32 | - Any Analog input (i.e. ButtonMatrix) 33 | - Analog-Stick 34 | - PlayStation2 Gamepad 35 | - [I2C Encoder](https://www.tindie.com/products/Saimon/i2c-encoder-connect-rotary-encoders-on-i2c-bus/) 36 | 37 | You can not only read out the current state of the button, but also if it's: 38 | - pushed 39 | - released 40 | - clicked 41 | - double clicked 42 | - holding 43 | 44 | It also works with buttons that are connected to a PCF8574, PCF8575 or MCP23017 GPIO expander! 45 | 46 | ## Installation 47 | 48 | 1) [Download](https://github.com/spacehuhn/SimpleButton/archive/master.zip) the source code from GitHub. 49 | 2) Unzip and rename the Folder name to "SimpleButton". 50 | 3) Paste it in your Library folder (Usually located somewhere at documents/Arduino/libraries). 51 | 4) Restart the Arduino IDE. 52 | 53 | ## Usage 54 | 55 | Also have a look at the [examples](https://github.com/spacehuhn/SimpleButton/tree/master/examples). 56 | 57 | ### Include the library 58 | ```c++ 59 | #include 60 | 61 | using namespace simplebutton; 62 | ``` 63 | 64 | ### Create a button 65 | 66 | #### Push Button Setup 67 | 68 | **Normal logic:** 69 | The usual way of connecting a push button as described [here](https://www.arduino.cc/en/Tutorial/Button). 70 | ```c++ 71 | // creates button on pin 12 72 | Button* b = new Button(12); 73 | ``` 74 | 75 | **Inverted logic:** 76 | 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. 77 | ```c++ 78 | // creates inverted button on pin 12 79 | Button* b = new Button(12, true); 80 | ``` 81 | 82 | **Pullup button:** 83 | This will use the internal resistor and you won't need to add an external one. Also described [here](https://www.arduino.cc/en/Tutorial/InputPullupSerial). 84 | ```c++ 85 | // creates pullup button on pin 12 86 | Button* b = new ButtonPullup(12); 87 | ``` 88 | 89 | #### On/Off Switch Setup 90 | You can use a switch as a button. Whenever you switch it, it will count it as a button click. 91 | ```c++ 92 | // creates switch on pin 12 93 | Button* b = new Switch(12); 94 | ``` 95 | 96 | #### Rotary Encoder Setup 97 | ```c++ 98 | // creates a rotary encoder connected to pin 5 and pin 4 and switch connected pin 3 (set switch to 255 to disable it) 99 | RotaryEncoder* myEncoder = new RotaryEncoder(5, 4, 3); 100 | 101 | // rotary encoder connected to the PCF on pin 2 and pin 3 102 | RotaryEncoder* myEncoder = new RotaryEncoder(exp, 2, 3, 255); 103 | 104 | // I2C encoder at address 0x30 105 | RotaryEncoderI2C* myEncoder = new RotaryEncoderI2C(0x30); 106 | 107 | // in case you have a rotary encoder that does 2 steps with each turn: 108 | // (default is 1 step per turn) 109 | myEncoder->setEncoding(2); 110 | 111 | // set a start position for the counter 112 | myEncoder->setPos(10); 113 | 114 | // set a minimum value threshold 115 | myEncoder->setMin(-20); 116 | 117 | // set a maximum value threshold 118 | myEncoder->setMax(20); 119 | 120 | // invert the directions 121 | myEncoder->setInverted(true); 122 | 123 | // enable looping (when the counter goes below the minimum it will be set to the maximum and vice versa) 124 | myEncoder->enableLoop(true); 125 | 126 | // ===== for the I2C encoder only ===== 127 | 128 | // enables a interrupt pin at gpio 12 with pullup enabled (true) 129 | myEncoder->enableInterrupt(12, true); 130 | 131 | // enables the dual-color led 132 | myEncoder->enableLed(true); 133 | 134 | // set LED-A to 255 (on) and LED-B to 0 (off) 135 | myEncoder->setLed(255, 0); 136 | 137 | // !!!!! IMPORTANT !!!!! 138 | // to enable all our config changes 139 | myEncoder->begin(); 140 | ``` 141 | 142 | #### Analog Button Setup 143 | ```c++ 144 | // create an analog button on pin A0 that is pushed when it reads a value between 0 and 20 145 | ButtonAnalog* b = ButtonAnalog(A0, 0, 20); 146 | ``` 147 | 148 | #### Analog Stick Setup 149 | ```c++ 150 | // creates an analog stick that has X connected to A0, Y connected to A1 and the switch connected to pin 5 151 | // (set switch to 255 to disable it) 152 | AnalogStick* analogStick = new AnalogStick(A0, A1, 5); 153 | 154 | // set the logic to read values from 0 to 1024 with 25% tolerance 155 | analogStick->setLogic(1024, 25); 156 | 157 | // set the logic to read values from 0 to 256 with 25% tolerance 158 | analogStick->setLogic(256, 25); 159 | ``` 160 | 161 | #### PlayStation2 Gamepad Setup 162 | 163 | To learn more about the wiring, protocol and usage of the PlayStation2 controller, please have a look [here](http://store.curiousinventor.com/guides/PS2/). 164 | It doesn't really matter (on most Arduino's) what pins you use to connect the controller. 165 | 166 | ```c++ 167 | // create the gamepad 168 | PS2Gamepad* gamepad = new PS2Gamepad(); 169 | 170 | // connect to it 171 | gamepad->setup(CLOCK_PIN, COMMAND_PIN, ATTENTION_PIN, DATA_PIN); 172 | 173 | // check for errros 174 | bool isConnected = gamepad->connected(); 175 | String errorMessage = gamepad->getError(); 176 | ``` 177 | 178 | #### GPIO Expander Setup 179 | ```c++ 180 | // start i2c 181 | Wire.begin(); 182 | 183 | // 0x20 = i2c address (use a i2c scanner sketch to find the right address) 184 | // create a PCF8574 185 | GPIOExpander* exp = new PCF8574(0x20); 186 | // create a PCF8575 187 | GPIOExpander* exp = new PCF8575(0x20); 188 | // create a MCP23017 189 | GPIOExpander* exp = new MCP23017(0x20); 190 | 191 | // creates a push button connected to the PCF on pin 0 192 | Button* b = new ButtonGPIOExpander(exp, 0); 193 | 194 | // creates a pullup button connected to the PCF on pin 1 195 | Button* b = new ButtonPullupGPIOExpander(exp, 1); 196 | 197 | // rotary encoder connected to the PCF on pin 2 and pin 3 198 | RotaryEncoder* myEncoder = new RotaryEncoder(exp, 2, 3, 255); 199 | 200 | // check for errors 201 | bool isConnected = exp->connected(); 202 | String errorMessage = exp->getError(); 203 | ``` 204 | 205 | ### Read out status 206 | `b` is a pointer to a created button (see above). 207 | 208 | #### Button status 209 | 210 | Important status methods a `Button` object has: 211 | ```c++ 212 | bool pushed(); 213 | bool released(); 214 | bool clicked(); 215 | bool clicked(uint32_t minPushTime); 216 | bool clicked(uint32_t minPushTime, uint32_t minReleaseTime); 217 | bool doubleClicked(); 218 | bool doubleClicked(uint32_t minPushTime); 219 | bool doubleClicked(uint32_t minPushTime, uint32_t timeSpan); 220 | bool doubleClicked(uint32_t minPushTime, uint32_t minReleaseTime, uint32_t timeSpan); 221 | bool holding(); 222 | bool holding(uint32_t interval); 223 | ``` 224 | 225 | Example usage: 226 | ```c++ 227 | // first update the button 228 | b->update(); 229 | 230 | // react on double click 231 | if(b->doubleClicked()){ ... } 232 | 233 | // with minimum push time in ms (default = 40) 234 | if(b->doubleClicked(uint32_t minPushTime)) { ... } 235 | 236 | // with minimum push time in a given time span in ms (default = 650) 237 | if(b->doubleClicked(uint32_t minPushTime, uint32_t timeSpan)) { ... } 238 | 239 | // react on a click 240 | if(b->clicked()){ ...} 241 | 242 | // with a minimum push time in ms (default = 40) 243 | if(b->clicked(uint32_t minPushTime)) { ... } 244 | 245 | // if button is beeing hold 246 | if(b->holding()){ ... } 247 | 248 | // with custom time interval in ms (default = 250) 249 | if(b->holding(uint32_t interval)){ ... }; 250 | 251 | // when the button is beeing pushed 252 | if(b->pushed()) { ... } 253 | 254 | // when the button is released 255 | if(b->released()) { ... } 256 | 257 | // you can also read the button state out directly 258 | bool currentButtonState = b->getState(); 259 | 260 | // read out the analog value 261 | uint8_t value = analogButton->getValue(); 262 | ``` 263 | 264 | #### Rotary Encoder status 265 | 266 | These are the `Button`s a `RotaryEncoder` object has: 267 | ```c++ 268 | Button* button; 269 | Button* clockwise; 270 | Button* anticlockwise; 271 | ``` 272 | 273 | Example usage: 274 | ```c++ 275 | // update the encoder 276 | myEncoder->update(); 277 | 278 | // read out the position counter 279 | int position = myEncoder->getPos(); 280 | 281 | // if rotary encoder switch was pushed 282 | bool clicked = myEncoder->clicked(); 283 | // here using the Button object 284 | bool clicked = myEncoder->button->clicked(); 285 | 286 | // read out the directions 287 | bool incremented = myEncoder->incremented(); 288 | bool decremented = myEncoder->decremented(); 289 | 290 | // read out the directions using the Button objects 291 | bool incremented = myEncoder->clockwise->clicked(); 292 | bool decremented = myEncoder->anticlockwise->clicked(); 293 | 294 | // read out if the counter hit a threshold 295 | bool hitMinValue = myEncoder->minVal(); 296 | bool hitMaxValue = myEncoder->maxVal(); 297 | 298 | // ==== for I2C encoder only ===== 299 | 300 | // read out if interrupt pin changed (will always be true if disabled) 301 | bool interrupt = myEncoder->interrupt(); 302 | 303 | // if the interrupt pin is enabled, you can also use the update function 304 | // to see if something changed 305 | bool changed = myEncoder->update(); 306 | ``` 307 | 308 | #### Analog Stick status 309 | 310 | These are the `Button`s a `AnalogStick` object has: 311 | ```c++ 312 | Button* button; 313 | ButtonAnalog* up; 314 | ButtonAnalog* down; 315 | ButtonAnalog* left; 316 | ButtonAnalog* right; 317 | ``` 318 | 319 | Example usage: 320 | ```c++ 321 | // read out the values 322 | uint8_t x = analogStick->left->getValue(); 323 | uint8_t y = analogStick->up->getValue(); 324 | 325 | // access the switch button (if you added one) 326 | if (analogStick->button->clicked()) Serial.println("clicked"); 327 | 328 | // react on any direction 329 | if (analogStick->left->holding()) Serial.println("left holding"); 330 | if (analogStick->right->holding()) Serial.println("right holding"); 331 | if (analogStick->up->holding()) Serial.println("up holding"); 332 | if (analogStick->down->holding()) Serial.println("down holding"); 333 | ``` 334 | 335 | #### PlayStation2 Gamepad status 336 | 337 | These are the `Button`s a `PS2Gamepad` object has: 338 | ```c++ 339 | ButtonAnalog* up; 340 | ButtonAnalog* down; 341 | ButtonAnalog* left; 342 | ButtonAnalog* right; 343 | 344 | ButtonAnalog* l1; 345 | ButtonAnalog* l2; 346 | ButtonAnalog* r1; 347 | ButtonAnalog* r2; 348 | 349 | ButtonAnalog* square; 350 | ButtonAnalog* triangle; 351 | ButtonAnalog* x; 352 | ButtonAnalog* circle; 353 | 354 | Button* select; 355 | Button* start; 356 | 357 | AnalogStick* analogLeft; 358 | AnalogStick* analogRight; 359 | ``` 360 | 361 | Example usage: 362 | ```c++ 363 | // getting the analog-stick values 364 | uint8_t left_x = gamepad->analogLeft->left->getValue(); 365 | uint8_t left_y = gamepad->analogLeft->up->getValue(); 366 | 367 | uint8_t right_x = gamepad->analogRight->left->getValue(); 368 | uint8_t right_y = gamepad->analogRight->up->getValue(); 369 | 370 | // d-pad 371 | if (gamepad->up->clicked()) Serial.println("up clicked"); 372 | if (gamepad->down->holding()) Serial.println("down clicked"); 373 | if (gamepad->left->clicked()) Serial.println("left clicked"); 374 | if (gamepad->right->clicked()) Serial.println("right clicked"); 375 | 376 | // L and R Buttons 377 | if (gamepad->l1->clicked()) Serial.println("L1 clicked"); 378 | if (gamepad->l2->clicked()) Serial.println("L2 clicked"); 379 | if (gamepad->r1->clicked()) Serial.println("R1 clicked"); 380 | if (gamepad->r2->clicked()) Serial.println("R2 clicked"); 381 | 382 | // start & select 383 | if (gamepad->select->clicked()) Serial.println("Select clicked"); 384 | if (gamepad->start->clicked()) Serial.println("Start clicked"); 385 | 386 | // triangle, circle, cross, square 387 | if (gamepad->square->clicked()) Serial.println("Square clicked"); 388 | if (gamepad->triangle->clicked()) Serial.println("Triangle clicked"); 389 | if (gamepad->cross->clicked()) Serial.println("Cross clicked"); 390 | if (gamepad->circle->clicked()) Serial.println("Circle clicked"); 391 | 392 | // left analog stick 393 | if (gamepad->analogLeft->button->clicked()) Serial.println("Stick-Left clicked"); 394 | 395 | if (gamepad->analogLeft->up->holding()) Serial.println("Stick-Left up"); 396 | if (gamepad->analogLeft->down->holding()) Serial.println("Stick-Left down"); 397 | if (gamepad->analogLeft->left->holding()) Serial.println("Stick-Left left"); 398 | if (gamepad->analogLeft->right->holding()) Serial.println("Stick-Left right"); 399 | 400 | // right analog stick 401 | if (gamepad->analogRight->button->clicked()) Serial.println("Stick-Right clicked"); 402 | 403 | if (gamepad->analogRight->up->holding()) Serial.println("Stick-Right up"); 404 | if (gamepad->analogRight->down->holding()) Serial.println("Stick-Right down"); 405 | if (gamepad->analogRight->left->holding()) Serial.println("Stick-Right left"); 406 | if (gamepad->analogRight->right->holding()) Serial.println("Stick-Right right"); 407 | ``` 408 | 409 | ### Use events 410 | Each button can have multiple events, you can add them with following methods: 411 | ```c++ 412 | void setOnPushed(void (* fnct)()); 413 | 414 | void setOnReleased(void (* fnct)()); 415 | 416 | void setOnClicked(void (* fnct)()); 417 | void setOnClicked(void (* fnct)(), uint32_t minPushTime); 418 | void setOnClicked(void (* fnct)(), uint32_t minPushTime, uint32_t minReleaseTime); 419 | 420 | void setOnDoubleClicked(void (* fnct)()); 421 | void setOnDoubleClicked(void (* fnct)(), uint32_t minPushTime); 422 | void setOnDoubleClicked(void (* fnct)(), uint32_t minPushTime, uint32_t timeSpan); 423 | void setOnDoubleClicked(void (* fnct)(), uint32_t minPushTime, uint32_t minReleaseTime, uint32_t timeSpan); 424 | 425 | void setOnHolding(void (* fnct)()); 426 | void setOnHolding(void (* fnct)(), uint32_t interval); 427 | ``` 428 | 429 | Here's an example: 430 | ```c++ 431 | button->setOnClicked([](){ 432 | Serial.println("Button clicked!"); 433 | }); 434 | 435 | rotaryEncoder->clockwise->setOnClicked([](){ 436 | Serial.println("Moved clockwise"); 437 | }); 438 | 439 | analogStick->left->setOnPushed([](){ 440 | Serial.println("Analog-left"); 441 | }); 442 | 443 | gamepad->up->setOnDoubleClicked([](){ 444 | Serial.println("Double clicked UP button"); 445 | }); 446 | ``` 447 | 448 | ## License 449 | 450 | This software is licensed under the MIT License. See the [license file](LICENSE) for details. 451 | -------------------------------------------------------------------------------- /examples/AnalogStick/AnalogStick.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace simplebutton; 4 | 5 | /* 6 | Analog stick connected to an Arduino micro 7 | - x axis is connected to pin A0 8 | - y axis is connected to pin A1 9 | - Switch is connected to pin 5 10 | */ 11 | 12 | AnalogStick* analogStick = NULL; 13 | 14 | void setup() { 15 | Serial.begin(115200); 16 | Serial.println(); 17 | 18 | analogStick = new AnalogStick(A0, A1, 5); 19 | 20 | /* === check your analogStick if it returns values from 0 - 255 or from 0 - 1024! === */ 21 | // analogStick->setLogic(256, 25); // logic => 256: values range from 0 to 255, tolarance => 25% 22 | analogStick->setLogic(1024, 25); // logic => 1024: values range from 0 to 1023, tolarance => 25% 23 | 24 | Serial.println("Started!"); 25 | } 26 | 27 | void loop() { 28 | // In case you want to read out the values manually for testing: 29 | // Serial.print(analogRead(A0)); 30 | // Serial.print(' '); 31 | // Serial.println(analogRead(A1)); 32 | 33 | analogStick->update(); 34 | 35 | // or read them out using the object 36 | // uint8_t x = analogStick->left->getValue(); 37 | // uint8_t y = analogStick->up->getValue(); 38 | 39 | if (analogStick->button->doubleClicked()) Serial.println("doubleClicked"); 40 | if (analogStick->button->clicked()) Serial.println("clicked"); 41 | if (analogStick->button->holding()) Serial.println("holding"); 42 | 43 | if (analogStick->left->clicked()) Serial.println("left clicked"); 44 | if (analogStick->right->clicked()) Serial.println("right clicked"); 45 | if (analogStick->up->clicked()) Serial.println("up clicked"); 46 | if (analogStick->down->clicked()) Serial.println("down clicked"); 47 | 48 | if (analogStick->left->holding()) Serial.println("left holding"); 49 | if (analogStick->right->holding()) Serial.println("right holding"); 50 | if (analogStick->up->holding()) Serial.println("up holding"); 51 | if (analogStick->down->holding()) Serial.println("down holding"); 52 | } -------------------------------------------------------------------------------- /examples/Button/Button.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace simplebutton; 4 | 5 | /* 6 | One button connected to gpio 12 (D6 on the NodeMCU) 7 | It's the standard wiring as descriped here: https://www.arduino.cc/en/Tutorial/Button 8 | */ 9 | 10 | Button* b = NULL; 11 | 12 | void setup() { 13 | Serial.begin(115200); 14 | Serial.println(); 15 | 16 | b = new Button(12); 17 | 18 | Serial.println("Started"); 19 | } 20 | 21 | void loop() { 22 | b->update(); 23 | 24 | if (b->doubleClicked()) Serial.println("doubleclicked"); 25 | 26 | if (b->clicked()) Serial.println("clicked"); 27 | 28 | if (b->holding()) Serial.println("holding"); 29 | } 30 | -------------------------------------------------------------------------------- /examples/Button_Event/Button_Event.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace simplebutton; 4 | 5 | /* 6 | One button connected to gpio 12 (D6 on the NodeMCU) 7 | It's the standard wiring as descriped here: https://www.arduino.cc/en/Tutorial/Button 8 | */ 9 | 10 | Button* b = NULL; 11 | 12 | void setup() { 13 | Serial.begin(115200); 14 | Serial.println(); 15 | 16 | b = new Button(12); 17 | 18 | b->setOnDoubleClicked([]() { 19 | Serial.println("doubleclicked"); 20 | }); 21 | 22 | b->setOnClicked([]() { 23 | Serial.println("clicked"); 24 | }); 25 | 26 | b->setOnHolding([]() { 27 | Serial.println("holding"); 28 | }); 29 | 30 | Serial.println("Started"); 31 | } 32 | 33 | void loop() { 34 | b->update(); 35 | } -------------------------------------------------------------------------------- /examples/Button_Matrix/Button_Matrix.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace simplebutton; 4 | 5 | /* 6 | A 4x4 Button matrix connected to an ESP8266 (Wemos D1 mini) on pin A0. 7 | */ 8 | 9 | #define MATRIX_SIZE 16 10 | 11 | Button* matrix[MATRIX_SIZE]; 12 | 13 | // these are the analog values of each button, they probably are different on your matrix, so test and change the 14 | // values! 15 | int addresses[] = { 1023, 960, 875, 810, 695, 650, 610, 575, 515, 490, 470, 450, 410, 330, 275, 240 }; 16 | int tolerance = 5; 17 | 18 | void setup() { 19 | Serial.begin(115200); 20 | Serial.println(); 21 | 22 | for (int i = 0; i < MATRIX_SIZE; i++) { 23 | matrix[i] = new ButtonAnalog(A0, addresses[i] - tolerance, addresses[i] + tolerance); 24 | } 25 | 26 | Serial.println("Started"); 27 | } 28 | 29 | void loop() { 30 | // Serial.println(analogRead(A0)); // <- you can use this to read out the value of each button 31 | 32 | for (int i = 0; i < MATRIX_SIZE; i++) { 33 | matrix[i]->update(); 34 | if (matrix[i]->clicked()) { 35 | Serial.println("clicked " + String(i + 1)); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /examples/Button_PCF8574/Button_PCF8574.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace simplebutton; 4 | 5 | /* 6 | 2 buttons are connected to a PCF8574 on a Wemos D1 mini (ESP8266) 7 | - PCF8574 is connected with SDA to GPIO 5 (D1) and with SCL to GPIO 4 (D2) 8 | - Button A is connected between the PCF pin 0 and GND 9 | - Button B is connected between the PCF pin 1 and 3.3V (VCC) 10 | */ 11 | 12 | GPIOExpander* myPCF = NULL; 13 | Button * buttonA = NULL; 14 | Button * buttonB = NULL; 15 | 16 | void setup() { 17 | // Wire.begin() 18 | Wire.begin(5, 4); // <- for esp8266 (5 = SDA pin, 4 = SCL pin) 19 | 20 | Serial.begin(115200); 21 | Serial.println(); 22 | 23 | myPCF = new PCF8574(0x38); // 0x38 = i2c address (use a i2c scanner sketch to find the right address) 24 | 25 | // check connction 26 | do { 27 | Serial.print("Connecting to PCF8574..."); 28 | myPCF->write(0); 29 | Serial.println(myPCF->getError()); 30 | 31 | if (!myPCF->connected()) { 32 | Serial.println("Please check the wiring, the i2c address and restart the device!"); 33 | delay(2000); 34 | } 35 | } while (!myPCF->connected()); 36 | 37 | buttonA = new ButtonPullupGPIOExpander(myPCF, 0); 38 | buttonB = new ButtonGPIOExpander(myPCF, 1); 39 | 40 | Serial.println("Started"); 41 | } 42 | 43 | void loop() { 44 | buttonA->update(); 45 | buttonB->update(); 46 | 47 | if (buttonA->doubleClicked()) Serial.println("Button A doubleclicked"); 48 | if (buttonA->clicked()) Serial.println("Button A clicked"); 49 | if (buttonA->holding()) Serial.println("Button A holding"); 50 | 51 | if (buttonB->doubleClicked()) Serial.println("Button B doubleclicked"); 52 | if (buttonB->clicked()) Serial.println("Button B clicked"); 53 | if (buttonB->holding()) Serial.println("Button B holding"); 54 | } -------------------------------------------------------------------------------- /examples/I2CEncoder/I2CEncoder.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace simplebutton; 5 | 6 | /* 7 | One I2C rotary encoder is connected to an Wemos D1 mini 8 | - SDA is connected to D1 (GPIO 5) 9 | - SCL is connected to D2 (GPIO 4) 10 | - Interrupt pin is connected to D6 (GPIO 12) 11 | */ 12 | 13 | RotaryEncoderI2C* myEncoder = NULL; 14 | 15 | void setup() { 16 | // EEPROM.begin(); 17 | EEPROM.begin(4096); // <- for ESP8266 18 | 19 | Serial.begin(115200); 20 | Serial.println(); 21 | 22 | // Wire.begin(); 23 | Wire.begin(D1, D2); // <- for ESP8266 24 | 25 | // setup the rotary encoder 26 | myEncoder = new RotaryEncoderI2C(0x30); 27 | 28 | // set the interrupt pin at D6 29 | myEncoder->enableInterrupt(12, true); 30 | 31 | myEncoder->setMin(-128); // set the minimum counter value 32 | myEncoder->setMax(127); // set the maximum counter value 33 | myEncoder->enableLed(true); // enable LED 34 | // myEncoder->setEncoding(2); // enable x2 encoding 35 | // myEncoder->enableLoop(true); 36 | myEncoder->setInverted(true); // invert direction of counting 37 | 38 | // read out the last counter value from the EEPROM and set it 39 | myEncoder->setPos((int8_t)EEPROM.read(0)); 40 | 41 | myEncoder->begin(); 42 | 43 | // check connction 44 | do { 45 | Serial.print("Connecting to I2C Encoder..."); 46 | Serial.println(myEncoder->getError()); 47 | 48 | if (!myEncoder->connected()) { 49 | Serial.println("Please check the wiring, the i2c address and restart the device!"); 50 | delay(2000); 51 | } 52 | } while (!myEncoder->connected()); 53 | 54 | Serial.println("Started!"); 55 | Serial.print("Value: "); 56 | Serial.println(myEncoder->getPos()); 57 | } 58 | 59 | void loop() { 60 | // check if interrupt pin has changed 61 | if (myEncoder->update()) { 62 | int val = myEncoder->getPos(); // get the value 63 | 64 | // save value in the eeprom 65 | EEPROM.write(0, (int8_t)val); 66 | EEPROM.commit(); // <- for ESP8266 67 | 68 | // print infos 69 | Serial.print(val); 70 | 71 | if (myEncoder->decremented()) { 72 | Serial.print(" down"); 73 | myEncoder->setLed(255, 0); 74 | } else if (myEncoder->incremented()) { 75 | Serial.print(" up"); 76 | myEncoder->setLed(0, 255); 77 | } 78 | 79 | if (myEncoder->clicked()) { 80 | Serial.print(" clicked"); 81 | myEncoder->setLed(0, 0); 82 | } 83 | 84 | Serial.println(); 85 | } 86 | } -------------------------------------------------------------------------------- /examples/Lameboy/Lameboy.ino: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | using namespace simplebutton; 5 | 6 | /* 7 | This example is for the Lameboy by @davedarko 8 | https://hackaday.io/project/26823-lameboy-another-esp12-handheld 9 | https://www.tindie.com/products/davedarko/lameboy-esp8266-handheld 10 | */ 11 | 12 | Button* up = NULL; 13 | Button* down = NULL; 14 | Button* left = NULL; 15 | Button* right = NULL; 16 | Button* a = NULL; 17 | Button* b = NULL; 18 | Button* sd = NULL; 19 | 20 | PCF8574* myPCF = NULL; 21 | 22 | void setup() { 23 | Serial.begin(115200); 24 | Serial.println(); 25 | 26 | Wire.begin(); 27 | 28 | myPCF = new PCF8574(0x3C); // 0x3c = i2c address (use a i2c scanner sketch to find the right address) 29 | 30 | // check connction 31 | do { 32 | Serial.print("Connecting to PCF8574..."); 33 | myPCF->write(0); 34 | Serial.println(myPCF->getError()); 35 | 36 | if (!myPCF->connected()) { 37 | Serial.println("Please check the wiring, the i2c address and restart the device!"); 38 | delay(2000); 39 | } 40 | } while (!myPCF->connected()); 41 | 42 | up = new ButtonPullupGPIOExpander(myPCF, 7); 43 | down = new ButtonPullupGPIOExpander(myPCF, 5); 44 | left = new ButtonPullupGPIOExpander(myPCF, 6); 45 | right = new ButtonPullupGPIOExpander(myPCF, 4); 46 | a = new ButtonPullupGPIOExpander(myPCF, 3); 47 | b = new ButtonPullupGPIOExpander(myPCF, 2); 48 | sd = new ButtonPullupGPIOExpander(myPCF, 1); 49 | 50 | Serial.println("Started"); 51 | } 52 | 53 | void loop() { 54 | up->update(); 55 | down->update(); 56 | left->update(); 57 | right->update(); 58 | a->update(); 59 | b->update(); 60 | sd->update(); 61 | 62 | if (up->doubleClicked()) Serial.println("up doubleclicked"); 63 | if (up->clicked()) Serial.println("up clicked"); 64 | if (up->holding()) Serial.println("up holding"); 65 | 66 | if (down->doubleClicked()) Serial.println("down doubleclicked"); 67 | if (down->clicked()) Serial.println("down clicked"); 68 | if (down->holding()) Serial.println("down holding"); 69 | 70 | if (left->doubleClicked()) Serial.println("left doubleclicked"); 71 | if (left->clicked()) Serial.println("left clicked"); 72 | if (left->holding()) Serial.println("left holding"); 73 | 74 | if (right->doubleClicked()) Serial.println("right doubleclicked"); 75 | if (right->clicked()) Serial.println("right clicked"); 76 | if (right->holding()) Serial.println("right holding"); 77 | 78 | if (a->doubleClicked()) Serial.println("a doubleclicked"); 79 | if (a->clicked()) Serial.println("a clicked"); 80 | if (a->holding()) Serial.println("a holding"); 81 | 82 | if (b->doubleClicked()) Serial.println("b doubleclicked"); 83 | if (b->clicked()) Serial.println("b clicked"); 84 | if (b->holding()) Serial.println("b holding"); 85 | 86 | if (sd->pushed()) Serial.println("sd card inserted"); 87 | if (sd->released()) Serial.println("sd card removed"); 88 | } -------------------------------------------------------------------------------- /examples/PlayStation2_Gamepad/PlayStation2_Gamepad.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace simplebutton; 4 | 5 | /* 6 | // Arduino Nano 7 | #define DATA_PIN 13 8 | #define COMMAND_PIN 11 9 | #define ATTENTION_PIN 10 10 | #define CLOCK_PIN 12 11 | */ 12 | 13 | // Wemos D1 mini 14 | #define DATA_PIN 5 // D1 15 | #define COMMAND_PIN 12 // D6 16 | #define ATTENTION_PIN 14 // D5 17 | #define CLOCK_PIN 13 // D7 18 | 19 | // enables to read analog values from buttons and activate the vibration motors 20 | // is on "true" by default, but some older/special controllers might not support it 21 | #define ENABLE_ANALOG true 22 | 23 | PS2Gamepad* gamepad = NULL; 24 | 25 | void setup() { 26 | Serial.begin(115200); 27 | Serial.println(); 28 | 29 | // create gamepad 30 | gamepad = new PS2Gamepad(); 31 | 32 | // try connecting to it 33 | while (!gamepad->connected()) { 34 | Serial.print("Connecting to PlayStation-2 Controller..."); 35 | 36 | // setup 37 | gamepad->setup(CLOCK_PIN, COMMAND_PIN, ATTENTION_PIN, DATA_PIN /*, ENABLE_ANALOG*/); 38 | 39 | // print OK or error message 40 | Serial.println(gamepad->getError()); 41 | 42 | // retry if connection failed 43 | if (!gamepad->connected()) { 44 | Serial.println("Retrying..."); 45 | delay(1000); 46 | } 47 | } 48 | 49 | Serial.println("Started"); 50 | } 51 | 52 | void loop() { 53 | gamepad->update(); 54 | 55 | // print analog values 56 | 57 | /* 58 | uint8_t left_x = gamepad->analogLeft->left->getValue(); 59 | uint8_t left_y = gamepad->analogLeft->up->getValue(); 60 | uint8_t right_x = gamepad->analogRight->left->getValue(); 61 | uint8_t right_y = gamepad->analogRight->up->getValue(); 62 | Serial.printf("Left-X: %+3u, Left-Y: %+3u, Right-X: %+3u, Right-Y: %+3u\n", left_x, left_y, right_x, right_y); 63 | */ 64 | 65 | // d-pad 66 | if (gamepad->up->clicked()) Serial.println("up clicked"); 67 | if (gamepad->up->holding()) Serial.println("up holding, value: " + String(gamepad->up->getValue())); 68 | 69 | if (gamepad->down->clicked()) Serial.println("down clicked"); 70 | if (gamepad->down->holding()) Serial.println("down holding, value: " + String(gamepad->down->getValue())); 71 | 72 | if (gamepad->left->clicked()) Serial.println("left clicked"); 73 | if (gamepad->left->holding()) Serial.println("left holding, value: " + String(gamepad->left->getValue())); 74 | 75 | if (gamepad->right->clicked()) Serial.println("right clicked"); 76 | if (gamepad->right->holding()) Serial.println("right holding, value: " + String(gamepad->right->getValue())); 77 | 78 | // L and R Buttons 79 | if (gamepad->l1->clicked()) Serial.println("L1 clicked"); 80 | if (gamepad->l1->holding()) Serial.println("L1 holding, value: " + String(gamepad->l1->getValue())); 81 | 82 | if (gamepad->l2->clicked()) Serial.println("L2 clicked"); 83 | if (gamepad->l2->holding()) Serial.println("L2 holding, value: " + String(gamepad->l2->getValue())); 84 | 85 | if (gamepad->r1->clicked()) Serial.println("R1 clicked"); 86 | if (gamepad->r1->holding()) Serial.println("R1 holding, value: " + String(gamepad->r1->getValue())); 87 | 88 | if (gamepad->r2->clicked()) Serial.println("R2 clicked"); 89 | if (gamepad->r2->holding()) Serial.println("R2 holding, value: " + String(gamepad->r2->getValue())); 90 | 91 | // triangle, circle, cross, square 92 | if (gamepad->square->clicked()) Serial.println("Square clicked"); 93 | if (gamepad->square->holding()) Serial.println("Square holding, value: " + String(gamepad->square->getValue())); 94 | 95 | if (gamepad->triangle->clicked()) Serial.println("Triangle clicked"); 96 | if (gamepad->triangle->holding()) Serial.println("Triangle holding, value: " + 97 | String(gamepad->triangle->getValue())); 98 | 99 | if (gamepad->cross->clicked()) Serial.println("Cross clicked"); 100 | if (gamepad->cross->holding()) Serial.println("Cross holding, value: " + String(gamepad->cross->getValue())); 101 | 102 | if (gamepad->circle->clicked()) Serial.println("Circle clicked"); 103 | if (gamepad->circle->holding()) Serial.println("Circle holding, value: " + String(gamepad->circle->getValue())); 104 | 105 | // start & select 106 | if (gamepad->select->clicked()) Serial.println("Select clicked"); 107 | if (gamepad->select->holding()) Serial.println("Select holding"); 108 | 109 | if (gamepad->start->clicked()) Serial.println("Start clicked"); 110 | if (gamepad->start->holding()) Serial.println("Start holding"); 111 | 112 | // analog sticks 113 | if (gamepad->analogLeft->button->clicked()) Serial.println("Stick-Left clicked"); 114 | if (gamepad->analogLeft->button->holding()) Serial.println("Stick-Left holding"); 115 | 116 | if (gamepad->analogLeft->up->holding()) Serial.println("Stick-Left up, value: " + 117 | String(gamepad->analogLeft->up->getValue())); 118 | if (gamepad->analogLeft->down->holding()) Serial.println("Stick-Left down, value: " + 119 | String(gamepad->analogLeft->down->getValue())); 120 | if (gamepad->analogLeft->left->holding()) Serial.println("Stick-Left left, value: " + 121 | String(gamepad->analogLeft->left->getValue())); 122 | if (gamepad->analogLeft->right->holding()) Serial.println("Stick-Left right, value: " + 123 | String(gamepad->analogLeft->right->getValue())); 124 | 125 | 126 | if (gamepad->analogRight->button->clicked()) Serial.println("Stick-Right clicked"); 127 | if (gamepad->analogRight->button->holding()) Serial.println("Stick-Right holding"); 128 | 129 | if (gamepad->analogRight->up->holding()) Serial.println("Stick-Right up, value: " + 130 | String(gamepad->analogRight->up->getValue())); 131 | if (gamepad->analogRight->down->holding()) Serial.println("Stick-Right down, value: " + 132 | String(gamepad->analogRight->down->getValue())); 133 | if (gamepad->analogRight->left->holding()) Serial.println("Stick-Right left, value: " + 134 | String(gamepad->analogRight->left->getValue())); 135 | if (gamepad->analogRight->right->holding()) Serial.println("Stick-Right right, value: " + 136 | String(gamepad->analogRight->right->getValue())); 137 | } 138 | -------------------------------------------------------------------------------- /examples/PullupButton/PullupButton.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace simplebutton; 4 | 5 | /* 6 | One button is connected to gpio 12 (D6 on the NodeMCU) and GND 7 | */ 8 | 9 | Button* b = NULL; 10 | 11 | void setup() { 12 | Serial.begin(115200); 13 | Serial.println(); 14 | 15 | // ButtonPullup will do "pinMode(12, INPUT_PULLUP)" isntead of "pinMode(12, INPUT)". 16 | // That way you can connect it between the pin and GND and don't need the additional resistor. 17 | b = new ButtonPullup(12); 18 | 19 | Serial.println("Started"); 20 | } 21 | 22 | void loop() { 23 | b->update(); 24 | 25 | if(b->doubleClicked()) Serial.println("doubleclicked"); 26 | if(b->clicked()) Serial.println("clicked"); 27 | if(b->holded()) Serial.println("holded"); 28 | } 29 | -------------------------------------------------------------------------------- /examples/RotaryEncoder/RotaryEncoder.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace simplebutton; 4 | 5 | /* 6 | 1 Rotary encoder is connected to a Wemos D1 mini (ESP8266) 7 | - Rotary encoder "Key" (that's the button) is connected to gpio 12 (D6) 8 | - Rotary encoder "S1" is connected to gpio 5 (D1) - that's channel A 9 | - Rotary encoder "S4" is connected to gpio 4 (D2) - that's channel B 10 | (the labels on your rotary encoder might be different!) 11 | */ 12 | 13 | RotaryEncoder* myEncoder = NULL; 14 | int32_t previousPosition = 0; 15 | 16 | void setup() { 17 | Serial.begin(115200); 18 | Serial.println(); 19 | 20 | myEncoder = new RotaryEncoder(5, 4, 12); // channel-A, channel-B, push button (255 = not used) 21 | // myEncoder->setEncoding(2); // <- if it used x2 encoding (x1 is default) 22 | myEncoder->setMin(-128); 23 | myEncoder->setMax(127); 24 | // myEncoder->setInverted(true); 25 | // myEncoder->enableLoop(true); 26 | 27 | Serial.println("Started"); 28 | } 29 | 30 | void loop() { 31 | myEncoder->update(); 32 | 33 | int32_t currentPosition = myEncoder->getPos(); 34 | 35 | if (currentPosition != previousPosition) { 36 | previousPosition = currentPosition; 37 | Serial.print(currentPosition); 38 | if (myEncoder->incremented()) Serial.println(" up"); 39 | if (myEncoder->decremented()) Serial.println(" down"); 40 | } 41 | 42 | if (myEncoder->clicked()) { 43 | Serial.println("clicked"); 44 | } 45 | } -------------------------------------------------------------------------------- /examples/RotaryEncoder_PCF8574/RotaryEncoder_PCF8574.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace simplebutton; 4 | 5 | /* 6 | 1 Rotary encoder is connected to a PCF8574 on a Wemos D1 mini (ESP8266) 7 | - PCF8574 is connected with SDA to GPIO 5 (D1) and with SCL to GPIO 4 (D2) 8 | - Rotary encoder CLK is connected to the PCF pin 0 - that's channel A 9 | - Rotary encoder DT is connected to PCF pin 1 - that's channel B 10 | - Rotary encoder SW (switch) is unconnected and therefor set to 255 11 | (the labels on your rotary encoder might be different!) 12 | */ 13 | 14 | PCF8574* myPCF = NULL; 15 | RotaryEncoder* myEncoder = NULL; 16 | int32_t previousPosition = 0; 17 | 18 | void setup() { 19 | // Wire.begin() 20 | Wire.begin(5, 4); // <- for esp8266 (5 = SDA pin, 4 = SCL pin) 21 | 22 | Serial.begin(115200); 23 | Serial.println(); 24 | 25 | myPCF = new PCF8574(0x20); // 0x20 = i2c address (use a i2c scanner sketch to find the right address) 26 | 27 | // check connection 28 | do { 29 | Serial.print("Connecting to PCF8574..."); 30 | myPCF->write(0); 31 | Serial.println(myPCF->getError()); 32 | 33 | if (!myPCF->connected()) { 34 | Serial.println("Please check the wiring, the i2c address and restart the device!"); 35 | delay(2000); 36 | } 37 | } while (!myPCF->connected()); 38 | 39 | myEncoder = new RotaryEncoder(myPCF, 0, 1, 255); // GPIOExpander, Channel-A, Channel-B, push button (255 = not used) 40 | 41 | Serial.println("Started"); 42 | } 43 | 44 | void loop() { 45 | myEncoder->update(); 46 | 47 | int32_t currentPosition = myEncoder->getPos(); 48 | 49 | if (currentPosition != previousPosition) { 50 | previousPosition = currentPosition; 51 | Serial.print(currentPosition); 52 | if (myEncoder->incremented()) Serial.println(" up"); 53 | if (myEncoder->decremented()) Serial.println(" down"); 54 | } 55 | 56 | if (myEncoder->clicked()) { 57 | Serial.println("clicked"); 58 | } 59 | } -------------------------------------------------------------------------------- /examples/Switch/Switch.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | using namespace simplebutton; 4 | 5 | /* 6 | A on/off switch is connected to gpio 12 7 | - the switch connects gpio 12 to either VCC (3.3V) or GND 8 | */ 9 | 10 | Switch* s = NULL; 11 | 12 | void setup() { 13 | Serial.begin(115200); 14 | Serial.println(); 15 | 16 | s = new Switch(12); 17 | 18 | Serial.println("Started"); 19 | } 20 | 21 | void loop() { 22 | s->update(); 23 | 24 | if (s->clicked()) Serial.println("clicked"); 25 | } -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SimpleButton", 3 | "keywords": "simple,button,spacehuhn", 4 | "description": "Manage buttons easily", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/spacehuhn/SimpleButton.git" 9 | }, 10 | "frameworks": "arduino", 11 | "platforms": "*" 12 | } 13 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=SimpleButton 2 | version=1.0.0 3 | author=Stefan Kremser 4 | maintainer=Stefan Kremser 5 | sentence=Manage buttons easily 6 | paragraph=Supports different types of buttons. Manages push, clicked, holding and double-click. 7 | category=Signal Input/Output 8 | url=https://github.com/spacehuhn/SimpleButton 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/Buttons/AnalogStick.cpp: -------------------------------------------------------------------------------- 1 | #include "AnalogStick.h" 2 | 3 | namespace simplebutton { 4 | AnalogStick::AnalogStick() { 5 | setup(255, 255, 255); 6 | } 7 | 8 | AnalogStick::AnalogStick(uint8_t xPin, uint8_t yPin, uint8_t buttonPin) { 9 | setup(xPin, yPin, buttonPin); 10 | } 11 | 12 | AnalogStick::~AnalogStick() { 13 | if (this->up) delete this->up; 14 | if (this->down) delete this->down; 15 | if (this->left) delete this->left; 16 | if (this->right) delete this->right; 17 | } 18 | 19 | void AnalogStick::setup(uint8_t xPin, uint8_t yPin, uint8_t buttonPin) { 20 | this->xPin = xPin; 21 | this->yPin = yPin; 22 | this->buttonPin = buttonPin; 23 | 24 | if (xPin < 255) pinMode(xPin, INPUT); 25 | if (yPin < 255) pinMode(yPin, INPUT); 26 | if (buttonPin < 255) pinMode(buttonPin, INPUT); 27 | 28 | this->button = new ButtonPullup(buttonPin); 29 | this->up = new ButtonAnalog(yPin); 30 | this->down = new ButtonAnalog(yPin); 31 | this->left = new ButtonAnalog(xPin); 32 | this->right = new ButtonAnalog(xPin); 33 | 34 | setLogic(1024); 35 | } 36 | 37 | void AnalogStick::update() { 38 | button->update(); 39 | up->update(); 40 | down->update(); 41 | left->update(); 42 | right->update(); 43 | 44 | this->xValue = left->getState(); 45 | this->yValue = up->getState(); 46 | } 47 | 48 | void AnalogStick::update(uint8_t xValue, uint8_t yValue, bool buttonPress) { 49 | this->xValue = xValue; 50 | this->yValue = yValue; 51 | 52 | button->update(buttonPress); 53 | up->update(yValue); 54 | down->update(yValue); 55 | left->update(xValue); 56 | right->update(xValue); 57 | } 58 | 59 | uint8_t AnalogStick::getX() { 60 | return xValue; 61 | } 62 | 63 | uint8_t AnalogStick::getY() { 64 | return yValue; 65 | } 66 | 67 | void AnalogStick::setButtons(ButtonAnalog* up, ButtonAnalog* down, ButtonAnalog* left, ButtonAnalog* right, 68 | Button* button) { 69 | if (this->up) delete this->up; 70 | if (this->down) delete this->down; 71 | if (this->left) delete this->left; 72 | if (this->right) delete this->right; 73 | 74 | this->up = up ? up : new ButtonAnalog(); 75 | this->down = down ? down : new ButtonAnalog(); 76 | this->left = left ? left : new ButtonAnalog(); 77 | this->right = right ? right : new ButtonAnalog(); 78 | 79 | this->button = button ? button : new Button(); 80 | 81 | setLogic(this->logic, this->tolerance); 82 | } 83 | 84 | void AnalogStick::setLogic(uint16_t logic) { 85 | setLogic(logic, tolerance); 86 | } 87 | 88 | void AnalogStick::setLogic(uint16_t logic, uint8_t tolerance) { 89 | this->logic = logic; 90 | this->tolerance = tolerance; 91 | 92 | uint16_t difference = (double)logic * ((double)tolerance / double(100)); 93 | 94 | up->setBounds(0, difference); 95 | down->setBounds(logic - difference, logic); 96 | left->setBounds(0, difference); 97 | right->setBounds(logic - difference, logic); 98 | } 99 | 100 | void AnalogStick::setUpdateInterval(uint32_t updateInterval) { 101 | button->setUpdateInterval(updateInterval); 102 | up->setUpdateInterval(updateInterval); 103 | down->setUpdateInterval(updateInterval); 104 | left->setUpdateInterval(updateInterval); 105 | right->setUpdateInterval(updateInterval); 106 | } 107 | 108 | void AnalogStick::setDefaultMinPushTime(uint32_t defaultMinPushTime) { 109 | button->setDefaultMinPushTime(defaultMinPushTime); 110 | up->setDefaultMinPushTime(defaultMinPushTime); 111 | down->setDefaultMinPushTime(defaultMinPushTime); 112 | left->setDefaultMinPushTime(defaultMinPushTime); 113 | right->setDefaultMinPushTime(defaultMinPushTime); 114 | } 115 | 116 | void AnalogStick::setDefaultMinReleaseTime(uint32_t defaultMinReleaseTime) { 117 | button->setDefaultMinReleaseTime(defaultMinReleaseTime); 118 | up->setDefaultMinReleaseTime(defaultMinReleaseTime); 119 | down->setDefaultMinReleaseTime(defaultMinReleaseTime); 120 | left->setDefaultMinReleaseTime(defaultMinReleaseTime); 121 | right->setDefaultMinReleaseTime(defaultMinReleaseTime); 122 | } 123 | 124 | void AnalogStick::setDefaultTimeSpan(uint32_t defaultTimeSpan) { 125 | button->setDefaultTimeSpan(defaultTimeSpan); 126 | up->setDefaultTimeSpan(defaultTimeSpan); 127 | down->setDefaultTimeSpan(defaultTimeSpan); 128 | left->setDefaultTimeSpan(defaultTimeSpan); 129 | right->setDefaultTimeSpan(defaultTimeSpan); 130 | } 131 | 132 | void AnalogStick::setDefaultHoldTime(uint32_t defaultHoldInterval) { 133 | button->setDefaultHoldTime(defaultHoldInterval); 134 | up->setDefaultHoldTime(defaultHoldInterval); 135 | down->setDefaultHoldTime(defaultHoldInterval); 136 | left->setDefaultHoldTime(defaultHoldInterval); 137 | right->setDefaultHoldTime(defaultHoldInterval); 138 | } 139 | } -------------------------------------------------------------------------------- /src/Buttons/AnalogStick.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_AnalogStick_h 2 | #define SimpleButton_AnalogStick_h 3 | 4 | #include "ButtonPullup.h" 5 | #include "ButtonAnalog.h" 6 | 7 | namespace simplebutton { 8 | class AnalogStick { 9 | public: 10 | Button* button = NULL; 11 | ButtonAnalog* up = NULL; 12 | ButtonAnalog* down = NULL; 13 | ButtonAnalog* left = NULL; 14 | ButtonAnalog* right = NULL; 15 | 16 | AnalogStick(); 17 | AnalogStick(uint8_t xPin, uint8_t yPin, uint8_t buttonPin); 18 | 19 | ~AnalogStick(); 20 | 21 | void setup(uint8_t xPin, uint8_t yPin, uint8_t buttonPin); 22 | 23 | void update(); 24 | void update(uint8_t xValue, uint8_t yValue, bool buttonPress); 25 | 26 | uint8_t getX(); 27 | uint8_t getY(); 28 | 29 | void setButtons(ButtonAnalog* up, ButtonAnalog* down, ButtonAnalog* left, ButtonAnalog* right, 30 | Button* button); 31 | void setLogic(uint16_t logic); 32 | void setLogic(uint16_t logic, uint8_t tolerance); 33 | 34 | void setUpdateInterval(uint32_t updateInterval); 35 | void setDefaultMinPushTime(uint32_t defaultMinPushTime); 36 | void setDefaultMinReleaseTime(uint32_t defaultMinReleaseTime); 37 | void setDefaultTimeSpan(uint32_t defaultTimeSpan); 38 | void setDefaultHoldTime(uint32_t defaultHoldInterval); 39 | 40 | private: 41 | uint16_t logic = 1024; 42 | uint8_t tolerance = 25; // percentage 43 | 44 | uint8_t xValue = 0; 45 | uint8_t yValue = 0; 46 | 47 | uint8_t xPin = 0; 48 | uint8_t yPin = 0; 49 | uint8_t buttonPin = 0; 50 | }; 51 | } 52 | 53 | #endif // ifndef SimpleButton_AnalogStick_h -------------------------------------------------------------------------------- /src/Buttons/Button.cpp: -------------------------------------------------------------------------------- 1 | #include "SimpleButton.h" 2 | 3 | namespace simplebutton { 4 | Button::Button() { 5 | setup(255, false); 6 | } 7 | 8 | Button::Button(uint8_t pin) { 9 | setup(pin, false); 10 | } 11 | 12 | Button::Button(uint8_t pin, bool inverted) { 13 | setup(pin, inverted); 14 | } 15 | 16 | Button::~Button() {} 17 | 18 | void Button::setup(uint8_t pin, bool inverted) { 19 | this->button_pin = pin; 20 | this->button_inverted = inverted; 21 | enable(); 22 | } 23 | 24 | void Button::enable() { 25 | button_enabled = true; 26 | 27 | if ((button_pin < 255) && !button_setup) { 28 | pinMode(button_pin, INPUT); 29 | button_setup = true; 30 | } 31 | } 32 | 33 | void Button::disable() { 34 | button_enabled = false; 35 | } 36 | 37 | void Button::reset() { 38 | pushedFlag = false; 39 | releasedFlag = false; 40 | holdFlag = false; 41 | } 42 | 43 | void Button::push() { 44 | if (!state) { 45 | state = true; 46 | 47 | prevPushTime = pushTime; 48 | prevReleaseTime = releaseTime; 49 | pushedFlag = true; 50 | 51 | pushTime = millis(); 52 | holdTime = millis(); 53 | holdFlag = false; 54 | } 55 | } 56 | 57 | void Button::release() { 58 | if (state) { 59 | state = false; 60 | releasedFlag = true; 61 | releaseTime = millis(); 62 | } 63 | } 64 | 65 | void Button::click() { 66 | click(defaultMinPushTime); 67 | } 68 | 69 | void Button::click(uint32_t time) { 70 | push(); 71 | pushTime = millis() - time - defaultMinReleaseTime; 72 | release(); 73 | releaseTime = millis() - defaultMinReleaseTime; 74 | 75 | updateEvents(); 76 | } 77 | 78 | int Button::read() { 79 | bool currentState = false; 80 | 81 | if (button_enabled && button_setup) { 82 | currentState = digitalRead(button_pin); 83 | 84 | if (button_inverted) currentState = !currentState; 85 | } 86 | 87 | return (int)currentState; 88 | } 89 | 90 | void Button::update() { 91 | if (millis() - updateTime >= updateInterval) { 92 | updateEvents(); 93 | if (button_enabled && button_setup) update(read()); 94 | } 95 | } 96 | 97 | void Button::update(int state) { 98 | // update time 99 | updateTime = millis(); 100 | 101 | // check events 102 | updateEvents(); 103 | 104 | // update state 105 | if (state > 0) push(); 106 | else release(); 107 | } 108 | 109 | void Button::updateEvents() { 110 | Event* e = this->events; 111 | 112 | while (e != NULL) { 113 | switch (e->getMode()) { 114 | case e->MODE::PUSHED: 115 | if (this->pushed()) e->run(); 116 | break; 117 | 118 | case e->MODE::RELEASED: 119 | if (this->released()) e->run(); 120 | break; 121 | 122 | case e->MODE::CLICKED: 123 | if (this->clicked(e->getMinPushTime(), e->getMinReleaseTime())) e->run(); 124 | break; 125 | 126 | case e->MODE::DOUBLECLICKED: 127 | if (this->doubleClicked(e->getMinPushTime(), e->getMinReleaseTime(), e->getTimeSpan())) e->run(); 128 | break; 129 | 130 | case e->MODE::HOLDING: 131 | if (this->holding(e->getInterval())) e->run(); 132 | break; 133 | } 134 | e = e->next; 135 | } 136 | } 137 | 138 | bool Button::isInverted() { 139 | return button_inverted; 140 | } 141 | 142 | bool Button::isEnabled() { 143 | return button_enabled; 144 | } 145 | 146 | bool Button::isSetup() { 147 | return button_setup; 148 | } 149 | 150 | bool Button::getState() { 151 | return state; 152 | } 153 | 154 | int Button::getClicks() { 155 | return (int)clicks; 156 | } 157 | 158 | int Button::getPushTime() { 159 | return (int)(millis() - pushTime); 160 | } 161 | 162 | bool Button::pushed() { 163 | if (pushedFlag) { 164 | pushedFlag = false; 165 | return true; 166 | } 167 | return false; 168 | } 169 | 170 | bool Button::released() { 171 | if (releasedFlag && (pushTime < releaseTime)) { 172 | releasedFlag = false; 173 | return true; 174 | } 175 | return false; 176 | } 177 | 178 | bool Button::clicked() { 179 | return clicked(defaultMinPushTime); 180 | } 181 | 182 | bool Button::clicked(uint32_t minPushTime) { 183 | return clicked(minPushTime, defaultMinReleaseTime); 184 | } 185 | 186 | bool Button::clicked(uint32_t minPushTime, uint32_t minReleaseTime) { 187 | bool notHolding = !holdFlag; 188 | bool minTime = millis() - pushTime >= minPushTime; 189 | bool releaseTimeout = millis() - releaseTime >= minReleaseTime; 190 | 191 | if (notHolding && minTime && releaseTimeout) { 192 | if (released()) { 193 | clicks++; 194 | return true; 195 | } 196 | } 197 | 198 | return false; 199 | } 200 | 201 | bool Button::doubleClicked() { 202 | return doubleClicked(defaultMinPushTime); 203 | } 204 | 205 | bool Button::doubleClicked(uint32_t minPushTime) { 206 | return doubleClicked(minPushTime, defaultTimeSpan); 207 | } 208 | 209 | bool Button::doubleClicked(uint32_t minPushTime, uint32_t timeSpan) { 210 | return doubleClicked(minPushTime, defaultMinReleaseTime, timeSpan); 211 | } 212 | 213 | bool Button::doubleClicked(uint32_t minPushTime, uint32_t minReleaseTime, uint32_t timeSpan) { 214 | bool wasPrevClicked = prevReleaseTime - prevPushTime >= minPushTime; 215 | bool inTimeSpan = millis() - prevPushTime <= timeSpan; 216 | bool releaseTimeout = millis() - prevReleaseTime >= minReleaseTime; 217 | 218 | if (wasPrevClicked && inTimeSpan && releaseTimeout) { 219 | if (clicked(minPushTime)) { 220 | pushTime = 0; 221 | return true; 222 | } 223 | } 224 | 225 | return false; 226 | } 227 | 228 | bool Button::holding() { 229 | return holding(defaultHoldInterval); 230 | } 231 | 232 | bool Button::holding(uint32_t interval) { 233 | if (getState() && (millis() - holdTime >= interval)) { 234 | holdTime = millis(); 235 | holdFlag = true; 236 | return true; 237 | } 238 | return false; 239 | } 240 | 241 | void Button::setUpdateInterval(uint32_t updateInterval) { 242 | this->updateInterval = updateInterval; 243 | } 244 | 245 | void Button::setDefaultMinPushTime(uint32_t defaultMinPushTime) { 246 | this->defaultMinPushTime = defaultMinPushTime; 247 | } 248 | 249 | void Button::setDefaultMinReleaseTime(uint32_t defaultMinReleaseTime) { 250 | this->defaultMinReleaseTime = defaultMinReleaseTime; 251 | } 252 | 253 | void Button::setDefaultTimeSpan(uint32_t defaultTimeSpan) { 254 | this->defaultTimeSpan = defaultTimeSpan; 255 | } 256 | 257 | void Button::setDefaultHoldTime(uint32_t defaultHoldInterval) { 258 | this->defaultHoldInterval = defaultHoldInterval; 259 | } 260 | 261 | void Button::setOnPushed(ButtonEventFunction) { 262 | this->addEvent(new PushEvent(fnct)); 263 | } 264 | 265 | void Button::setOnReleased(ButtonEventFunction) { 266 | this->addEvent(new ReleaseEvent(fnct)); 267 | } 268 | 269 | void Button::setOnClicked(ButtonEventFunction) { 270 | setOnClicked(fnct, defaultMinPushTime, defaultMinReleaseTime); 271 | } 272 | 273 | void Button::setOnClicked(ButtonEventFunction, uint32_t minPushTime) { 274 | setOnClicked(fnct, minPushTime, defaultMinReleaseTime); 275 | } 276 | 277 | void Button::setOnClicked(ButtonEventFunction, uint32_t minPushTime, uint32_t minReleaseTime) { 278 | this->addEvent(new ClickEvent(fnct, minPushTime, minReleaseTime)); 279 | } 280 | 281 | void Button::setOnDoubleClicked(ButtonEventFunction) { 282 | setOnDoubleClicked(fnct, defaultMinPushTime, defaultMinReleaseTime, defaultTimeSpan); 283 | } 284 | 285 | void Button::setOnDoubleClicked(ButtonEventFunction, uint32_t minPushTime) { 286 | setOnDoubleClicked(fnct, minPushTime, defaultMinReleaseTime, defaultTimeSpan); 287 | } 288 | 289 | void Button::setOnDoubleClicked(ButtonEventFunction, uint32_t minPushTime, uint32_t timeSpan) { 290 | setOnDoubleClicked(fnct, minPushTime, defaultMinReleaseTime, timeSpan); 291 | } 292 | 293 | void Button::setOnDoubleClicked(ButtonEventFunction, uint32_t minPushTime, uint32_t minReleaseTime, uint32_t timeSpan) { 294 | this->addEvent(new DoubleclickEvent(fnct, minPushTime, minReleaseTime, timeSpan)); 295 | } 296 | 297 | void Button::setOnHolding(ButtonEventFunction) { 298 | setOnHolding(fnct, defaultHoldInterval); 299 | } 300 | 301 | void Button::setOnHolding(ButtonEventFunction, uint32_t interval) { 302 | this->addEvent(new HoldEvent(fnct, interval)); 303 | } 304 | 305 | void Button::clearEvents() { 306 | delete events; 307 | events = NULL; 308 | } 309 | 310 | void Button::addEvent(Event* e) { 311 | if (events == NULL) events = e; 312 | else { 313 | Event* tmp = events; 314 | 315 | while (tmp->next != NULL) tmp = tmp->next; 316 | tmp->next = e; 317 | } 318 | } 319 | } -------------------------------------------------------------------------------- /src/Buttons/Button.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_Button_h 2 | #define SimpleButton_Button_h 3 | 4 | #include "Arduino.h" 5 | #include "Events/Event.h" 6 | #include "Events/PushEvent.h" 7 | #include "Events/ReleaseEvent.h" 8 | #include "Events/ClickEvent.h" 9 | #include "Events/DoubleclickEvent.h" 10 | #include "Events/HoldEvent.h" 11 | 12 | namespace simplebutton { 13 | class Button { 14 | public: 15 | Button(); 16 | Button(uint8_t pin); 17 | Button(uint8_t pin, bool inverted); 18 | 19 | virtual ~Button(); 20 | 21 | void setup(uint8_t pin, bool inverted); 22 | 23 | virtual void enable(); 24 | virtual void disable(); 25 | virtual void reset(); 26 | 27 | virtual void push(); 28 | virtual void release(); 29 | 30 | virtual void click(); 31 | virtual void click(uint32_t time); 32 | 33 | virtual int read(); 34 | virtual void update(); 35 | virtual void update(int state); 36 | virtual void updateEvents(); 37 | 38 | virtual bool isInverted(); 39 | virtual bool isEnabled(); 40 | virtual bool isSetup(); 41 | 42 | virtual bool getState(); 43 | virtual int getClicks(); 44 | virtual int getPushTime(); 45 | 46 | virtual bool pushed(); 47 | virtual bool released(); 48 | virtual bool clicked(); 49 | virtual bool clicked(uint32_t minPushTime); 50 | virtual bool clicked(uint32_t minPushTime, uint32_t minReleaseTime); 51 | virtual bool doubleClicked(); 52 | virtual bool doubleClicked(uint32_t minPushTime); 53 | virtual bool doubleClicked(uint32_t minPushTime, uint32_t timeSpan); 54 | virtual bool doubleClicked(uint32_t minPushTime, uint32_t minReleaseTime, uint32_t timeSpan); 55 | virtual bool holding(); 56 | virtual bool holding(uint32_t interval); 57 | 58 | virtual void setUpdateInterval(uint32_t updateInterval); 59 | virtual void setDefaultMinPushTime(uint32_t defaultMinPushTime); 60 | virtual void setDefaultMinReleaseTime(uint32_t defaultMinReleaseTime); 61 | virtual void setDefaultTimeSpan(uint32_t defaultTimeSpan); 62 | virtual void setDefaultHoldTime(uint32_t defaultHoldInterval); 63 | 64 | virtual void setOnPushed(ButtonEventFunction); 65 | virtual void setOnReleased(ButtonEventFunction); 66 | virtual void setOnClicked(ButtonEventFunction); 67 | virtual void setOnClicked(ButtonEventFunction, uint32_t minPushTime); 68 | virtual void setOnClicked(ButtonEventFunction, uint32_t minPushTime, uint32_t minReleaseTime); 69 | virtual void setOnDoubleClicked(ButtonEventFunction); 70 | virtual void setOnDoubleClicked(ButtonEventFunction, uint32_t minPushTime); 71 | virtual void setOnDoubleClicked(ButtonEventFunction, uint32_t minPushTime, uint32_t timeSpan); 72 | virtual void setOnDoubleClicked( 73 | ButtonEventFunction, uint32_t minPushTime, uint32_t minReleaseTime, uint32_t timeSpan); 74 | virtual void setOnHolding(ButtonEventFunction); 75 | virtual void setOnHolding(ButtonEventFunction, uint32_t interval); 76 | 77 | virtual void clearEvents(); 78 | 79 | protected: 80 | Event* events = NULL; 81 | 82 | bool button_inverted = false; 83 | bool button_setup = false; 84 | bool button_enabled = false; 85 | bool state = false; 86 | bool pushedFlag = false; 87 | bool releasedFlag = false; 88 | bool holdFlag = false; 89 | 90 | uint8_t button_pin = 255; 91 | 92 | uint16_t clicks = 0; 93 | 94 | uint32_t pushTime = 0; 95 | uint32_t releaseTime = 0; 96 | uint32_t prevPushTime = 0; 97 | uint32_t prevReleaseTime = 0; 98 | uint32_t holdTime = 0; 99 | uint32_t updateTime = 0; 100 | 101 | uint32_t updateInterval = 5; 102 | uint32_t defaultMinPushTime = 40; 103 | uint32_t defaultMinReleaseTime = 40; 104 | uint32_t defaultTimeSpan = 500; 105 | uint32_t defaultHoldInterval = 500; 106 | 107 | void addEvent(Event* e); 108 | }; 109 | } 110 | 111 | #endif // ifndef SimpleButton_Button_h -------------------------------------------------------------------------------- /src/Buttons/ButtonAnalog.cpp: -------------------------------------------------------------------------------- 1 | #include "ButtonAnalog.h" 2 | 3 | namespace simplebutton { 4 | ButtonAnalog::ButtonAnalog() { 5 | setup(255, 0, 1024); 6 | } 7 | 8 | ButtonAnalog::ButtonAnalog(uint8_t pin) { 9 | setup(pin, 0, 1024); 10 | } 11 | 12 | ButtonAnalog::ButtonAnalog(uint16_t minValue, uint16_t maxValue) { 13 | setup(255, minValue, maxValue); 14 | } 15 | 16 | ButtonAnalog::ButtonAnalog(uint8_t pin, uint16_t minValue, uint16_t maxValue) { 17 | setup(pin, minValue, maxValue); 18 | } 19 | 20 | ButtonAnalog::~ButtonAnalog() {} 21 | 22 | void ButtonAnalog::setup(uint8_t pin, uint16_t minValue, uint16_t maxValue) { 23 | this->button_pin = pin; 24 | this->minValue = minValue; 25 | this->maxValue = maxValue; 26 | enable(); 27 | } 28 | 29 | int ButtonAnalog::read() { 30 | int currentState = 0; 31 | 32 | if (button_enabled && button_setup) { 33 | currentState = analogRead(button_pin); 34 | } 35 | 36 | return currentState; 37 | } 38 | 39 | void ButtonAnalog::update() { 40 | if (millis() - updateTime >= updateInterval) { 41 | Button::updateEvents(); 42 | if (button_enabled && button_setup) update(read()); 43 | } 44 | } 45 | 46 | void ButtonAnalog::update(int state) { 47 | uint16_t newState = state; 48 | 49 | updateTime = millis(); 50 | 51 | value = newState; 52 | 53 | if ((newState >= minValue) && (newState <= maxValue)) push(); 54 | else release(); 55 | } 56 | 57 | void ButtonAnalog::setMin(uint16_t minValue) { 58 | this->minValue = minValue; 59 | } 60 | 61 | void ButtonAnalog::setMax(uint16_t maxValue) { 62 | this->maxValue = maxValue; 63 | } 64 | 65 | void ButtonAnalog::setBounds(uint16_t minValue, uint16_t maxValue) { 66 | setMin(minValue); 67 | setMax(maxValue); 68 | } 69 | 70 | void ButtonAnalog::setValue(int value) { 71 | this->value = (uint16_t)value; 72 | } 73 | 74 | uint16_t ButtonAnalog::getValue() { 75 | return value; 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /src/Buttons/ButtonAnalog.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_ButtonAnalog_h 2 | #define SimpleButton_ButtonAnalog_h 3 | 4 | #include "Button.h" 5 | 6 | 7 | namespace simplebutton { 8 | class ButtonAnalog : public Button { 9 | public: 10 | ButtonAnalog(); 11 | ButtonAnalog(uint8_t pin); 12 | ButtonAnalog(uint16_t minValue, uint16_t maxValue); 13 | ButtonAnalog(uint8_t pin, uint16_t minValue, uint16_t maxValue); 14 | 15 | ~ButtonAnalog(); 16 | 17 | void setup(uint8_t pin, uint16_t minValue, uint16_t maxValue); 18 | 19 | int read(); 20 | 21 | void update(); 22 | void update(int state); 23 | 24 | void setMin(uint16_t minValue); 25 | void setMax(uint16_t maxValue); 26 | void setBounds(uint16_t minValue, uint16_t maxValue); 27 | 28 | uint16_t getValue(); 29 | void setValue(int value); 30 | 31 | private: 32 | uint16_t minValue = 0; 33 | uint16_t maxValue = 1024; 34 | uint16_t value = 0; 35 | }; 36 | } 37 | #endif // ifndef SimpleButton_ButtonAnalog_h 38 | -------------------------------------------------------------------------------- /src/Buttons/ButtonGPIOExpander.cpp: -------------------------------------------------------------------------------- 1 | #include "ButtonGPIOExpander.h" 2 | 3 | namespace simplebutton { 4 | ButtonGPIOExpander::ButtonGPIOExpander() { 5 | setup(NULL, 255, false); 6 | } 7 | 8 | ButtonGPIOExpander::ButtonGPIOExpander(GPIOExpander* pcf, uint8_t pin) { 9 | setup(pcf, pin, false); 10 | } 11 | 12 | ButtonGPIOExpander::ButtonGPIOExpander(GPIOExpander* pcf, uint8_t pin, bool inverted) { 13 | setup(pcf, pin, inverted); 14 | } 15 | 16 | ButtonGPIOExpander::~ButtonGPIOExpander() {} 17 | 18 | void ButtonGPIOExpander::setup(GPIOExpander* pcf, uint8_t pin, bool inverted) { 19 | this->pcf = pcf; 20 | this->button_pin = pin; 21 | this->button_inverted = inverted; 22 | enable(); 23 | } 24 | 25 | void ButtonGPIOExpander::enable() { 26 | button_enabled = true; 27 | 28 | if (pcf) { 29 | pcf->write(button_pin, 0); 30 | if (pcf->connected()) button_setup = true; 31 | } 32 | } 33 | 34 | int ButtonGPIOExpander::read() { 35 | bool currentState = false; 36 | 37 | if (button_enabled && button_setup) { 38 | currentState = pcf->read(button_pin) > 0; 39 | 40 | if (button_inverted) currentState = !currentState; 41 | } 42 | 43 | return (int)currentState; 44 | } 45 | 46 | void ButtonGPIOExpander::update() { 47 | if (button_enabled && button_setup) { 48 | update(read()); 49 | } 50 | } 51 | 52 | void ButtonGPIOExpander::update(int state) { 53 | Button::update(state); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Buttons/ButtonGPIOExpander.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_ButtonGPIOExpander_h 2 | #define SimpleButton_ButtonGPIOExpander_h 3 | 4 | #include "Button.h" 5 | #include "libs/GPIOExpander.h" 6 | #include "libs/PCF8574.h" 7 | #include "libs/PCF8575.h" 8 | #include "libs/MCP23017.h" 9 | 10 | namespace simplebutton { 11 | class ButtonGPIOExpander : public Button { 12 | public: 13 | ButtonGPIOExpander(); 14 | ButtonGPIOExpander(GPIOExpander* pcf, uint8_t pin); 15 | ButtonGPIOExpander(GPIOExpander* pcf, uint8_t pin, bool inverted); 16 | 17 | virtual ~ButtonGPIOExpander(); 18 | 19 | void setup(GPIOExpander* pcf, uint8_t pin, bool inverted); 20 | 21 | virtual void enable(); 22 | 23 | virtual int read(); 24 | virtual void update(); 25 | virtual void update(int state); 26 | 27 | protected: 28 | GPIOExpander* pcf = NULL; 29 | }; 30 | } 31 | 32 | #endif // ifndef SimpleButton_ButtonGPIOExpander_h -------------------------------------------------------------------------------- /src/Buttons/ButtonPullup.cpp: -------------------------------------------------------------------------------- 1 | #include "ButtonPullup.h" 2 | 3 | namespace simplebutton { 4 | ButtonPullup::ButtonPullup() { 5 | setup(255); 6 | } 7 | 8 | ButtonPullup::ButtonPullup(uint8_t pin) { 9 | setup(pin); 10 | } 11 | 12 | ButtonPullup::~ButtonPullup() {} 13 | 14 | void ButtonPullup::setup(uint8_t pin) { 15 | this->button_pin = pin; 16 | this->button_inverted = true; 17 | enable(); 18 | } 19 | 20 | void ButtonPullup::enable() { 21 | button_enabled = true; 22 | 23 | if ((button_pin < 255) && !button_setup) { 24 | pinMode(button_pin, INPUT_PULLUP); 25 | button_setup = true; 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/Buttons/ButtonPullup.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_ButtonPullup_h 2 | #define SimpleButton_ButtonPullup_h 3 | 4 | #include "Button.h" 5 | 6 | namespace simplebutton { 7 | class ButtonPullup : public Button { 8 | public: 9 | ButtonPullup(); 10 | ButtonPullup(uint8_t pin); 11 | 12 | ~ButtonPullup(); 13 | 14 | void setup(uint8_t pin); 15 | 16 | void enable(); 17 | }; 18 | } 19 | 20 | #endif // ifndef SimpleButton_ButtonPullup_h -------------------------------------------------------------------------------- /src/Buttons/ButtonPullupGPIOExpander.cpp: -------------------------------------------------------------------------------- 1 | #include "ButtonPullupGPIOExpander.h" 2 | 3 | namespace simplebutton { 4 | ButtonPullupGPIOExpander::ButtonPullupGPIOExpander() { 5 | setup(NULL, 255); 6 | } 7 | 8 | ButtonPullupGPIOExpander::ButtonPullupGPIOExpander(GPIOExpander* pcf, uint8_t pin) { 9 | setup(pcf, pin); 10 | } 11 | 12 | ButtonPullupGPIOExpander::~ButtonPullupGPIOExpander() {} 13 | 14 | void ButtonPullupGPIOExpander::setup(GPIOExpander* pcf, uint8_t pin) { 15 | this->pcf = pcf; 16 | this->button_pin = pin; 17 | this->button_inverted = true; 18 | enable(); 19 | } 20 | 21 | void ButtonPullupGPIOExpander::enable() { 22 | button_enabled = true; 23 | 24 | if (pcf) { 25 | pcf->write(button_pin, 1); 26 | if (pcf->connected()) button_setup = true; 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/Buttons/ButtonPullupGPIOExpander.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_ButtonPullupGPIOExpander_h 2 | #define SimpleButton_ButtonPullupGPIOExpander_h 3 | 4 | #include "ButtonGPIOExpander.h" 5 | 6 | namespace simplebutton { 7 | class ButtonPullupGPIOExpander : public ButtonGPIOExpander { 8 | public: 9 | ButtonPullupGPIOExpander(); 10 | ButtonPullupGPIOExpander(GPIOExpander* pcf, uint8_t pin); 11 | 12 | ~ButtonPullupGPIOExpander(); 13 | 14 | void setup(GPIOExpander* pcf, uint8_t pin); 15 | 16 | void enable(); 17 | }; 18 | } 19 | 20 | #endif // ifndef SimpleButton_ButtonPullupGPIOExpander_h -------------------------------------------------------------------------------- /src/Buttons/PS2Gamepad.cpp: -------------------------------------------------------------------------------- 1 | #include "PS2Gamepad.h" 2 | 3 | namespace simplebutton { 4 | #include "PS2Gamepad.h" 5 | 6 | PS2Gamepad::PS2Gamepad() {} 7 | 8 | PS2Gamepad::PS2Gamepad(uint8_t clockPin, uint8_t cmdPin, uint8_t attPin, uint8_t dataPin, bool analog) { 9 | setup(clockPin, cmdPin, attPin, dataPin, analog); 10 | } 11 | 12 | PS2Gamepad::~PS2Gamepad() { 13 | if (up) delete up; 14 | if (down) delete down; 15 | if (left) delete left; 16 | if (right) delete right; 17 | 18 | if (l1) delete l1; 19 | if (l2) delete l2; 20 | if (r1) delete r1; 21 | if (r2) delete r2; 22 | 23 | if (square) delete square; 24 | if (triangle) delete triangle; 25 | if (cross) delete cross; 26 | if (circle) delete circle; 27 | 28 | if (select) delete select; 29 | if (start) delete start; 30 | 31 | if (analogLeft) delete analogLeft; 32 | if (analogRight) delete analogRight; 33 | } 34 | 35 | void PS2Gamepad::setup(uint8_t clockPin, uint8_t cmdPin, uint8_t attPin, uint8_t dataPin, bool analog) { 36 | // pin setup 37 | this->clockPin = clockPin; 38 | this->cmdPin = cmdPin; 39 | this->attPin = attPin; 40 | this->dataPin = dataPin; 41 | this->rumbleEnabled = analog; 42 | this->pressureEnabled = analog; 43 | 44 | pinMode(clockPin, OUTPUT); 45 | pinMode(attPin, OUTPUT); 46 | pinMode(cmdPin, OUTPUT); 47 | 48 | pinMode(dataPin, INPUT_PULLUP); 49 | 50 | digitalWrite(cmdPin, HIGH); 51 | digitalWrite(clockPin, HIGH); 52 | 53 | // button setup 54 | if (!up) up = new ButtonAnalog(1, 255); 55 | if (!down) down = new ButtonAnalog(1, 255); 56 | if (!left) left = new ButtonAnalog(1, 255); 57 | if (!right) right = new ButtonAnalog(1, 255); 58 | 59 | if (!l1) l1 = new ButtonAnalog(1, 255); 60 | if (!l2) l2 = new ButtonAnalog(1, 255); 61 | if (!r1) r1 = new ButtonAnalog(1, 255); 62 | if (!r2) r2 = new ButtonAnalog(1, 255); 63 | 64 | if (!square) square = new ButtonAnalog(1, 255); 65 | if (!triangle) triangle = new ButtonAnalog(1, 255); 66 | if (!cross) cross = new ButtonAnalog(1, 255); 67 | if (!circle) circle = new ButtonAnalog(1, 255); 68 | 69 | if (!select) select = new Button(); 70 | if (!start) start = new Button(); 71 | 72 | if (!analogLeft) { 73 | analogLeft = new AnalogStick(); 74 | analogLeft->setLogic(256); 75 | } 76 | if (!analogRight) { 77 | analogRight = new AnalogStick(); 78 | analogRight->setLogic(256); 79 | } 80 | 81 | // connection setup 82 | poll(); 83 | delay(10); 84 | poll(); 85 | delay(10); 86 | poll(); 87 | 88 | /* 89 | if ((gamepadData[1] != 0x41) && (gamepadData[1] != 0x73) && (gamepadData[1] != 0x79)) { 90 | errorCode = 1; 91 | return; 92 | } 93 | */ 94 | 95 | int tries = 0; 96 | bool success = false; 97 | 98 | do { 99 | sendEnter(); 100 | 101 | delayMicroseconds(5); 102 | 103 | digitalWrite(cmdPin, HIGH); 104 | digitalWrite(clockPin, HIGH); 105 | digitalWrite(attPin, LOW); // enable joystick 106 | 107 | delayMicroseconds(5); 108 | 109 | sendRead(); 110 | 111 | digitalWrite(attPin, HIGH); // disable joystick 112 | 113 | sendMode(); 114 | if (rumbleEnabled) sendRumble(); 115 | if (pressureEnabled) sendBytesLarge(); 116 | sendExit(); 117 | 118 | poll(); 119 | 120 | if ((gamepadData[1] == 0x79) || (gamepadData[1] == 0x73)) { 121 | success = true; 122 | } else { 123 | tries++; 124 | } 125 | } while (tries < 10 && !success); 126 | 127 | if (!success) { 128 | errorCode = 2; 129 | } else { 130 | is_connected = true; 131 | } 132 | } 133 | 134 | void PS2Gamepad::update() { 135 | if (is_connected && (millis() - lastPoll > updateInterval)) { 136 | poll(); 137 | 138 | up->update(getDigitalValue(4)); 139 | down->update(getDigitalValue(6)); 140 | left->update(getDigitalValue(7)); 141 | right->update(getDigitalValue(5)); 142 | 143 | l1->update(getDigitalValue(10)); 144 | l2->update(getDigitalValue(8)); 145 | r1->update(getDigitalValue(11)); 146 | r2->update(getDigitalValue(9)); 147 | 148 | square->update(getDigitalValue(15)); 149 | triangle->update(getDigitalValue(12)); 150 | cross->update(getDigitalValue(14)); 151 | circle->update(getDigitalValue(13)); 152 | 153 | select->update(getDigitalValue(0)); 154 | start->update(getDigitalValue(3)); 155 | 156 | analogLeft->update(getAnalogValue(7), getAnalogValue(8), getDigitalValue(1)); 157 | analogRight->update(getAnalogValue(5), getAnalogValue(6), getDigitalValue(2)); 158 | 159 | if (pressureEnabled) { 160 | up->setValue(getDigitalValue(4) | getAnalogValue(11)); 161 | down->setValue(getDigitalValue(6) | getAnalogValue(12)); 162 | left->setValue(getDigitalValue(7) | getAnalogValue(10)); 163 | right->setValue(getDigitalValue(5) | getAnalogValue(9)); 164 | 165 | l1->setValue(getDigitalValue(10) | getAnalogValue(17)); 166 | l2->setValue(getDigitalValue(8) | getAnalogValue(19)); 167 | r1->setValue(getDigitalValue(11) | getAnalogValue(18)); 168 | r2->setValue(getDigitalValue(9) | getAnalogValue(20)); 169 | 170 | square->setValue(getDigitalValue(15) | getAnalogValue(16)); 171 | triangle->setValue(getDigitalValue(12) | getAnalogValue(13)); 172 | cross->setValue(getDigitalValue(14) | getAnalogValue(15)); 173 | circle->setValue(getDigitalValue(13) | getAnalogValue(14)); 174 | } 175 | } 176 | } 177 | 178 | String PS2Gamepad::getError() { 179 | String msg; 180 | 181 | switch (errorCode) { 182 | case 0: 183 | msg += String("OK"); 184 | break; 185 | 186 | case 1: 187 | msg += String("Mode not matched or not found ["); 188 | msg += String(gamepadData[1], HEX); 189 | msg += String("]"); 190 | break; 191 | 192 | case 2: 193 | msg += String("Not accepting commands ["); 194 | msg += String(gamepadData[1], HEX); 195 | msg += String("]"); 196 | break; 197 | } 198 | 199 | errorCode = 0; 200 | 201 | return msg; 202 | } 203 | 204 | bool PS2Gamepad::connected() { 205 | return is_connected; 206 | } 207 | 208 | void PS2Gamepad::setUpdateInterval(uint32_t updateInterval) { 209 | this->updateInterval = updateInterval; 210 | } 211 | 212 | void PS2Gamepad::setMotors(uint8_t motorA, uint8_t motorB) { 213 | if (!rumbleEnabled) { 214 | rumbleEnabled = true; 215 | pressureEnabled = true; 216 | reconfig(); 217 | } 218 | poll(motorA, motorB); 219 | } 220 | 221 | void PS2Gamepad::reconfig() { 222 | sendEnter(); 223 | sendMode(); 224 | if (rumbleEnabled) sendRumble(); 225 | if (pressureEnabled) sendBytesLarge(); 226 | sendExit(); 227 | } 228 | 229 | void PS2Gamepad::poll() { 230 | if (millis() - lastPoll > 1500) reconfig(); 231 | lastPoll = millis(); 232 | 233 | uint8_t dwordA[9] = { 0x01, 0x42, 0x00, motorA, motorB, 0x00, 0x00, 0x00, 0x00 }; 234 | uint8_t dwordB[12] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; 235 | 236 | int tries = 0; 237 | bool success = false; 238 | 239 | do { 240 | digitalWrite(cmdPin, HIGH); 241 | digitalWrite(clockPin, HIGH); 242 | digitalWrite(attPin, LOW); // low enable joystick 243 | 244 | delayMicroseconds(5); 245 | 246 | // send command to ask for data 247 | for (int i = 0; i < 9; i++) { 248 | gamepadData[i] = shift(dwordA[i]); 249 | } 250 | 251 | // if controller is in full data return mode, get the rest of data 252 | if (gamepadData[1] == 0x79) { 253 | for (int i = 0; i < 12; i++) { 254 | gamepadData[i + 9] = shift(dwordB[i]); 255 | } 256 | } 257 | 258 | digitalWrite(attPin, HIGH); // disable joystick 259 | 260 | // Check to see if we received valid data or not. 261 | // We should be in analog mode for our data to be valid (analog == 0x7_) 262 | if ((gamepadData[1] & 0xf0) == 0x70) { 263 | success = true; 264 | } else { 265 | // If we got to here, we are not in analog mode, try to recover... 266 | reconfig(); 267 | tries++; 268 | } 269 | } while (tries < 10 && !success); 270 | 271 | prevButtonData = buttonData; 272 | 273 | buttonData = ((uint16_t)gamepadData[4] << 8) | gamepadData[3]; 274 | } 275 | 276 | void PS2Gamepad::poll(uint8_t motorA, uint8_t motorB) { 277 | this->motorA = motorA; 278 | this->motorB = motorB; 279 | poll(); 280 | } 281 | 282 | bool PS2Gamepad::getDigitalValue(uint8_t button) { 283 | if (/*button >= 0 && */ button <= 15) return (~buttonData >> button) & 1; 284 | else return 0; 285 | } 286 | 287 | uint8_t PS2Gamepad::getAnalogValue(uint8_t button) { 288 | return gamepadData[button]; 289 | } 290 | 291 | uint8_t PS2Gamepad::shift(uint8_t data) { 292 | uint8_t tmp = 0; 293 | 294 | for (int i = 0; i < 8; i++) { 295 | if (data & (1 << i)) digitalWrite(cmdPin, HIGH); 296 | else digitalWrite(cmdPin, LOW); 297 | 298 | digitalWrite(clockPin, LOW); 299 | delayMicroseconds(5); 300 | 301 | if (digitalRead(dataPin)) bitSet(tmp, i); 302 | 303 | digitalWrite(clockPin, HIGH); 304 | delayMicroseconds(5); 305 | } 306 | 307 | digitalWrite(cmdPin, HIGH); 308 | delayMicroseconds(5); 309 | 310 | return tmp; 311 | } 312 | 313 | void PS2Gamepad::sendEnter() { 314 | digitalWrite(attPin, LOW); 315 | 316 | shift(0x01); 317 | shift(0x43); 318 | shift(0x00); 319 | shift(0x01); 320 | shift(0x00); 321 | 322 | digitalWrite(attPin, HIGH); 323 | } 324 | 325 | void PS2Gamepad::sendMode() { 326 | digitalWrite(attPin, LOW); 327 | 328 | shift(0x01); 329 | shift(0x44); 330 | shift(0x00); 331 | shift(0x01); 332 | shift(0x03); 333 | shift(0x00); 334 | shift(0x00); 335 | shift(0x00); 336 | shift(0x00); 337 | 338 | digitalWrite(attPin, HIGH); 339 | } 340 | 341 | void PS2Gamepad::sendRumble() { 342 | digitalWrite(attPin, LOW); 343 | 344 | shift(0x01); 345 | shift(0x4D); 346 | shift(0x00); 347 | shift(0x00); 348 | shift(0x01); 349 | 350 | digitalWrite(attPin, HIGH); 351 | } 352 | 353 | void PS2Gamepad::sendBytesLarge() { 354 | digitalWrite(attPin, LOW); 355 | 356 | shift(0x01); 357 | shift(0x4F); 358 | shift(0x00); 359 | shift(0xFF); 360 | shift(0xFF); 361 | shift(0x03); 362 | shift(0x00); 363 | shift(0x00); 364 | shift(0x00); 365 | 366 | digitalWrite(attPin, HIGH); 367 | } 368 | 369 | void PS2Gamepad::sendExit() { 370 | digitalWrite(attPin, LOW); 371 | 372 | shift(0x01); 373 | shift(0x43); 374 | shift(0x00); 375 | shift(0x00); 376 | shift(0x5A); 377 | shift(0x5A); 378 | shift(0x5A); 379 | shift(0x5A); 380 | shift(0x5A); 381 | 382 | digitalWrite(attPin, HIGH); 383 | } 384 | 385 | void PS2Gamepad::sendRead() { 386 | digitalWrite(attPin, LOW); 387 | 388 | shift(0x01); 389 | shift(0x45); 390 | shift(0x00); 391 | controllerType = shift(0x5A); 392 | shift(0x5A); 393 | shift(0x5A); 394 | shift(0x5A); 395 | shift(0x5A); 396 | shift(0x5A); 397 | 398 | digitalWrite(attPin, HIGH); 399 | } 400 | } 401 | -------------------------------------------------------------------------------- /src/Buttons/PS2Gamepad.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_PS2Gamepad_h 2 | #define SimpleButton_PS2Gamepad_h 3 | 4 | #include "Button.h" 5 | #include "AnalogStick.h" 6 | 7 | namespace simplebutton { 8 | class PS2Gamepad { 9 | public: 10 | ButtonAnalog* up = NULL; 11 | ButtonAnalog* down = NULL; 12 | ButtonAnalog* left = NULL; 13 | ButtonAnalog* right = NULL; 14 | 15 | ButtonAnalog* l1 = NULL; 16 | ButtonAnalog* l2 = NULL; 17 | ButtonAnalog* r1 = NULL; 18 | ButtonAnalog* r2 = NULL; 19 | 20 | ButtonAnalog* square = NULL; 21 | ButtonAnalog* triangle = NULL; 22 | ButtonAnalog* cross = NULL; 23 | ButtonAnalog* circle = NULL; 24 | 25 | Button* select = NULL; 26 | Button* start = NULL; 27 | 28 | AnalogStick* analogLeft = NULL; 29 | AnalogStick* analogRight = NULL; 30 | 31 | PS2Gamepad(); 32 | PS2Gamepad(uint8_t clockPin, uint8_t cmdPin, uint8_t attPin, uint8_t dataPin, bool analog = true); 33 | ~PS2Gamepad(); 34 | 35 | void setup(uint8_t clockPin, uint8_t cmdPin, uint8_t attPin, uint8_t dataPin, bool analog = true); 36 | void reconfig(); 37 | 38 | void update(); 39 | 40 | String getError(); 41 | 42 | bool connected(); 43 | 44 | void setUpdateInterval(uint32_t updateInterval); 45 | void setMotors(uint8_t motorA, uint8_t motorB); 46 | 47 | private: 48 | void poll(); 49 | void poll(uint8_t motorA, uint8_t motorB); 50 | 51 | bool getDigitalValue(uint8_t button); 52 | uint8_t getAnalogValue(uint8_t button); 53 | 54 | // config 55 | uint8_t motorA = 0x00; // 0 = OFF, 1 = ONN 56 | uint8_t motorB = 0x00; // usually 0-39 = OFF, 40 - 255 = ON 57 | 58 | bool rumbleEnabled = false; 59 | bool pressureEnabled = false; 60 | 61 | uint32_t updateInterval = 25; 62 | 63 | // class variables 64 | bool is_connected = false; 65 | 66 | uint8_t errorCode = 0; 67 | uint8_t controllerType = 0; 68 | uint8_t gamepadData[21]; 69 | 70 | uint16_t buttonData = 0; 71 | uint16_t prevButtonData = 0; 72 | 73 | uint32_t lastPoll = 0; 74 | 75 | uint8_t clockPin = 0; 76 | uint8_t cmdPin = 0; 77 | uint8_t attPin = 0; 78 | uint8_t dataPin = 0; 79 | 80 | // in/output 81 | uint8_t shift(uint8_t data); 82 | 83 | // commands 84 | void sendEnter(); 85 | void sendMode(); 86 | void sendRumble(); 87 | void sendBytesLarge(); 88 | void sendExit(); 89 | void sendRead(); 90 | }; 91 | } 92 | 93 | #endif // ifndef SimpleButton_PS2Gamepad_h 94 | -------------------------------------------------------------------------------- /src/Buttons/RotaryEncoder.cpp: -------------------------------------------------------------------------------- 1 | #include "RotaryEncoder.h" 2 | 3 | namespace simplebutton { 4 | RotaryEncoder::RotaryEncoder() { 5 | setButtons(NULL, NULL, NULL); 6 | } 7 | 8 | RotaryEncoder::RotaryEncoder(uint8_t channelA, uint8_t channelB, uint8_t button) { 9 | setup(channelA, channelB, button); 10 | } 11 | 12 | RotaryEncoder::RotaryEncoder(GPIOExpander* pcf, uint8_t channelA, uint8_t channelB, uint8_t button) { 13 | setup(pcf, channelA, channelB, button); 14 | } 15 | 16 | RotaryEncoder::RotaryEncoder(Button* clockwise, Button* anticlockwise, Button* button) { 17 | setup(clockwise, anticlockwise, button); 18 | } 19 | 20 | RotaryEncoder::~RotaryEncoder() { 21 | if (this->clockwise) delete this->clockwise; 22 | if (this->anticlockwise) delete this->anticlockwise; 23 | if (this->button) delete this->button; 24 | } 25 | 26 | void RotaryEncoder::setup(uint8_t channelA, uint8_t channelB, uint8_t button) { 27 | this->clockwise = new ButtonPullup(channelA); 28 | this->anticlockwise = new ButtonPullup(channelB); 29 | this->button = new ButtonPullup(button); 30 | 31 | prevA = clockwise->read(); 32 | prevB = anticlockwise->read(); 33 | } 34 | 35 | void RotaryEncoder::setup(GPIOExpander* pcf, uint8_t channelA, uint8_t channelB, uint8_t button) { 36 | this->clockwise = new ButtonPullupGPIOExpander(pcf, channelA); 37 | this->anticlockwise = new ButtonPullupGPIOExpander(pcf, channelB); 38 | this->button = new ButtonPullupGPIOExpander(pcf, button); 39 | 40 | prevA = clockwise->read(); 41 | prevB = anticlockwise->read(); 42 | } 43 | 44 | void RotaryEncoder::setup(Button* clockwise, Button* anticlockwise, Button* button) { 45 | setButtons(clockwise, anticlockwise, button); 46 | 47 | prevA = clockwise->read(); 48 | prevB = anticlockwise->read(); 49 | } 50 | 51 | void RotaryEncoder::update() { 52 | update(clockwise->read(), anticlockwise->read(), button->read()); 53 | } 54 | 55 | void RotaryEncoder::update(bool stateA, bool stateB, bool buttonState) { 56 | button->update(buttonState); 57 | 58 | if (curState == State::STILL) { 59 | if ((stateA != prevA) && (stateB == prevB)) { 60 | prevA = stateA; 61 | curState = State::ANTICLOCKWISE; 62 | } else if ((stateA == prevA) && (stateB != prevB)) { 63 | prevB = stateB; 64 | curState = State::CLOCKWISE; 65 | } 66 | } else if ((curState != State::STILL) && (stateA == stateB)) { 67 | prevA = stateA; 68 | prevB = stateB; 69 | 70 | if (curState == prevState) steps++; 71 | else steps = 1; 72 | 73 | if (steps >= button_steps) { 74 | if (curState == State::CLOCKWISE) { 75 | if (!inverted) goClockwise(); 76 | else goAnticlockwise(); 77 | } else if (curState == State::ANTICLOCKWISE) { 78 | if (!inverted) goAnticlockwise(); 79 | else goClockwise(); 80 | } 81 | 82 | steps = 0; 83 | } 84 | 85 | prevState = curState; 86 | curState = State::STILL; 87 | } 88 | } 89 | 90 | void RotaryEncoder::reset() { 91 | button->reset(); 92 | clockwise->reset(); 93 | anticlockwise->reset(); 94 | 95 | curState = State::STILL; 96 | prevState = State::STILL; 97 | 98 | steps = 0; 99 | } 100 | 101 | int32_t RotaryEncoder::getPos() { 102 | return pos; 103 | } 104 | 105 | void RotaryEncoder::setButtons(Button* clockwise, Button* anticlockwise, Button* button) { 106 | if (this->clockwise) delete this->clockwise; 107 | if (this->anticlockwise) delete this->anticlockwise; 108 | if (this->button) delete this->button; 109 | 110 | this->clockwise = clockwise ? clockwise : new Button(); 111 | this->anticlockwise = anticlockwise ? anticlockwise : new Button(); 112 | this->button = button ? button : new Button(); 113 | } 114 | 115 | void RotaryEncoder::setPos(int32_t pos) { 116 | this->pos = pos; 117 | } 118 | 119 | void RotaryEncoder::setMin(int32_t value) { 120 | this->min = value; 121 | } 122 | 123 | void RotaryEncoder::setMax(int32_t value) { 124 | this->max = value; 125 | } 126 | 127 | void RotaryEncoder::setEncoding(uint8_t steps) { 128 | if ((steps == 1) || (steps == 2) || (steps == 4)) this->button_steps = steps; 129 | } 130 | 131 | void RotaryEncoder::enableLoop(bool loop) { 132 | this->loop = loop; 133 | } 134 | 135 | void RotaryEncoder::setInverted(bool inverted) { 136 | this->inverted = inverted; 137 | } 138 | 139 | void RotaryEncoder::goClockwise() { 140 | clockwise->click(); 141 | anticlockwise->reset(); 142 | if (pos < max) pos++; 143 | else if (loop) pos = min; 144 | } 145 | 146 | void RotaryEncoder::goAnticlockwise() { 147 | anticlockwise->click(); 148 | clockwise->reset(); 149 | if (pos > min) pos--; 150 | else if (loop) pos = max; 151 | } 152 | 153 | bool RotaryEncoder::clicked() { 154 | return button->clicked(); 155 | } 156 | 157 | bool RotaryEncoder::incremented() { 158 | return clockwise->clicked(); 159 | } 160 | 161 | bool RotaryEncoder::decremented() { 162 | return anticlockwise->clicked(); 163 | } 164 | 165 | bool RotaryEncoder::minVal() { 166 | return pos == min; 167 | } 168 | 169 | bool RotaryEncoder::maxVal() { 170 | return pos == max; 171 | } 172 | } -------------------------------------------------------------------------------- /src/Buttons/RotaryEncoder.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_RotaryEncoder_h 2 | #define SimpleButton_RotaryEncoder_h 3 | 4 | #include "Button.h" 5 | #include "ButtonPullup.h" 6 | #include "ButtonGPIOExpander.h" 7 | #include "ButtonPullupGPIOExpander.h" 8 | 9 | namespace simplebutton { 10 | class RotaryEncoder { 11 | public: 12 | Button* button = NULL; 13 | Button* clockwise = NULL; 14 | Button* anticlockwise = NULL; 15 | 16 | RotaryEncoder(); 17 | RotaryEncoder(uint8_t channelA, uint8_t channelB, uint8_t button); 18 | RotaryEncoder(GPIOExpander* pcf, uint8_t channelA, uint8_t channelB, uint8_t button); 19 | RotaryEncoder(Button* clockwise, Button* anticlockwise, Button* button); 20 | 21 | ~RotaryEncoder(); 22 | 23 | void setup(uint8_t channelA, uint8_t channelB, uint8_t button); 24 | void setup(GPIOExpander* pcf, uint8_t channelA, uint8_t channelB, uint8_t button); 25 | void setup(Button* clockwise, Button* anticlockwise, Button* button); 26 | 27 | void update(); 28 | void update(bool stateA, bool stateB, bool buttonState); 29 | 30 | void reset(); 31 | 32 | int32_t getPos(); 33 | 34 | void setButtons(Button* clockwise, Button* anticlockwise, Button* button); 35 | void setPos(int32_t pos); 36 | void enableLoop(bool loop); 37 | void setEncoding(uint8_t steps); 38 | void setMin(int32_t value); 39 | void setMax(int32_t value); 40 | void setInverted(bool inverted); 41 | 42 | bool clicked(); 43 | bool incremented(); 44 | bool decremented(); 45 | bool minVal(); 46 | bool maxVal(); 47 | 48 | private: 49 | int32_t pos = 0; 50 | 51 | bool prevA = false; 52 | bool prevB = false; 53 | 54 | enum State { STILL = 0, CLOCKWISE = 1, ANTICLOCKWISE = 2 }; 55 | State curState = State::STILL; 56 | State prevState = State::STILL; 57 | 58 | uint8_t button_steps = 1; // how many steps per turn (encoding) 59 | uint8_t steps = 0; // tmp counter 60 | 61 | int32_t min = -128; 62 | int32_t max = 127; 63 | bool loop = false; 64 | bool inverted = false; 65 | 66 | void goClockwise(); 67 | void goAnticlockwise(); 68 | }; 69 | } 70 | 71 | #endif // ifndef SimpleButton_RotaryEncoder_h -------------------------------------------------------------------------------- /src/Buttons/RotaryEncoderI2C.cpp: -------------------------------------------------------------------------------- 1 | #include "RotaryEncoderI2C.h" 2 | namespace simplebutton { 3 | RotaryEncoderI2C::RotaryEncoderI2C() { 4 | setup(0x30); 5 | } 6 | 7 | RotaryEncoderI2C::RotaryEncoderI2C(uint8_t i2cAddress) { 8 | setup(i2cAddress); 9 | } 10 | 11 | RotaryEncoderI2C::RotaryEncoderI2C(uint8_t i2cAddress, TwoWire* wire) { 12 | setup(i2cAddress, wire); 13 | } 14 | 15 | RotaryEncoderI2C::~RotaryEncoderI2C() {} 16 | 17 | void RotaryEncoderI2C::setup(uint8_t i2cAddress) { 18 | setup(i2cAddress, &Wire); 19 | } 20 | 21 | void RotaryEncoderI2C::setup(uint8_t i2cAddress, TwoWire* wire) { 22 | this->i2cAddress = i2cAddress; 23 | this->wire = wire; 24 | 25 | this->clockwise = new Button(); 26 | this->anticlockwise = new Button(); 27 | this->button = new Button(); 28 | 29 | setMin(-128); 30 | setMax(127); 31 | 32 | begin(); 33 | } 34 | 35 | bool RotaryEncoderI2C::interrupt() { 36 | if (interruptEnable) return digitalRead(interruptPin) == LOW; 37 | 38 | return true; 39 | } 40 | 41 | void RotaryEncoderI2C::enableInterrupt(uint8_t pin, bool pullup) { 42 | interruptPin = pin; 43 | interruptEnable = true; 44 | interruptPullup = pullup; 45 | pinMode(pin, INPUT); 46 | } 47 | 48 | bool RotaryEncoderI2C::update() { 49 | if (interrupt()) { 50 | readStatus(); 51 | 52 | if (clicked()) button->click(); 53 | 54 | if (incremented()) clockwise->click(); 55 | 56 | if (decremented()) anticlockwise->click(); 57 | 58 | return true; 59 | } 60 | 61 | return false; 62 | } 63 | 64 | void RotaryEncoderI2C::begin() { 65 | uint8_t config = 0x00; 66 | 67 | if (interruptEnable) config = config | 0x01; 68 | if (ledEnabled) config = config | 0x02; 69 | if (loop) config = config | 0x04; 70 | if (inverted) config = config | 0x08; 71 | if (!interruptPullup) config = config | 0x10; 72 | if (encoding) config = config | 0x20; 73 | 74 | setConfig(config); 75 | } 76 | 77 | void RotaryEncoderI2C::reset() { 78 | button->reset(); 79 | clockwise->reset(); 80 | anticlockwise->reset(); 81 | 82 | setConfig(0x80); 83 | update(); 84 | } 85 | 86 | bool RotaryEncoderI2C::connected() { 87 | return error == 0; 88 | } 89 | 90 | String RotaryEncoderI2C::getError() { 91 | String msg; 92 | 93 | switch (error) { 94 | case 0: 95 | msg += "OK"; 96 | break; 97 | 98 | case 1: 99 | msg += String(F("Data too long to fit in transmit buffer")); 100 | break; 101 | 102 | case 2: 103 | msg += String(F("Received NACK on transmit of address")); 104 | break; 105 | 106 | case 3: 107 | msg += String(F("Received NACK on transmit of data")); 108 | 109 | case 4: 110 | msg += String(F("Unknown transmission error")); 111 | break; 112 | 113 | case 5: 114 | msg += String(F("I2C error")); 115 | break; 116 | } 117 | 118 | return msg; 119 | } 120 | 121 | void RotaryEncoderI2C::setConfig(uint8_t config) { 122 | write(0x00, config); 123 | } 124 | 125 | void RotaryEncoderI2C::enableLed(bool led) { 126 | ledEnabled = led; 127 | } 128 | 129 | void RotaryEncoderI2C::enableLoop(bool loop) { 130 | this->loop = loop; 131 | } 132 | 133 | void RotaryEncoderI2C::setEncoding(uint8_t encoding) { 134 | if (encoding == 1) this->encoding = false; 135 | else if (encoding == 2) this->encoding = true; 136 | } 137 | 138 | void RotaryEncoderI2C::setInverted(bool inverted) { 139 | this->inverted = inverted; 140 | } 141 | 142 | void RotaryEncoderI2C::setPos(int32_t value) { 143 | write(0x02, value); 144 | } 145 | 146 | void RotaryEncoderI2C::setMin(int32_t value) { 147 | write(0x0A, value); 148 | } 149 | 150 | void RotaryEncoderI2C::setMax(int32_t value) { 151 | write(0x06, value); 152 | } 153 | 154 | void RotaryEncoderI2C::setLed(uint8_t valueA, uint8_t valueB) { 155 | setLedA(valueA); 156 | setLedB(valueB); 157 | } 158 | 159 | void RotaryEncoderI2C::setLedA(uint8_t value) { 160 | if (ledEnabled) write(0x0E, value); 161 | } 162 | 163 | void RotaryEncoderI2C::setLedB(uint8_t value) { 164 | if (ledEnabled) write(0x0F, value); 165 | } 166 | 167 | int32_t RotaryEncoderI2C::getPos() { 168 | return read32(0x02); 169 | } 170 | 171 | uint8_t RotaryEncoderI2C::readStatus() { 172 | status = read(0x01); 173 | return status; 174 | } 175 | 176 | uint8_t RotaryEncoderI2C::readLedA() { 177 | return read(0x0E); 178 | } 179 | 180 | uint8_t RotaryEncoderI2C::readLedB() { 181 | return read(0x0F); 182 | } 183 | 184 | int32_t RotaryEncoderI2C::readMax() { 185 | return read32(0x06); 186 | } 187 | 188 | int32_t RotaryEncoderI2C::readMin() { 189 | return read32(0x0A); 190 | } 191 | 192 | bool RotaryEncoderI2C::clicked() { 193 | return status & 0x01; 194 | } 195 | 196 | bool RotaryEncoderI2C::incremented() { 197 | return status & 0x02; 198 | } 199 | 200 | bool RotaryEncoderI2C::decremented() { 201 | return status & 0x04; 202 | } 203 | 204 | bool RotaryEncoderI2C::minVal() { 205 | return status & 0x10; 206 | } 207 | 208 | bool RotaryEncoderI2C::maxVal() { 209 | return status & 0x08; 210 | } 211 | 212 | void RotaryEncoderI2C::write(uint8_t address, uint8_t value) { 213 | wire->beginTransmission(i2cAddress); 214 | 215 | wire->write(address); 216 | wire->write(value); 217 | 218 | error = wire->endTransmission(); 219 | } 220 | 221 | void RotaryEncoderI2C::write(uint8_t address, int32_t value) { 222 | wire->beginTransmission(i2cAddress); 223 | 224 | wire->write(address); 225 | wire->write(((uint32_t)value >> 24) & 0xFF); 226 | wire->write(((uint32_t)value >> 16) & 0xFF); 227 | wire->write(((uint32_t)value >> 8) & 0xFF); 228 | wire->write((uint32_t)value & 0xFF); 229 | 230 | error = wire->endTransmission(); 231 | } 232 | 233 | uint8_t RotaryEncoderI2C::read(uint8_t address) { 234 | uint8_t data = 0xFF; 235 | 236 | // ask for some sweet data 237 | 238 | wire->beginTransmission(i2cAddress); 239 | wire->write(address); 240 | error = wire->endTransmission(); 241 | 242 | // read out the sweet data 243 | wire->requestFrom(i2cAddress, (uint8_t)1); 244 | 245 | if (wire->available() == 1) { 246 | data = wire->read(); 247 | } else { 248 | error = ROTARY_ENCODER_I2C_ERROR; 249 | } 250 | 251 | return data; 252 | } 253 | 254 | int32_t RotaryEncoderI2C::read32(uint8_t address) { 255 | uint32_t data = 0xFFFFFFFF; 256 | 257 | // ask for some sweet data 258 | wire->beginTransmission(i2cAddress); 259 | wire->write(address); 260 | error = wire->endTransmission(); 261 | 262 | // read out the sweet data 263 | wire->requestFrom(i2cAddress, (uint8_t)4); 264 | 265 | if (wire->available() == 4) { 266 | data = wire->read(); 267 | data = (data << 8) | wire->read(); 268 | data = (data << 8) | wire->read(); 269 | data = (data << 8) | wire->read(); 270 | } else { 271 | error = ROTARY_ENCODER_I2C_ERROR; 272 | } 273 | 274 | return (int32_t)data; 275 | } 276 | } -------------------------------------------------------------------------------- /src/Buttons/RotaryEncoderI2C.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_RotaryEncoderI2C_h 2 | #define SimpleButton_RotaryEncoderI2C_h 3 | 4 | #include "Arduino.h" 5 | #include 6 | 7 | #define ROTARY_ENCODER_I2C_ERROR 5 8 | 9 | #include "Button.h" 10 | 11 | namespace simplebutton { 12 | class RotaryEncoderI2C { 13 | public: 14 | Button* clockwise = NULL; 15 | Button* anticlockwise = NULL; 16 | Button* button = NULL; 17 | 18 | RotaryEncoderI2C(); 19 | RotaryEncoderI2C(uint8_t i2cAddress); 20 | RotaryEncoderI2C(uint8_t i2cAddressdress, TwoWire* wire); 21 | 22 | ~RotaryEncoderI2C(); 23 | 24 | void setup(uint8_t i2cAddress); 25 | void setup(uint8_t i2cAddress, TwoWire* wire); 26 | 27 | bool update(); 28 | 29 | void begin(); 30 | void reset(); 31 | 32 | bool connected(); 33 | String getError(); 34 | 35 | void setConfig(uint8_t config); 36 | 37 | void enableInterrupt(uint8_t pin, bool pullup); 38 | void enableLed(bool led); 39 | void enableLoop(bool loop); 40 | void setEncoding(uint8_t encoding); 41 | void setInverted(bool inverted); 42 | 43 | bool interrupt(); 44 | 45 | void setPos(int32_t value); 46 | void setMin(int32_t value); 47 | void setMax(int32_t value); 48 | void setLed(uint8_t valueA, uint8_t valueB); 49 | void setLedA(uint8_t value); 50 | void setLedB(uint8_t value); 51 | 52 | int32_t getPos(); 53 | 54 | uint8_t readStatus(); 55 | uint8_t readLedA(); 56 | uint8_t readLedB(); 57 | int32_t readMax(); 58 | int32_t readMin(); 59 | 60 | bool clicked(); 61 | bool incremented(); 62 | bool decremented(); 63 | bool minVal(); 64 | bool maxVal(); 65 | 66 | private: 67 | // temp variables 68 | uint8_t status = 0x00; 69 | uint8_t error = 0; 70 | 71 | // i2c stuff 72 | uint8_t i2cAddress = 0x00; 73 | TwoWire* wire = NULL; 74 | 75 | // config 76 | uint8_t interruptPin = 0; 77 | bool interruptEnable = false; // INTE 78 | bool interruptPullup = true; 79 | bool ledEnabled = false; // LEDE 80 | bool encoding = false; // x1 = false, x2 = true 81 | bool loop = false; // WRAPE 82 | bool inverted = false; // DIRE 83 | 84 | // internal functions 85 | void write(uint8_t address, uint8_t value); 86 | void write(uint8_t address, int32_t value); 87 | 88 | uint8_t read(uint8_t address); 89 | int32_t read32(uint8_t address); 90 | }; 91 | } 92 | #endif // ifndef SimpleButton_RotaryEncoderI2C_h -------------------------------------------------------------------------------- /src/Buttons/Switch.cpp: -------------------------------------------------------------------------------- 1 | #include "Switch.h" 2 | 3 | namespace simplebutton { 4 | Switch::Switch() { 5 | button = new Button(); 6 | } 7 | 8 | Switch::Switch(uint8_t pin) { 9 | setup(pin); 10 | } 11 | 12 | Switch::Switch(GPIOExpander* pcf, uint8_t pin) { 13 | setup(pcf, pin); 14 | } 15 | 16 | Switch::Switch(Button* button) { 17 | setup(button); 18 | } 19 | 20 | Switch::~Switch() { 21 | if (this->button) delete this->button; 22 | } 23 | 24 | void Switch::setup(uint8_t pin) { 25 | button = new Button(pin); 26 | tmpState = button->read(); 27 | } 28 | 29 | void Switch::setup(GPIOExpander* pcf, uint8_t pin) { 30 | button = new ButtonGPIOExpander(pcf, pin); 31 | tmpState = button->read(); 32 | } 33 | 34 | void Switch::setup(Button* button) { 35 | setButton(button); 36 | tmpState = button->read(); 37 | } 38 | 39 | void Switch::update() { 40 | update(button->read()); 41 | } 42 | 43 | void Switch::update(bool state) { 44 | bool prevState = tmpState; 45 | 46 | tmpState = state > 0; 47 | 48 | if (prevState != tmpState) button->click(); 49 | } 50 | 51 | void Switch::setButton(Button* button) { 52 | if (this->button) delete this->button; 53 | this->button = button ? button : new Button(); 54 | } 55 | 56 | bool Switch::getState() { 57 | return tmpState; 58 | } 59 | 60 | bool Switch::clicked() { 61 | return button->clicked(); 62 | } 63 | } -------------------------------------------------------------------------------- /src/Buttons/Switch.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_Switch_h 2 | #define SimpleButton_Switch_h 3 | 4 | #include "Button.h" 5 | #include "ButtonGPIOExpander.h" 6 | 7 | namespace simplebutton { 8 | class Switch { 9 | public: 10 | Button* button = NULL; 11 | 12 | Switch(); 13 | Switch(uint8_t pin); 14 | Switch(GPIOExpander* pcf, uint8_t pin); 15 | Switch(Button* button); 16 | 17 | ~Switch(); 18 | 19 | void setup(uint8_t pin); 20 | void setup(GPIOExpander* pcf, uint8_t pin); 21 | void setup(Button* button); 22 | 23 | void update(); 24 | void update(bool state); 25 | 26 | void setButton(Button* button); 27 | 28 | bool getState(); 29 | bool clicked(); 30 | 31 | private: 32 | bool tmpState = false; 33 | }; 34 | } 35 | 36 | #endif // ifndef SimpleButton_Switch_h -------------------------------------------------------------------------------- /src/Events/ClickEvent.cpp: -------------------------------------------------------------------------------- 1 | #include "ClickEvent.h" 2 | 3 | namespace simplebutton { 4 | ClickEvent::ClickEvent(ButtonEventFunction, uint32_t minPushTime, uint32_t minReleaseTime) { 5 | this->fnct = fnct; 6 | this->minPushTime = minPushTime; 7 | this->minReleaseTime = minReleaseTime; 8 | } 9 | 10 | ClickEvent::~ClickEvent() { 11 | if (next) { 12 | delete next; 13 | next = NULL; 14 | } 15 | } 16 | 17 | uint8_t ClickEvent::getMode() { 18 | return MODE::CLICKED; 19 | } 20 | 21 | uint32_t ClickEvent::getMinPushTime() { 22 | return minPushTime; 23 | } 24 | 25 | uint32_t ClickEvent::getMinReleaseTime() { 26 | return minReleaseTime; 27 | } 28 | } -------------------------------------------------------------------------------- /src/Events/ClickEvent.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_ClickEvent_h 2 | #define SimpleButton_ClickEvent_h 3 | 4 | #include "Event.h" 5 | 6 | namespace simplebutton { 7 | class ClickEvent : public Event { 8 | public: 9 | ClickEvent(ButtonEventFunction, uint32_t minPushTime, uint32_t minReleaseTime); 10 | ~ClickEvent(); 11 | 12 | uint8_t getMode(); 13 | uint32_t getMinPushTime(); 14 | uint32_t getMinReleaseTime(); 15 | 16 | private: 17 | uint32_t minPushTime = 0; 18 | uint32_t minReleaseTime = 0; 19 | }; 20 | } 21 | 22 | #endif // ifndef SimpleButton_ClickEvent_h -------------------------------------------------------------------------------- /src/Events/DoubleclickEvent.cpp: -------------------------------------------------------------------------------- 1 | #include "DoubleclickEvent.h" 2 | 3 | namespace simplebutton { 4 | DoubleclickEvent::DoubleclickEvent(ButtonEventFunction, uint32_t minPushTime, uint32_t minReleaseTime, 5 | uint32_t timeSpan) { 6 | this->fnct = fnct; 7 | this->minPushTime = minPushTime; 8 | this->minReleaseTime = minReleaseTime; 9 | this->timeSpan = timeSpan; 10 | } 11 | 12 | DoubleclickEvent::~DoubleclickEvent() { 13 | if (next) { 14 | delete next; 15 | next = NULL; 16 | } 17 | } 18 | 19 | uint8_t DoubleclickEvent::getMode() { 20 | return MODE::DOUBLECLICKED; 21 | } 22 | 23 | uint32_t DoubleclickEvent::getMinPushTime() { 24 | return minPushTime; 25 | } 26 | 27 | uint32_t DoubleclickEvent::getMinReleaseTime() { 28 | return minReleaseTime; 29 | } 30 | 31 | uint32_t DoubleclickEvent::getTimeSpan() { 32 | return timeSpan; 33 | } 34 | } -------------------------------------------------------------------------------- /src/Events/DoubleclickEvent.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_DoubleclickEvent_h 2 | #define SimpleButton_DoubleclickEvent_h 3 | 4 | #include "Event.h" 5 | 6 | namespace simplebutton { 7 | class DoubleclickEvent : public Event { 8 | public: 9 | DoubleclickEvent(ButtonEventFunction, uint32_t minPushTime, uint32_t minReleaseTime, uint32_t timeSpan); 10 | ~DoubleclickEvent(); 11 | 12 | uint8_t getMode(); 13 | uint32_t getMinPushTime(); 14 | uint32_t getMinReleaseTime(); 15 | uint32_t getTimeSpan(); 16 | 17 | private: 18 | uint32_t minPushTime = 0; 19 | uint32_t minReleaseTime = 0; 20 | uint32_t timeSpan = 0; 21 | }; 22 | } 23 | 24 | #endif // ifndef SimpleButton_DoubleclickEvent_h -------------------------------------------------------------------------------- /src/Events/Event.cpp: -------------------------------------------------------------------------------- 1 | #include "Event.h" 2 | 3 | namespace simplebutton { 4 | Event::~Event() { 5 | if (next) { 6 | delete next; 7 | next = NULL; 8 | } 9 | } 10 | 11 | void Event::run() { 12 | if (fnct) fnct(); 13 | } 14 | 15 | uint8_t Event::getMode() { 16 | return MODE::NONE; 17 | } 18 | 19 | uint32_t Event::getMinPushTime() { 20 | return 0; 21 | } 22 | 23 | uint32_t Event::getMinReleaseTime() { 24 | return 0; 25 | } 26 | 27 | uint32_t Event::getTimeSpan() { 28 | return 0; 29 | } 30 | 31 | uint32_t Event::getInterval() { 32 | return 0; 33 | } 34 | } -------------------------------------------------------------------------------- /src/Events/Event.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_Event_h 2 | #define SimpleButton_Event_h 3 | 4 | #include "Arduino.h" 5 | #include 6 | 7 | #define ButtonEventFunction std::functionfnct 8 | 9 | namespace simplebutton { 10 | class Event { 11 | public: 12 | Event* next = NULL; 13 | enum MODE { NONE = 0, PUSHED = 1, RELEASED = 2, CLICKED = 3, DOUBLECLICKED = 4, HOLDING = 5 }; 14 | 15 | virtual ~Event(); 16 | 17 | virtual void run(); 18 | 19 | virtual uint8_t getMode(); 20 | virtual uint32_t getMinPushTime(); 21 | virtual uint32_t getMinReleaseTime(); 22 | virtual uint32_t getTimeSpan(); 23 | virtual uint32_t getInterval(); 24 | 25 | protected: 26 | ButtonEventFunction = NULL; 27 | }; 28 | } 29 | 30 | #endif // ifndef Event_h -------------------------------------------------------------------------------- /src/Events/HoldEvent.cpp: -------------------------------------------------------------------------------- 1 | #include "HoldEvent.h" 2 | 3 | namespace simplebutton { 4 | HoldEvent::HoldEvent(ButtonEventFunction, uint32_t interval) { 5 | this->fnct = fnct; 6 | this->interval = interval; 7 | } 8 | 9 | HoldEvent::~HoldEvent() { 10 | if (next) { 11 | delete next; 12 | next = NULL; 13 | } 14 | } 15 | 16 | uint8_t HoldEvent::getMode() { 17 | return MODE::HOLDING; 18 | } 19 | 20 | uint32_t HoldEvent::getInterval() { 21 | return interval; 22 | } 23 | } -------------------------------------------------------------------------------- /src/Events/HoldEvent.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_HoldEvent_h 2 | #define SimpleButton_HoldEvent_h 3 | 4 | #include "Event.h" 5 | namespace simplebutton { 6 | class HoldEvent : public Event { 7 | public: 8 | HoldEvent(ButtonEventFunction, uint32_t interval); 9 | ~HoldEvent(); 10 | 11 | uint8_t getMode(); 12 | uint32_t getInterval(); 13 | 14 | private: 15 | uint32_t interval = 0; 16 | }; 17 | } 18 | 19 | #endif // ifndef SimpleButton_HoldEvent_h -------------------------------------------------------------------------------- /src/Events/PushEvent.cpp: -------------------------------------------------------------------------------- 1 | #include "PushEvent.h" 2 | 3 | namespace simplebutton { 4 | PushEvent::PushEvent(ButtonEventFunction) { 5 | this->fnct = fnct; 6 | } 7 | 8 | PushEvent::~PushEvent() { 9 | if (next) { 10 | delete next; 11 | next = NULL; 12 | } 13 | } 14 | 15 | uint8_t PushEvent::getMode() { 16 | return MODE::PUSHED; 17 | } 18 | } -------------------------------------------------------------------------------- /src/Events/PushEvent.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_PushEvent_h 2 | #define SimpleButton_PushEvent_h 3 | 4 | #include "Event.h" 5 | 6 | namespace simplebutton { 7 | class PushEvent : public Event { 8 | public: 9 | PushEvent(ButtonEventFunction); 10 | ~PushEvent(); 11 | 12 | uint8_t getMode(); 13 | }; 14 | } 15 | 16 | #endif // ifndef SimpleButton_PushEvent_h -------------------------------------------------------------------------------- /src/Events/ReleaseEvent.cpp: -------------------------------------------------------------------------------- 1 | #include "ReleaseEvent.h" 2 | 3 | namespace simplebutton { 4 | ReleaseEvent::ReleaseEvent(ButtonEventFunction) { 5 | this->fnct = fnct; 6 | } 7 | 8 | ReleaseEvent::~ReleaseEvent() { 9 | if (next) { 10 | delete next; 11 | next = NULL; 12 | } 13 | } 14 | 15 | uint8_t ReleaseEvent::getMode() { 16 | return MODE::RELEASED; 17 | } 18 | } -------------------------------------------------------------------------------- /src/Events/ReleaseEvent.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_ReleaseEvent_h 2 | #define SimpleButton_ReleaseEvent_h 3 | 4 | #include "Event.h" 5 | 6 | namespace simplebutton { 7 | class ReleaseEvent : public Event { 8 | public: 9 | ReleaseEvent(ButtonEventFunction); 10 | ~ReleaseEvent(); 11 | 12 | uint8_t getMode(); 13 | }; 14 | } 15 | 16 | #endif // ifndef SimpleButton_ReleaseEvent_h -------------------------------------------------------------------------------- /src/SimpleButton.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_h 2 | #define SimpleButton_h 3 | 4 | #include "Buttons/Button.h" 5 | #include "Buttons/ButtonGPIOExpander.h" 6 | #include "Buttons/ButtonPullup.h" 7 | #include "Buttons/ButtonPullupGPIOExpander.h" 8 | #include "Buttons/ButtonAnalog.h" 9 | #include "Buttons/Switch.h" 10 | #include "Buttons/RotaryEncoder.h" 11 | #include "Buttons/RotaryEncoderI2C.h" 12 | #include "Buttons/AnalogStick.h" 13 | #include "Buttons/PS2Gamepad.h" 14 | 15 | #endif // ifndef SimpleButton_h 16 | -------------------------------------------------------------------------------- /src/libs/GPIOExpander.cpp: -------------------------------------------------------------------------------- 1 | #include "GPIOExpander.h" 2 | 3 | namespace simplebutton { 4 | void GPIOExpander::setup(uint8_t address) { 5 | this->wire = &Wire; 6 | this->address = address; 7 | write(0); 8 | } 9 | 10 | void GPIOExpander::setup(uint8_t address, TwoWire* wire) { 11 | this->wire = wire; 12 | this->address = address; 13 | write(0); 14 | } 15 | 16 | bool GPIOExpander::connected() { 17 | return error == 0; 18 | } 19 | 20 | String GPIOExpander::getError() { 21 | String msg; 22 | 23 | switch (error) { 24 | case 0: 25 | msg += String(F("OK")); 26 | break; 27 | 28 | case 1: 29 | msg += String(F("Data too long to fit in transmit buffer")); 30 | break; 31 | 32 | case 2: 33 | msg += String(F("Received NACK on transmit of address")); 34 | break; 35 | 36 | case 3: 37 | msg += String(F("Received NACK on transmit of data")); 38 | 39 | case 4: 40 | msg += String(F("Unknown transmission error")); 41 | break; 42 | 43 | case 5: 44 | msg += String(F("Pin error")); 45 | break; 46 | 47 | case 6: 48 | msg += String(F("I2C error")); 49 | break; 50 | } 51 | 52 | return msg; 53 | } 54 | } -------------------------------------------------------------------------------- /src/libs/GPIOExpander.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_GPIOExpander_h 2 | #define SimpleButton_GPIOExpander_h 3 | 4 | #include "Arduino.h" 5 | #include 6 | 7 | #define PCF_PIN_ERROR 5 8 | #define PCF_I2C_ERROR 6 9 | 10 | namespace simplebutton { 11 | class GPIOExpander { 12 | public: 13 | virtual ~GPIOExpander() = default; 14 | 15 | virtual void setup(uint8_t address); 16 | virtual void setup(uint8_t address, TwoWire* wire); 17 | 18 | virtual int read() = 0; 19 | virtual int read(uint8_t pin) = 0; 20 | 21 | virtual void write(int value) = 0; 22 | virtual void write(uint8_t pin, bool value) = 0; 23 | 24 | virtual void toggle() = 0; 25 | virtual void toggle(uint8_t pin) = 0; 26 | 27 | virtual bool connected(); 28 | virtual String getError(); 29 | 30 | protected: 31 | uint8_t error = 0; 32 | 33 | TwoWire* wire; 34 | uint8_t address; 35 | }; 36 | } 37 | #endif // ifndef SimpleButton_GPIOExpander_h -------------------------------------------------------------------------------- /src/libs/MCP23017.cpp: -------------------------------------------------------------------------------- 1 | #include "MCP23017.h" 2 | namespace simplebutton { 3 | MCP23017::MCP23017(uint8_t address) { 4 | setup(address); 5 | } 6 | 7 | MCP23017::MCP23017(uint8_t address, TwoWire* wire) { 8 | setup(address, wire); 9 | } 10 | 11 | MCP23017::~MCP23017() {} 12 | 13 | void MCP23017::setup(uint8_t address) { 14 | setup(address, &Wire); 15 | } 16 | 17 | void MCP23017::setup(uint8_t address, TwoWire* wire) { 18 | this->address = address; 19 | this->wire = wire; 20 | 21 | setIO(); 22 | setPullups(); 23 | } 24 | 25 | int MCP23017::read() { 26 | this->pinData = readRegister16(0x12); // 0x12 = GPIOA 27 | 28 | return this->pinData; 29 | } 30 | 31 | int MCP23017::read(uint8_t pin) { 32 | if (pin >= 16) { 33 | error = PCF_PIN_ERROR; 34 | return 0; 35 | } 36 | 37 | // make sure the pin is set to be an input 38 | if (getPinMode(pin) == OUTPUT) { 39 | bool pullup = getPinState(pin); 40 | setPinMode(pin, pullup ? INPUT_PULLUP : INPUT); 41 | } 42 | 43 | return (read() >> pin) & 0x1; 44 | } 45 | 46 | void MCP23017::write(int value) { 47 | // make sure all pins are set as outputs 48 | for (int i = 0; i < 16; i++) { 49 | bool output = (value >> i) & 0x1; 50 | if (output && (getPinMode(i) != OUTPUT)) setPinMode(i, OUTPUT); 51 | } 52 | 53 | this->pinData = value; 54 | 55 | writeRegister(0x12, value); // 0x12 = GPIOA 56 | } 57 | 58 | void MCP23017::write(uint8_t pin, bool value) { 59 | if (pin >= 16) { 60 | error = PCF_PIN_ERROR; 61 | return; 62 | } 63 | 64 | if (getPinState(pin) != value) toggle(pin); 65 | } 66 | 67 | void MCP23017::toggle() { 68 | pinData = ~pinData; 69 | write(pinData); 70 | } 71 | 72 | void MCP23017::toggle(uint8_t pin) { 73 | if (pin >= 16) { 74 | error = PCF_PIN_ERROR; 75 | return; 76 | } 77 | 78 | pinData ^= 1 << pin; 79 | write(pinData); 80 | } 81 | 82 | void MCP23017::setIO() { 83 | writeRegister(0x00, this->pinModes); // 0x00 = IODIRA register 84 | } 85 | 86 | void MCP23017::setPullups() { 87 | writeRegister(0x0C, this->pinPullups); // 0x0C = GPPUA register 88 | } 89 | 90 | void MCP23017::setPinMode(uint8_t pin, uint8_t mode) { 91 | if (pin >= 16) { 92 | error = PCF_PIN_ERROR; 93 | return; 94 | } 95 | 96 | if (getPinMode(pin) == mode) return; 97 | 98 | bool input = (mode == INPUT || mode == INPUT_PULLUP); 99 | bool pullup = (mode == INPUT_PULLUP); 100 | 101 | bitWrite(pinModes, pin, input); 102 | bitWrite(pinPullups, pin, pullup); 103 | 104 | setIO(); 105 | setPullups(); 106 | } 107 | 108 | uint8_t MCP23017::getPinMode(uint8_t pin) { 109 | if (pin >= 16) { 110 | error = PCF_PIN_ERROR; 111 | return 0; 112 | } 113 | 114 | bool input = (this->pinModes >> pin) & 0x1; 115 | bool pullup = (this->pinPullups >> pin) & 0x1; 116 | 117 | if (!input) return OUTPUT; 118 | 119 | if (pullup) return INPUT_PULLUP; 120 | 121 | return INPUT; 122 | } 123 | 124 | bool MCP23017::getPinState(uint8_t pin) { 125 | if (pin >= 16) { 126 | error = PCF_PIN_ERROR; 127 | return false; 128 | } 129 | 130 | return (pinData >> pin) & 0x1; 131 | } 132 | 133 | uint8_t MCP23017::readRegister8(uint8_t address) { 134 | wire->beginTransmission(this->address); 135 | wire->write(address); 136 | error = wire->endTransmission(); 137 | 138 | wire->requestFrom(this->address, (uint8_t)1); 139 | 140 | if (wire->available() == 1) { 141 | return wire->read(); 142 | } else { 143 | error = PCF_I2C_ERROR; 144 | return 0; 145 | } 146 | } 147 | 148 | uint16_t MCP23017::readRegister16(uint8_t address) { 149 | wire->beginTransmission(this->address); 150 | wire->write(address); 151 | error = wire->endTransmission(); 152 | 153 | wire->requestFrom(this->address, (uint8_t)2); 154 | 155 | if (wire->available() == 2) { 156 | uint16_t dataA = wire->read(); 157 | uint16_t dataB = wire->read(); 158 | 159 | return (dataB << 8) | dataA; 160 | } else { 161 | error = PCF_I2C_ERROR; 162 | return 0; 163 | } 164 | } 165 | 166 | void MCP23017::writeRegister(uint8_t address, uint16_t value) { 167 | wire->beginTransmission(this->address); 168 | wire->write(address); 169 | wire->write(value & 0xFF); 170 | wire->write(value >> 8); 171 | error = wire->endTransmission(); 172 | } 173 | } -------------------------------------------------------------------------------- /src/libs/MCP23017.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_MCP23017_h 2 | #define SimpleButton_MCP23017_h 3 | 4 | #include "GPIOExpander.h" 5 | 6 | namespace simplebutton { 7 | class MCP23017 : public GPIOExpander { 8 | public: 9 | MCP23017(uint8_t address); 10 | MCP23017(uint8_t address, TwoWire* wire); 11 | 12 | ~MCP23017(); 13 | 14 | void setup(uint8_t address); 15 | void setup(uint8_t address, TwoWire* wire); 16 | 17 | int read(); 18 | int read(uint8_t pin); 19 | 20 | void write(int value); 21 | void write(uint8_t pin, bool value); 22 | 23 | void toggle(); 24 | void toggle(uint8_t pin); 25 | 26 | private: 27 | uint16_t pinData = 0x0000; 28 | uint16_t pinModes = 0x0000; 29 | uint16_t pinPullups = 0x0000; 30 | 31 | void setIO(); 32 | void setPullups(); 33 | 34 | void setPinMode(uint8_t pin, uint8_t mode); 35 | uint8_t getPinMode(uint8_t pin); 36 | 37 | bool getPinState(uint8_t pin); 38 | 39 | uint8_t readRegister8(uint8_t address); 40 | uint16_t readRegister16(uint8_t address); 41 | 42 | void writeRegister(uint8_t address, uint16_t value); 43 | }; 44 | } 45 | 46 | #endif // ifndef SimpleButton_MCP23017_h -------------------------------------------------------------------------------- /src/libs/PCF8574.cpp: -------------------------------------------------------------------------------- 1 | #include "PCF8574.h" 2 | 3 | namespace simplebutton { 4 | PCF8574::PCF8574(uint8_t address) { 5 | setup(address); 6 | } 7 | 8 | PCF8574::PCF8574(uint8_t address, TwoWire* wire) { 9 | setup(address, wire); 10 | } 11 | 12 | PCF8574::~PCF8574() {} 13 | 14 | int PCF8574::read() { 15 | wire->requestFrom(address, (uint8_t)1); 16 | 17 | data = 0; 18 | 19 | if (wire->available() >= 1) { 20 | data = wire->read(); 21 | } else { 22 | error = PCF_I2C_ERROR; 23 | } 24 | 25 | return data; 26 | } 27 | 28 | int PCF8574::read(uint8_t pin) { 29 | if (pin < 8) { 30 | data = read(); 31 | return (data & (1 << pin)) > 0; 32 | } else { 33 | error = PCF_PIN_ERROR; 34 | return -1; 35 | } 36 | } 37 | 38 | void PCF8574::write(int value) { 39 | wire->beginTransmission(address); 40 | 41 | pinModeMask &= 0xff00; 42 | pinModeMask |= value; 43 | data = pinModeMask; 44 | 45 | wire->write(data); 46 | 47 | error = wire->endTransmission(); 48 | } 49 | 50 | void PCF8574::write(uint8_t pin, bool value) { 51 | if (pin >= 8) { 52 | error = PCF_PIN_ERROR; 53 | return; 54 | } 55 | 56 | if (value) pinModeMask |= value << pin; 57 | else pinModeMask &= ~(1 << pin); 58 | 59 | write(pinModeMask); 60 | } 61 | 62 | void PCF8574::toggle() { 63 | pinModeMask = ~pinModeMask; 64 | write(pinModeMask); 65 | } 66 | 67 | void PCF8574::toggle(uint8_t pin) { 68 | if (pin < 8) { 69 | pinModeMask ^= 1 << pin; 70 | 71 | write(pinModeMask); 72 | } else { 73 | error = PCF_PIN_ERROR; 74 | } 75 | } 76 | } -------------------------------------------------------------------------------- /src/libs/PCF8574.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_PCF8574_h 2 | #define SimpleButton_PCF8574_h 3 | 4 | #include "GPIOExpander.h" 5 | 6 | namespace simplebutton { 7 | class PCF8574 : public GPIOExpander { 8 | public: 9 | PCF8574(uint8_t address); 10 | PCF8574(uint8_t address, TwoWire* wire); 11 | ~PCF8574(); 12 | 13 | int read(); 14 | int read(uint8_t pin); 15 | 16 | void write(int value); 17 | void write(uint8_t pin, bool value); 18 | 19 | void toggle(); 20 | void toggle(uint8_t pin); 21 | 22 | private: 23 | uint8_t data; 24 | uint8_t pinModeMask; 25 | }; 26 | } 27 | 28 | #endif // ifndef SimpleButton_PCF8574_h -------------------------------------------------------------------------------- /src/libs/PCF8575.cpp: -------------------------------------------------------------------------------- 1 | #include "PCF8575.h" 2 | 3 | namespace simplebutton { 4 | PCF8575::PCF8575(uint8_t address) { 5 | setup(address); 6 | } 7 | 8 | PCF8575::PCF8575(uint8_t address, TwoWire* wire) { 9 | setup(address, wire); 10 | } 11 | 12 | PCF8575::~PCF8575() {} 13 | 14 | int PCF8575::read() { 15 | wire->requestFrom(address, (uint8_t)2); 16 | 17 | data = 0; 18 | 19 | if (wire->available() >= 2) { 20 | data = wire->read(); 21 | data |= wire->read() << 8; 22 | } 23 | 24 | return data; 25 | } 26 | 27 | int PCF8575::read(uint8_t pin) { 28 | data = read(); 29 | 30 | return (data & (1 << pin)) > 0; 31 | } 32 | 33 | void PCF8575::write(int value) { 34 | wire->beginTransmission(address); 35 | 36 | pinModeMask = value; 37 | data = pinModeMask; 38 | 39 | wire->write((uint8_t)data); 40 | wire->write((uint8_t)(data >> 8)); 41 | 42 | wire->endTransmission(); 43 | } 44 | 45 | void PCF8575::write(uint8_t pin, bool value) { 46 | if (value) pinModeMask |= value << pin; 47 | else pinModeMask &= ~(1 << pin); 48 | 49 | write(pinModeMask); 50 | } 51 | 52 | void PCF8575::toggle() { 53 | pinModeMask = ~pinModeMask; 54 | write(pinModeMask); 55 | } 56 | 57 | void PCF8575::toggle(uint8_t pin) { 58 | pinModeMask ^= 1 << pin; 59 | 60 | write(pinModeMask); 61 | } 62 | } -------------------------------------------------------------------------------- /src/libs/PCF8575.h: -------------------------------------------------------------------------------- 1 | #ifndef SimpleButton_PCF8575_h 2 | #define SimpleButton_PCF8575_h 3 | 4 | #include "GPIOExpander.h" 5 | 6 | namespace simplebutton { 7 | class PCF8575 : public GPIOExpander { 8 | public: 9 | PCF8575(uint8_t address); 10 | PCF8575(uint8_t address, TwoWire* wire); 11 | 12 | ~PCF8575(); 13 | 14 | int read(); 15 | int read(uint8_t pin); 16 | 17 | void write(int value); 18 | void write(uint8_t pin, bool value); 19 | 20 | void toggle(); 21 | void toggle(uint8_t pin); 22 | 23 | private: 24 | uint16_t data; 25 | uint16_t pinModeMask; 26 | }; 27 | } 28 | #endif // ifndef SimpleButton_PCF8575_h --------------------------------------------------------------------------------