[SOLUCIONADO] problemilla con contador y condiciones ( if)

Hola me llamo julio y llevo poco tiempo con arduino.
esta es la parte donde tengo el problemilla

const int GAS = A0; 
const int pinPiezo  =2;
int t;

void setup(){
   Serial.begin(9600);
 pinMode(pinPiezo, OUTPUT);
 }

void loop(){ 
  
 if(analogRead(GAS) < 256){
   digitalWrite(pinPiezo, LOW); 
   t=0; 
   } 
 
 if(analogRead(GAS) >= 256)
 {
  digitalWrite(pinPiezo, HIGH);
 t=t+1;
 }

 if (analogRead(GAS) >= 256 && t>=5){
   digitalWrite(pinPiezo, LOW);
 }
 
 if (analogRead(GAS) >= 256 && t>=15){
  t=0;
  }  
             
  Serial.print(t);
  Serial.print("  ");
  Serial.println(analogRead(GAS));
  delay(1000);
  }

la verdad es que funciona excepto cuando se cumple la condición de GAS>=256 && t>=5 y el pin del piezo se pone en LOW, en el piezo se sigue escuchando el paso por el

if(analogRead(GAS) >= 256)
{
 digitalWrite(pinPiezo, HIGH);
t=t+1;
}

el sonido es como un cri,cri muy tenue.
como podría arreglarlo o mejorar.
esto es una implementación que quiero meter a un detector de gas para que avise pero no moleste demasiado
Gracias y un Saludo

Hola julio, que es específicamente lo que buscas lograr?
y te sugiero que leas en las normas como es que se copian códigos.
Saludos

Lee las normas del foro para ver como se postea el codigo.No se entiende muy bien que quieres hacer ,supongo que alguna especie de detector de gas con tonos de aviso.Para generar tonos ,echa un vistazo a la funcion especifica para ello: tone().En cuanto al codigo,podria ser mas eficiente si solo haces una lectura del sensor por cada loop,almacenas el resultado en una variable y usas esa variable con el condicional:

void loop() { 
  int lectura = analogRead(GAS);
  if (lectura < 256) {
    // codigo
  }
  if (lectura >= 256) {
   // codigo
  }
 }

Hola.

El problema es que si fijas esta condicion:

 if(analogRead(GAS) >= 256)
  {
   digitalWrite(pinPiezo, HIGH);
  t=t+1;
  }

Estas otras no son determinantes:

 if (analogRead(GAS) >= 256 && t>=5){
    digitalWrite(pinPiezo, LOW);
  }
 
  if (analogRead(GAS) >= 256 && t>=15){
   t=0;
   }

Porque siempre se ejecutará la primera con independencia del valor de , de ahí los tics que escuchas

Creo que una forma de arreglar tu codigo seria

if(analogRead(GAS) >= 256)
  { 
     if ( t>=5){

     digitalWrite(pinPiezo, LOW);

      }

     else {

           digitalWrite(pinPiezo, HIGH);

            t=t+1;
          }
}

Igual, considero mejor que reveas todo tu codigo y lo reescribas tal como lo indico Jose

Ante todo Gracias por la rapidez en contestar y el interés mostrado. Lo de las normas la verdad que lo leí pero sinceramente por encima, por lo que pido disculpas.

Efectivamente lo que pretendo es que el detector de gas me indique con un piezo activo 3 mediciones de gas,
la primera medición por ejemplo 800ppm. avise con un intervalo de sonido (ON de 5 segundos y OFF de 15 minutos) solo mientras se mantenga los 800ppm. (es solo para indicar que el aire no esta limpio), si llega hasta 1000ppm (ON 15 segundos y OFF 10 minutos) y si llega a los 3000ppm pitido constante.
el programa que pongo lo tengo en el principio del (void loop) del programa real.

José lo del tone() lo conozco, pero no se como me serviría para contar el tiempo de (ON-OFF) del piezo en las determinadas mediciones.

Un saludo

Te acabo de enviar un privado para que corrijas tu post inicial. Esta todo muy bien detallado y no tendrás problemas en hacerlo bien.
Eso de que las leí por encima y aún asi no has hecho nada a mi me hace pensar que no te importa, porque de lo contrario si 2 personas te piden que coloques un código usando las normas y aún asi no lo haces .. me da que pensar.
Supongamos que no pudiste o no entendiste, cosa que dudo, pues bien ahora presta atención a mi comentario x privado.

Ya tres personas te han dicho como podrías resolverlo.

El enfoque de Ezequiel es el mas aproximado a lo que tu pretendes. Para mi contar hasta 5 me parece algo muy veloz y me gusta mas por tiempo y te doy entonces mi sugerencia.

#define  TON    5000  // 5 segundos
#define  TOFF   10000 // 10 segundos
const int GAS      = A0; 
const int pinPiezo = 2;
unsigned int valor;
unsigned int t = 0;
unsigned long start;
byte estado = 0;

void setup(){
  Serial.begin(9600);
  pinMode(pinPiezo, OUTPUT);
}
 
void loop(){ 
   
  valor = analogRead(GAS);

  if (valor < 256){
     digitalWrite(pinPiezo, LOW); 
     t = 0;
  } 
  else {
     t++;
     switch (estado) {
          case 0: digitalWrite(pinPiezo, HIGH);
                  estado = 1;
                  start = millis() + TON;   // tiempo que suena
                  break;
          case 1: if (millis()>start) {
                      digitalWrite(pinPiezo, LOW);
                      estado = 2;
                      start = millis() + TOFF;   // tiempo que suena
                  }
                  break;
          case 2: if (millis()>start) {
                     estado = 0;
                  }
                  break;
          default: break;
      }
  }
  char buffer[20];
  sprintf(buffer, "t=%d GAS = %d", t, valor);        
  Serial.print(buffer);
  delay(1000);
}

Lo ultimo es solo uan forma de presentar t y GAS, pero básicamente es lo mismo que hiciste tu.

Gracias por responder surbyte

Te sirve de ese modo?
Puedes ajustar TON y TOFF a tu gusto.

Hola surbyte
Esta claro que seguro que si me servira, pues viendo lo básico que es el mio no hay duda.
Lo que ocurre es que no me gusta hacer un copi/pega y ya esta, me gusta entender el funcionamiento de lo que no se o no conozco y no dispongo de mucho tiempo, solamente los finde dispongo de algo mas de tiempo.
Asi que este finde me pondré a buscar por internet algunas funciones de las que hay en tu programa que no conozco y despues a entender el funcionamiento y aprender su uso. Cuando lo tenga probado te lo dire.

Un saludo

Esto es una máquina de estados

 switch (estado) {
          case 0: digitalWrite(pinPiezo, HIGH);
                  estado = 1;
                  start = millis() + TON;   // tiempo que suena
                  break;
          case 1: if (millis()>start) {
                      digitalWrite(pinPiezo, LOW);
                      estado = 2;
                      start = millis() + TOFF;   // tiempo que suena
                  }
                  break;
          case 2: if (millis()>start) {
                     estado = 0;
                  }
                  break;
          default: break;
      }

parece y luce como compleja pero no lo es.
Primero tiene una variable que la controla.. se llama estado en este caso.
Estado toma valores 0..1..2.. y vuelve a 0
Cada vez que te encuentras en un estado se ejectua solo una acción y no otra.
Entonces si miras.. comienzas con estado = 0
con estado = 0 solo se ejecuta esto

 digitalWrite(pinPiezo, HIGH);
                  estado = 1;
                  start = millis() + TON;   // tiempo que suena
                  break;

Activas el buzzer
pones la variable estado = 1 o sea que cuando vuelva a pasar no lo hará mas por esta parte.
cargas la variable start con el vallor de millis() mas TON
Eso es lo mismo que cargar una variable con el valor actual de un cronometro mas el momento que tu quieres usar para hacer algo.

Tu TON se va a mantener durante ese tiempo.
El programa se ejecuta y acabo de darme cuenta que esto
delay(1000) debe borrarse

en el siguiente ciclo el switch case pasara por case 1:
pero como no se da la condición hasta que millis() supere a start entonces seguirá varios ciclos hasta que se cumpla
Cuando se cumpla apagará el buzzer.
Ahora repite lo anterior pero para el tiempo en que quieres que se mantenga en silencio.
De nuevo le cambia el estado a 2
Espera a que se cumpla el tiempo TOFF y cuando lo haga pone estado = 0 y repite el ciclo.

