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:
@@ -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();
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user