#include "mqtt_client.h" #ifndef QEMU_BUILD #include #include #include #include #include "state.h" #include "config.h" // --- Config --- static bool mqttActif = false; static String mqttServer = "192.168.1.36"; static uint16_t mqttPort = 1883; static String mqttUser; static String mqttPass; static String mqttBase = "solar"; // topic de base static uint32_t mqttInterval = 30000; // ms entre publications // --- État runtime --- static WiFiClient wifiClient; static PubSubClient mqttClient(wifiClient); static bool mqttConnecte = false; static unsigned long tDernierePubli = 0; static unsigned long tDerniereConnex = 0; static const unsigned long RECONNECT_INTERVAL = 15000UL; // --------------------------------------------------------------------------- // NVS // --------------------------------------------------------------------------- static void chargerNVS() { Preferences p; p.begin("mqtt", true); mqttActif = p.getBool("enabled", false); mqttServer = p.getString("server", "192.168.1.36"); mqttPort = p.getUShort("port", 1883); mqttUser = p.getString("user", ""); mqttPass = p.getString("pass", ""); mqttBase = p.getString("base", "solar"); mqttInterval = p.getULong("interval", 30000); p.end(); } // --------------------------------------------------------------------------- // Topics dérivés du topic de base // --------------------------------------------------------------------------- static String t(const char* suffixe) { return mqttBase + "/" + suffixe; } // --------------------------------------------------------------------------- // Callback réception (commande relais / entrées) // --------------------------------------------------------------------------- static void mqttCallback(char* topic, byte* payload, unsigned int len) { String msg; for (unsigned int i = 0; i < len; i++) msg += (char)payload[i]; msg.trim(); bool etat = (msg == "ON" || msg == "1" || msg == "true"); String top = String(topic); if (top == t("relay/1/set")) { state.relay1 = etat; digitalWrite(PIN_RELAY1, etat ? HIGH : LOW); Serial.printf("[MQTT] Relais 1 → %s\n", etat ? "ON" : "OFF"); } else if (top == t("relay/2/set")) { state.relay2 = etat; digitalWrite(PIN_RELAY2, etat ? HIGH : LOW); Serial.printf("[MQTT] Relais 2 → %s\n", etat ? "ON" : "OFF"); } } // --------------------------------------------------------------------------- // Connexion / reconnexion // --------------------------------------------------------------------------- static void connecterMqtt() { if (WiFi.status() != WL_CONNECTED) return; mqttClient.setServer(mqttServer.c_str(), mqttPort); mqttClient.setCallback(mqttCallback); mqttClient.setBufferSize(512); String clientId = "kc868-" + WiFi.macAddress(); clientId.replace(":", ""); bool ok; if (mqttUser.length() > 0) { ok = mqttClient.connect(clientId.c_str(), mqttUser.c_str(), mqttPass.c_str()); } else { ok = mqttClient.connect(clientId.c_str()); } if (ok) { mqttConnecte = true; Serial.printf("[MQTT] Connecté — %s:%u base: %s intervalle: %us\n", mqttServer.c_str(), mqttPort, mqttBase.c_str(), mqttInterval / 1000); // Abonnements commandes mqttClient.subscribe(t("relay/1/set").c_str()); mqttClient.subscribe(t("relay/2/set").c_str()); // Publication immédiate après connexion mqttPublierEtat(); } else { mqttConnecte = false; Serial.printf("[MQTT] Échec connexion (rc=%d) — retry dans %lus\n", mqttClient.state(), RECONNECT_INTERVAL / 1000); } } // --------------------------------------------------------------------------- // Publication état complet // --------------------------------------------------------------------------- static void pub(const char* suffixe, float val, int decimales = 2) { char buf[16]; dtostrf(val, 1, decimales, buf); mqttClient.publish(t(suffixe).c_str(), buf, true); } static void pub(const char* suffixe, bool val) { mqttClient.publish(t(suffixe).c_str(), val ? "ON" : "OFF", true); } static void pub(const char* suffixe, int val) { mqttClient.publish(t(suffixe).c_str(), String(val).c_str(), true); } void mqttPublierEtat() { if (!mqttConnecte || !mqttClient.connected()) return; // Panneau solaire pub("pv/voltage", state.pv); pub("pv/current", state.pvCurrent); // Batterie pub("battery/voltage", state.battery); pub("battery/soc", (int)state.batSOC); pub("battery/temperature", state.batTemperature, 1); pub("battery/status", (int)state.batStatut); // Sortie de charge pub("load/voltage", state.loadVoltage); pub("load/current", state.loadCurrent); pub("load/power", state.loadPower, 1); // Énergie pub("energy/generated/today", state.energieGenJour); pub("energy/generated/total", state.energieGenTotal); pub("energy/consumed/today", state.energieConJour); pub("energy/consumed/total", state.energieConTotal); // État général pub("sun", state.sun); pub("rs485/ok", state.rs485_ok); // Relais pub("relay/1", state.relay1); pub("relay/2", state.relay2); // Entrées numériques pub("input/1", state.di1); pub("input/2", state.di2); tDernierePubli = millis(); } // --------------------------------------------------------------------------- // API publique // --------------------------------------------------------------------------- void initMqtt() { chargerNVS(); Serial.printf("[MQTT] %s — %s:%u base: %s intervalle: %us\n", mqttActif ? "Activé" : "Désactivé", mqttServer.c_str(), mqttPort, mqttBase.c_str(), mqttInterval / 1000); } void gererMqtt() { if (!mqttActif) return; if (WiFi.status() != WL_CONNECTED) { if (mqttConnecte) { mqttConnecte = false; } return; } if (!mqttClient.connected()) { mqttConnecte = false; unsigned long maintenant = millis(); if ((maintenant - tDerniereConnex) >= RECONNECT_INTERVAL) { tDerniereConnex = maintenant; connecterMqtt(); } return; } mqttClient.loop(); // Publication périodique if ((millis() - tDernierePubli) >= mqttInterval) { mqttPublierEtat(); } } bool setMqttConfig(bool enabled, const char* server, uint16_t port, const char* user, const char* pass, const char* topicBase, uint32_t intervalMs) { Preferences p; p.begin("mqtt", false); p.putBool("enabled", enabled); p.putString("server", server); p.putUShort("port", port); p.putString("user", user); p.putString("pass", pass); p.putString("base", topicBase); p.putULong("interval", intervalMs); p.end(); if (mqttConnecte) { mqttClient.disconnect(); mqttConnecte = false; } tDerniereConnex = 0; chargerNVS(); Serial.printf("[MQTT] Config mise à jour — %s\n", mqttActif ? "activé" : "désactivé"); return true; } void getMqttJson(String &out) { JsonDocument doc; doc["enabled"] = mqttActif; doc["connected"] = mqttConnecte; doc["server"] = mqttServer; doc["port"] = mqttPort; doc["user"] = mqttUser; doc["pass"] = mqttPass; doc["base"] = mqttBase; doc["interval"] = mqttInterval / 1000; // en secondes pour l'UI serializeJson(doc, out); } #else // --- Stubs QEMU --- void initMqtt() { Serial.println("[MQTT] Désactivé (build QEMU)"); } void gererMqtt() {} void mqttPublierEtat() {} void getMqttJson(String &out) { out = "{\"enabled\":false,\"connected\":false}"; } bool setMqttConfig(bool, const char*, uint16_t, const char*, const char*, const char*, uint32_t) { return false; } #endif