El Protocolo de Tiempo de Red opera sobre el puerto UDP 123. Su propósito es compensar la latencia del internet para sincronizar relojes de computadoras a nivel global. En lugar de escribir el complejo cliente UDP en nuestro STM32, el ESP32 nos regala dos comandos AT mágicos:
AT+CIPSNTPCFG=1,-6,"pool.ntp.org". Le decimos al chip: Activa SNTP (1), ajusta la zona horaria a GMT-6 (Hora Estándar del Centro, CST, Guadalajara), y pregúntale al servidor global pool.ntp.org.AT+CIPSNTPTIME?. Con este comando interrogamos al ESP32 sobre qué hora tiene registrada actualmente.El ESP32 no nos responde con un lindo `struct` binario en lenguaje C. Nos responde con texto puro (ASCII):
+CIPSNTPTIME:Fri Sep 11 08:15:13 2026
Para el STM32, esto es solo una cadena de caracteres inútiles. Necesitamos escribir un Parser: una función en C que divida esta oración, encuentre que "Sep" significa el mes 9, que "11" es el día, y que alimente estos números en la estructura RTC_TimeTypeDef del hardware STM32.
Circuito: El STM32 tiene un periférico interno llamado RTC (Real-Time Clock). Funciona independientemente de la CPU y requiere que le hayas conectado un cristal de cuarzo físico de 32.768 kHz en los pines OSC32_IN y OSC32_OUT, respaldado por la batería de botón en el pin VBAT.
Ejercicio 1: Escribiremos la función en C que recibe el buffer sucio del puerto Serial, extrae las variables matemáticas utilizando la clásica función sscanf, y configura el silicio del STM32. Esta función debe ejecutarse solo una vez cada 24 horas por nuestra FSM de red.
/**
* @file ntp_parser_rtos.c
* @brief Extracción de fecha/hora de un String AT y actualización del Hardware RTC
*/
#include "stm32f401xe.h"
#include "cmsis_os2.h"
#include <stdio.h>
#include <string.h>
// Manejador Global del Reloj en Tiempo Real
extern RTC_HandleTypeDef hrtc;
// =======================================================
// TRADUCTOR DE MESES (De "Sep" a 9)
// =======================================================
uint8_t Convertir_Mes_A_Numero(const char* mes_str) {
if (strncmp(mes_str, "Jan", 3) == 0) return RTC_MONTH_JANUARY;
if (strncmp(mes_str, "Feb", 3) == 0) return RTC_MONTH_FEBRUARY;
if (strncmp(mes_str, "Mar", 3) == 0) return RTC_MONTH_MARCH;
if (strncmp(mes_str, "Apr", 3) == 0) return RTC_MONTH_APRIL;
if (strncmp(mes_str, "May", 3) == 0) return RTC_MONTH_MAY;
if (strncmp(mes_str, "Jun", 3) == 0) return RTC_MONTH_JUNE;
if (strncmp(mes_str, "Jul", 3) == 0) return RTC_MONTH_JULY;
if (strncmp(mes_str, "Aug", 3) == 0) return RTC_MONTH_AUGUST;
if (strncmp(mes_str, "Sep", 3) == 0) return RTC_MONTH_SEPTEMBER;
if (strncmp(mes_str, "Oct", 3) == 0) return RTC_MONTH_OCTOBER;
if (strncmp(mes_str, "Nov", 3) == 0) return RTC_MONTH_NOVEMBER;
if (strncmp(mes_str, "Dec", 3) == 0) return RTC_MONTH_DECEMBER;
return 1; // Default por error
}
// =======================================================
// EL PARSER Y CONFIGURADOR
// =======================================================
bool Parsear_y_Ajustar_RTC(const char* buffer_uart) {
// Ejemplo de entrada:
// "+CIPSNTPTIME:Fri Sep 11 08:15:13 2026"
char str_mes[4];
int dia, hr, min, seg, anio;
// %*s descarta la primera palabra ("Fri")
// %3s lee exactamente 3 caracteres del mes
int conversiones = sscanf(buffer_uart, "+CIPSNTPTIME:%*s %3s %d %d:%d:%d %d",
str_mes, &dia, &hr, &min, &seg, &anio);
if (conversiones == 6) {
// --- 1. ESTRUCTURA DE TIEMPO ---
RTC_TimeTypeDef sTime = {0};
sTime.Hours = hr;
sTime.Minutes = min;
sTime.Seconds = seg;
// Obligatorio en STM32: Formato BIN (Binario) o BCD. Escogemos BIN.
if (HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) != HAL_OK) {
return false;
}
// --- 2. ESTRUCTURA DE FECHA ---
RTC_DateTypeDef sDate = {0};
sDate.Date = dia;
sDate.Month = Convertir_Mes_A_Numero(str_mes);
sDate.Year = anio - 2000; // STM32 guarda el año en dos dígitos (Ej. 26 en lugar de 2026)
if (HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) != HAL_OK) {
return false;
}
UART_Enviar_String("[NTP] Hardware RTC Sincronizado Exitosamente!\r\n");
return true;
}
UART_Enviar_String("[NTP] ERROR: Formato de String desconocido.\r\n");
return false;
}
// =======================================================
// INTEGRACIÓN EN LA TAREA DE RED (Fragmento)
// =======================================================
// ... Dentro de Thread_WiFi_Modem, después de conectarse al WiFi:
// 1. Configurar NTP para Zona CST (GMT-6, Guadalajara)
AT_Enviar_Comando("AT+CIPSNTPCFG=1,-6,\"pool.ntp.org\"\r\n", 1000);
// Dejar que el ESP32 hable con el servidor NTP unos segundos...
osDelay(3000);
// 2. Pedir la hora. Aquí usamos una función especial que no solo
// espera el "OK", sino que captura la cadena que tiene el "+CIPSNTPTIME"
char buffer_respuesta[128];
if (AT_Capturar_Respuesta("AT+CIPSNTPTIME?\r\n", "+CIPSNTPTIME:", buffer_respuesta, 2000)) {
// 3. Ejecutar el Parser
Parsear_y_Ajustar_RTC(buffer_respuesta);
}
Objetivo del día: Integridad Temporal para Certificación Fiscal y Auditoría.
La Ley Federal para estaciones de carburación (NOM-005-SCFI o equivalentes internacionales) exige que toda máquina despachadora mantenga un registro inalterable (Datalogger) de cada venta. Si tu Despachador de Gas LP almacena un log diciendo que vendiste 100 litros el "1 de Enero de 1970 a las 00:00:15", ante una auditoría fiscal del SAT o Profeco, ese registro será considerado inválido o corrompido, enfrentándote a multas millonarias por evasión o alteraciones metrológicas.
Al dotar a tu máquina de un cliente **NTP con Parser Inteligente**, has resuelto el problema de la entropía del tiempo. No importa si tu batería de litio muere y la RAM del reloj se borra; en el preciso milisegundo en que tu despachador arranca y logra conectarse a internet, interrogará a los servidores atómicos globales, ajustará su huso horario a Guadalajara (GMT-6), cortará quirúrgicamente el string ASCII y grabará en el silicio (RTC) que hoy es Viernes 11 de Septiembre de 2026. A partir de ese momento, cada ticket emitido, cada error de la Tarea Centinela y cada válvula cerrada quedará sellada con una marca de tiempo absoluta e incuestionable. Tu telemetría ya es digna de un reporte fiscal corporativo.
sscanf de la librería estándar (<stdio.h>) permite buscar patrones y extraer cadenas de texto y enteros ignorando "basura" (con el formato %*s).RTC_TimeTypeDef y RTC_DateTypeDef), lo cual estandariza el cronometraje en milisegundos reales y permite disparar alarmas por hardware en el futuro.