noob here
So what I am trying to do is output a different number depending on the number of button presses e.g. for 1 button press it should output 1 and so forth. After that I only really want the outputs for 1 to 4 presses. I am using these inputs for a game. Right now, it does work and counts up until 4 and then resets back to 0. The problem that I am having is it continues after 1 click to 2 clicks; what I want it to do is count 1 click when its clicked once, twice when its clicked twice etc. I am trying to use a debouncing code to distinguish between single tap, double tap, triple tap etc @VE7JRO kindly provided a great solution to this problem, albeit it was for one button. I am trying to adjust the code to accommodate 2 separate buttons (on separate pins), but it is giving me an error.
This is the code:
#include <Bounce2.h>
// Connect both buttons in series with one connection
// to GND and the other to a digital pin.
const int buttonPin1 = 2;
const int buttonPin2 = 3;
class Button
{
private:
int m_buttonPin1;
int m_buttonPin2;
int m_counter;
unsigned long m_buttonPressTimeout;
unsigned long m_previousMillis;
public:
Button(int button):
m_buttonPin1(buttonPin1),
m_buttonPin2(buttonPin2),
m_counter(0),
m_buttonPressTimeout(1500), // Button press timeout 500 ms.
m_previousMillis(0){}
void Update()
{
int valA = digitalRead(button1); // read button stateint
int valB = digitalRead(button2); // read button state
if (valA == LOW &&valB == LOW)
{
if(m_counter > 0 && millis() - m_previousMillis >= m_buttonPressTimeout)
{
Serial.print("Count from Update() just before it's re-set to 0 = ");
Serial.println(GetCounter());
m_counter = 0;
}
}
void IncrementCounter(){
m_counter++;
if(m_counter > 4){m_counter = 4;}
if(m_counter == 1){
m_previousMillis = millis();
}
}
byte GetCounter(){
return m_counter;
}
};
Bounce button1Debouncer = Bounce();
Bounce button2Debouncer = Bounce();
Button MyButton(buttonPin1);
Button MyButton(buttonPin2);
void setup(){
Serial.begin(9600);
pinMode(buttonPin1, INPUT_PULLUP);
button1Debouncer.attach(buttonPin);
button1Debouncer.interval(5);
pinMode(buttonPin2, INPUT_PULLUP);
button2Debouncer.attach(buttonPin);
button2Debouncer.interval(5);
}
void loop(){
// Call the Update function as fast as possible.
MyButton.Update();
// Button pressed.
if(button1Debouncer.update() && button2Debouncer.update())
{
if(button1Debouncer.fell() && button2Debouncer.fell()){
MyButton.IncrementCounter();
Serial.print("Count from Button Debouncer = ");
Serial.println(MyButton.GetCounter());
}
}
}
}
The error is: "'buttonPin1' is not a type", I do not understand this error as it didn't give that error for when a single button was used.
4 Answers 4
If it’s possible to connect the 2 buttons in series, then this sketch may give you the desired results.
#include <Bounce2.h>
// Connect both buttons in series with one connection
// to GND and the other to a digital pin.
const byte buttonPin = 2;
class Button{
private:
byte m_buttonPin;
byte m_counter;
unsigned long m_buttonPressTimeout;
unsigned long m_previousMillis;
public:
Button(byte buttonPin):
m_buttonPin(buttonPin),
m_counter(0),
m_buttonPressTimeout(500), // Button press timeout 500 ms.
m_previousMillis(0){}
void Update(){
if(m_counter > 0 && millis() - m_previousMillis >= m_buttonPressTimeout){
Serial.print("Count from Update() just before it's re-set to 0 = ");
Serial.println(GetCounter());
m_counter = 0;
}
}
void IncrementCounter(){
m_counter++;
if(m_counter > 4){m_counter = 4;}
if(m_counter == 1){
m_previousMillis = millis();
}
}
byte GetCounter(){
return m_counter;
}
};
Bounce buttonDebouncer = Bounce();
Button MyButton(buttonPin);
void setup(){
Serial.begin(9600);
pinMode(buttonPin, INPUT_PULLUP);
buttonDebouncer.attach(buttonPin);
buttonDebouncer.interval(5);
}
void loop(){
// Call the Update function as fast as possible.
MyButton.Update();
// Button pressed.
if(buttonDebouncer.update()){
if(buttonDebouncer.fell()){
MyButton.IncrementCounter();
Serial.print("Count from Button Debouncer = ");
Serial.println(MyButton.GetCounter());
}
}
}
EDIT
Based on your last update to the question:
I am trying to adjust the code to accommodate 2 separate buttons (on separate pins), but it is giving me an error.
I have updated the sketch to use 2 buttons on separate digital pins.
#include <Bounce2.h>
// Connect each button with one connection
// to GND and the other to a digital pin.
const byte buttonPin = 2;
const byte buttonPin2 = 3;
class Button{
private:
byte m_buttonPin;
byte m_counter = 0;
unsigned long m_buttonPressTimeout;
unsigned long m_previousMillis;
public:
Button(byte buttonPin):
m_buttonPin(buttonPin),
m_counter(0),
m_buttonPressTimeout(750), // Button press timeout in ms.
m_previousMillis(0){}
void Update(){
if(m_counter > 0 && millis() - m_previousMillis >= m_buttonPressTimeout){
Serial.print("Count from Update() just before it's reset to 0 = ");
Serial.println(GetCounter());
m_counter = 0;
}
}
void IncrementCounter(){
m_counter++;
if(m_counter > 4){m_counter = 4;}
if(m_counter == 1){
m_previousMillis = millis();
}
}
friend void IncrementCounter(Button&);
void IncrementCounter(Button&){
IncrementCounter();
}
byte GetCounter(){
return m_counter;
}
};
Bounce buttonOneDebouncer = Bounce();
Bounce buttonTwoDebouncer = Bounce();
Button ButtonOne(buttonPin);
Button ButtonTwo(buttonPin2);
void setup(){
Serial.begin(9600);
pinMode(buttonPin, INPUT_PULLUP);
pinMode(buttonPin2, INPUT_PULLUP);
buttonOneDebouncer.attach(buttonPin);
buttonTwoDebouncer.attach(buttonPin2);
buttonOneDebouncer.interval(25);
buttonTwoDebouncer.interval(25);
}
void loop(){
// Call the Update function as fast as possible.
ButtonOne.Update();
ButtonTwo.Update();
// Button one pressed.
if(buttonOneDebouncer.update()){
if(buttonOneDebouncer.fell()){
if(digitalRead(buttonPin2) == 0){
ButtonOne.IncrementCounter();
}
}
}
// Button two pressed.
if(buttonTwoDebouncer.update()){
if(buttonTwoDebouncer.fell()){
if(digitalRead(buttonPin) == 0){
ButtonOne.IncrementCounter(ButtonTwo);
}
}
}
}
-
thank you, I will try this. Does this use an external library? @VE7JROuser19964– user199642019年11月19日 18:51:17 +00:00Commented Nov 19, 2019 at 18:51
-
Yes, the Bounce2 library. It's available in the IDE library manager.VE7JRO– VE7JRO2019年11月19日 18:54:13 +00:00Commented Nov 19, 2019 at 18:54
-
Thank you; it works rather well...now just have to figure out how to connect two separate buttons on two separate pins @VE7JROuser19964– user199642019年11月20日 00:01:20 +00:00Commented Nov 20, 2019 at 0:01
-
@user19964 - It's easy to add another button. Define another variable such as
buttonPin2
, then do something like thisBounce buttonDebouncer2 = Bounce();
and thisButton MyButton2(buttonPin2);
. A bit of copy / pasting should get 2 buttons working quickly. The most difficult part will be to change the logic in theloop()
to work with two buttons.VE7JRO– VE7JRO2019年11月20日 00:25:43 +00:00Commented Nov 20, 2019 at 0:25 -
for the loop() couldn't you just add something like: if(button1Debouncer.update() && button2Debouncer.update()) { if(button1Debouncer.fell() && button2Debouncer.fell())}} ? @VE7JROuser19964– user199642019年11月20日 00:48:04 +00:00Commented Nov 20, 2019 at 0:48
You SHOULD pay attention to the following issues:
1. Floating input problem:
Symptom: the reading value from the input pin is not matched with the button's pressing state.
Cause: input pin is NOT used pull-up or pull-down resistor.
Solution: Use pull-up or pull-down resistor. See Arduino Button (with pull-up/pull-down)
2. Chattering phenomenon
It should be considered in only some application that needs to detect exactly number of the pressing.
Symptom: Button is pressed one, but Arduino code detects several times.
Cause: Due to mechanical and physical issues, the state of the button (or switch) is quickly toggled between LOW and HIGH several times
Solution: Debounce. See Arduino Button Debounce
-
1Thank you for the tips. I Have a pull up resistor on both my buttons and I had implemented a debounce code in this code. I am getting pretty much the same thing as before @Rozona Zorouser19964– user199642019年11月12日 00:02:22 +00:00Commented Nov 12, 2019 at 0:02
Your updated code has lots of problems. To do button debouncing, try something like this:
//Adjust debounceTime as desired. Shorter times will make fore faster button
//response, but make the debouncing less effective. 50 is 1/20th of a second.
#define debounceTime 50
unsigned long nextButtonCheckTime = 0;
bool buttonAState = false;
bool buttonBState = false;
void loop() {
//Only check the buttons if the debounce time has passed
if (millis() > nextButtonCheckTime)
bool newButtonAState = digitalRead(buttonAPin) == LOW;
bool newButtonBState = digitalRead(buttonBPin) == LOW;
//If one or both buttons have changed state
if (buttonAState != newButtonAState || buttonBState != newButtonBState)) {
nextButtonCheckTime = millis() + debounceTime;
buttonAState = newButtonAState;
buttonBState = newButtonBState;
//Handle change of button state
}
}
}
That is pseudocode. It almost certainly contains syntax errors. You should use it as a guide, and rewrite it to suit your needs. Do not say "I copied your code into my sketch and it won't compile."
-
Thank you; this does help as a guideline indeed. I will use it accordingly @DuncanCuser19964– user199642019年11月19日 00:43:16 +00:00Commented Nov 19, 2019 at 0:43
It is likely you are experiencing problems associated with button contact bounce. This is where the processor is so fast that it "sees" the button make several contacts when the user only intends for 1 contact to be seen by the program. Consider using this button de-bounce library instead of reading the state of the buttons directly in the sketch.
The code for the above debounce Arduino library is hosted here at github.com. In general, people using github.com will have an "example" folder along with the source code and documentation folders. The debounce Arduino library example folder is here. There are about half a dozen Arduino sketch examples including this 2 button example which might be used as the starting point for the project in the question.
There may be a logic problem in the code when interpreting the number of button presses into "a", "b", "c" & "d". The modulo operator "%" is used. And in the first test, the number of button presses % 1 is tested. If the result is zero the test is true. As any number % 1 has no remainder this test will always be zero and therefor always true. As all other tests are nested inside "else" statements none of the other tests will be executed. Instead consider using a switch / case statement where the code switches on the number of button presses and the case statements are "case 1:", case 2:", "case 3:" & "default:". It is good coding practice to always have a "default:" case in a switch statement. Here we use "default:" instead of "case 4:". After using the value representing the number of button presses, always remember to clear this value before the next iteration. Remember, any "global" values (values defined outside a function) will retain their values between calls to that function. Any "local" value (values defined inside a function) will not retain their values and need to be initialize each time they are used.
-
are there any examples showing how to implement this library...I have it imported to my project but am not sure how to implement it @st2000user19964– user199642019年11月12日 13:52:02 +00:00Commented Nov 12, 2019 at 13:52
-
I'll add where to find examples in my answer. Also, I appreciate you editing your question to make it better. I added text to your question to make it clear more information was added and what that information is showing.st2000– st20002019年11月12日 14:49:32 +00:00Commented Nov 12, 2019 at 14:49
-
yes, I see that you've edited my question: thank you...it reads much better now. Yes, those examples would be much appreciated @st2000user19964– user199642019年11月13日 14:01:27 +00:00Commented Nov 13, 2019 at 14:01
-
@st2000just thought to mention that the counter is working now, but it isn't working exactly how I would like it to.user19964– user199642019年11月14日 00:27:55 +00:00Commented Nov 14, 2019 at 0:27
-
You are using the Modulo "%" operator. I do not think that is what you want. For instance, you can divide any integer by 1 and not get a remainder. So 1 % 1 is 0, 2 % 1 is 0 anything % 1 is 0. That statement is always true and nothing below it will execute. You will always print out "a". Do you want me to alter my answer again?st2000– st20002019年11月14日 04:04:08 +00:00Commented Nov 14, 2019 at 4:04
PIND
, so you read pin 0 to 7 (advanced). Or, when I analyze your code, you just want to be notified of changes, you can use interrupts.