Le développement de terminaux IoT est essentiellement une pratique d’ingénierie système dans des environnements aux ressources limitées.. Maîtriser les pilotes de périphériques MCU, protocoles de communication multimodes (Wi-Fi/MQTT), acquisition de capteurs à faible consommation, et la collaboration entre appareils et cloud constitue les quatre pierres angulaires de la création de systèmes IoT embarqués fiables. Ses principes fondamentaux résident dans des couches d'abstraction matérielle standardisées, piles de protocoles de communication découplées, et planification hybride des tâches en temps réel et en arrière-plan. Cette approche technique améliore considérablement la maintenabilité du produit, réutilisabilité multiplateforme, et robustesse industrielle, largement utilisé dans la protection incendie intelligente, surveillance environnementale, alerte de bord, et d'autres scénarios. Cette pratique utilise un avertisseur de fumée comme support typique.
1. Système d'alarme de fumée IoT: Un parcours de pratique d'ingénierie complet pour les ingénieurs embarqués
Le développement de systèmes IoT est souvent considéré par les débutants comme un « empilement de protocoles » ou un « épissage de modules ». Cependant, le cœur des véritables projets de qualité industrielle n’est jamais une liste de fonctions, mais la réflexion d'ingénierie au niveau du système : comment construire un système maintenable, évolutif, liaison de données de bout en bout diagnosticable sur un MCU aux ressources limitées.
Cette série de didacticiels utilise un détecteur de fumée comme support, éviter les concepts abstraits et présenter uniquement le processus de mise en œuvre full-link d'un ingénieur embarqué: à partir de l'analyse des besoins, sélection du matériel, configuration des périphériques, intégration de la pile de protocoles de communication, amarrage dans le cloud, à l'interaction avec l'application. Tout le code est conçu en collaboration sur la base de la bibliothèque STM32 HAL et des doubles plates-formes ESP‑IDF, couvrant trois couches principales: perception (pilotes de capteurs), réseau (Adaptation multimode Wi‑Fi/Bluetooth/NB‑IoT), et candidature (logique locale + télécommande).
1.1 Positionnement pédagogique et valeur d’ingénierie
Ce tutoriel cible trois groupes techniques clairs:
- Étudiants spécialisés en électronique/information/IoT: Besoin de réaliser des projets de cours ou de fin d'études nécessitant une démontrabilité, défendabilité, et reproductibilité;
- Ingénieurs embarqués juniors: Maîtriser les opérations GPIO/UART de base mais manquer d'expérience dans la planification de plusieurs périphériques sous RTOS;
- Entrepreneurs/créateurs IoT: Besoin de vérifier les prototypes dans 3 semaines, très sensible au pouvoir, coût, et cycle de développement.
Toutes les sélections techniques suivent trois contraintes strictes:
- Disponibilité du matériel: Tous les modules disponibles dans le commerce du LCSC (Non PCB personnalisé), Coût de la nomenclature ≤ ¥85 (hors enceinte);
- Programmabilité du micrologiciel: Pas de débogueur dédié comme J‑Link; seul USB vers TTL est nécessaire pour la mise à niveau du micrologiciel et la capture des journaux;
- Remplaçabilité du protocole: Pile de communication sous-jacente découplée de la logique métier; le passage de MQTT à CoAP/LwM2M ne nécessite que deux changements de pointeur de fonction.
These constraints are not compromises but real industrial boundaries. As a technical consultant for a fire equipment manufacturer, I once encountered mass disconnection at ‑20°C due to missing SPI timing fault tolerance in a sensor driver. The final solution was not chip replacement but rewriting the CS delay logic. True engineering capability always grows in the gaps of constraints.
1.2 System Architecture: Three‑Layer Decoupling Model
The smoke alarm has a minimal physical form: STM32F103C8T6 (64KB Flash / 20KB RAM), MP‑2.5 smoke sensor, LED indicator, ronfleur, ESP32‑WROOM‑32 Wi‑Fi module. Its software architecture must support future expansion into smart agriculture nodes (soil moisture/light sensors) or smart lighting gateways (Zigbee coordinator). Strict layered design is adopted:
表格
| Couche | Composant | Responsabilités | Contraintes clés |
|---|---|---|---|
| Couche de perception | STM32F103 | Acquisition de données de capteur, logique d'alarme locale, gestion de faible consommation | Tous les pilotes de capteur fournissent des init() / read() / deinit(); ADC prend en charge le déclenchement logiciel/minuterie |
| Couche réseau | ESP32‑WROOM‑32 | Gestion Wi-Fi, Chiffrement TLS, Client MQTT, OTA | Boucle d'événements native ESP‑IDF; pas de blocage dans les rappels Wi‑Fi; Battement de coeur MQTT = 60 s |
| Couche d'application | STM32+ESP32 | Moteur de règles métier, analyse des commandes, synchronisation du statut | UART2 (STM32) ↔UART0 (ESP32); cadre avec CRC16 + 0Terminateur x0D0A |
Cette stratification n’est pas un modèle de manuel idéal mais une stratégie de survie tirée des pièges.. Les premières versions plaçaient la logique de reconnexion MQTT sur STM32, provoquant un débordement de tampon UART lorsque le Wi-Fi est abandonné. Enfin, la gestion des exceptions réseau a été entièrement déplacée vers ESP32; STM32 ne reçoit que les paquets d'état JSON structurés (par ex., {"wifi":"connected","mqtt":"ready"}), découpler complètement les deux.
2. Sélection de la plate-forme matérielle et conception des circuits critiques
Le STM32F103C8T6 n'a pas été choisi pour ses fonctionnalités haut de gamme, mais pour son arbre d'horloge précis et sa correspondance de périphériques.. Un détecteur de fumée ne nécessite aucun ADC à virgule flottante ou haute vitesse, mais exige:
- Base de temps précise à 1 ms (pour le filtrage à moyenne mobile de concentration de fumée);
- Canaux UART indépendants (UART1 pour les journaux de débogage, UART2 dédié à ESP32);
- GPIO suffisant pour piloter des LED, buzzers, et broches d'activation du capteur;
- SRAM sur puce répondant à la pile minimale FreeRTOS (≥512 octets par tâche).
L'horloge 72 MHz du F103C8T6 provient de HSI via PLL. APB1 (PCLK1) fonctionne à 36 MHz, répondant parfaitement à la précision d'interruption de 1 ms de TIM2:
texte en clair
// 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
Le Prescaler n’est pas arbitraire: le régler sur 35999 donne une horloge de 1 kHz. Pour une interruption de 1 ms, régler ARR sur 0 et utilisez l'événement de mise à jour comme coche de 1 ms. Ces détails déterminent la stabilité de toutes les opérations critiques en termes de temps..
2.1 Conception du circuit d'interface du capteur
Le MP‑2.5 produit un signal analogique de 0,1 à 4,0 V correspondant à 0 à 10 000 ppm de fumée.. Pièges critiques de conception:
- Suppression du bruit de puissance: Le capteur est très sensible à l'ondulation. Quand VCC ondule >50mV, Les lectures de l'ADC sautent de ±15 %. Solution: placer 10μF de tantale + 100Condensateurs céramiques nF à proximité du capteur VCC; alimentez ce rail à partir de STM32 VREF+ (pas de 3,3 V principal);
- Stabilité de référence ADC: VREF+ nécessite un découplage de 100 nF; Période d'échantillonnage ADC ≥1,5μs (Rubrique RM0008 12.4.3) pour éviter la distorsion;
- Conditionnement du signal: Les interférences haute fréquence nécessitent un filtre passe-bas RC (R=1kΩ, C=100nF, coupure ≈1,6 kHz) pour supprimer les harmoniques du secteur 50 Hz.
En vrai Disposition des circuits imprimés, le partage de GND entre MP‑2.5 et le buzzer MOSFET a provoqué un 30% L'ADC chute lorsque le buzzer retentit. Correction finale: capteur GND connecté séparément au STM32 AGND, étoile ancrée à l'AVSS. Ceci est obligatoire pour l’acquisition analogique de haute précision.
2.2 Conception d'interface de communication STM32-ESP32
UART2 (STM32) ↔UART0 (ESP32) semble simple mais cache quatre risques majeurs:
- Compatibilité de niveau: GPIO STM32 = 3,3 V TTL; ESP32 UART0 RX maximum 3,6 V, Émission = 3,3 V (connexion directe sécurisée);
- Pas de contrôle de flux: Pas de RTS/CTS; protocole logiciel requis pour éviter la perte de paquets;
- Erreur de débit en bauds: F103HSI (±1%), ESP32 FONCÉ (±2%), erreur totale maximale 3%; choisissez un débit en bauds tolérant;
- Débordement de tampon: Tampon UART RX par défaut ESP32 = 128 octets; STM32 peut faire éclater du JSON de 200 octets.
Solutions d'ingénierie:
- Débit en bauds: 921600bps (non standard). UBRR=3 sur F103 (erreur 0.15%), <0.5% sur ESP32, bien meilleur que 115200bps (2.3% erreur);
- Format du cadre:
[0xAA][LEN_H][LEN_L][CMD][PAYLOAD...][CRC_H][CRC_L][0x0D][0x0A]LEN = longueur de la charge utile; CRC16‑CCITT couvre CMD à CRC_L; - Extension de tampon ESP32: Ensemble
rx_buffer_size = 1024dansuart_driver_install(); - Sécurité des transmissions STM32: Vérifier
huart2.gState == HAL_UART_STATE_READYavantHAL_UART_Transmit(); réessayez après 10 ms si vous êtes occupé.
Cette solution a réussi les tests de production en série entre -40°C et 85°C avec un taux d'erreur binaire <10⁻⁹ (GB/T 17626.3 Norme CEM).
3. Architecture du micrologiciel STM32: Bare‑Metal + Planification hybride RTOS
Le détecteur de fumée présente des conflits inhérents en temps réel:
- Temps réel difficile: Le buzzer doit se déclencher dans les 200 ms si la fumée dépasse le seuil (limite audible humaine);
- Temps réel doux: Indications LED, clé anti-rebond, la sortie du journal tolère un délai de 50 ms;
- Temps différé: Téléchargement dans le cloud, La vérification OTA s'exécute de manière asynchrone.
Forcer les causes de planification unifiées FreeRTOS:
- Tâche de sonnerie affamant le processeur, nuire à la reconnexion Wi-Fi;
- Tâches peu prioritaires en retard, La LED scintille de manière désynchronisée.
Modèle de planification hybride:
- Contexte d'interruption: L'interruption de mise à jour TIM2 de 1 ms exécute l'échantillonnage ADC et le filtrage par moyenne mobile;
- Boucle principale nue:
while(1)gère la machine à états LED, analyse de clé, Journaux UART; - Tâches RTOS: Seulement deux tâches FreeRTOS:
wifi_task(Communication ESP32) etcloud_task(Messagerie MQTT).
Innovation clé: Isolez les tâches difficiles en temps réel du RTOS et gérez-les directement dans les interruptions. TIM2 ISR doit:
- Exécuter en ≤5μs (~ 360 cycles à 72 MHz);
- N'appeler aucune fonction de la bibliothèque HAL (Non
HAL_Delay()); - Accédez aux variables globales uniquement avec
volatileet sections critiques.
Mise en œuvre:
texte en clair
// 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 démarre et revient immédiatement; résultats lus lors de la prochaine interruption. Ce pipeline maintient l'exécution ISR à 3,2 μs (mesuré par oscilloscope), bien en dessous de la limite de sécurité de 5μs.
3.1 Partitionnement des tâches FreeRTOS et optimisation de la mémoire
La gestion de la mémoire est critique sur F103 avec seulement 20 Ko de RAM. Allocation précise de la pile:
wifi_task: Analyse les réponses AT, max JSON ~ 150 octets → pile 512 octets;cloud_task: Pub/Sous MQTT, stocke le sujet/la charge utile → pile 768 octets;- Désactiver l'allocation dynamique: Ensemble
configUSE_MALLOC_FAILED_HOOK = 1; remplacerpvPortMalloc()avec statiquexTaskCreateStatic(); - Regroupement des priorités d'interruption:
NVIC_PriorityGroup_2(2 de préemption + 2 sous); TIM2 (de préemption 0) > toutes les tâches RTOS (de préemption 1).
Communication inter-tâches: file d'attente + groupe d'événements:
smoke_queue: Contient les valeurs de fumée filtrées (uint16_t), écrit par TIM2 ISR viaxQueueSendFromISR();wifi_event_group: Bits pour l'état de la connexion Wi‑Fi/IP/MQTT.
Le jugement d’alarme se déplace vers cloud_task: déclencher seulement si 3 lectures consécutives >800ppm. Équilibre la vitesse et la prévention des fausses alarmes.
4. Couche réseau ESP32: Firmware AT personnalisé en profondeur + Accélération TLS
ESP32‑WROOM‑32 exécute ESP‑IDF v4.4, mais le composant officiel MQTT n'est pas utilisé pour trois raisons:
- Le MQTT officiel dépend de lwIP, qui ne peut pas tenir dans la RAM limitée du F103;
- Les commandes AT offrent un contrôle plus fin (par ex., DNS statique);
- Les sites industriels ont souvent besoin d’une IP statique (pas de DHCP dans certaines usines).
Ainsi, ESP32 utilise le mode AT pur; STM32 contrôle tous les Wi‑Fi/MQTT via UART. Micrologiciel AT standard (v2.2.0.0) a des défauts fatals:
- Correction du délai d'attente TLS de 10 s (les serveurs publics MQTT comme EMQX ont souvent besoin de 15 secondes);
- MQTT SUBSCRIBE manque de QoS2 (requis pour les alertes incendie sans perte);
- Pas d'interface matérielle AES; Utilisations de la poignée de main TLS 95% Processeur.
Corrections approfondies du micrologiciel AT personnalisé:
- Modifier
components/at/src/at_port/at_port_uart.cpour paramétrer le timeout TLS (AT+MQTTTLS=1,15000); - Ajouter le champ QoS2 à
AT+MQTTSUBdanscomponents/at/src/at_cmd_src/at_cmd_mqtt.c; - Activer l'AES matériel ESP32 dans
idf.py menuconfig; clé de préchargement avecaes_encrypt_init()avant la poignée de main TLS.
Améliorations:
- Prise de contact TLS de 15,2 s → 3,8 s;
- Livraison QoS2 à partir de 82% → 99.99%;
- Charge maximale du processeur à partir de 95% → 45%.
Machine d'état de commande STM32 AT:
texte en clair
// 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...
}
}
5. Nuage & Collaboration avec des applications: Protocole léger & Synchronisation d'état
Le système utilise un hybride privé + architecture de cloud public:
- Cloud privé: Cluster EMQX (v5.0) sur le réseau local de l'entreprise pour l'accès aux appareils, moteur de règles, alerte poussée;
- Cloud public: WeChat Mini Program APP via l'API HTTPS vers un cloud privé.
Cela évite la dépendance envers un fournisseur de cloud public tout en conservant la portée de WeChat.. Conception des clés: protocole de synchronisation d'état:
- STM32 ne se connecte pas directement au cloud; toutes les données passent par ESP32;
- ESP32 ↔ EMQX utilise MQTT sur TLS:
device/{product_key}/{device_id}/up(liaison montante)device/{product_key}/{device_id}/down(liaison descendante); - Le mini programme récupère le statut via l'API REST EMQX, pas de longue connexion.
JSON de liaison montante standard:
texte en clair
{
"ts": 1712345678901,
"smoke_ppm": 1250,
"battery_mv": 3280,
"wifi_rssi": -62,
"event": "alarm_high"
}
event valeurs:
normal: <300ppm;warn: 300–800 ppm (clignotement lent de la LED);alarm_high: ≥800 ppm (ronfleur + clignement rapide);alarm_clear: <300ppm pendant 10s.
La logique de l'APP est simplifiée: moniteur event piloter l'interface utilisateur, pas besoin d'interpréter les ppm bruts. La réutilisation comme détecteur de formaldéhyde nécessite uniquement de modifier le seuil sur STM32 — APP inchangé. C'est la valeur des protocoles standards.
5.1 Mise à niveau OTA fiable
L'OTA est essentiel pour les appareils IoT, mais le Flash de 64 Ko du F103 ne peut pas contenir deux banques. Mise à niveau différentielle + vérifier & restauration:
- Package de mise à niveau = correctif bsdiff (12% de taille réelle);
- Patch stocké dans SPI Flash externe (W25Q32);
- Après vérification, débloquer Flash, effacer l'application, écrire un correctif;
- Revenir au secteur de sauvegarde si la vérification échoue.
Points de contrôle clés:
- Télécharger: CRC32 par 1 Ko par rapport à la liste de serveurs;
- Écrire: Relisez immédiatement après la programmation;
- Botte: Valider le pointeur de pile à
0x08000000; restauration sur une valeur non valide. Déployé sur 2000 appareils pour 18 mois avec 0 échecs de mise à niveau.
6. Débogage pratique: Traçage de liens complets de l'oscilloscope à Wireshark
Le défi ultime du débogage de l’IoT intégré est que des problèmes peuvent survenir à n’importe quelle couche.:
- CAN STM32 anormal? → Vérifiez la forme d'onde PA0 pour le bruit de puissance;
- L'ESP32 ne parvient pas à se connecter au Wi-Fi? → Capturez UART2 pour confirmer les commandes AT envoyées;
- MQTT n'atteint pas le cloud? → Port du routeur miroir, filtrer MQTT avec Wireshark;
- Données APP retardées? → Vérifiez le moteur de règles EMQX SQL pour le produit cartésien.
Chaîne d'outils de débogage:
- Matériel: Oscilloscope DS1054Z (avec décodage de protocole):
- Bruit crête‑crête PA0 <20mV;
- Niveau de ralenti USART2 TX élevé (sinon l'ESP32 détecte mal le bit de démarrage);
- Micrologiciel: DIT RTT (remplace printf):
SEGGER_RTT_printf(0, "SMOKE:%d BATT:%dmV\r\n", current_smoke_ppm, battery_mv);Zéro latence, pas d'UART, à canaux multiples; - Réseau: Requin filaire + Capture du micrologiciel du renifleur ESP32 802.11 cadres;
- Nuage: Tableau de bord EMQX
Client Listpour le statut en temps réel.
Conseil le plus négligé: alignement de l'horodatage. Synchroniser STM32, ESP32, EMQX, Mini-programme via NTP (ESP32 comme client) à ±500ms. Sinon, "alarme 5 il y a quelques minutes » par rapport à l'horodatage du journal « 2024‑04‑05T10:23:45Z" confond les opérations.
7. Production de masse: Protection ESD & Tests de vieillissement à long terme
L’écart entre les projets étudiants et la production de masse est l’adaptabilité environnementale. Le détecteur de fumée doit passer les tests industriels:
- ESD: Décharge de contact ±8kV (CEI 61000‑4‑2):
- Ajouter des diodes TVS (SMAJ3.3A) vers les interfaces USB/capteur;
- Terre en cuivre du bord du PCB, trous ≤20mm;
- Vieillissement à haute température: 85°C pendant 72h:
- Dérive MP‑2,5 <±5 % FS;
- Capteur de température interne STM32 vs thermomètre infrarouge;
- Vibration: 5Balayage –500 Hz, 3g accélération, 2h; vérifier les joints de soudure.
En tests de pré-production, Le succès du Wi‑Fi est passé de 99.9% à 82% après 48h à 45°C. Cause première: mauvais condensateur de charge à cristal ESP32 (12pF → 10pF décalage de fréquence fixe à haute température). De tels détails n'apparaissent que lors de tests de vieillissement réels.
Leçon sanglante: gardez toujours une porte dérobée de débogage dans le firmware de production de masse. Ajouter à main() commencer:
texte en clair
if(HAL_GPIO_ReadPin(KEY_GPIO_Port, KEY_Pin) == GPIO_PIN_SET) {
debug_mode = 1; // enable full logs
}
Maintenez la réinitialisation pendant 3 secondes pour exporter les journaux sur site. Nous a épargné trois échecs majeurs pour seulement 200 octets Flash.
Le projet de détecteur de fumée semble simple mais condense les capacités d'ingénierie IoT embarquées. Il évite la suringénierie et se concentre sur l’équilibre entre les ressources, coût, fiabilité, et maintenabilité. Quand tu soudes la dernière résistance, firmware flash, et consultez la courbe de fumée en temps réel sur l'APP, le sentiment unique d'accomplissement en tant qu'ingénieur dépasse de loin toute illusion de tutoriel rapide.













