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