Reputation: 125
So my Question is more about the search for an elegant solution. I have several pushbuttons connected to arduino which individualy work as intended. For simplicity just look at two. I want different actions on press of buttons A, B and A + B at the same time (AB). I can check which buttons are pressed at any given moment and perform the action, the problem is, one button is always pressed first so that method gets called immediatly and I have not really deterministic behavior. What I have done is put a delay and check if the other button is also pressed in given time:
void loop() {
delay(100);
A = digitalRead(ButtonA);
B = digitalRead(ButtonB);
if (A == 0) {
delay(150);
B = digitalRead(ButtonB);
if (B == 0) {
doAB();
}
else {
doA();
}
}
A = digitalRead(ButtonA);
B = digitalRead(ButtonB);
if (B == 0) {
delay(150);
A = digitalRead(ButtonA);
if (A == 0) {
doAB();
}
else {
doB();
}
}
}
The Problem is, that this code depends on button pushing behavior. I'm sure I can find a sufficient solution with lots of testing and checking what gets pressed when. With more Buttons though this seems to give an awful lot of if-nesting and I imagine many people had this problem before at designing firmware. So my question is if and how this can be done in a cleaner way.
misc:
If someone holds Buttons I want that action to be repeated over and over.
The coding is done in C++.
doA,B,AB actions are taking some time (1-2sec).
Upvotes: 1
Views: 4176
Reputation: 2904
Try combine all buttons state into one variable, like:
int allBtnStates;
unsigned long btnTimeStamp = 0;
void loop() {
A = digitalRead(ButtonA);
B = digitalRead(ButtonB);
allBtnStates = A + 2*B;
if(allBtnStates < 3){ //Any button pressed
if(btnTimeStamp == 0) btnTimeStamp = millis(); //Create timestamp
else if(millis() - btnTimeStamp > 150){
switch(allBtnStates){
case 2: doA(); break; //Only A pressed
case 1: doB(); break; //Only B pressed
case 0: doAB(); break; //Both A and B pressed
}
btnTimeStamp = 0; //Reset timestamp
}
}
//Monitor other input if needed
}
If you have button C, then change allBtnStates = A + 2*B;
to allBtnStates = A + 2*B + 4*C;
and work out all the conditions accordingly. Hope that helps!
Upvotes: 3
Reputation:
this should do the same (or better) as your version
void loop() {
delay(100);
A = digitalRead(ButtonA);
B = digitalRead(ButtonB);
if (A == 0 || B == 0) {
delay(150);
A = digitalRead(ButtonA);
B = digitalRead(ButtonB);
if (A == 0 && B == 0) {
doAB();
} else if (A == 0) {
doA();
} else {
doB();
}
}
}
Upvotes: 1
Reputation: 136
What if you had a collection of Button objects. Each Button would have a "pressed" property and a public setter method that allows clients (your loop) to only set it true. In your loop you iterate over the Button collection each time, checking for a "digitalRead" on each button. As soon as at least 1 button returns 0 (pressed), you start a timer in the loop for say the 150 ms you use. Naturally you set each appropriate Button to pressed. While the timer is counting down it is possible for other Buttons to get set too. Ones that were previously set stay set even if their digitalRead goes high. At the end of the 150 ms delay, you collect all Button presses at once. Call the "doAB" method for whatever combination was pressed- doA, doACF, doBEK, etc. Clear the timer and have all Button presses go false. This latter part can happen when you collect the press status. Now your response logic happens in just one place.
This approach avoids the multiple button letter combination checks you had. Once input is detected you sort of activate an input session for a time of your choosing. It works for repeated action on holding buttons too.
Upvotes: 1