Chris is right, the problem is that the mechanical contact of the switch is bouncing. However, I disagree with his statement that the most elegant solution is polling. Polling is very inefficient for the task of counting how many times a button was pressed, and so I decided to post my own answer for clarification:
Interrupts are what you want. Polling is used when you want to constantly capture information, for example from a range sensor. Interrupts give you time sensitive control, and only performs processing at the exact time you need it.
For a more thorough answer...
This design problem is commonly referred to as "switch debouncing." As Chris correctly mentions, you can do this in hardware or software. The optimal method of implementation with respect to count accuracy and CPU load is to use hardware to smooth out the signal and connect your button to an interrupt pin on the Arduino. You can then use the built in attachInterrupt() function to eliminate the need to poll constantly. Polling is a waste of CPU power. The attachInterrupt() function will assign an interrupt handler that will be called whenever the interrupt pin sees a RISING or FALLING edge (this depends on how you have your switch electrically connected), or even more generally on a CHANGE. This would look similar to the following:
attachInterrupt(0, buttonHandler, RISING);
void buttonHandler() {
// turn LED on:
digitalWrite(ledPin, HIGH);
count = count + 1;
Serial.print(count);
}
This page explains the problem of switch debouncing and shows some example hardware circuits you can easily implement: http://www.elexp.com/t_bounc.htm
Note: You probably won't be able to see the led go HIGH because it will be handled so quickly. This code will increment the count once every time the button is pressed or released, depending on whether you attach the interrupt to the rising or falling edge of the signal.
Edit for OP's comment:
If you want the serial.print to say that the button is pressed the entire time, then add a while loop in the interrupt:
void buttonHandler() {
// turn LED on:
digitalWrite(ledPin, HIGH);
count = count + 1;
Serial.print(count);
while(digitalRead(buttonPin)) //This will loop until the button is released
Serial.print("ON");
}
Please note that this will cause your program to stay inside the interrupt handler, and your main function will not execute. In your main function, you can then have a Serial.print("OFF").
You may also be able to use a boolean that is set to true in your interrupt handler:
void buttonHandler() {
// turn LED on:
digitalWrite(ledPin, HIGH);
count = count + 1;
Serial.print(count);
buttonWasPressed = true;
}
That way, in your main you can have an if statement to prevent your code from having to loop in the interrupt handler:
void loop() {
if(buttonWasPressed) {
while(digitalRead(buttonPin)) {
Serial.print("ON");
}
buttonWasPressed = false;
}
else
Serial.print("OFF");
}