Cronómetro con 2 botones [inicio + restablecimiento]

Buenos días a tod@s,

Soy nuevo por aquí y en programación :slight_smile:

He montado un cronómetro con 2 botones + lcd 20x4, estoy contento con los resultados pero me gustaría mejores resultados.

El tiempo se adelanta en 3,5 segundos cada hora, he comprobado con un cronómetro del móvil y la verdad no tengo ni idea de como solucionar este punto.

Y por otro lado, cuando enciendes el arduino empieza la puesta en marcha del tiempo, tienes un botón para pausar el tiempo y otro botón para poner el contador a cero.

Si se pausa el tiempo, y pulso el botón de restablecer a cero, no funciona, el restablecimiento es para cuando está en ejecución el cronómetro.

Lo ideal y lo que busco es, al encender, el contador esta a cero, al pulsar inicio empieza a contar, si presiono pausa poder restablecer con el botón de restablecimiento.

#include <LiquidCrystal_I2C.h>//Libreria para LCD I2C
#include <Wire.h>//libreria requrida para usar SDA y SCL
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

// Variables para los cálculos internos del cronómetro
int horas = 0;
int minutos = 0;
int segundos = 0;
int decimas = 0;
long milisegundos = 0;

int pulsador_inicio = 10; // pulsador_inicio en PIN digital 10
int pulsador_pausa = 9; // pulsador_pausa en PIN digital 9

void setup() {
Serial.begin(9600); // Comienzo de la comunicación serie
pinMode(pulsador_inicio, INPUT); // Pin digital 10 como entrada  
pinMode(pulsador_pausa, INPUT); // Pin digital 9 como entrada
lcd.begin(20, 4);//Inicializar la LCD 20x4
lcd.backlight();//Encender la luz de fondo
}

void loop()  {
    // Si presionamos el pulsador de inicio se pone todo a cero
    if(digitalRead(pulsador_inicio) == HIGH)
    {
      horas = 0;
      minutos = 0;
      segundos = 0;
      decimas = 0;
      lcd.clear();
    }
   
    // Si presionamos el pulsador de pausa se congela el tiempo hasta que lo volvamos a presionar
    if(digitalRead(pulsador_pausa) == HIGH)
    {
      delay(200);
      while(digitalRead(pulsador_pausa) == LOW);
      delay(200);
    }
   
    // Se cuentan los milisegundos transcurridos
    milisegundos = millis();
   
    // Si el tiempo transcurrido es mayor a una décima de segundo
    if(milisegundos % 100 == 0)
    {
      decimas++;
      // Si han pasado 10 décimas de segundo se cuenta un segundo
      if(decimas == 10)
      {
        decimas = 0;
        segundos++;
      }
      // Si han pasado 60 segundos se cuenta un minuto
      if(segundos == 60)
      {
        segundos = 0;
        minutos++;
      }
      // Si han pasado 60 minutos se cuenta una hora
      if(minutos == 60)
      {
        minutos = 0;
        horas++;
      }
     
      // Mostramos el tiempo a través del LCD      
      lcd.setCursor(1, 0);
      lcd.print("------------------");
      lcd.setCursor(1, 3);
      lcd.print("------------------");
      lcd.setCursor(4, 1);
      lcd.print("tiempo");
      lcd.setCursor(4,2);
      if(horas < 10)
      {
        lcd.print("0");
      }
      lcd.print(horas);
      lcd.print(":");
      lcd.setCursor(7,2);
      if(minutos < 10)
      {
        lcd.print("0");
      }
      lcd.print(minutos);
      lcd.print(":");
      lcd.setCursor(10,2);
      if(segundos < 10)
      {
        lcd.print("0");
      }
      lcd.print(segundos);
      lcd.print("[");
      lcd.setCursor(13,2);
      lcd.print(decimas);
      lcd.print("]");
    }    
  }

Gracias por todo!

Y cual es el boton de reestablecimiento si solo tienes start y pausa?

Explícate mejor porque no termino de entender cuál es la función de cada botón.
Tienes dos pero los describes como si tuvieses 3.

La idea es que te entendamos sin tener que mirar el código.

Tu mayor problema es este

    if(digitalRead(pulsador_pausa) == HIGH)
    {
      delay(200);
      while(digitalRead(pulsador_pausa) == LOW);
      delay(200);
    }

Cuando está en pausa ahí se queda esperando que pulses otra vez el botón. ¿En que momento leería el otro para hacer la vuelta a 0?

Lo que quieres hacer no es complicado, he hecho un cronómetro que hace lo mismo con un solo pulsador.

