// https://wokwi.com/projects/393553254409100289
// https://forum.arduino.cc/t/simultaneous-short-and-long-presses/1240315
# include "Toggle.h" // Toggle Library, by dlloydev https://github.com/Dlloydev/Toggle
# include <Adafruit_NeoPixel.h>
# define NINE 9
# define LED_PIN 8
# define LED_COUNT NINE
Adafruit_NeoPixel disaply(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
const byte b1Pin = 2;
const byte b2Pin = 3;
Toggle b1, b2;
const uint16_t deltaT = 3500; // ms after that duration, a pressed button is considered in long press state
enum SystemState : byte {
B1R_B2R, B1P_B2R, B1L_B2R,
B1R_B2P, B1P_B2P, B1L_B2P,
B1R_B2L, B1P_B2L, B1L_B2L, NO_STATE,
} state = B1R_B2R;
const char* stateDescriptor[] = {
"B1R_B2R", "B1P_B2R", "B1L_B2R",
"B1R_B2P", "B1P_B2P", "B1L_B2P",
"B1R_B2L", "B1P_B2L", "B1L_B2L", "NO STATE",
};
const unsigned long stateColor[] = {
0xff0000, 0xffda33, 0x00ff00,
0xffda33, 0xffff00, 0xff00ff,
0x00ff00, 0xff00ff, 0x0000ff, 0xffffff,
};
const struct {
int code;
char *act;
} transAction[] = {
{(B1R_B2R << 4) + B1R_B2P, "GREEN"},
{(B1R_B2P << 4) + B1R_B2R, "ungreen"},
{(B1R_B2R << 4) + B1P_B2R, "BLUE"},
{(B1P_B2R << 4) + B1R_B2R, "unblue"},
{(B1R_B2P << 4) + B1R_B2L, "LONG GREEN"},
{(B1R_B2L << 4) + B1R_B2R, "allawy back"},
{(B1P_B2R << 4) + B1L_B2R, "LONG BLUE"},
{(B1L_B2R << 4) + B1R_B2R, "allawy back"},
{(B1R_B2L << 4) + B1P_B2L, "long green + BLUE"},
{(B1L_B2R << 4) + B1L_B2P, "long blue + GREEN"},
{(B1P_B2L << 4) + B1L_B2L, "long green + long BLUE"},
{(B1L_B2P << 4) +B1L_B2L, "long blue + long GREEN"},
};
const int nTransActions = sizeof transAction / sizeof *transAction;
void manageState() {
b1.poll();
b2.poll();
switch (state) {
case B1R_B2R:
//b1.poll();
if (b1.onPress()) {
b1.clearTimer();
state = B1P_B2R;
} else {
//b2.poll();
if (b2.onPress()) {
b2.clearTimer();
state = B1R_B2P;
}
}
break;
case B1R_B2P:
//b1.poll();
if (b1.onPress()) {
b1.clearTimer();
state = B1P_B2P;
} else {
//b2.poll();
if (b2.onRelease()) {
state = B1R_B2R;
} else {
if (b2.pressedFor(deltaT)) {
state = B1R_B2L;
}
}
}
break;
case B1R_B2L:
//b1.poll();
if (b1.onPress()) {
b1.clearTimer();
state = B1P_B2L;
} else {
//b2.poll();
if (b2.onRelease()) {
state = B1R_B2R;
}
}
break;
case B1P_B2R:
//b1.poll();
if (b1.onRelease()) {
state = B1R_B2R;
} else {
//b2.poll();
if (b2.onPress()) {
b2.clearTimer();
state = B1P_B2P;
}
else if (b1.pressedFor(deltaT)) {
state = B1L_B2R;
}
}
break;
case B1P_B2P:
//b1.poll();
if (b1.onRelease()) {
state = B1R_B2P;
} else {
//b2.poll();
if (b2.onRelease()) {
state = B1P_B2R;
}
else if (b1.pressedFor(deltaT)) {
state = B1L_B2P;
}
else if (b2.pressedFor(deltaT)) {
state = B1P_B2L;
}
}
break;
case B1P_B2L:
//b1.poll();
if (b1.onRelease()) {
state = B1R_B2L;
} else {
//b2.poll();
if (b2.onRelease()) {
state = B1P_B2R;
}
else if (b1.pressedFor(deltaT)) {
state = B1L_B2L;
}
}
break;
case B1L_B2R:
//b1.poll();
if (b1.onRelease()) {
state = B1R_B2R;
} else {
//b2.poll();
if (b2.onPress()) {
b2.clearTimer();
state = B1L_B2P;
}
}
break;
case B1L_B2P:
//b1.poll();
if (b1.onRelease()) {
state = B1R_B2P;
} else {
//b2.poll();
if (b2.onRelease()) {
state = B1L_B2R;
}
else if (b2.pressedFor(deltaT)) {
state = B1L_B2L;
}
}
break;
case B1L_B2L:
//b1.poll();
if (b1.onRelease()) {
state = B1R_B2L;
} else {
//b2.poll();
if (b2.onRelease()) {
state = B1L_B2R;
}
}
break;
}
}
void reportAndAct()
{
static SystemState previousState = NO_STATE;
if (previousState != state) {
if (0) {
Serial.print("State changed from ");
Serial.print(stateDescriptor[previousState]);
Serial.print(" to ");
Serial.println(stateDescriptor[state]);
}
int theTransition = (previousState << 4) + state;
for (int ii = 0; ii < nTransActions; ii++) {
if (transAction[ii].code == theTransition) {
Serial.print(" ");
Serial.println(transAction[ii].act);
}
}
previousState = state;
disaply.clear();
disaply.setPixelColor(state, stateColor[state]);
disaply.show();
}
}
void setup() {
Serial.begin(115200);
Serial.println("Jello Whirled!\n");
b1.begin(b1Pin);
b2.begin(b2Pin);
disaply.begin();
disaply.setPixelColor(1, 0xff0000);
disaply.show();
delay(777);
}
void loop() {
manageState();
reportAndAct();
}