- SOLUCIONADO - Operador ternario en maquina de estados

Hola de nuevo,

sigo probando distintas maneras de realizar mi proyecto de campanas, y me he encontrado con el siguente problema que no se resolver.

He creado una maquina de estados muy simple utilizando en los case un operador ternario que saqué del ejemplo Blink without delay for 2 different time intervals.

La idea es que cada 3 campanadas de las horas toque una vez la de los cuartos, lo que vendria a ser un Toque a Muertos para hombre.

El resultado es que despues de activar tres veces seguidas el relé (100ms) de la horas, activa durante 1500ms el relé de la campana de los cuartos.

Creo que el operador ternario interactua de alguna forma estranya con el if else, pero no consigo entendre el problema.

const byte hores = 4;           //Campana de les hores al relé pin 4
const byte quarts = 3;       //Campana dels quarts al relé pin 3

byte repic = 0;                        //Valor inicia variable case
int contadorCampanades = 0;               //Contador de campanades
const int intervalRepic = 100;           //Interval de repic
const int intervalCampana = 1500;           //Interval de repic

unsigned long ms = millis();        //temps millis()
unsigned long msLast1;    //darrer canvi d'estat Relé1
unsigned long msLast2;    //darrer canvi d'estat Relé2

bool buttonState1 = 0;                 //Declarar el valor de la variable botó S1
boolean EstatRele1;        //canvi interval/repic hores
boolean EstatRele2;        //canvi interval/repic quarts

void setup() {
  pinMode(hores, OUTPUT);
  pinMode(quarts, OUTPUT);
}

void loop() {

  ms = millis();

  if (contadorCampanades >= 3)
    repic = 2;
  else
    repic = 1;

  switch (repic){

    case 0:                         // no sonen les campanes
      digitalWrite(hores, LOW);
      digitalWrite(quarts, LOW);
      break;

    case 1:                         // Toc de campana hores
        if (ms - msLast1 >= (EstatRele1 ? intervalRepic : intervalCampana)) {
        digitalWrite(hores, EstatRele1 = !EstatRele1);
        msLast1 = millis();
        contadorCampanades++;
      }
      break;

    case 2:                       // Toc de campana quarts
      if (ms - msLast2 >= (EstatRele2 ? intervalRepic : intervalCampana)) {
        digitalWrite(quarts, EstatRele2 = !EstatRele2);
        msLast2 = millis();
        contadorCampanades = 0;
        repic = 0;
      }
      break;
  }
}

Agradeceré ayuda con el código, pero apreciaria mucho mas la explicacion de porqué no consigo el resultado deseado.

Aprendo despacio...

Si ya tenias este HILO abierto ¿por qué abres otro para el mismo tema? Preparate para la bronca del moderador.

No te has explicado muy bien lo que pretendes lograr y lo único que veo, y por lo que no puedo seguir adelante, es que la variable repic vale 0; por lo que al entrar al switch para por el caso 0, no haces nada excepto apagar los pines... Por lo tanto repic no cambia nunca así que el case 1 y 2 no se ejecuta nunca.

Gracias por tu aportación victorjam.

Estaré a lo que diga el Moderador, pero entiendo que el HILO al que haces referencia era para una consulta concreta, que ya se resolvió y consta como SOLUCIONADO. A mi entender reabrir un hilo solucionado seria lo incorrecto…

Respeto al comentario sobre el código, si se ejecuta. El valor cero es para que pare de ejecutarse (en la versión definitiva sera un botón el que active la máquina de estados, ahora al no haber botón de inicio se ejecuta sinfín…)).

Quizá no supe explicar bien el problema: Repic 2 solo se ejecuta si el contador ha llegado a 3, por lo que la máquina siempre ejecuta tres veces Repic 1. Una vez en ese punto ejecuta Repic 2, pero en lugar de hacerlo con un encendido de 100ms y una “pausa” de 1500ms (como el el otro case), el relé persiste encendido 1500ms y luego se apaga.

No entiendo porque siendo el mismo código se comporta de forma distinta.

Vale, no me percaté del "if" del principio.

Me ha costado entender lo que hace tu programa y no se si sabre explicarlo.

He abreviado el nombre de las variables, para no escribir mucho y sea mas legible.

R == repic.
CC == contadorCampanades.
ER1 == EstatRele1
ER2 == EstatRele2


R   CC  ER1    ER2    
1   0   false  false  despues de esperar IC entra en el if
1   1   true   false  despues de esperar IR entra en el if
1   2   false  false  despues de esperar IC entra en el if
1   3   true   false  Despues de aqui R valdrá 2 por lo que entra en el caso 2
                      Pero recuerda que comparas con msLast2 y millis ya ha transcurrido
                      IC+IR+IC por lo tanto entra directamente en el if sin esperar
