A diferencia de los comandos de configuración (como AT+CWMODE) que se resuelven instantáneamente con un OK, transmitir un paquete HTTP a la nube involucra hardware que transiciona de estado (Buffers UART a Buffers TCP). El ESP-AT exige un protocolo de dos pasos inquebrantable:
AT+CIPSEND=256\r\n. Le estás diciendo al módem: "Prepárate, voy a enviarte exactamente 256 bytes crudos. Acomoda tu memoria".> (mayor que). Este es tu semáforo en verde. Si mandas los datos antes de ver este símbolo, el ESP32 los leerá como "basura de configuración" y fallará.>, el STM32 arroja todo su bloque HTTP POST por UART (sin importar si hay llaves, retornos de carro, etc., porque el ESP32 ahora está en "Modo Passthrough" contando bytes).SEND OK.Una vez enviado, el servidor AWS procesará tu venta. Si todo salió bien, el servidor HTTP responderá con un HTTP/1.1 200 OK. El ESP32 intercepta esto de la nube y te lo pasa por el UART prefijándolo con el texto +IPD (Incoming Packet Data). Esto te asegura, sin lugar a dudas, que tu registro financiero vive ahora seguro en la base de datos global.
Circuito: Físicamente seguimos sobre el mismo túnel. El túnel debe haber sido abierto por AT+CIPSTART en el estado previo de la máquina.
Ejercicio 1: Integraremos todo. Crearemos la función final Sincronizar_Venta_Nube. Esta rutina extraerá un ticket de la Cola de Ventas (si lo hay), construirá el JSON (Día 207), envolverá en HTTP (Día 208), y ejecutará la secuencia AT+CIPSEND cuidando los tiempos de respuesta del ESP32.
/**
* @file cloud_tx_rtos.c
* @brief Transmisor definitivo de Ventas de Gas LP hacia AWS IoT mediante ESP-AT
*/
#include "stm32f401xe.h"
#include "cmsis_os2.h"
#include <stdio.h>
#include <string.h>
// Banderas de la Interrupción UART (Manejan las respuestas asíncronas)
#define FLAG_AT_OK (1U << 0)
#define FLAG_AT_PROMPT (1U << 2) // La ISR levanta esto cuando ve el caracter '>'
#define FLAG_HTTP_200 (1U << 3) // La ISR busca "+IPD" seguido de "200 OK"
extern osEventFlagsId_t uart_rx_flags_id;
extern osMessageQueueId_t mailbox_telemetria_id; // De donde sacamos las ventas locales
// Utilidades de los días anteriores
extern bool Generar_Payload_JSON(Venta_GasLP_t* obj, char* buf, size_t max);
extern size_t Generar_Paquete_HTTP_POST(const char* json, char* buf_http, size_t max);
// =======================================================
// LA RUTINA MAESTRA DE TRANSMISIÓN
// =======================================================
/**
* @retval bool: true si la Nube respondió 200 OK, false si falló el canal.
*/
bool Sincronizar_Venta_Nube() {
Venta_GasLP_t venta_pendiente;
// 1. Verificamos si hay una venta lista (Timeout 0, si no hay, salimos rápido)
if (osMessageQueueGet(mailbox_telemetria_id, &venta_pendiente, NULL, 0) != osOK) {
return true; // No hay nada que enviar, retornamos true para no dar falsa alarma.
}
char buffer_json[128];
char buffer_http[512];
// 2. Construcción de Cadena Determinista
if (!Generar_Payload_JSON(&venta_pendiente, buffer_json, sizeof(buffer_json))) return false;
size_t tamaño_paquete_total = Generar_Paquete_HTTP_POST(buffer_json, buffer_http, sizeof(buffer_http));
if (tamaño_paquete_total == 0) return false;
// 3. PASO 1: DECLARACIÓN DE INTENCIONES (AT+CIPSEND)
char comando_send[32];
snprintf(comando_send, sizeof(comando_send), "AT+CIPSEND=%zu\r\n", tamaño_paquete_total);
osEventFlagsClear(uart_rx_flags_id, FLAG_AT_PROMPT); // Limpiar bandera vieja
HAL_UART_Transmit(&huart1, (uint8_t*)comando_send, strlen(comando_send), 100);
// 4. PASO 2: ESPERAR LA LUZ VERDE '>' (Timeout corto de 1 seg)
uint32_t wait_result = osEventFlagsWait(uart_rx_flags_id, FLAG_AT_PROMPT, osFlagsWaitAny, 1000);
if (wait_result == (uint32_t)osErrorTimeout) {
UART_Enviar_String("[AWS ERROR] El Módem no aceptó la petición de envío.\r\n");
return false;
}
// 5. PASO 3: INYECCIÓN DE LA CARGA (Bloque HTTP + JSON)
osEventFlagsClear(uart_rx_flags_id, FLAG_AT_OK | FLAG_HTTP_200);
HAL_UART_Transmit(&huart1, (uint8_t*)buffer_http, tamaño_paquete_total, 1000);
// 6. PASO 4: ESPERAR SEND OK Y RESPUESTA 200 OK (Timeout largo por latencia de nube)
// El RTOS entra en modo de bajo consumo durante 10 segundos máximo, esperando
// a que AWS valide el ticket y retorne el +IPD con el 200 OK.
wait_result = osEventFlagsWait(uart_rx_flags_id, FLAG_HTTP_200, osFlagsWaitAny, 10000);
if (wait_result & FLAG_HTTP_200) {
UART_Enviar_String("[AWS SUCCESS] ¡Venta Registrada Exitosamente en la Nube!\r\n");
return true;
} else {
UART_Enviar_String("[AWS ERROR] Timeout. AWS no respondió o devolvió Error 4xx/5xx.\r\n");
// Nota: En un sistema industrial real, al devolver 'false' aquí, la FSM de red
// debería volver a meter 'venta_pendiente' en la cola local o en la EEPROM
// para no perder el registro contable.
return false;
}
}
// =======================================================
// INTEGRACIÓN EN LA MÁQUINA DE ESTADOS (Fragmento)
// =======================================================
/*
void Thread_WiFi_Modem(void *argument) {
for (;;) {
switch (estado_actual) {
// ...
case ESTADO_IDLE_READY:
// Intentar vaciar una venta hacia la Nube
if (!Sincronizar_Venta_Nube()) {
// Si falla el envío (CIPSEND falla o AWS no responde),
// degradamos el estado para reconectar y reintentar.
estado_actual = ESTADO_WIFI_RECONECTAR;
}
osDelay(500); // Ritmo cardíaco de la tarea
break;
}
}
}
*/
Objetivo del día: Sincronización Transaccional Exacta Extremo-a-Extremo.
El talón de Aquiles de los dispensadores antiguos es la "Pérdida de Operaciones Offline". Una máquina vieja vende $500 de gas y emite el paquete a la red. Si justo en ese instante el cable telefónico se cae, la máquina no se entera. Borra el ticket de la RAM local creyendo que ya se envió, y cuando llega el auditor a fin de mes, falta dinero en la plataforma en línea.
Al implementar la danza de `AT+CIPSEND` mediante **Event Flags Asíncronos** del RTOS, has construido una Transacción de Red Garantizada. Tu Despachador de Gas LP no borra el ticket de su memoria RAM cuando lo envía por el puerto UART; lo mantiene secuestrado. El STM32 se duerme pacientemente esperando que el servidor remoto AWS procese los bits, aplique su lógica de base de datos, y regrese el bendito string `HTTP 200 OK`. Solo si la bandera `FLAG_HTTP_200` se dispara exitosamente, la función retorna `true` y el ticket se da por purgado. Si ocurre un fallo en el último nanosegundo, el hilo retorna `false` y el ticket es resguardado. Contabilidad hermética garantizada.
+IPD del módem.