Tienes que guardar el estado en el que está el cronómetro en una variable.
Por ejemplo, defines una variable estado que guarde 0 cuando está detenido, 1 cuando está corriendo, 2 cuando está en pausa.

Ahora, cuando lees el botón inicio, si estado es 0 solo puedes iniciar el cronómetro, nada más. Pones estado en 1 e inicias la cuenta.
En cambio si estado es 2 (está en pausa), hace el restablecimiento a 0 y pones 0 en estado.

Cuando lees el botón pausa, si estado está en 1 (está corriendo) solo puedes pausar entonces pones estado en 2 y haces la pausa, y si estado está en 2, solo puede continuar, entonces pones otra vez estado en 1 y sales de la pausa.

Otra cosa, millis() cuenta el tiempo desde que enciendes el arduino, pero si cuando inicias tu cronómetro guardas el valor de millis() en una variable y luego restas ese valor a millis() sabes los mseg transcurridos, luego puedes ir calculando hs, min, seg y cent, sin la necesidad de tener contadores separados (que además te agregan retrasos).

En definitiva, tendrías que repensar el código. :man_facepalming:t2:

Saludos

Hasta acá esta versión usa millis() solamente.
Compila, falta el POSITIVE en la inicialización del LCD pero como no has puesto de donde has extraído la librería cuesta adivinar.
Cada 100 mseg o sea cada decima presenta un valor si es que puede hacerlo.

//#include <Arduino.h>

#include <LiquidCrystal_I2C.h>//Libreria para LCD I2C
#include <Wire.h>//libreria requrida para usar SDA y SCL
LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3);

// Variables para los cálculos internos del cronómetro
int horas         = 0;
int minutos       = 0;
int segundos      = 0;
int decimas       = 0;
long milisegundos = 0;
unsigned long time_start, time_pausa, nexttime;
int start, stop, pausa, startAnt = LOW, stopAnt = LOW, pausaAnt = LOW;

const byte pulsador_inicio = 10; // pulsador_inicio en PIN digital 10
const byte pulsador_pausa  =  9; // pulsador_pausa en PIN digital 9

void millis2time() {
  unsigned long runMillis  = millis();
  unsigned long todosLosSegundos = millis()/1000;
  horas     = todosLosSegundos/3600;
  int segundosRestantes = todosLosSegundos%3600;
  minutos   = segundosRestantes/60;
  segundos  = segundosRestantes%60;
  decimas   = (runMillis/100 - todosLosSegundos*10); 
}

void presentacionLCD() {
      // Mostramos el tiempo a través del LCD      
    lcd.setCursor(1, 0);
    lcd.print("------------------");
    lcd.setCursor(1, 3);
    lcd.print("---------------r--");
    lcd.setCursor(4, 1);
    lcd.print("tiempo");
    lcd.setCursor(4,2);
    millis2time();
    char buffer[20];
    sprintf(buffer, "tiempo %02d:%02d:%02d.%d", horas, minutos, segundos, decimas);
    lcd.print(buffer);
}



void setup() {
  Serial.begin(9600); // Comienzo de la comunicación serie
  pinMode(pulsador_inicio, INPUT); // Pin digital 10 como entrada  
  pinMode(pulsador_pausa, INPUT); // Pin digital 9 como entrada
  lcd.begin(20, 4);//Inicializar la LCD 20x4
  lcd.backlight();//Encender la luz de fondo
}

void loop()  {

  // Si presionamos el pulsador de inicio se pone todo a cero
  start = digitalRead(pulsador_inicio);
  if (start && !startAnt)  {
      time_start = millis();
      nexttime = time_start+100;
  }
  startAnt = start;

  // Si presionamos el pulsador de pausa se congela el tiempo hasta que lo volvamos a presionar
  pausa = digitalRead(pulsador_pausa);
  if (pausa && !pausaAnt)  {
      time_pausa = millis();
  }
  pausaAnt = pausa;

  // stop = digitalRead(pulsador_stop);
  // if (stop && !stopAnt)  {
  //     time_stop = millis();
  // }
  // stopAnt = stop; 
  if (millis() >= nexttime) {
      presentacionLCD();
      nexttime += 100;
  }
}

Gracias por vuestras respuestas,

Me explicado mal :stuck_out_tongue:

Tengo 2 botones solamente [1 de pausa] + [1 para restablecer].
Cada vez que se enciende el Arduino, de manera automática, empieza a correr el tiempo.

