OTA (Over-The-Air) es la capacidad de actualizar el firmware de un dispositivo de forma inalámbrica. En un producto IoT convencional, el chip Wi-Fi se actualiza a sí mismo. Pero en nuestra arquitectura industrial de Coprocesamiento, tenemos dos cerebros: el ESP32 (Maneja el Wi-Fi) y el STM32 (Maneja el control de Gas LP de misión crítica). Actualizar el ESP32 es trivial. El verdadero desafío de hardware es cómo el ESP32 puede flashear al STM32.
Podríamos escribir un bootloader personalizado en el STM32, pero es arriesgado; si un apagón ocurre durante el flasheo, el STM32 quedaría "brickeado" (inutilizado). En lugar de eso, usaremos el System Memory Bootloader nativo grabado en el silicio (ROM) de STMicroelectronics por el fabricante, el cual es inborrable e invulnerable.
Para obligar al STM32 a entrar a este modo en lugar de ejecutar su código normal, el hardware debe manipular el pin BOOT0. Aquí está la magia: conectamos un pin GPIO del ESP32 directamente al pin BOOT0 del STM32, y otro pin del ESP32 al pin NRST (Reset) del STM32.
Paso 1: Ruteo Físico en KiCad.
En el esquemático de tu placa base, debes conectar dos pines GPIO disponibles de tu ESP32 hacia el STM32.
- El pin GPIO5 del ESP32 va directamente al pin BOOT0 del STM32 (Asegúrate de tener una resistencia Pull-Down de 10k en BOOT0 para que el arranque normal no se altere si el ESP32 falla).
- El pin GPIO4 del ESP32 va conectado al pin NRST del STM32 (El pin de reset del STM32 opera a lógica baja activa).
Paso 2: Descarga y Criptografía (Lado ESP32).
El ESP32 se conecta a AWS S3 mediante HTTPS y descarga el archivo firmware_v2.bin a su memoria Flash SPI interna (usando una partición SPIFFS o LittleFS). Antes de tocar el STM32, el ESP32 debe calcular el hash SHA-256 del archivo descargado y compararlo con el servidor. Si hay un solo bit corrupto por el ruido del Wi-Fi y flasheas el STM32 con él, lo destruirás.
Paso 3: Protocolo UART (Bootloader de ST).
El System Bootloader del STM32 no lee archivos `.bin` directamente; obedece a un conjunto de comandos binarios por puerto serial (USART1 en PA9/PA10 típicamente) que incluyen comandos como 0x31 (Erase Memory) y 0x31 (Write Memory). El ESP32 debe actuar como la computadora (Host) e implementar este protocolo para inyectar el código bloque por bloque.
Este es un fragmento del código en C++ (estilo Arduino/ESP-IDF) que se ejecuta en el ESP32. Su misión es poner al STM32 en estado de coma, obligarlo a arrancar en modo fábrica, y prepararlo para recibir los datos.
/**
* @file stm32_ota_flasher.cpp
* @brief Orquestador ESP32 para inyectar Firmware al STM32 (Modo System ROM)
*/
#include <Arduino.h>
// Definición de los pines de hardware que cruzan la PCB
#define STM32_BOOT0_PIN 5 // Conectado al pin BOOT0 del STM32
#define STM32_RESET_PIN 4 // Conectado al pin NRST del STM32
// HardwareSerial conectado al USART1 del STM32 (TX/RX)
HardwareSerial STM32_UART(2);
/**
* @brief Fuerza al STM32 a entrar en el System Memory Bootloader nativo.
*/
void STM32_Enter_Bootloader_Mode(void) {
Serial.println("[OTA] Tomando control del procesador principal...");
// 1. Configurar los pines de control como salidas
pinMode(STM32_BOOT0_PIN, OUTPUT);
pinMode(STM32_RESET_PIN, OUTPUT);
// 2. Colocar BOOT0 en ALTO (Condición de entrada a Bootloader de fábrica)
digitalWrite(STM32_BOOT0_PIN, HIGH);
delay(10);
// 3. Emitir un pulso BAJO en NRST para reiniciar físicamente el chip
Serial.println("[OTA] Reiniciando STM32 (Hard Reset)...");
digitalWrite(STM32_RESET_PIN, LOW);
delay(50); // Mantener el procesador apagado 50ms
digitalWrite(STM32_RESET_PIN, HIGH); // Soltar el reset
// 4. El STM32 ha despertado. Como BOOT0 está en HIGH, ignorará
// su memoria Flash principal y saltará a la System ROM inborrable.
delay(100);
// 5. Iniciar la comunicación UART a paridad PAR (Even), requerido por ST.
STM32_UART.begin(115200, SERIAL_8E1, 16, 17);
Serial.println("[OTA] STM32 en Modo Bootloader. Listo para recibir comandos.");
}
/**
* @brief Orquesta el proceso completo de Flasheo Inalámbrico.
*/
bool Execute_OTA_Update(const char* filepath_in_spiffs) {
// 1. Apoderarse del STM32
STM32_Enter_Bootloader_Mode();
// 2. Inicializar la comunicación con el Bootloader (Envío del byte de sincro 0x7F)
STM32_UART.write(0x7F);
// Esperar respuesta ACK (0x79) del STM32
long timeout = millis();
while(!STM32_UART.available()) {
if(millis() - timeout > 1000) {
Serial.println("[X] OTA_FAIL: El STM32 no respondió al comando de sincronización.");
return false;
}
}
uint8_t ack = STM32_UART.read();
if(ack != 0x79) {
Serial.println("[X] OTA_FAIL: Error de sincronización UART.");
return false;
}
Serial.println("[V] ACK Recibido. Borrando memoria Flash del STM32...");
// 3. Aquí iniciaría el ciclo pesado:
// - Enviar comando Erase Memory
// - Leer archivo .bin desde la SPIFFS del ESP32 en trozos de 256 bytes.
// - Enviar comando Write Memory, inyectar los 256 bytes.
// - Verificar Checksum. Repetir hasta el fin del archivo.
// (Por brevedad, se omite el bucle del protocolo).
Serial.println("[V] Flasheo completado al 100%.");
// 4. Liberar al STM32 para que arranque la nueva aplicación
digitalWrite(STM32_BOOT0_PIN, LOW); // Retornar a modo ejecución normal
Serial.println("[OTA] Reiniciando STM32 a la nueva aplicación...");
digitalWrite(STM32_RESET_PIN, LOW);
delay(50);
digitalWrite(STM32_RESET_PIN, HIGH);
return true;
}
Objetivo del día: Mantenimiento Remoto Zero-Touch y Escalabilidad de Flota.
Los ingenieros aficionados construyen placas que funcionan en su escritorio; los ingenieros industriales diseñan sistemas que pueden sobrevivir 10 años en el campo. Si la normativa fiscal mexicana sobre la facturación de energéticos cambia, o si necesitas modificar el algoritmo de la Tabla API 54, actualizar el software no puede depender de abrir el gabinete hermético NEMA 4X y destruir el delicado barniz protector.
Con la arquitectura **Dual-MCU OTA**, has dotado a tu Despachador de Gas de la capacidad de regenerarse físicamente. El módulo ESP32 (el copiloto de comunicaciones) monitorea la nube. Al recibir la orden de la central, descarga el nuevo código de forma cifrada (HTTPS) y valida su firma. Luego, mediante un despiadado control de hardware sobre los pines **BOOT0 y NRST**, "secuestra" al procesador principal STM32, lo fuerza a entrar en su modo de recuperación nativo y le inyecta la nueva lógica vía puerto serial. Finalmente, lo reinicia. Todo esto ocurre en segundos, a las 3:00 de la mañana, mientras la estación de servicio está cerrada, sin intervención humana y sin vulnerar la integridad mecánica del equipo. Este nivel de autonomía (Zero-Touch Provisioning) es el sello definitivo de un producto comercial global.