From 65ad8f624cd086215e3b0f2ddf973804ef16ddf6 Mon Sep 17 00:00:00 2001 From: Emil Madsen Date: Thu, 24 Sep 2020 19:49:45 +0200 Subject: [PATCH] Add support for the Gavazzi EM24 --- Makefile | 1 + README.md | 1 + src/meter_em24.cc | 178 ++++++++++++++++++++++++++++++++++++++++++++++ src/meters.h | 4 ++ src/units.h | 24 ++++--- 5 files changed, 197 insertions(+), 11 deletions(-) create mode 100644 src/meter_em24.cc diff --git a/Makefile b/Makefile index 66dfda4..4001cc0 100644 --- a/Makefile +++ b/Makefile @@ -119,6 +119,7 @@ METER_OBJS:=\ $(BUILD)/meter_ebzwmbe.o \ $(BUILD)/meter_ehzp.o \ $(BUILD)/meter_esyswm.o \ + $(BUILD)/meter_em24.o \ $(BUILD)/meter_eurisii.o \ $(BUILD)/meter_fhkvdataiii.o \ $(BUILD)/meter_hydrus.o \ diff --git a/README.md b/README.md index 708f7f3..6493705 100644 --- a/README.md +++ b/README.md @@ -260,6 +260,7 @@ Easy Meter ESYS-WM20 (esyswm) eBZ wMB-E01 (ebzwmbe) EMH Metering (ehzp) Tauron Amiplus (amiplus) (includes vendor apator and echelon) +Gavazzi EM24 (em24) ``` The wmbus dongles imst871a can listen to one type of wmbus telegrams diff --git a/src/meter_em24.cc b/src/meter_em24.cc new file mode 100644 index 0000000..a2def49 --- /dev/null +++ b/src/meter_em24.cc @@ -0,0 +1,178 @@ +/* + Copyright (C) 2017-2019 Fredrik Öhrström + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +#include"dvparser.h" +#include"meters.h" +#include"meters_common_implementation.h" +#include"wmbus.h" +#include"wmbus_utils.h" +#include"util.h" + +using namespace std; + + +#define ERROR_CODE_VOLTAGE_PHASE_1_OVERFLOW 0x01 +#define ERROR_CODE_VOLTAGE_PHASE_2_OVERFLOW 0x02 +#define ERROR_CODE_VOLTAGE_PHASE_3_OVERFLOW 0x04 + +#define ERROR_CODE_CURRENT_PHASE_1_OVERFLOW 0x08 +#define ERROR_CODE_CURRENT_PHASE_2_OVERFLOW 0x10 +#define ERROR_CODE_CURRENT_PHASE_3_OVERFLOW 0x20 + +#define ERROR_CODE_FREQUENCY_OUT_OF_RANGE 0x40 + + +struct MeterEM24 : public virtual ElectricityMeter, public virtual MeterCommonImplementation { + MeterEM24(WMBus *bus, MeterInfo &mi); + + double totalEnergyConsumption(Unit u); + double totalEnergyProduction(Unit u); + + double totalReactiveEnergyConsumption(Unit u); + double totalReactiveEnergyProduction(Unit u); + + string status(); + +private: + void processContent(Telegram *t); + + double total_true_energy_consumption_kwh_ {}; + double total_true_energy_production_kwh_ {}; + + double total_reactive_energy_consumption_kvarh_ {}; + double total_reactive_energy_production_kvarh_ {}; + + uint8_t error_codes_ {}; +}; + +unique_ptr createEM24(WMBus *bus, MeterInfo &mi) +{ + return unique_ptr(new MeterEM24(bus, mi)); +} + +MeterEM24::MeterEM24(WMBus *bus, MeterInfo &mi) : + MeterCommonImplementation(bus, mi, MeterType::EM24) +{ + setExpectedELLSecurityMode(ELLSecurityMode::AES_CTR); + + addLinkMode(LinkMode::C1); + + addPrint("total_energy_consumption", Quantity::Energy, + [&](Unit u){ return totalEnergyConsumption(u); }, + "The total energy consumption recorded by this meter.", + true, true); + + addPrint("total_energy_production", Quantity::Energy, + [&](Unit u){ return totalEnergyProduction(u); }, + "The total energy production recorded by this meter.", + true, true); + + addPrint("total_reactive_energy_consumption", Quantity::Reactive_Energy, + [&](Unit u){ return totalReactiveEnergyConsumption(u); }, + "The total reactive energy consumption recorded by this meter.", + true, true); + + addPrint("total_reactive_energy_production", Quantity::Reactive_Energy, + [&](Unit u){ return totalReactiveEnergyProduction(u); }, + "The total reactive energy production recorded by this meter.", + true, true); + + addPrint("errors", Quantity::Text, + [&](){ return status(); }, + "Any errors currently being reported.", + false, true); +} + +double MeterEM24::totalEnergyConsumption(Unit u) +{ + assertQuantity(u, Quantity::Energy); + return convert(total_true_energy_consumption_kwh_, Unit::KWH, u); +} + +double MeterEM24::totalEnergyProduction(Unit u) +{ + assertQuantity(u, Quantity::Energy); + return convert(total_true_energy_production_kwh_, Unit::KWH, u); +} + +double MeterEM24::totalReactiveEnergyConsumption(Unit u) +{ + assertQuantity(u, Quantity::Reactive_Energy); + return convert(total_reactive_energy_consumption_kvarh_, Unit::KVARH, u); +} + +double MeterEM24::totalReactiveEnergyProduction(Unit u) +{ + assertQuantity(u, Quantity::Reactive_Energy); + return convert(total_reactive_energy_production_kvarh_, Unit::KVARH, u); +} + + +void MeterEM24::processContent(Telegram *t) +{ + int offset; + + // 04 dif (32 Bit Integer/Binary Instantaneous value) + // 05 vif (Energy 10² Wh) + extractDVdouble(&t->values, "0405", &offset, &total_true_energy_consumption_kwh_); + t->addMoreExplanation(offset, " total power (%f kwh)", total_true_energy_consumption_kwh_); + + // 04 dif (32 Bit Integer/Binary Instantaneous value) + // FB vif (First extension of VIF-codes) + // 82 vife (Reserved) + // 75 vife (Cold / Warm Temperature Limit 10^-2 Celsius) + extractDVdouble(&t->values, "04FB8275", &offset, &total_reactive_energy_consumption_kvarh_); + t->addMoreExplanation(offset, " total reactive power (%f kvarh)", total_reactive_energy_consumption_kvarh_); + + // 04 dif (32 Bit Integer/Binary Instantaneous value) + // 85 vif (Energy 10² Wh) + // 3C vife (backward flow) + extractDVdouble(&t->values, "04853C", &offset, &total_true_energy_production_kwh_); + t->addMoreExplanation(offset, " total power (%f kwh)", total_true_energy_production_kwh_); + + // 04 dif (32 Bit Integer/Binary Instantaneous value) + // FB vif (First extension of VIF-codes) + // 82 vife (Reserved) + // F5 vife (Cold / Warm Temperature Limit 10^-2 Celsius) + // 3C vife (Reserved) + extractDVdouble(&t->values, "04FB82F53C", &offset, &total_reactive_energy_production_kvarh_); + t->addMoreExplanation(offset, " total reactive power (%f kvarh)", total_reactive_energy_production_kvarh_); + + // 01 dif (8 Bit Integer/Binary Instantaneous value) + // FD vif (Second extension of VIF-codes) + // 17 vife (Error flags (binary)) + extractDVuint8(&t->values, "01FD17", &offset, &error_codes_); + t->addMoreExplanation(offset, " error codes (%s)", status().c_str()); +} + +string MeterEM24::status() +{ + string s; + if (error_codes_ & ERROR_CODE_VOLTAGE_PHASE_1_OVERFLOW) s.append("V 1 OVERFLOW "); + if (error_codes_ & ERROR_CODE_VOLTAGE_PHASE_2_OVERFLOW) s.append("V 2 OVERFLOW "); + if (error_codes_ & ERROR_CODE_VOLTAGE_PHASE_3_OVERFLOW) s.append("V 3 OVERFLOW "); + if (error_codes_ & ERROR_CODE_CURRENT_PHASE_1_OVERFLOW) s.append("I 1 OVERFLOW "); + if (error_codes_ & ERROR_CODE_CURRENT_PHASE_2_OVERFLOW) s.append("I 2 OVERFLOW "); + if (error_codes_ & ERROR_CODE_CURRENT_PHASE_3_OVERFLOW) s.append("I 3 OVERFLOW "); + if (error_codes_ & ERROR_CODE_FREQUENCY_OUT_OF_RANGE) s.append("FREQUENCY "); + if (s.length() > 0) { + s.pop_back(); // Remove final space + return s; + } + return s; +} + diff --git a/src/meters.h b/src/meters.h index adbce78..e538140 100644 --- a/src/meters.h +++ b/src/meters.h @@ -36,6 +36,7 @@ X(ehzp, T1_bit, Electricity, EHZP, EHZP) \ X(esyswm, T1_bit, Electricity, ESYSWM, ESYSWM) \ X(flowiq3100, C1_bit, Water, FLOWIQ3100, FlowIQ3100) \ + X(em24, C1_bit, Electricity, EM24, EM24) \ X(fhkvdataiii, T1_bit, HeatCostAllocation, FHKVDATAIII, FHKVDataIII) \ X(hydrus, T1_bit, Water, HYDRUS, Hydrus) \ X(hydrodigit, T1_bit, Water, HYDRODIGIT, Hydrodigit) \ @@ -88,6 +89,7 @@ X(EHZP, MANUFACTURER_EMH, 0x02, 0x02) \ X(ESYSWM, MANUFACTURER_ESY, 0x37, 0x30) \ X(FLOWIQ3100,MANUFACTURER_KAM, 0x16, 0x1d) \ + X(EM24, MANUFACTURER_KAM, 0x02, 0x33) \ X(FHKVDATAIII,MANUFACTURER_TCH, 0x80, 0x69) \ X(HYDRUS, MANUFACTURER_DME, 0x07, 0x70) \ X(HYDRUS, MANUFACTURER_HYD, 0x07, 0x24) \ @@ -107,6 +109,7 @@ X(LANSENPU, MANUFACTURER_LAS, 0x00, 0x0b) \ X(MKRADIO3, MANUFACTURER_TCH, 0x62, 0x74) \ X(MKRADIO3, MANUFACTURER_TCH, 0x72, 0x74) \ + X(MULTICAL21, MANUFACTURER_KAM, 0x06, 0x1b) \ X(MULTICAL21, MANUFACTURER_KAM, 0x16, 0x1b) \ X(MULTICAL302,MANUFACTURER_KAM, 0x04, 0x30) \ X(MULTICAL302,MANUFACTURER_KAM, 0x0d, 0x30) \ @@ -312,6 +315,7 @@ unique_ptr createCompact5(WMBus *bus, MeterInfo &m); unique_ptr createWaterstarM(WMBus *bus, MeterInfo &m); unique_ptr createOmnipower(WMBus *bus, MeterInfo &m); unique_ptr createAmiplus(WMBus *bus, MeterInfo &m); +unique_ptr createEM24(WMBus *bus, MeterInfo &m); unique_ptr createSupercom587(WMBus *bus, MeterInfo &m); unique_ptr createMKRadio3(WMBus *bus, MeterInfo &m); unique_ptr createApator08(WMBus *bus, MeterInfo &m); diff --git a/src/units.h b/src/units.h index e2ca6cf..d51318b 100644 --- a/src/units.h +++ b/src/units.h @@ -21,22 +21,24 @@ #include #include -#define LIST_OF_QUANTITIES \ - X(Energy,KWH) \ - X(Power,KW) \ - X(Volume,M3) \ - X(Flow,M3H) \ - X(Temperature,C) \ - X(RelativeHumidity,RH) \ - X(HCA,HCA) \ - X(Text,TXT) \ - X(Counter,INT) \ - X(Time,Hour) \ +#define LIST_OF_QUANTITIES \ + X(Energy,KWH) \ + X(Reactive_Energy,KVARH) \ + X(Power,KW) \ + X(Volume,M3) \ + X(Flow,M3H) \ + X(Temperature,C) \ + X(RelativeHumidity,RH) \ + X(HCA,HCA) \ + X(Text,TXT) \ + X(Counter,INT) \ + X(Time,Hour) \ #define LIST_OF_UNITS \ X(KWH,kwh,"kWh",Energy,"kilo Watt hour") \ X(MJ,mj,"MJ",Energy,"Mega Joule") \ X(GJ,gj,"GJ",Energy,"Giga Joule") \ + X(KVARH,kvarh,"kVARh",Reactive_Energy,"kilo volt amperes reactive hour") \ X(M3,m3,"m3",Volume,"cubic meter") \ X(L,l,"l",Volume,"litre") \ X(KW,kw,"kW",Power,"kilo Watt") \