Soy muy nuevo en [Arduino], e intento aprender buscando información de aquí y de allá. Para mi es un reto comprender el código y sus funciones, y compatibilizar con mi trabajo.

Si te digo la verdad, tengo el zip de la librería guardado, pero no recuerdo la web de la descarga, pero creo que no es Adafruit, es un LCD 20x4 conectado por i2c.

Gracias por vuestra ayuda, voy a probar el código y os cuento. :slight_smile:

Si, porque así lo has programado, empieza a contar cada 100 mseg sin verificar si antes has pulsado "inicio".

Saludos

En el código que te he escrito uso las teclas start y pause para que se acciones solo al presionarlas.
Tomo en cada caso el valor de millis().
Ahora me doy cuenta que al presionar start o pausa no hago lo que se pretende.

EDITO: debe ser de mis peores códigos. Acabo de descubrir que omití

 sprintf(buffer, "%02d:%02d:%02d.%d", horas, minutos, segundos, decimas);

ya lo edité.
Igualmente el código no hace mucho.

EDITADO:
Olvida el código anterior. Lleno de errores.
Esta tmb pero al menos se aproxima

#include <Arduino.h>

//#include <LiquidCrystal_I2C.h>//Libreria para LCD I2C
#include <Wire.h>//libreria requrida para usar SDA y SCL
//LiquidCrystal_I2C lcd(0x27, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE);

// Variables para los cálculos internos del cronómetro
unsigned long horas         = 0;
unsigned long minutos       = 0;
unsigned long segundos      = 0;
unsigned long decimas       = 0;
unsigned long milisegundos = 0;
unsigned long time_reestablecer, time_pausa, nexttime, tiempo_corriendo;
int reestablecer, stop, pausa, reestablecerAnt = LOW, stopAnt = LOW, pausaAnt = LOW;

const byte pulsador_inicio = 3; // pulsador_inicio en PIN digital 10
const byte pulsador_pausa  = 4; // pulsador_pausa en PIN digital 9
bool fpausa = true;
char buffer[30];

void millis2time(unsigned long tmp) {
  unsigned long todosLosSegundos = tmp/1000;
  horas     = todosLosSegundos/3600;
  unsigned long segundosRestantes = todosLosSegundos%3600;
  minutos   = segundosRestantes/60;
  segundos  = segundosRestantes%60;
  decimas   = (tmp/100 - todosLosSegundos*10); 
}

void presentacionLCD() {
      // Mostramos el tiempo a través del LCD      

    //lcd.setCursor(4, 1);
    if (!fpausa)
        millis2time(millis()-tiempo_corriendo);
    else 
        millis2time(time_reestablecer-tiempo_corriendo);
                
    sprintf(buffer, "%02ld:%02ld:%02ld.%ld", horas, minutos, segundos, decimas);
    //lcd.print(buffer);

    Serial.println(buffer);
  }


void setup() {
  Serial.begin(115200); // Comienzo de la comunicación serie
  Serial.println("Comenzando el cronometro");  
  delay(1000);
  pinMode(pulsador_inicio, INPUT_PULLUP); // Pin digital 10 como entrada  
  pinMode(pulsador_pausa, INPUT_PULLUP); // Pin digital 9 como entrada
  //lcd.begin(20, 4);//Inicializar la LCD 20x4
  //lcd.backlight();//Encender la luz de fondo
  //lcd.setCursor(1, 0);
  //lcd.print("------------------");
  //lcd.setCursor(1, 3);
  //lcd.print("------------------");
}

void loop()  {

  // Si presionamos el pulsador de pausa se congela el tiempo hasta que lo volvamos a presionar
  pausa = digitalRead(pulsador_pausa);
  if (!pausa && pausaAnt)  {    
      fpausa = false;
      nexttime = tiempo_corriendo = millis();  
      Serial.println("Inicio: "+String(tiempo_corriendo)); 
  }
  pausaAnt = pausa;

  // Si presionamos el pulsador de inicio se pone todo a cero
  reestablecer = digitalRead(pulsador_inicio);   

  if (!reestablecer && reestablecerAnt)  {
      time_reestablecer = millis();

      Serial.println("Reestablezco: "+String(time_reestablecer)); 
      fpausa = true;
  }
  reestablecerAnt = reestablecer;

  if (millis() >= nexttime && !fpausa) {
      presentacionLCD();
      nexttime += 100;
  }
}

He comentado todo lo referente a lcd asi que quita los // y lo tienes activo otra vez.
Hay cosas para mejorar aún.

Gracias por el esfuerzo, te lo agradezco mucho.

Pruebo el código después y comento los resultados.