Enfocados en el desarrollo de soluciones ESP32.

Práctica de desarrollo de IoT de alarma de humo STM32 + ESP32

El desarrollo de dispositivos terminales de IoT es esencialmente una práctica de ingeniería de sistemas en entornos con recursos limitados.. Dominar los controladores periféricos de MCU, protocolos de comunicación multimodo (Wi‑Fi/MQTT), adquisición de sensores de baja potencia, y la colaboración entre dispositivos y la nube constituye los cuatro pilares de la construcción de sistemas IoT integrados confiables. Sus principios básicos residen en capas de abstracción de hardware estandarizadas., pilas de protocolos de comunicación desacoplados, y programación híbrida de tareas en tiempo real y en segundo plano. Este enfoque técnico mejora significativamente la mantenibilidad del producto., reutilización multiplataforma, y robustez industrial, Ampliamente utilizado en protección inteligente contra incendios., monitoreo ambiental, alerta de borde, y otros escenarios. Esta práctica utiliza una alarma de humo como portador típico..

Los principiantes suelen considerar el desarrollo de sistemas de IoT como "apilamiento de protocolos" o "empalme de módulos". Sin embargo, El núcleo de los proyectos reales de grado industrial nunca es una lista de funciones., pero el pensamiento de ingeniería a nivel de sistema: cómo construir un sistema mantenible., escalable, Enlace de datos de extremo a extremo diagnosticable en una MCU con recursos limitados.

Esta serie de tutoriales utiliza una alarma de humo como portador., evitando conceptos abstractos y presentando solo el proceso de implementación de enlace completo de un ingeniero integrado: del análisis de requisitos, selección de hardware, configuración periférica, integración de la pila de protocolos de comunicación, acoplamiento en la nube, a la interacción de la aplicación. Todo el código está diseñado de forma colaborativa en base a la biblioteca STM32 HAL y las plataformas duales ESP-IDF., cubriendo tres capas centrales: percepción (controladores de sensores), red (Adaptación multimodo Wi‑Fi/Bluetooth/NB‑IoT), y aplicación (lógica local + mando a distancia).

1.1 Posicionamiento docente y valor de ingeniería

Este tutorial se dirige a tres grupos técnicos claros.:

  • Estudiantes con especialización en electrónica/información/IoT: Necesidad de completar cursos o proyectos de graduación que requieran demostrabilidad., defensa, y reproducibilidad;
  • Ingenieros integrados junior: Domina las operaciones GPIO/UART básicas pero carece de experiencia en la programación de múltiples periféricos bajo RTOS;
  • Emprendedores/creadores de IoT: Necesidad de verificar prototipos dentro 3 semanas, muy sensible al poder, costo, y ciclo de desarrollo.

Todas las selecciones técnicas siguen tres estrictas limitaciones.:

  1. Disponibilidad de hardware: Todos los módulos disponibles en el mercado de LCSC (No PCB personalizado), Costo de la lista de materiales ≤ ¥85 (excluyendo el recinto);
  2. Programabilidad del firmware: Ningún depurador dedicado como J‑Link; solo se necesita USB a TTL para la actualización del firmware y la captura de registros;
  3. Reemplazabilidad del protocolo: Pila de comunicación subyacente desacoplada de la lógica empresarial; cambiar MQTT a CoAP/LwM2M requiere solo dos cambios de puntero de función.

Estas limitaciones no son compromisos sino fronteras industriales reales.. Como consultor técnico para un fabricante de equipos contra incendios., Una vez encontré una desconexión masiva a -20°C debido a la falta de tolerancia a fallas de sincronización SPI en un controlador de sensor. La solución final no fue reemplazar el chip sino reescribir la lógica de retardo del CS.. La verdadera capacidad de ingeniería siempre crece en las lagunas de las limitaciones..

1.2 Arquitectura del sistema: Modelo de desacoplamiento de tres capas

