feat: module MQTT — PubSubClient, deadband, reconnexion non-bloquante

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 <noreply@anthropic.com>
This commit is contained in:
2026-05-23 16:45:01 +02:00
parent 49c54f4e48
commit 4854faa027
3 changed files with 85 additions and 0 deletions
+7
View File
@@ -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();
+4
View File
@@ -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();
}
+74
View File
@@ -0,0 +1,74 @@
#include "mqtt_manager.h"
#include "config.h"
#include <WiFi.h>
#include <PubSubClient.h>
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);
}
}
}
}