Este es el principio fundamental de la telemetría tolerante a fallos espaciales e industriales. Consta de dos fases inseparables:
0x00 (Pendiente de Envío). Si en ese instante cae un rayo y la máquina se apaga, la venta ya es inmortal.0x00. Si encuentra uno, lo lee, lo convierte a JSON y lo envía a AWS. Solo cuando AWS responde con un 200 OK, la Tarea WiFi accede a la EEPROM y cambia el Byte de Estado a 0x01 (Enviado Exitosamente).Con este patrón, la Tarea de Metrología no sabe ni le importa si hay internet. Su trabajo termina al guardar en EEPROM. La Tarea WiFi actúa como un "barrendero" de fondo (Sweeper), que poco a poco va subiendo a la nube todas las ventas que se hayan acumulado durante el día, respetando los límites de velocidad del ESP32.
Circuito: Este patrón requiere un chip de memoria no volátil, típicamente una EEPROM I2C externa (como la AT24C256) conectada a los pines I2C del STM32 (SCL, SDA). La escritura a este chip está protegida por un Mutex (bus_hardware_mutex_id), garantizando que si la Tarea Metrología quiere guardar un nuevo ticket, la Tarea WiFi no interfiera si estaba leyendo un ticket viejo.
Ejercicio 1: Modificaremos el flujo de nuestra Tarea IoT (WiFi). En el estado ESTADO_IDLE_READY, la tarea ya no leerá una Cola de RAM. Ahora llamará a una función de la capa de acceso a datos (EEPROM) para buscar el ticket más antiguo no sincronizado. Si el envío es un éxito, sobrescribirá el byte de estado de ese ticket específico.
/**
* @file store_and_forward.c
* @brief Implementación del patrón de resiliencia Store & Forward en RTOS
*/
#include "stm32f401xe.h"
#include "cmsis_os2.h"
#include <stdbool.h>
// El Mutex global para no chocar en el bus I2C (Día 190)
extern osMutexId_t bus_hardware_mutex_id;
// =======================================================
// CAPA DE ACCESO A DATOS (EEPROM Driver Abstracto)
// =======================================================
// Definimos los estados del Ticket en la memoria física
#define TICKET_PENDIENTE 0x00
#define TICKET_ENVIADO 0x01
// Estructura adaptada para alineación de Memoria Física
#pragma pack(push, 1)
typedef struct {
uint8_t estado_sync; // 1 byte: 0=Pendiente, 1=Enviado
uint32_t ticket_num; // 4 bytes
float litros_venta; // 4 bytes
float monto_total; // 4 bytes
uint32_t timestamp; // 4 bytes (Fecha NTP UNIX)
} Ticket_Fisico_t; // Total: 17 bytes por registro
#pragma pack(pop)
/**
* @brief Escanea la EEPROM I2C buscando el primer ticket con estado TICKET_PENDIENTE
*/
bool EEPROM_Buscar_Ticket_No_Sincronizado(Ticket_Fisico_t* buffer_destino, uint16_t* direccion_memoria) {
bool encontrado = false;
// Adquirimos el candado I2C (Esperamos máximo 500ms si alguien más lo usa)
if (osMutexAcquire(bus_hardware_mutex_id, 500) == osOK) {
// Simulación: Escaneamos los 1000 espacios de memoria disponibles en la AT24C256
for (uint16_t mem_addr = 0; mem_addr < 17000; mem_addr += sizeof(Ticket_Fisico_t)) {
// Leemos 1 solo byte (el estado_sync) de la EEPROM
uint8_t estado;
I2C_EEPROM_Read(mem_addr, &estado, 1);
if (estado == TICKET_PENDIENTE) {
// ¡Bingo! Encontramos un ticket atorado. Lo leemos completo.
I2C_EEPROM_Read(mem_addr, (uint8_t*)buffer_destino, sizeof(Ticket_Fisico_t));
*direccion_memoria = mem_addr; // Guardamos su ubicación para luego marcarlo
encontrado = true;
break;
}
}
osMutexRelease(bus_hardware_mutex_id);
}
return encontrado;
}
/**
* @brief Cambia el byte 'estado_sync' a TICKET_ENVIADO en la memoria física
*/
void EEPROM_Marcar_Ticket_Enviado(uint16_t direccion_memoria) {
if (osMutexAcquire(bus_hardware_mutex_id, 500) == osOK) {
uint8_t nuevo_estado = TICKET_ENVIADO;
// Sobrescribimos exclusivamente el primer byte de ese registro
I2C_EEPROM_Write(direccion_memoria, &nuevo_estado, 1);
osMutexRelease(bus_hardware_mutex_id);
}
}
// =======================================================
// LA TAREA IOT (El Barrendero "Store & Forward")
// =======================================================
// Asumimos que la Tarea WiFi llegó al estado ESTADO_IDLE_READY (Túnel abierto)
void Tarea_WiFi_Forward(void) {
Ticket_Fisico_t ticket_perdido;
uint16_t ubicacion_eeprom;
// 1. Preguntarle al hardware físico si quedó algo pendiente tras el apagón
if (EEPROM_Buscar_Ticket_No_Sincronizado(&ticket_perdido, &ubicacion_eeprom)) {
UART_Enviar_String("[FORWARD] Ticket pendiente encontrado. Intentando enviar...\r\n");
// 2. Convertir a JSON y enviar (Rutina del Día 209)
// Simulamos la llamada a la función que empaqueta y hace el AT+CIPSEND
bool exito_nube = Sincronizar_Venta_Nube_Estructura(&ticket_perdido);
// 3. LA CONFIRMACIÓN CRÍTICA
if (exito_nube == true) {
// AWS respondió 200 OK. La plata está segura en la base de datos central.
// AHORA Y SÓLO AHORA, borramos (marcamos) el ticket localmente.
EEPROM_Marcar_Ticket_Enviado(ubicacion_eeprom);
UART_Enviar_String("[FORWARD] Sincronización sellada en EEPROM.\r\n");
} else {
// AWS falló (Timeout, 500 Internal Error, o cable roto).
// NO HACEMOS NADA. El ticket sigue marcado como PENDIENTE (0x00).
// En el próximo ciclo (tras la reconexión de red), volveremos a intentarlo.
UART_Enviar_String("[FORWARD] Falla de nube. Conservando ticket offline.\r\n");
}
}
}
Objetivo del día: Cumplimiento de la Norma Oficial Mexicana (Bitácora Inalterable) y Tolerancia Total a Fallos Eléctricos.
Las autoridades fiscales y metrológicas auditan los despachadores de combustible buscando una cosa: la certeza de que el medidor físico coincide con la contabilidad reportada. Si diseñas una máquina que "olvida" las ventas cuando se va el Internet o se corta la luz, estás diseñando una máquina ilegal.
Al migrar la arquitectura de tu RTOS al patrón **Store & Forward**, has blindado el proyecto. Tu sistema prioriza la física sobre la red: el gas no se despacha hasta que la EEPROM garantiza que hay espacio, y la venta se graba en silicio (Store) en nanosegundos al cerrar la válvula. Si un huracán destruye las antenas celulares de la región, tu despachador seguirá vendiendo gas localmente durante semanas, acumulando hasta 17,000 registros seguros en su chip AT24C256. El día que un técnico conecte un cable de red funcional, la Tarea WiFi (Forward) despertará, barrerá la EEPROM y vaciará el historial completo de ventas atrasadas hacia el servidor de Amazon, validando cada una contra un `HTTP 200 OK`. Has construido una infraestructura a prueba de desastres naturales.