La alarma de humo tiene una forma física mínima.: STM32F103C8T6 (64Flash de KB / 20KB de RAM), Sensor de humo MP‑2.5, indicador LED, zumbador, Módulo Wi‑Fi ESP32‑WROOM‑32. Su arquitectura de software debe respaldar la futura expansión hacia nodos de agricultura inteligente. (sensores de humedad/luz del suelo) o pasarelas de iluminación inteligentes (Coordinador Zigbee). Se adopta un estricto diseño en capas.:

hoja

CapaComponenteResponsabilidadesRestricciones clave
Capa de percepciónSTM32F103Adquisición de datos del sensor, lógica de alarma local, gestión de bajo consumoTodos los controladores de sensores proporcionan estándar init() / read() / deinit(); ADC admite disparador de software/temporizador
Capa de redESP32‑WROOM‑32Gestión de wifi, Cifrado TLS, Cliente MQTT, OTABucle de eventos nativo ESP‑IDF; sin bloqueo en devoluciones de llamadas Wi-Fi; Latido del corazón MQTT = 60 s
Capa de aplicaciónSTM32+ESP32Motor de reglas de negocio, análisis de comandos, sincronización de estadoUART2 (STM32) ↔ UART0 (ESP32); marco con CRC16 + 0terminador x0D0A

Esta estratificación no es un modelo de libro de texto ideal, sino una estrategia de supervivencia aprendida de los obstáculos.. Las primeras versiones colocaron la lógica de reconexión MQTT en STM32, provocando un desbordamiento del búfer UART cuando se cae el Wi-Fi. Finalmente, El manejo de excepciones de red se trasladó completamente a ESP32.; STM32 solo recibe paquetes de estado JSON estructurados (p.ej., {"wifi":"connected","mqtt":"ready"}), desacoplando completamente los dos.

El STM32F103C8T6 no se elige por sus características de alta gama, sino por su árbol de reloj preciso y combinación de periféricos.. Una alarma de humo no requiere un ADC de punto flotante o de alta velocidad, pero exige:

  • Base de tiempo precisa de 1 ms (para filtrado de media móvil de concentración de humo);
  • Canales UART independientes (UART1 para registros de depuración, UART2 dedicado a ESP32);
  • GPIO suficiente para controlar los LED, zumbadores, y pines de habilitación del sensor;
  • SRAM en chip que cumple con la pila mínima de FreeRTOS (≥512 bytes por tarea).

El reloj de 72 MHz del F103C8T6 proviene de HSI a través de PLL. APB1 (PCLK1) funciona a 36MHz, Cumpliendo perfectamente con la precisión de interrupción de 1 ms de TIM2.:

texto plano

// Key TIM2 initialization (HAL Library)
htim2.Instance = TIM2;
htim2.Init.Prescaler = 36000 - 1; // 36MHz / 36000 = 1kHz
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 1000 - 1; // 1s overflow

El Prescaler no es arbitrario: configurándolo en 35999 da un reloj temporizador de 1kHz. Para una interrupción de 1 ms, establezca ARR en 0 y use el evento de actualización como marca de 1 ms. Estos detalles determinan la estabilidad de todas las operaciones en las que el tiempo es crítico..

2.1 Diseño del circuito de interfaz del sensor

El MP‑2.5 emite 0,1–4,0 V analógicos correspondientes a 0–10000 ppm de humo.. Errores críticos de diseño:

  • Supresión de ruido de potencia: El sensor es muy sensible a las ondulaciones.. Cuando VCC se ondula >50mV, Las lecturas del ADC saltan ±15%. Solución: coloque tantalio 10μF + 100Condensadores cerámicos nF cerca del sensor VCC; alimenta este riel desde STM32 VREF+ (no principal 3.3V);
  • Estabilidad de referencia del ADC: VREF+ necesita un desacoplamiento de 100 nF; Período de muestreo del ADC ≥1,5 μs (Sección RM0008 12.4.3) para evitar la distorsión;
  • Acondicionamiento de señal: La interferencia de alta frecuencia requiere un filtro de paso bajo RC (R=1kΩ, C=100nF, corte ≈1.6kHz) para suprimir armónicos de red de 50 Hz.

En verdad diseño de PCB, compartir GND entre MP‑2.5 y el MOSFET del zumbador provocó un 30% Caída del ADC cuando sonó el timbre. arreglo final: sensor GND conectado por separado a STM32 AGND, aterrizado en estrellas en AVSS. Esto es obligatorio para la adquisición analógica de alta precisión..

2.2 Diseño de interfaz de comunicación STM32 – ESP32

UART2 (STM32) ↔ UART0 (ESP32) Parece simple pero esconde cuatro riesgos principales.:

  1. Compatibilidad de niveles: STM32 GPIO = 3,3 VTTL; ESP32 UART0 RX máx. 3,6 V, TX = 3,3 V (conexión directa segura);
  2. Sin control de flujo: Sin RTS/CTS; protocolo de software necesario para evitar la pérdida de paquetes;
  3. Error de velocidad de baudios: F103 HSI (±1%), ESP32 OSCURO (±2%), error total máximo 3%; elegir velocidad de baudios tolerante;
  4. Desbordamiento del búfer: Búfer UART RX predeterminado de ESP32 = 128 bytes; STM32 puede explotar JSON de 200 bytes.

Soluciones de ingeniería:

  • Velocidad de baudios: 921600bps (no estándar). UBRR=3 en F103 (error 0.15%), <0.5% en ESP32, mucho mejor que 115200bps (2.3% error);
  • Formato de marco:[0xAA][LEN_H][LEN_L][CMD][PAYLOAD...][CRC_H][CRC_L][0x0D][0x0A]LEN = longitud de la carga útil; CRC16‑CCITT cubre CMD a CRC_L;
  • Expansión del búfer ESP32: Colocar rx_buffer_size = 1024 en uart_driver_install();
  • Seguridad de la transmisión STM32: Controlar huart2.gState == HAL_UART_STATE_READY antes HAL_UART_Transmit(); reintente después de 10 ms si está ocupado.

Esta solución pasó pruebas de producción en masa de -40 °C a 85 °C con tasa de error de bits <10⁻⁹ (GB/T 17626.3 Estándar CEM).

La alarma de humo tiene conflictos inherentes en tiempo real:

  • Tiempo real difícil: El timbre debe activarse dentro de 200 ms si el humo excede el umbral (límite audible humano);
  • Suave en tiempo real: indicación LED, rebote clave, la salida del registro tolera un retraso de 50 ms;
  • Tiempo no real: Subir a la nube, La verificación OTA se ejecuta de forma asincrónica.

Forzar causas de programación unificada de FreeRTOS:

  • La tarea del zumbador agota la CPU, Dañando la reconexión Wi-Fi;
  • Tareas de baja prioridad retrasadas, El LED parpadea fuera de sincronización.

Modelo de programación híbrido:

  • Contexto de interrupción: La interrupción de actualización de 1 ms de TIM2 ejecuta muestreo ADC y filtrado de promedio móvil;
  • Bucle principal de metal desnudo: while(1) maneja la máquina de estado LED, escaneo de claves, Registros UART;
  • Tareas RTOS: Sólo dos tareas FreeRTOS: wifi_task (comunicación ESP32) y cloud_task (Mensajería MQTT).

Innovación clave: Aísle tareas difíciles en tiempo real de RTOS y manejelas directamente en interrupciones. TIM2 ISR debe:

  • Ejecutar en ≤5μs (~360 ciclos a 72MHz);
  • No llamar a funciones de biblioteca HAL (No HAL_Delay());
  • Acceda a variables globales solo con volatile y secciones críticas.

Implementación:

texto plano

// TIM2 ISR (simplified)
void TIM2_IRQHandler(void)
{
 if(__HAL_TIM_GET_FLAG(&htim2, TIM_FLAG_UPDATE) != RESET)
 {
 __HAL_TIM_CLEAR_FLAG(&htim2, TIM_FLAG_UPDATE);

 // Non‑blocking ADC start
 HAL_ADC_Start(&hadc1);

 // 1ms tick for LED blink
 ms_tick++;

 // Moving average filter (window = 8)
 static uint16_t smoke_buf[8] = {0};
 static uint8_t buf_idx = 0;
 uint16_t adc_val;

 if(HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK)
 {
 adc_val = HAL_ADC_GetValue(&hadc1);
 smoke_buf[buf_idx] = adc_val;
 buf_idx = (buf_idx + 1) & 0x07;
 uint32_t sum = 0;
 for(uint8_t i=0; i<8; i++) sum += smoke_buf[i];
 current_smoke_ppm = sum >> 3; // divide by 8
 }
 }
}

ADC inicia y regresa inmediatamente; resultados leídos en la siguiente interrupción. Este canal mantiene la ejecución de ISR en 3,2 μs (medido por osciloscopio), muy por debajo del límite de seguridad de 5μs.

3.1 Partición de tareas y optimización de memoria de FreeRTOS

La gestión de la memoria es fundamental en F103 con sólo 20 KB de RAM. Asignación precisa de la pila:

  • wifi_task: Analiza las respuestas AT, JSON máximo ~ 150 bytes → pila 512 bytes;
  • cloud_task: Publicación/suscripción MQTT, almacena tema/carga útil → pila 768 bytes;
  • Deshabilitar la asignación dinámica: Colocar configUSE_MALLOC_FAILED_HOOK = 1; reemplazar pvPortMalloc() con estática xTaskCreateStatic();
  • Agrupación de prioridad de interrupción: NVIC_PriorityGroup_2 (2 con derecho preferente + 2 sub); TIM2 (con derecho preferente 0) > todas las tareas RTOS (con derecho preferente 1).

Comunicación entre tareas: cola + grupo de eventos:

  • smoke_queue: Mantiene los valores de humo filtrado. (uint16_t), escrito por TIM2 ISR vía xQueueSendFromISR();
  • wifi_event_group: Bits para el estado de la conexión Wi‑Fi/IP/MQTT.

El juicio de alarma se mueve a cloud_task: activar sólo si 3 lecturas consecutivas >800ppm. Equilibra la velocidad y la prevención de falsas alarmas.

ESP32‑WROOM‑32 ejecuta ESP‑IDF v4.4, pero El componente oficial MQTT no se utiliza. por tres razones:

  • MQTT oficial depende de lwIP, que no cabe en la RAM limitada del F103;
  • Los comandos AT ofrecen un control más preciso (p.ej., DNS estático);
  • Los sitios industriales a menudo necesitan IP estática (no hay DHCP en algunas fábricas).

De este modo, ESP32 usa modo AT puro; STM32 controla todo Wi-Fi/MQTT a través de UART. Firmware AT estándar (v2.2.0.0) tiene defectos fatales:

  • Se corrigió el tiempo de espera de TLS de 10 segundos (Los servidores públicos MQTT como EMQX a menudo necesitan 15);
  • La SUSCRIPCIÓN MQTT carece de QoS2 (requerido para alertas de incendio sin pérdidas);
  • Sin interfaz AES de hardware; Usos del protocolo de enlace TLS 95% UPC.

Correcciones de firmware AT personalizadas profundas:

  1. Modificar components/at/src/at_port/at_port_uart.c para parametrizar el tiempo de espera de TLS (AT+MQTTTLS=1,15000);
  2. Agregue el campo QoS2 a AT+MQTTSUB en components/at/src/at_cmd_src/at_cmd_mqtt.c;
  3. Habilite el hardware ESP32 AES en idf.py menuconfig; llave de precarga con aes_encrypt_init() antes del protocolo de enlace TLS.

Mejoras:

  • Apretón de manos TLS de 15,2 s → 3,8 s;
  • Entrega de QoS2 desde 82% → 99.99%;
  • Carga máxima de CPU desde 95% → 45%.

Máquina de estado de comando STM32 AT:

texto plano

// AT state machine (pseudo code)
typedef enum {
 AT_STATE_IDLE,
 AT_STATE_WAITING_OK,
 AT_STATE_WAITING_IP,
 AT_STATE_MQTT_CONNECTED
} at_state_t;

at_state_t at_state = AT_STATE_IDLE;
uint8_t at_retry_cnt = 0;

void at_send_command(const char* cmd) {
 HAL_UART_Transmit(&huart2, (uint8_t*)cmd, strlen(cmd), 100);
 at_state = AT_STATE_WAITING_OK;
 at_retry_cnt = 0;
}

// UART2 RX interrupt parsing
void USART2_IRQHandler(void) {
 uint8_t rx_byte;
 HAL_UART_Receive(&huart2, &rx_byte, 1, 1);

 switch(at_state) {
 case AT_STATE_WAITING_OK:
 if(strstr(rx_buffer, "OK")) {
 at_state = AT_STATE_IDLE;
 } else if(strstr(rx_buffer, "ERROR")) {
 if(++at_retry_cnt < 3)
 at_send_command(last_cmd);
 else
 at_switch_apn();
 }
 break;
 // other states...
 }
}

El sistema utiliza un híbrido privado + arquitectura de nube pública:

  • Nube privada: Clúster EMQX (v5.0) en LAN empresarial para acceso a dispositivos, motor de reglas, empuje de alerta;
  • Nube pública: Aplicación WeChat Mini Program a través de API HTTPS para nube privada.

Esto evita la dependencia del proveedor de la nube pública y al mismo tiempo mantiene el alcance de WeChat.. Diseño clave: protocolo de sincronización de estado:

  • STM32 no se conecta directamente a la nube; todos los datos pasan a través de ESP32;
  • ESP32 ↔ EMQX usa MQTT sobre TLS:device/{product_key}/{device_id}/up (enlace ascendente)device/{product_key}/{device_id}/down (enlace descendente);
  • El mini programa obtiene el estado a través de la API REST de EMQX, no hay conexión larga.

JSON de enlace ascendente estándar:

texto plano

{
 "ts": 1712345678901,
 "smoke_ppm": 1250,
 "battery_mv": 3280,
 "wifi_rssi": -62,
 "event": "alarm_high"
}

event valores:

  • normal: <300ppm;
  • warn: 300–800 ppm (parpadeo lento del LED);
  • alarm_high: ≥800 ppm (zumbador + parpadeo rápido);
  • alarm_clear: <300ppm durante 10 segundos.

La lógica de la aplicación se simplifica: monitor event para conducir la interfaz de usuario, no es necesario interpretar las ppm sin procesar. La reutilización como detector de formaldehído solo requiere cambiar el umbral en STM32—APP sin cambios. Este es el valor de los protocolos estándar.

5.1 Actualización OTA confiable

OTA es fundamental para los dispositivos IoT, pero el Flash de 64 KB del F103 no puede contener bancos duales. Actualización diferencial + verificar & revertir:

  • Paquete de actualización = parche bsdiff (12% de tamaño completo);
  • Parche almacenado en SPI Flash externo (W25Q32);
  • Después de la verificación, desbloquear flash, borrar aplicación, escribe parche;
  • Regresar al sector de respaldo si falla la verificación.

Puntos de control clave:

  • Descargar: CRC32 por 1 KB contra la lista de servidores;
  • Escribir: Vuelva a leer inmediatamente después de la programación.;
  • Bota: Validar el puntero de la pila en 0x08000000; revertir un valor no válido. Implementado en 2000 dispositivos para 18 meses con 0 fallas de actualización.

