213 lines
6.5 KiB
C
213 lines
6.5 KiB
C
#ifndef MQTT_H
|
|
#define MQTT_H
|
|
|
|
#include <PubSubClient.h>
|
|
#include <WiFiClient.h>
|
|
#include <ArduinoJson.h>
|
|
|
|
const char* mqtt_server = "broker.emqx.io";
|
|
const int mqtt_port = 1883;
|
|
|
|
// Topics
|
|
const char* mqtt_topic_state = "ups/dev15/state";
|
|
const char* mqtt_topic_cmd = "ups/dev15/cmd";
|
|
|
|
WiFiClient espClient;
|
|
PubSubClient mqttClient(espClient);
|
|
|
|
unsigned long lastMqttReconnectAttempt = 0;
|
|
unsigned long lastMqttPublish = 0;
|
|
const unsigned long mqttPublishInterval = 2000; // Publish state every 2 seconds
|
|
|
|
// Forward declaration
|
|
void mqttPublishState();
|
|
|
|
void mqttCallback(char* topic, byte* payload, unsigned int length) {
|
|
Serial.print("Message arrived on topic: ");
|
|
Serial.print(topic);
|
|
Serial.print(". Message: ");
|
|
String messageTemp;
|
|
|
|
for (int i = 0; i < length; i++) {
|
|
Serial.print((char)payload[i]);
|
|
messageTemp += (char)payload[i];
|
|
}
|
|
Serial.println();
|
|
|
|
// Parse command
|
|
if (String(topic) == mqtt_topic_cmd) {
|
|
DynamicJsonDocument doc(256);
|
|
DeserializationError error = deserializeJson(doc, payload, length);
|
|
if (!error) {
|
|
String cmd = doc["cmd"].as<String>();
|
|
String v = doc["val"].as<String>();
|
|
bool updated = false;
|
|
|
|
if (cmd == "on") {
|
|
bool tgtState = v != "0";
|
|
if (tgtState != powerOutputActive) {
|
|
// This function is defined in upsSvr.h / main logic
|
|
// Note: mqtt.h is included after or before? Need to ensure it can call setOutputPowerState
|
|
// Actually, setOutputPowerState is declared in upsCfg.h maybe?
|
|
// If compilation fails, we can just set the flag. Wait, let's use the function since it's global.
|
|
setOutputPowerState(tgtState);
|
|
updated = true;
|
|
}
|
|
} else if (cmd == "auto") {
|
|
uint8_t tmpAutoOnMode = v.toInt();
|
|
if ((tmpAutoOnMode != cfgAutoOn) && (tmpAutoOnMode <= 2)) {
|
|
cfgAutoOn = tmpAutoOnMode;
|
|
configChanged = true;
|
|
updated = true;
|
|
}
|
|
} else if (cmd == "shutdown") {
|
|
uint16_t tSec = v.toInt();
|
|
if (tSec > 999) tSec = 999;
|
|
uint32_t nanoT = ((uint32_t)tSec) * 1000000UL;
|
|
if (nanoT < 999999) nanoT = 999999;
|
|
bool ok = setPersistentShutdown(nanoT);
|
|
if (cfgShutdownInSeconds != tSec && ok) {
|
|
cfgShutdownInSeconds = tSec;
|
|
configChanged = true;
|
|
updated = true;
|
|
}
|
|
} else if (cmd == "shutlv") {
|
|
int16_t lv = v.toInt();
|
|
if (lv >= 0 && lv <= 5) {
|
|
if (lv != cfgShutdownSuggestion) {
|
|
cfgShutdownSuggestion = lv;
|
|
configChanged = true;
|
|
updated = true;
|
|
}
|
|
}
|
|
} else if (cmd == "cm") {
|
|
int16_t i = v.toInt();
|
|
if (i >= 0 && i <= 9) {
|
|
cfgChargeMode = i;
|
|
configChanged = true;
|
|
updated = true;
|
|
}
|
|
} else if (cmd == "coil") {
|
|
int16_t i = v.toInt();
|
|
if (i >= 0 && i <= 5) {
|
|
cfgInputLostTriggerChargeMode = i;
|
|
configChanged = true;
|
|
updated = true;
|
|
}
|
|
} else if (cmd == "covp") {
|
|
int16_t i = v.toInt();
|
|
if (i > 1) i = 1;
|
|
if (i != cfgChargeOvervoltageProtection) {
|
|
cfgChargeOvervoltageProtection = i;
|
|
configChanged = true;
|
|
updated = true;
|
|
}
|
|
} else if (cmd == "rofc") {
|
|
int16_t i = v.toInt();
|
|
if (i > 0 && needFullChargeOnce) {
|
|
resetOnGoingFullChargeAfterLineLost();
|
|
updated = true;
|
|
}
|
|
} else if (cmd == "car") {
|
|
int16_t i = v.toInt();
|
|
if (i > 1) i = 1;
|
|
if (i != cfgChargeAutomaticReducer) {
|
|
cfgChargeAutomaticReducer = i;
|
|
configChanged = true;
|
|
updated = true;
|
|
}
|
|
}
|
|
|
|
if (updated) {
|
|
mqttPublishState();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
boolean mqttReconnect() {
|
|
String clientId = "UPS_DEV15_" + String(random(0xffff), HEX);
|
|
Serial.print("Attempting MQTT connection as ");
|
|
Serial.println(clientId);
|
|
if (mqttClient.connect(clientId.c_str())) {
|
|
Serial.println("MQTT Connected");
|
|
// Once connected, publish an announcement...
|
|
mqttClient.publish("ups/dev15/status", "online");
|
|
// ... and resubscribe
|
|
mqttClient.subscribe(mqtt_topic_cmd);
|
|
} else {
|
|
Serial.print("failed, rc=");
|
|
Serial.print(mqttClient.state());
|
|
Serial.println(" try again in 5 seconds");
|
|
}
|
|
return mqttClient.connected();
|
|
}
|
|
|
|
void mqttInit() {
|
|
mqttClient.setServer(mqtt_server, mqtt_port);
|
|
mqttClient.setBufferSize(1024); // Increase buffer size for large JSON payloads
|
|
mqttClient.setCallback(mqttCallback);
|
|
}
|
|
|
|
void mqttPublishState() {
|
|
DynamicJsonDocument jsonDoc(1024);
|
|
String jsonResponse;
|
|
|
|
uint8_t lineActiveState = isUpsPoweredFromBattery() ? 2 : isInputLinePowered() ? 1 : isUpsOn() ? 2 : 0;
|
|
|
|
jsonDoc["success"] = 2;
|
|
jsonDoc["on"] = powerOutputActive ? 1 : 0;
|
|
jsonDoc["state"] = isUpsOn() ? 1 : 0;
|
|
jsonDoc["v"] = String(batteryVoltage, 2);
|
|
jsonDoc["batt"] = batteryLevel;
|
|
jsonDoc["battP"] = batteryPercentage;
|
|
jsonDoc["line"] = lineActiveState;
|
|
jsonDoc["uptime"] = millis() / 1000;
|
|
|
|
jsonDoc["auto"] = cfgAutoOn;
|
|
jsonDoc["battBlink"] = isUpsBatteryIndicatorChargingOrBlink() ? 1 : 0;
|
|
jsonDoc["battCritical"] = isUpsBatteryCritical() ? 1 : 0;
|
|
jsonDoc["chargeLv"] = getRawBatteryLevelFromPercentage(batteryPercentage);
|
|
jsonDoc["battMax"] = 4;
|
|
jsonDoc["timedShutdown"] = (persistentShutdown && powerOutputActive) ? 1 : 0;
|
|
jsonDoc["shutdownRemain"] = getPersistentShutdownTick() / 1000000;
|
|
jsonDoc["shutdownSuggestMode"] = cfgShutdownSuggestion;
|
|
jsonDoc["shutdownSuggest"] = getShutdownSuggestionFlag() ? 1 : 0;
|
|
jsonDoc["shutLastTimer"] = cfgShutdownInSeconds;
|
|
|
|
jsonDoc["chgError"] = chargeError;
|
|
jsonDoc["charging"] = getChargingState();
|
|
jsonDoc["chgMode"] = cfgChargeMode;
|
|
jsonDoc["chgOVP"] = cfgChargeOvervoltageProtection;
|
|
jsonDoc["chgReducer"] = cfgChargeAutomaticReducer;
|
|
jsonDoc["chgFullTrig"] = cfgInputLostTriggerChargeMode;
|
|
jsonDoc["chgOFC"] = needFullChargeOnce ? 1 : 0;
|
|
|
|
serializeJson(jsonDoc, jsonResponse);
|
|
mqttClient.publish(mqtt_topic_state, jsonResponse.c_str());
|
|
}
|
|
|
|
void mqttRoutine() {
|
|
if (!mqttClient.connected()) {
|
|
long now = millis();
|
|
if (now - lastMqttReconnectAttempt > 5000) {
|
|
lastMqttReconnectAttempt = now;
|
|
// Attempt to reconnect
|
|
if (mqttReconnect()) {
|
|
lastMqttReconnectAttempt = 0;
|
|
}
|
|
}
|
|
} else {
|
|
mqttClient.loop();
|
|
|
|
// Publish state periodically
|
|
long now = millis();
|
|
if (now - lastMqttPublish > mqttPublishInterval) {
|
|
lastMqttPublish = now;
|
|
mqttPublishState();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|