Sincronización precisa del RTC DS3231 con GPS en Arduino Nano
Componentes y suministros
| Arduino Nano R3 | × | 1 |
| Módulo reloj DS3231 | × | 1 |
| Módulo GPS NEO-6M | × | 1 |
| Conversor de nivel bidireccional (level shifter) | × | 1 |
| Pantalla LCD 16×2 | × | 1 |
| Mochila I2C para LCD | × | 1 |
| Zócalos y tiras de pines (varios) | × | — |
| Placa de tiras (stripboard) 24×37 | × | 1 |
| Resistencias 4.7 kΩ | × | 2 |
| Pulsador momentáneo 6 mm | × | 1 |
Sobre este proyecto
Mi DS3231 deriva. Lo actualizaba compilando código con la hora del sistema, pero resultaba impreciso porque la compilación y subida se interrumpían por otros procesos del ordenador.
Al descubrir que el GPS proporciona hora y fecha UTC precisas, decidí usarlo para ajustar el DS3231.
Circuito
Mochila I2C LCD → Arduino:
- GND – GND
- VCC – 5 V
- SDA – SDA (A4)
- SCL – SCL (A5)
Módulo DS3231 → Arduino:
- GND – GND
- VCC – 5 V
- SDA – SDA (A4)
- SCL – SCL (A5)
CONVERSOR DE NIVEL desde Arduino:
- 5 V – 5 V
- GND – GND
- D3 – BUS
- D4 – BUS
NEO-6M GPS desde conversor:
- 3 V – VCC
- GND – GND
- BUS – TXD (llega a D3)
- BUS – RXD (llega a D4)
CONTACTOS INTERRUPTOR:
- INTERRUPTOR – D9
- INTERRUPTOR – GND
Alimentación
Alimento el circuito mediante el cable USB.
Software
El 99 % del software es el ejemplo parsing.ino de la librería Adafruit GPS. Añadí código para pasar la fecha/hora GPS extraída al DS3231 y código adicional para mostrar la hora del DS3231 en el LCD.
Librerías usadas
- Adafruit GPS Library v1.0.3
- RTClib (Adafruit) v1.2.0
- NewLiquidCrystal para la mochila I2C del LCD.
Funcionamiento
- Enciende la unidad.
- El LCD muestra la fecha/hora actual en los registros del DS3231.
- Espera a que el GPS haga fix (bloqueo).
- Pulsa el botón para ajustar la hora vía señal GPS.
- El LCD muestra la nueva hora.
Limitaciones de funcionamiento
- El GPS tarda en hacer fix y no hay indicador de que se haya logrado. Pulsar el botón sin fix puso mi DS3231 a 2000:0:0 y 0:0:0.
- Cuando el LED rojo del GPS empieza a parpadear parece el mejor momento para pulsar el botón – pueden ser decenas de minutos (en interiores).
- Inicialmente mi DS3231 tenía la pila gastada; al pulsar SET la fecha se ponía a 2000:0:0 y la hora a 0:164:164. Tardó un rato en cargar lo suficiente para aceptar datos GPS y seguir funcionando solo.
Particularidades del GPS
La placa comprada decía simplemente "NEO 6M GPS" con pines marcados. El datasheet [1] indica que funciona a ~3 V, de ahí el conversor de nivel. Sin embargo, al inspeccionar encontré un chip 4A2D soldado, que es un regulador de tensión, pero no garantiza que las líneas de comunicación estén adaptadas de nivel (mi unidad no traía diagrama).
He leído que distintos módulos GPS varían en su capacidad para recibir señal en interiores, de ahí la pantalla LCD para poder sacarlo fuera.
Particularidad del LCD
Uso uno de los modelos raros con dirección I2C 0x3F; la mayoría usan 0x27. Si no se ve nada, prueba a cambiar esa dirección primero.
Particularidad de la librería
La librería RTClib (Adafruit v1.2.0) establece los registros de hora y fecha pero no el de día de la semana del DS3231. La librería tiene una función para devolver el día de la semana calculado al vuelo con los valores del registro de fecha. Al principio me pareció raro, pero luego vi que tiene sentido: establecer el registro de día requeriría que el código lo actualice en años bisiestos, mientras que calcularlo por fórmula evita esto.
UTC y hora local
Busqué sobre UTC y vi que no requería cambio para mi hora local; si lo necesitas, tendrás que añadir ese código tú mismo.
Referencias
[1] https://www.u-blox.com/sites/default/files/products/documents/NEO-6_DataSheet_(GPS.G6-HW-09005).pdf (consultado dic/2018)
Código
Archivo de código para configurar DS3231
// Funciones de fecha/hora con DS3231 RTC vía I2C y librería Wire
#include <Wire.h>
#include "RTClib.h" // Adafruit v1.2.0
RTC_DS3231 RTC;
char daysOfTheWeek[7][12] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"};
const int days_string_length = 12;
// Para display LCD
#include <LiquidCrystal_I2C.h>
LiquidCrystal_I2C lcd(0x3F, 2, 1, 0, 4, 5, 6, 7, 3, POSITIVE); // Dirección 0x3F (la mayoría 0x27)
// Para botón
#define button 9
// Test code for Adafruit GPS modules using MTK3329/MTK3339 driver
#include <Adafruit_GPS.h>
#include <SoftwareSerial.h>
SoftwareSerial mySerial(3, 2); // GPS TX→D3, RX→D2 (con level shifter)
Adafruit_GPS GPS(&mySerial);
#define GPSECHO false
boolean usingInterrupt = false;
void useInterrupt(boolean);
void setup() {
Serial.begin(115200);
Serial.println("Adafruit GPS library basic test!");
GPS.begin(9600);
GPS.sendCommand(PMTK_SET_NMEA_OUTPUT_RMCGGA);
GPS.sendCommand(PMTK_SET_NMEA_UPDATE_1HZ);
GPS.sendCommand(PGCMD_ANTENNA);
useInterrupt(true);
delay(1000);
mySerial.println(PMTK_Q_RELEASE);
lcd.begin(16, 2);
lcd.backlight();
lcd.clear();
pinMode(button, INPUT_PULLUP);
}
SIGNAL(TIMER0_COMPA_vect) {
char c = GPS.read();
#ifdef UDR0
if (GPSECHO) if (c) UDR0 = c;
#endif
}
void useInterrupt(boolean v) {
if (v) {
OCR0A = 0xAF;
TIMSK0 |= _BV(OCIE0A);
usingInterrupt = true;
} else {
TIMSK0 &= ~_BV(OCIE0A);
usingInterrupt = false;
}
}
uint32_t timer = millis();
void loop() {
if (!usingInterrupt) {
char c = GPS.read();
if (GPSECHO) if (c) Serial.print(c);
}
if (GPS.newNMEAreceived()) {
if (!GPS.parse(GPS.lastNMEA())) return;
}
if (timer > millis()) timer = millis();
static bool second_time_round = false;
// Escribir datos al reloj si se pulsa botón (normalmente HIGH por pullup)
if (!digitalRead(button)) {
RTC.adjust(DateTime(GPS.year, GPS.month, GPS.day, GPS.hour, GPS.minute, GPS.seconds));
lcd.clear();
lcd.setCursor(2, 0);
lcd.print("Pressed set");
delay(5000);
lcd.clear();
}
DateTime now = RTC.now();
lcd.setCursor(4, 0);
lcd.print(now.year(), DEC);
lcd.print('/');
lcd.print(now.month(), DEC);
lcd.print('/');
lcd.print(now.day(), DEC);
lcd.print(' ');
lcd.setCursor(4, 1);
lcd.print(now.hour(), DEC);
lcd.print(':');
lcd.print(now.minute(), DEC);
lcd.print(':');
lcd.print(now.second(), DEC);
lcd.print(" ");
}
Esquemáticos


Proceso de manufactura
- Dejando un legado duradero: liderazgo auténtico para un futuro mejor
- SMRP Publica Conjunto Estandarizado de KPIs para Profesionales de Mantenimiento y Fiabilidad
- Rectificado plano: Técnicas tradicionales para precisión
- Por Qué un Torno CNC de Guías Prismáticas (Box-Way) Es la Elección Óptima para Precisión y Rendimiento
- Juegos de Punzones: Guía Completa y Detallada
- Tornos CNC de guía lineal: precisión, velocidad y producción ligera
- Guía esencial para elegir el juego de herramientas de mecánico adecuado
- Entendiendo Diagramas de Venn: Visualizando Relaciones entre Conjuntos
- Tornillos de presión (set screws) explicados: diseño, diferencias y aplicaciones
- Escuadras Explicadas: Propósito, Tipos y Usos en Dibujo Técnico
- Configuración del Entorno de Desarrollo Java 10: Compilar, Construir y Ejecutar