Crónometro más contador con sensores inductivos y LCD

Buenas tardes a todos!! Encantado.

Me presento. Soy Sergio y soy recien iniciado en este mundo de Arduino.

Me ha surgido la necesidad de realizar un programa para una aplicación de estudios y aunque en el tema de electrónica ando bien y ya tengo todos los componentes preparados, en el tema de programación en lenguaje arduino y texto estructurado soy completamente Novato :sweat_smile:.

Os explico cual es el funcionamiento de la aplicación que estoy desarrollando y los componentes que utilizo para ello. Todos los componentes están probados por separado con programitas simples de ejemplo de aqui del foro y funcionan, el problema es que desconozco como desarrollar el programa para hacer funcionar todo en conjunto.

La placa que disponogo es una Arduino nano y el funcionamiento debe ser el siguiente:
Deseo realizar mediante 2 sensores inductivos que actuan como finales de carrera una especie de cronómetro + contador; es decir cuando detecta 1 sensor inductivo empieza el tiempo a contar y suma una unidad al contador y cuando detecta el otro sensor inductivo, el tiempo para de contar y así durante las veces que se necesite. La información del tiempo y cuenta será mostrada por un display LCD I2C 2x16, en una linea el tiempo y en la otra el contador.

También incluirá dos pulsadores, uno que haga reset tanto del contador como del tiempo y dejar todo a cero, y otro que puedas restar unidades del contador cuando quieras.

Si alguien pudiera ayudarme se lo agradecería mucho!! :smiley: :clap:

Encantado de entrar a formar parte de esta comunidad. Gracias a todos de antemano!!

Hola amigo, no se ve tan difícil lo que dices pero
Primero
Esto para imaginarme es como un portón eléctrico ? O algo así
Creo q te faltaría un botón que aumente y otro que disminuya y otro de reset .
Estoy bien o me equivoco.

Moderador:
Por favor, lee las Normas del foro y edita tu titulo usando uno que indique la naturaleza de tu consulta.
Lo que has puesto es considerado un título inútil.
Ver punto 5 de las normas.

Determina que LCD vas a usar sea paralelo o i2c y luego busca en Google Arduino LCD y verás librerías y tutoriales que te dirá como usarlos.
Por otro lado, lo que tienes que hacer mas alla que sean sensores magnéticos o no, funcionará con dos pulsadores asi que para tus pruebas puedes comenzar por ahi.
Usa dos pulsadores y como ya sabrás presentar datos en un LCD te recomiendo que mires en Documentación los tutoriales sobre millis().
Estan ni bien entras en la sección.

Buenos días @Surbyte.

Antes de nada gracias por la respuesta y disculpa por lo del título, soy nuevo aquí y aún no se bien como funciona esto. :sweat_smile: Ya está modificado!!

En cuanto a la electrónica que voy a utilizar tal y como he comentado ya la tengo clara y toda probada independientemente con programas individuales simples sacados de aquí del foro, el LCD es un i2C de 2x16. Mi problema es que nunca he programado en Arduino y no se como desarrollar la estructura para que todo el conjunto funcione, por eso solicito ayuda porque estoy un poco estancado. :weary:

Saludos. Gracias.

Buenas @b_paredes.

Te explico: Es como una especie de carro que entra y sale. Los dos finales de carrera son los que hacen la puesta en marcha y paro del cronómetro y a la vez 1 de ellos va sumando al contador. Es decir cuando el carro llega a un final de carrera, el cronómetro debe empezar a contar tiempo y sumar una unidad al contador y al llegar al otro final de carrera parar el cronómetro. En caso de que se quieran restar unidades manualmente del contador, habrá un pulsador que lo haga, sumar lo hace el primer final de carrera únicamente, y luego tal y como dices, un pulsador de reset que ponga todo a cero, tanto tiempo como contador.

Espero haberte aportado más información. Muchas gracias por tu interes.

Saludos.

Si. yo te he comprendido.
Tmb comprende que no hacemos códigos a medida. Debes venir y plantear dudas. Yo te he orientado.
Ve a documentación, mira como puedes usar millis() para contar usando pulsadores que es lo mismo que planteas.
Tienes muchos ejemplos útiles. Comienza por ahi y luego vamos viendo.

Buenas @Surbyte .

No pretendía que me hicierais el código, solo orientación. Imagino que por aquí todos habeís sido novatos en el mundo de arduino y habeis pasado por mi situación. :sweat_smile:

Mañana me pondré a hacer pruebas con los millis() y os voy diciendo mis avances! :blush:

Por cierto, ¿que software me recomendais para programar? ¿utilizo el IDE?

Gracias de nuevo.

