Contact switch (including most buttons) bouncing is a regular topic here.
Step one is know what's going on. As metal contacts come very close, sparks cross the gap. An Arduino that is not being slowed down (serial buffer overflow makes your code wait until it's only full, more often the execution block is caused by use of the delay command) can see spikes as short as a few microseconds. They can appear as button press and release events, as you see now.
The Nick gammon blog on switches and debouncing, a full treatment.
You can use a delay() in place of the prints. I can't recommend it as it cuts the capability of your code (makes reuse or adding more that needs speed or close timing buggy) badly but maybe no worse than those prints already do.
You could debounce the button with a capacitor across the leads (hardware debounce).
You could debounce it in software. I have a button library for that, there's dozens of routines that do the same more or less as it's a kind of hobby around here.
Whichever way you go, there's a few other things you can do to clean up your code that will save you time and ram in later projects.
Of course I like my library. It lets the user make button code-objects, even button arrays. The objects only use 4 bytes of memory each.
Example showing how to use the library to use multiple buttons is below. I have it set up so that the library files go into the project folder. That lets the user work on them in IDE tabs and save to the right file.
You don't need to know how the library works except for what's in the example.
The button object returns a value of 2 when pressed and 1 when released, you don't need to keep track of previous state because the library rolled that into the return value but you can track actual current up or down or bouncing state as 0 or 3 or > 3 as well (like for game use).
One thing. I code buttons to use the AVR on-chip pullup resistor. All the button has to do is ground the pin for press. The resistor is 20K to 50K, safe to ground, saves me a resistor and lets me use jumpers as buttons (I ground them on the USB port box). It does mean that LOW is pressed and HIGH is not pressed but your code never sees that. Your wiring does need to ground the button instead of pull it up (safe if you don't but it won't work) and you need to mode the pin(s) as INPUT_PULLUP instead of INPUT but again, it's safe if you don't. That uses a great AVR feature.
Any specific questions, just ask.
/* buttonarrayclass example -- by GoForSmoke 2015 for public domain use
revised with better library Oct 2016
This uses 2 buttons or switches or jumpers for pins 2 & 3 to GND.
Depending on switch, debounce value may need tuning.
Button class object output is a state value for the main sketch code.
// bit 0 = current, bit 1 = previous, bit 2 = bounce
0, button currently still pressed. bits 000; same current and previous
1, button has just been released. bits 100; different current and previous
2, button has just been pressed. bits 010; different the other way
3, button currently still released. bits 110; same current and previous
4 or more means the button is being debounced but not finished.
*/
#include "Arduino.h"
#include "button.h" // check for newest
// these variables are here to ward off the WinXP bug!
// if you're not running Windoze XP you won't need them
// but in any case, the compiler will optimize them away.
int a, b, c;
byte d, e, f;
const byte howManyButtons = 4;
byte buttonIdx;
byte buttonPin[ howManyButtons ] = { 4,5,6,7 };
button user[ howManyButtons ]; // button array
const byte ledpin = 13;
void setup()
{
Serial.begin( 250000 );
Serial.println( F( "\n Startup\n" ));
pinMode( ledpin, OUTPUT ); // default is INPUT LOW, now is OUTPUT LOW
for ( buttonIdx = 0; buttonIdx < howManyButtons; buttonIdx++ )
{
user[ buttonIdx ].setButton( buttonPin[ buttonIdx ], 5 ); // buttons on pins 4,5,6,7
}
buttonIdx = 0;
}
void loop()
{
static byte buttonRead; // allocated once, used often
buttonRead = user[ buttonIdx ].runButton(); // turns the crank once, make sure it runs often!
if ( buttonRead == 2 ) // just pressed
{
digitalWrite( ledpin, HIGH );
Serial.print( F( "button " ));
Serial.print( buttonIdx );
Serial.print( F( " pressed millis() == " ));
Serial.println( millis());
}
else if ( buttonRead == 1 ) // just released
{
digitalWrite( ledpin, LOW );
Serial.print( F( "button " ));
Serial.print( buttonIdx );
Serial.print( F( " released millis() == " ));
Serial.println( millis());
Serial.println( );
}
if ( ++buttonIdx >= howManyButtons ) buttonIdx = 0; // adds 1 then tests limit
}
The library .h file
/*
button.h for public domain use by GoForSmoke May 29 2015
Revised Sept 2016
To use:
make a buttonclass object in your sketch
run that object every time through loop(), it is quickly done
when you want to know the status of the button, you read the object
it returns state; bit 0 = current, bit 1 = previous, bit 2 = bounce
*/
// button library, Sept 25th, 2016 by GoForSmoke
#ifndef button_h
#define button_h
#include "Arduino.h"
#define CURRENT_1 1
#define PREVIOUS_2 2
#define BOUNCE_4 4
typedef class button
{
private:
byte arduPin;
byte buttonState; // bit 0 = current, bit 1 = previous, bit 2 = bounce
byte startMs;
byte debounceMs;
public:
button(); // default constructor
void setButton( byte, byte ); // pin, debounce millis
button( byte, byte ); // pin, debounce millis
void setUpButton( void );
byte runButton( void ); // returns buttonState as below
// buttonState: bit 0 = current, bit 1 = previous, bit 2 = bounce
byte buttonRead(); // returns buttonState as above
};
#endif
The library .cpp file
// button library, Sept 25th, 2016 by GoForSmoke
#include "Arduino.h"
#include "button.h"
// button ================================================
button::button() // default constructor for arrays, needs the full setup
{
buttonState = CURRENT_1;
}
button::button( byte ap, byte dbm )
{
arduPin = ap;
debounceMs = dbm;
buttonState = CURRENT_1;
};
void button::setButton( byte ap, byte dbm ) // pin, debounce millis
{
arduPin = ap;
debounceMs = dbm;
pinMode( arduPin, INPUT_PULLUP );
};
void button::setUpButton( void )
{
pinMode( arduPin, INPUT_PULLUP );
};
byte button::buttonRead()
{
return buttonState;
};
byte button::runButton( void )
{
// static byte msNow;
buttonState &= BOUNCE_4 | CURRENT_1; // clears previous state bit
buttonState |= ( buttonState & CURRENT_1 ) << 1; // copy current state to previous
buttonState &= BOUNCE_4 | PREVIOUS_2; // clears current state bit
buttonState += digitalRead( arduPin ); // current state loaded into bit 0
// msNow = (byte) millis(); // gets the low byte of millis
if ( buttonState & 3 == CURRENT_1 || buttonState & 3 == PREVIOUS_2 ) // state change detected
{
buttonState ^= BOUNCE_4; // toggles debounce bit
// on 1st and odd # changes since last stable state, debounce is on
// on bounces back to original state, debounce is off
if ( buttonState & BOUNCE_4 )
{
// startMs = msNow; // starts/restarts the bounce clock on any change.
startMs = (byte) millis(); // starts/restarts the bounce clock on any change.
}
}
else if ( buttonState & BOUNCE_4 ) // then wait to clear debounce bit
{
// if ( msNow - startMs >= debounceMs ) // then stable button state achieved
if ( (byte)((byte)millis()) - startMs >= debounceMs ) // then stable button state achieved
{
// understand that stable state means no change for debounceMs. When time
// is over the state bits are manipulated to show a state change.
buttonState &= CURRENT_1; // clear all but the current state bit
if ( buttonState == 0 ) buttonState = PREVIOUS_2; // HIGH->LOW
else buttonState = CURRENT_1; // LOW->HIGH
// buttonState now appears as a debounced state change in bits 0 and 1
}
}
return buttonState;
};
// end button ============================================