fecha, hora, contador de pulsos y sumatorio de los pulsos

buenas noches foro,

mi pregunta es sencilla, de que manera enfocaríais la programación para mostrar por serial (y guardar en una SD por ejemplo, línea a línea) cada50 segundos por ejemplo los siguientes datos:

(en una sóla línea): la fecha y hora del reloj RTC, el tiempo total de los pulsos que haya, el nº total de pulsos.

Algo estoy haciendo mal, la hora se muestra correctamente cada 5 segundos en el ejemplo, pero los pulsos realizados en ese intervalo de tiempo NO son contabilizados, mostrando únicamente como un segundo de pulso, cuando en realidad han sido 5…

aislando el código de los pulsos me funciona:

//modificado de https://giltesa3.wordpress.com/2017/08/21/contabilizar-horas-minutos-y-segundos-de-un-pin-activo-del-arduino/

HardwareSerial &pc = Serial;
const byte pLED = 19;
unsigned int totalSeconds = 0, hours, minutes, seconds;
const int timeThreshold = 1000;
const int intPin = 19;
volatile int ISRCounter = 1;
int counter = 1;
long startTime = 0;


void setup()
{
  pinMode(pLED, OUTPUT);
  pinMode(intPin, INPUT_PULLUP);
  /*
   * Debounce por software
El debounce por software tiene la ventaja de no requerir componentes adicionales. Resolvemos el rebote únicamente modificando el código de nuestro programa.
Como desventaja, incrementa levemente el tiempo de ejecución y la complejidad del código. Además, si no aplicamos el código correctamente podemos ignorar interrupciones "verdaderas".
La forma más sencilla de aplicar un debounce por software es comprobar el tiempo entre disparos de la interrupción. Si el tiempo es inferior a un determinado umbral de tiempo (threshold) 
simplemente ignoramos la interrupción. En definitiva, hemos definido una "zona muerta" en la que ignoramos las interrupciones generadas.
Para aplicar el debounce por software, modificamos la función ISR de la siguiente forma.
   */
  attachInterrupt(digitalPinToInterrupt(intPin), debounceCount, CHANGE);
  /*
   *  Parámetros
interrupt:  el número de interrupción (int)
pin:  el número de pin (solo Arduino Due, Zeo, MKR1000)
USR:   la ISR llamada cuando sucede la interrupción; esta función no debe tener parámetros ni devolver nada. Esta función a veces se denomina Rutina del Servicio         de Interrupciones.
mode:   define cuando debe ser disparada la interrupción. Existen cuatro constantes predefinidas como valores válidos: 
    LOW dispara la interruoción cuando el pin está a nivel bajo.
    CHANGE dispara la interrupción cuando el pin cambia su valor.
    RISING dispara la interrupción cuando el pin pasa de nivel bajo a nivel alto.
    FALLING dispara la interrupción cuando el pin pasa de nivel alto a nivel bajo.
   */
  pc.begin(9600);
  while (!pc);
  // pc.println("Contador de tiempo de LED 13 activo (H:M:S)");
}

void loop()
{
  static unsigned long tNow      = 0;
  static unsigned long tPrevious = 0;

  tNow = millis();

  if ( digitalRead(pLED) && tNow - tPrevious >= timeThreshold )
  {
    tPrevious     = tNow;
    totalSeconds += 1;
    hours         = totalSeconds / 3600;
    minutes       = totalSeconds % 3600 / 60;
    seconds       = totalSeconds % 60;
  }
//https://giltesa3.wordpress.com/2017/08/21/contabilizar-horas-minutos-y-segundos-de-un-pin-activo-del-arduino/
  pc.print(hours < 10 ? "0" : "");
  pc.print(hours);
  pc.print(":");
  pc.print(minutes < 10 ? "0" : "");
  pc.print(minutes);
  pc.print(":");
  pc.print(seconds < 10 ? "0" : "");
  pc.print(seconds);
  pc.print(" - ");
  pc.println(counter);
 
 if (counter != ISRCounter)
   {
    counter = ISRCounter;
    counter = round(counter/2);
    // Serial.println(counter);
  }
  delay(1000);

}