El IDE está mas que bien.
Si sigues los tutoriales en Documentación sobre milllis() te aseguro que tienes 75 % o mas hecho.

Buenas tardes @Surbyte.

Tal y como me comentaste, buscando ejemplos he conseguido hacer gran parte de lo que necesito.

Ya tengo funcionando que se inicie el cronometro y sume una unidad el contador con un sensor y que se pare cuando detecta el otro y mostrarlo todo por el display.

Ahora tengo problema al intentar hacer la resta del contador con un botón y el reset de todo con otro. Al pulsar los botones no hace nada de nada.

El código del programa és el siguiente y el código de los botones está al final del todo:

  // Importar librerías
  #include <Wire.h>
  #include <LiquidCrystal_I2C.h>
  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;
  int contador = 0;
  long milisegundos = 0;

  int boton_resta = 7; // restar unidades contador
  int boton_reset = 8; // resetear contador y cronometro
  int sensor_inicio = 9; // pulsador_inicio en PIN digital 10
  int sensor_fin = 10; // pulsador_pausa en PIN digital 9

  
  void setup()
  {
    Serial.begin(9600); // Comienzo de la comunicación serie
    pinMode(sensor_inicio, INPUT_PULLUP); // Pin digital 10 como entrada  
    pinMode(sensor_fin, INPUT_PULLUP); // Pin digital 9 como entrada
    pinMode(boton_reset, INPUT); // Pin digital 8 como entrada
    pinMode(boton_resta, INPUT); // Pin digital 7 como entrada
    
    // Inicializamos el LCD
    lcd.begin(16,2);
    lcd.backlight();
  }
  void(* resetFunc) (void) = 0;//declare reset function at address 0
  
  void loop()
  {
    // Si presionamos el pulsador de inicio se pone todo a cero, se reinicia em cronómetro y se suma una unidad al contador
    if(digitalRead(sensor_inicio) == HIGH)
    {
      horas = 0;
      minutos = 0;
      segundos = 0;
      decimas = 0;
      lcd.clear();

      // Se suma una unidad al contador
      contador++;
      
      // Se muestra el contador por el LCD
      lcd.setCursor(3,1);
      lcd.print("CONTADOR:");
      lcd.print( contador);
      
      
      while(digitalRead(sensor_fin) == LOW)
      {

       // 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(4,0);
      if(horas < 10)
      {
        lcd.print("0");
      }
      lcd.print(horas);
      lcd.print(":");
      lcd.setCursor(7,0);
      if(minutos < 10)
      {
        lcd.print("0");
      }
      lcd.print(minutos);
      lcd.print(":");
      lcd.setCursor(10,0);
      if(segundos < 10)
      {
        lcd.print("0");
      }
      lcd.print(segundos);
      
    }
    }    
    }
    
    // Si se detecta el sensor de fin, se para el tiempo.
    if(digitalRead(sensor_fin) == HIGH)
    {
      delay(200);
      while(digitalRead(sensor_inicio) == LOW);
      delay(200);
      
    }
    if(digitalRead(boton_reset) == LOW)
    {
      resetFunc();
     
    }
      if(digitalRead(boton_resta) == LOW)
    {
      contador --;
    }
   
  }

Cualquier ayuda o aclaración es bien recibida. Gracias.

Para que todo esto?

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++;
        }

Si ya lleva la cuenta millis(). Volver a contar lo que ya esta contabilizado es una pérdida de tiempo.

Solo opera con dos variables, una que capture el momento en el que empiezas, tu Start o Arranque y otro en el que terminas, tu Stop o Fin. Restas millis() y listo y presentas convirtiendo millis() a h:m:s.xxxx lo que gustes.

Esta es una versión funcional, solo muestra hh:mm:ss asi que amplia a tu gusto

#include <LiquidCrystal_I2C.h>
#include <Wire.h>
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;
int contador      = 0;

unsigned long startTime, stopTime;
bool start, startAnt  = false;
bool stop, stopAnt    = false;
bool reset, resetAnt  = false;

int boton_resta   = 7;   // restar unidades contador
int boton_reset   = 8;   // resetear contador y cronometro
int sensor_inicio = 9; // pulsador_inicio en PIN digital 10
int sensor_fin    = 10;   // pulsador_pausa en PIN digital 9
bool flag = false;
bool freset = false;

// estas variables son para simular
byte estado = 0;
unsigned long demoro;

//char *convertirSegundosAHMmSs(long seconds) {
void convertirSegundosAHMmSs(unsigned long seconds) {
    static unsigned long secondsAnt = 0;
    char tmp[20];

    
    int s = seconds % 60;
    int m = (seconds / 60) % 60;
    int h = (seconds / (60 * 60)) % 24;
    sprintf(tmp, "%02d:%02d:%02d", h,m,s);
    if (seconds != secondsAnt) {
        Serial.println("Recibidos: " + String(seconds));
        Serial.println(tmp);
    }
    secondsAnt = seconds;  
    //return tmp;
}

