mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-04 02:09:30 +02:00
add hostname to mqtt ids. Use arduinojson library
This commit is contained in:
parent
581ca5d705
commit
920786a356
4 changed files with 7660 additions and 107 deletions
|
@ -4,6 +4,7 @@
|
|||
#include <freertos/FreeRTOS.h>
|
||||
#include "../../../USER_SETTINGS.h"
|
||||
#include "../../battery/BATTERIES.h"
|
||||
#include "../../lib/bblanchon-ArduinoJson/ArduinoJson.h"
|
||||
#include "../../lib/knolleary-pubsubclient/PubSubClient.h"
|
||||
#include "../utils/timer.h"
|
||||
|
||||
|
@ -28,76 +29,59 @@ static void publish_values(void) {
|
|||
|
||||
static void publish_cell_voltages(void) {
|
||||
static bool mqtt_first_transmission = true;
|
||||
|
||||
// If the cell voltage number isn't initialized...
|
||||
static JsonDocument doc;
|
||||
static const char* hostname = WiFi.getHostname();
|
||||
if (nof_cellvoltages == 0u) {
|
||||
return;
|
||||
}
|
||||
// At startup, re-post the discovery message for home assistant
|
||||
|
||||
if (mqtt_first_transmission == true) {
|
||||
mqtt_first_transmission = false;
|
||||
|
||||
// Base topic for any cell voltage "sensor"
|
||||
String topic = "homeassistant/sensor/battery-emulator/cell_voltage";
|
||||
for (int i = 0; i < nof_cellvoltages; i++) {
|
||||
// Build JSON message with device configuration for each cell voltage
|
||||
// Probably shouldn't be BatteryEmulator here, instead "LeafBattery"
|
||||
// or similar but hey, it works.
|
||||
// mqtt_msg is a global buffer, should be fine since we run too much
|
||||
// in a single thread :)
|
||||
snprintf(mqtt_msg, sizeof(mqtt_msg),
|
||||
"{"
|
||||
"\"device\": {"
|
||||
"\"identifiers\": ["
|
||||
"\"battery-emulator\""
|
||||
"],"
|
||||
"\"manufacturer\": \"DalaTech\","
|
||||
"\"model\": \"BatteryEmulator\","
|
||||
"\"name\": \"BatteryEmulator\""
|
||||
"},"
|
||||
"\"device_class\": \"voltage\","
|
||||
"\"enabled_by_default\": true,"
|
||||
"\"object_id\": \"sensor_battery_voltage_cell%d\","
|
||||
"\"origin\": {"
|
||||
"\"name\": \"BatteryEmulator\","
|
||||
"\"sw\": \"%s-mqtt\","
|
||||
"\"url\": \"https://github.com/dalathegreat/Battery-Emulator\""
|
||||
"},"
|
||||
"\"state_class\": \"measurement\","
|
||||
"\"name\": \"Battery Cell Voltage %d\","
|
||||
"\"state_topic\": \"battery-emulator/spec_data\","
|
||||
"\"unique_id\": \"battery-emulator_battery_voltage_cell%d\","
|
||||
"\"unit_of_measurement\": \"V\","
|
||||
"\"value_template\": \"{{ value_json.cell_voltages[%d] }}\""
|
||||
"}",
|
||||
i + 1, version_number, i + 1, i + 1, i);
|
||||
// End each discovery topic with cell number and '/config'
|
||||
String cell_topic = topic + String(i + 1) + "/config";
|
||||
mqtt_publish_retain(cell_topic.c_str());
|
||||
}
|
||||
} else {
|
||||
// Every 5-ish seconds, build the JSON payload for the state topic. This requires
|
||||
// some annoying formatting due to C++ not having nice Python-like string formatting.
|
||||
// msg_length is a cumulative variable to track start position (param 1) and for
|
||||
// modifying the maxiumum amount of characters to write (param 2). The third parameter
|
||||
// is the string content
|
||||
|
||||
// If cell voltages haven't been populated...
|
||||
if (cellvoltages[0] == 0u / 1000) { //cell voltage is in mV and homeassistant expects V
|
||||
for (int i = 0; i < nof_cellvoltages; i++) {
|
||||
char cellNumber[4]; // Buffer to hold the formatted cell number
|
||||
sprintf(cellNumber, "%03d", i + 1); // Format the cell number with leading zeros
|
||||
|
||||
doc["name"] = "Battery Cell Voltage " + String(cellNumber);
|
||||
doc["object_id"] = "battery_voltage_cell" + String(cellNumber);
|
||||
doc["unique_id"] = "battery-emulator_battery_voltage_cell" + String(cellNumber);
|
||||
doc["device_class"] = "voltage";
|
||||
doc["state_class"] = "measurement";
|
||||
doc["state_topic"] = "battery-emulator/spec_data";
|
||||
doc["unit_of_measurement"] = "V";
|
||||
doc["enabled_by_default"] = true;
|
||||
doc["expire_after"] = 240;
|
||||
doc["value_template"] = "{{ value_json.cell_voltages[" + String(i) + "] }}";
|
||||
doc["device"]["identifiers"][0] = "battery-emulator";
|
||||
doc["device"]["manufacturer"] = "DalaTech";
|
||||
doc["device"]["model"] = "BatteryEmulator";
|
||||
doc["device"]["name"] = "BatteryEmulator_" + String(hostname);
|
||||
doc["origin"]["name"] = "BatteryEmulator";
|
||||
doc["origin"]["sw"] = String(version_number) + "-mqtt";
|
||||
doc["origin"]["url"] = "https://github.com/dalathegreat/Battery-Emulator";
|
||||
|
||||
serializeJson(doc, mqtt_msg, sizeof(mqtt_msg));
|
||||
String cell_topic = topic + String(i + 1) + "/config";
|
||||
mqtt_publish(cell_topic.c_str(), mqtt_msg, true);
|
||||
}
|
||||
doc.clear(); // clear after sending autoconfig
|
||||
} else {
|
||||
if (cellvoltages[0] == 0u) {
|
||||
return;
|
||||
}
|
||||
|
||||
size_t msg_length = snprintf(mqtt_msg, sizeof(mqtt_msg), "{\n\"cell_voltages\":[");
|
||||
JsonArray cell_voltages = doc["cell_voltages"].to<JsonArray>();
|
||||
for (size_t i = 0; i < nof_cellvoltages; ++i) {
|
||||
msg_length +=
|
||||
snprintf(mqtt_msg + msg_length, sizeof(mqtt_msg) - msg_length, "%s%d", (i == 0) ? "" : ", ", cellvoltages[i]);
|
||||
cell_voltages.add(cellvoltages[i] / 1000.0);
|
||||
}
|
||||
snprintf(mqtt_msg + msg_length, sizeof(mqtt_msg) - msg_length, "]\n}\n");
|
||||
|
||||
// Publish and print error if not OK
|
||||
if (mqtt_publish_retain("battery-emulator/spec_data") == false) {
|
||||
serializeJson(doc, mqtt_msg, sizeof(mqtt_msg));
|
||||
|
||||
if (!mqtt_publish("battery-emulator/spec_data", mqtt_msg, false)) {
|
||||
Serial.println("Cell voltage MQTT msg could not be sent");
|
||||
}
|
||||
doc.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -132,59 +116,48 @@ SensorConfig sensorConfigs[] = {
|
|||
};
|
||||
|
||||
static void publish_common_info(void) {
|
||||
static JsonDocument doc;
|
||||
static bool mqtt_first_transmission = true;
|
||||
static char* state_topic = "battery-emulator/info";
|
||||
static const char* hostname = WiFi.getHostname();
|
||||
if (mqtt_first_transmission == true) {
|
||||
mqtt_first_transmission = false;
|
||||
for (int i = 0; i < sizeof(sensorConfigs) / sizeof(sensorConfigs[0]); i++) {
|
||||
SensorConfig& config = sensorConfigs[i];
|
||||
snprintf(mqtt_msg, sizeof(mqtt_msg),
|
||||
"{"
|
||||
"\"name\": \"%s\","
|
||||
"\"state_topic\": \"%s\","
|
||||
"\"unique_id\": \"battery-emulator_%s\","
|
||||
"\"object_id\": \"sensor_battery_%s\","
|
||||
"\"device\": {"
|
||||
"\"identifiers\": ["
|
||||
"\"battery-emulator\""
|
||||
"],"
|
||||
"\"manufacturer\": \"DalaTech\","
|
||||
"\"model\": \"BatteryEmulator\","
|
||||
"\"name\": \"BatteryEmulator\""
|
||||
"},"
|
||||
"\"origin\": {"
|
||||
"\"name\": \"BatteryEmulator\","
|
||||
"\"sw\": \"%s-mqtt\","
|
||||
"\"url\": \"https://github.com/dalathegreat/Battery-Emulator\""
|
||||
"},"
|
||||
"\"value_template\": \"%s\","
|
||||
"\"unit_of_measurement\": \"%s\","
|
||||
"\"device_class\": \"%s\","
|
||||
"\"enabled_by_default\": true,"
|
||||
"\"state_class\": \"measurement\""
|
||||
"}",
|
||||
config.name, state_topic, config.object_id, config.object_id, version_number, config.value_template,
|
||||
config.unit, config.device_class);
|
||||
mqtt_publish_retain(config.topic);
|
||||
doc["name"] = config.name;
|
||||
doc["state_topic"] = state_topic;
|
||||
doc["unique_id"] = "battery-emulator_" + String(hostname) + "_" + String(config.object_id);
|
||||
doc["object_id"] = String(hostname) + "_" + String(config.object_id);
|
||||
doc["value_template"] = config.value_template;
|
||||
doc["unit_of_measurement"] = config.unit;
|
||||
doc["device_class"] = config.device_class;
|
||||
doc["enabled_by_default"] = true;
|
||||
doc["state_class"] = "measurement";
|
||||
doc["expire_after"] = 240;
|
||||
doc["device"]["identifiers"][0] = "battery-emulator";
|
||||
doc["device"]["manufacturer"] = "DalaTech";
|
||||
doc["device"]["model"] = "BatteryEmulator";
|
||||
doc["device"]["name"] = "BatteryEmulator_" + String(hostname);
|
||||
doc["origin"]["name"] = "BatteryEmulator";
|
||||
doc["origin"]["sw"] = String(version_number) + "-mqtt";
|
||||
doc["origin"]["url"] = "https://github.com/dalathegreat/Battery-Emulator";
|
||||
serializeJson(doc, mqtt_msg);
|
||||
mqtt_publish(config.topic, mqtt_msg, true);
|
||||
}
|
||||
doc.clear();
|
||||
} else {
|
||||
snprintf(mqtt_msg, sizeof(mqtt_msg),
|
||||
"{\n"
|
||||
" \"SOC\": %.3f,\n"
|
||||
" \"state_of_health\": %.3f,\n"
|
||||
" \"temperature_min\": %.3f,\n"
|
||||
" \"temperature_max\": %.3f,\n"
|
||||
" \"stat_batt_power\": %.3f,\n"
|
||||
" \"battery_current\": %.3f,\n"
|
||||
" \"cell_max_voltage\": %d,\n"
|
||||
" \"cell_min_voltage\": %d,\n"
|
||||
" \"battery_voltage\": %d\n"
|
||||
"}\n",
|
||||
((float)SOC) / 100.0, ((float)StateOfHealth) / 100.0, ((float)((int16_t)temperature_min)) / 10.0,
|
||||
((float)((int16_t)temperature_max)) / 10.0, ((float)((int16_t)stat_batt_power)),
|
||||
((float)((int16_t)battery_current)) / 10.0, cell_max_voltage / 1000, cell_min_voltage / 1000,
|
||||
battery_voltage / 10.0);
|
||||
bool result = client.publish(state_topic, mqtt_msg, true);
|
||||
doc["SOC"] = ((float)SOC) / 100.0;
|
||||
doc["state_of_health"] = ((float)StateOfHealth) / 100.0;
|
||||
doc["temperature_min"] = ((float)((int16_t)temperature_min)) / 10.0;
|
||||
doc["temperature_max"] = ((float)((int16_t)temperature_max)) / 10.0;
|
||||
doc["stat_batt_power"] = ((float)((int16_t)stat_batt_power));
|
||||
doc["battery_current"] = ((float)((int16_t)battery_current)) / 10.0;
|
||||
doc["cell_max_voltage"] = cell_max_voltage / 1000.0;
|
||||
doc["cell_min_voltage"] = cell_min_voltage / 1000.0;
|
||||
doc["battery_voltage"] = battery_voltage / 10.0;
|
||||
|
||||
serializeJson(doc, mqtt_msg);
|
||||
bool result = mqtt_publish(state_topic, mqtt_msg, false);
|
||||
}
|
||||
|
||||
//Serial.println(mqtt_msg); // Uncomment to print the payload on serial
|
||||
|
@ -206,9 +179,10 @@ static void reconnect() {
|
|||
// attempt one reconnection
|
||||
Serial.print("Attempting MQTT connection... ");
|
||||
const char* hostname = WiFi.getHostname();
|
||||
String clientId = "LilyGoClient-" + String(hostname);
|
||||
char clientId[64]; // Adjust the size as needed
|
||||
snprintf(clientId, sizeof(clientId), "LilyGoClient-%s", hostname);
|
||||
// Attempt to connect
|
||||
if (client.connect(clientId.c_str(), mqtt_user, mqtt_password)) {
|
||||
if (client.connect(clientId, mqtt_user, mqtt_password)) {
|
||||
Serial.println("connected");
|
||||
|
||||
for (int i = 0; i < mqtt_nof_subscriptions; i++) {
|
||||
|
@ -238,20 +212,20 @@ void mqtt_loop(void) {
|
|||
client.loop();
|
||||
if (publish_global_timer.elapsed() == true) // Every 5s
|
||||
{
|
||||
publish_values(); // Update values heading towards inverter. Prepare for sending on CAN, or write directly to Modbus.
|
||||
publish_values();
|
||||
}
|
||||
} else {
|
||||
if (millis() - previousMillisUpdateVal >= 5000) // Every 5s
|
||||
{
|
||||
previousMillisUpdateVal = millis();
|
||||
reconnect(); // Update values heading towards inverter. Prepare for sending on CAN, or write directly to Modbus.
|
||||
reconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool mqtt_publish_retain(const char* topic) {
|
||||
bool mqtt_publish(const char* topic, const char* mqtt_msg, bool retain) {
|
||||
if (client.connected() == true) {
|
||||
return client.publish(topic, mqtt_msg, true);
|
||||
return client.publish(topic, mqtt_msg, retain);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue