Una máquina Offline-First asume que el internet no existe por defecto. En lugar de preguntar "hacia arriba" si una tarjeta RFID es válida, la Nube "empuja" (Push) una lista actualizada de clientes autorizados hacia abajo cada noche o cada vez que hay conexión. El Despachador guarda esta lista en su memoria local no volátil (EEPROM o Flash). Cuando un cliente acerca su tarjeta, el STM32 busca el ID en su propio cerebro y responde en milisegundos.
O(n) a O(log n)Si tienes una base de datos local de 5,000 tarjetas (Whitelists de choferes y clientes VIP), y utilizas un bucle for tradicional para buscar una por una (Búsqueda Lineal), el procesador tardaría demasiado y el usuario sentiría que la máquina "se trabó". Peor aún, bloquearías las interrupciones del RTOS.
La ingeniería dicta que la Nube (AWS) debe enviarte el arreglo de IDs matemáticamente ordenado de menor a mayor. Al estar ordenado, podemos utilizar en C el algoritmo de Búsqueda Binaria (Binary Search). En lugar de 5,000 intentos, el STM32 encontrará (o rechazará) la tarjeta en un máximo de 13 saltos lógicos. El consumo de CPU es imperceptible.
Estructuras en C para Bases de Datos: Para que nuestro sistema consuma poca RAM y aproveche la EEPROM de forma secuencial, definiremos una estructura estricta (`struct`). En C, esta estructura ocupa bytes contiguos en memoria, simulando perfectamente las filas de una tabla de base de datos SQL.
Ejercicio 1: Búsqueda Binaria MISRA-C. Escribiremos el código para buscar a un cliente dentro de un arreglo en memoria. Cumpliremos la Regla 15.5 de MISRA-C ("Una función debe tener un único punto de salida en su final") para garantizar la trazabilidad de la ejecución en entornos críticos.
/**
* @file db_local_rfid.c
* @brief Base de Datos Local y Autenticación Offline-First (MISRA-C)
*/
#include "stm32f401xe.h"
#include <stdint.h>
#include <stdbool.h>
// =========================================================================
// ESTRUCTURA DE LA BASE DE DATOS (Filas de la Tabla)
// =========================================================================
// Empacamos la estructura para no desperdiciar Bytes en la EEPROM
#pragma pack(push, 1)
typedef struct {
uint32_t id_cliente; // ID Criptográfico leído del Sector Mifare (Día 245)
uint32_t saldo_max_litros;// Saldo prepagado multiplicado x10 para evitar floats
uint8_t estado_bloqueo; // 0 = Activo, 1 = Tarjeta Robada/Suspendida
} FilaCliente_t;
#pragma pack(pop)
// Simulación de la memoria EEPROM ya cargada por AWS (Siempre ORDENADA de menor a mayor por ID)
#define TOTAL_CLIENTES_LOCALES (5U)
static const FilaCliente_t db_clientes_eeprom[TOTAL_CLIENTES_LOCALES] = {
{ 1010U, 500U, 0U }, // Cliente 1010, Saldo 50.0L, Activo
{ 2450U, 0U, 0U }, // Cliente 2450, Saldo 0.0L, Activo (Sin saldo)
{ 3045U, 120U, 0U }, // Cliente 3045, Saldo 12.0L, Activo
{ 6780U, 900U, 1U }, // Cliente 6780, Saldo 90.0L, BLOQUEADO (Robo)
{ 9912U, 450U, 0U } // Cliente 9912, Saldo 45.0L, Activo
};
// =========================================================================
// MOTOR DE BÚSQUEDA BINARIA (O(log n))
// =========================================================================
/**
* @brief Busca un ID Criptográfico en la DB local usando algoritmo Binario.
* @param id_objetivo: El ID decodificado de la tarjeta física.
* @param p_saldo_salida: Puntero para devolver el saldo disponible si existe.
* @retval bool: TRUE si el cliente existe Y está autorizado.
*/
bool Verificador_Tokens_Local(uint32_t id_objetivo, uint32_t* p_saldo_salida) {
// Variables de control de la Búsqueda Binaria
int32_t limite_izquierdo = 0;
int32_t limite_derecho = (int32_t)(TOTAL_CLIENTES_LOCALES - 1U);
// MISRA-C 15.5: Único punto de salida (Single exit point)
bool acceso_concedido = false;
bool busqueda_terminada = false;
// Asignación segura por defecto
*p_saldo_salida = 0U;
// Bucle de partición a la mitad
while ((limite_izquierdo <= limite_derecho) && (!busqueda_terminada)) {
// Calcular el punto medio para cortar la tabla en dos
int32_t punto_medio = limite_izquierdo + ((limite_derecho - limite_izquierdo) / 2);
uint32_t id_leido = db_clientes_eeprom[punto_medio].id_cliente;
if (id_leido == id_objetivo) {
// ¡MATCH ENCONTRADO!
// Evaluamos la lógica de negocio (Saldo y Bloqueo)
if (db_clientes_eeprom[punto_medio].estado_bloqueo == 0U) {
if (db_clientes_eeprom[punto_medio].saldo_max_litros > 0U) {
*p_saldo_salida = db_clientes_eeprom[punto_medio].saldo_max_litros;
acceso_concedido = true;
}
}
// Forzamos salida del bucle
busqueda_terminada = true;
} else if (id_leido < id_objetivo) {
// El objetivo está en la mitad superior de la tabla
limite_izquierdo = punto_medio + 1;
} else {
// El objetivo está en la mitad inferior de la tabla
limite_derecho = punto_medio - 1;
}
}
// Único return de toda la función
return acceso_concedido;
}
// =========================================================================
// INTEGRACIÓN CON LA TAREA RFID (Día 245)
// =========================================================================
/*
// ... dentro del estado RFID_ESTADO_LEYENDO ...
uint32_t id_cliente_hex = (buffer[0]<<24) | (buffer[1]<<16) | (buffer[2]<<8) | buffer[3];
uint32_t limite_litros_disponibles = 0U;
if (Verificador_Tokens_Local(id_cliente_hex, &limite_litros_disponibles)) {
// LCD_Mostrar_Mensaje("ACCESO OK");
// Despachador_Iniciar_Venta(limite_litros_disponibles);
} else {
// LCD_Mostrar_Mensaje("TARJETA RECHAZADA O SIN SALDO");
}
*/
Objetivo del día: Alta Disponibilidad (99.99% Uptime) y Computación en el Borde (Edge Computing).
Vender una solución IoT a una corporación gasera requiere garantizar la operación. Si su flotilla de camiones llega a cargar gas en la madrugada y tu Despachador no abre la válvula porque el proveedor de internet "está en mantenimiento", perderán miles de dólares en logística. El cliente te devolverá el equipo.
Al implementar la arquitectura **Offline-First**, has trasladado la inteligencia de la Nube hacia el "Borde" (Edge Computing). El despachador consulta una réplica microscópica de la base de datos dentro de su propia EEPROM mediante un algoritmo de **Búsqueda Binaria `O(log n)`**. Esto permite que la autenticación tarde menos de 1 milisegundo. Si un chofer reporta el robo de su tarjeta, el sistema de AWS enviará por MQTT una actualización delta que modificará el bit de `estado_bloqueo` en la EEPROM local (Background Sync). Esta robustez híbrida (Inteligencia Local + Actualización Global Asíncrona) es lo que distingue al hardware amateur de grado "Arduino" de un verdadero activo industrial de misión crítica.
O(n)) consume demasiados ciclos de CPU, bloqueando el RTOS a medida que la base de datos crece.O(log n)). Esto reduce exponencialmente el tiempo de procesamiento (ej. buscar entre 10,000 clientes toma un máximo de 14 operaciones lógicas en lugar de 10,000).