From 4854faa0278a82e570f74ca9e817c79eefbb6191 Mon Sep 17 00:00:00 2001 From: Gilles Soulier Date: Sat, 23 May 2026 16:45:01 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20module=20MQTT=20=E2=80=94=20PubSubClien?= =?UTF-8?q?t,=20deadband,=20reconnexion=20non-bloquante?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implémente mqtt_manager.h/.cpp : connexion sans authentification si MQTT_USER vide, filtre deadband par sonde, retain=true, jamais de valeur d'erreur publiée. Intégration dans main.cpp (mqtt_init/update). Co-Authored-By: Claude Sonnet 4.6 --- include/mqtt_manager.h | 7 ++++ src/main.cpp | 4 +++ src/mqtt_manager.cpp | 74 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+) create mode 100644 include/mqtt_manager.h create mode 100644 src/mqtt_manager.cpp diff --git a/include/mqtt_manager.h b/include/mqtt_manager.h new file mode 100644 index 0000000..a81d49d --- /dev/null +++ b/include/mqtt_manager.h @@ -0,0 +1,7 @@ +#pragma once + +// Initialise le client MQTT (serveur + port) +void mqtt_init(); + +// À appeler à chaque loop() : reconnexion non-bloquante + publication deadband +void mqtt_update(); diff --git a/src/main.cpp b/src/main.cpp index 64513be..4a9fb62 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -3,6 +3,7 @@ #include "network.h" #include "sensors.h" #include "web_server.h" +#include "mqtt_manager.h" SondeConfig sondesConfig[NB_SONDES] = { { "T°C Ext", "maison/jardin/ext/temperature", 60000, 0.2f }, @@ -23,6 +24,8 @@ void setup() { network_init(); sensors_init(); web_server_init(); + mqtt_init(); + Serial.println("[BOOT] Initialisation terminée"); } void loop() { @@ -31,4 +34,5 @@ void loop() { if (nouvelleMesure) { web_server_notify_clients(); } + mqtt_update(); } diff --git a/src/mqtt_manager.cpp b/src/mqtt_manager.cpp new file mode 100644 index 0000000..569bb24 --- /dev/null +++ b/src/mqtt_manager.cpp @@ -0,0 +1,74 @@ +#include "mqtt_manager.h" +#include "config.h" +#include +#include + +static WiFiClient _wifiClient; +static PubSubClient _mqtt(_wifiClient); +static uint32_t _dernierRetryMs = 0; + +// Tente une connexion MQTT (bloquant le temps de la poignée de main TCP) +static bool _connecter() { + Serial.printf("[MQTT] Connexion → %s:%d\n", MQTT_BROKER, MQTT_PORT); + bool ok; + if (strlen(MQTT_USER) > 0) { + ok = _mqtt.connect(MQTT_CLIENT_ID, MQTT_USER, MQTT_PASS_STR); + } else { + ok = _mqtt.connect(MQTT_CLIENT_ID); + } + if (ok) { + Serial.println("[MQTT] Connecté"); + netStatus.mqttConnecte = true; + } else { + Serial.printf("[MQTT] Échec, état=%d\n", _mqtt.state()); + netStatus.mqttConnecte = false; + } + return ok; +} + +void mqtt_init() { + _mqtt.setServer(MQTT_BROKER, MQTT_PORT); +} + +void mqtt_update() { + // Pas de WiFi STA = pas de MQTT + if (!netStatus.wifiConnecte || netStatus.modeAP) return; + + // Reconnexion non-bloquante : on ne retente que toutes les MQTT_RETRY_MS + if (!_mqtt.connected()) { + netStatus.mqttConnecte = false; + if (millis() - _dernierRetryMs > MQTT_RETRY_MS) { + _dernierRetryMs = millis(); + _connecter(); + } + return; + } + + // Traitement du keepalive et des messages entrants + _mqtt.loop(); + + // Publication par sonde avec filtre deadband + for (uint8_t i = 0; i < NB_SONDES; i++) { + // Ne jamais publier une valeur d'erreur (−127 °C, 85 °C, NAN) + if (sondesEtat[i].erreur) continue; + + float delta = fabsf(sondesEtat[i].tempActuelle - sondesEtat[i].dernierPublie); + uint32_t ecart = millis() - sondesEtat[i].dernierPubliMs; + bool deadbandOk = delta >= sondesConfig[i].deadband; + bool intervalOk = ecart >= sondesConfig[i].intervalleMs; + + // Publication si variation hors deadband OU intervalle max écoulé + // Note : au premier cycle, dernierPubliMs == 0 → ecart très grand → publication immédiate + if (deadbandOk || intervalOk) { + char payload[8]; + snprintf(payload, sizeof(payload), "%.1f", sondesEtat[i].tempActuelle); + // retain=true : le broker mémorise la dernière valeur pour les nouveaux abonnés + bool ok = _mqtt.publish(sondesConfig[i].topic, payload, true); + if (ok) { + sondesEtat[i].dernierPublie = sondesEtat[i].tempActuelle; + sondesEtat[i].dernierPubliMs = millis(); + Serial.printf("[MQTT] %s → %s °C\n", sondesConfig[i].topic, payload); + } + } + } +}