Desde la publicación en polaridad.es de la librería de Arduino para consultar fecha y temperatura del integrado DS3231 por I2C muchos usuarios han preguntado sobre su uso y sobre cómo gestionar con ella la fecha y la hora, no necesariamente con el RTC DS3231 sino de forma genérica.
Como estas cuestiones parecen necesarias para explotar un reloj en tiempo real, en este artículo trataré de aclararlas con el enfoque didáctico de costumbre, que me parece más valioso que simplemente dar la solución a la descarga aunque, por supuesto, quien lo prefiera, también podrá simplemente descargar las nuevas librerías al final del artículo.
Problemas de conexión
Para empezar aclarando las dudas más sencillas, el principal problema que he encontrado en los usuarios que me consultan está relacionado con la conexión I²C, tanto hardware como software. El problema físico no suele darse cuando se usa el módulo (suponiendo que todo se conecta en su sitio) pero al hacer un circuito con el DS3231 sin otras comunicaciones I²C parece un clásico olvidar las resistencias de pull-up en el bus. Ante la duda, estas resistencias pueden ser de 10 KΩ aunque, como es lógico, se pueden calcular considerando la tensión de alimentación y la intensidad en las patillas correspondientes.
Por otra parte, el error clásico de software usando Arduino es olvidar iniciar las comunicaciones o hacerlo varias veces si hay varios dispositivos. Para dejarlo definitivamente claro: sólo un Wire.begin()
ya esté en el programa principal o el la librería, pero uno y sólo uno.
Optimización de la librería DS3231
Añadir nuevas funcionalidades a la librería va a plantear un problema nuevo: la necesidad de más memoria. Como las series pequeñas de Arduino ya van un poco justas, lo más sensato será optimizar su uso, por un lado para usar lo menos posible y por otro para poder eliminar de la versión de la librería que se use en un proyecto concreto los métodos que no sean necesarios. Esto último resulta una obviedad pero es muy importante y por la comodidad de trabajo que supone el uso de librerías con Arduino se nos olvida frecuentemente… hasta que nos quedamos sin memoria.
Para conseguir este objetivo el primer paso es separar los componentes de la librería de Arduino para el DS3231 de manera que sólo se encargue de la fecha, la hora y la temperatura, y crear una nueva librería, independiente de la anterior, que sirva para la gestión genérica de fecha y hora. Una ventaja añadida al ahorro de memoria es que se podrán usar estas nuevas funciones para otras tareas que no necesariamente impliquen al RTC DS3231 y además será mucho más fácil para los usuarios eliminar los componentes que no necesiten (por ejemplo la gestión de la temperatura) de una y otra librerías para economizar memoria.
Para hacer más compacta la consulta ya no es necesario cargar primero la hora del DS3231 y luego consultar el valor. Ahora la función cargar_fecha_hora()
devuelve un puntero al valor de la fecha y la hora. De todas formas, valor_fecha_hora()
se conserva por compatibilidad con la versión anterior de la librería de Arduino para el DS3231.
El siguiente código es el de la nueva librería y más abajo un ejemplo de aplicación, por ahora sin usar la librería de gestión de fechas, solo la fecha y hora del RTC DS3231.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
//DS3231.h #if defined(ARDUINO) && ARDUINO>=100 #include "Arduino.h" #else #include "WProgram.h" #endif #include <Wire.h> #define SEGUNDO 0 // Posición de los segundos en el registro del DS3231 #define MINUTO 1 // Posición de los minutos en el registro del DS3231 #define HORA 2 // Posición de las horas en el registro del DS3231 #define SEMANA 3 // Posición del día de la semana en el registro del DS3231 #define DIA 4 // Posición del día en el registro del DS3231 #define MES 5 // Posición del mes en el registro del DS3231 #define ANO 6 // Posición del año en el registro del DS3231 #define TEMPERATURA_MAXIMA_DS3231 85.0 // Máxima temperatura que se puede medir con un DS3231 (70 grados en la versión comercial / no-industrial según datasheet) #define TEMPERATURA_MINIMA_DS3231 -40.0 // Mínima temperatura que se puede medir con un DS3231 (0 grados en la versión comercial / no-industrial según datasheet) #define DIRECCION_DS3231 B1101000 // Según datasheet #define TIMEOUT_I2C_DS3231 200 // Máximo tiempo de espera del bus I2C del DS3231 #define NUMERO_ELEMENTOS_FECHA 7 // Número de elementos (un byte por elemento) que tiene la matriz con los datos de la fecha para el DS3231 #define NUMERO_BYTES_TEMPERATURA 2 // Número de bytes con los que se representa la temperatura (uno para la parte entera y el signo y otro para la parte decimal representada con una resolución de 0.25 grados) #define RESOLUCION_DECIMALES_DS3231 0.25 // Grados de cada paso de la parte decimal #define ROTACION_DECIMALES 6 // Rotación necesaria hasta llegar a los bits que contienen la parte que representa los decimales de la temperatura (rotar 6 corresponde a atender a los bits 7 y 8) #define MASCARA_DECIMALES B11000000 // Máscara para eliminar con una operación and la parte no significativa. En el caso el DS3231 no hace nada ya que al rotar queda sólo la parte relevante. class DS3231 { private: char valor_fecha_hora_DS3231[NUMERO_ELEMENTOS_FECHA]; // Matriz de valores numéricos (7 char) de la fecha y la hora. El índice 0 representa los segundos, el 1 los minutos, el 2 las horas (en formato de 24), el 3 el día de la semana empezando en el domingo que es 1, el 4 el día del mes, el 5 el número del mes y el 6 los dos últimos dígitos del año char bcd_a_decimal(char bcd); // Convertir de BCD a decimal char decimal_a_bcd(char decimal); // Convertir de decimal a BCD void grabar_registro_DS3231(char *fecha,char inicio,char longitud); // Grabar el registro de fecha y hora del DS3231 protected: public: DS3231(); ~DS3231(); char *cargar_fecha_hora(); char *valor_fecha_hora(); void grabar_fecha_hora(char *hora); double leer_temperatura(); double temperatura_minima(); double temperatura_maxima(); }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
//DS3231.cpp #include "DS3231.h" DS3231::DS3231() // Constructor { char contador; for(contador=0;contador<NUMERO_ELEMENTOS_FECHA;contador++) { valor_fecha_hora_DS3231[contador]=0; } } DS3231::~DS3231() // Destructor { } char *DS3231::cargar_fecha_hora() { unsigned long timeout_i2c; char contador; Wire.beginTransmission(DIRECCION_DS3231); // Comunicar con el DS3231 en la dirección correspondiente Wire.write(0x00); // pedir registros desde la primera dirección Wire.endTransmission(); // Liberar el bus I2C Wire.requestFrom(DIRECCION_DS3231,NUMERO_ELEMENTOS_FECHA); // esperar NUMERO_ELEMENTOS_FECHA bytes timeout_i2c=millis()+TIMEOUT_I2C_DS3231; while(Wire.available()<NUMERO_ELEMENTOS_FECHA&&millis()<timeout_i2c){} // Esperar a que lleguen los datos o a que pase el tiempo mínimo de espera // Para usar sin espera: if(Wire.available()) for(contador=0;contador<NUMERO_ELEMENTOS_FECHA;contador++) { valor_fecha_hora_DS3231[contador]= Wire.read(); // Leer todos los datos sin discriminar aunque luego tendrán distinto tratamiento } valor_fecha_hora_DS3231[SEGUNDO]=bcd_a_decimal(valor_fecha_hora_DS3231[0]); // segundos en BCD valor_fecha_hora_DS3231[MINUTO]=bcd_a_decimal(valor_fecha_hora_DS3231[1]); // minutos en BCD valor_fecha_hora_DS3231[HORA]=((valor_fecha_hora_DS3231[2]&B00110000)>>4)*10+(valor_fecha_hora_DS3231[2]&B00001111); // BCD en modo de 24 horas valor_fecha_hora_DS3231[SEMANA]=valor_fecha_hora_DS3231[3]&B00000111; // Número de día de la semana empezando en 1 que es domingo valor_fecha_hora_DS3231[DIA]=((valor_fecha_hora_DS3231[4]&B00110000)>>4)*10+(valor_fecha_hora_DS3231[4]&B00001111); // Número del día del mes valor_fecha_hora_DS3231[MES]=((valor_fecha_hora_DS3231[5]&B00010000)>>4)*10+(valor_fecha_hora_DS3231[5]&B00001111); // Número de mes (sin MSB) valor_fecha_hora_DS3231[ANO]=bcd_a_decimal(valor_fecha_hora_DS3231[6]); // Año en BCD (dos últimos dígitos) return valor_fecha_hora_DS3231; } char *DS3231::valor_fecha_hora() { return valor_fecha_hora_DS3231; } void DS3231::grabar_registro_DS3231(char *fecha,char inicio,char longitud) { char contador; Wire.beginTransmission(DIRECCION_DS3231); // Comunicar con el DS3231 en la dirección correspondiente Wire.write(inicio); // Empezar el envío en la dirección indicada (el registro empieza en 0x00) for(contador=0;contador<longitud;contador++) { Wire.write(decimal_a_bcd(fecha[contador])); // Escribir cada valor expresándolo en BCD } Wire.endTransmission(); // Liberar el bus I2C } void DS3231::grabar_fecha_hora(char *fecha) { grabar_registro_DS3231(fecha,SEGUNDO,NUMERO_ELEMENTOS_FECHA); } double DS3231::leer_temperatura() { byte msb; // El byte más significativo contiene la parte entera de la temperatura (en complemento a 2 para poder representar temperaturas bajo cero) byte lsb; // El byte menos significatico contiene la parte decimal con una resolución de un cuarto de grado float temperatura=TEMPERATURA_MAXIMA_DS3231+1.0; // Un número mayor que el máximo como aviso de que algo va mal boolean negativo=false; // Inicialmente se considera postivo unsigned long timeout_i2c; Wire.beginTransmission(DIRECCION_DS3231); // Preparar el dispositivo Wire.write(0x11); // Solicitar temperatura (empieza en 11h y termina en 12h) Wire.endTransmission(); Wire.requestFrom(DIRECCION_DS3231,NUMERO_BYTES_TEMPERATURA); // Esperar temperatura: pedir dos bytes en la dirección del integrado timeout_i2c=millis()+TIMEOUT_I2C_DS3231; while(Wire.available()<NUMERO_BYTES_TEMPERATURA&&millis()<timeout_i2c){}// Esperar a que lleguen los datos o pase el tiempo de espera máximo // Para usar sin espera: if(Wire.available()) msb=Wire.read(); // parte entera con signo en complemento a dos lsb=Wire.read(); // parte fraccional con resolución de 0.25 grados negativo=msb>B01111111; // Es negativo si el primer dígito es uno temperatura=msb&B01111111; // revertir complemento a dos temperatura+=((lsb&MASCARA_DECIMALES)>>ROTACION_DECIMALES)*RESOLUCION_DECIMALES_DS3231; // atender sólo a los bits que contienen la parte decimal (7 y 8), multiplicar por el paso de la resolución y sumar a la parte entera de la temperatura if(negativo) { temperatura*=-1; // Cambiar el signo } return temperatura; } double DS3231::temperatura_minima() { return TEMPERATURA_MINIMA_DS3231; } double DS3231::temperatura_maxima() { return TEMPERATURA_MAXIMA_DS3231; } char DS3231::bcd_a_decimal(char bcd) // Convertir de BCD a decimal { return ((bcd&B11110000)>>4)*10+(bcd&B00001111); } char DS3231::decimal_a_bcd(char decimal) // Convertir de decimal a BCD { return decimal/10*16+(decimal%10); } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
#define INTERVALO_MEDICION 10000 // Intervalo entre lecturas sucesivas de la fecha y la hora #define ESPERA_ERROR 1000 // Tiempo de espera antes de volver a medir si se ha producido un error #define LONGITUD_BUFFER_DS3231 7 // Candidad de caracteres que tiene la fecha en los registros del DS3231 #define LONGITUD_FECHA_FORMATEADA 18 // Cantidad de caracteres de la fecha con aspecto humano #include "DS3231.h" #include <Wire.h> char buffer_fecha[LONGITUD_BUFFER_DS3231]; // {segundo,minuto,hora,semana,dia,mes,año} char *puntero_fecha; char buffer_fecha_formateada[LONGITUD_FECHA_FORMATEADA]; unsigned long cronometro; DS3231 reloj; void setup() { Serial.begin(9600); while(!Serial){};// Esperar a Arduino Leonardo 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) // Si ha pasado el tiempo entre lecturas, cargar la fecha y la hora y mostrarlas en la consola serie { cronometro=millis()+INTERVALO_MEDICION; puntero_fecha=reloj.cargar_fecha_hora(); //puntero_fecha=reloj.valor_fecha_hora(); // Ya no es necesario consultar la función valor_fecha_hora() porque cargar_fecha_hora() también devuelve un puntero al valor. De todas formas valor_fecha_hora() se conserva por compatibilidad con la versión anterior sprintf ( buffer_fecha_formateada, "%02d/%02d/%02d %02d:%02d:%02d", int(*(puntero_fecha+4)), int(*(puntero_fecha+5)), int(*(puntero_fecha+6)), int(*(puntero_fecha+2)), int(*(puntero_fecha+1)), int(*(puntero_fecha+0)) ); Serial.println(buffer_fecha_formateada); } } |
Como puede verse en la anterior captura de pantalla, la suma del programa de ejemplo, la librería Wire
y la nueva librería ocupan 8340 bytes de programa y 433 bytes de memoria dinámica.
Sincronizar el montaje leyendo la fecha y la hora desde el puerto serie
Otro problema clásico (y consulta habitual de los usuarios) es la primera puesta en hora del DS3231 cuando está alimentado por baterías (se supone que seguirá en hora después) y no dispone de ningún recurso para sincronizarse más que leer «manualmente» la hora.
Una forma sencilla de dar respuesta a esta necesidad es leyendo del puerto serie una entrada con una codificación esquemática de la fecha y la hora. Como ejemplo he elegido el formato {ssmmhhDDMMAA} en el que «ss» representa el valor de los segundos, «mm» el de los minutos, «hh» la hora (en formato de 24/día), «DD» el día del mes, «MM» el número del mes y «AA» el año. Todos los valores usan dos dígitos, completados con un cero a la izquierda si fuera el caso, y del año sólo se usan los dos últimos dígitos supuesto siglo XXI. Para representar, por ejemplo, el 25 de marzo de 2016 a las 17:18:19 se enviaría por el puerto serie el texto {191817250316}
Ya que la operación de puesta en hora «manual» no se realiza en el funcionamiento normal he preferido no incluir la función en la librería para el DS3231 aunque nada impide usarla en un montaje definitivo sin más que incluirla en el código como se hace en el ejemplo siguiente.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
#define INTERVALO_MEDICION 10000 // Intervalo entre lecturas sucesivas de la fecha y la hora #define ESPERA_ERROR 1000 // Tiempo de espera antes de volver a medir si se ha producido un error #define LONGITUD_BUFFER_DS3231 7 // Candidad de caracteres que tiene la fecha en los registros del DS3231 #define LONGITUD_FECHA_FORMATEADA 18 // Cantidad de caracteres de la fecha con aspecto humano #include "DS3231.h" #include <Wire.h> #include "recibir_fecha.h" char buffer_fecha[LONGITUD_BUFFER_DS3231]={0,0,0,0,0,0,0}; // {segundo,minuto,hora,semana,dia,mes,año} char *puntero_fecha; char contador_fecha; char buffer_serie; bool nueva_fecha=false; char buffer_fecha_formateada[LONGITUD_FECHA_FORMATEADA]; unsigned long cronometro; DS3231 reloj; void setup() { Serial.begin(9600); while(!Serial){};// Esperar a Arduino Leonardo 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(Serial.available()>0) // Si hay datos disponibles en el puerto serie cargarlos y tratar de formar una fecha con ellos { buffer_serie=Serial.read(); nueva_fecha=recibir_fecha(buffer_serie,buffer_fecha); } if(nueva_fecha) // Si se ha cargado una nueva fecha por el puerto serie, grabarla en el DS3231 y esperar otra { reloj.grabar_fecha_hora(buffer_fecha); for(contador_fecha=0;contador_fecha<LONGITUD_BUFFER_DS3231;contador_fecha++) { buffer_fecha[contador_fecha]=0; } nueva_fecha=false; } if(millis()>cronometro) // Si ha pasado el tiempo entre lecturas, cargar la fecha y la hora y mostrarlas en la consola serie { cronometro=millis()+INTERVALO_MEDICION; reloj.cargar_fecha_hora(); puntero_fecha=reloj.valor_fecha_hora(); sprintf ( buffer_fecha_formateada, "%02d/%02d/%02d %02d:%02d:%02d", int(*(puntero_fecha+4)), int(*(puntero_fecha+5)), int(*(puntero_fecha+6)), int(*(puntero_fecha+2)), int(*(puntero_fecha+1)), int(*(puntero_fecha+0)) ); Serial.println(buffer_fecha_formateada); } } |
El código de abajo corresponde con la cabecera y la función que lee la fecha y la hora desde el puerto serie para cambiar «manualmente» la de los registros del DS3231
1 2 3 4 5 6 7 |
// recibir_fecha.h #define INICIO_DATOS '{' // char de inicio de fecha enviada por el puerto serie #define FIN_DATOS '}' // char de final de fecha enviada por el puerto serie #define LONGITUD_FECHA 13 // Último caracter de la fecha incluyendo los de inicio y fin 6*2+2-1 bool recibir_fecha(char buffer_serie,char *buffer_fecha); |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
// recibir_fecha.cpp #include "recibir_fecha.h" bool recibir_fecha(char buffer_serie,char *buffer_fecha) { static unsigned char puntero_serie=0; bool nueva_fecha=false; if(puntero_serie>0&&puntero_serie<LONGITUD_FECHA&&buffer_serie>='0'&&buffer_serie<='9') { buffer_fecha[(puntero_serie+(puntero_serie>6?+1:-1))/2]+=(buffer_serie-'0')*(puntero_serie%2?10:1); puntero_serie++; } else { if(puntero_serie==LONGITUD_FECHA&&buffer_serie==FIN_DATOS) { puntero_serie=0; nueva_fecha=true; } else { if(puntero_serie==0&&buffer_serie==INICIO_DATOS) { puntero_serie++; } else { puntero_serie=0; } } } return nueva_fecha; } |
Librería para gestionar fecha y hora con Arduino
La nueva librería de gestión de fecha y hora debe encargarse de las funciones que se han quitado a la de comunicación con el DS3231: básicamente de la presentación como texto en varios formatos, y de la comparación y modificación de fechas y horas; en concreto, debe ser capaz de establecer si una fecha u hora es igual a otra o mayor que ella (con un wrapper se podrá determinar el caso contrario), de determinar si una está comprendida entre otras dos y de incrementar o decrementar la hora.
Determinar si dos fechas u horas son iguales
La forma más sencilla de comprobar si dos fechas son iguales es ir comparando cada uno de sus componentes; sólo con que uno sea diferente la condición de igualdad no se cumplirá. Con una única función puede realizarse la comprobación de forma genérica y usar luego implementaciones para comparar solamente la hora, solamente la fecha o ambas.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
bool tiempo::vector_mayor(char *vector_1, char *vector_2, char inicio, char fin) // true si el primero es mayor que el segundo { bool resultado=false; // Cuando todos los elementos son iguales devuelve false por defecto. Cambiando este valor la función devuelve cero si el vector 1 es mayor O IGUAL que el 2 bool buscando_resultado=true; char contador=fin; while(contador>=inicio&&buscando_resultado) { if(vector_1[contador]==vector_2[contador]) { contador--; } else { resultado=vector_1[contador]>vector_2[contador]; buscando_resultado=false; } } return resultado; } |
1 2 3 4 |
bool tiempo::hora_igual(tiempo otra_fecha) { return vector_igual(otra_fecha.valor_tiempo,valor_tiempo,INDICE_SEGUNDO,INDICE_HORA); } |
Calcular si una fecha es posterior a otra (más reciente que otra)
Para establecer si una fecha es mayor (posterior) que otra hay que ir comprobando que los componentes sean mayores o iguales empezando por el último (los dos primeros dígitos del año, que en la librería se llaman «siglo» por simplificar) Al encontrar un valor menor en la comparación la fecha no será mayor; hasta ahí no hay ambigüedad pero al llegar al primero de los componentes (el valor de los segundos de la hora o el valore del día según se considere la hora o solo se trabaje con la fecha) debe elegirse cuándo el resultado se considera válido: si la fecha es mayor o igual o solamente si es mayor. Como ya existe en la librería una forma de comprobar si la fecha es igual, he elegido que se valide como cierto el resultado solamente en el caso de ser mayor. Igual que en el caso anterior, una función se ocupa de la comprobación y se usa desde otras para comparar la fecha, la hora…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
bool tiempo::vector_mayor(char *vector_1, char *vector_2, char inicio, char fin) // true si el primero es mayor que el segundo { bool resultado=false; // Cuando todos los elementos son iguales devuelve false por defecto. Cambiando este valor la función devuelve cero si el vector 1 es mayor O IGUAL que el 2 bool buscando_resultado=true; char contador=fin; while(contador>=inicio&&buscando_resultado) { if(vector_1[contador]==vector_2[contador]) { contador--; } else { resultado=vector_1[contador]>vector_2[contador]; buscando_resultado=false; } } return resultado; } |
1 2 3 4 |
bool tiempo::fecha_hora_mayor(tiempo otra_fecha) { return vector_mayor(valor_tiempo,otra_fecha.valor_tiempo,INDICE_SEGUNDO,INDICE_SIGLO); } |
Comprobar si una fecha está comprendida entre otras dos
Si una fecha es mayor que otra pero menor que una tercera, operaciones que pueden realizarse con la función del apartado anterior, se habrá verificado que está comprendida entre ellas. Solo será necesario implementar el envoltorio (wrapper) para usarlas según se necesite comprobar la fecha, la hora o las dos.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
bool tiempo::fecha_hora_comprendida(tiempo primera_fecha, tiempo segunda_fecha) { return vector_mayor ( valor_tiempo,primera_fecha.valor_tiempo,INDICE_SEGUNDO,INDICE_SIGLO ) && vector_mayor ( segunda_fecha.valor_tiempo,valor_tiempo,INDICE_SEGUNDO,INDICE_SIGLO ); } |
Determinar si un año es bisiesto
Se considera año bisiesto cuando es divisible por 4 pero no lo es por 10 salvo que los sea por 400. Para implementarlo se puede utilizar el operador % que devuelve el resto de la división: si el resultado es cero el número será divisible.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
bool tiempo::bisiesto() { return bisiesto((unsigned int)valor_tiempo[INDICE_SIGLO]*100+valor_tiempo[INDICE_ANO]); } bool tiempo::bisiesto(char ano, char siglo) { return bisiesto((unsigned int)siglo*100+ano); } bool tiempo::bisiesto(unsigned int ano) { return ano%4==0&&(ano%100!=0||ano%400==0); } |
Calcular los días del mes de cierto año
La siguiente función utiliza un entero como una matriz de bits para obtener el número de días del año en función del número del mes. Para calcular si febrero tiene 28 o 29 días se utiliza la función del apartado anterior, los meses largos (31 días) se determinan por los bits con valor uno y los cortos (30 días) cuando el bit correspondiente vale cero.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
char tiempo::dias_mes(char mes, char ano, char siglo) { unsigned int plantilla_meses; // dos bytes (unsigned short) if(mes==2) { return 28+bisiesto(ano,siglo); // 28 + 1 si es bisiesto + 0 si no es bisiesto } else { plantilla_meses=0B0000101011010101<<(16-mes); return 30+((plantilla_meses>>15)==1); // 30 + 1 si es un mes largo + 0 si es un mes corto (menos febrero) } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
char tiempo::dias_mes() { return dias_mes(valor_tiempo[INDICE_MES],valor_tiempo[INDICE_ANO],valor_tiempo[INDICE_SIGLO]); } char tiempo::dias_mes(char mes) { return dias_mes(mes,valor_tiempo[INDICE_ANO],valor_tiempo[INDICE_SIGLO]); } char tiempo::dias_mes(char mes, char ano) { return dias_mes(mes,ano,valor_tiempo[INDICE_SIGLO]); } |
Buscar el día de la semana de una fecha
La forma convencional de calcular el día de la semana sabiendo el día del mes, el mes y el año es por medio del algoritmo de congruencia de Zeller. La siguiente función para Arduino devuelve el día de la semana empezando en cero, que corresponde al domingo, y usando una versión adaptada del algoritmo de Zeller. Es importante recordar que el reloj en tiempo real DS3231 codifica el día de la semana empezando en uno para el domingo y terminando en siete para el sábado mientras que esta función devuelve cero para el domingo, uno para el lunes…
1 2 3 4 5 6 7 8 9 10 11 |
char tiempo::dia_semana(char dia, char mes, char ano, char siglo) // Cálculo del día de la semana por el algoritmo de la congruencia de Zeller { ano-=(mes<=2); mes+=mes>2?-2:10; return (700+((26*mes-2)/10)+dia+ano+ano/4+siglo/4-2*siglo)%7; // «siglo» corresponde con las dos primeras cifras del año } char tiempo::dia_semana(char dia, char mes, char ano) { return dia_semana(dia,mes,ano,SIGLO_XXI); } |
Disponiendo del número del día de la semana es posible modificar la asignación de la fecha y la hora en el RTC DS3231 de forma que si el día de la semana indicado como parámetro de la misma es cero se calcule automáticamente usando la anterior implementación del algoritmo de Zeller.
Es muy sencillo preparar un vector con los nombres de los meses (y de los días de la semana) para presentar fechas en «formatos humanos» pero es muy importante tener en cuenta que necesitará una buena cantidad de la memoria disponible en una placa Arduino de las series pequeñas. Aunque esta y otras opciones estarán en el documento de la librería que se descarga al final del texto, lo más sensato es eliminar los métodos que no se utilicen para ahorrar memoria.
Adelantar o retrasar la hora
Para cualquier elemento de la fecha y la hora, aumentar o disminuir un dígito supone además verificar que no se supera el máximo o el mínimo de ese dígito, 24 y 0 en el caso de la hora, y arrastrar a los siguientes dígitos el cambio en caso necesario. Las siguientes funciones implementan el proceso para la hora, que será necesaria para el siguiente apartado y que sirve como ejemplo para implementarlo en otros componentes. Como la finalidad es mantener la hora estable en el reloj en tiempo real DS3231 durante el horario estacional, se modifica directamente el vector al leer la hora o grabarla.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
void tiempo::adelantar_hora(char *fecha) // En verano se adelanta la hora con respecto a la almacenada en el DS3231 { if(fecha[INDICE_HORA]<23) // Si la hora es menor que la última… { fecha[INDICE_HORA]++; // …añadir una hora } else // Si la hora es la última… { fecha[INDICE_HORA]=0; // …pasar a la primera del siguiente día if(fecha[INDICE_DIA]<dias_mes(fecha[INDICE_MES],fecha[INDICE_ANO],fecha[INDICE_SIGLO])) // Si quedan días en el mes… { fecha[INDICE_DIA]++; // …pasar al siguiente día } else // Si no quedan días en el mes… { fecha[INDICE_DIA]=1; // …pasar al primer día del siguiente mes if(fecha[INDICE_MES]<12) // Si quedan meses en el año… { fecha[INDICE_MES]++; // Pasar al siguiente mes } else // Si no quedan meses en el año… { fecha[INDICE_MES]=1; // …pasar al primer mes… if(fecha[INDICE_ANO]<99) // Si quedan años en el siglo… { fecha[INDICE_ANO]++; // …del siguiente año } else // Si no quedan años en el siglo… { fecha[INDICE_ANO]=1; // …del primer año… fecha[INDICE_SIGLO]++; // …del siguiente siglo } } } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
void tiempo::retrasar_hora(char *fecha) { if(fecha[INDICE_HORA]==0) // Si la hora es 0… { fecha[INDICE_HORA]=23; // …pasar a las 23 del día anterior if(fecha[INDICE_DIA]==1) // Si es el primer día del mes… (hay que pasar al último día del mes anterior) { if(fecha[INDICE_MES]==1) // Si es el primer mes del año… (hay que pasar al último mes del año anterior) { fecha[INDICE_MES]=12; if(fecha[INDICE_ANO]==0) // Si es el primer año del siglo… (hay que pasar al último año del siglo anterior) { fecha[INDICE_ANO]=99; // …pasar al último año del siglo anterior fecha[INDICE_SIGLO]--; // …pasar al siglo anterior } else { fecha[INDICE_ANO]--; // …pasar al año anterior } } else // Si no es el primer mes del año… { fecha[INDICE_MES]--; // …pasar al mes anterior } fecha[INDICE_DIA]=dias_mes(fecha[INDICE_MES],fecha[INDICE_ANO],fecha[INDICE_SIGLO]); // …pasar al último día del mes anterior } else // Si el día del mes es mayor que 1… { fecha[INDICE_DIA]--; // …restar un día } } else // Si la hora es mayor que 0… { fecha[INDICE_HORA]--; // …restar una hora } } |
Compensar el horario estacional
Según la directiva 2000/84/CE del Parlamento Europeo y del Consejo, que en España se establece por el Real Decreto 236/2002, «período de la hora de verano», lo que llamamos comúnmente «horario de verano», es el período del año durante el cual la hora se adelanta en sesenta minutos respecto a la hora del resto del año. Según este primer artículo del RD, la «hora normal» es la que queda fuera de este intervalo.
Conforme a esta directiva y consiguiente RD, a partir del año 2002, el período de la hora de verano comenzará en todos los estados miembros a la 1 de la madrugada, hora universal, del último domingo de marzo y terminará en todos los Estados miembros a la 1 de la madrugada, hora universal, del último domingo de octubre. Como España ha elegido estar en UTC+1 (aunque geográficamente no sea muy exacto) la hora se cambia a las 2 de la madrugada. Para la gestión de la hora estacional en otros países deberá tenerse en cuenta esta circunstancia.
Para que la la librería de control del reloj en tiempo real DS3231 de Arduino considere el horario estacional es necesario verificar si se encuentra dentro del periodo de verano. De ser así devolvería una hora más de la que almacene o, al cambiar manualmente la hora, almacenaría una hora menos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
bool tiempo::horario_estacional(char *fecha) { return vector_mayor ( fecha,inicio_verano,INDICE_SEGUNDO,INDICE_SIGLO ) && vector_mayor ( fin_verano,fecha,INDICE_SEGUNDO,INDICE_SIGLO ); } bool tiempo::horario_estacional() { return en_horario_estacional; } |
Para calcular el último domingo de marzo y de octubre hay que restar de 31 (último día del mes) el valor que devuelve la función dia_semana()
para el último día del mes, es decir, los días que «sobran» del domingo. ultimo_domingo=31-dia_semana(31,3,16);
El resto de los métodos de la clase de la librería de Arduino para consultar fecha y temperatura del integrado DS3231 por I2C con los que dar formato a la fecha y la hora para MySQL, registros logs queda tal como en el original con la única diferencia de que ahora se usa un único buffer para formatearla para ahorrar unos bytes de memoria.
Un ejemplo de uso de las nuevas librerías para gestionar fecha y hora con el módulo RTC DS3231 y Arduino, que puedes descargar aquí, quedaría así:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
#define INTERVALO_MEDICION 15000 // Intervalo entre lecturas sucesivas de la fecha y la hora #include "DS3231.h" #include "tiempo.h" #include <Wire.h> unsigned long cronometro; DS3231 reloj; tiempo fecha; tiempo fecha_anterior; tiempo fecha_posterior; void setup() { Serial.begin(9600); while(!Serial){};// Esperar a Arduino Leonardo 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) // Si ha pasado el tiempo entre lecturas, cargar la fecha y la hora y mostrarlas en la consola serie { cronometro=millis()+INTERVALO_MEDICION; fecha.cargar_DS3231(reloj.cargar_fecha_hora()); Serial.print("Día "); Serial.print(fecha.fecha_humana()); Serial.print(" a las "); Serial.println(fecha.hora_humana()); Serial.print("Para MYSQL "); Serial.println(fecha.fecha_hora_MySQL()); Serial.print("Formato compacto para log "); Serial.println(fecha.fecha_hora_compacta()); Serial.print("En un reloj de 4 dígitos "); Serial.println(fecha.reloj_4_digitos_7_segmentos()); fecha_anterior.establecer_fecha_hora(random(0,60),random(0,60),random(0,24),random(1,29),random(1,13),random(0,100)); fecha_posterior.establecer_fecha_hora(random(0,60),random(0,60),random(0,24),random(1,29),random(1,13),random(0,100)); if(!fecha.fecha_hora_comprendida(fecha_anterior,fecha_posterior)) { Serial.print("no "); } Serial.print("está comprendida entre "); Serial.print(fecha_anterior.fecha_humana()); Serial.print(" "); Serial.print(fecha_anterior.hora_humana()); Serial.print(" y "); Serial.print(fecha_posterior.fecha_humana()); Serial.print(" "); Serial.print(fecha_posterior.hora_humana()); Serial.println("\n"); } } |
Y esta es código de la nueva librería para gestionar el tiempo con Arduino
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 |
//tiempo.h #if defined(ARDUINO) && ARDUINO>=100 #include "Arduino.h" #else #include "WProgram.h" #endif #define INDICE_SEGUNDO 0 // Posición del valor de los segundos en el vector de los datos de la fecha #define INDICE_MINUTO 1 // …de los minutos #define INDICE_HORA 2 // …de las horas #define INDICE_DIA 3 // …del día del mes #define INDICE_MES 4 // …del mes #define INDICE_ANO 5 // …del año (dos últimos dígitos) #define INDICE_SIGLO 6 // …de lo que falta del año. No se refiere al valor del siglo sino a las dos primeras cifras del año #define ELEMENTOS_TIEMPO 7 // Cantidad de valores con los que se define la fecha #define HORA_DE_CAMBIO_ESTACIONAL 2 // En España (UTC+1) las dos de la madrugada (la 1 de la madrugada oficial +1) #define SIGLO_XXI 20 // Dos primeros dígitos del año en el siglo XXI #define DIA_SEMANA_DS3231 3 // Posición del día de la semana en los registros del DS3231 #define LONGITUD_BUFFER_FECHA_FORMATEADA 20 // Longitud del espacio reservado para las fechas formateadas (todas usan el mismo) class tiempo { private: char valor_tiempo[ELEMENTOS_TIEMPO]; // Valor de la fecha char fecha_DS3231[ELEMENTOS_TIEMPO]; // Buffer para la fecha en formato DS3231 char fecha_formateada[LONGITUD_BUFFER_FECHA_FORMATEADA]; // Buffer para las fechas formateadas (todas usan el mismo) char inicio_verano[ELEMENTOS_TIEMPO]; // Inicio del verano (para determinar el horario estacional) Se calcula cada vez que se modifica el año char fin_verano[ELEMENTOS_TIEMPO]; // Final del verano (para determinar el horario estacional) Se calcula cada vez que se modifica el año char numero_dia_semana; // Número del día de la semana empezando en domingo 0 y terminando en sábado 6. Se calcula cada vez que se modifica la fecha char contador; // Contador genérico de los elementos de la fecha y la hora bool en_horario_estacional; // True si la fecha está en el horario estacional. Se recalcula a cada cambio de la fecha o de la hora bool vector_igual(char *vector_1, char *vector_2, char inicio, char fin); // compara los elementos de un vector (fecha) con otro hasta encontrar uno diferente o hasta terminar. El primer índice (inicio) debe ser mayor que el último (fin) bool vector_mayor(char *vector_1, char *vector_2, char inicio, char fin); // compara los elementos de un vector (fecha) con otro empezando por el último (más relevancia) hasta encontrar uno no mayor o hasta terminar. El primer índice (inicio) debe ser mayor que el último (fin) void asignar_hora_intervalo_estacional(); void asignar_fecha_intervalo_estacional(); bool horario_estacional(char *fecha); // True si la fecha indicada está en el horario estacional (no modifica la hora ni la variable en_horario_estacional) protected: public: tiempo(); // Constructor sin datos. Los valores de la fecha y la hora se establecen a cero. La fecha y la hora correctas deberán asignarse después de crear el objeto. tiempo(char segundo, char minuto, char hora, char dia, char mes, char ano); // Crear un objeto tiempo con los datos (el siglo se considera XXI por defecto, es decir, su valor es 20) tiempo(char segundo, char minuto, char hora, char dia, char mes, char ano, char siglo); // Crear un objeto tiempo con todos los datos tiempo(char *fecha_DS3231); // Crear un objeto tiempo leyendo la fecha y la hora del vector de datos de un objeto DS3231 (el día de la semana se calcula, no se lee) ~tiempo(); // Destructor void establecer_hora(char segundo, char minuto, char hora); // Cambia la hora (dejando la fecha actual) void establecer_fecha(char dia, char mes, char ano); // Cambia la fecha (sin modificar la hora) Toma por defecto el siglo XXI (el valor es 20) void establecer_fecha_siglo(char dia, char mes, char ano, char siglo); // Cambia la fecha (sin modificar la hora) void establecer_fecha_hora(char segundo, char minuto, char hora, char dia, char mes, char ano); // Cambia la fecha y la hora. Toma por defecto el siglo XXI (el valor es 20) void establecer_fecha_hora_siglo(char segundo, char minuto, char hora, char dia, char mes, char ano, char siglo); // Cambia la fecha y la hora char segundos(); // Devuelve el valor de los segundos char minutos(); // Devuelve el valor de los minutos char horas(); // Devuelve el valor de las horas en formato de 24 al día char dia(); // Devuelve el número del día del mes char mes(); // Devuelve el número del mes char ano(); // Devuelve el dos últimas cifras del año char siglo(); // Devuelve el «siglo» (es el nombre que se le ha dado a las dos primeras cifras del año por simplificar) bool horario_estacional(); // True si la fecha actual está en el horario estacional (no modifica la hora) bool hora_igual(tiempo otra_fecha); // Compara la parte de la hora de dos objetos tiempo para ver si son iguales bool fecha_igual(tiempo otra_fecha); // Compara dos fechas para ver si son iguales bool fecha_hora_igual(tiempo otra_fecha); // Compara todos los valores de dos objetos tiempo para ver si son iguales bool hora_mayor(tiempo otra_fecha); // Compara la parte de la hora de dos objetos tiempo para ver si la primera (objeto actual) es mayor (más reciente) que la segunda bool fecha_mayor(tiempo otra_fecha); // Compara dos fechas para ver si la primera (objeto actual) es mayor (más reciente) que la segunda bool fecha_hora_mayor(tiempo otra_fecha); // Compara todos los valores de dos objetos tiempo para ver si la primera (objeto actual) es mayor (más reciente) que la segunda bool hora_comprendida(tiempo primera_fecha, tiempo segunda_fecha); // True si la hora actual es mayor que la de primera_fecha pero menor que la de segunda_fecha bool fecha_comprendida(tiempo primera_fecha, tiempo segunda_fecha); // True si la fecha actual es mayor que la de primera_fecha pero menor que la de segunda_fecha bool fecha_hora_comprendida(tiempo primera_fecha, tiempo segunda_fecha); // True si la fecha y la hora actual es mayor que la de primera_fecha pero menor que la de segunda_fecha bool bisiesto(); // True si el año de la fecha actual es bisiesto (objeto actual de la clase) bool bisiesto(char ano, char siglo); // True si el año indicado por dos valores es bisiesto (primer valor dos últimos dígitos del año y segundo dos primeros dígitos del año, lo que en la función se llama «siglo») bool bisiesto(unsigned int ano); // True si el año es bisiesto (expresado como un año natural como 2016) char dias_mes(); // Devuelve el número de días que tiene el mes de la fecha actual char dias_mes(char mes); // Devuelve el número de días de un mes, que se pasa como parámetro, del año de la fecha actual char dias_mes(char mes,char ano); // Calcula el número de días de cierto mes y cierto año. Ambos se pasan como parámetros y el año por los dos últimos dígitos, considerando 20 los dos primeros (siglo XXI) char dias_mes(char mes,char ano, char siglo); // Calcula el número de días de cierto mes de cierto año de cierto siglo. Todos los valores se pasan como parámetros ¡Cuidado! el siglo no es el real sino los dos primeros dígitos del año void adelantar_hora(char *fecha); // Cambia el vector fecha para adelantar una hora void retrasar_hora(char *fecha); // Cambia el vector fecha para retrasar una hora void cargar_DS3231(char *fecha_DS3231); // Lee los datos de fecha y hora de un vector como los apuntados por la librería DS3231 char *fecha_hora_DS3231(); // Devuelve los datos de fecha y hora del objeto tiempo en el formato de vector de la librería para el DS3231 char dia_semana(); // devuelve el día de la semana calculado al asignar la fecha char dia_semana(char dia, char mes, char ano); // calcula del día de la semana de una fecha (siglo XXI por defecto) char dia_semana(char dia, char mes, char ano, char siglo); // calcula del día de la semana de una fecha char *hora_humana(); // Hora en el formato hh:mm:ss siendo hh la hora (en formato de 24) representada con 2 dígitos, mm los minutos con 2 dígitos y ss los segundos con 2 dígitos char *fecha_humana(); // Fecha en formato DD/MM/AAAA siendo DD el día representado con 2 dígitos, MM el mes con 2 dgitos y AAAA el año con 4 dgitos char *fecha_hora_MySQL(); // Fecha en formato AAAA-MM-DD hh:mm:ss (estilo MySQL) siendo AAAA el año representado con 4 dgitos, MM el mes con 2 dgitos, DD el día con 2 dígitos, hh la hora (en formato de 24) con 2 dígitos, mm los minutos con 2 dígitos y ss los segundos con 2 dígitos char *fecha_hora_compacta(); // Fecha en formato compacto (como la anterior pero sin adornos y dos dígitos para el año) para escribir en log y en bases de datos unsigned int reloj_4_digitos_7_segmentos(); // La hora tal como la esperan la mayoría de relojes de cuatro dígitos LED de 7 segmentos }; |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 |
//tiempo.cpp #include "tiempo.h" tiempo::tiempo() // Constructor sin parámetros { for(contador=INDICE_SEGUNDO;contador<ELEMENTOS_TIEMPO;contador++) { valor_tiempo[contador]=0; } } tiempo::tiempo(char segundo, char minuto, char hora, char dia, char mes, char ano) // Constructor con los datos de la fecha sin siglo (ya que se espera que funcione solamente en el XXI) { establecer_fecha_hora(segundo,minuto,hora,dia,mes,ano); } tiempo::tiempo(char segundo, char minuto, char hora, char dia, char mes, char ano, char siglo) // Constructor con todos los datos de fecha y hora { establecer_fecha_hora_siglo(segundo,minuto,hora,dia,mes,ano,siglo); } tiempo::tiempo(char *fecha_DS3231) // Constructor desde un puntero al valor de los registros del DS3231 { cargar_DS3231(fecha_DS3231); } tiempo::~tiempo() // Destructor { } void tiempo::establecer_hora(char segundo, char minuto, char hora) { valor_tiempo[INDICE_SEGUNDO]=segundo; valor_tiempo[INDICE_MINUTO]=minuto; valor_tiempo[INDICE_HORA]=hora; asignar_hora_intervalo_estacional(); en_horario_estacional=horario_estacional(valor_tiempo); } void tiempo::establecer_fecha(char dia, char mes, char ano) { establecer_fecha_siglo(dia,mes,ano,SIGLO_XXI); } void tiempo::establecer_fecha_siglo(char dia, char mes, char ano, char siglo) { valor_tiempo[INDICE_DIA]=dia; valor_tiempo[INDICE_MES]=mes; valor_tiempo[INDICE_ANO]=ano; valor_tiempo[INDICE_SIGLO]=siglo; numero_dia_semana=dia_semana(dia,mes,ano,siglo); asignar_fecha_intervalo_estacional(); en_horario_estacional=horario_estacional(valor_tiempo); } void tiempo::establecer_fecha_hora(char segundo, char minuto, char hora, char dia, char mes, char ano) { establecer_fecha_hora_siglo(segundo,minuto,hora,dia,mes,ano,SIGLO_XXI); // NO es el número del siglo sino las dos primeras cifras del año } void tiempo::establecer_fecha_hora_siglo(char segundo, char minuto, char hora, char dia, char mes, char ano, char siglo) { establecer_hora(segundo,minuto,hora); establecer_fecha_siglo(dia,mes,ano,siglo); } void tiempo::asignar_hora_intervalo_estacional() { inicio_verano[INDICE_SEGUNDO]=0; inicio_verano[INDICE_MINUTO]=0; inicio_verano[INDICE_HORA]=HORA_DE_CAMBIO_ESTACIONAL; fin_verano[INDICE_SEGUNDO]=0; fin_verano[INDICE_MINUTO]=0; fin_verano[INDICE_HORA]=HORA_DE_CAMBIO_ESTACIONAL; } void tiempo::asignar_fecha_intervalo_estacional() { fin_verano[INDICE_ANO]=valor_tiempo[INDICE_ANO]; fin_verano[INDICE_SIGLO]=valor_tiempo[INDICE_SIGLO]; // NO es el número del siglo sino las dos primeras cifras del año inicio_verano[INDICE_DIA]=31-dia_semana(31,3,valor_tiempo[INDICE_ANO],valor_tiempo[INDICE_SIGLO]); // Calcular el número del día del mes del último domingo inicio_verano[INDICE_MES]=3; inicio_verano[INDICE_ANO]=valor_tiempo[INDICE_ANO]; inicio_verano[INDICE_SIGLO]=valor_tiempo[INDICE_SIGLO]; fin_verano[INDICE_DIA]=31-dia_semana(31,10,valor_tiempo[INDICE_ANO],valor_tiempo[INDICE_SIGLO]); // Calcular el número del día del mes del último domingo fin_verano[INDICE_MES]=10; fin_verano[INDICE_ANO]=valor_tiempo[INDICE_ANO]; fin_verano[INDICE_SIGLO]=valor_tiempo[INDICE_SIGLO]; } char tiempo::segundos() { return valor_tiempo[INDICE_SEGUNDO]; } char tiempo::minutos() { return valor_tiempo[INDICE_MINUTO]; } char tiempo::horas() { return valor_tiempo[INDICE_HORA]; } char tiempo::dia() { return valor_tiempo[INDICE_DIA]; } char tiempo::mes() { return valor_tiempo[INDICE_MES]; } char tiempo::ano() { return valor_tiempo[INDICE_ANO]; } char tiempo::siglo() { return valor_tiempo[INDICE_SIGLO]; } bool tiempo::horario_estacional() { return en_horario_estacional; } bool tiempo::horario_estacional(char *fecha) { return vector_mayor ( fecha,inicio_verano,INDICE_SEGUNDO,INDICE_SIGLO ) && vector_mayor ( fin_verano,fecha,INDICE_SEGUNDO,INDICE_SIGLO ); } bool tiempo::hora_igual(tiempo otra_fecha) { return vector_igual(otra_fecha.valor_tiempo,valor_tiempo,INDICE_SEGUNDO,INDICE_HORA); } bool tiempo::fecha_igual(tiempo otra_fecha) { return vector_igual(otra_fecha.valor_tiempo,valor_tiempo,INDICE_DIA,INDICE_SIGLO); } bool tiempo::fecha_hora_igual(tiempo otra_fecha) { return vector_igual(otra_fecha.valor_tiempo,valor_tiempo,INDICE_SEGUNDO,INDICE_SIGLO); } bool tiempo::vector_igual(char *vector_1, char *vector_2, char inicio, char fin) { bool resultado=true; char contador=inicio; fin++; while(contador<fin&&resultado) { if(vector_1[contador]==vector_2[contador]) { contador++; } { resultado=false; } } return resultado; } bool tiempo::hora_mayor(tiempo otra_fecha) { return vector_mayor(valor_tiempo,otra_fecha.valor_tiempo,INDICE_SEGUNDO,INDICE_HORA); } bool tiempo::fecha_mayor(tiempo otra_fecha) { return vector_mayor(valor_tiempo,otra_fecha.valor_tiempo,INDICE_DIA,INDICE_SIGLO); } bool tiempo::fecha_hora_mayor(tiempo otra_fecha) { return vector_mayor(valor_tiempo,otra_fecha.valor_tiempo,INDICE_SEGUNDO,INDICE_SIGLO); } bool tiempo::vector_mayor(char *vector_1, char *vector_2, char inicio, char fin) // true si el primero es mayor que el segundo { bool resultado=false; // Cuando todos los elementos son iguales devuelve false por defecto. Cambiando este valor la función devuelve cero si el vector 1 es mayor O IGUAL que el 2 bool buscando_resultado=true; char contador=fin; while(contador>=inicio&&buscando_resultado) { if(vector_1[contador]==vector_2[contador]) { contador--; } else { resultado=vector_1[contador]>vector_2[contador]; buscando_resultado=false; } } return resultado; } bool tiempo::hora_comprendida(tiempo primera_fecha, tiempo segunda_fecha) { return vector_mayor ( valor_tiempo,primera_fecha.valor_tiempo,INDICE_SEGUNDO,INDICE_HORA ) && vector_mayor ( segunda_fecha.valor_tiempo,valor_tiempo,INDICE_SEGUNDO,INDICE_HORA ); } bool tiempo::fecha_comprendida(tiempo primera_fecha, tiempo segunda_fecha) { return vector_mayor ( valor_tiempo,primera_fecha.valor_tiempo,INDICE_DIA,INDICE_SIGLO ) && vector_mayor ( segunda_fecha.valor_tiempo,valor_tiempo,INDICE_DIA,INDICE_SIGLO ); } bool tiempo::fecha_hora_comprendida(tiempo primera_fecha, tiempo segunda_fecha) { return vector_mayor ( valor_tiempo,primera_fecha.valor_tiempo,INDICE_SEGUNDO,INDICE_SIGLO ) && vector_mayor ( segunda_fecha.valor_tiempo,valor_tiempo,INDICE_SEGUNDO,INDICE_SIGLO ); } void tiempo::cargar_DS3231(char *fecha_DS3231) { for(contador=INDICE_SEGUNDO;contador<ELEMENTOS_TIEMPO-1;contador++) { valor_tiempo[contador]=fecha_DS3231[contador>INDICE_HORA?contador+1:contador]; } valor_tiempo[INDICE_SIGLO]=SIGLO_XXI; // NO es el número del siglo sino las dos primeras cifras del año asignar_hora_intervalo_estacional(); asignar_fecha_intervalo_estacional(); en_horario_estacional=horario_estacional(valor_tiempo); if(en_horario_estacional) { adelantar_hora(valor_tiempo); } } char *tiempo::fecha_hora_DS3231() { for(contador=INDICE_SEGUNDO;contador<INDICE_DIA;contador++) { fecha_DS3231[contador]=valor_tiempo[contador]; } fecha_DS3231[DIA_SEMANA_DS3231]=dia_semana(); for(contador=INDICE_DIA;contador<INDICE_ANO;contador++) { fecha_DS3231[contador+1]=valor_tiempo[contador]; } if(en_horario_estacional) { retrasar_hora(fecha_DS3231); } return fecha_DS3231; } bool tiempo::bisiesto() { return bisiesto((unsigned int)valor_tiempo[INDICE_SIGLO]*100+valor_tiempo[INDICE_ANO]); } bool tiempo::bisiesto(char ano, char siglo) { return bisiesto((unsigned int)siglo*100+ano); } bool tiempo::bisiesto(unsigned int ano) { return ano%4==0&&(ano%100!=0||ano%400==0); } char tiempo::dia_semana() { return numero_dia_semana; } char tiempo::dias_mes() { return dias_mes(valor_tiempo[INDICE_MES],valor_tiempo[INDICE_ANO],valor_tiempo[INDICE_SIGLO]); } char tiempo::dias_mes(char mes) { return dias_mes(mes,valor_tiempo[INDICE_ANO],valor_tiempo[INDICE_SIGLO]); } char tiempo::dias_mes(char mes, char ano) { return dias_mes(mes,ano,valor_tiempo[INDICE_SIGLO]); } char tiempo::dias_mes(char mes, char ano, char siglo) { unsigned int plantilla_meses; // dos bytes (unsigned short) if(mes==2) { return 28+bisiesto(ano,siglo); // 28 + 1 si es bisiesto + 0 si no es bisiesto } else { plantilla_meses=0B0000101011010101<<(16-mes); return 30+((plantilla_meses>>15)==1); // 30 + 1 si es un mes largo + 0 si es un mes corto (menos febrero) } } char tiempo::dia_semana(char dia, char mes, char ano) { return dia_semana(dia,mes,ano,SIGLO_XXI); } char tiempo::dia_semana(char dia, char mes, char ano, char siglo) // Cálculo del día de la semana por el algoritmo de la congruencia de Zeller { ano-=(mes<=2); mes+=mes>2?-2:10; return (700+((26*mes-2)/10)+dia+ano+ano/4+siglo/4-2*siglo)%7; // «siglo» corresponde con las dos primeras cifras del año } void tiempo::adelantar_hora(char *fecha) // En verano se adelanta la hora con respecto a la almacenada en el DS3231 { if(fecha[INDICE_HORA]<23) // Si la hora es menor que la última… { fecha[INDICE_HORA]++; // …añadir una hora } else // Si la hora es la última… { fecha[INDICE_HORA]=0; // …pasar a la primera del siguiente día if(fecha[INDICE_DIA]<dias_mes(fecha[INDICE_MES],fecha[INDICE_ANO],fecha[INDICE_SIGLO])) // Si quedan días en el mes… { fecha[INDICE_DIA]++; // …pasar al siguiente día } else // Si no quedan días en el mes… { fecha[INDICE_DIA]=1; // …pasar al primer día del siguiente mes if(fecha[INDICE_MES]<12) // Si quedan meses en el año… { fecha[INDICE_MES]++; // Pasar al siguiente mes } else // Si no quedan meses en el año… { fecha[INDICE_MES]=1; // …pasar al primer mes del siguiente año if(fecha[INDICE_ANO]<99) // Si quedan años en el siglo… { fecha[INDICE_ANO]++; // …del siguiente año } else // Si no quedan años en el siglo… { fecha[INDICE_SIGLO]++; // …pasar al siguiente siglo } } } } } void tiempo::retrasar_hora(char *fecha) { if(fecha[INDICE_HORA]==0) // Si la hora es 0… { fecha[INDICE_HORA]=23; // …pasar a las 23 del día anterior if(fecha[INDICE_DIA]==1) // Si es el primer día del mes… { if(fecha[INDICE_MES]==1) // Si es el primer mes del año { if(fecha[INDICE_ANO]==0) // Si es el primer año del siglo… { fecha[6]=99; // …pasar al último año del siglo anterior if(fecha[INDICE_ANO]==0) // Si es el primer año del siglo… { fecha[INDICE_ANO]=99; // …pasar al último año del siglo anterior fecha[INDICE_SIGLO]--; } } else { fecha[INDICE_ANO]--; // …pasar al año anterior } } else // Si no es el primer mes del año… { fecha[INDICE_MES]--; // …pasar al mes anterior } fecha[INDICE_DIA]=dias_mes(fecha[INDICE_MES],fecha[INDICE_ANO],fecha[INDICE_SIGLO]); // …pasar al último día del mes anterior } else // Si el día del mes es mayor que 1… { fecha[INDICE_DIA]--; // …restar un día } } else // Si la hora es mayor que 0… { fecha[INDICE_HORA]--; // …restar una hora } } char *tiempo::hora_humana() { sprintf ( fecha_formateada, "%02d:%02d:%02d", valor_tiempo[INDICE_HORA], valor_tiempo[INDICE_MINUTO], valor_tiempo[INDICE_SEGUNDO] ); return fecha_formateada; } unsigned int tiempo::reloj_4_digitos_7_segmentos() { return (unsigned int)valor_tiempo[INDICE_HORA]*100+(unsigned int)valor_tiempo[INDICE_MINUTO]; } char *tiempo::fecha_humana() { sprintf ( fecha_formateada, "%02d/%02d/%02d%02d", // Formato de fecha y hora estilo español ¡Olé! valor_tiempo[INDICE_DIA], valor_tiempo[INDICE_MES], valor_tiempo[INDICE_SIGLO], valor_tiempo[INDICE_ANO] ); return fecha_formateada; } char *tiempo::fecha_hora_compacta() { sprintf ( fecha_formateada, "%02d%02d%02d%02d%02d%02d", // Formato de fecha y hora compacta para log y base de datos valor_tiempo[INDICE_ANO], valor_tiempo[INDICE_MES], valor_tiempo[INDICE_DIA], valor_tiempo[INDICE_HORA], valor_tiempo[INDICE_MINUTO], valor_tiempo[INDICE_SEGUNDO] ); return fecha_formateada; } char *tiempo::fecha_hora_MySQL() { sprintf ( fecha_formateada, "%02d%02d-%02d-%02d %02d:%02d:%02d", // Formato de fecha y hora estilo MySQL valor_tiempo[INDICE_SIGLO], valor_tiempo[INDICE_ANO], valor_tiempo[INDICE_MES], valor_tiempo[INDICE_DIA], valor_tiempo[INDICE_HORA], valor_tiempo[INDICE_MINUTO], valor_tiempo[INDICE_SEGUNDO] ); return fecha_formateada; } |
Chrcntos
Hola Víctor, un trabajo impresionante.
Podrias explicarme partiendo de la ultima libreria como se podria incrementar por ejemplo 1 hora ó 1 mes, en el DS3231 sin que esto modifique el resto de variables.
Probe el codigo:
fecha[INDICE_HORA]--;
Víctor Ventura
Hola, Chrcntos.
No estoy seguro de entenderte. Si quieres modificar solamente la hora, es correcto usar
fecha[INDICE_HORA]--;
, lo que me extraña poderosamente es que quieras hacerlo. Precisamente lo útil de (por ejemplo) del métodoretrasar_hora()
es que «arrastra» al resto de propiedades. Suponiendo que te refieras a lo que me parece más lógico (no a lo que me parece que preguntas), es decir, incrementar el mes (ya que incrementar la hora se haría como describe el métodoadelantar_hora()
, descrito arriba), el código sería algo así:Verás que he cambiado el siglo; aunque en el objeto fecha para Arduino no hay ningún problema, el RTC DS3231 solamente funciona en el siglo XXI.
Si lo que quieres es retrasar un mes, puedes usar un código como el siguiente:
Por cierto, en la función original, había un error (ya está corregido) al retrasar la hora, además del siglo, se había colado un índice numérico en lugar de una constante que lo enredaba todo. Además de cambiarlo, he puesto algún comentario más para tratar de explicarlo mejor.
Espero haber sido útil pero, si no he acertado con tu pregunta, no tengas inconveniente en volver a formularla.
Saludos y gracias por visitar polaridad.es
Chrcntos
Gracias por tu rapida respuesta Víctor.
Mi idea es usar 4 interruptores, 2 para moverme por la matriz (hora, min, seg, año, mes, dia) y 2 para aumentar o disminuir el valor del dato seleccionado en el reloj.
Entiendo que el codigo que me mandas iria implementado en el cpp. ¿Seria posible hacerlo directamente desde el programa .ino?.
¿De ser posible podrias poner un ejemplo con minutos o meses, del codigo que habria que usar para pegarlo dentro del programa que pones en tu ejemplo?
Ejem:
Epero que puedas ayudarme porque despues de varios intentos, se poner el reloj en hora, pero cuando intento hacerlo solo con un elemento de la matriz o se me pone el resto a 0 o me salta un error como con
fecha[INDICE_HORA]--
;sketch_jun03b:11: error: 'INDICE_HORA' was not declared in this scope
Un saludo Víctor
Víctor Ventura
Hola, Chrcntos.
Puedo ponerte un ejemplo para usarlo con todos los elementos, pero me va a llevar un poco de tiempo, y es información de más calado que puede interesar a más lectores así que lo que voy a hacer es, en cuanto saque un rato, añadiré una versión de la librería que tenga métodos para modificar minutos, horas, días…
El código de ejemplo con el que te respondía al comentario anterior tiene el formato de método de la clase con la que se gestiona el tiempo, así que, en efecto, está pensado para disponerlo en el documento .cpp de la librería pero no sería un problema «versionarlo» para usarlo dentro del sketch de Arduino (documento .ino).
Como el ejemplo que mandas está incompleto no es raro el error que muestras: no has declarado (o definido) la constante
INDICE_HORA
(como hago yo en el documento de cabecera .h de la librería).Te desaconsejo la forma de esperar que usas en la línea 7, creo que es mejor el estilo que usas en las siguientes: no parar el micro, verificar que ha pasado el tiempo.
Saludos y gracias por participar en polaridad.es
Chrcntos.
Ok Víctor pues gracias, quedo a la espera de la nueva libreria.
Cuando tengas la libreria lista, pones un ejemplo para torpes como yo, con todas estas cosas, como adelantar hora, retrasar un día, cambiar horario verano, etc, en el sketch, eso siempre ayuda.
Sabia que me ibas a desaconsejar el delay y me ibas a aconsejas millis (o millis y microseconds), pero un delay tiene una gran ventaja y es que se escribe más rapido :p , en mi codigo final tengo que usar millis porque voy a tener varios contadores en marcha.
Querria adaptar esto en una pantalla con interfaz I2C, y moverme por los menus con un swich case, creo que hay opciones mejores, pero voy a andar justito de espacio en el arduino cuando junte todas las partes del programa.
Un saludo Víctor
Gracias por tu trabajo
Víctor Ventura
Hola, Chrcntos.
He publicado un artículo con modificaciones a la librería para ajustar la hora con Arduino que puede responder a lo que preguntabas. El texto explica cómo usar la librería para cambiar la hora adelantando o retrasando cada elemento que, si no me equivoco, es lo que necesitabas.
Espero que te sirva. Saludos.
Angel
si quiero sincronizar el tiempo por enternet time.nist.govi una vez al dia en vez del puerto de serie se puede.
Víctor Ventura
Hola, Ángel.
Claro que puedes, pero necesitas acceso a Internet con Ethernet o Wifi. Precisamente, en el artículo sobre la sincronización horaria con el módulo ESP8266 doy alguna pista sobre cómo hacerlo con el protocolo NTP, que es el que utilizan los servidores de fecha y hora como el de tu ejemplo.
Gracias por participar en polaridad.es.
Roberto Preziuso
Hola Víctor, estoy tratando de armar con Arduino UNO como hobby, un circuito que accione una puerta a partir de leer fecha y hora en código de barras. Es decir que, si la fecha es correcta y la hora está dentro de los 10 minutos más o menos abre una puerta. Dispongo del Arduino y estoy buscando un lector del código de barras para interconectar. No me dio cuenta de cómo realizar la comparación de los datos, la leída y la interna del Arduino. Espero tus comentarios.
Víctor Ventura
Hola, Roberto.
No puedo ayudarte mucho 🙁 nunca he utilizado un lector de código de barras desde Arduino. Por lo que sé, normalmente utilizan protocolos HID, así que no debe ser difícil conectarlos por USB o incluso más fácil por PS/2 a Arduino. A lo mejor le echo un vistazo al tema para un futuro artículo.
¡Hasta pronto!
Roberto Preziuso
Victor:
Gracias por tu pronta respuesta.
Pero, puedo consultar lo siguiente:
Los lectores de barra vienen para USB. Entonces al Arduino UNO le instalaré el modulo USB para dicho lector (la impresora de barras imprime fecha y hora) y luego pregunto ¿debería colocar el módulo DS 3231?
De ser así, al leer la impresión de barra, del puerto serie tendré una entrada DDMMAAAAHHMMSS, luego debería leer la fecha y hora del DS 3231 para comparar si está comprendida dentro del lapso previsto. Es correcto?
Víctor Ventura
Hola, Roberto.
Sí, la mayoría de los escáneres de códigos de barras (que yo conozco) tienen conexión USB. También hay (todavía) con conexión PS/2, que pueden ser aún más sencillos (y económicos) para conectarlos a una placa Arduino, aunque se pueden quedar antiguos antes, peores si lo quieres usar también para PC.
Hasta donde sé, los lectores de códigos de barras devuelven un valor equivalente a la pulsación de una tecla, es decir, si distinguen en las barras un número 4 no mandan un valor 4 sino el valor que corresponde a la pulsación de la tecla del 4. Luego está el tema del dígito de control (que también depende de la codificación utilizada) que suele enviar una pulsación de la tecla «enter» (aunque he visto algunos que permiten configurar esto).
Por lo demás, supongo que sí, que lo que planteas es viable, no sé si lo más adecuado (salvo que lo que quieras sea practicar con lectores de códigos de barras) pero podría funcionar.
Suerte con tu proyecto y gracias por participar en polaridad.es
Ale
Hola excelente post!!
Tengo un problema con este modulo, no mantiene la hora, lo uso para un proyecto donde esta alimentado constantemente pero luego de unos días la hora se pierde y el dispositivo deja de funcionar para lo que fue programado. Cuando voy a revisar la pila esta agotada. No deberia estar agotada ya que se mantenia alimentado a través del conector J1. ¿Que puede estar pasando?
Víctor Ventura
Hola, Ale.
Lo que te está pasando es muy habitual en las versiones más baratas de estos módulos. La pila se descarga (independiente de que esté alimentando al DS3231 o no) y si es una batería, no se recarga; así que dura unos pocos días cuando, bien gestionada, podría durar meses.
Creo que es viable repararlos pero, por lo que valen, no merece la pena 🙁 es mejor comprarlos en un proveedor fiable, no todos los módulos (ni por supuesto el integrado) tienen este problema.
Saludos.
Vladi mir
Hola Víctor.
No puedo lograr disparar una determinada acción cada 48 HS .
Poseo:
Arduino Nano
Rtc ds3231
Gestionó bien cálculos de fecha pero no puedo lograrlo cada 48hs ya que me resulta difícil utilizar la eeprom del Arduino o la del rtc.
La idea es que no se vea truncado el algoritmo por cortes de energí
Desde ya muchas gracias
jamt
hola, cordial saludo
que pasa si durante el evento ay un cote de energía.
si programo una lampara para que encienda a las 22h y se pague a las 06h si en ese intervalo de las 8 horas hay un corte de energia al reiniciarse el arduino uno, pero estando el RTC con la hora actual como sincroniza el tiempo que falta y enciende la lampra para terminar el proceso ?
#include
#include «RTClib.h»
RTC_DS1307 RTC;
DateTime HoraFecha;
void setup () {
pinMode(13,OUTPUT);
Wire.begin();
RTC.begin();
Serial.begin(9600);
}
void loop () {
DateTime now = RTC.now();
if(now.hour() == 15 && now.minute() == 55){
digitalWrite(13,HIGH);
}
else if(now.hour() == 16 && now.minute() == 50){
digitalWrite(13,LOW);
}
delay(2000);
RUDYARD BARCENA DIAZ
HOLA VICTOR ESTE ES UN TRABAJO MUY BUENO Y TITANICO PERO NO ENTIENDO COMO HACER EL CALMBIO DE HORARIO DE FORMA AUTOMATICO EN Compensar el horario estacional VEO LA LIBRERIA PERO DESCARGUE LA LIBRERIA DEL DS3231 Y NO SE CUAL DE LOS CODIGOS TENGO QUE AGREGAR, O BORRAR EL ORIGINAL QUE TRAE LA LIBRERIA ESPERO ME PUEDAS AYUDAR GRACIAS
FRANCISCO
Tendras una libreria o ejemplo que cambie el horario estacional en México
Aqui empieza el primer domingo de abril (se adelanta una hora) y finaliza el ultimo domingo de octubre se atraza 1 hora
Edson Rubio
Buenas Noches, me podria dar una idea, necesito mostrar en un lcd los numeros no significativos en cuestion de minutos, es decir me aparece, 23:3 (23 horas 3 minutos) y quisiera que me apareciera 23:03, como puedo modifocarlo ??? saludos
Cesar Hidalgo
Hola como están me podrían ayudar soy nuevo en esto de los arduinos y las codificaciones estoy realizando un ejercicio de temperatura con un DTH11 y necesito programar que en el mismo se me active un ventilador según la temperatura pero a su vez con el RTC que el mismo se active según los días
Ejemplo dentro de los primeros 10 días el ventilador se active cuando la temperatura sea de 20 grados después de pasar los 15 días el ventilador se active cuando la temperatura este en 30 grados y a los 20 días transcurridos el ventilador se active cuando la temperatura este a los 40 grados.
Estoy utilizando este código para la temperatura
#include «DHT.h»
#define DHTPIN 2 // Pin donde está conectado el sensor
//#define DHTTYPE DHT11 // Descomentar si se usa el DHT 11
#define DHTTYPE DHT22 // Sensor DHT22
#define pventilador 12
DHT dht(DHTPIN, DHTTYPE);
void setup() {
Serial.begin(9600);
Serial.println(«Iniciando…»);
dht.begin();
pinMode(13, OUTPUT);
}
long tiempoUltimaLectura=0;; //Para guardar el tiempo de la última lectura
void loop() {
//———Lectura del Sensor————————–
if(millis()-tiempoUltimaLectura>2000)
{
float h = dht.readHumidity(); //Leemos la Humedad
float t = dht.readTemperature(); //Leemos la temperatura en grados Celsius
float f = dht.readTemperature(true); //Leemos la temperatura en grados Fahrenheit
//——–Enviamos las lecturas por el puerto serial————-
Serial.print(«Humedad «);
Serial.print(h);
Serial.print(» %t»);
Serial.print(«Temperatura: «);
Serial.print(t);
Serial.print(» *C «);
Serial.print(f);
Serial.println(» *F»);
tiempoUltimaLectura=millis(); //actualizamos el tiempo de la última lectura
}
//—-Fin de la lectura—————————
//———Resto del código del proyecto——–
//…
//…
//…
digitalWrite(13, HIGH);
delay(100);
digitalWrite(13, LOW);
delay(100);
//——————————-
}
if (t < 24.00)
{
digitalWrite(pVentilador, HIGH);
}
else digitalWrite(pVentilador, LOW);
}
Antonio
Hola Víctor.
Llevo más de media hora leyendo los 3 artículos relacionados con el DS3231.
Has hecho un trabajo buenísimo.
Todo muy bien explicado, con ejemplos, aportando soluciones…
Estoy empezando con arduino y por fin puedo unir mis dos gran pasiones: electrónica e informática.
Muchas gracias.
Un saludo desde Murcia.
pd: voy a leer el artículo del ESP8266 porque he querido empezar ‘fuerte’ y el módulo me está dándo muchos dolores de cabeza.
Víctor Ventura
¡Muchas gracias! Me alegra que te guste. 🙂