I am designing a turn signal bicycle blinker using two pushbuttons as input, one for each side, with a corresponding light for each button. The idea is this:
- Pressing one side makes same side start blinking;
- Pressing same side again to turn it off;
- Pressing other side turns off this side and turns other side on;
- (now the tricky part!) if both buttons are pressed (or more precisely, if one button is pressed before the other is released), then - and only then - I start blinking both sides ("warning signal" feature).
As I see it, the problem is that I need to wait until I release a single button (that would be one complete click) to turn a single side on, because if I press the second while the first remains pressed, that would be another event/gesture ("both_click", for lack of a better name).
Another design option would be to turn a single light on immediately on press, and if I press the second before release the first, then in would also turn the other light (thus comprising the "warning", both-lights-blinking mode). While that sounds feasible, there is all the debouncing logic in the Button ("Botao") class, and I am afraid I painted myself into a corner with the specific object-oriented design choices I've made.
EDIT: a third design option would be to add a run
method on each button and, instead of only running the tested "pseudo-event" method, save the current state and event in variables, testing those variables in loop()
(property button.wasPressed
instead of method button.wasPressed()
, for example).
I'm posting my current code (relevant parts only). Note there is a comment in the loop where I think I should detect double click.
Main .ino file:
#include "PiscaPisca.cpp"
#include "Botao.cpp"
PiscaPisca pisca;
Botao botao1;
Botao botao4;
void setup() {
pisca.configure(LEFT, RIGHT, BUZZER);
botao1.configure(BUTTON1);
botao4.configure(BUTTON4);
}
void loop() {
// HOW SHOULD I DETECT BOTH WERE PRESSED??
// if (bothPressed()) { pisca.toggleWarning(); }
if (botao1.wasPressed()) { pisca.toggleLeft(); }
if (botao4.wasPressed()) { pisca.toggleRight(); }
pisca.run();
}
Botao.cpp (this is a button class with debounce)
#include <Arduino.h>
class Botao
{
int _pino;
const int DEBOUNCE_DELAY = 30;
int buttonState;
int lastState = HIGH;
int lastDebounceTime;
public : void configure(int pino)
{
_pino = pino;
pinMode(pino, INPUT_PULLUP);
}
public : boolean wasPressed()
{
return debounce(LOW);
}
public : boolean wasReleased()
{
return debounce(HIGH);
}
public : boolean debounce(int state)
{
boolean gotEvent = false;
int reading = digitalRead(_pino);
if (reading != lastState) {
lastDebounceTime = millis();
}
if ((millis() - lastDebounceTime) > DEBOUNCE_DELAY) {
if (reading != buttonState) {
buttonState = reading;
if (buttonState == state) {
gotEvent = true;
}
}
}
lastState = reading;
return gotEvent;
}
};
PiscaPisca.cpp (the state-machine itself)
#include "Arduino.h"
#include "PwmPin.cpp"
#include "Beeper.cpp"
typedef enum {
NONE = 0,
RIGHT_LIGHT = 1,
LEFT_LIGHT = 2,
BOTH = 3
};
class PiscaPisca
{
PwmPin _left;
PwmPin _right;
Beeper _beeper;
long _timeReference = 0;
const int PERIOD = 350;
boolean
_running = false,
_lightState = false;
int _sides_to_turn = NONE;
public : void configure(int leftPin, int rightPin, int buzzerPin)
{
_left.configure(leftPin);
_right.configure(rightPin);
_beeper.configure(buzzerPin);
}
public : void run()
{
evaluateBlink();
}
public : void toggleLeft()
{
checkRestart(LEFT_LIGHT);
}
public : void toggleRight()
{
checkRestart(RIGHT_LIGHT);
}
void checkRestart(int lightSide)
{
_timeReference = 0;
// some clever bit-twiddling can never hurt too much:
_sides_to_turn = lightSide & ~_sides_to_turn;
Serial.println(lightSide);
Serial.println(_sides_to_turn);
if (_sides_to_turn > 0)
{
_running = true;
}
else
{
lightsOff();
_running = false;
_lightState = false;
}
}
void evaluateBlink()
{
if (!_running)
{
return;
}
else
{
long currentMillis = millis();
if (currentMillis - _timeReference > PERIOD) {
_timeReference = currentMillis;
_lightState = !_lightState;
performBlink();
}
}
}
void performBlink()
{
if (_lightState)
{
_beeper.beepIn();
lightsOn();
}
else
{
_beeper.beepOut();
lightsOff();
}
}
void lightsOn()
{
if (isLightSet(LEFT_LIGHT))
{
_left.on();
}
if (isLightSet(RIGHT_LIGHT))
{
_right.on();
}
}
boolean isLightSet(int lightSide)
{
return (_sides_to_turn & lightSide) == lightSide;
}
void lightsOff()
{
_left.off();
_right.off();
}
};