void debounceCount()
{
  if (millis() - startTime > timeThreshold)
  {
    ISRCounter++;
    startTime = millis();
  }
}

pero cuando uno el programa para mostrar fecha y hora, los impulsos no son contabilizados correctamente…

me arroja el siguiente resultado:

 22/01/2020 | 21:45:57 | Tº sistema: 22.00C | 00:00:02 - 1 | 
 22/01/2020 | 21:46:02 | Tº sistema: 22.00C | 00:00:03 - 1 | 
 22/01/2020 | 21:46:07 | Tº sistema: 22.00C | 00:00:04 - 2 | 
 22/01/2020 | 21:46:12 | Tº sistema: 22.00C | 00:00:05 - 2 | 
 22/01/2020 | 21:46:17 | Tº sistema: 22.00C | 00:00:06 - 2 | 
 22/01/2020 | 21:46:22 | Tº sistema: 22.00C | 00:00:06 - 3 | 
 22/01/2020 | 21:46:27 | Tº sistema: 22.00C | 00:00:06 - 3 | 
 22/01/2020 | 21:46:32 | Tº sistema: 22.00C | 00:00:06 - 3 |

Para la hora y fecha estoy usando libreria DS3231.h y el siguiente código:

// modificado de https://polaridad.es/libreria-arduino-fecha-hora-temperatura-rtc-ds3231-i2c/
#define INTERVALO_MEDICION 100000 // Medir temperatura cada 100 segundos (se renueva internamente en el DS3231 cada 64 segundos)
#define ESPERA_ERROR 1000 // Tiempo de espera antes de volver a medir si se ha producido un error
#define ELEMENTOS_MATRIZ_FECHA 7

#include "DS3231.h"
#include <Wire.h>

char buffer_fecha[ELEMENTOS_MATRIZ_FECHA];
char *puntero_fecha;
float temperatura;
unsigned long cronometro;
byte contador;
String dia_semana[]={"lunes","martes","miércoles","jueves","viernes","sábado","domingo"};
DS3231 reloj;

void setup()
{
 Serial.begin(9600);
 Wire.begin(); // Inicializar Wire sólo si no se hace dentro del constructor (de la librería) Este método, hacerlo en la aplicación, supone que se usa Wire para comunicar con otros dispositivos, no sólo con el DS3231
 cronometro=0; // para que empiece inmediatamente
}

void loop()
{
 if(millis()>cronometro)
 {
 temperatura=reloj.leer_temperatura();
 if(temperatura>reloj.temperatura_maxima()||temperatura<reloj.temperatura_minima())
 {
 cronometro=millis()+ESPERA_ERROR;
 }
 else
 {
 cronometro=millis()+INTERVALO_MEDICION;
 reloj.cargar_fecha_hora();
 puntero_fecha=reloj.valor_fecha_hora();
 for(contador=0;contador<ELEMENTOS_MATRIZ_FECHA;contador++)
 {
 buffer_fecha[contador]=*(puntero_fecha+contador);
 Serial.println("Contenido de la posición "+String(contador,DEC)+" del buffer de la fecha -> "+String(int(buffer_fecha[contador]),DEC));
 }
 Serial.print("El día ");
 Serial.print(reloj.fecha_humana());
 Serial.print(", ");
 Serial.print(dia_semana[reloj.numero_dia_semana()-1]);
 Serial.print(", a las ");
 Serial.println(reloj.hora_humana());
 Serial.print("(");
 Serial.print(reloj.reloj_4_digitos_7_segmentos());
 Serial.print(" en un reloj de 4 dígitos y ");
 Serial.print(reloj.fecha_hora_MySQL());
 Serial.print(" según MySQL o ");
 Serial.print(reloj.fecha_hora_compacta());
 Serial.println(" abreviadamente)");
 Serial.print("la temperatura era de ");
 Serial.print(temperatura); // Mostrar la temperatura
 Serial.println(" grados centígrados");
 }
 }
}

