2

I have a simple set of 8 push buttons wired to a Teensy 3.2 board (which uses Arduino via Teensyduino plugin). The 8 buttons are on pins 1-8 and their common ground line (one line soldered to each of them) is on the GND pin. I have code to get any one button working that works. It's currently set to make pressing the second button type "A."

I'd like to make push buttons 1 through 8 type out A,B,C...etc respectively as you press them. I've been told of two problems with my setup, the first being that copying the code for every button is a bad way to go about it and second that it's subject to bouncing issues (creates a keystroke once every 5 presses or so.) I'd also like to set this up so in the future I can write a 3rd party app that configures the keys based on the user's preference.

Adding those needs up I'm not sure where to go next. I'm beginner enough that I'm not sure how to properly incorporate the bounce class or if that's the right way to go given the needs.

Is a matrix the way to go or is there an elegant way to manual set each button and compensate for bounce? I'd really appreciate a small example if anyone has done this. Thanks~ Current code follows:

#define CHECK_EVERY_MS 20
#define MIN_STABLE_VALS 5

unsigned long previousMillis;
char stableVals;
char buttonPressed;

void setup() {
  // make pin 2 an input and turn on the 
  // pullup resistor so it goes high unless
  // connected to ground:
  pinMode(2, INPUT_PULLUP);
  Keyboard.begin();
}

void  loop() {
    if ((millis() - previousMillis) > CHECK_EVERY_MS)
    {
        previousMillis += CHECK_EVERY_MS;
        if (digitalRead(2) != buttonPressed)
        {
            stableVals++;
            if (stableVals >= MIN_STABLE_VALS)
            {
                buttonPressed = !buttonPressed;
                stableVals = 0;

                if (buttonPressed)
                {
                    //Send an ASCII 'A', 
                    Keyboard.write(65);
                }
            }
        }
        else
            stableVals = 0;
    }
}

------- Edit: WORKING CODE FOLLOWS

/*  jw - 3 Nov 2015 - Program for button debounce demo
Ref: http://arduino.stackexchange.com/questions/17453/arduino-sending-keystrokes-via-push-buttons-proper-bouncing-and-manually-settin
*/

//--------------------------------------------------------
enum { sw0=3, sw1=4, sw2=5, sw3=6, sw4=7, sw5=8, sw6=9, sw7=10}; // Switchbutton lines
enum { nSwitches=8, bounceMillis=42}; // # of switches; debounce delay
struct ButtonStruct {
  unsigned long int bounceEnd;  // Debouncing-flag and end-time
  // Switch number, press-action, release-action, and prev reading
  byte swiNum, swiActP, swiActR, swiPrev;
};

struct ButtonStruct buttons[nSwitches] = {
  {0, sw0, 'A'}, 
  {0, sw1, 'B'}, 
  {0, sw2, 'C'}, 
  {0, sw3, 'D'},
  {0, sw4, 'E'},
  {0, sw5, 'F'},
  {0, sw6, 'G'},
  {0, sw7, 'H'}};
//--------------------------------------------------------
void setup() {
  for (int i=0; i<nSwitches; ++i)
    pinMode(buttons[i].swiNum, INPUT_PULLUP);
Keyboard.begin();
}
//--------------------------------------------------------
byte readSwitch (byte swiNum) {
  // Following inverts the pin reading (assumes pulldown = pressed)
  return 1 - digitalRead(swiNum);
}
//--------------------------------------------------------
void doAction(byte swin, char code, char action) {
  Keyboard.write(action);
}
//--------------------------------------------------------
void doButton(byte bn) {
  struct ButtonStruct *b = buttons + bn;
  if (b->bounceEnd) { // Was button changing?
    // It was changing, see if debounce time is done.
      if (b->bounceEnd < millis()) {
    b->bounceEnd = 0;    // Debounce time is done, so clear flag
    // See if the change was real, or a glitch
    if (readSwitch(b->swiNum) == b->swiPrev) {
      // Current reading is same as trigger reading, so do it
      if (b->swiPrev) {
        doAction(b->swiNum, 'P', b->swiActP);
      } else {
        doAction(b->swiNum, 'R', b->swiActR);
      }
    }
      }
  } else {  // It wasn't changing; but see if it's changing now
    if (b->swiPrev != readSwitch(b->swiNum)) {
      b->swiPrev = readSwitch(b->swiNum);
      b->bounceEnd = millis()+bounceMillis; // Set the Debounce flag
    }
  }
}
//--------------------------------------------------------
long int seconds, prevSec=0;
void loop() {
  for (int i=0; i<nSwitches; ++i)
    doButton(i);
}
2
  • You already have it working with one button. Just replicate it for all the others. This could be a good time to learn about arrays and loops.
    – Majenko
    Nov 3, 2015 at 23:25
  • Yes but it suffers from only sending a keystroke once ever 5-10 clicks, one of the things I'm trying to learn to fix. The other is how to loop this correctly, as I can't find a tutorial online~
    – sylcat
    Nov 4, 2015 at 2:21

1 Answer 1

1

I'm not sure what you mean by “Is a matrix the way to go”, so will ignore that question.

