En el protocolo MQTT, puedes decidir qué tan importante es el mensaje que estás a punto de enviar. Esto se configura con un solo número (0, 1 o 2) llamado **Calidad de Servicio (Quality of Service)**.
PUBACK (Publish Acknowledge). Si el cliente no recibe el PUBACK tras unos milisegundos, retransmitirá el mensaje indefinidamente. Uso ideal: Ventas de Gas, Tickets, Alarmas Críticas.Cuando utilizas la instrucción AT+MQTTPUB=0,"tema","datos",1,0 en tu ESP32 (Observa el `1` indicando QoS 1), el firmware interno del módem se encarga de la burocracia por ti. El módem no te devolverá la palabra OK por el puerto UART hasta que el propio módem haya recibido el sagrado `PUBACK` desde los servidores de Amazon. Si falla, el ESP32 responderá ERROR. ¡Esto facilita increíblemente nuestra vida en el STM32!
Circuito: Físicamente seguimos operando sobre el módulo ESP32. La diferencia radicará en cómo nuestra Máquina de Estados del STM32 (Día 210) interpreta las respuestas del UART al enviar transacciones monetarias versus datos de mantenimiento.
Ejercicio 1: Modificaremos nuestra librería mqtt_topics.c para soportar el envío paramétrico de QoS. Luego, actualizaremos el método Sincronizar_Venta_Nube para que utilice QoS 1 y condicione el borrado del ticket en EEPROM a la validación estricta de este protocolo.
/**
* @file mqtt_qos_manager.c
* @brief Gestión estricta de Calidad de Servicio (QoS) en transacciones financieras MQTT
*/
#include "stm32f401xe.h"
#include "cmsis_os2.h"
#include <stdio.h>
// =======================================================
// CONSTRUCTOR AVANZADO DE COMANDO MQTTPUB
// =======================================================
/**
* @brief Ensambla el comando AT permitiendo seleccionar el nivel de QoS
*/
bool Construir_MQTTPUB_QoS(const char* sub_topic, const char* json_payload, int qos_level, char* buffer_salida, size_t tam_maximo) {
// Tópico duro: gas_corp/jalisco/guadalajara/disp_01
char topic_abs[128];
snprintf(topic_abs, sizeof(topic_abs), "%s/%s/%s/%s/%s",
MQTT_ROOT_COMPANY, MQTT_REGION, MQTT_CITY, DEVICE_ID, sub_topic);
// Inyectamos el nivel de QoS solicitado (0 o 1)
int len_at = snprintf(buffer_salida, tam_maximo,
"AT+MQTTPUB=0,\"%s\",\"%s\",%d,0\r\n",
topic_abs, json_payload, qos_level);
if (len_at < 0 || (size_t)len_at >= tam_maximo) return false;
return true;
}
// =======================================================
// EL TRANSMISOR CON PROTECCIÓN FINANCIERA (Store & Forward)
// =======================================================
bool Sincronizar_Venta_Nube_Segura(Ticket_Fisico_t* ticket) {
char buffer_json[128];
char comando_mqtt[256];
// 1. Generamos el Payload JSON
if (!Generar_Payload_JSON(ticket, buffer_json, sizeof(buffer_json))) return false;
// 2. CRÍTICO: Solicitamos QoS 1 (Garantía de entrega)
if (!Construir_MQTTPUB_QoS("telemetria/ventas", buffer_json, 1, comando_mqtt, sizeof(comando_mqtt))) {
return false;
}
UART_Enviar_String("[QoS 1] Publicando venta... Esperando acuse de AWS (PUBACK).\r\n");
// 3. Envío y Espera (Bloqueo lógico)
// El módem ESP32 tardará más de lo habitual porque la radio frecuencia
// debe viajar a la nube, la nube verificar el JSON, y enviar la respuesta.
// Damos 5000 ms de Timeout para no declarar un falso error de red.
bool resultado_qos1 = AT_Enviar_Comando_Asincrono(comando_mqtt, 5000);
if (resultado_qos1 == true) {
// La bandera UART_OK se disparó.
// Esto significa matemáticamente que AWS recibió el paquete.
UART_Enviar_String("[QoS 1] ¡PUBACK Recibido! Transacción Sellada.\r\n");
return true;
} else {
// Pudo haber sido Timeout o ERROR explícito.
// El ESP32 reintentó internamente varias veces y se rindió.
UART_Enviar_String("[QoS 1] FALLO CRÍTICO. Protegiendo datos en EEPROM.\r\n");
return false;
}
}
// =======================================================
// RECORDATORIO: EL LATIDO DE VIDA (QoS 0)
// =======================================================
/*
void Publicar_Latido_Vida(void) {
char json_salud[64] = "{\"status\":1}";
char cmd_mqtt[128];
// Para el Latido, pedimos QoS 0 (Ahorramos ancho de banda y tiempo)
Construir_MQTTPUB_QoS("estado/salud", json_salud, 0, cmd_mqtt, sizeof(cmd_mqtt));
// El ESP32 regresará OK inmediatamente sin esperar a AWS
AT_Enviar_Comando_Asincrono(cmd_mqtt, 1000);
}
*/
Objetivo del día: Garantía de Idempotencia y Prevención de Robo Cibernético de Datos.
Imagina que la Tarea de Red de tu Despachador intenta subir una venta de $1000 pesos usando QoS 0. El mensaje sale al aire, pero hay ruido celular. El STM32, confiado, borra la venta de la EEPROM creyendo que ya se fue. Al finalizar el mes, al dueño de la gasolinera le faltan $1000 pesos en el portal web corporativo, pero el nivel del tanque físico dice que el gas sí salió. El dueño acusará a los empleados de robo físico, cuando en realidad fue "robo de datos" en tránsito.
Al elevar la exigencia de tus transmisiones financieras a **MQTT QoS 1**, le has prohibido a tu STM32 borrar cualquier registro de su disco duro físico (EEPROM) hasta obtener un recibo firmado por el servidor de Amazon (PUBACK). Si hay ruido celular, el ESP32 retransmitirá internamente el paquete hasta el cansancio. Aviso de Arquitectura: QoS 1 garantiza que el paquete llegue *al menos una vez*, lo que significa que a veces, AWS podría recibir el mismo ticket de venta dos veces. Para solucionar esto sin usar el lento QoS 2, tu corporativo simplemente programará la base de datos para ignorar tickets que tengan el mismo `folio_numero`. Has cerrado la brecha de pérdida de dinero a nivel de telecomunicaciones.
PUBACK). Si el emisor no lo recibe a tiempo, la red retransmitirá el paquete. Es obligatorio para Telemetría Financiera, Control de Actuadores y Alarmas Críticas de Gas.