me pueden indicar en que estoy fallando?
muchas gracias

Primer enfoque: leer el pulso bien.

Mira lo que hace tu rutina de interrupción:

void debounceCount()
{
  if (millis() - startTime > timeThreshold)
  {
    ISRCounter++;
    startTime = millis();
  }
}

timeThreshold vale 1000, por lo tanto, ya te puedes cansar de apretar veces el botón, que solo funcionara cuando haya transcurrido ese segundo, y solo contará ese pulso, ni uno mas...

Estar forma de leer un botón no es nada elegante, y un despilfarro de tecnología para algo que no necesita una interrupción. Pasate por la sección DOCUMENTACION y verás el tutorial que hice: Como NO leer un botón y como SI debemos hacerlo..

En cuanto a lo de guardar los datos... Si estas haciendo una cosa cada segundo, y entiendes lo que hace:

  if ( digitalRead(pLED) && tNow - tPrevious >= timeThreshold )
  {
    tPrevious     = tNow;
    totalSeconds += 1;
    hours         = totalSeconds / 3600;
    minutes       = totalSeconds % 3600 / 60;
    seconds       = totalSeconds % 60;
  }

¿qué te impide hacer otro código similar para un tiempo de 50000 milisegundos?

En el segundo código no veo por ningún lado que midas pulsos y es un código totalmente distinto al primero y tampoco veo claro lo que pretendes hacer con él.

Por ejemplo:

      reloj.cargar_fecha_hora();
      puntero_fecha = reloj.valor_fecha_hora();

Me extraña que eso compile. reloj es un objeto DS3231 y me extraña que hayas hecho una libreria para él.

Creo que el problema es que estas mezclando código al tun tun... y claro eso no va a funcionar por ningún lado, distribuye el trabajo en el mismo código: leer pulsos, leer reloj, crear temporizadores que hagan lo que quieres cada cierto tiempo. Y sobretodo olvidate del copy+paste.

Gracias por tu tiempo. Los codigos por separado fucionan con las adaptaciones con los requisitos que necesito, cuanfo intento fusionar los dos, es cuando empiezo con problemas. Me leeré el link que me pasas, llevo muy mal lo de las interrupciones y los millis... No soy programador y voy aprendiendo poco a poco

Estoy ahora con el ejercicio 1 tuyo de contar el tiempo que esta pulsado un boton, si quisiera el tiempo total, de todas las pulsaciones, deberia crear otra variable verdad?

te respondí en la sección de DOCUMENTACION

muchisimas gracias, habiendo simplificado muchísimo el código de contabilizar los pulsos…

#include <BotonSimple.h>


BotonSimple boton(19);
unsigned long inicio, fin, total;
int contador;
   
void setup() {
  Serial.begin(9600);
  total = 0;
  contador = 0;
}
 
void loop() {
  boton.actualizar();
  if ( boton.leer()==APRETANDOLO )
    inicio = millis();
  if ( boton.leer()==SOLTANDOLO ) {
    fin = millis();
    total += (fin-inicio);
    contador++;
    Serial.print("El boton ha estado pulsado: "); Serial.println(fin-inicio);
    Serial.print("Total de tiempo que el botón ha estado pulsado: "); Serial.println(total);
    Serial.print("El número de pulsaciones es ahora: "); Serial.println(contador);
  }
}

ahora, si quiero darle una vuelta de más, si instalo un reloj rtc, el DS3231 por ejemplo (iré paso a paso, pero toda esta informació a la larga me gustaría guardarla en una SD cada cierto tiempo, lo digo por el replantear la forma de realizar el código), ¿debo seguir usando la funcion millis para seguir ejecutando codigo, de tal manera que me gustaría poder hacer foto del momento justo cuando es presionado el pulsador, no se si me explico… intento hacerlo y te comento…

El uso de millis() no afecta el que luego o mientras consultes el RTC para saber el tiempo en el que el evento se produce.

Para guardar cada X tiempo vuelves a usar millis() y entonces consultas al RTC sobre que hora es y estampas todos los datos en la SD mas tus sensores.