Shown below is a way of using an array of structs (one of C's ways of organizing a collection of items) to keep data for several buttons, such that it's straightforward to use one subroutine to process each button the same way, using a for-loop to call the routine for each button.

Each element of the array buttons is a ButtonStruct, and each ButtonStruct contains five items: bounceEnd, swiNum, swiActP, swiActR, swiPrev, which respectively represent a debouncing-flag and end-time; a switch number [ie a pin number]; a code for action to take on a press; a code for action to take on a release; and previous state of button. (In many special cases, some of these items will be irrelevant and you can leave them out of the ButtonStruct.) The form b->x that is used in the doButton() routine says to access element x of the struct that b points to.

At the start of each loop() pass, doButton() is called once for each button. It first tests if debouncing was underway for the button. If so, it sees if enough time has gone by to suppose button bounce is over. If so, it reads the button's state, and if different from previous state, does an action for whichever way the button changed. (If the button state is unchanged after a debounce delay, the program supposes there was a glitch.) If debouncing was not underway, the program checks if the button state is different from most recent pass. In that case, it sets up a debounce test for the button.

The buttons are tested and debounced individually, as you can see in the sample output that is shown following the program. (This program was tested on an Uno, with four independent buttons used in the test. Edit: Made corrections to handle 32-bit overflow, 2016.10.17)

/*  jw - 3 Nov 2015 - Program for button debounce demo
Ref: http://arduino.stackexchange.com/questions/17453/arduino-sending-keystrokes-via-push-buttons-proper-bouncing-and-manually-settin
*/

//--------------------------------------------------------
enum { sw0=8, sw1=9, sw2=10, sw3=11}; // Switchbutton lines
enum { nSwitches=4, bounceMillis=42}; // # of switches; debounce delay
struct ButtonStruct {
  unsigned long int bounceGo;   // Debouncing-flag and end-time
  // Switch number, press-action, release-action, and prev reading
  byte swiNum, swiActP, swiActR, swiPrev;
};

struct ButtonStruct buttons[nSwitches] = {
  {0, sw0, 'A','a', 0}, {0, sw1, 'B','b', 0}, {0, sw2, 'C','c', 0}, {0, sw3, 'D','d', 0}};
//--------------------------------------------------------
void setup() {
  for (int i=0; i<nSwitches; ++i)
    pinMode(buttons[i].swiNum, INPUT_PULLUP);
  Serial.begin(115200);
  Serial.println("Starting");
}
//--------------------------------------------------------
byte readSwitch (byte swiNum) {
  // Following inverts the pin reading (assumes pulldown = pressed)
  return 1 - digitalRead(swiNum);
}
//--------------------------------------------------------
void doAction(byte swin, char code, char action) {
  Serial.print("Switch ");
  Serial.print(swin);
  Serial.print(code);
  Serial.print(' ');
  Serial.print(action);
  Serial.print(" at t=");
  Serial.print(millis());
  Serial.println();
}
//--------------------------------------------------------
void doButton(byte bn) {
  struct ButtonStruct *b = buttons + bn;
  if (b->bounceGo) { // Was button changing?
    // It was changing, see if debounce time is done.
      if (millis() - b->bounceGo > bounceMillis) {
    b->bounceGo = 0;    // Debounce time is done, so clear flag
    // See if the change was real, or a glitch
    if (readSwitch(b->swiNum) == b->swiPrev) {
      // Current reading is same as trigger reading, so do it
      if (b->swiPrev) {
        doAction(b->swiNum, 'P', b->swiActP);
      } else {
        doAction(b->swiNum, 'R', b->swiActR);
      }
    }
      }
  } else {  // It wasn't changing; but see if it's changing now
    if (b->swiPrev != readSwitch(b->swiNum)) {
      b->swiPrev = readSwitch(b->swiNum);
      b->bounceGo = millis();   // Set the Debounce flag
    }
  }
}
//--------------------------------------------------------
long int seconds, prevSec=0;
void loop() {
  for (int i=0; i<nSwitches; ++i)
    doButton(i);
}

In the sample output below, a P line indicates a button press, detected at given millis() time, and an R line indicates a button release. Observe, near the end, overlaps among separate button presses and releases.

Starting
Switch 8P A at t=2170
Switch 8R a at t=2322
Switch 9P B at t=2789
Switch 9R b at t=2969
Switch 10P C at t=3395
Switch 10R c at t=3562
Switch 11P D at t=4087
Switch 11R d at t=4260
Switch 11P D at t=6518
Switch 10P C at t=6961
Switch 8P A at t=7070
Switch 10R c at t=7112
Switch 11R d at t=7427
Switch 8R a at t=7598
Switch 10P C at t=8502
Switch 9P B at t=8642
Switch 10R c at t=8961
Switch 9R b at t=9054
Switch 9P B at t=9954
Switch 11P D at t=10073
Switch 8P A at t=10164
Switch 8R a at t=10215
Switch 9R b at t=10315
Switch 11R d at t=10354
10
  • This is great information...I'm just confused as using it exactly as shown displays nothing when I press any of my buttons...
    – sylcat
    Nov 4, 2015 at 18:13
  • For the moment, adjust the pin numbers in the enum { sw0=8, sw1=9, sw2=10, sw3=11} line, eg change to sw0=3, sw1=4, sw2=5, sw3=6 to test the buttons on pins 3, 4, 5, 6. I don't have a teensy. For the longer run, if your buttons are on "pins 1-8" make 8 entries in buttons with the swiNum values from 1 to 8, and set nSwitch to 8. Note, since pin 1 is TX1, having a button on it may conflict with Serial. Nov 4, 2015 at 19:11
  • Aye I've set to try different pins even to get 4 work and nothing shows. I attached some other switches I know work in case and still nothing. I'm generally keeping the buttons' common wire on gnd but I've tried changing that around too with no avail
    – sylcat
    Nov 4, 2015 at 19:26
  • If it helps to see, here's a pic of my current simple wiring to test buttons on 3,4,5,6: tinyurl.com/px6rezv and here's the button set I'll eventually use (all have been tested working individually with my first bit of code) tinyurl.com/oajbpck
    – sylcat
    Nov 4, 2015 at 19:44
  • I should have mentioned I'm testing in notepad, not console. An update, I swapped all the serial commands for a simple Keyboard.write(action); which now prints Aa, Bb, etc... I think I follow this now, standby~
    – sylcat
    Nov 4, 2015 at 20:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.