Sunday, June 19, 2016

Teensy 2.0 buttons/control using the Bounce2 library.

I am currently working on a raspberry pi 3/gamegear build (Which I plan to start blogging about in the near future hopefully). Well I was in the process of programming my teensy 2.0 the other night, and configuring my buttons. During testing I had noticed that on certain buttons, when I pressed them, I was experiencing multiple unwanted button presses. At first I thought it was something to do with my code, which was based off of some sketch I had found on the internet.

Turns out it was being caused by what is commonly referred to as "contact bounce". This is where the button or switch is literally bouncing on the pad/contacts, each time you press or release it. I did some Googling and I came across a little Arduino/Wiring library known as Bounce2 that handles this problem.

I was able to whip up a little sketch that works very well.  Both eliminating bounce, and giving me access to a nice little easy to manage piece of code for all my future handheld projects. It's based on a 12 button setup. You will have to number your pins accordingly, depending on which ones you've wired your buttons to on the teensy.

Enjoy!

/*
Description:
A 12 button input control sketch for the teensy 2.0,
using the Bounce2 library. This sketch eliminates (debounces)
side effects caused by mechanical bounce, such as rapid
fire, multiple undesired button presses etc. It also detects
when the button is being held down, and if so retriggers it.

Written by: Steve Sherrod
Date: 06/19/16
URL: http://retro-rpi.blogspot.com/

*/

// Include the Bounce2 library found here :
// https://github.com/thomasfredericks/Bounce2
#include <Bounce2.h>

#define DEBUG 1            //enable debugging/serial output
#define INTERVAL 5         //button update interval
#define RETRIG_RATE 100    //ms until button is considered being held

//buttons current state and timestamp
int buttonState;
unsigned long buttonPressTimeStamp;

//pin number placeholders
const int pinBtnUp = 10;
const int pinBtnRight = 8;
const int pinBtnDown = 23;
const int pinBtnLeft = 9;

const int pinBtnSelect = 6;
const int pinBtnStart = 4;

const int pinBtnB = 2;
const int pinBtnA = 0;
const int pinBtnY = 1;
const int pinBtnX = 3;

const int pinBtnTrigLeft = 5;
const int pinBtnTrigRight = 7;

const int pinLEDOutput = 11;

//array of pin numbers
byte pins[] = {  pinBtnUp, pinBtnRight, pinBtnDown, pinBtnLeft, pinBtnSelect, pinBtnStart,
                    pinBtnB, pinBtnA, pinBtnY, pinBtnX, pinBtnTrigLeft, pinBtnTrigRight
                 };

//bounce object placeholders
Bounce btnUp = Bounce();
Bounce btnRight = Bounce();
Bounce btnDown = Bounce();
Bounce btnLeft = Bounce();
Bounce btnSelect = Bounce();
Bounce btnStart = Bounce();
Bounce btnB = Bounce();
Bounce btnA = Bounce();
Bounce btnY = Bounce();
Bounce btnX = Bounce();
Bounce btnTrigLeft = Bounce();
Bounce btnTrigRight = Bounce();

//array of Bounce objects
Bounce buttons[] = {btnUp, btnRight, btnDown, btnLeft, btnSelect, btnStart, btnB, btnA, btnY,
 btnX, btnTrigLeft, btnTrigRight};

//array of key presses to be sent
short keys[] = {KEY_U, KEY_R, KEY_D, KEY_L, KEY_ENTER, KEY_TAB, KEY_B, KEY_A, KEY_Y, KEY_X, KEY_P, KEY_Q};

void myset_key1(uint8_t c){Keyboard.set_key1(c);}
void myset_key2(uint8_t c){Keyboard.set_key2(c);}
void myset_key3(uint8_t c){Keyboard.set_key3(c);}
void myset_key4(uint8_t c){Keyboard.set_key4(c);}
void myset_key5(uint8_t c){Keyboard.set_key5(c);}
void myset_key6(uint8_t c){Keyboard.set_key6(c);}

//sendkey function pointers
typedef void KeyFunction_t(uint8_t c);

KeyFunction_t* buttonActive[12];
KeyFunction_t* keyList[] = {myset_key6, myset_key5, myset_key4, myset_key3, myset_key2, myset_key1};
int            keySlot = sizeof(keyList) / sizeof(KeyFunction_t*);

//setup the buttons
void setup() {
  if (DEBUG)
    Serial.begin(57600);
  
  //set pin mode to input on all pins:
  for (byte i=0; i< 12; i++) {
    pinMode(pins[i], INPUT);
    digitalWrite(pins[i],HIGH);
  
    if (DEBUG)
         Serial.printf("Pin %d -> input HIGH\n", pins[i]);
       
    //setup our bounce instances
    buttons[i].attach(pins[i]);   //attach to corresponding pin
    buttons[i].interval(INTERVAL);
  }

  //setup the LED pin
  pinMode(pinLEDOutput,OUTPUT);

}

void loop() {

  for (byte i=0; i< 12; i++) {

  //update buttons
  boolean changed = buttons[i].update();

  //last action
  bool keysPressed = false;
  bool keysReleased = false;

     //button state changed
     if ( changed ) {
    
        //read the update value
        int value = buttons[i].read();
        if ( value == HIGH) {
        
           //button released
           keysReleased = true;
           buttonState = 0;
           releaseButton(i);

           if (DEBUG)
              Serial.println("Button released");
     
       } else {
      
             //button pressed
             buttonState = 1;
             keysPressed = true;
             activateButton(i);
           
             if (DEBUG)
                Serial.println("Button pressed");
             buttonPressTimeStamp = millis();
          }
      }

      //check for long button press and if so retrigger the button
      if  ( buttonState == 1 ) {
        if ( millis() - buttonPressTimeStamp >= RETRIG_RATE ) {
             buttonPressTimeStamp = millis();
           
             if (DEBUG)
                Serial.println("Button held, retriggering");
        }
      }
    
        if (keysPressed || keysReleased)
          Keyboard.send_now();            //update all the keypresses
  }
}

void activateButton(byte index)
{
  if (keySlot)      //any key slots left?
  {
    keySlot--;                                //Push the keySlot stack
    buttonActive[index] = keyList[keySlot];   //Associate the keySlot function pointer with the button
    (*keyList[keySlot])(keys[index]);         //Call the key slot function to set the key value
  }
}

void releaseButton(byte index)
{
  keyList[keySlot] = buttonActive[index];    //retrieve the keySlot function pointer
  buttonActive[index] = 0;                   //mark the button as no longer pressed
  (*keyList[keySlot])(0);                    //release the key slot
  keySlot++;                                 //pop the keySlot stack
}
References:

https://en.wikipedia.org/wiki/Contact_bounce
http://www.allaboutcircuits.com/textbook/digital/chpt-4/contact-bounce/
https://github.com/thomasfredericks/Bounce2/wiki
https://www.pjrc.com/teensy/td_libs_Bounce.html
https://www.pjrc.com/teensy/pinout.html
https://learn.adafruit.com/raspberry-gear/wiring-front-case