diff --git a/README.md b/README.md index 5b62258..0e9b13f 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,27 @@ should be possible to adapt for use outside of the Arduino environment. When using Arduino, version 1.6.6 or above is required because this library needs C++11 support which was enabled in that version. +Modyfied by Willem Aandewiel +============================ +The assumtion of the original library by Matthijs Kooijman is that the GAS meter +is always connected to MBUS_ID 1 .. which is not the case. If a GAS meter is +replaced it can/will be connected to the first free MBUS_ID and that could be +anything.. +That has some implications for parsing, for instance, the unit's. The unit +should be parsed and used but should not raise an error as we don't know what +meter is connected and what unit's it will use. So I removed the units check +but it would be better to have some kind of "wild card" unit. + +There are two type of GAS meters: "Temperature Compensated" and +"Not Temperature Compensated" meters. +My assumption is that de device_type of a GAS meter is always "3". If that +is not the case: all bets are off. + +Do not try to use this library for something usefull with an Arduino UNO +(atmega328) as it will not work but .. it will not raise an error so your +completely "in the blind"! (see this issue) + + Protocol -------- Every smart meter in the Netherlands has to comply with the Dutch Smart diff --git a/examples/minimal_parse/minimal_parse.ino b/examples/minimal_parse/minimal_parse.ino index c4b2655..2d73fbd 100644 --- a/examples/minimal_parse/minimal_parse.ino +++ b/examples/minimal_parse/minimal_parse.ino @@ -9,7 +9,7 @@ * the result. */ -#include "dsmr.h" +#include "dsmr2.h" // Data to parse const char msg[] = @@ -31,7 +31,8 @@ using MyData = ParsedData< void setup() { Serial.begin(115200); - + delay(250); + MyData data; ParseResult res = P1Parser::parse(&data, msg, lengthof(msg)); if (res.err) { diff --git a/examples/parse/parse.ino b/examples/parse/parse.ino index 2907ab2..043249f 100644 --- a/examples/parse/parse.ino +++ b/examples/parse/parse.ino @@ -9,10 +9,10 @@ * the result. */ -#include "dsmr.h" +#include "dsmr2.h" // Data to parse -const char raw[] = +const char rawcrc[] = "/KFM5KAIFA-METER\r\n" "\r\n" "1-3:0.2.8(40)\r\n" @@ -42,6 +42,38 @@ const char raw[] = "0-1:24.2.1(150117180000W)(00473.789*m3)\r\n" "0-1:24.4.0(1)\r\n" "!6F4A\r\n"; + +// Data to parse +const char rawnocrc[] = + "/KMP5 KA6U001585654321\r\n" + "\r\n" + "0-0:96.1.1(4530303336303033373839373331234567)\r\n" + "1-0:1.8.1(000180.670*kWh)\r\n" + "1-0:1.8.2(000091.890*kWh)\r\n" + "1-0:2.8.1(000117.100*kWh)\r\n" + "1-0:2.8.2(000079.500*kWh)\r\n" + "0-0:96.14.0(0002)\r\n" + "1-0:1.7.0(212.33*kW)\r\n" + "1-0:2.7.0(029.73*kW)\r\n" + "0-0:96.13.0()\r\n" + "0-0:96.13.1()\r\n" + "0-1:24.1.0(3)\r\n" + "0-1:96.1.0(4730301234567031363532303530323136)\r\n" + "0-1:24.3.0(140101004100)(08)(60)(1)(0-1:24.2.1)(m3)\r\n" + "(00100.006)\r\n" + "0-3:24.1.0(3)\r\n" + "0-3:96.1.0(4730301234567031363532303530323136)\r\n" + "0-3:24.3.0(140101004100)(08)(60)(1)(0-1:24.2.1)(m3)\r\n" + "(00300.006)\r\n" + "0-2:24.1.0(3)\r\n" + "0-2:96.1.0(4730301234567031363532303530323136)\r\n" + "0-2:24.3.0(140101004100)(08)(60)(1)(0-1:24.2.1)(m3)\r\n" + "(00200.006)\r\n" + "0-4:24.1.0(3)\r\n" + "0-4:96.1.0(4730301234567031363532303530323136)\r\n" + "0-4:24.3.0(140101004100)(08)(60)(1)(0-1:24.2.1)(m3)\r\n" + "(00400.006)\r\n" + "!\r\n"; /** * Define the data we're interested in, as well as the datastructure to @@ -51,58 +83,71 @@ const char raw[] = * Each template argument below results in a field of the same name. */ using MyData = ParsedData< - /* String */ identification, - /* String */ p1_version, - /* String */ timestamp, - /* String */ equipment_id, - /* FixedValue */ energy_delivered_tariff1, - /* FixedValue */ energy_delivered_tariff2, - /* FixedValue */ energy_returned_tariff1, - /* FixedValue */ energy_returned_tariff2, - /* String */ electricity_tariff, - /* FixedValue */ power_delivered, - /* FixedValue */ power_returned, - /* FixedValue */ electricity_threshold, - /* uint8_t */ electricity_switch_position, - /* uint32_t */ electricity_failures, - /* uint32_t */ electricity_long_failures, - /* String */ electricity_failure_log, - /* uint32_t */ electricity_sags_l1, - /* uint32_t */ electricity_sags_l2, - /* uint32_t */ electricity_sags_l3, - /* uint32_t */ electricity_swells_l1, - /* uint32_t */ electricity_swells_l2, - /* uint32_t */ electricity_swells_l3, - /* String */ message_short, - /* String */ message_long, - /* FixedValue */ voltage_l1, - /* FixedValue */ voltage_l2, - /* FixedValue */ voltage_l3, - /* FixedValue */ current_l1, - /* FixedValue */ current_l2, - /* FixedValue */ current_l3, - /* FixedValue */ power_delivered_l1, - /* FixedValue */ power_delivered_l2, - /* FixedValue */ power_delivered_l3, - /* FixedValue */ power_returned_l1, - /* FixedValue */ power_returned_l2, - /* FixedValue */ power_returned_l3, - /* uint16_t */ gas_device_type, - /* String */ gas_equipment_id, - /* uint8_t */ gas_valve_position, - /* TimestampedFixedValue */ gas_delivered, - /* uint16_t */ thermal_device_type, - /* String */ thermal_equipment_id, - /* uint8_t */ thermal_valve_position, - /* TimestampedFixedValue */ thermal_delivered, - /* uint16_t */ water_device_type, - /* String */ water_equipment_id, - /* uint8_t */ water_valve_position, - /* TimestampedFixedValue */ water_delivered, - /* uint16_t */ slave_device_type, - /* String */ slave_equipment_id, - /* uint8_t */ slave_valve_position, - /* TimestampedFixedValue */ slave_delivered + /* String */ identification + /* String */ ,p1_version + /* String */ ,p1_version_be + /* String */ ,timestamp + /* String */ ,equipment_id + /* FixedValue */ ,energy_delivered_tariff1 + /* FixedValue */ ,energy_delivered_tariff2 + /* FixedValue */ ,energy_returned_tariff1 + /* FixedValue */ ,energy_returned_tariff2 + /* String */ ,electricity_tariff + /* FixedValue */ ,power_delivered + /* FixedValue */ ,power_returned + /* FixedValue */ ,electricity_threshold + /* uint8_t */ ,electricity_switch_position + /* uint32_t */ ,electricity_failures + /* uint32_t */ ,electricity_long_failures + /* String */ ,electricity_failure_log + /* uint32_t */ ,electricity_sags_l1 + /* uint32_t */ ,electricity_sags_l2 + /* uint32_t */ ,electricity_sags_l3 + /* uint32_t */ ,electricity_swells_l1 + /* uint32_t */ ,electricity_swells_l2 + /* uint32_t */ ,electricity_swells_l3 + /* String */ ,message_short + /* String */ ,message_long + /* FixedValue */ ,voltage_l1 + /* FixedValue */ ,voltage_l2 + /* FixedValue */ ,voltage_l3 + /* FixedValue */ ,current_l1 + /* FixedValue */ ,current_l2 + /* FixedValue */ ,current_l3 + /* FixedValue */ ,power_delivered_l1 + /* FixedValue */ ,power_delivered_l2 + /* FixedValue */ ,power_delivered_l3 + /* FixedValue */ ,power_returned_l1 + /* FixedValue */ ,power_returned_l2 + /* FixedValue */ ,power_returned_l3 + /* uint16_t */ ,mbus1_device_type + /* String */ ,mbus1_equipment_id_tc + /* String */ ,mbus1_equipment_id_ntc + /* uint8_t */ ,mbus1_valve_position + /* TimestampedFixedValue */ ,mbus1_delivered + /* TimestampedFixedValue */ ,mbus1_delivered_ntc + /* TimestampedFixedValue */ ,mbus1_delivered_dbl + /* uint16_t */ ,mbus2_device_type + /* String */ ,mbus2_equipment_id_tc + /* String */ ,mbus2_equipment_id_ntc + /* uint8_t */ ,mbus2_valve_position + /* TimestampedFixedValue */ ,mbus2_delivered + /* TimestampedFixedValue */ ,mbus2_delivered_ntc + /* TimestampedFixedValue */ ,mbus2_delivered_dbl + /* uint16_t */ ,mbus3_device_type + /* String */ ,mbus3_equipment_id_tc + /* String */ ,mbus3_equipment_id_ntc + /* uint8_t */ ,mbus3_valve_position + /* TimestampedFixedValue */ ,mbus3_delivered + /* TimestampedFixedValue */ ,mbus3_delivered_ntc + /* TimestampedFixedValue */ ,mbus3_delivered_dbl + /* uint16_t */ ,mbus4_device_type + /* String */ ,mbus4_equipment_id_tc + /* String */ ,mbus4_equipment_id_ntc + /* uint8_t */ ,mbus4_valve_position + /* TimestampedFixedValue */ ,mbus4_delivered + /* TimestampedFixedValue */ ,mbus4_delivered_ntc + /* TimestampedFixedValue */ ,mbus4_delivered_dbl >; /** @@ -139,16 +184,39 @@ struct Printer { void setup() { Serial.begin(115200); + while(!Serial) {/*wait a while*/ delay(100);} + delay(2000); + Serial.println("\r\n----------------------------------------------------"); + Serial.print(rawcrc); + Serial.println("----------------------------------------------------"); MyData data; - ParseResult res = P1Parser::parse(&data, raw, lengthof(raw), true); - if (res.err) { + ParseResult res1 = P1Parser::parse(&data, rawcrc, lengthof(rawcrc), true, true); + if (res1.err) { + // Parsing error, show it + Serial.println("P1Parser: Error found!"); + Serial.println(res1.fullError(rawcrc, rawcrc + lengthof(rawcrc))); + } else { + // Parsed succesfully, print all values + Serial.println("P1Parser: OK!\r\n"); + data.applyEach(Printer()); + } + + Serial.println("\r\n----------------------------------------------------"); + Serial.print(rawnocrc); + Serial.println("----------------------------------------------------"); + data = {}; + ParseResult res2 = P1Parser::parse(&data, rawnocrc, lengthof(rawnocrc), true, false); + if (res2.err) { // Parsing error, show it - Serial.println(res.fullError(raw, raw + lengthof(raw))); + Serial.println("P1Parser: Error found!"); + Serial.println(res2.fullError(rawnocrc, rawnocrc + lengthof(rawnocrc))); } else { // Parsed succesfully, print all values + Serial.println("P1Parser: OK!\r\n"); data.applyEach(Printer()); } + } void loop () { diff --git a/examples/parse_to_json/parse_to_json.ino b/examples/parse_to_json/parse_to_json.ino new file mode 100644 index 0000000..cd2a776 --- /dev/null +++ b/examples/parse_to_json/parse_to_json.ino @@ -0,0 +1,427 @@ +/* + * Permission is hereby granted, free of charge, to anyone + * obtaining a copy of this document and accompanying files, + * to do whatever they want with them without any restriction, + * including, but not limited to, copying, modification and redistribution. + * NO WARRANTY OF ANY KIND IS PROVIDED. + * + * Example that shows how to parse a P1 message and automatically print + * the result. +*/ + +#include "dsmr2.h" +#include // needs version 6+ + +//--- if you have a real Slimme Meter connected --- +//--- activate the next two #defines -------------- +//#define READSLIMMEMETER +//#define DTR_ENABLE 12 // GPIO-pin to use for DTR + + +// Data to parse +//--- LandisGyr E350 KMP5 DSMR50 +const char msg1[] = + "/XMX5LGBBLB2410065887\r\n" + "\r\n" + "1-3:0.2.8(50)\r\n" // p1_version + "0-0:1.0.0(200408063501S)\r\n" // timestamp + "0-0:96.1.1(4530303336303000000000000000000040)\r\n" // equiptment_id + "1-0:1.8.1(000234.191*kWh)\r\n" // energy_delivered_tariff1 + "1-0:1.8.2(000402.930*kWh)\r\n" // energy_delivered_tariff2 + "1-0:2.8.1(000119.045*kWh)\r\n" // energy_returned_tariff1 + "1-0:2.8.2(000079.460*kWh)\r\n" // energy_returned_tariff2 + "0-0:96.14.0(0001)\r\n" // electricity_tariff + "1-0:1.7.0(001.22*kW)\r\n" // power_delivered + "1-0:2.7.0(001.11*kW)\r\n" // power_returned + "0-0:96.7.21(00010)\r\n" // electricity_failures + "0-0:96.7.9(00000)\r\n" // electricity_long_failures + "1-0:99.97.0(0)(0-0:96.7.19)\r\n" // electricity_failure_log + "1-0:32.32.0(00002)\r\n" // electricity_sags_l1 + "1-0:52.32.0(00003)\r\n" // electricity_sags_l2 + "1-0:72.32.0(00003)\r\n" // electricity_sags_l3 + "1-0:32.36.0(00000)\r\n" // electricity_swells_l1 + "1-0:52.36.0(00000)\r\n" // electricity_swells_l2 + "1-0:72.36.0(00000)\r\n" // electricity_swells_l3 + "0-0:96.13.0()\r\n" // message_long + "1-0:32.7.0(241.0*V)\r\n" // voltage_l1 + "1-0:52.7.0(237.0*V)\r\n" // voltage_l2 + "1-0:72.7.0(235.0*V)\r\n" // voltage_l3 + "1-0:31.7.0(000*A)\r\n" // current_l1 + "1-0:51.7.0(000*A)\r\n" // current_l2 + "1-0:71.7.0(000*A)\r\n" // current_l3 + "1-0:21.7.0(00.536*kW)\r\n" // power_delivered_l1 + "1-0:41.7.0(00.194*kW)\r\n" // power_delivered_l2 + "1-0:61.7.0(00.487*kW)\r\n" // power_delivered_l3 + "1-0:22.7.0(00.013*kW)\r\n" // power_returned_l1 + "1-0:42.7.0(00.611*kW)\r\n" // power_returned_l2 + "1-0:62.7.0(00.486*kW)\r\n" // power_returned_l3 + "0-1:24.1.0(003)\r\n" // mbus1_device_type + "0-1:96.1.0(4730303339303031363532303530323136)\r\n" // mbus1_equipment_id_tc + "0-1:24.2.1(200408063501S)(00169.156*m3)\r\n" // mbus1_delivered_tc + "!0876\r\n"; + +//--- Sagemcom XS210 ESMR5 (1Fase) +const char msg2[] = + "/Ene5\\XS210 ESMR 5.0\r\n" + "\r\n" + "1-3:0.2.8(50)\r\n" // p1_version + "0-0:1.0.0(190508094821S)\r\n" // timestamp + "0-0:96.1.1(4530303437303030123456789134343137)\r\n" // equiptment_id + "1-0:1.8.1(000769.736*kWh)\r\n" // energy_delivered_tariff1 + "1-0:1.8.2(000664.646*kWh)\r\n" // energy_delivered_tariff2 + "1-0:2.8.1(000000.016*kWh)\r\n" // energy_returned_tariff1 + "1-0:2.8.2(000000.000*kWh)\r\n" // energy_returned_tariff2 + "0-0:96.14.0(0002)\r\n" // electricity_tariff + "1-0:1.7.0(00.037*kW)\r\n" // power_delivered + "1-0:2.7.0(00.000*kW)\r\n" // power_returned + "0-0:96.7.21(00204)\r\n" // electricity_failures + "0-0:96.7.9(00147)\r\n" // electricity_long_failures // electricity_failure_log + "1-0:99.97.0(10)(0-0:96.7.19)(190508094303S)(0000055374*s)(190507165813S)(0000007" + "991*s)(190507141021S)(0000000274*s)(190507135954S)(0000000649*s)(190507134811S)(" + "0000083213*s)(190506143928S)(0000090080*s)(190505123501S)(0000073433*s)(19050415" + "2603S)(0000003719*s)(190504120844S)(0000337236*s)(190430142638S)(0000165493*s)\r\n" + "1-0:32.32.0(00149)\r\n" // electricity_sags_l1 + "1-0:32.36.0(00000)\r\n" // electricity_swells_l1 + "0-0:96.13.0()\r\n" // message_long + "1-0:32.7.0(231.0*V)\r\n" // voltage_l1 + "1-0:31.7.0(000*A)\r\n" // current_l1 + "1-0:21.7.0(00.037*kW)\r\n" // power_delivered_l1 + "1-0:22.7.0(00.000*kW)\r\n" // power_returned_l1 + "0-1:24.1.0(003)\r\n" // mbus1_device_type + "0-1:96.1.0(4730303533303987654321373431393137)\r\n" // mbus1_equipment_id_tc + "0-1:24.2.1(632525252525S)(00000.000)\r\n" // mbus1_delivered_tc <-- error (no unit) + "!DE4A\r\n"; + +//--- Sagemcom Fluvius ? --(Belgie) +const char msg3[] = + "/FLU5\\253769484_A\r\n" + "\r\n" + "0-0:96.1.4(50213)\r\n" // p1_version_be + "0-0:96.1.1(3153414123456789303638373236)\r\n" // equiptment_id + "0-0:1.0.0(191204184601W)\r\n" // timestamp + "1-0:1.8.1(000050.069*kWh)\r\n" // energy_delivered_tariff1 + "1-0:1.8.2(000055.085*kWh)\r\n" // energy_delivered_tariff2 + "1-0:2.8.1(000019.870*kWh)\r\n" // energy_returned_tariff1 + "1-0:2.8.2(000005.678*kWh)\r\n" // energy_returned_tariff2 + "0-0:96.14.0(0001)\r\n" // electricity_tariff + "1-0:1.7.0(00.655*kW)\r\n" // power_delivered + "1-0:2.7.0(00.000*kW)\r\n" // power_returned + "1-0:32.7.0(225.1*V)\r\n" // voltage_l1 + "1-0:52.7.0(000.0*V)\r\n" // voltage_l2 + "1-0:72.7.0(225.7*V)\r\n" // voltage_l3 + "1-0:31.7.0(001*A)\r\n" // current_l1 + "1-0:51.7.0(002*A)\r\n" // current_l2 + "1-0:71.7.0(001*A)\r\n" // current_l3 + "0-0:96.3.10(1)\r\n" // electricity_switch_position + "0-0:17.0.0(999.9*kW)\r\n" // electricity_threshold + "1-0:31.4.0(999*A)\r\n" // fuse_treshold_l1 + "0-0:96.13.0()\r\n" // message_long + "0-1:24.1.0(3)\r\n" // mbus1_device_type + "0-1:96.1.0(4730301234567031363532303511111111)\r\n" // mbus1_equipment_id_tc + "0-1:24.3.0(140101004100)(08)(60)(1)(0-1:24.2.1)(m3)\r\n" // mbus1_delivered_dbl + "(00111.006)\r\n" // mbus1_delivered_dbl (line 2) + "0-3:24.1.0(3)\r\n" // mbus3_device_type + "0-3:96.1.0(4730301234567031363532303333333333)\r\n" // mbus3_equipment_id_tc + "0-3:24.3.0(140101004100)(08)(60)(1)(0-3:24.2.1)(m3)\r\n" // mbus3_delivered_dbl + "(00333.006)\r\n" + "0-2:24.1.0(3)\r\n" // mbus2_device_type + "0-2:96.1.0(4730301234567031363532303222222222)\r\n" // mbus2_equipment_id_tc + "0-2:24.3.0(140101004100)(08)(60)(1)(0-2:24.2.1)(m3)\r\n" // mbus2_delivered_dbl + "(00222.006)\r\n" + "0-4:24.1.0(3)\r\n" // mbus4_device_type + "0-4:96.1.0(47303012345670313635323034444444444)\r\n" // mbus4_equipment_id_tc + "0-4:24.3.0(140101004100)(08)(60)(1)(0-4:24.2.1)(m3)\r\n" // mbus4_delivered_dbl + "(00444.006)\r\n" + "!7934\r\n"; //<-- wrong checksum! don't check it! + +/** + * Define the data we're interested in, as well as the datastructure to + * hold the parsed data. + * Each template argument below results in a field of the same name. + */ +using MyData = ParsedData< + /* String */ identification + /* String */ ,p1_version + /* String */ ,p1_version_be + /* String */ ,timestamp + /* String */ ,equipment_id + /* FixedValue */ ,energy_delivered_tariff1 + /* FixedValue */ ,energy_delivered_tariff2 + /* FixedValue */ ,energy_returned_tariff1 + /* FixedValue */ ,energy_returned_tariff2 + /* String */ ,electricity_tariff + /* FixedValue */ ,power_delivered + /* FixedValue */ ,power_returned + /* FixedValue */ ,electricity_threshold + /* uint8_t */ ,electricity_switch_position + /* uint32_t */ ,electricity_failures + /* uint32_t */ ,electricity_long_failures + /* String */ ,electricity_failure_log + /* uint32_t */ ,electricity_sags_l1 + /* uint32_t */ ,electricity_sags_l2 + /* uint32_t */ ,electricity_sags_l3 + /* uint32_t */ ,electricity_swells_l1 + /* uint32_t */ ,electricity_swells_l2 + /* uint32_t */ ,electricity_swells_l3 + /* String */ ,message_short + /* String */ ,message_long + /* FixedValue */ ,voltage_l1 + /* FixedValue */ ,voltage_l2 + /* FixedValue */ ,voltage_l3 + /* FixedValue */ ,current_l1 + /* FixedValue */ ,current_l2 + /* FixedValue */ ,current_l3 + /* FixedValue */ ,power_delivered_l1 + /* FixedValue */ ,power_delivered_l2 + /* FixedValue */ ,power_delivered_l3 + /* FixedValue */ ,power_returned_l1 + /* FixedValue */ ,power_returned_l2 + /* FixedValue */ ,power_returned_l3 + /* uint16_t */ ,mbus1_device_type + /* String */ ,mbus1_equipment_id_tc + /* String */ ,mbus1_equipment_id_ntc + /* uint8_t */ ,mbus1_valve_position + /* TimestampedFixedValue */ ,mbus1_delivered + /* TimestampedFixedValue */ ,mbus1_delivered_ntc + /* TimestampedFixedValue */ ,mbus1_delivered_dbl + /* uint16_t */ ,mbus2_device_type + /* String */ ,mbus2_equipment_id_tc + /* String */ ,mbus2_equipment_id_ntc + /* uint8_t */ ,mbus2_valve_position + /* TimestampedFixedValue */ ,mbus2_delivered + /* TimestampedFixedValue */ ,mbus2_delivered_ntc + /* TimestampedFixedValue */ ,mbus2_delivered_dbl + /* uint16_t */ ,mbus3_device_type + /* String */ ,mbus3_equipment_id_tc + /* String */ ,mbus3_equipment_id_ntc + /* uint8_t */ ,mbus3_valve_position + /* TimestampedFixedValue */ ,mbus3_delivered + /* TimestampedFixedValue */ ,mbus3_delivered_ntc + /* TimestampedFixedValue */ ,mbus3_delivered_dbl + /* uint16_t */ ,mbus4_device_type + /* String */ ,mbus4_equipment_id_tc + /* String */ ,mbus4_equipment_id_ntc + /* uint8_t */ ,mbus4_valve_position + /* TimestampedFixedValue */ ,mbus4_delivered + /* TimestampedFixedValue */ ,mbus4_delivered_ntc + /* TimestampedFixedValue */ ,mbus4_delivered_dbl +>; + +#if defined(READSLIMMEMETER) + #ifdef DTR_ENABLE + P1Reader slimmeMeter(&Serial, DTR_ENABLE); + #else + P1Reader slimmeMeter(&Serial, 0); + #endif +#endif + +//===========================GLOBAL VAR'S====================================== + MyData DSMRdata; + DynamicJsonDocument jsonDoc(4000); // generic doc to return, clear() before use! + JsonObject jsonObj; + uint32_t readTimer; + char jsonArrayName[20] = ""; + + +struct buildJson +{ + JsonArray root = jsonDoc.createNestedArray(jsonArrayName); + + template + void apply(Item &i) + { + String Name = String(Item::name); + + if (i.present()) + { + JsonObject nested = root.createNestedObject(); + nested["name"] = Name; + String Unit = String(Item::unit()); + nested["value"] = value_to_json(i.val()); + + if (Unit.length() > 0) + { + nested["unit"] = Unit; + } + } + } + + template + Item& value_to_json(Item& i) + { + return i; + } + + String value_to_json(TimestampedFixedValue i) + { + return String(i); + } + + float value_to_json(FixedValue i) + { + return i; + } + +}; // buildjson{} + + +//======================================================================= +void makeJson() +{ + String toReturn; + + jsonDoc.clear(); + + strcpy(jsonArrayName, "fields"); + DSMRdata.applyEach(buildJson()); + Serial.println(); + + jsonObj = jsonDoc.as(); + + //serializeJson(jsonObj, toReturn); // for production + serializeJsonPretty(jsonObj, toReturn); // for human readable testing + Serial.printf("JSON String is %d chars\r\n", toReturn.length()); + Serial.println(toReturn); + +} // makeJson() + + +//--------------------------------------------------------------------------- +void setup() +{ + Serial.begin(115200); + delay(250); + while (!Serial) {/* wait a while */ delay(100); } + + Serial.println("\n\nAnd now it begins ...\n"); + + Serial.println(ESP.getResetReason()); + if ( ESP.getResetReason() == "Exception" + || ESP.getResetReason() == "Software Watchdog" + || ESP.getResetReason() == "Soft WDT reset" + ) + { + while (1) + { + delay(500); + digitalWrite(LED_BUILTIN, !digitalRead(LED_BUILTIN)); + } + } + + +#if defined(READSLIMMEMETER) + slimmeMeter.enable(true); +#else + ParseResult res; + //--- read first telegram --- + Serial.println("\r\n===================================================="); + Serial.println("Start parsing telegram 1 "); + DSMRdata = {}; + //--------------------------------- do check CheckSum! vvvv + res = P1Parser::parse(&DSMRdata, msg1, lengthof(msg1), true); + if (res.err) + { + // Parsing error, show it + Serial.println(res.fullError(msg1, msg1 + lengthof(msg1))); + } + else if (!DSMRdata.all_present()) + { + Serial.println("DSMR: Some fields are missing"); + Serial.println("\r\nAs with this \"dsmr2Lib\" library we check for the \"mbusx_delivered_\""); + Serial.println("\"Temperature Corrected (tc)\", \"Not Temperature Corrected (ntc)\" and "); + Serial.println("\"double lines (dbl)\" fields."); + Serial.println("Normaly only one or the other is in a telegram."); + Serial.println("So, the object \"all_present()\" will always return false!"); } + // Succesfully parsed, make JSON: + makeJson(); + + //--- read second telegram --- + Serial.println("\r\n===================================================="); + Serial.println("Start parsing telegram 2"); + DSMRdata = {}; + //--------------------------------- do check CheckSum! vvvv + res = P1Parser::parse(&DSMRdata, msg2, lengthof(msg2), true); + if (res.err) + { + // Parsing error, show it + Serial.println(res.fullError(msg2, msg2 + lengthof(msg2))); + } + else if (!DSMRdata.all_present()) + { + Serial.println("DSMR: Some fields are missing"); + } + // Succesfully parsed, make JSON: + makeJson(); + + //--- read third telegram --- + Serial.println("\r\n===================================================="); + Serial.println("Start parsing telegram 3 (do NOT check CheckSum)"); + DSMRdata = {}; + //----------------------------- do NOT check CheckSum! vvvvv + res = P1Parser::parse(&DSMRdata, msg3, lengthof(msg3), false); + if (res.err) + { + // Parsing error, show it + Serial.println(res.fullError(msg3, msg3 + lengthof(msg3))); + } + else if (!DSMRdata.all_present()) + { + Serial.println("DSMR: Some fields are missing"); + Serial.println("\r\nAs with this \"dsmr2Lib\" library we check for the \"mbusx_delivered_\""); + Serial.println("\"Temperature Corrected (tc)\", \"Not Temperature Corrected (ntc)\" and "); + Serial.println("\"double lines (dbl)\" fields."); + Serial.println("Normaly only one or the other is in a telegram."); + Serial.println("So, the object \"all_present()\" will always return false!"); + } + // Succesfully parsed, make JSON: + makeJson(); + + //--- read third telegram --- + Serial.println("\r\n===================================================="); + Serial.println("Start parsing telegram 3 (do check CheckSum)"); + DSMRdata = {}; + //--------------------------------- do check CheckSum! vvvv + res = P1Parser::parse(&DSMRdata, msg3, lengthof(msg3), true); + if (res.err) + { + // Parsing error, show it + Serial.println(res.fullError(msg3, msg3 + lengthof(msg3))); + } + else if (!DSMRdata.all_present()) + { + Serial.println("DSMR: Some fields are missing"); + Serial.println("\r\nAs with this \"dsmr2Lib\" library we check for the \"mbusx_delivered_\""); + Serial.println("\"Temperature Corrected (tc)\", \"Not Temperature Corrected (ntc)\" and "); + Serial.println("\"double lines (dbl)\" fields."); + Serial.println("Normaly only one or the other is in a telegram."); + Serial.println("So, the object \"all_present()\" will always return false!"); + } + // Succesfully parsed, make JSON: + makeJson(); + +#endif + +} // setup() + + +//--------------------------------------------------------------------------- +void loop () { +#if defined(READSLIMMEMETER) + slimmeMeter.loop(); + slimmeMeter.enable(true); + if (millis() - readTimer > 10000) + { + readTimer = millis(); + if (slimmeMeter.available()) + { + DSMRdata = {}; + String DSMRerror; + + if (slimmeMeter.parse(&DSMRdata, &DSMRerror)) // Parse succesful, print result + { + buildJson(); + } + } + } +#endif +} // loop() diff --git a/examples/read/read.ino b/examples/read/read.ino index 1e6d3d3..c848801 100644 --- a/examples/read/read.ino +++ b/examples/read/read.ino @@ -9,7 +9,7 @@ * serial port and automatically print the result. */ -#include "dsmr.h" +#include "dsmr2.h" /** * Define the data we're interested in, as well as the datastructure to @@ -19,58 +19,71 @@ * Each template argument below results in a field of the same name. */ using MyData = ParsedData< - /* String */ identification, - /* String */ p1_version, - /* String */ timestamp, - /* String */ equipment_id, - /* FixedValue */ energy_delivered_tariff1, - /* FixedValue */ energy_delivered_tariff2, - /* FixedValue */ energy_returned_tariff1, - /* FixedValue */ energy_returned_tariff2, - /* String */ electricity_tariff, - /* FixedValue */ power_delivered, - /* FixedValue */ power_returned, - /* FixedValue */ electricity_threshold, - /* uint8_t */ electricity_switch_position, - /* uint32_t */ electricity_failures, - /* uint32_t */ electricity_long_failures, - /* String */ electricity_failure_log, - /* uint32_t */ electricity_sags_l1, - /* uint32_t */ electricity_sags_l2, - /* uint32_t */ electricity_sags_l3, - /* uint32_t */ electricity_swells_l1, - /* uint32_t */ electricity_swells_l2, - /* uint32_t */ electricity_swells_l3, - /* String */ message_short, - /* String */ message_long, - /* FixedValue */ voltage_l1, - /* FixedValue */ voltage_l2, - /* FixedValue */ voltage_l3, - /* FixedValue */ current_l1, - /* FixedValue */ current_l2, - /* FixedValue */ current_l3, - /* FixedValue */ power_delivered_l1, - /* FixedValue */ power_delivered_l2, - /* FixedValue */ power_delivered_l3, - /* FixedValue */ power_returned_l1, - /* FixedValue */ power_returned_l2, - /* FixedValue */ power_returned_l3, - /* uint16_t */ gas_device_type, - /* String */ gas_equipment_id, - /* uint8_t */ gas_valve_position, - /* TimestampedFixedValue */ gas_delivered, - /* uint16_t */ thermal_device_type, - /* String */ thermal_equipment_id, - /* uint8_t */ thermal_valve_position, - /* TimestampedFixedValue */ thermal_delivered, - /* uint16_t */ water_device_type, - /* String */ water_equipment_id, - /* uint8_t */ water_valve_position, - /* TimestampedFixedValue */ water_delivered, - /* uint16_t */ slave_device_type, - /* String */ slave_equipment_id, - /* uint8_t */ slave_valve_position, - /* TimestampedFixedValue */ slave_delivered + /* String */ identification + /* String */ ,p1_version + /* String */ ,p1_version_be + /* String */ ,timestamp + /* String */ ,equipment_id + /* FixedValue */ ,energy_delivered_tariff1 + /* FixedValue */ ,energy_delivered_tariff2 + /* FixedValue */ ,energy_returned_tariff1 + /* FixedValue */ ,energy_returned_tariff2 + /* String */ ,electricity_tariff + /* FixedValue */ ,power_delivered + /* FixedValue */ ,power_returned + /* FixedValue */ ,electricity_threshold + /* uint8_t */ ,electricity_switch_position + /* uint32_t */ ,electricity_failures + /* uint32_t */ ,electricity_long_failures + /* String */ ,electricity_failure_log + /* uint32_t */ ,electricity_sags_l1 + /* uint32_t */ ,electricity_sags_l2 + /* uint32_t */ ,electricity_sags_l3 + /* uint32_t */ ,electricity_swells_l1 + /* uint32_t */ ,electricity_swells_l2 + /* uint32_t */ ,electricity_swells_l3 + /* String */ ,message_short + /* String */ ,message_long + /* FixedValue */ ,voltage_l1 + /* FixedValue */ ,voltage_l2 + /* FixedValue */ ,voltage_l3 + /* FixedValue */ ,current_l1 + /* FixedValue */ ,current_l2 + /* FixedValue */ ,current_l3 + /* FixedValue */ ,power_delivered_l1 + /* FixedValue */ ,power_delivered_l2 + /* FixedValue */ ,power_delivered_l3 + /* FixedValue */ ,power_returned_l1 + /* FixedValue */ ,power_returned_l2 + /* FixedValue */ ,power_returned_l3 + /* uint16_t */ ,mbus1_device_type + /* String */ ,mbus1_equipment_id_tc + /* String */ ,mbus1_equipment_id_ntc + /* uint8_t */ ,mbus1_valve_position + /* TimestampedFixedValue */ ,mbus1_delivered + /* TimestampedFixedValue */ ,mbus1_delivered_ntc + /* TimestampedFixedValue */ ,mbus1_delivered_dbl + /* uint16_t */ ,mbus2_device_type + /* String */ ,mbus2_equipment_id_tc + /* String */ ,mbus2_equipment_id_ntc + /* uint8_t */ ,mbus2_valve_position + /* TimestampedFixedValue */ ,mbus2_delivered + /* TimestampedFixedValue */ ,mbus2_delivered_ntc + /* TimestampedFixedValue */ ,mbus2_delivered_dbl + /* uint16_t */ ,mbus3_device_type + /* String */ ,mbus3_equipment_id_tc + /* String */ ,mbus3_equipment_id_ntc + /* uint8_t */ ,mbus3_valve_position + /* TimestampedFixedValue */ ,mbus3_delivered + /* TimestampedFixedValue */ ,mbus3_delivered_ntc + /* TimestampedFixedValue */ ,mbus3_delivered_dbl + /* uint16_t */ ,mbus4_device_type + /* String */ ,mbus4_equipment_id_tc + /* String */ ,mbus4_equipment_id_ntc + /* uint8_t */ ,mbus4_valve_position + /* TimestampedFixedValue */ ,mbus4_delivered + /* TimestampedFixedValue */ ,mbus4_delivered_ntc + /* TimestampedFixedValue */ ,mbus4_delivered_dbl >; /** @@ -119,6 +132,9 @@ unsigned long last; void setup() { Serial.begin(115200); Serial1.begin(115200); + delay(250); + Serial.println("\r\nAnd then it all starts ...\r\n"); + #ifdef VCC_ENABLE // This is needed on Pinoccio Scout boards to enable the 3V3 pin. pinMode(VCC_ENABLE, OUTPUT); diff --git a/library.properties b/library.properties index 5c272df..7cd7b51 100644 --- a/library.properties +++ b/library.properties @@ -1,10 +1,10 @@ -name=Dsmr -author=Matthijs Kooijman -email=matthijs@stdin.nl +name=Dsmr2Lib +author=Willem Aandewiel (original by Matthijs Kooijman) +email=Willem@Aandewiel.nl sentence=Parser and utilities for Dutch Smart Meters (Implementing DSMR) paragraph= category=Data Processing -url=https://github.com/matthijskooijman/arduino-dsmr +url=https://github.com/mrWheel/dsmr2Lib architectures=* version=0.1 dependencies= diff --git a/specs/OMS-Spec_Vol2_Primary_v402.pdf b/specs/OMS-Spec_Vol2_Primary_v402.pdf new file mode 100644 index 0000000..13b9dc7 Binary files /dev/null and b/specs/OMS-Spec_Vol2_Primary_v402.pdf differ diff --git a/src/dsmr/fields.cpp b/src/dsmr/fields.cpp deleted file mode 100644 index 02781c4..0000000 --- a/src/dsmr/fields.cpp +++ /dev/null @@ -1,262 +0,0 @@ -/** - * Arduino DSMR parser. - * - * This software is licensed under the MIT License. - * - * Copyright (c) 2015 Matthijs Kooijman - * - * Permission is hereby granted, free of charge, to any person obtaining - * a copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, sublicense, and/or sell copies of the Software, and to - * permit persons to whom the Software is furnished to do so, subject to - * the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS - * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN - * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - * - * Field parsing functions - */ - -#include "fields.h" - - -using namespace dsmr; -using namespace dsmr::fields; - -// Since C++11 it is possible to define the initial values for static -// const members in the class declaration, but if their address is -// taken, they still need a normal definition somewhere (to allocate -// storage). -constexpr char units::none[]; -constexpr char units::kWh[]; -constexpr char units::Wh[]; -constexpr char units::kW[]; -constexpr char units::W[]; -constexpr char units::V[]; -constexpr char units::mV[]; -constexpr char units::A[]; -constexpr char units::mA[]; -constexpr char units::m3[]; -constexpr char units::dm3[]; -constexpr char units::GJ[]; -constexpr char units::MJ[]; - -constexpr ObisId identification::id; -constexpr char identification::name_progmem[]; -constexpr const __FlashStringHelper *identification::name; - -constexpr ObisId p1_version::id; -constexpr char p1_version::name_progmem[]; -constexpr const __FlashStringHelper *p1_version::name; - -constexpr ObisId timestamp::id; -constexpr char timestamp::name_progmem[]; -constexpr const __FlashStringHelper *timestamp::name; - -constexpr ObisId equipment_id::id; -constexpr char equipment_id::name_progmem[]; -constexpr const __FlashStringHelper *equipment_id::name; - -constexpr ObisId energy_delivered_tariff1::id; -constexpr char energy_delivered_tariff1::name_progmem[]; -constexpr const __FlashStringHelper *energy_delivered_tariff1::name; - -constexpr ObisId energy_delivered_tariff2::id; -constexpr char energy_delivered_tariff2::name_progmem[]; -constexpr const __FlashStringHelper *energy_delivered_tariff2::name; - -constexpr ObisId energy_returned_tariff1::id; -constexpr char energy_returned_tariff1::name_progmem[]; -constexpr const __FlashStringHelper *energy_returned_tariff1::name; - -constexpr ObisId energy_returned_tariff2::id; -constexpr char energy_returned_tariff2::name_progmem[]; -constexpr const __FlashStringHelper *energy_returned_tariff2::name; - -constexpr ObisId electricity_tariff::id; -constexpr char electricity_tariff::name_progmem[]; -constexpr const __FlashStringHelper *electricity_tariff::name; - -constexpr ObisId power_delivered::id; -constexpr char power_delivered::name_progmem[]; -constexpr const __FlashStringHelper *power_delivered::name; - -constexpr ObisId power_returned::id; -constexpr char power_returned::name_progmem[]; -constexpr const __FlashStringHelper *power_returned::name; - -constexpr ObisId electricity_threshold::id; -constexpr char electricity_threshold::name_progmem[]; -constexpr const __FlashStringHelper *electricity_threshold::name; - -constexpr ObisId electricity_switch_position::id; -constexpr char electricity_switch_position::name_progmem[]; -constexpr const __FlashStringHelper *electricity_switch_position::name; - -constexpr ObisId electricity_failures::id; -constexpr char electricity_failures::name_progmem[]; -constexpr const __FlashStringHelper *electricity_failures::name; - -constexpr ObisId electricity_long_failures::id; -constexpr char electricity_long_failures::name_progmem[]; -constexpr const __FlashStringHelper *electricity_long_failures::name; - -constexpr ObisId electricity_failure_log::id; -constexpr char electricity_failure_log::name_progmem[]; -constexpr const __FlashStringHelper *electricity_failure_log::name; - -constexpr ObisId electricity_sags_l1::id; -constexpr char electricity_sags_l1::name_progmem[]; -constexpr const __FlashStringHelper *electricity_sags_l1::name; - -constexpr ObisId electricity_sags_l2::id; -constexpr char electricity_sags_l2::name_progmem[]; -constexpr const __FlashStringHelper *electricity_sags_l2::name; - -constexpr ObisId electricity_sags_l3::id; -constexpr char electricity_sags_l3::name_progmem[]; -constexpr const __FlashStringHelper *electricity_sags_l3::name; - -constexpr ObisId electricity_swells_l1::id; -constexpr char electricity_swells_l1::name_progmem[]; -constexpr const __FlashStringHelper *electricity_swells_l1::name; - -constexpr ObisId electricity_swells_l2::id; -constexpr char electricity_swells_l2::name_progmem[]; -constexpr const __FlashStringHelper *electricity_swells_l2::name; - -constexpr ObisId electricity_swells_l3::id; -constexpr char electricity_swells_l3::name_progmem[]; -constexpr const __FlashStringHelper *electricity_swells_l3::name; - -constexpr ObisId message_short::id; -constexpr char message_short::name_progmem[]; -constexpr const __FlashStringHelper *message_short::name; - -constexpr ObisId message_long::id; -constexpr char message_long::name_progmem[]; -constexpr const __FlashStringHelper *message_long::name; - -constexpr ObisId voltage_l1::id; -constexpr char voltage_l1::name_progmem[]; -constexpr const __FlashStringHelper *voltage_l1::name; - -constexpr ObisId voltage_l2::id; -constexpr char voltage_l2::name_progmem[]; -constexpr const __FlashStringHelper *voltage_l2::name; - -constexpr ObisId voltage_l3::id; -constexpr char voltage_l3::name_progmem[]; -constexpr const __FlashStringHelper *voltage_l3::name; - -constexpr ObisId current_l1::id; -constexpr char current_l1::name_progmem[]; -constexpr const __FlashStringHelper *current_l1::name; - -constexpr ObisId current_l2::id; -constexpr char current_l2::name_progmem[]; -constexpr const __FlashStringHelper *current_l2::name; - -constexpr ObisId current_l3::id; -constexpr char current_l3::name_progmem[]; -constexpr const __FlashStringHelper *current_l3::name; - -constexpr ObisId power_delivered_l1::id; -constexpr char power_delivered_l1::name_progmem[]; -constexpr const __FlashStringHelper *power_delivered_l1::name; - -constexpr ObisId power_delivered_l2::id; -constexpr char power_delivered_l2::name_progmem[]; -constexpr const __FlashStringHelper *power_delivered_l2::name; - -constexpr ObisId power_delivered_l3::id; -constexpr char power_delivered_l3::name_progmem[]; -constexpr const __FlashStringHelper *power_delivered_l3::name; - -constexpr ObisId power_returned_l1::id; -constexpr char power_returned_l1::name_progmem[]; -constexpr const __FlashStringHelper *power_returned_l1::name; - -constexpr ObisId power_returned_l2::id; -constexpr char power_returned_l2::name_progmem[]; -constexpr const __FlashStringHelper *power_returned_l2::name; - -constexpr ObisId power_returned_l3::id; -constexpr char power_returned_l3::name_progmem[]; -constexpr const __FlashStringHelper *power_returned_l3::name; - -constexpr ObisId gas_device_type::id; -constexpr char gas_device_type::name_progmem[]; -constexpr const __FlashStringHelper *gas_device_type::name; - -constexpr ObisId gas_equipment_id::id; -constexpr char gas_equipment_id::name_progmem[]; -constexpr const __FlashStringHelper *gas_equipment_id::name; - -constexpr ObisId gas_valve_position::id; -constexpr char gas_valve_position::name_progmem[]; -constexpr const __FlashStringHelper *gas_valve_position::name; - -constexpr ObisId gas_delivered::id; -constexpr char gas_delivered::name_progmem[]; -constexpr const __FlashStringHelper *gas_delivered::name; - -constexpr ObisId thermal_device_type::id; -constexpr char thermal_device_type::name_progmem[]; -constexpr const __FlashStringHelper *thermal_device_type::name; - -constexpr ObisId thermal_equipment_id::id; -constexpr char thermal_equipment_id::name_progmem[]; -constexpr const __FlashStringHelper *thermal_equipment_id::name; - -constexpr ObisId thermal_valve_position::id; -constexpr char thermal_valve_position::name_progmem[]; -constexpr const __FlashStringHelper *thermal_valve_position::name; - -constexpr ObisId thermal_delivered::id; -constexpr char thermal_delivered::name_progmem[]; -constexpr const __FlashStringHelper *thermal_delivered::name; - -constexpr ObisId water_device_type::id; -constexpr char water_device_type::name_progmem[]; -constexpr const __FlashStringHelper *water_device_type::name; - -constexpr ObisId water_equipment_id::id; -constexpr char water_equipment_id::name_progmem[]; -constexpr const __FlashStringHelper *water_equipment_id::name; - -constexpr ObisId water_valve_position::id; -constexpr char water_valve_position::name_progmem[]; -constexpr const __FlashStringHelper *water_valve_position::name; - -constexpr ObisId water_delivered::id; -constexpr char water_delivered::name_progmem[]; -constexpr const __FlashStringHelper *water_delivered::name; - -constexpr ObisId slave_device_type::id; -constexpr char slave_device_type::name_progmem[]; -constexpr const __FlashStringHelper *slave_device_type::name; - -constexpr ObisId slave_equipment_id::id; -constexpr char slave_equipment_id::name_progmem[]; -constexpr const __FlashStringHelper *slave_equipment_id::name; - -constexpr ObisId slave_valve_position::id; -constexpr char slave_valve_position::name_progmem[]; -constexpr const __FlashStringHelper *slave_valve_position::name; - -constexpr ObisId slave_delivered::id; -constexpr char slave_delivered::name_progmem[]; -constexpr const __FlashStringHelper *slave_delivered::name; - diff --git a/src/dsmr.h b/src/dsmr2.h similarity index 83% rename from src/dsmr.h rename to src/dsmr2.h index ced73ec..80b73c7 100644 --- a/src/dsmr.h +++ b/src/dsmr2.h @@ -4,6 +4,10 @@ * This software is licensed under the MIT License. * * Copyright (c) 2015 Matthijs Kooijman + * + * Changed by Willem Aandewiel to incorporate Non Temperature Compensated + * GAS meter readings. GAS meter is no longer hardcoded on M-BUS 1 and + * the obis for the Belgium P1_Version (p1_versionBE) is available. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the @@ -32,9 +36,9 @@ #ifndef DSMR_INCLUDE_DSMR_H #define DSMR_INCLUDE_DSMR_H -#include "dsmr/parser.h" -#include "dsmr/reader.h" -#include "dsmr/fields.h" +#include "dsmr2/parser2.h" +#include "dsmr2/reader2.h" +#include "dsmr2/fields2.h" // Allow using everything without the namespace prefixes using namespace dsmr; diff --git a/src/dsmr/crc16.h b/src/dsmr2/crc16.h similarity index 100% rename from src/dsmr/crc16.h rename to src/dsmr2/crc16.h diff --git a/src/dsmr2/fields2.cpp b/src/dsmr2/fields2.cpp new file mode 100644 index 0000000..cc7b54c --- /dev/null +++ b/src/dsmr2/fields2.cpp @@ -0,0 +1,333 @@ +/** + * Arduino DSMR parser. + * + * This software is licensed under the MIT License. + * + * Copyright (c) 2015 Matthijs Kooijman + * + *------------------------------------------------------------------------------ + * Changed by Willem Aandewiel + * In the original library it is assumed that the Mbus GAS meter is + * always connected to MBUS_ID 1. But this is wrong. Mostly on + * an initial installation the GAS meter is at MBUS_ID 1 but if an other + * meter is installed it is connected to the first free MBUS_ID. + * So you cannot make any assumption about what mbus is connected to + * which MBUS_ID. Therfore it is also not possible to check the units + * on the basis of the MBUS_ID. It can be anything. + * My assumption is that the device_type of a GAS meter is always "3" + * and that of, f.i. a water meter is always "5". + * I hope I'm right but have not been able to verify this with the + * original documenation. + *------------------------------------------------------------------------------ + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + * Field parsing functions + */ + +#include "fields2.h" + + +using namespace dsmr; +using namespace dsmr::fields; + +// Since C++11 it is possible to define the initial values for static +// const members in the class declaration, but if their address is +// taken, they still need a normal definition somewhere (to allocate +// storage). +constexpr char units::none[]; +constexpr char units::kWh[]; +constexpr char units::Wh[]; +constexpr char units::kW[]; +constexpr char units::W[]; +constexpr char units::V[]; +constexpr char units::mV[]; +constexpr char units::A[]; +constexpr char units::mA[]; +constexpr char units::m3[]; +constexpr char units::dm3[]; +constexpr char units::GJ[]; +constexpr char units::MJ[]; + +/* + removed 3e lines as of https://github.com/matthijskooijman/arduino-dsmr/issues/36 +*/ + +constexpr ObisId identification::id; +constexpr char identification::name_progmem[]; +//constexpr const __FlashStringHelper *identification::name; + +constexpr ObisId p1_version::id; +constexpr char p1_version::name_progmem[]; +//constexpr const __FlashStringHelper *p1_version::name; + +constexpr ObisId p1_version_be::id; +constexpr char p1_version_be::name_progmem[]; +//constexpr const __FlashStringHelper *p1_version_be::name; + +constexpr ObisId timestamp::id; +constexpr char timestamp::name_progmem[]; +//constexpr const __FlashStringHelper *timestamp::name; + +constexpr ObisId equipment_id::id; +constexpr char equipment_id::name_progmem[]; +//constexpr const __FlashStringHelper *equipment_id::name; + +constexpr ObisId energy_delivered_tariff1::id; +constexpr char energy_delivered_tariff1::name_progmem[]; +//constexpr const __FlashStringHelper *energy_delivered_tariff1::name; + +constexpr ObisId energy_delivered_tariff2::id; +constexpr char energy_delivered_tariff2::name_progmem[]; +//constexpr const __FlashStringHelper *energy_delivered_tariff2::name; + +constexpr ObisId energy_returned_tariff1::id; +constexpr char energy_returned_tariff1::name_progmem[]; +//constexpr const __FlashStringHelper *energy_returned_tariff1::name; + +constexpr ObisId energy_returned_tariff2::id; +constexpr char energy_returned_tariff2::name_progmem[]; +//constexpr const __FlashStringHelper *energy_returned_tariff2::name; + +constexpr ObisId electricity_tariff::id; +constexpr char electricity_tariff::name_progmem[]; +//constexpr const __FlashStringHelper *electricity_tariff::name; + +constexpr ObisId power_delivered::id; +constexpr char power_delivered::name_progmem[]; +//constexpr const __FlashStringHelper *power_delivered::name; + +constexpr ObisId power_returned::id; +constexpr char power_returned::name_progmem[]; +//constexpr const __FlashStringHelper *power_returned::name; + +constexpr ObisId electricity_threshold::id; +constexpr char electricity_threshold::name_progmem[]; +//constexpr const __FlashStringHelper *electricity_threshold::name; + +constexpr ObisId electricity_switch_position::id; +constexpr char electricity_switch_position::name_progmem[]; +//constexpr const __FlashStringHelper *electricity_switch_position::name; + +constexpr ObisId electricity_failures::id; +constexpr char electricity_failures::name_progmem[]; +//constexpr const __FlashStringHelper *electricity_failures::name; + +constexpr ObisId electricity_long_failures::id; +constexpr char electricity_long_failures::name_progmem[]; +//constexpr const __FlashStringHelper *electricity_long_failures::name; + +constexpr ObisId electricity_failure_log::id; +constexpr char electricity_failure_log::name_progmem[]; +//constexpr const __FlashStringHelper *electricity_failure_log::name; + +constexpr ObisId electricity_sags_l1::id; +constexpr char electricity_sags_l1::name_progmem[]; +//constexpr const __FlashStringHelper *electricity_sags_l1::name; + +constexpr ObisId electricity_sags_l2::id; +constexpr char electricity_sags_l2::name_progmem[]; +//constexpr const __FlashStringHelper *electricity_sags_l2::name; + +constexpr ObisId electricity_sags_l3::id; +constexpr char electricity_sags_l3::name_progmem[]; +//constexpr const __FlashStringHelper *electricity_sags_l3::name; + +constexpr ObisId electricity_swells_l1::id; +constexpr char electricity_swells_l1::name_progmem[]; +//constexpr const __FlashStringHelper *electricity_swells_l1::name; + +constexpr ObisId electricity_swells_l2::id; +constexpr char electricity_swells_l2::name_progmem[]; +//constexpr const __FlashStringHelper *electricity_swells_l2::name; + +constexpr ObisId electricity_swells_l3::id; +constexpr char electricity_swells_l3::name_progmem[]; +//constexpr const __FlashStringHelper *electricity_swells_l3::name; + +constexpr ObisId message_short::id; +constexpr char message_short::name_progmem[]; +//constexpr const __FlashStringHelper *message_short::name; + +constexpr ObisId message_long::id; +constexpr char message_long::name_progmem[]; +//constexpr const __FlashStringHelper *message_long::name; + +constexpr ObisId voltage_l1::id; +constexpr char voltage_l1::name_progmem[]; +//constexpr const __FlashStringHelper *voltage_l1::name; + +constexpr ObisId voltage_l2::id; +constexpr char voltage_l2::name_progmem[]; +//constexpr const __FlashStringHelper *voltage_l2::name; + +constexpr ObisId voltage_l3::id; +constexpr char voltage_l3::name_progmem[]; +//constexpr const __FlashStringHelper *voltage_l3::name; + +constexpr ObisId current_l1::id; +constexpr char current_l1::name_progmem[]; +//constexpr const __FlashStringHelper *current_l1::name; + +constexpr ObisId current_l2::id; +constexpr char current_l2::name_progmem[]; +//constexpr const __FlashStringHelper *current_l2::name; + +constexpr ObisId current_l3::id; +constexpr char current_l3::name_progmem[]; +//constexpr const __FlashStringHelper *current_l3::name; + +constexpr ObisId power_delivered_l1::id; +constexpr char power_delivered_l1::name_progmem[]; +//constexpr const __FlashStringHelper *power_delivered_l1::name; + +constexpr ObisId power_delivered_l2::id; +constexpr char power_delivered_l2::name_progmem[]; +//constexpr const __FlashStringHelper *power_delivered_l2::name; + +constexpr ObisId power_delivered_l3::id; +constexpr char power_delivered_l3::name_progmem[]; +//constexpr const __FlashStringHelper *power_delivered_l3::name; + +constexpr ObisId power_returned_l1::id; +constexpr char power_returned_l1::name_progmem[]; +//constexpr const __FlashStringHelper *power_returned_l1::name; + +constexpr ObisId power_returned_l2::id; +constexpr char power_returned_l2::name_progmem[]; +//constexpr const __FlashStringHelper *power_returned_l2::name; + +constexpr ObisId power_returned_l3::id; +constexpr char power_returned_l3::name_progmem[]; +//constexpr const __FlashStringHelper *power_returned_l3::name; + +constexpr ObisId mbus1_device_type::id; +constexpr char mbus1_device_type::name_progmem[]; +//constexpr const __FlashStringHelper *mbus1_device_type::name; + +constexpr ObisId mbus1_equipment_id_tc::id; +constexpr char mbus1_equipment_id_tc::name_progmem[]; +//constexpr const __FlashStringHelper *mbus1_equipment_id_tc::name; + +constexpr ObisId mbus1_equipment_id_ntc::id; +constexpr char mbus1_equipment_id_ntc::name_progmem[]; +//constexpr const __FlashStringHelper *mbus1_equipment_id_ntc::name; + +constexpr ObisId mbus1_valve_position::id; +constexpr char mbus1_valve_position::name_progmem[]; +//constexpr const __FlashStringHelper *mbus1_valve_position::name; + +constexpr ObisId mbus1_delivered::id; +constexpr char mbus1_delivered::name_progmem[]; +//constexpr const __FlashStringHelper *mbus1_delivered::name; + +constexpr ObisId mbus1_delivered_ntc::id; +constexpr char mbus1_delivered_ntc::name_progmem[]; +//constexpr const __FlashStringHelper *mbus1_delivered_ntc::name; + +constexpr ObisId mbus1_delivered_dbl::id; +constexpr char mbus1_delivered_dbl::name_progmem[]; +//constexpr const __FlashStringHelper *mbus1_delivered_dbl::name; + +constexpr ObisId mbus2_device_type::id; +constexpr char mbus2_device_type::name_progmem[]; +//constexpr const __FlashStringHelper *mbus2_device_type::name; + +constexpr ObisId mbus2_equipment_id_tc::id; +constexpr char mbus2_equipment_id_tc::name_progmem[]; +//constexpr const __FlashStringHelper *mbus2_equipment_id_tc::name; + +constexpr ObisId mbus2_equipment_id_ntc::id; +constexpr char mbus2_equipment_id_ntc::name_progmem[]; +//constexpr const __FlashStringHelper *mbus2_equipment_id_ntc::name; + +constexpr ObisId mbus2_delivered_dbl::id; +constexpr char mbus2_delivered_dbl::name_progmem[]; +//constexpr const __FlashStringHelper *mbus2_delivered_dbl::name; + +constexpr ObisId mbus2_valve_position::id; +constexpr char mbus2_valve_position::name_progmem[]; +//constexpr const __FlashStringHelper *mbus2_valve_position::name; + +constexpr ObisId mbus2_delivered::id; +constexpr char mbus2_delivered::name_progmem[]; +//constexpr const __FlashStringHelper *mbus2_delivered::name; + +constexpr ObisId mbus2_delivered_ntc::id; +constexpr char mbus2_delivered_ntc::name_progmem[]; +//constexpr const __FlashStringHelper *mbus2_delivered_ntc::name; + +constexpr ObisId mbus3_device_type::id; +constexpr char mbus3_device_type::name_progmem[]; +//constexpr const __FlashStringHelper *mbus3_device_type::name; + +constexpr ObisId mbus3_equipment_id_tc::id; +constexpr char mbus3_equipment_id_tc::name_progmem[]; +//constexpr const __FlashStringHelper *mbus3_equipment_id_tc::name; + +constexpr ObisId mbus3_equipment_id_ntc::id; +constexpr char mbus3_equipment_id_ntc::name_progmem[]; +//constexpr const __FlashStringHelper *mbus3_equipment_id_ntc::name; + +constexpr ObisId mbus3_valve_position::id; +constexpr char mbus3_valve_position::name_progmem[]; +//constexpr const __FlashStringHelper *mbus3_valve_position::name; + +constexpr ObisId mbus3_delivered::id; +constexpr char mbus3_delivered::name_progmem[]; +//constexpr const __FlashStringHelper *mbus3_delivered::name; + +constexpr ObisId mbus3_delivered_ntc::id; +constexpr char mbus3_delivered_ntc::name_progmem[]; +//constexpr const __FlashStringHelper *mbus3_delivered_ntc::name; + +constexpr ObisId mbus3_delivered_dbl::id; +constexpr char mbus3_delivered_dbl::name_progmem[]; +//constexpr const __FlashStringHelper *mbus3_delivered_dbl::name; + +constexpr ObisId mbus4_device_type::id; +constexpr char mbus4_device_type::name_progmem[]; +//constexpr const __FlashStringHelper *mbus4_device_type::name; + +constexpr ObisId mbus4_equipment_id_tc::id; +constexpr char mbus4_equipment_id_tc::name_progmem[]; +//constexpr const __FlashStringHelper *mbus4_equipment_id_tc::name; + +constexpr ObisId mbus4_equipment_id_ntc::id; +constexpr char mbus4_equipment_id_ntc::name_progmem[]; +//constexpr const __FlashStringHelper *mbus4_equipment_id_ntc::name; + +constexpr ObisId mbus4_valve_position::id; +constexpr char mbus4_valve_position::name_progmem[]; +//constexpr const __FlashStringHelper *mbus4_valve_position::name; + +constexpr ObisId mbus4_delivered::id; +constexpr char mbus4_delivered::name_progmem[]; +//constexpr const __FlashStringHelper *mbus4_delivered::name; + +constexpr ObisId mbus4_delivered_ntc::id; +constexpr char mbus4_delivered_ntc::name_progmem[]; +//constexpr const __FlashStringHelper *mbus4_delivered_ntc::name; + +constexpr ObisId mbus4_delivered_dbl::id; +constexpr char mbus4_delivered_dbl::name_progmem[]; +//constexpr const __FlashStringHelper *mbus4_delivered_dbl::name; + diff --git a/src/dsmr/fields.h b/src/dsmr2/fields2.h similarity index 59% rename from src/dsmr/fields.h rename to src/dsmr2/fields2.h index a6a10f5..3a22249 100644 --- a/src/dsmr/fields.h +++ b/src/dsmr2/fields2.h @@ -5,6 +5,21 @@ * * Copyright (c) 2015 Matthijs Kooijman * + *------------------------------------------------------------------------------ + * Changed by Willem Aandewiel + * In the original library it is assumed that the Mbus GAS meter is + * always connected to MBUS_ID 1. But this is wrong. Mostly on + * an initial installation the GAS meter is at MBUS_ID 1 but if an other + * meter is installed it is connected to the first free MBUS_ID. + * So you cannot make any assumption about what mbus is connected to + * which MBUS_ID. Therfore it is also not possible to check the units + * on the basis of the MBUS_ID. It can be anything. + * My assumption is that the device_type of a GAS meter is always "3" + * and that of, f.i. a water meter is always "5". + * I hope I'm right but have not been able to verify this with the + * original documenation. + *------------------------------------------------------------------------------ + * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -32,7 +47,7 @@ #define DSMR_INCLUDE_FIELDS_H #include "util.h" -#include "parser.h" +#include "parser2.h" namespace dsmr { @@ -122,6 +137,74 @@ struct TimestampedFixedField : public FixedField { } }; +// Some gas meters follow different specifications and output +// something like this, e.g.: +// 0-1:24.3.0(150623120000)(00)(60)(1)(0-1:24.2.1)(m3) +// (01100.658) +// Note that the output spans two lines +template +struct DoubleLineTimestampedFixedField : public FixedField { + ParseResult parse(const char *str, const char *end) { + // First, parse timestamp + ParseResult res = StringParser::parse_string(12, 12, str, end); + if (res.err) + return res; + + static_cast(this)->val().timestamp = res.result; + + // The timestamp is followed by 3 sets of numerical values, parse them + ParseResult numres = NumParser::parse(0, NULL, res.next, end); + if (numres.err) + return numres; + + numres = NumParser::parse(0, NULL, numres.next, end); + if (res.err) + return numres; + + numres = NumParser::parse(0, NULL, numres.next, end); + if (numres.err) + return numres; + + // Afther the numerical values, another ObisID is presented, + // skip the first ')' + ParseResult idres = ObisIdParser::parse(numres.next + 1, end); + if (idres.err) + return idres; + + // The last item on the line is the unit, again skip the closing ')' + size_t unit_size = strnlen(_unit, 3); + ParseResult unitres = StringParser::parse_string(unit_size, unit_size, idres.next + 1, end); + if (unitres.err) + return unitres; + + // Verify the unit. + const char *unit = unitres.result.c_str(); + if(memcmp(unit, _unit, unit_size) != 0) { + return unitres.fail((const __FlashStringHelper*)INVALID_UNIT, idres.next + 1); + } + + // Now move to the next line. + const char *start = unitres.next; + if (*start == '\r') + ++start; + + if (*start == '\n') + ++start; + + // Since the start line is moved, also move the end line. + const char *newend = start; + while (*newend != '\r' && *newend != '\n' && newend != end) + ++newend; + + // Finally parse the value. + numres = NumParser::parse(3, NULL, start, newend); + if (!numres.err) + static_cast(this)->val()._value = numres.result; + + return numres; + } +}; + // A integer number is just represented as an integer. template struct IntField : ParsedField { @@ -171,10 +254,25 @@ struct units { static constexpr char MJ[] = "MJ"; }; -const uint8_t GAS_MBUS_ID = 1; -const uint8_t WATER_MBUS_ID = 2; -const uint8_t THERMAL_MBUS_ID = 3; -const uint8_t SLAVE_MBUS_ID = 4; +/* + added as of https://github.com/matthijskooijman/arduino-dsmr/issues/36 +*/ +template +struct NameConverter { + public: + operator const __FlashStringHelper*() const { return reinterpret_cast(&FieldT::name_progmem); } +}; + +/* + changed as of https://github.com/matthijskooijman/arduino-dsmr/issues/36 + + changed: + static constexpr const __FlashStringHelper *name = reinterpret_cast(&name_progmem); \ + to: + static constexpr NameConverter name = {}; \ + + as by commit "29b1d33fb4397a779b9647f8a3e29ecf9dee116e" +*/ #define DEFINE_FIELD(fieldname, value_t, obis, field_t, field_args...) \ struct fieldname : field_t { \ @@ -182,7 +280,7 @@ const uint8_t SLAVE_MBUS_ID = 4; bool fieldname ## _present = false; \ static constexpr ObisId id = obis; \ static constexpr char name_progmem[] DSMR_PROGMEM = #fieldname; \ - static constexpr const __FlashStringHelper *name = reinterpret_cast(&name_progmem); \ + static constexpr NameConverter name = {}; \ value_t& val() { return fieldname; } \ bool& present() { return fieldname ## _present; } \ } @@ -193,6 +291,8 @@ DEFINE_FIELD(identification, String, ObisId(255, 255, 255, 255, 255, 255), RawFi /* Version information for P1 output */ DEFINE_FIELD(p1_version, String, ObisId(1, 3, 0, 2, 8), StringField, 2, 2); +/* Version information for P1 output (Belgium)*/ +DEFINE_FIELD(p1_version_be, String, ObisId(0, 0, 96, 1, 4), StringField, 0, 5); /* Date-time stamp of the P1 message */ DEFINE_FIELD(timestamp, String, ObisId(0, 0, 1, 0, 0), TimestampField); @@ -268,11 +368,17 @@ DEFINE_FIELD(voltage_l2, FixedValue, ObisId(1, 0, 52, 7, 0), FixedField, units:: DEFINE_FIELD(voltage_l3, FixedValue, ObisId(1, 0, 72, 7, 0), FixedField, units::V, units::mV); /* Instantaneous current L1 in A resolution */ -DEFINE_FIELD(current_l1, uint16_t, ObisId(1, 0, 31, 7, 0), IntField, units::A); +//DEFINE_FIELD(current_l1, uint16_t, ObisId(1, 0, 31, 7, 0), IntField, units::A); +/* Instantaneous current L1 in mA resolution */ +DEFINE_FIELD(current_l1, FixedValue, ObisId(1, 0, 31, 7, 0), FixedField, units::A, units::mA); /* Instantaneous current L2 in A resolution */ -DEFINE_FIELD(current_l2, uint16_t, ObisId(1, 0, 51, 7, 0), IntField, units::A); +//DEFINE_FIELD(current_l2, uint16_t, ObisId(1, 0, 51, 7, 0), IntField, units::A); +/* Instantaneous current L2 in mA resolution */ +DEFINE_FIELD(current_l2, FixedValue, ObisId(1, 0, 51, 7, 0), FixedField, units::A, units::mA); /* Instantaneous current L3 in A resolution */ -DEFINE_FIELD(current_l3, uint16_t, ObisId(1, 0, 71, 7, 0), IntField, units::A); +//DEFINE_FIELD(current_l3, uint16_t, ObisId(1, 0, 71, 7, 0), IntField, units::A); +/* Instantaneous current L3 in mA resolution */ +DEFINE_FIELD(current_l3, FixedValue, ObisId(1, 0, 71, 7, 0), FixedField, units::A, units::mA); /* Instantaneous active power L1 (+P) in W resolution */ DEFINE_FIELD(power_delivered_l1, FixedValue, ObisId(1, 0, 21, 7, 0), FixedField, units::kW, units::W); @@ -290,60 +396,79 @@ DEFINE_FIELD(power_returned_l3, FixedValue, ObisId(1, 0, 62, 7, 0), FixedField, /* Device-Type */ -DEFINE_FIELD(gas_device_type, uint16_t, ObisId(0, GAS_MBUS_ID, 24, 1, 0), IntField, units::none); - -/* Equipment identifier (Gas) */ -DEFINE_FIELD(gas_equipment_id, String, ObisId(0, GAS_MBUS_ID, 96, 1, 0), StringField, 0, 96); - -/* Valve position Gas (on/off/released) (Note: Removed in 4.0.7 / 4.2.2 / 5.0). */ -DEFINE_FIELD(gas_valve_position, uint8_t, ObisId(0, GAS_MBUS_ID, 24, 4, 0), IntField, units::none); - -/* Last 5-minute value (temperature converted), gas delivered to client +DEFINE_FIELD(mbus1_device_type, uint16_t, ObisId(0, 1, 24, 1, 0), IntField, units::none); +/* Equipment identifier (temperature corrected) */ +DEFINE_FIELD(mbus1_equipment_id_tc, String, ObisId(0, 1, 96, 1, 0), StringField, 0, 96); +/* Equipment identifier (Not Temp. Corrected) */ +DEFINE_FIELD(mbus1_equipment_id_ntc, String, ObisId(0, 1, 96, 1, 1), StringField, 0, 96); +/* Valve position (on/off/released) (Note: Removed in 4.0.7 / 4.2.2 / 5.0). */ +DEFINE_FIELD(mbus1_valve_position, uint8_t, ObisId(0, 1, 24, 4, 0), IntField, units::none); +/* Last 5-minute value (temperature converted), delivered to client * in m3, including decimal values and capture time (Note: 4.x spec has * "hourly value") */ -DEFINE_FIELD(gas_delivered, TimestampedFixedValue, ObisId(0, GAS_MBUS_ID, 24, 2, 1), TimestampedFixedField, units::m3, units::dm3); +DEFINE_FIELD(mbus1_delivered, TimestampedFixedValue, ObisId(0, 1, 24, 2, 1), TimestampedFixedField, units::m3, units::dm3); +// OBIS: Last value of ‘not temperature corrected’ volume, including decimal values and capture time +DEFINE_FIELD(mbus1_delivered_ntc, TimestampedFixedValue, ObisId(0, 1, 24, 2, 3), TimestampedFixedField, units::m3, units::dm3); +/* Last hourly value (temperature compensated or not, depending on the display + * setting of the device), volume in m3, including decimal values + * double line */ +DEFINE_FIELD(mbus1_delivered_dbl, TimestampedFixedValue, ObisId(0, 1, 24, 3, 0), DoubleLineTimestampedFixedField, units::m3, units::dm3); /* Device-Type */ -DEFINE_FIELD(thermal_device_type, uint16_t, ObisId(0, THERMAL_MBUS_ID, 24, 1, 0), IntField, units::none); - -/* Equipment identifier (Thermal: heat or cold) */ -DEFINE_FIELD(thermal_equipment_id, String, ObisId(0, THERMAL_MBUS_ID, 96, 1, 0), StringField, 0, 96); - +DEFINE_FIELD(mbus2_device_type, uint16_t, ObisId(0, 2, 24, 1, 0), IntField, units::none); +/* Equipment identifier (temperature corrected) */ +DEFINE_FIELD(mbus2_equipment_id_tc, String, ObisId(0, 2, 96, 1, 0), StringField, 0, 96); +/* Equipment identifier (Not Temp. Corrected) */ +DEFINE_FIELD(mbus2_equipment_id_ntc, String, ObisId(0, 2, 96, 1, 1), StringField, 0, 96); /* Valve position (on/off/released) (Note: Removed in 4.0.7 / 4.2.2 / 5.0). */ -DEFINE_FIELD(thermal_valve_position, uint8_t, ObisId(0, THERMAL_MBUS_ID, 24, 4, 0), IntField, units::none); - -/* Last 5-minute Meter reading Heat or Cold in 0,01 GJ and capture time +DEFINE_FIELD(mbus2_valve_position, uint8_t, ObisId(0, 2, 24, 4, 0), IntField, units::none); +/* Last 5-minute Meter reading and capture time * (Note: 4.x spec has "hourly meter reading") */ -DEFINE_FIELD(thermal_delivered, TimestampedFixedValue, ObisId(0, THERMAL_MBUS_ID, 24, 2, 1), TimestampedFixedField, units::GJ, units::MJ); +DEFINE_FIELD(mbus2_delivered, TimestampedFixedValue, ObisId(0, 2, 24, 2, 1), TimestampedFixedField, units::GJ, units::MJ); +// OBIS: Last value of ‘not temperature corrected’ volume, including decimal values and capture time +DEFINE_FIELD(mbus2_delivered_ntc, TimestampedFixedValue, ObisId(0, 2, 24, 2, 3), TimestampedFixedField, units::m3, units::dm3); +/* Last hourly value (temperature compensated or not, depending on the display + * setting of the device), volume in m3, including decimal values + * double line */ +DEFINE_FIELD(mbus2_delivered_dbl, TimestampedFixedValue, ObisId(0, 2, 24, 3, 0), DoubleLineTimestampedFixedField, units::m3, units::dm3); /* Device-Type */ -DEFINE_FIELD(water_device_type, uint16_t, ObisId(0, WATER_MBUS_ID, 24, 1, 0), IntField, units::none); - -/* Equipment identifier (Thermal: heat or cold) */ -DEFINE_FIELD(water_equipment_id, String, ObisId(0, WATER_MBUS_ID, 96, 1, 0), StringField, 0, 96); - +DEFINE_FIELD(mbus3_device_type, uint16_t, ObisId(0, 3, 24, 1, 0), IntField, units::none); +/* Equipment identifier (temperature corrected) */ +DEFINE_FIELD(mbus3_equipment_id_tc, String, ObisId(0, 3, 96, 1, 0), StringField, 0, 96); +/* Equipment identifier (Not Temp. Corrected) */ +DEFINE_FIELD(mbus3_equipment_id_ntc, String, ObisId(0, 3, 96, 1, 1), StringField, 0, 96); /* Valve position (on/off/released) (Note: Removed in 4.0.7 / 4.2.2 / 5.0). */ -DEFINE_FIELD(water_valve_position, uint8_t, ObisId(0, WATER_MBUS_ID, 24, 4, 0), IntField, units::none); - -/* Last 5-minute Meter reading in 0,001 m3 and capture time +DEFINE_FIELD(mbus3_valve_position, uint8_t, ObisId(0, 3, 24, 4, 0), IntField, units::none); +/* Last 5-minute Meter reading and capture time * (Note: 4.x spec has "hourly meter reading") */ -DEFINE_FIELD(water_delivered, TimestampedFixedValue, ObisId(0, WATER_MBUS_ID, 24, 2, 1), TimestampedFixedField, units::m3, units::dm3); - +DEFINE_FIELD(mbus3_delivered, TimestampedFixedValue, ObisId(0, 3, 24, 2, 1), TimestampedFixedField, units::m3, units::dm3); +// OBIS: Last value of ‘not temperature corrected’ volume, including decimal values and capture time +DEFINE_FIELD(mbus3_delivered_ntc, TimestampedFixedValue, ObisId(0, 3, 24, 2, 3), TimestampedFixedField, units::m3, units::dm3); +/* Last hourly value (temperature compensated or not, depending on the display + * setting of the device), volume in m3, including decimal values + * double line */ +DEFINE_FIELD(mbus3_delivered_dbl, TimestampedFixedValue, ObisId(0, 3, 24, 3, 0), DoubleLineTimestampedFixedField, units::m3, units::dm3); /* Device-Type */ -DEFINE_FIELD(slave_device_type, uint16_t, ObisId(0, SLAVE_MBUS_ID, 24, 1, 0), IntField, units::none); - -/* Equipment identifier (Thermal: heat or cold) */ -DEFINE_FIELD(slave_equipment_id, String, ObisId(0, SLAVE_MBUS_ID, 96, 1, 0), StringField, 0, 96); - +DEFINE_FIELD(mbus4_device_type, uint16_t, ObisId(0, 4, 24, 1, 0), IntField, units::none); +/* Equipment identifier (temperature corrected) */ +DEFINE_FIELD(mbus4_equipment_id_tc, String, ObisId(0, 4, 96, 1, 0), StringField, 0, 96); +/* Equipment identifier (Not Temp. Corrected) */ +DEFINE_FIELD(mbus4_equipment_id_ntc, String, ObisId(0, 4, 96, 1, 1), StringField, 0, 96); /* Valve position (on/off/released) (Note: Removed in 4.0.7 / 4.2.2 / 5.0). */ -DEFINE_FIELD(slave_valve_position, uint8_t, ObisId(0, SLAVE_MBUS_ID, 24, 4, 0), IntField, units::none); - -/* Last 5-minute Meter reading Heat or Cold and capture time (e.g. slave +DEFINE_FIELD(mbus4_valve_position, uint8_t, ObisId(0, 4, 24, 4, 0), IntField, units::none); +/* Last 5-minute Meter reading and capture time (e.g. mbus * E meter) (Note: 4.x spec has "hourly meter reading") */ -DEFINE_FIELD(slave_delivered, TimestampedFixedValue, ObisId(0, SLAVE_MBUS_ID, 24, 2, 1), TimestampedFixedField, units::m3, units::dm3); +DEFINE_FIELD(mbus4_delivered, TimestampedFixedValue, ObisId(0, 4, 24, 2, 1), TimestampedFixedField, units::m3, units::dm3); +// OBIS: Last value of ‘not temperature corrected’ volume , including decimal values and capture time +DEFINE_FIELD(mbus4_delivered_ntc, TimestampedFixedValue, ObisId(0, 4, 24, 2, 3), TimestampedFixedField, units::m3, units::dm3); +/* Last hourly value (temperature compensated or not, depending on the display + * setting of the device), volume in m3, including decimal values + * double line */ +DEFINE_FIELD(mbus4_delivered_dbl, TimestampedFixedValue, ObisId(0, 4, 24, 3, 0), DoubleLineTimestampedFixedField, units::m3, units::dm3); } // namespace fields diff --git a/src/dsmr/parser.h b/src/dsmr2/parser2.h similarity index 81% rename from src/dsmr/parser.h rename to src/dsmr2/parser2.h index 68805a6..9cf50ea 100644 --- a/src/dsmr/parser.h +++ b/src/dsmr2/parser2.h @@ -5,6 +5,11 @@ * * Copyright (c) 2015 Matthijs Kooijman * + *------------------------------------------------------------------------------ + * Changed by Willem Aandewiel + * - Skip UNIT test (it should test for the unit but not raise an error) + *------------------------------------------------------------------------------ + * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including @@ -187,6 +192,7 @@ struct NumParser { // Parse integer part while(num_end < end && !strchr("*.)", *num_end)) { + delay(0); // yield() if (*num_end < '0' || *num_end > '9') return res.fail((const __FlashStringHelper*)INVALID_NUMBER, num_end); value *= 10; @@ -199,6 +205,7 @@ struct NumParser { ++num_end; while(num_end < end && !strchr("*)", *num_end) && max_decimals--) { + delay(0); // yield() if (*num_end < '0' || *num_end > '9') return res.fail((const __FlashStringHelper*)INVALID_NUMBER, num_end); value *= 10; @@ -216,11 +223,20 @@ struct NumParser { return res.fail(F("Missing unit"), num_end); const char *unit_start = ++num_end; // skip * while(num_end < end && *num_end != ')' && *unit) { + delay(0); // yield() if (*num_end++ != *unit++) - return res.fail((const __FlashStringHelper*)INVALID_UNIT, unit_start); + { + //--AaW accept all unit's + //--AaW don't raise an error + //return res.fail((const __FlashStringHelper*)INVALID_UNIT, unit_start); + } } if (*unit) - return res.fail((const __FlashStringHelper*)INVALID_UNIT, unit_start); + { + //--AaW accept all unit's + //--AaW don't raise an error + //return res.fail((const __FlashStringHelper*)INVALID_UNIT, unit_start); + } } if (num_end >= end || *num_end != ')') @@ -242,6 +258,7 @@ struct ObisIdParser { while (res.next < end) { char c = *res.next; + delay(0); // yield() if (c >= '0' && c <= '9') { uint8_t digit = c - '0'; if (id.v[part] > 25 || (id.v[part] == 25 && digit > 5)) @@ -306,7 +323,7 @@ struct P1Parser { * pointer in the result will indicate the next unprocessed byte. */ template - static ParseResult parse(ParsedData *data, const char *str, size_t n, bool unknown_error = false) { + static ParseResult parse(ParsedData *data, const char *str, size_t n, bool unknown_error = false, bool checksum = true) { ParseResult res; if (!n || str[0] != '/') return res.fail(F("Data should start with /"), str); @@ -316,27 +333,47 @@ struct P1Parser { // Look for ! that terminates the data const char *data_end = data_start; - uint16_t crc = _crc16_update(0, *str); // Include the / in CRC - while (data_end < str + n && *data_end != '!') { - crc = _crc16_update(crc, *data_end); - ++data_end; - } + const char *next = NULL; + if(checksum) { + + uint16_t crc = _crc16_update(0, *str); // Include the / in CRC + while (data_end < str + n && *data_end != '!') { + delay(0); // yield() + crc = _crc16_update(crc, *data_end); + ++data_end; + } - if (data_end >= str + n) - return res.fail(F("No checksum found"), data_end); + if (data_end >= str + n) + return res.fail(F("No checksum found"), data_end); - crc = _crc16_update(crc, *data_end); // Include the ! in CRC + crc = _crc16_update(crc, *data_end); // Include the ! in CRC - ParseResult check_res = CrcParser::parse(data_end + 1, str + n); - if (check_res.err) - return check_res; + ParseResult check_res = CrcParser::parse(data_end + 1, str + n); + if (check_res.err) + return check_res; - // Check CRC - if (check_res.result != crc) - return res.fail(F("Checksum mismatch"), data_end + 1); + // Check CRC + if (check_res.result != crc) + return res.fail(F("Checksum mismatch"), data_end + 1); + next = check_res.next; + } else { + // There's no CRC check, still need to know where the message ends (!) + while (data_end < str + n && *data_end != '!') { + delay(0); // yield() + ++data_end; + } + + // Skip to end-of-line, everything afther that is not yet processed. + next = data_end; + while (next < str + n && *next != '\r' && *next != '\n') { + delay(0); // yield() + ++next; + } + } res = parse_data(data, data_start, data_end, unknown_error); - res.next = check_res.next; + //res.next = check_res.next; + res.next = next; return res; } @@ -346,13 +383,16 @@ struct P1Parser { * checksum. Does not verify the checksum. */ template - static ParseResult parse_data(ParsedData *data, const char *str, const char *end, bool unknown_error = false) { + //--static ParseResult parse_data(ParsedData *data, const char *str, const char *end, bool unknown_error = false) { + static ParseResult parse_data(ParsedData *data, const char *str, const char *end, bool checksum = false) { ParseResult res; + bool unknown_error = false; // Split into lines and parse those const char *line_end = str, *line_start = str; // Parse ID line while (line_end < end) { + delay(0); // yield() if (*line_end == '\r' || *line_end == '\n') { // The first identification line looks like: // XXX5 @@ -379,11 +419,16 @@ struct P1Parser { // Parse data lines while (line_end < end) { - if (*line_end == '\r' || *line_end == '\n') { + delay(0); // yield() + // When a line is skipped line_start > line_end. + // i.e. line_start is already at the next line, line_end is still behind, + // continue iterating over line_end until the next line is found. + if (*line_end == '\r' || *line_end == '\n' && line_start <= line_end) { ParseResult tmp = parse_line(data, line_start, line_end, unknown_error); if (tmp.err) return tmp; - line_start = line_end + 1; + + line_start = tmp.next; } line_end++; } @@ -397,8 +442,9 @@ struct P1Parser { template static ParseResult parse_line(Data *data, const char *line, const char *end, bool unknown_error) { ParseResult res; + if (line == end) - return res; + return res.until(end + 1); ParseResult idres = ObisIdParser::parse(line, end); if (idres.err) @@ -408,15 +454,18 @@ struct P1Parser { if (datares.err) return datares; + // If datares.next > end, a line is skipped. + if(datares.next != idres.next && datares.next > end) + return res.until(datares.next); // If datares.next didn't move at all, there was no parser for // this field, that's ok. But if it did move, but not all the way // to the end, that's an error. - if (datares.next != idres.next && datares.next != end) + else if (datares.next != idres.next && datares.next != end) return res.fail(F("Trailing characters on data line"), datares.next); else if (datares.next == idres.next && unknown_error) return res.fail(F("Unknown field"), line); - return res.until(end); + return res.until(end + 1); } }; diff --git a/src/dsmr/reader.h b/src/dsmr2/reader2.h similarity index 86% rename from src/dsmr/reader.h rename to src/dsmr2/reader2.h index 61b8742..a76ac09 100644 --- a/src/dsmr/reader.h +++ b/src/dsmr2/reader2.h @@ -35,7 +35,7 @@ #include #include "crc16.h" -#include "parser.h" +#include "parser2.h" namespace dsmr { @@ -69,10 +69,21 @@ class P1Reader { * output, the Stream is assumed to be already set up (e.g. baud * rate configured). */ - P1Reader(Stream *stream, uint8_t req_pin) + P1Reader(Stream *stream, uint8_t req_pin, bool checksum = true) : stream(stream), req_pin(req_pin), once(false), state(State::DISABLED_STATE) { pinMode(req_pin, OUTPUT); digitalWrite(req_pin, LOW); + this->checksum = checksum; + } + + /** + * Enable checksum test. + * @param checksum When + * true: calculate and check checksum + * false: there is no checksum, so skip it + */ + void doChecksum(bool checksum) { + this->checksum = checksum; } /** @@ -110,7 +121,7 @@ class P1Reader { bool available() { return this->_available; } - + /** * Check for new data to read. Should be called regularly, such as * once every loop. Returns true if a complete message is available @@ -121,27 +132,37 @@ class P1Reader { if (state == State::CHECKSUM_STATE) { // Let the Stream buffer the CRC bytes. Convert to size_t to // prevent unsigned vs signed comparison - if ((size_t)this->stream->available() < CrcParser::CRC_LEN) - return false; - + if (this->checksum) { + if ((size_t)this->stream->available() < CrcParser::CRC_LEN) + return false; + } + char buf[CrcParser::CRC_LEN]; for (uint8_t i = 0; i < CrcParser::CRC_LEN; ++i) + { + delay(0); // yield() buf[i] = this->stream->read(); - + } ParseResult crc = CrcParser::parse(buf, buf + lengthof(buf)); // Prepare for next message state = State::WAITING_STATE; - if (!crc.err && crc.result == this->crc) { - // Message complete, checksum correct + if (this->checksum) { + if (!crc.err && crc.result == this->crc) { + // Message complete, checksum correct + this->_available = true; + } + } + else + { this->_available = true; + } + if (once) + this->disable(); - if (once) - this->disable(); + return true; - return true; - } } else { // For other states, read bytes one by one int c = this->stream->read(); @@ -177,6 +198,7 @@ class P1Reader { break; } } + delay(0); // force yield() } return false; } @@ -198,7 +220,8 @@ class P1Reader { * message is appended to that string. */ template - bool parse(ParsedData *data, String *err) { + //--bool parse(ParsedData *data, String *err, bool unknown_error = false) { + bool parse(ParsedData *data, String *err, bool checksum = false) { const char *str = buffer.c_str(), *end = buffer.c_str() + buffer.length(); ParseResult res = P1Parser::parse_data(data, str, end); @@ -231,7 +254,7 @@ class P1Reader { CHECKSUM_STATE, }; bool _available; - bool once; + bool once, checksum; State state; String buffer; uint16_t crc; diff --git a/src/dsmr/util.h b/src/dsmr2/util.h similarity index 100% rename from src/dsmr/util.h rename to src/dsmr2/util.h