2   0   true   true   Como se ha hecho CC=0 entonces ya no vuelve a entrar en el caso 2 y sigue
                      por el 1. Como ER1 es true espera un tiempo IR.
1   1   false  true   Ahora espera un tiempo IC
1   2   true   true   Ahora espera un tiempo IR
1   3   false  true   Ya CC vale de nuevo 3, por lo que en la siguiente iteracion R valdrá dos
                      y ha transcurrido un tiempo IC+IR con lo que al evaluar el if entrará 
      directamente, apagando el led
2   0   false  false  En la siguiente iteracion ya no pasa por el caso dos, ya que R valdrá 1
                      Como no ha transcurrido tiempo y ER1 es false espera un tiempo IC
1   1   true   false  Espera un tiempo IR
1   2   false  false  Espera un tiempo IC
1   3   true   false  Aqui ya vuelve a entrar en el caso dos y ocurre lo mismo ha transcurrido un
                      tiempo IR+IC con lo que entra directamente en el if.
Y asi sucesivamente...

Moraleja: el operador ternario funciona, lo que no funciona en si es el planteamiento de la máquina de estado.

Mmmmmm... creo que entiendo dónde me equivoqué. Los cambios de estado de Relé i no "cuadran" con el condador de campanadas. Gracias a tu explicacion he llegado a este codigo:

const byte hores = 4;           //Campana de les hores al relé pin 4
const byte quarts = 3;       //Campana dels quarts al relé pin 3

byte repic = 0;                        //Valor inicia variable case
int contadorCampanades = 0;               //Contador de campanades
const int intervalRepic = 100;           //Interval de repic
const int intervalCampana = 1500;           //Interval de repic

unsigned long ms = millis();        //temps millis()
unsigned long msLast1;    //darrer canvi d'estat Relé1
unsigned long msLast2;    //darrer canvi d'estat Relé2

bool buttonState1 = 0;                 //Declarar el valor de la variable botó S1
boolean EstatRele1;        //canvi interval/repic hores
boolean EstatRele2;        //canvi interval/repic quarts

void setup() {
  pinMode(hores, OUTPUT);
  pinMode(quarts, OUTPUT);
}

void loop() {

  ms = millis();

  if (contadorCampanades > 5)
    repic = 2;
  else
    repic = 1;

  switch (repic) {

    case 0:                         // no sonen les campanes
      digitalWrite(hores, LOW);
      digitalWrite(quarts, LOW);
      break;

    case 1:                         // Toc de campana hores
      if (ms - msLast1 >= (EstatRele1 ? intervalRepic : intervalCampana)) {
        digitalWrite(hores, EstatRele1 = !EstatRele1);
        msLast1 = millis();
        contadorCampanades++;

      }
      break;

    case 2:                       // Toc de campana quarts
      if (ms - msLast2 >= (EstatRele2 ? intervalRepic : intervalCampana)) {
        digitalWrite(quarts, EstatRele2 = !EstatRele2);
        msLast2 = millis();
        contadorCampanades++;
      }


      if (contadorCampanades == 8) {
        contadorCampanades = 0;
      }
      break;
  }
}

Consigo que haga tres campanades con el rele de las Horas, i una con la de los cuartos, para luego volver a repetir la secuencia. El fallo que no consigo arreglar es el intervalo entre la 3a capanada de las horas y la de los cuartos, que deberia ser de 1500ms y es de 100ms. Ahí me he atascado...

Te referias a eso con lo de "el planteamiento de la máquina"?

Una vez mas, gracias.

Con el nuevo código solucionas la parte del número de campanadas, pero no tocas la parte de la campanada de cuartos, es decir, sigues teniendo el mismo problema, cuando llega al caso 2, no espera ya que utilizas dos variables de tiempo distintas para controlar (msLast1, msLast2).

De hecho se me ocurre probar solo con una variable de tiempo, sustituir msLast1, msLast2 por una sola variable [u]ms[/u].

Efectivamente, al tener dos variables que almacenaban el tiempo, se producía este error al entrar el segundo case.

Te agradezco mucho tu ayuda, en especial porque me has explicado donde me equivocaba, y así he podido ser yo quin corrigiera el código, así es como uno aprende y es gracias a los que tenéis paciencia con los novatos que eso es posible.

Doy por SOLUCIONADO el topic, y espero contar de nuevo con vuestra ayuda cunado me vuelva a encallar :)