This is the 8 button sketch that can be made into 16 buttons by using 8 more pins.
It is easier to wire than the 4x4 multiplex. This one, you just connect the button to a pin and then to ground.
Tested on an UNO, you have plenty more buttons on the Mega but let me know if you want multiplexed buttons.
And yeah, I'm going to have to rip out some, maybe many comments to fit it in one post.
You may or may not need to tweak the debounce time if you have really dirty buttons.
Right now the button state must be stable for 20ms to be considered debounced.
// All you need to do this example is an Arduino and jumpers in pins 4 to 11.
// Push and let go the button on pin 11 once, led blinks. Again, led turns off.
// Push and let go the button on pin 4 once, led blink slows down.
// Push and let go the button on pin 5 once, led blink speeds up.
// Push or let go of any button and a status line will show it on Serial Monitor.
// Buttons are debounced and wired directly to ground or, the buttons can
// be jumpers from the pins to grounded on USB connector housing.
// This sketch is tested with jumpers.
// By GoForSmoke for free public use. Using Arduino 1.0.5-r2
const byte ledPin = 13; // this is the onboard led pin
byte ledPinState = 0;
byte blinkLed = 1; // start with led blinking
word ledBlinkMs = 1000U; // blink interval
word ledBlinkNowMs = 0U; // low 16 bits of millis()
word ledBlinkStartMs = 0U; // 16 bit blink (on or off) time ms
// button variables button variables button variables button variables
byte currentButton = 0;
const byte BUTTONS = 8;
__attribute__ ((__packed__)) enum buttonNames
{
fSlowDown = 0, fSpeedUp, fButton2, fButton3, fButton4, fButton5, fButton6, fBlinkToggle
};
const byte buttonPin[ BUTTONS ] =
{
4, 5, 6, 7, 8, 9, 10, 11
};
__attribute__ ((__packed__)) enum buttonBitName
{
bCurrentRead = 0, bLastRead, bCurrentState, bLastState, bCheckDebounce
};
// buttonBit array, using 8 bytes to do what 40 bytes could instead
byte buttonBits[ BUTTONS ] = // current and last read in bit 0 and 1
{ // current and last state in bit 2 and 3
2, 2, 2, 2, 2, 2, 2, 2 // start with all lastRead states HIGH, all others LOW
};
const byte buttonReadBits = 3; // bit mask for the read bitss
const byte buttonStateBits = 12; // bit mask for the state bits
// button debounce timing variables
const word debounceMs = 20; // debounced when reads for debounceMs ms are the same
word debounceMillisLowByte[ BUTTONS ];
word msNow = 0;
byte blinkProcessState = 0; // the button gets pushed and released twice
void bar()
{
Serial.println( F( "============================================================" ));
}
void printMillis()
{
Serial.print( F( " time " ));
Serial.println( millis() );
}
void setup()
{
Serial.begin( 115200 );
Serial.print( "\n BWD with 8 buttons\n" );
pinMode( ledPin, OUTPUT ); // default is LOW
for ( currentButton = 0; currentButton < BUTTONS; currentButton++ )
{
pinMode( buttonPin[ currentButton ], INPUT_PULLUP ); // my button connects to ground, not 5V
}
// start button processing with button 0 then each loop(), process the next
currentButton = BUTTONS;
}
void loop() // make sure that loop() runs fast and stays in the "now".
{
static byte tmp;
// BUTTON CODE BLOCK, it handles debouncing.
// the task is to set the button state when a Stable Button State is reached.
if ( ++currentButton >= BUTTONS )
{
currentButton = 0;
}
// reads and states are stored as bits in buttonBits[ BUTTONS ]
// bCurrentRead = 0, bLastRead, bCurrentState, bLastState, bCheckDebounce
// msNow gets the low byte of millis() to time the very short debounce time
msNow = (byte) ( millis() & 0xFFFF ); // don't worry, unsigned rollover makes it work
// read the pin which may be changing fast and often as the jumper contacts the housing
tmp = digitalRead( buttonPin[ currentButton ] );
bitWrite( buttonBits[ currentButton ], bCurrentRead, tmp ); // momentary state
if ((( buttonBits[ currentButton ] & buttonReadBits ) == 1 ) ||
(( buttonBits[ currentButton ] & buttonReadBits ) == 2 )) // if currentRead != lastRead
{
debounceMillisLowByte[ currentButton ] = msNow;
bitWrite( buttonBits[ currentButton ], bCheckDebounce, 1 ); // button changed
}
else if ( bitRead( buttonBits[ currentButton ], bCheckDebounce )) // if checkDebounce == true
{
if ( msNow - debounceMillisLowByte[ currentButton ] >= debounceMs ) // stable button state achieved
{
bitWrite( buttonBits[ currentButton ], bCurrentState, !bitRead( buttonBits[ currentButton ], bCurrentRead )); // mission accomplished, button is stable
// note that buttonState is opposite buttonRead
bitWrite( buttonBits[ currentButton ], bCheckDebounce, 0 ); // stop debounce checking until pin change
}
}
bitWrite( buttonBits[ currentButton ], bLastRead, bitRead( buttonBits[ currentButton ], bCurrentRead ));
//
// End of the BUTTON CODE BLOCK
// BUTTON CHANGED CODE BLOCK
// which button is current?
// fSlowDown = 0, fSpeedUp, fButton2, fButton3, fButton4, fButton5, fButton6, fBlinkToggle
if ((( buttonBits[ currentButton ] & buttonStateBits ) == 4 ) ||
(( buttonBits[ currentButton ] & buttonStateBits ) == 8 )) // if currentState != lastState
{
bitWrite( buttonBits[ currentButton ], bLastState, bitRead( buttonBits[ currentButton ], bCurrentState ));
switch ( currentButton )
{
case fSlowDown :
if ( bitRead( buttonBits[ currentButton ], bCurrentState ))
{
if ( ledBlinkMs < 4000U )
{
ledBlinkMs *= 2U;
}
bar();
Serial.print( F( "slow down -- blink millis " ));
Serial.print( ledBlinkMs );
printMillis();
bar();
}
break;
case fSpeedUp :
if ( bitRead( buttonBits[ currentButton ], bCurrentState ))
{
if ( ledBlinkMs > 20U )
{
ledBlinkMs /= 2U;
}
bar();
Serial.print( F( "speed up -- blink millis " ));
Serial.print( ledBlinkMs );
printMillis();
bar();
}
break;
case fBlinkToggle :
// debug type prints -- also testing some loading, does it alter the blink much?
bar();
Serial.print( F( "processState " ));
Serial.print( blinkProcessState );
Serial.print( F( " blink toggle current state " ));
Serial.print( bitRead( buttonBits[ currentButton ], bCurrentState ), HEX );
printMillis();
bar();
switch ( blinkProcessState )
{
case 0: // 0 = 1st press has not happened, led is OFF, looking for press
if ( bitRead( buttonBits[ currentButton ], bCurrentState ) == 1 ) // button is pressed
{
blinkProcessState = 1;
blinkLed = 0;
ledPinState = 0;
ledBlinkStartMs = (word) ( millis() & 0xFFFF )- ledBlinkMs;
}
break; // note that until the 1st press, this case runs over and over
case 1: // 1 = 1st press is down, led is ON, looking for release
if ( bitRead( buttonBits[ currentButton ], bCurrentState ) == 0 ) // button is released
{
blinkProcessState = 2;
}
break; // note that until the 1st release, this case runs over and over
case 2: // 2 = 1st push relesased, led stays ON, looking for 2nd press
if ( bitRead( buttonBits[ currentButton ], bCurrentState ) == 1 ) // button is pressed
{
blinkProcessState = 3;
blinkLed = 1;
}
break; // note that until the 2nd press, this case runs over and over
case 3: // 3 = 2nd press is down, led is OFF, looking for release
if ( bitRead( buttonBits[ currentButton ], bCurrentState ) == 0 ) // button is released
{
blinkProcessState = 0; // back to the start
}
break; // note that until the 2nd release, this case runs over and over
}
break;
default : // switch currentButton
bar();
Serial.print( F( "switch " ));
Serial.print( currentButton );
if ( bitRead( buttonBits[ currentButton ], bCurrentState ))
{
Serial.print( F( " pressed" ));
}
else
{
Serial.print( F( " released" ));
}
printMillis();
bar();
break;
}
}
// End of the LED CONTROL CODE
// LED BLINK CODE BLOCK
if ( blinkLed )
{
ledBlinkNowMs = (word) ( millis() & 0xFFFF );
if ( ledBlinkNowMs - ledBlinkStartMs >= ledBlinkMs )
{
ledPinState = !ledPinState;
digitalWrite(ledPin, ledPinState );
ledBlinkStartMs = ledBlinkNowMs;
}
}
else
{
ledPinState = LOW;
digitalWrite(ledPin, LOW );
}
// End of the LED BLINK CODE
}