Feature: Dump hex data from meter to MQTT (#1071)

* Send raw data debug to MQTT

* Publish hexdump to /data

* Sensor for /data, but it doesnt work
This commit is contained in:
Gunnar Skjold
2025-12-01 14:02:07 +01:00
committed by GitHub
parent 9252a810df
commit 8438020dbd
15 changed files with 96 additions and 33 deletions

View File

@@ -57,7 +57,7 @@ public:
virtual bool publishTemperatures(AmsConfiguration*, HwTools*) { return false; }; virtual bool publishTemperatures(AmsConfiguration*, HwTools*) { return false; };
virtual bool publishPrices(PriceService* ps) { return false; }; virtual bool publishPrices(PriceService* ps) { return false; };
virtual bool publishSystem(HwTools*, PriceService*, EnergyAccounting*) { return false; }; virtual bool publishSystem(HwTools*, PriceService*, EnergyAccounting*) { return false; };
virtual bool publishRaw(String data) { return false; }; virtual bool publishRaw(uint8_t* raw, size_t length) { return false; };
virtual bool publishFirmware() { return false; }; virtual bool publishFirmware() { return false; };
virtual void onMessage(String &topic, String &payload) {}; virtual void onMessage(String &topic, String &payload) {};

View File

@@ -25,7 +25,7 @@ public:
bool publishTemperatures(AmsConfiguration*, HwTools*); bool publishTemperatures(AmsConfiguration*, HwTools*);
bool publishPrices(PriceService*); bool publishPrices(PriceService*);
bool publishSystem(HwTools* hw, PriceService* ps, EnergyAccounting* ea); bool publishSystem(HwTools* hw, PriceService* ps, EnergyAccounting* ea);
bool publishRaw(String data); bool publishRaw(uint8_t* raw, size_t length);
void onMessage(String &topic, String &payload); void onMessage(String &topic, String &payload);

View File

@@ -103,7 +103,7 @@ uint8_t DomoticzMqttHandler::getFormat() {
return 3; return 3;
} }
bool DomoticzMqttHandler::publishRaw(String data) { bool DomoticzMqttHandler::publishRaw(uint8_t* raw, size_t length) {
return false; return false;
} }

View File

@@ -27,7 +27,7 @@ public:
bool publishTemperatures(AmsConfiguration*, HwTools*); bool publishTemperatures(AmsConfiguration*, HwTools*);
bool publishPrices(PriceService*); bool publishPrices(PriceService*);
bool publishSystem(HwTools* hw, PriceService* ps, EnergyAccounting* ea); bool publishSystem(HwTools* hw, PriceService* ps, EnergyAccounting* ea);
bool publishRaw(String data); bool publishRaw(uint8_t* raw, size_t length);
bool publishFirmware(); bool publishFirmware();
bool postConnect(); bool postConnect();
@@ -51,7 +51,7 @@ private:
String updateTopic; String updateTopic;
String sensorNamePrefix; String sensorNamePrefix;
bool l1Init, l2Init, l2eInit, l3Init, l3eInit, l4Init, l4eInit, rtInit, rteInit, pInit, sInit, rInit, fInit; bool l1Init, l2Init, l2eInit, l3Init, l3eInit, l4Init, l4eInit, rtInit, rteInit, pInit, sInit, rInit, fInit, dInit;
bool tInit[32] = {false}; bool tInit[32] = {false};
uint8_t priceImportInit = 0, priceExportInit = 0; uint8_t priceImportInit = 0, priceExportInit = 0;
uint32_t lastThresholdPublish = 0; uint32_t lastThresholdPublish = 0;

View File

@@ -124,5 +124,6 @@ const HomeAssistantSensor SystemSensors[SystemSensorCount] PROGMEM = {
const HomeAssistantSensor TemperatureSensor PROGMEM = {"Temperature sensor %s", "/temperatures", "temperatures['%s']", 900, "°C", "temperature", "measurement", ""}; const HomeAssistantSensor TemperatureSensor PROGMEM = {"Temperature sensor %s", "/temperatures", "temperatures['%s']", 900, "°C", "temperature", "measurement", ""};
const HomeAssistantSensor DataSensor PROGMEM = {"Data", "/data", "data", 900, "", "", "", ""};
#endif #endif

View File

@@ -3,7 +3,6 @@
"stat_t" : "%s%s", "stat_t" : "%s%s",
"uniq_id" : "%s_%s", "uniq_id" : "%s_%s",
"obj_id" : "%s_%s", "obj_id" : "%s_%s",
"unit_of_meas" : "%s",
"val_tpl" : "{{ value_json.%s | is_defined }}", "val_tpl" : "{{ value_json.%s | is_defined }}",
"expire_after" : %d, "expire_after" : %d,
"dev" : { "dev" : {
@@ -13,5 +12,8 @@
"sw" : "%s", "sw" : "%s",
"mf" : "%s", "mf" : "%s",
"cu" : "%s" "cu" : "%s"
}%s%s%s%s%s%s }
%s%s%s
%s%s%s
%s%s%s
} }

