Stefan,
below the steps i did mention that the beep should be concurrrent (it could be either way).
After a lot of fiddling i've managed to make it work, BUT i'm having a weird issue with the display and the beeping function ended up being a horrible kludge with intermediate flags all over the place to make it work for the 3 beep at the end, i don't really like how it came out but it's working...
The problem with the display is that the countdown on TWO of the cases(the 10s intermediate steps case comienzo_10: and case intermedio_10:) both go from 2 to 0 instantly it's like it's skipping displaying 1, even when the serial monitor shows it going to 1. I don't know why as i'm using the exact same countdown code from the big countdowns.
I even included display functions for when the variable is checked to 0 to display the 0 -which it does-.
¿could it be that because tiempo_turno is decremented after the display, once it reaches 0 i don't have another round of timing that's why it ends up skipping 2 to 0 super fast?.
If i move the display function AFTER decrementing then it does go from 1 to 0 in 1second, but i never display the starting value:
case comienzo_10:
// timer de 10 segundos preparacion inicial
if (TimePeriodIsOver(waiting10_timer, 1000)) {
// tick cada 1 segundo
tiempo_preparacion--;
dbg("Tiempo restante preparación ", tiempo_preparacion);
display.showString("PR", 2, 0);
display.showNumber(tiempo_preparacion, false, 2, 2);
}
if (tiempo_preparacion == 0) {
display.showNumber(tiempo_preparacion, false, 2, 2);
beep_req = true; //pedir beep
tiempo_turno = duracion_turno; //init duración turno
beeptimestamp = millis(); //timestamp beeper
waiting180_timer = millis(); //timestamp turno A
//display.clear();
myState = turno_a;
}
break;
What's weird to me is that i'm using the exact same ticking/countdown code for all counting cases, but the weird behavior only happens on those two i mentioned.
Here's the "buggy" code, i shortened the 180s to 5s for debugging
/*
ref https://forum.arduino.cc/t/a-demo-code-explaining-the-switch-case-state-machine-and-how-to-do-things-almost-in-parallel/888172
*/
#define dbg(myFixedText, variableName) \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName);
// usage: dbg("1:my fixed text",myVariable);
// myVariable can be any variable or expression that is defined in scope
#define dbgi(myFixedText, variableName,timeInterval) \
{ \
static unsigned long intervalStartTime; \
if ( millis() - intervalStartTime >= timeInterval ){ \
intervalStartTime = millis(); \
Serial.print( F(#myFixedText " " #variableName"=") ); \
Serial.println(variableName); \
} \
}
//includes
#include <Bounce2.h>
#include <TM1637TinyDisplay.h>
// the states for the state-machine
const byte standby = 0;
const byte comienzo_10 = 1;
const byte turno_a = 2;
const byte intermedio_10 = 3;
const byte turno_b = 4;
const byte triple_beep = 5;
byte myState = standby; //estado inicial
//constantes
const unsigned int duracion_turno = 5; //180
const int pin_boton = 2;
const PROGMEM char FlashString[] = "ERTA TIMER";
//vars
byte tiempo_preparacion = 10; //contador 10s, resetear al llamarlo
int tiempo_turno = duracion_turno; //variable contador tiempo de turnos
unsigned long waiting10_timer; //timestamp inicio de 10s
unsigned long waiting180_timer; //timestamp inicio de 180s
unsigned long beeptimestamp; //timestamp inicio de beeper
unsigned long triplebeeptimestamp; //timestamp inicio de triple beep final
bool beep_req = false;
bool beeping;
byte beepcount;
//instancias
Bounce boton = Bounce(); //instancing bounce
TM1637TinyDisplay display(4, 3); //clock pin 4, data 3
void setup() {
boton.attach(pin_boton, INPUT_PULLUP); // Attach the debouncer to a pin with INPUT_PULLUP mode
boton.interval(25); // Use a debounce interval of 25 milliseconds
pinMode(LED_BUILTIN, OUTPUT); // Setup the LED
display.begin(); // init display
display.clear();
Serial.begin(115200);
Serial.println("ERTA TIMER v0.2"); //insertar version
}
void loop() {
boton.update(); // Update the Bounce instance
if (boton.fell()) { // Call code if button transitions from HIGH to LOW
if (myState == standby) {
Serial.println("boton presionado, iniciar");
waiting10_timer = millis(); //timestamp para inicio de preparación
beep_req = true;
beeptimestamp = millis(); //timestamp beeper
myState = comienzo_10;
tiempo_preparacion = 10;
display.stopAnimation();
display.clear();
beepcount = 0;
} else if (myState >= 1) { //forced stop/reset
// beep_req=1;
beeptimestamp = millis(); //timestamp beeper
triplebeeptimestamp = millis();
Serial.println("Abort/reset");
display.clear();
myState = triple_beep;
}
}
switch (myState) {
case standby:
//display scroll text, enviar por BT
if (!display.Animate()) {
display.startStringScroll_P(FlashString, 250);
}
break;
case comienzo_10:
// timer de 10 segundos preparacion inicial
if (TimePeriodIsOver(waiting10_timer, 1000)) {
// tick cada 1 segundo
dbg("Tiempo restante preparación ", tiempo_preparacion);
display.showString("PR", 2, 0);
display.showNumber(tiempo_preparacion, false, 2, 2);
tiempo_preparacion--;
}
if (tiempo_preparacion == 0) {
display.showNumber(tiempo_preparacion, false, 2, 2);
beep_req = true; //pedir beep
beeptimestamp = millis(); //timestamp beeper
waiting180_timer = millis(); //timestamp turno A
tiempo_turno = duracion_turno; //init duración turno
//display.clear();
myState = turno_a;
}
break;
case turno_a:
// ciclo turno A
if (TimePeriodIsOver(waiting180_timer, 1000)) {
// tick cada 1 segundo
dbg("Tiempo restante A ", tiempo_turno);
display.showString("A", 1, 0);
display.showNumber(tiempo_turno, false, 3, 1);
tiempo_turno--;
}
if (tiempo_turno == 0) {
beep_req = true;
tiempo_preparacion = 10; //init duración intermedio
beeptimestamp = millis(); //timestamp beeper
waiting10_timer = millis(); //timestamp para inicio de preparación
//display.clear();
myState = intermedio_10;
}
break;
case intermedio_10:
if (TimePeriodIsOver(waiting10_timer, 1000)) {
// tick cada 1 segundo
dbg("Tiempo restante intermedio ", tiempo_preparacion);
display.showString("PR", 2, 0);
display.showNumber(tiempo_preparacion, false, 2, 2);
tiempo_preparacion--;
}
if (tiempo_preparacion == 0) {
display.showNumber(tiempo_preparacion, false, 2, 2);
beep_req = true;
beeptimestamp = millis(); //timestamp beeper
waiting180_timer = millis(); //timestamp turno A
tiempo_turno = duracion_turno; //init duración turno
//display.clear();
myState = turno_b;
}
break;
case turno_b:
// timer de 180 segundos
if (TimePeriodIsOver(waiting180_timer, 1000)) {
// tick cada 1 segundo
dbg("Tiempo restante B ", tiempo_turno);
display.showString("B", 1, 0);
display.showNumber(tiempo_turno, false, 3, 1);
tiempo_turno--;
}
if (tiempo_turno == 0) {
// beep_req = true;
beeptimestamp = millis(); //timestamp beeper
triplebeeptimestamp = millis();
display.clear();
myState = triple_beep;
}
break;
case triple_beep:
if (!beeping && TimePeriodIsOver(triplebeeptimestamp, 1000)) {
beeptimestamp = millis();
beep_req = true;
dbg("conteo beep", beepcount)
dbg("triple beep_req end", millis());
beepcount++;
if (beepcount >= 3) {
myState = standby;
break;
}
}
break; // end
}
beeperv2(); //funcion recurrente beeper
}
// easy to use helper-function for non-blocking timing
boolean TimePeriodIsOver(unsigned long &startOfPeriod, unsigned long TimePeriod) {
unsigned long currentMillis = millis();
if (currentMillis - startOfPeriod >= TimePeriod) {
// more time than TimePeriod has elapsed since last time if-condition was true
startOfPeriod = currentMillis; // a new period starts right here so set new starttime
return true;
} else return false; // actual TimePeriod is NOT yet over
}
// beeper function, 1s on
void beeperv2() {
if (beep_req == true) {
digitalWrite(LED_BUILTIN, HIGH);
beeping = true;
//tone(8, 400, 3000);
dbgi("beep_req start", beeptimestamp, 100);
beep_req = false;
} else if (TimePeriodIsOver(beeptimestamp, 1000)) {
//dbgi("beep_req end",millis(),100);
digitalWrite(LED_BUILTIN, LOW);
beeping = false;
}
}
Here's the wokwi link(the code might differ as i'm doing the tests moving the countdown line in it): ERTIMER - Wokwi ESP32, STM32, Arduino Simulator