void setup() {
  Serial.begin(115200);                   // Comienzo de la comunicación serie
  Serial.println("Iniciando programa.");
  pinMode(sensor_inicio, INPUT_PULLUP); // Pin digital 10 como entrada
  pinMode(sensor_fin, INPUT_PULLUP);    // Pin digital 9 como entrada
  pinMode(boton_reset, INPUT);          // Pin digital 8 como entrada
  pinMode(boton_resta, INPUT);          // Pin digital 7 como entrada

  // Inicializamos el LCD
  lcd.begin(16, 2);
  lcd.backlight();
}
void (*resetFunc)(void) = 0; // declare reset function at address 0

void loop() {
  // Si presionamos el pulsador de inicio se pone todo a cero, se reinicia em
  // cronómetro y se suma una unidad al contador
  //start = digitalRead(sensor_inicio);
 // stop  = digitalRead(sensor_fin);
  //reset = digitalRead(boton_reset);
  // switch(estado) {
  //   case 0: // arranco
  //             start = true;
  //             estado = true;
  //             demoro = millis();
  //             break;
  //   case 1:   start = 0;
  //             estado = 2;
  //             break;
  //   case 2:   if (millis() - demoro > 10000UL) {
  //                 stop = true;
  //                 estado = 3;
  //             }
  //             break;
  //   case 3:   stop = 0;
  //             estado = 4;
  //             demoro = millis();
  //             break;
  //   case 4:   if (millis() - demoro > 4000UL) {
  //                 reset = true;
  //                 estado = 5;
  //             }
  //             break;
  //   case 5:   if (millis() - demoro > 5000UL) {
  //                 reset = false;
  //                 estado = 6;
  //             }
  //             break;
  //   case 6:   // vuelvo a empezar
  //             estado = 0;
  //             break;

  // }

  if (start && !startAnt) {   // siempre usa este criterio. El de mirar el flanco no un estado.
      startTime = millis();
      flag = true;
      freset = false;
  }
  startAnt = start;
  
  if (stop && !stopAnt) {   // siempre usa este criterio. El de mirar el flanco no un estado.
      stopTime = millis()-startTime;
      flag = false;
  }
  stopAnt = stop;

  if (reset && !resetAnt) {   // siempre usa este criterio. El de mirar el flanco no un estado.
      freset = true;
  }
  resetAnt = reset;

  if (freset) 
      //Serial.println(convertirSegundosAHMmSs(0));
      convertirSegundosAHMmSs(0);
  else {
      if (flag) {
          //Serial.println(convertirSegundosAHMmSs(startTime/1000));
          convertirSegundosAHMmSs(startTime/1000);
      }
      else {
          //Serial.println(convertirSegundosAHMmSs(stopTime/1000));
          convertirSegundosAHMmSs(stopTime/1000);
      }
        
  } 
}

Buenas.

Tal y como comenté al principio del post, soy nuevo en arduino y no tengo conocimientos en programación, por lo que practicamente no entiendo el programa que me has pasado :sweat_smile:. Tu me animaste a mirar ejemplos y eso hice hasta entre unos y otros conseguir prácticamente hacer funcionar el programa que deseo.

El problema que tengo actualmente no es con los millis, imagino que la manera de programarlo no será la mejor, pero es extraida de otro hilo de este foro y funciona y la suma del contador de unidades también. El problema que tengo es que necesito realizar resta de unidades y además realizar un reset completo del programa con 2 botones y no se como realizarlo.

He probado a utilizar el código que tu me has pasado y tengo varios problemas:

  • Si copio y subo el programa tal cual, no recibo nada por el serial, unicamente "Iniciando programa", ya que mucha parte del código está como comentada con // y no funciona.

  • Si quito la parte comentada con // recibo los siiguientes errores:

Arduino:1.8.16 (Windows 10), Tarjeta:"Arduino Nano, ATmega328P"

C:\Users\DOMOTECNIC\Documents\Arduino\sketch_dec14b\sketch_dec14b.ino: In function 'char* convertirSegundosAHMmSs(long int)':

