Reloj DS3231 de alta precisión y eficiencia

El reloj DS3231 es un integrado bastante versátil capaz de llevar cuenta del tiempo y fecha con una precisión de +/- 2 minutos por año.

Reloj-DS3231-i2c-bateria-arduinoPuedes conseguirlo por un costo de 5 a 10 USD en la tienda de Amazon.

El integrado DS3231 es presentado usualmente en un pequeño circuito que incluye, entre otros componentes, una pila de litio CR2032 (en la parte de abajo del circuito, por eso no se ve en la foto) que le brinda autonomía de hasta unos 5 años, aún sin otra fuente de alimentación.

El DS3231 cuenta con un interfaz i2c desde el cual podremos ajustar y consultar su información.

Puede ser alimentado tanto con 3.3v como 5v lo que da buena comodidad en nuestros proyectos Arduino independientemente de que controladora y voltajes utilicemos.

Manipulando el DS3231 desde Arduino

Hay numerosas librerías disponibles para controlar al DS3231 en nuestros proyectos Arduino.

En mi caso, me destilé por la librería SODAQ DS3231 que está disponible para su integración desde el propio gestor de librerías del IDE oficial de Arduino.

NOTA: La librería SODAQ no compila (al menos la versión que tengo en este momento) correctamente para el ESP8266. Tuve que buscar el archivo Sodaq_DS3231.cpp y al principio donde se hace el #include avr\pgmspace.h cambiarlo por:

#if (defined(__AVR__))
#include <avr\pgmspace.h>
#else
#include <pgmspace.h>
#endif

Para mi actual proyecto, conecté el reloj DS3231 a una controladora ESP8266 para así obtener buena precisión horaria en  el registro histórico de datos.

Cómo vuelta de tuerca, hago que el ESP8266 consulte via Internet con un servidor de NTP al ser encendido y así ajuste el reloj del DS3231.

Ajustando el reloj DS3231 con un servidor NTP via Internet

Antes de presentar el código que utilizo para ajustar el DS3231 via Internet con un servidor NTP, necesito explicar algunas cosas que verán allí:

Primero, el objeto ssd es simplemente una pantalla OLED que tengo conectada -también vía i2c a mi ESP8266.

Segundo, la función que presento aquí abajo es llamada dentro de mi código general luego de confirmar que el ESP8266 se ha conectado a mi red WIFI.  No estoy poniendo toda esa parte del software porque serían cientos de líneas de código que no considero «centrales» al tema de este artículo.

Por último, hay una serie de variables y objetos que se inicializan para que puedan ser consumidos por esta función, que voy a intentar detallar aquí debajo e iría en los inicios de tu proyecto de software para Arduino:

/* ==== Includes ==== */
#include <WiFiUdp.h> //Se necesita para consultar el NTP y ajustar el reloj
#include <Sodaq_DS3231.h> //Librería para el DS3231

/*==== Objetos-libs ====*/
Sodaq_DS3231 clock;  //RTC DS3231
WiFiUDP udp;	//Instancia UDP para enviar y recibir paquetes al Servidor NTP

/* Variables para el RTC y acceso NTP */
unsigned int localPort = 2390;      //puerto local para recibir paquete UDP desde el NTP server
IPAddress timeServerIP; // donde voy a guardar la IP del servidor NTP
const char* ntpServerName = "0.pool.ntp.org"; //el servidor NTP
const int NTP_PACKET_SIZE = 48; // tamaño del paquete NTP que nos interesa
byte packetBuffer[NTP_PACKET_SIZE]; //buffer para paquetes entrantes y salientes al NTP
const double timezone = -3.0;

/* ==== Setup ==== */
void setup() {
        //aquí va todo el código de tu Setup, y al final, activas el reloj:
	clock.begin();
}

Esta es la función que hace la magia de consultar al servidor NTP y así ajustar el DS3231 con los datos precisos del tiempo y fecha:

//Actualizar reloj RTC con datos del NTP
void actualizareloj()
{
	ssd.clearDisplay();
	ssd.setCursor(0, 0);
	ssd.println("Actualizando RTC");
	ssd.println(ntpServerName);
	ssd.display();
	if (udp.begin(localPort) == 0) //activo puerto udp 2390 para los paquetes del servidor NTP
	{
		ssd.print("\nERROR PUERTO UDP");
	}
	else {
		while (udp.parsePacket() > 0); // discard any previously received packets
		WiFi.hostByName(ntpServerName, timeServerIP); //Busco IP del servidor NTP
		if (timeServerIP[0] == 0)
		{
			ssd.println("\nERROR NO IP");
		}
		else
		{
			ssd.println("IP " + (String)timeServerIP[0] + '.' + (String)timeServerIP[1] + '.' + (String)timeServerIP[2] + '.' + (String)timeServerIP[3]);
			ssd.display();
			yield();
			// Envio paquete NTP para pedir hora
			memset(packetBuffer, 0, NTP_PACKET_SIZE);
			packetBuffer[0] = 0b11100011;   // LI, Version, Mode
			packetBuffer[1] = 0;     // Stratum, or type of clock
			packetBuffer[2] = 6;     // Polling Interval
			packetBuffer[3] = 0xEC;  // Peer Clock Precision
									 // 8 bytes of zero for Root Delay & Root Dispersion
			packetBuffer[12] = 49;
			packetBuffer[13] = 0x4E;
			packetBuffer[14] = 49;
			packetBuffer[15] = 52;
			udp.beginPacket(timeServerIP, 123); //NTP requests are to port 123
			udp.write(packetBuffer, NTP_PACKET_SIZE);
			udp.endPacket();
			yield();
			//Ahora a esperar la respuesta del servidor NTP:
			int cb = 0;
			unsigned long mi = millis();
			while (millis() - mi < 4000 && !cb)
			{
				cb = udp.parsePacket();
				yield();
			}
			if (cb != 0)
			{
				//Vino paquete con hora
				udp.read(packetBuffer, NTP_PACKET_SIZE); // Levantamos paquete al buffer
				unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
				unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
				// combine the four bytes (two words) into a long integer
				// this is NTP time (seconds since Jan 1 1900):
				unsigned long secsSince1900 = highWord << 16 | lowWord;
				// now convert NTP time into everyday time:
				// Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
				const unsigned long seventyYears = 2208988800UL;
				// subtract seventy years:
				unsigned long epoch = secsSince1900 - seventyYears;
				// adjust to user timezone
				epoch += timezone * 3600;
				ssd.println("\nOK");
				clock.setEpoch(epoch);
			}
			else {
				ssd.print("\nERROR NO RESPONDE");
			}
		}
		udp.stopAll();
	}
	ssd.display();
	delay(3000);
}

Como siempre, por favor espero dudas y comentarios acerca de este código para gestionar el DS3231, o simplemente para que cuenten en que andan con Arduino.

4 respuestas a «Reloj DS3231 de alta precisión y eficiencia»

  1. Hola, gracias por el artículo. He hecho lo indicado y el sketch compila y corre pero me da un error «ERROR NO IP»
    Me puedes ayudar por favor.

    1. Hola, mira, el error lo da el código y está explícito. El procesador tiene que estar conectado a una red wifi que tenga acceso a internet y acceso a un servidor de DNS, entregado dentro del DHCP. Por último, el servidor de NTP debe estar activo y ser accesible localmente. Son todas cosas que debes verificar.

  2. Buenas noches, me podrias facilitar el codigo completo. Estoy tratando de implementar un reloj con matriz de leds, el DS3231 el NODEMCU; y su proyecto me facilitaría mucho las cosas ya que mi idea es actualizar el RTC cada 24 horas.

  3. Buenas tardes. Ando con un proyecto parecido, mediante el cual pretendo actualizar el DS3231 vía wifi gracias al ESP01 o ESP32, para que de la hora mediante unos LEDs RGB.
    Me podría facilitar el código de su proyecto por favor. Estoy intentando aplicar el sketch que ha aportado en esta página pero no consigo resolverlo.
    Muchas Gracias por compartir sus conocimientos.
    Saludos.

Deja una respuesta

Tu dirección de correo electrónico no será publicada.

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.