Bien, repito, tengo un error que es delay(1000) al final.
aunque eso hace que tu variable t++ funcione, no debería estar.
asi que me dices si quieres corregirlo o se te ocurre como.

Usar millis() implica un cambio drástico a usar delay, pero tiene grandes ventajas. Fundamentalmente nada detiene el flujo del programa y entonces toda acción ocurre rápidamente.

millis() es un cronométro que se incrementa cada milisegundo desde que Arduino se energiza.

asi que 5 seg despues millis() puede tener un valor como 5030 milisegundos
si tu quieres hacer que algo actue 10 segundos despues , lo que haces es capaturar el valor de millis() y sumar lo que quieres.
Veamos para TOFF.
capturamos en start = millis() + TOFF = 5030 + 10000 = 15030 o sea
cuando el if (millis()>start) o sea cuando millis() valgoa 15031 > 15030 entonces hara la instrucción if.
y la acción que corresponda.

Hola surbyte

Sin palabras para tu interés por mi consulta.
La verdad es que con tu explicación no me hace falta mirar mucho por internet, solo adaptarlo al programa principal modificando algunas cosillas y ampliándolo para que la función de tiempo de apagado y encendido del piezo varíe según el nivel de gas detectado, que seria tres fases :

1ª seria de un corto TON y largo TOFF de aviso (Algo esta pasando en el Aire)
2º seria un TON mucho mas largo y un corto TOFF de aviso (Empieza a abrir ventanas y a cerrar la llave del gas)
3ª seria un TON continuo de (Peligro eminente de explosión)

Un saludo y muchas gracias

Habría otra posibilidad fácil de implementar que tal vez sea interesante, que sería hacer TON y TOFF proporcional e inversamente proporcional respectivamente a la lectura. Osea, que cuanto más alta sea la lectura, más continuo será el pitido, hasta llegar al continuo, con muchos niveles intermedios.

Julio, ve las posibilidades que tiene el método de programar por máquina de estados.
Puedes hacer lo que gustes y puedes crear varias máquinas con variables que se llamen diferente y pasen por la cantidad de estados que desees.
No tiene límites salvo la memoria.

Ahora, practica con dos máquinas o dos situaciones que respondan a lo que deseas hacer y a ver que resulta.

Hola
Note, es muy interesante tu propuesta pero creo que se sale por el momento de mis capacidades.
Estoy modificando el de surbyte con varios void() y tres IF. Cuando vea que funcione lo pongo para que lo valoréis.
Lo que si me preocupa es el desbordamiento de millis(), ya que veo que no se puede resetear.

Un saludo

Hola.

millis() vuelve a cero cada 50 dias mas o menos, pero las comparaciones del tipo: "(millis()-contador) > limite" que hagas en tu programa siguen siendo válidas.

Saludos

Hola
Después de caharrear unas pocas de horas y probar, parece que hay algo que no estoy haciendo bien

#define  TON    10000  // 10 segundos
#define  TOFF   60000 // 60 segundos
const int GAS      = A0; 
const int pinPiezo = 2;
unsigned int valor;
unsigned long start;
byte estado = 0;


void setup(){
  Serial.begin(9600);
  pinMode(pinPiezo, OUTPUT);
}
 
