From a055465ce09d231f549b3948e811213428356378 Mon Sep 17 00:00:00 2001 From: Gunnar Skjold Date: Tue, 16 Aug 2022 08:22:43 +0200 Subject: [PATCH 1/5] Untestet L&G data parser --- frames/lng.raw | 34 +++++++++++++++++++++ src/AmsData.h | 1 + src/AmsToMqttBridge.ino | 4 ++- src/LNG.cpp | 65 ++++++++++++++++++++++++++++++++++++++++ src/LNG.h | 29 ++++++++++++++++++ src/web/AmsWebServer.cpp | 3 ++ 6 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 src/LNG.cpp create mode 100644 src/LNG.h diff --git a/frames/lng.raw b/frames/lng.raw index 9dc84a6e..00b2b497 100644 --- a/frames/lng.raw +++ b/frames/lng.raw @@ -43,3 +43,37 @@ FF // Last byte of OBIS in previous block 0600000000 // Accumulated export 8BA4 7E + + + + +7E A1 23 CE FF 03 13 21 55 E6 E7 00 + +0F 00 00 08 E2 +0C 07 E5 07 13 01 0C 1A 0A FF 80 00 00 + +02 0B // 11 + 01 0B // 11 + 02 04 12 00 28 09 06 00 08 19 09 00 FF 0F 02 12 00 00 + 02 04 12 00 28 09 06 00 08 19 09 00 FF 0F 01 12 00 00 + 02 04 12 00 01 09 06 00 00 60 01 00 FF 0F 02 12 00 00 + 02 04 12 00 03 09 06 01 00 01 07 00 FF 0F 02 12 00 00 + 02 04 12 00 03 09 06 01 00 02 07 00 FF 0F 02 12 00 00 + 02 04 12 00 03 09 06 01 01 01 08 00 FF 0F 02 12 00 00 + 02 04 12 00 03 09 06 01 01 02 08 00 FF 0F 02 12 00 00 + 02 04 12 00 03 09 06 01 01 05 08 00 FF 0F 02 12 00 00 + 02 04 12 00 03 09 06 01 01 06 08 00 FF 0F 02 12 00 00 + 02 04 12 00 03 09 06 01 01 07 08 00 FF 0F 02 12 00 00 + 02 04 12 00 03 09 06 01 01 08 08 00 FF 0F 02 12 00 00 + 09 06 00 08 19 09 00 FF + 09 08 34 33 30 39 34 33 35 31 + 06 00 00 00 0B + 06 00 00 00 00 + 06 00 00 00 10 + 06 00 00 00 04 + 06 00 00 00 00 + 06 00 00 00 08 + 06 00 00 00 00 + 06 00 00 00 01 +7C 8B +7E \ No newline at end of file diff --git a/src/AmsData.h b/src/AmsData.h index 3ffcd296..21d29827 100644 --- a/src/AmsData.h +++ b/src/AmsData.h @@ -12,6 +12,7 @@ enum AmsType { AmsTypeIskra = 0x08, AmsTypeLandis = 0x09, AmsTypeSagemcom = 0x0A, + AmsTypeLng = 0x0B, AmsTypeCustom = 0x88, AmsTypeUnknown = 0xFF }; diff --git a/src/AmsToMqttBridge.ino b/src/AmsToMqttBridge.ino index 19136294..6c722baa 100644 --- a/src/AmsToMqttBridge.ino +++ b/src/AmsToMqttBridge.ino @@ -65,6 +65,7 @@ ADC_MODE(ADC_VCC); #include "IEC6205621.h" #include "IEC6205675.h" +#include "LNG.h" #include "ams/DataParsers.h" @@ -851,7 +852,8 @@ bool readHanPort() { if(Debug.isActive(RemoteDebug::VERBOSE)) debugPrint(hanBuffer+pos, 0, ctx.length); // TODO: Split IEC6205675 into DataParserKaifa and DataParserObis. This way we can add other means of parsing, for those other proprietary formats - data = IEC6205675(((char *) (hanBuffer)) + pos, meterState.getMeterType(), &meterConfig, ctx); + //data = IEC6205675(((char *) (hanBuffer)) + pos, meterState.getMeterType(), &meterConfig, ctx); + data = LNG(((char *) (hanBuffer)) + pos, meterState.getMeterType(), &meterConfig, ctx); } else if(ctx.type == DATA_TAG_DSMR) { data = IEC6205621(((char *) (hanBuffer)) + pos); } diff --git a/src/LNG.cpp b/src/LNG.cpp new file mode 100644 index 00000000..d982f874 --- /dev/null +++ b/src/LNG.cpp @@ -0,0 +1,65 @@ +#include "LNG.h" +#include "lwip/def.h" +#include "ams/Cosem.h" + +LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx) { + LngHeader* h = (LngHeader*) payload; + if(h->tag == CosemTypeStructure && h->arrayTag == CosemTypeArray) { + meterType = AmsTypeLng; + this->packageTimestamp = ctx.timestamp; + + uint8_t* ptr = (uint8_t*) &h[1]; + uint8_t* data = ptr + (18*h->arrayLength); // Skip descriptors + + for(uint8_t i = 0; i < h->arrayLength; i++) { + LngObisDescriptor* descriptor = (LngObisDescriptor*) ptr; + if(descriptor->obis[2] == 1) { + if(descriptor->obis[3] == 7) { + if(descriptor->obis[4] == 0) { + CosemDLongUnsigned* item = (CosemDLongUnsigned*) data; + activeImportPower = ntohl(item->data); + listType = listType >= 1 ? listType : 1; + } + } else if(descriptor->obis[3] == 8) { + if(descriptor->obis[4] == 0) { + CosemDLongUnsigned* item = (CosemDLongUnsigned*) data; + activeImportCounter = ntohl(item->data); + listType = listType >= 3 ? listType : 3; + } + } + } else if(descriptor->obis[2] == 2) { + if(descriptor->obis[3] == 7) { + if(descriptor->obis[4] == 0) { + CosemDLongUnsigned* item = (CosemDLongUnsigned*) data; + activeExportPower = ntohl(item->data); + listType = listType >= 2 ? listType : 2; + } + } else if(descriptor->obis[3] == 8) { + if(descriptor->obis[4] == 0) { + CosemDLongUnsigned* item = (CosemDLongUnsigned*) data; + activeExportCounter = ntohl(item->data); + listType = listType >= 3 ? listType : 3; + } + } + } else if(descriptor->obis[2] == 96) { + if(descriptor->obis[3] == 1) { + if(descriptor->obis[4] == 0) { + CosemString* item = (CosemString*) data; + char str[item->length+1]; + memcpy(str, item->data, item->length); + str[item->length] = '\0'; + meterId = String(str); + } + } + } + + ptr = (uint8_t*) &descriptor[1]; + + if((*data) == 0x09) { + data += (*data+1)+2; + } else { + data += 5; + } + } + } +} \ No newline at end of file diff --git a/src/LNG.h b/src/LNG.h new file mode 100644 index 00000000..19b14061 --- /dev/null +++ b/src/LNG.h @@ -0,0 +1,29 @@ +#ifndef _LNG_H +#define _LNG_H + +#include "AmsData.h" +#include "AmsConfiguration.h" +#include "ams/DataParser.h" + +struct LngHeader { + uint8_t tag; + uint8_t values; + uint8_t arrayTag; + uint8_t arrayLength; +} __attribute__((packed)); + +struct LngObisDescriptor { + uint8_t ignore1[5]; + uint8_t octetTag; + uint8_t octetLength; + uint8_t obis[6]; + uint8_t ignore2[5]; +} __attribute__((packed)); + + +class LNG : public AmsData { +public: + LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx); +}; + +#endif diff --git a/src/web/AmsWebServer.cpp b/src/web/AmsWebServer.cpp index 13770818..bbbf7238 100644 --- a/src/web/AmsWebServer.cpp +++ b/src/web/AmsWebServer.cpp @@ -336,6 +336,9 @@ void AmsWebServer::configMeterHtml() { case AmsTypeSagemcom: manufacturer = "Sagemcom"; break; + case AmsTypeLng: + manufacturer = "L&G"; + break; default: manufacturer = "Unknown"; break; From 01547f9a521462b21a4db5998261f190e0c77b49 Mon Sep 17 00:00:00 2001 From: Gunnar Skjold Date: Fri, 19 Aug 2022 13:26:29 +0200 Subject: [PATCH 2/5] Adjustments to make L&G parser work --- src/AmsToMqttBridge.ino | 2 +- src/LNG.cpp | 47 ++++++++++++++++++++++++++--------------- src/LNG.h | 3 ++- 3 files changed, 33 insertions(+), 19 deletions(-) diff --git a/src/AmsToMqttBridge.ino b/src/AmsToMqttBridge.ino index 320a7238..a5726a84 100644 --- a/src/AmsToMqttBridge.ino +++ b/src/AmsToMqttBridge.ino @@ -859,7 +859,7 @@ bool readHanPort() { // TODO: Split IEC6205675 into DataParserKaifa and DataParserObis. This way we can add other means of parsing, for those other proprietary formats //data = IEC6205675(((char *) (hanBuffer)) + pos, meterState.getMeterType(), &meterConfig, ctx); - data = LNG(((char *) (hanBuffer)) + pos, meterState.getMeterType(), &meterConfig, ctx); + data = LNG(((char *) (hanBuffer)) + pos, meterState.getMeterType(), &meterConfig, ctx, &Debug); } else if(ctx.type == DATA_TAG_DSMR) { data = IEC6205621(((char *) (hanBuffer)) + pos); } diff --git a/src/LNG.cpp b/src/LNG.cpp index d982f874..dea4ecf5 100644 --- a/src/LNG.cpp +++ b/src/LNG.cpp @@ -2,7 +2,7 @@ #include "lwip/def.h" #include "ams/Cosem.h" -LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx) { +LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx, RemoteDebug* debugger) { LngHeader* h = (LngHeader*) payload; if(h->tag == CosemTypeStructure && h->arrayTag == CosemTypeArray) { meterType = AmsTypeLng; @@ -11,55 +11,68 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da uint8_t* ptr = (uint8_t*) &h[1]; uint8_t* data = ptr + (18*h->arrayLength); // Skip descriptors - for(uint8_t i = 0; i < h->arrayLength; i++) { - LngObisDescriptor* descriptor = (LngObisDescriptor*) ptr; + LngObisDescriptor* descriptor = (LngObisDescriptor*) ptr; + for(uint8_t x = 0; x < h->arrayLength-1; x++) { + ptr = (uint8_t*) &descriptor[1]; + descriptor = (LngObisDescriptor*) ptr; + if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("(L&G) OBIS %d.%d.%d with type 0x%02X", descriptor->obis[2], descriptor->obis[3], descriptor->obis[4], *data); + + CosemData* item = (CosemData*) data; if(descriptor->obis[2] == 1) { if(descriptor->obis[3] == 7) { if(descriptor->obis[4] == 0) { - CosemDLongUnsigned* item = (CosemDLongUnsigned*) data; - activeImportPower = ntohl(item->data); + activeImportPower = ntohl(item->dlu.data); listType = listType >= 1 ? listType : 1; + if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); } } else if(descriptor->obis[3] == 8) { if(descriptor->obis[4] == 0) { - CosemDLongUnsigned* item = (CosemDLongUnsigned*) data; - activeImportCounter = ntohl(item->data); + activeImportCounter = ntohl(item->dlu.data); listType = listType >= 3 ? listType : 3; + if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); } } } else if(descriptor->obis[2] == 2) { if(descriptor->obis[3] == 7) { if(descriptor->obis[4] == 0) { - CosemDLongUnsigned* item = (CosemDLongUnsigned*) data; - activeExportPower = ntohl(item->data); + activeExportPower = ntohl(item->dlu.data); listType = listType >= 2 ? listType : 2; + if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); } } else if(descriptor->obis[3] == 8) { if(descriptor->obis[4] == 0) { - CosemDLongUnsigned* item = (CosemDLongUnsigned*) data; - activeExportCounter = ntohl(item->data); + activeExportCounter = ntohl(item->dlu.data); listType = listType >= 3 ? listType : 3; + if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); } } } else if(descriptor->obis[2] == 96) { if(descriptor->obis[3] == 1) { if(descriptor->obis[4] == 0) { - CosemString* item = (CosemString*) data; - char str[item->length+1]; - memcpy(str, item->data, item->length); - str[item->length] = '\0'; + char str[item->oct.length+1]; + memcpy(str, item->oct.data, item->oct.length); + str[item->oct.length] = '\0'; meterId = String(str); + if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %s (oct)", str); + } else if(descriptor->obis[4] == 1) { + char str[item->oct.length+1]; + memcpy(str, item->oct.data, item->oct.length); + str[item->oct.length] = '\0'; + meterModel = String(str); + if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %s (oct)", str); } } } - ptr = (uint8_t*) &descriptor[1]; + if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("\n"); if((*data) == 0x09) { - data += (*data+1)+2; + data += (*(data+1))+2; } else { data += 5; } + + lastUpdateMillis = millis(); } } } \ No newline at end of file diff --git a/src/LNG.h b/src/LNG.h index 19b14061..f448b105 100644 --- a/src/LNG.h +++ b/src/LNG.h @@ -4,6 +4,7 @@ #include "AmsData.h" #include "AmsConfiguration.h" #include "ams/DataParser.h" +#include "RemoteDebug.h" struct LngHeader { uint8_t tag; @@ -23,7 +24,7 @@ struct LngObisDescriptor { class LNG : public AmsData { public: - LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx); + LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, DataParserContext &ctx, RemoteDebug* debugger); }; #endif From 44bcd386d175d85f808377466aea77ce78e2043f Mon Sep 17 00:00:00 2001 From: Gunnar Skjold Date: Tue, 23 Aug 2022 09:04:45 +0200 Subject: [PATCH 3/5] aggregate obis 1.8.x and 2.8.x for L&G --- src/LNG.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/LNG.cpp b/src/LNG.cpp index dea4ecf5..607e80e0 100644 --- a/src/LNG.cpp +++ b/src/LNG.cpp @@ -11,6 +11,8 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da uint8_t* ptr = (uint8_t*) &h[1]; uint8_t* data = ptr + (18*h->arrayLength); // Skip descriptors + uint16_t o181 = 0, o182 = 0; + uint16_t o281 = 0, o282 = 0; LngObisDescriptor* descriptor = (LngObisDescriptor*) ptr; for(uint8_t x = 0; x < h->arrayLength-1; x++) { ptr = (uint8_t*) &descriptor[1]; @@ -30,6 +32,12 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da activeImportCounter = ntohl(item->dlu.data); listType = listType >= 3 ? listType : 3; if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); + } else if(descriptor->obis[4] == 1) { + o181 = ntohl(item->dlu.data); + if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); + } else if(descriptor->obis[4] == 2) { + o182 = ntohl(item->dlu.data); + if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); } } } else if(descriptor->obis[2] == 2) { @@ -44,6 +52,12 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da activeExportCounter = ntohl(item->dlu.data); listType = listType >= 3 ? listType : 3; if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); + } else if(descriptor->obis[4] == 1) { + o281 = ntohl(item->dlu.data); + if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); + } else if(descriptor->obis[4] == 2) { + o282 = ntohl(item->dlu.data); + if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); } } } else if(descriptor->obis[2] == 96) { @@ -66,6 +80,15 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("\n"); + if(o181 > 0 || o182 > 0) { + activeImportCounter = o181 + o182; + listType = listType >= 3 ? listType : 3; + } + if(o281 > 0 || o282 > 0) { + activeExportCounter = o281 + o282; + listType = listType >= 3 ? listType : 3; + } + if((*data) == 0x09) { data += (*(data+1))+2; } else { From 9cc75299345922cb6a7ba800edc6340ff217a973 Mon Sep 17 00:00:00 2001 From: Gunnar Skjold Date: Wed, 31 Aug 2022 08:19:09 +0200 Subject: [PATCH 4/5] Fixed L&G scaling --- src/LNG.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/LNG.cpp b/src/LNG.cpp index 607e80e0..881953a5 100644 --- a/src/LNG.cpp +++ b/src/LNG.cpp @@ -29,7 +29,7 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da } } else if(descriptor->obis[3] == 8) { if(descriptor->obis[4] == 0) { - activeImportCounter = ntohl(item->dlu.data); + activeImportCounter = ntohl(item->dlu.data) / 1000.0; listType = listType >= 3 ? listType : 3; if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); } else if(descriptor->obis[4] == 1) { @@ -49,7 +49,7 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da } } else if(descriptor->obis[3] == 8) { if(descriptor->obis[4] == 0) { - activeExportCounter = ntohl(item->dlu.data); + activeExportCounter = ntohl(item->dlu.data) / 1000.0; listType = listType >= 3 ? listType : 3; if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); } else if(descriptor->obis[4] == 1) { @@ -81,11 +81,11 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("\n"); if(o181 > 0 || o182 > 0) { - activeImportCounter = o181 + o182; + activeImportCounter = (o181 + o182) / 1000.0; listType = listType >= 3 ? listType : 3; } if(o281 > 0 || o282 > 0) { - activeExportCounter = o281 + o282; + activeExportCounter = (o281 + o282) / 1000.0; listType = listType >= 3 ? listType : 3; } From 992e1b6121c1997aa44ee2244b4dad66e1792861 Mon Sep 17 00:00:00 2001 From: Gunnar Skjold Date: Mon, 12 Sep 2022 08:03:32 +0200 Subject: [PATCH 5/5] Net value for active power (L&G) --- src/LNG.cpp | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/src/LNG.cpp b/src/LNG.cpp index 881953a5..b5c9817b 100644 --- a/src/LNG.cpp +++ b/src/LNG.cpp @@ -11,6 +11,7 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da uint8_t* ptr = (uint8_t*) &h[1]; uint8_t* data = ptr + (18*h->arrayLength); // Skip descriptors + uint16_t o170 = 0, o270 = 0; uint16_t o181 = 0, o182 = 0; uint16_t o281 = 0, o282 = 0; LngObisDescriptor* descriptor = (LngObisDescriptor*) ptr; @@ -23,8 +24,7 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da if(descriptor->obis[2] == 1) { if(descriptor->obis[3] == 7) { if(descriptor->obis[4] == 0) { - activeImportPower = ntohl(item->dlu.data); - listType = listType >= 1 ? listType : 1; + o170 = ntohl(item->dlu.data); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); } } else if(descriptor->obis[3] == 8) { @@ -43,8 +43,7 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da } else if(descriptor->obis[2] == 2) { if(descriptor->obis[3] == 7) { if(descriptor->obis[4] == 0) { - activeExportPower = ntohl(item->dlu.data); - listType = listType >= 2 ? listType : 2; + o270 = ntohl(item->dlu.data); if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf(" and value %d (dlu)", ntohl(item->dlu.data)); } } else if(descriptor->obis[3] == 8) { @@ -80,6 +79,17 @@ LNG::LNG(const char* payload, uint8_t useMeterType, MeterConfig* meterConfig, Da if(debugger->isActive(RemoteDebug::VERBOSE)) debugger->printf("\n"); + if(o170 > 0 || o270 > 0) { + int32_t sum = o170-o270; + if(sum > 0) { + listType = listType >= 1 ? listType : 1; + activeImportPower = sum; + } else { + listType = listType >= 2 ? listType : 2; + activeExportPower = sum * -1; + } + } + if(o181 > 0 || o182 > 0) { activeImportCounter = (o181 + o182) / 1000.0; listType = listType >= 3 ? listType : 3;