sketch_dec14b:29:53: error: a function-definition is not allowed here before '{' token

 void convertirSegundosAHMmSs(unsigned long seconds) {

                                                     ^

sketch_dec14b:46:14: error: a function-definition is not allowed here before '{' token

 void setup() {

              ^

sketch_dec14b:60:13: error: a function-definition is not allowed here before '{' token

 void loop() {

             ^

sketch_dec14b:132:1: error: expected '}' at end of input

 }

 ^

Se encontraron varias bibliotecas para "Wire.h"

Usado: C:\Users\DOMOTECNIC\Documents\Arduino\libraries\Wire

 No usado: C:\Program Files (x86)\Arduino\hardware\arduino\avr\libraries\Wireold

exit status 1

a function-definition is not allowed here before '{' token

Estoy mega atascado y no se por donde seguir la verdad.... :weary:

Gracias de todas formas.

La resta de unidades esta aca

stopTime = millis()-startTime;

Restas el tiempo actual en millis() menos el que tomaste en el arranque.

Que librería LiquidCrystal_I2C.h usas? dime de quien es y si tienes un link mejor.

Acabo de probar el código en el IDE porque yo uso PlatformIO y este es el resultado

El Sketch usa 8940 bytes (3%) del espacio de almacenamiento de programa. El máximo es 253952 bytes.
Las variables Globales usan 522 bytes (6%) de la memoria dinámica, dejando 7670 bytes para las variables locales. El máximo es 8192 bytes.

Vuelvo a poner el código

#include <Wire.h>
#include <LiquidCrystal_I2C.h>      // https://github.com/fmalpartida/New-LiquidCrystal
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;
int contador      = 0;

unsigned long startTime, stopTime;
bool start, startAnt  = false;
bool stop, stopAnt    = false;
bool reset, resetAnt  = false;

int boton_resta   = 7;   // restar unidades contador
int boton_reset   = 8;   // resetear contador y cronometro
int sensor_inicio = 9; // pulsador_inicio en PIN digital 10
int sensor_fin    = 10;   // pulsador_pausa en PIN digital 9
bool flag = false;
bool freset = false;

// estas variables son para simular
byte estado = 0;
unsigned long demoro;

//char *convertirSegundosAHMmSs(long seconds) {
void convertirSegundosAHMmSs(unsigned long seconds) {
    static unsigned long secondsAnt = 0;
    char tmp[20];

    
    int s = seconds % 60;
    int m = (seconds / 60) % 60;
    int h = (seconds / (60 * 60)) % 24;
    sprintf(tmp, "%02d:%02d:%02d", h,m,s);
    if (seconds != secondsAnt) {
        Serial.println("Recibidos: " + String(seconds));
        Serial.println(tmp);
    }
    secondsAnt = seconds;  
    //return tmp;
}

void setup() {
  Serial.begin(115200);                   // Comienzo de la comunicación serie
  Serial.println("Iniciando programa.");
  pinMode(sensor_inicio, INPUT_PULLUP); // Pin digital 10 como entrada
  pinMode(sensor_fin, INPUT_PULLUP);    // Pin digital 9 como entrada
  pinMode(boton_reset, INPUT);          // Pin digital 8 como entrada
  pinMode(boton_resta, INPUT);          // Pin digital 7 como entrada

  // Inicializamos el LCD
  lcd.begin(16, 2);
  lcd.backlight();
}
void loop() {
  // Si presionamos el pulsador de inicio se pone todo a cero, se reinicia em
  // cronómetro y se suma una unidad al contador
  if (start && !startAnt) {   // siempre usa este criterio. El de mirar el flanco no un estado.
      startTime = millis();
      flag = true;
      freset = false;
  }
  startAnt = start;
  
  if (stop && !stopAnt) {   // siempre usa este criterio. El de mirar el flanco no un estado.
      stopTime = millis()-startTime;
      flag = false;
  }
  stopAnt = stop;

  if (reset && !resetAnt) {   // siempre usa este criterio. El de mirar el flanco no un estado.
      freset = true;
  }
  resetAnt = reset;

  if (freset) 
      //Serial.println(convertirSegundosAHMmSs(0));
      convertirSegundosAHMmSs(0);
  else {
      if (flag) {
          //Serial.println(convertirSegundosAHMmSs(startTime/1000));
          convertirSegundosAHMmSs(startTime/1000);
      }
      else {
          //Serial.println(convertirSegundosAHMmSs(stopTime/1000));
          convertirSegundosAHMmSs(stopTime/1000);
      }
  } 
}

NOTA:
Revisa porque has tomado un código que luego cambié.

Buenas tardes @Surbyte . Disculpa por la demora al contestar.

El código de ejemplo que utilicé para hacer el programa lo cogí de este blog:

Y la librería también que es del mismo chico, Mariano del campo creo que se llama en el siguiente enlace:

¿Crees que puedo tener un problema con la librería?¿Donde podría encontrarla funcionando correctamente?

Saludos. Gracias.

Yo hice un programa, dediqué tiempo, lo probaste?