View File

@@ -20,7 +20,7 @@
#endif #endif
void HomeAssistantMqttHandler::setHomeAssistantConfig(HomeAssistantConfig config, char* hostname) { void HomeAssistantMqttHandler::setHomeAssistantConfig(HomeAssistantConfig config, char* hostname) {
l1Init = l2Init = l2eInit = l3Init = l3eInit = l4Init = l4eInit = rtInit = rteInit = pInit = sInit = rInit = fInit = false; l1Init = l2Init = l2eInit = l3Init = l3eInit = l4Init = l4eInit = rtInit = rteInit = pInit = sInit = rInit = fInit = dInit = false;
if(strlen(config.discoveryNameTag) > 0) { if(strlen(config.discoveryNameTag) > 0) {
snprintf_P(json, 128, PSTR("AMS reader (%s)"), config.discoveryNameTag); snprintf_P(json, 128, PSTR("AMS reader (%s)"), config.discoveryNameTag);
@@ -541,7 +541,6 @@ void HomeAssistantMqttHandler::publishSensor(const HomeAssistantSensor sensor) {
mqttConfig.publishTopic, sensor.topic, mqttConfig.publishTopic, sensor.topic,
deviceUid.c_str(), uid.c_str(), deviceUid.c_str(), uid.c_str(),
deviceUid.c_str(), uid.c_str(), deviceUid.c_str(), uid.c_str(),
sensor.uom,
sensor.path, sensor.path,
sensor.ttl, sensor.ttl,
deviceUid.c_str(), deviceUid.c_str(),
@@ -550,13 +549,20 @@ void HomeAssistantMqttHandler::publishSensor(const HomeAssistantSensor sensor) {
FirmwareVersion::VersionString, FirmwareVersion::VersionString,
manufacturer.c_str(), manufacturer.c_str(),
deviceUrl.c_str(), deviceUrl.c_str(),
strlen_P(sensor.devcl) > 0 ? ",\"dev_cla\":\"" : "", strlen_P(sensor.devcl) > 0 ? ",\"dev_cla\":\"" : "",
strlen_P(sensor.devcl) > 0 ? (char *) FPSTR(sensor.devcl) : "", strlen_P(sensor.devcl) > 0 ? (char *) FPSTR(sensor.devcl) : "",
strlen_P(sensor.devcl) > 0 ? "\"" : "", strlen_P(sensor.devcl) > 0 ? "\"" : "",
strlen_P(sensor.stacl) > 0 ? ",\"stat_cla\":\"" : "", strlen_P(sensor.stacl) > 0 ? ",\"stat_cla\":\"" : "",
strlen_P(sensor.stacl) > 0 ? (char *) FPSTR(sensor.stacl) : "", strlen_P(sensor.stacl) > 0 ? (char *) FPSTR(sensor.stacl) : "",
strlen_P(sensor.stacl) > 0 ? "\"" : "" strlen_P(sensor.stacl) > 0 ? "\"" : "",
strlen_P(sensor.uom) > 0 ? ",\"unit_of_meas\":\"" : "",
strlen_P(sensor.uom) > 0 ? (char *) FPSTR(sensor.uom) : "",
strlen_P(sensor.uom) > 0 ? "\"" : ""
); );
mqtt.publish(sensorTopic + "/" + deviceUid + "_" + uid + "/config", json, true, 0); mqtt.publish(sensorTopic + "/" + deviceUid + "_" + uid + "/config", json, true, 0);
loop(); loop();
} }
@@ -831,8 +837,26 @@ uint8_t HomeAssistantMqttHandler::getFormat() {
return 4; return 4;
} }
bool HomeAssistantMqttHandler::publishRaw(String data) { bool HomeAssistantMqttHandler::publishRaw(uint8_t* raw, size_t length) {
return false; if(strlen(mqttConfig.publishTopic) == 0 || !mqtt.connected())
return false;
if(length <= 0 || length > BufferSize) return false;
if(!dInit) {
// Not sure how this sensor should be defined in HA, so skipping for now
//publishSensor(DataSensor);
dInit = true;
}
String str = toHex(raw, length);
snprintf_P(json, BufferSize, PSTR("{\"data\":\"%s\"}"), str.c_str());
char topic[192];
snprintf_P(topic, 192, PSTR("%s/data"), mqttConfig.publishTopic);
bool ret = mqtt.publish(topic, json);
loop();
return ret;
} }
bool HomeAssistantMqttHandler::publishFirmware() { bool HomeAssistantMqttHandler::publishFirmware() {
@@ -865,7 +889,7 @@ void HomeAssistantMqttHandler::onMessage(String &topic, String &payload) {
if (debugger->isActive(RemoteDebug::INFO)) if (debugger->isActive(RemoteDebug::INFO))
#endif #endif
debugger->printf_P(PSTR("Received online status from HA, resetting sensor status\n")); debugger->printf_P(PSTR("Received online status from HA, resetting sensor status\n"));
l1Init = l2Init = l2eInit = l3Init = l3eInit = l4Init = l4eInit = rtInit = rteInit = pInit = sInit = rInit = false; l1Init = l2Init = l2eInit = l3Init = l3eInit = l4Init = l4eInit = rtInit = rteInit = pInit = sInit = rInit = fInit = dInit = false;
for(uint8_t i = 0; i < 32; i++) tInit[i] = false; for(uint8_t i = 0; i < 32; i++) tInit[i] = false;
priceImportInit = 0; priceImportInit = 0;
priceExportInit = 0; priceExportInit = 0;

View File

@@ -23,7 +23,7 @@ public:
bool publishTemperatures(AmsConfiguration*, HwTools*); bool publishTemperatures(AmsConfiguration*, HwTools*);
bool publishPrices(PriceService*); bool publishPrices(PriceService*);
bool publishSystem(HwTools* hw, PriceService* ps, EnergyAccounting* ea); bool publishSystem(HwTools* hw, PriceService* ps, EnergyAccounting* ea);
bool publishRaw(String data); bool publishRaw(uint8_t* raw, size_t length);
bool publishFirmware(); bool publishFirmware();
void onMessage(String &topic, String &payload); void onMessage(String &topic, String &payload);

View File

@@ -472,8 +472,20 @@ uint8_t JsonMqttHandler::getFormat() {
return 0; return 0;
} }
bool JsonMqttHandler::publishRaw(String data) { bool JsonMqttHandler::publishRaw(uint8_t* raw, size_t length) {
return false; if(strlen(mqttConfig.publishTopic) == 0 || !mqtt.connected())
return false;
if(length <= 0 || length > BufferSize) return false;
String str = toHex(raw, length);
snprintf_P(json, BufferSize, PSTR("{\"data\":\"%s\"}"), str.c_str());
char topic[192];
snprintf_P(topic, 192, PSTR("%s/data"), mqttConfig.publishTopic);
bool ret = mqtt.publish(topic, json);
loop();
return ret;
} }
bool JsonMqttHandler::publishFirmware() { bool JsonMqttHandler::publishFirmware() {

View File

@@ -13,6 +13,7 @@
#endif #endif
#include "AmsData.h" #include "AmsData.h"
#include "AmsConfiguration.h" #include "AmsConfiguration.h"
#include "AmsMqttHandler.h"
class MeterCommunicator { class MeterCommunicator {
public: public:
@@ -24,6 +25,13 @@ public:
virtual bool isConfigChanged(); virtual bool isConfigChanged();
virtual void ackConfigChanged(); virtual void ackConfigChanged();
virtual void getCurrentConfig(MeterConfig& meterConfig); virtual void getCurrentConfig(MeterConfig& meterConfig);
virtual void setMqttHandlerForDebugging(AmsMqttHandler* mqttHandler) {
this->mqttDebug = mqttHandler;
};
protected:
AmsMqttHandler* mqttDebug = NULL;
}; };
#endif #endif

View File

@@ -14,7 +14,7 @@
#include "AmsConfiguration.h" #include "AmsConfiguration.h"
#include "DataParsers.h" #include "DataParsers.h"
#include "Timezone.h" #include "Timezone.h"
#include "PassthroughMqttHandler.h" #include "AmsMqttHandler.h"
#if defined(ESP8266) #if defined(ESP8266)
#include "SoftwareSerial.h" #include "SoftwareSerial.h"
@@ -36,7 +36,6 @@ public:
bool isConfigChanged(); bool isConfigChanged();
void ackConfigChanged(); void ackConfigChanged();
void getCurrentConfig(MeterConfig& meterConfig); void getCurrentConfig(MeterConfig& meterConfig);
void setPassthroughMqttHandler(PassthroughMqttHandler*);
HardwareSerial* getHwSerial(); HardwareSerial* getHwSerial();
void rxerr(int err); void rxerr(int err);
@@ -51,8 +50,6 @@ protected:
bool configChanged = false; bool configChanged = false;
Timezone* tz; Timezone* tz;
PassthroughMqttHandler* pt = NULL;
uint8_t *hanBuffer = NULL; uint8_t *hanBuffer = NULL;
uint16_t hanBufferSize = 0; uint16_t hanBufferSize = 0;
Stream *hanSerial; Stream *hanSerial;

View File

@@ -174,8 +174,8 @@ bool PassiveMeterCommunicator::loop() {
lastError = pos; lastError = pos;
printHanReadError(pos); printHanReadError(pos);
len += hanSerial->readBytes(hanBuffer+len, hanBufferSize-len); len += hanSerial->readBytes(hanBuffer+len, hanBufferSize-len);
if(pt != NULL) { if(mqttDebug != NULL) {
pt->publishBytes(hanBuffer, len); mqttDebug->publishRaw(hanBuffer, len);
} }
#if defined(AMS_REMOTE_DEBUG) #if defined(AMS_REMOTE_DEBUG)
if (debugger->isActive(RemoteDebug::VERBOSE)) if (debugger->isActive(RemoteDebug::VERBOSE))
@@ -229,8 +229,8 @@ AmsData* PassiveMeterCommunicator::getData(AmsData& meterState) {
char* payload = ((char *) (hanBuffer)) + pos; char* payload = ((char *) (hanBuffer)) + pos;
if(maxDetectedPayloadSize < pos) maxDetectedPayloadSize = pos; if(maxDetectedPayloadSize < pos) maxDetectedPayloadSize = pos;
if(ctx.type == DATA_TAG_DLMS) { if(ctx.type == DATA_TAG_DLMS) {
if(pt != NULL) { if(mqttDebug != NULL) {
pt->publishBytes((uint8_t*) payload, ctx.length); mqttDebug->publishRaw((uint8_t*) payload, ctx.length);
} }
#if defined(AMS_REMOTE_DEBUG) #if defined(AMS_REMOTE_DEBUG)
@@ -405,8 +405,8 @@ int16_t PassiveMeterCommunicator::unwrapData(uint8_t *buf, DataParserContext &co
if (debugger->isActive(RemoteDebug::VERBOSE)) if (debugger->isActive(RemoteDebug::VERBOSE))
#endif #endif
debugger->printf_P(PSTR("HDLC frame:\n")); debugger->printf_P(PSTR("HDLC frame:\n"));
if(pt != NULL) { if(mqttDebug != NULL) {
pt->publishBytes(buf, curLen); mqttDebug->publishRaw(buf, curLen);
} }
break; break;
case DATA_TAG_MBUS: case DATA_TAG_MBUS:
@@ -414,8 +414,8 @@ int16_t PassiveMeterCommunicator::unwrapData(uint8_t *buf, DataParserContext &co
if (debugger->isActive(RemoteDebug::VERBOSE)) if (debugger->isActive(RemoteDebug::VERBOSE))
#endif #endif
debugger->printf_P(PSTR("MBUS frame:\n")); debugger->printf_P(PSTR("MBUS frame:\n"));
if(pt != NULL) { if(mqttDebug != NULL) {
pt->publishBytes(buf, curLen); mqttDebug->publishRaw(buf, curLen);
} }
break; break;
case DATA_TAG_GBT: case DATA_TAG_GBT:
@@ -447,8 +447,8 @@ int16_t PassiveMeterCommunicator::unwrapData(uint8_t *buf, DataParserContext &co
if (debugger->isActive(RemoteDebug::VERBOSE)) if (debugger->isActive(RemoteDebug::VERBOSE))
#endif #endif
debugger->printf_P(PSTR("DSMR frame:\n")); debugger->printf_P(PSTR("DSMR frame:\n"));
if(pt != NULL) { if(mqttDebug != NULL) {
pt->publishString((char*) buf); mqttDebug->publishRaw(buf, curLen);
} }
break; break;
case DATA_TAG_SNRM: case DATA_TAG_SNRM:

View File

@@ -26,7 +26,7 @@ public:
bool publishTemperatures(AmsConfiguration*, HwTools*); bool publishTemperatures(AmsConfiguration*, HwTools*);
bool publishPrices(PriceService*); bool publishPrices(PriceService*);
bool publishSystem(HwTools* hw, PriceService* ps, EnergyAccounting* ea); bool publishSystem(HwTools* hw, PriceService* ps, EnergyAccounting* ea);
bool publishRaw(String data); bool publishRaw(uint8_t* raw, size_t length);
void onMessage(String &topic, String &payload); void onMessage(String &topic, String &payload);

View File

@@ -396,8 +396,16 @@ uint8_t RawMqttHandler::getFormat() {
return full ? 3 : 2; return full ? 3 : 2;
} }
bool RawMqttHandler::publishRaw(String data) { bool RawMqttHandler::publishRaw(uint8_t* raw, size_t length) {
return false; if(topic.isEmpty() || !connected())
return false;
if(length <= 0 || length > BufferSize) return false;
String str = toHex(raw, length);
bool ret = mqtt.publish(topic + "/data", str);
loop();
return ret;
} }
void RawMqttHandler::onMessage(String &topic, String &payload) { void RawMqttHandler::onMessage(String &topic, String &payload) {

View File

@@ -1041,6 +1041,9 @@ void handleMeterConfig() {
debugE_P(PSTR("Unknown meter source selected: %d"), meterConfig.source); debugE_P(PSTR("Unknown meter source selected: %d"), meterConfig.source);
} }
ws.setMeterConfig(meterConfig.distributionSystem, meterConfig.mainFuse, meterConfig.productionCapacity); ws.setMeterConfig(meterConfig.distributionSystem, meterConfig.mainFuse, meterConfig.productionCapacity);
if(mc != NULL && Debug.isActive(RemoteDebug::DEBUG)) {
mc->setMqttHandlerForDebugging(mqttHandler);
}
config.ackMeterChanged(); config.ackMeterChanged();
} }
} }
@@ -1557,6 +1560,9 @@ void postConnect() {
if(!debug.telnet) { if(!debug.telnet) {
Debug.stop(); Debug.stop();
} }
if(mc != NULL && Debug.isActive(RemoteDebug::DEBUG)) {
mc->setMqttHandlerForDebugging(mqttHandler);
}
} else { } else {
Debug.stop(); Debug.stop();
} }
@@ -1632,6 +1638,7 @@ void MQTT_connect() {
if(mqttHandler->getFormat() != mqttConfig.payloadFormat) { if(mqttHandler->getFormat() != mqttConfig.payloadFormat) {
delete mqttHandler; delete mqttHandler;
mqttHandler = NULL; mqttHandler = NULL;
mc->setMqttHandlerForDebugging(NULL);
} else if(config.isMqttChanged()) { } else if(config.isMqttChanged()) {
mqttHandler->setConfig(mqttConfig); mqttHandler->setConfig(mqttConfig);
switch(mqttConfig.payloadFormat) { switch(mqttConfig.payloadFormat) {
@@ -1680,10 +1687,14 @@ void MQTT_connect() {
break; break;
case 255: case 255:
mqttHandler = new PassthroughMqttHandler(mqttConfig, &Debug, (char*) commonBuffer, &updater); mqttHandler = new PassthroughMqttHandler(mqttConfig, &Debug, (char*) commonBuffer, &updater);
mc->setMqttHandlerForDebugging(mqttHandler);
break; break;
} }
} }
ws.setMqttHandler(mqttHandler); ws.setMqttHandler(mqttHandler);
if(mc != NULL && Debug.isActive(RemoteDebug::DEBUG)) {
mc->setMqttHandlerForDebugging(mqttHandler);
}
if(mqttHandler != NULL) { if(mqttHandler != NULL) {
mqttHandler->connect(); mqttHandler->connect();