// GPIO · WiFi · MQTT · Mosquitto · Node-RED ESP32 & IoT
Architecture matérielle, GPIO, ADC, WiFi, protocole MQTT, broker Mosquitto, Node-RED et projet complet. Chaque bit expliqué. Zéro hypothèse.
Qu'est-ce que l' ESP32 ?
L'ESP32 est un microcontrôleur sur une seule puce intégrant nativement WiFi et Bluetooth. Pour moins de 5€, il mesure le monde physique ET l'envoie sur Internet.
Arduino Uno vs ESP32
| Critère | Arduino Uno | ESP32 |
|---|---|---|
| CPU | ATmega328P 16 MHz | Xtensa LX6 × 2 à 240 MHz |
| RAM | 2 KB | 520 KB (260× plus) |
| Flash | 32 KB | 4 MB (125× plus) |
| WiFi | ❌ Absent | ✅ Intégré |
| Logique | 5 V | 3.3 V ⚠️ |
| ADC | 10 bits | 12 bits |
| Prix | ~15 € | ~3–8 € |
Architecture interne ESP32
L'ESP32 travaille en logique 3.3V. Brancher un signal 5V directement sur un GPIO peut griller définitivement la puce. Utiliser un diviseur de tension ou un level-shifter bidirectionnel pour tout capteur ou Arduino 5V.
Pinout ESP32 — Carte des Broches
Chaque broche a un rôle principal et souvent plusieurs fonctions alternatives. Connaître les contraintes matérielles évite les bugs mystérieux.
| Broche | Rôle principal | Règle d'or |
|---|---|---|
| GPIO6–11 | Flash SPI interne | JAMAIS UTILISER — crash garanti |
| GPIO34, 35, 36, 39 | ADC entrée seule | INPUT uniquement — pas OUTPUT ni pull-up interne |
| GPIO0 | Boot mode | LOW au démarrage = mode flash. Pull-up 10 kΩ conseillé. |
| GPIO2 | LED DevKit + Boot | Doit être libre (flottant ou HIGH) pendant programmation |
| GPIO21 / 22 | SDA / SCL I2C par défaut | Pull-up 4.7 kΩ externe vers 3.3V obligatoires |
| ADC2 (0,2,4,12–15,25–27) | ADC partagé WiFi | analogRead() retourne des erreurs quand WiFi est actif |
GPIO — Entrées & Sorties Digitales
GPIO = General Purpose Input/Output. En sortie tu commandes LED, relais, buzzers. En entrée tu lis boutons et capteurs digitaux. La règle d'or : jamais de delay() dans un projet IoT.
delay(1000) = ESP32 FIGÉ 1 seconde. WiFi coupé, MQTT perdu, rien ne répond. Solution : millis() — noter l'heure du dernier évènement et comparer sans bloquer.
const int LED_PIN = 2; // GPIO2 = LED bleue intégrée DevKit unsigned long lastTime = 0; bool ledState = false; void setup() { Serial.begin(115200); pinMode(LED_PIN, OUTPUT); } void loop() { unsigned long now = millis(); if (now - lastTime >= 1000) { lastTime = now; ledState = !ledState; digitalWrite(LED_PIN, ledState); Serial.printf("LED: %s\n", ledState ? "ON" : "OFF"); } // Ici : WiFi, MQTT, capteurs continuent de fonctionner ! }
const int BTN = 15; // GPIO15 → câbler vers GND const long DEBOUNCE_MS = 50; unsigned long lastDebounce = 0; int lastRaw = HIGH, stableState = HIGH; void setup() { Serial.begin(115200); pinMode(BTN, INPUT_PULLUP); // Résistance interne ~45 kΩ vers 3.3V // Repos = HIGH, Appuyé = LOW (logique inversée !) } void loop() { int raw = digitalRead(BTN); if (raw != lastRaw) lastDebounce = millis(); if ((millis() - lastDebounce) > DEBOUNCE_MS) { if (raw != stableState) { stableState = raw; Serial.println(stableState == LOW ? "APPUYÉ" : "relâché"); } } lastRaw = raw; }
ADC & PWM — Le Monde Analogique
Les capteurs réels produisent des tensions variables. L'ADC 12 bits les convertit en valeurs 0–4095. Le PWM simule une tension variable en sortie.
📊 ADC — Référence rapide
| Résolution | Niveaux | 1 LSB @ 3.3V |
|---|---|---|
| 8 bits | 256 | 12.9 mV |
| 10 bits | 1 024 | 3.22 mV |
| 12 bits (défaut) | 4 096 | 0.806 mV |
GPIO0,2,4,12–15,25–27 = ADC2. analogRead() retourne des erreurs quand WiFi est actif. Toujours utiliser GPIO32–39 (ADC1) dans les projets IoT.
🌊 PWM — LEDC 16 canaux
Le PWM allume/éteint très rapidement → valeur moyenne = tension voulue. L'ESP32 dispose de 16 canaux LEDC indépendants.
| Fonction | Rôle |
|---|---|
ledcSetup(canal, freq, bits) | Configure le canal PWM |
ledcAttachPin(pin, canal) | Attache GPIO au canal |
ledcWrite(canal, duty) | Définit le rapport cyclique |
const int POT = 34; // ADC1 — fonctionne avec WiFi ! const int LED = 2, CANAL = 0; void setup() { Serial.begin(115200); analogReadResolution(12); // 12 bits → 0 à 4095 analogSetAttenuation(ADC_11db); // plage 0–3.3V ledcSetup(CANAL, 5000, 8); // 5 kHz, 8 bits (0–255) ledcAttachPin(LED, CANAL); } void loop() { int val = analogRead(POT); float volt = val * 3.3f / 4095.0f; int pwm = map(val, 0, 4095, 0, 255); ledcWrite(CANAL, pwm); Serial.printf("ADC:%4d %.2fV PWM:%d\n", val, volt, pwm); delay(100); }
I2C — Communiquer avec les Capteurs
I2C = 2 fils (SDA + SCL) pour connecter jusqu'à 127 appareils sur le même bus. Sur ESP32 : SDA = GPIO21, SCL = GPIO22. Pull-up 4.7 kΩ obligatoires sur les deux lignes.
#include <Wire.h> #include <Adafruit_BME280.h> // Adafruit BME280 + Adafruit Unified Sensor Adafruit_BME280 bme; void setup() { Serial.begin(115200); Wire.begin(21, 22); // SDA=21, SCL=22 if (!bme.begin(0x76)) { Serial.println("❌ BME280 non trouvé ! Vérifier câblage et adresse I2C"); while (true) delay(1000); } Serial.println("✅ BME280 initialisé"); } void loop() { Serial.printf("Temp:%.1f°C Humi:%.1f%% Pres:%.1fhPa\n", bme.readTemperature(), bme.readHumidity(), bme.readPressure() / 100.0); delay(2000); } // Scanner I2C — trouver l'adresse d'un module inconnu : // for (byte a=1; a<127; a++) { // Wire.beginTransmission(a); // if (Wire.endTransmission()==0) Serial.printf("Trouvé 0x%02X\n",a); // }
WiFi — Connexion au Réseau
Le WiFi intégré se connecte à ton routeur comme un smartphone — obtient une adresse IP, communique avec le broker MQTT sur ton réseau local ou Internet.
#include <WiFi.h> const char* SSID = "Ton_Reseau"; const char* PASS = "Ton_Pass"; void connectWiFi() { if (WiFi.status() == WL_CONNECTED) return; WiFi.mode(WIFI_STA); WiFi.setAutoReconnect(true); WiFi.begin(SSID, PASS); Serial.print("WiFi"); unsigned long t = millis(); while (WiFi.status() != WL_CONNECTED) { if (millis() - t > 15000) { Serial.println("\nTimeout !"); return; } delay(500); Serial.print("."); } Serial.printf("\n✅ IP: %s | Signal: %d dBm\n", WiFi.localIP().toString().c_str(), WiFi.RSSI()); // -50 dBm = excellent | -70 dBm = correct | -90 dBm = faible } void setup() { Serial.begin(115200); connectWiFi(); } unsigned long lastCheck = 0; void loop() { if (millis() - lastCheck > 10000) { // Vérifier toutes les 10s lastCheck = millis(); if (WiFi.status() != WL_CONNECTED) connectWiFi(); } }
Solutions : (1) WiFiManager — portail web de configuration, (2) config.h dans .gitignore, (3) NVS — stockage non-volatile interne à l'ESP32 (clé/valeur persistant hors tension).
Architecture IoT Complète
Chaque système IoT suit le même pipeline. Comprendre chaque couche permet de diagnostiquer n'importe quel problème en 5 minutes.
🌐 HTTP vs MQTT — pourquoi MQTT pour l'IoT
| Critère | HTTP | MQTT |
|---|---|---|
| Modèle | Requête → Réponse → Fermeture | Connexion persistante |
| En-tête | 200–800 octets | 2 octets minimum ! |
| Données | Pull — tu demandes | Push — arrivent auto |
| Conçu pour | Navigateurs web | Millions de capteurs |
| Reconnexion | Nouvelle requête | Automatique + LWT |
📮 Analogie MQTT — le journal par abonnement
L'ESP32 (correspondant) envoie ses articles (données) à la rédaction (broker Mosquitto). Tous ceux abonnés au sujet "température salon" (Node-RED, app mobile) reçoivent automatiquement chaque nouveau message dès qu'il arrive. Pas besoin de demander "y a-t-il du nouveau ?"
En-tête 2 octets → MQTT fonctionne avec un signal GSM à 9600 bps ou une connexion satellite à 100 ms de latence.
MQTT — Le Protocole IoT
MQTT = Message Queuing Telemetry Transport. Créé en 1999 par IBM pour la télémétrie de pipelines pétroliers via satellite. Standard mondial de l'IoT.
| Concept | Définition | Exemple concret |
|---|---|---|
Topic | Adresse hiérarchique, niveaux séparés par / | maison/salon/temperature |
Payload | Contenu du message (texte, JSON, binaire) | {"temp":23.5,"humi":65.2} |
QoS 0 | At most once — envoi unique sans confirmation | Mesures très fréquentes non critiques |
QoS 1 | At least once — confirmé, peut arriver en double | Commandes importantes |
QoS 2 | Exactly once — 4 échanges, garanti une seule fois | Transactions critiques |
Retain | Broker garde le dernier message, l'envoie aux nouveaux abonnés | État ON/OFF d'un appareil |
LWT | Last Will : message publié si déconnexion anormale | maison/salon/status → "offline" |
#include <WiFi.h> #include <PubSubClient.h> #include <ArduinoJson.h> const char* WIFI_SSID = "Ton_Reseau"; const char* WIFI_PASS = "Ton_Pass"; const char* MQTT_HOST = "192.168.1.10"; // IP Raspberry Pi const int MQTT_PORT = 1883; const char* CLIENT_ID = "esp32_salon_01"; // UNIQUE par appareil ! const char* T_DATA = "maison/salon/meteo"; const char* T_STATUS = "maison/salon/status"; const char* T_CMD = "maison/salon/cmd"; WiFiClient wc; PubSubClient mqtt(wc); void onMsg(char* topic, byte* payload, unsigned int len) { String msg = ""; for (uint i=0; i<len; i++) msg += (char)payload[i]; Serial.printf("[%s] → %s\n", topic, msg.c_str()); if (String(topic) == T_CMD) { if (msg == "ON") digitalWrite(2, HIGH); if (msg == "OFF") digitalWrite(2, LOW); } } void reconnectMQTT() { while (!mqtt.connected()) { Serial.print("MQTT connexion..."); if (mqtt.connect(CLIENT_ID, nullptr, nullptr, T_STATUS, 1, true, "offline")) { mqtt.publish(T_STATUS, "online", true); // retain=true mqtt.subscribe(T_CMD); Serial.println(" ✅ OK"); } else { Serial.printf(" ❌ err %d\n", mqtt.state()); delay(5000); } } } void setup() { Serial.begin(115200); pinMode(2, OUTPUT); WiFi.begin(WIFI_SSID, WIFI_PASS); while (WiFi.status() != WL_CONNECTED) delay(500); mqtt.setServer(MQTT_HOST, MQTT_PORT); mqtt.setCallback(onMsg); mqtt.setBufferSize(512); reconnectMQTT(); } unsigned long lastPub = 0; void loop() { if (!mqtt.connected()) reconnectMQTT(); mqtt.loop(); // ⚠️ INDISPENSABLE — messages + keepalive if (millis() - lastPub >= 30000) { lastPub = millis(); StaticJsonDocument<200> doc; doc["temp"] = 23.5; doc["humi"] = 65.2; doc["rssi"] = WiFi.RSSI(); doc["uptime"] = millis() / 1000; char buf[200]; serializeJson(doc, buf); bool ok = mqtt.publish(T_DATA, buf); Serial.printf("Publié [%s]: %s\n", ok ? "OK" : "FAIL", buf); } }
Mosquitto — Le Serveur MQTT
Mosquitto est le broker MQTT open source le plus utilisé au monde. Léger, fiable, tourne parfaitement sur Raspberry Pi. C'est la centrale téléphonique de ton IoT.
# 1. Installation sur Raspberry Pi / Ubuntu / Debian sudo apt update && sudo apt install -y mosquitto mosquitto-clients sudo systemctl enable mosquitto && sudo systemctl start mosquitto sudo systemctl status mosquitto # → active (running) ✓ # 2. Configuration /etc/mosquitto/conf.d/maison.conf listener 1883 allow_anonymous true persistence true persistence_location /var/lib/mosquitto/ log_type all log_dest file /var/log/mosquitto/mosquitto.log # 3. Tests — ouvrir 2 terminaux mosquitto_sub -h localhost -t "#" -v # Terminal 1 : écouter TOUT mosquitto_pub -h localhost -t "test/hello" -m "Bonjour ESP32 !" # 4. Diagnostic production mosquitto_sub -h 192.168.1.10 -t "#" -v # Voir tous les messages mosquitto_pub -h 192.168.1.10 -t "salon/temp" -m "23.5" -r # retain sudo tail -f /var/log/mosquitto/mosquitto.log # 5. Authentification (production) sudo mosquitto_passwd -c /etc/mosquitto/passwd monuser # créer sudo mosquitto_passwd /etc/mosquitto/passwd user2 # ajouter # Puis dans maison.conf : # allow_anonymous false # password_file /etc/mosquitto/passwd
broker.hivemq.com:1883 — public · test.mosquitto.org:1883 — officiel Eclipse · EMQX Cloud — gratuit avec compte. ⚠️ Ne jamais envoyer de données personnelles sur un broker public.
Node-RED — Programmation Visuelle
Node-RED est un outil de programmation par flux visuels développé par IBM. Tu assembles des blocs graphiques (nœuds) avec des fils pour créer des automatisations puissantes — en 10 minutes tu as un dashboard complet.
# Installation officielle (Raspberry Pi / Ubuntu / Debian) bash <(curl -sL https://raw.githubusercontent.com/node-red/linux-installers/master/deb/update-nodejs-and-nodered) sudo systemctl enable nodered && sudo systemctl start nodered # Interface web : http://IP_DU_PI:1880 # Dashboard : http://IP_DU_PI:1880/ui # Installer le module Dashboard (jauges, graphiques, boutons) cd ~/.node-red && npm install node-red-dashboard # Puis : Menu → Restart dans l'interface web # Installer InfluxDB pour l'historique des données npm install node-red-contrib-influxdb
| Nœud | Catégorie | Rôle exact |
|---|---|---|
mqtt in | network | Reçoit messages depuis un topic MQTT — entrée IoT principale |
mqtt out | network | Publie sur un topic MQTT — commande l'ESP32 |
function | function | JavaScript pour transformer/filtrer/enrichir msg.payload — le plus puissant |
change | function | Modifie/copie/supprime propriétés du message — sans code |
switch | function | Route le message vers sorties différentes selon conditions |
inject | input | Injecte un message manuellement ou par timer — tests |
debug | output | Affiche la valeur en console debug — indispensable |
ui_gauge | dashboard | Jauge analogique temps réel |
ui_chart | dashboard | Graphique historique des mesures |
ui_switch | dashboard | Bouton ON/OFF → envoie commande MQTT |
🎛️ Flow visuel — Capteur → Dashboard + Alerte
// Nœud Function : reçoit msg.payload = '{"temp":23.5,"humi":65.2}' let data; try { data = JSON.parse(msg.payload); } catch(e) { node.warn("JSON invalide: " + msg.payload); return null; } if (data.temp < -40 || data.temp > 85) { node.warn("Temp hors plage"); return null; } msg.payload = data.temp; msg.color = data.temp < 18 ? '#4cc9f0' // bleu = froid : data.temp < 25 ? '#39ff7a' // vert = confortable : data.temp < 30 ? '#ffb703' // orange = chaud : '#ff4d6d'; // rouge = alerte let hist = global.get('temp_hist') || []; hist.push({ t: Date.now(), v: data.temp }); if (hist.length > 288) hist.shift(); // 288 = 24h à 5min d'intervalle global.set('temp_hist', hist); return msg;
5 Projets Réels
De la station météo au joystick DMA — projets terrain avec code C complet. Toutes les solutions sont affichées directement.
ESP32 + capteur BME280 mesure température/humidité/pression toutes les 30 secondes. Publie en JSON sur MQTT. Node-RED affiche tout en temps réel et envoie une alerte si humidité > 80%.
#include <WiFi.h> #include <PubSubClient.h> #include <Wire.h> #include <Adafruit_BME280.h> #include <ArduinoJson.h> const char* SSID = "Ton_Reseau"; const char* PASS = "Ton_Pass"; const char* BROKER = "192.168.1.10"; const char* DEVICE = "station_salon"; WiFiClient wc; PubSubClient mqtt(wc); Adafruit_BME280 bme; unsigned long lastPub = 0; void setup() { Serial.begin(115200); Wire.begin(21, 22); if (!bme.begin(0x76)) { Serial.println("❌ BME280!"); while(true); } WiFi.begin(SSID, PASS); while (WiFi.status() != WL_CONNECTED) delay(400); mqtt.setServer(BROKER, 1883); while (!mqtt.connect(DEVICE, nullptr, nullptr, "maison/salon/status", 1, true, "offline")) delay(3000); mqtt.publish("maison/salon/status", "online", true); Serial.println("✅ Prêt !"); } void loop() { if (!mqtt.connected()) { mqtt.connect(DEVICE, nullptr, nullptr, "maison/salon/status", 1, true, "offline"); } mqtt.loop(); if (millis() - lastPub >= 30000) { lastPub = millis(); StaticJsonDocument<200> doc; doc["temp"] = round(bme.readTemperature() * 10) / 10.0; doc["humi"] = round(bme.readHumidity() * 10) / 10.0; doc["pres"] = round(bme.readPressure() / 10.0) / 10.0; doc["rssi"] = WiFi.RSSI(); doc["up"] = millis() / 1000; char buf[200]; serializeJson(doc, buf); mqtt.publish("maison/salon/meteo", buf); Serial.printf("📤 %s\n", buf); } }
LDR (photorésistance) + R fixe 10 kΩ en diviseur sur GPIO34 (ADC1). Si luminosité basse (ADC > 2800) → allumer LED + publier MQTT "ON". Si luminosité haute (ADC < 1200) → éteindre. Hystérésis pour éviter le clignotement.
#define THR_DARK 2800 // seuil allumage (sombre) #define THR_BRIGHT 1200 // seuil extinction (lumineux) #define LED_PIN 2 static bool ledOn = false; unsigned long lastRead = 0; void loop() { mqtt.loop(); if (millis() - lastRead >= 200) { lastRead = millis(); int raw = analogRead(34); if (!ledOn && raw > THR_DARK) { ledOn = true; digitalWrite(LED_PIN, HIGH); mqtt.publish("maison/salon/lumiere", "ON", true); } else if (ledOn && raw < THR_BRIGHT) { ledOn = false; digitalWrite(LED_PIN, LOW); mqtt.publish("maison/salon/lumiere", "OFF", true); } } }
LiPo 1S (3.0V–4.2V) via diviseur R1=R2=100kΩ sur GPIO32. Suréchantillonnage ×16. Calculer tension réelle + SoC 0–100%. Publier MQTT retained. Clignoter LED si V < 3.2V.
#define N_OS 16 // suréchantillonnage ×16 uint32_t sum = 0; for (int i=0; i<N_OS; i++) sum += analogRead(32); float v_adc = ((float)(sum / N_OS) / 4095.0f) * 3.3f; float v_bat = v_adc * 2.0f; // diviseur /2 → ×2 pour reconstruire float soc = (v_bat - 3.0f) / (4.2f - 3.0f) * 100.0f; soc = constrain(soc, 0.0f, 100.0f); StaticJsonDocument<80> doc; doc["vbat"] = v_bat; doc["soc"] = (int)soc; char buf[80]; serializeJson(doc, buf); mqtt.publish("maison/batterie", buf, true); // retained if (v_bat < 3.2f) { // Alerte batterie faible digitalWrite(2, HIGH); delay(150); digitalWrite(2, LOW); delay(150); }
L'ESP32 se réveille toutes les 5 minutes, mesure, publie MQTT, puis dort. Consommation active ~250mA pendant ~4s, puis ~10µA en deep sleep → batterie dure des semaines. Variables RTC pour compter les réveils.
#include <esp_sleep.h> #define SLEEP_US (uint64_t)300 * 1000000 // 5 minutes RTC_DATA_ATTR int boot_count = 0; // survit au deep sleep ! RTC_DATA_ATTR float last_temp = 0; void setup() { Serial.begin(115200); boot_count++; Serial.printf("Réveil #%d\n", boot_count); // Toute la logique ici — loop() jamais atteint ! Wire.begin(21, 22); Adafruit_BME280 bme; if (bme.begin(0x76)) { last_temp = bme.readTemperature(); // ... WiFi + MQTT + publish ... } // Déconnexion propre avant sleep mqtt.disconnect(); WiFi.disconnect(true); WiFi.mode(WIFI_OFF); delay(200); esp_sleep_enable_timer_wakeup(SLEEP_US); esp_deep_sleep_start(); // ← setup() sera rappelé au réveil } void loop(){} // jamais atteint avec deep sleep
Joystick XY sur GPIO32/33 (ADC1). Normaliser -100% à +100%. Zone morte ±150 pts autour de 2048. Publier axes sur MQTT toutes les 50ms. Node-RED affiche les valeurs en jauge et contrôle un servo.
#define JOY_X 32 #define JOY_Y 33 #define CENTER 2048 #define DZ 150 float deadzone(int raw) { int e = raw - CENTER; if (e > -DZ && e < DZ) return 0.0f; float v = (float)(e - (e > 0 ? DZ : -DZ)) / (float)(CENTER - DZ); return constrain(v, -1.0f, 1.0f); } unsigned long lastPub = 0; void loop() { mqtt.loop(); if (millis() - lastPub >= 50) { lastPub = millis(); float x = deadzone(analogRead(JOY_X)) * 100; float y = deadzone(analogRead(JOY_Y)) * 100; StaticJsonDocument<60> doc; doc["x"] = (int)x; doc["y"] = (int)y; char buf[60]; serializeJson(doc, buf); mqtt.publish("robot/joystick", buf); } }
📝 Quiz — 40 Questions
Du débutant à l'expert — ESP32, GPIO, WiFi, MQTT, Mosquitto, Node-RED. Réponses directes avec explication.
Q01 — Que signifie GPIO ?
Quel est le développement complet de GPIO dans le contexte des microcontrôleurs ?
Q02 — Tension logique ESP32
Un capteur 5V peut-il être directement branché sur un GPIO ESP32 ?
Q03 — Pourquoi éviter delay() en IoT ?
Que se passe-t-il si on met delay(2000) dans loop() dans un projet MQTT ?
Q04 — Résolution ADC
L'ESP32 lit la valeur brute 2048. VREF = 3.3V, 12 bits. Quelle tension ?
Q05 — ADC2 + WiFi
Pourquoi éviter analogRead() sur GPIO26 dans un projet IoT ?
Q06 — INPUT_PULLUP
Un bouton est câblé entre GPIO15 et GND. pinMode(15, INPUT_PULLUP). Valeur au repos ?
Q07 — I2C pull-up
Le bus I2C fonctionne-t-il sans résistances pull-up sur SDA et SCL ?
Q08 — MQTT signification
Que signifie l'acronyme MQTT et pourquoi est-il adapté à l'IoT ?
Q09 — Topic MQTT
Quelle est la différence entre le wildcard + et # dans les topics MQTT ?
Q10 — Port MQTT
Quel port utilise MQTT non chiffré ? Et chiffré TLS ?
Q11 — mqtt.loop()
Que fait mqtt.loop() et pourquoi l'appeler à chaque itération ?
Q12 — Message retained
À quoi sert le flag retain dans mqtt.publish() ?
Q13 — LWT
Que publie le broker quand un ESP32 se déconnecte sans envoyer DISCONNECT ?
Q14 — CLIENT_ID unique
Que se passe-t-il si deux ESP32 utilisent le même CLIENT_ID MQTT ?
Q15 — ArduinoJson
Pourquoi StaticJsonDocument est préféré à DynamicJsonDocument sur ESP32 ?
Q16 — Broker rôle
Quelle est la différence entre publisher, broker et subscriber en MQTT ?
Q17 — QoS 0 vs 1
Quand utiliser QoS 0 et quand utiliser QoS 1 en IoT maison ?
Q18 — mosquitto_sub diagnostic
Quelle commande voir TOUS les messages MQTT en temps réel sur localhost ?
mosquitto_sub -h localhost -t "#" -v — -t "#" s'abonne à TOUS les topics, -v affiche aussi le nom du topic. Commande de débogage n°1.Q19 — Node-RED function
Que fait un nœud function dans Node-RED et quel langage utilise-t-il ?
Q20 — Deep sleep consommation
Quelle est la consommation typique de l'ESP32 en deep sleep et que reste-t-il actif ?
Q21 — RTC_DATA_ATTR
Comment conserver une variable entre deux réveils du deep sleep ?
RTC_DATA_ATTR : RTC_DATA_ATTR int compteur = 0; — stockée dans la mémoire RTC (8KB) qui reste alimentée pendant le sleep.Q22 — allow_anonymous Mosquitto
Que signifie allow_anonymous false dans la configuration Mosquitto ?
Q23 — WiFiClientSecure
Quelle est la différence entre WiFiClient et WiFiClientSecure pour MQTT ?
Q24 — FreeRTOS core allocation
Comment FreeRTOS gère-t-il le WiFi et ton code simultanément sur ESP32 ?
Q25 — Node-RED switch
Comment déclencher une alerte email si la température dépasse 30°C dans Node-RED ?
Q26 — Hystérésis
Pourquoi ajouter une hystérésis à une LED déclenchée par seuil ADC ?
Q27 — JSON IoT
Pourquoi publier {"temp":23.5,"humi":65} plutôt que "23.5,65" sur MQTT ?
Q28 — OTA
Comment mettre à jour le firmware d'un ESP32 déployé sans câble USB ?
Q29 — Reconnexion MQTT robuste
Pourquoi éviter while(!mqtt.connected()) avec delay() dans loop() ?
Q30 — Store-and-forward
Comment ne pas perdre de données quand le WiFi est temporairement absent ?
Q31 — Dashboard bidirectionnel
Comment contrôler un relais ESP32 depuis un bouton Node-RED dashboard ?
Q32 — millis() pattern
Écrire le pattern millis() pour publier MQTT toutes les 30 secondes.
unsigned long last=0; dans loop() : if(millis()-last>=30000){last=millis(); mqtt.publish(...);} — non bloquant, tout continue de fonctionner.Q33 — MQTT 5.0 nouveautés
Quels sont les 3 principaux apports de MQTT 5.0 par rapport à 3.1.1 ?
Q34 — InfluxDB + Grafana
Pourquoi InfluxDB + Grafana plutôt que le seul dashboard Node-RED pour l'historique ?
Q35 — ESP-IDF vs Arduino
Quand utiliser ESP-IDF plutôt qu'Arduino pour programmer l'ESP32 ?
Q36 — Suréchantillonnage
Comment passer de 12 bits à 14 bits de résolution effective sans ADC externe ?
Q37 — LWT scénario
L'ESP32 plante à cause d'une exception. Que publie Mosquitto automatiquement ?
Q38 — PWM fréquence
Pour un servo moteur RC (20ms = 50Hz), quelle fréquence configurer en ledcSetup() ?
ledcSetup(canal, 50, 16) — 50Hz, 16 bits de résolution. Rapport cyclique 1ms = ~3276 (5% de 65535), 2ms = ~6553 (10%). Position servo = valeur CCR.Q39 — Débogage couche par couche
Node-RED ne reçoit aucune donnée de l'ESP32. Quelle est la première étape de débogage ?
mosquitto_sub -h localhost -t "#" -v — vérifier que le broker reçoit bien les messages. Si oui → problème Node-RED. Si non → problème ESP32 (vérifier Serial.println).Q40 — Sécurité port 1883
Pourquoi ne jamais exposer le port 1883 directement sur Internet ?
Cheat Sheet & Conseils Pro
Référence rapide — ce qui distingue un projet IoT de production d'un prototype fragile.
GPIO Digital & PWM
| Fonction | Usage |
|---|---|
pinMode(p, OUTPUT) | LED, relais, buzzer |
pinMode(p, INPUT_PULLUP) | Bouton vers GND |
digitalWrite(p, HIGH) | Mettre à 3.3V |
digitalRead(p) | Lire HIGH ou LOW |
ledcSetup(c,f,b) | Config PWM canal |
ledcWrite(c, duty) | Rapport cyclique |
Ne Jamais Faire
- ❌ Appliquer > 3.3V sur GPIO
- ❌ GPIO6–11 (Flash SPI interne)
- ❌ ADC2 avec WiFi actif
- ❌ delay() dans un projet MQTT
- ❌ Same CLIENT_ID sur 2 ESP32
- ❌ Buffer DMA sur la pile locale
- ❌ Port 1883 exposé sur Internet
MQTT — Guide rapide
| Concept | Usage |
|---|---|
| Port 1883 | Réseau local non chiffré |
| Port 8883 | Internet TLS/SSL |
| QoS 0 | Mesures fréquentes |
| QoS 1 | Commandes importantes |
| retain=true | État ON/OFF persistant |
| LWT | Détection panne auto |
| mqtt.loop() | Obligatoire dans loop() |
Débogage Couche par Couche
Serial.println ESP32 — confirme que l'ESP32 publie
mosquitto_sub "#" -v — confirme que le broker reçoit
Nœud debug Node-RED — confirme la réception
Vérifier les topics — "Salon" ≠ "salon" (casse exacte)
| Erreur fréquente | Symptôme | Solution |
|---|---|---|
| delay() dans loop() | MQTT se déconnecte, WiFi instable | Remplacer par pattern millis() |
| ADC2 avec WiFi | analogRead() retourne -1 ou valeur aléatoire | Utiliser GPIO32–39 (ADC1 uniquement) |
| Oublier mqtt.loop() | Messages jamais reçus, broker déconnecte | Appeler mqtt.loop() à chaque itération |
| CLIENT_ID dupliqué | ESP32 se déconnecte en boucle | Utiliser WiFi.macAddress() comme ID |
| Buffer MQTT trop petit | mqtt.publish() retourne false | mqtt.setBufferSize(512) dans setup() |
| GPIO flottant en entrée | Lectures aléatoires HIGH/LOW | INPUT_PULLUP ou INPUT_PULLDOWN |
| V_in GPIO > 3.3V | GPIO grillé définitivement | Diviseur de tension ou level-shifter |
| Topic avec faute de frappe | Aucune donnée dans Node-RED | mosquitto_sub "#" -v pour voir les topics réels |