void loop(){ 
  
   valor = analogRead(GAS);
  
  if (valor < 256){
     digitalWrite(pinPiezo, LOW); 
     } else
   if (valor > 256 && valor < 410){
     caso1();
     } else
   if (valor > 410 && valor < 510){
      caso2(); 
     } else
    if (valor > 510){
      caso3(); 
    }
    
   // char buffer[20];
    // sprintf(buffer, "t=%d GAS = %d", valor);  
    // Serial.println(buffer);
    // delay(100);
    
     }
     
    void caso1(){
     
    switch (estado) {
          case 0: digitalWrite(pinPiezo, HIGH);    // Piezo encendido
                  estado = 1;
                  start = millis() + TON;          // tiempo que suena 10 segundos
                  break;
          case 1: if (millis()>start) {
                      digitalWrite(pinPiezo, LOW);  // Piezo apagado
                      estado = 2;
                      start = millis() + TOFF;      // tiempo que no suena 1 minuto
                  }
                  break;
          case 2: if (millis()>start) {
                     estado = 0;
                  }
                 break;
          default: break;
      }
      
     }
     
    void caso2(){
   
    switch (estado) {
          case 0: digitalWrite(pinPiezo, HIGH);   // Piezo encendido
                  estado = 1;
                  start = millis() + TON *4;      // tiempo que suena 40 segundos
                  break;
          case 1: if (millis()>start) {
                      digitalWrite(pinPiezo, LOW); // Piezo apagado
                      estado = 2;
                      start = millis() + TOFF /3;   // tiempo que no suena 20 segundos
                  }
                  break;
          case 2: if (millis()>start) {
                     estado = 0;
                  }
                  break;
          default: break;
      }
     }
     
    void caso3(){
         
         digitalWrite(pinPiezo, HIGH);  // Pitido Continuo
      
     }

Si le quito como se ve el char buffer y lo demás que tengo con // el void caso1 solo hace un cri y ya esta
si le pongo un delay() funciona y sin el delay() pero con el char buffer, sprintf y Serial.println.
El caso 2 hace el pitido pero de 1 0 2 segundos y el caso 3 si se queda el pitido fijo.
No entiendo el porque.
Para emular las señales que me daría el sensor estoy utilizando un potenciómetro para ver bien los valores.
Un saludo

Hola Julio

Intentando aportar algo de ayuda te pido ¿ puedes hacer esta prueba ?

#define  TON    10000UL  // <<== Obliga el tipo
#define  TOFF   60000UL  // <<== Obliga el tipo
.
.
.
  if (valor < 256){
     digitalWrite(pinPiezo, LOW);
     } else
   if ((valor >= 256) && (valor <= 410)){  // <<== No deja valores indefinidos
     caso1();
     } else
   if ((valor >= 410) && (valor <= 510)){  // <<== No deja valores indefinidos
      caso2();
     } else
    if (valor > 510){
      caso3();
    }

El programa aparenta estar bien y solo el detalle de los tipos puede ser la fuente del problema porque si no sumas a millis() un "unsigned long" puede que el resultado no sean los timeout que esperas.

Comenta el resultado.

Hola Alfaville

De hecho al principio lo tenia asi con >= y <= en todas los IF lo quite por probar pero nada. Viendo que podía ser otra metedura de pata mía me puse a trabajar con el programa original de surbyte y pasa lo mismo si le quito lo que antes e mencionado.
De todas maneras se lo e vuelto a poner por que creo que es lo correcto, pero sigue sin funcionar.

Un saludo

Hola.
Este código está sin probar, aunque sí que compila, al menos. Aunque le falte algún fleco por arreglar, creo que se entiende por dónde va la idea. Estúdialo detenidamente a ver si lo comprendes:

unsigned long t_on, t_off;
const int GAS = A0;
const int pinPiezo = 2;


void setup(){
  Serial.begin(9600);
  pinMode(pinPiezo, OUTPUT);
}

void loop(){
  unsigned int valor = analogRead(GAS);

  if (valor < 256) {
    t_on=10000;
    t_off=50000;
  } else
  if (valor < 410) {
    t_on=20000;
    t_off=40000;
  } else
  if (valor < 510){
    t_on=30000;
    t_off=30000;
  } else {
    t_on=60000;
    t_off=0;
  }

  suena();
}

void suena (){
  static int estado = 0;
  static unsigned long start = millis();
  switch (estado) {
    case 0:
      if ( (millis()-start) < t_on ) {
        digitalWrite(pinPiezo, HIGH); // Piezo encendido
      } else {
        estado = 1;
        start += t_on;
      }
    break;
    case 1:
      if ( (millis()-start) < t_off ) {
        digitalWrite(pinPiezo, LOW);  // Piezo apagado
      } else {
        estado = 0;
        start += t_off;
      }
    break;
  }
}

Aunque me parecen unos tiempos enormes. Yo pensé que sería más o menos estilo detector de metales, de impulsos de alrededor de un segundo, en lugar de un minuto.