El desafío final en la depuración de IoT integrada es que los problemas pueden ocurrir en cualquier capa.:

  • ADC STM32 anormal? → Verifique la forma de onda PA0 para detectar ruido de energía;
  • ESP32 no se puede conectar a Wi‑Fi? → Capture UART2 para confirmar los comandos AT enviados;
  • MQTT no llega a la nube? → Puerto del enrutador espejo, filter MQTT with Wireshark;
  • APP data delayed? → Check EMQX rule engine SQL for Cartesian product.

Debug toolchain:

  • Hardware: DS1054Z oscilloscope (with protocol decoding):
    • PA0 peak‑peak noise <20mV;
    • USART2 TX idle level high (else ESP32 misdetects start bit);
  • firmware: SEGGER RTT (replaces printf):SEGGER_RTT_printf(0, "SMOKE:%d BATT:%dmV\r\n", current_smoke_ppm, battery_mv);Zero latency, no UART, multi‑channel;
  • Network: Wireshark + ESP32 Sniffer firmware capture 802.11 frames;
  • Nube: EMQX Dashboard Client List for real‑time status.

Most overlooked tip: timestamp alignment. Sync STM32, ESP32, EMQX, Mini Program via NTP (ESP32 as client) to ±500ms. Otherwise, “alarm 5 minutes ago” vs log timestamp “2024‑04‑05T10:23:45Z” confuses operations.

The gap between student projects and mass production is environmental adaptability. The smoke alarm must pass industrial tests:

  • ESD: Contact discharge ±8kV (CEI 61000‑4‑2):
    • Agregar diodos TVS (SMAJ3.3A) a interfaces USB/sensor;
    • Tierra de cobre del borde de PCB, agujeros ≤20 mm;
  • Envejecimiento a alta temperatura: 85°C durante 72h:
    • Deriva MP‑2.5 <±5% fondo de escala;
    • Sensor de temperatura interno STM32 frente a termómetro infrarrojo;
  • Vibración: 5–Barrido de 500 Hz, 3g aceleración, 2h; comprobar las uniones de soldadura.

En pruebas de preproducción, El éxito de Wi‑Fi disminuyó de 99.9% a 82% después de 48h a 45°C. Causa principal: condensador de carga de cristal ESP32 incorrecto (12pF → 10pF cambio de frecuencia fija a alta temperatura). Estos detalles sólo salen a la superficie en pruebas de envejecimiento reales..

Lección sangrienta: Mantenga siempre una puerta trasera de depuración en el firmware de producción en masa.. Añadir en main() comenzar:

texto plano

if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_SET) {
 debug_mode = 1; // enable full logs
}

Mantenga presionado restablecer 3 segundos para exportar registros en el sitio. Nos salvó tres grandes fracasos por sólo 200 bytes flash.

El proyecto de alarma de humo parece simple pero condensa la capacidad de ingeniería de IoT integrada. Evita la ingeniería excesiva y se centra en el equilibrio entre los recursos., costo, fiabilidad, y mantenibilidad. Cuando sueldas la última resistencia, firmware flash, y vea la curva de humo en tiempo real en la APLICACIÓN, La sensación única de logro como ingeniero supera con creces cualquier ilusión de tutorial rápido..

Imagen de Berg Zhou

Berg Zhou

Berg Zhou se centra en el diseño esquemático de ESP32, diseño de PCB, desarrollo de firmware y producción en masa de PCBA. Competente en diseño de circuitos., selección de componentes, Pruebas de prototipos y soluciones OEM/ODM integrales.. Proporcionar estabilidad, Módulos funcionales y tableros de control ESP32 confiables y rentables para clientes globales, Apoyar el desarrollo personalizado y la fabricación en volumen..

Publicaciones recientes

Traducción
Establecer como idioma predeterminado
Whatsapp
Whatsapp
Correo electrónico
Correo electrónico
chatear
chatear
chatear

Obtenga una cotización

Nuestros expertos en productos y técnicos responderán sus preguntas dentro de 24 horas.

Utilizamos cookies para asegurarnos de brindarle la mejor experiencia en nuestro sitio web..