first commit

This commit is contained in:
Maurice Makaay 2020-12-24 13:19:37 +01:00
commit 401777e1ee
9 changed files with 283 additions and 0 deletions

282
P1Meter/P1Meter.ino Normal file
View File

@ -0,0 +1,282 @@
// Some useful websites that I used for writing this sketch:
//
// DSMR parser library: https://github.com/matthijskooijman/arduino-dsmr
// MQTT example code: https://www.instructables.com/MQTT-Bare-Minimum-Sketch/
// Circuit that inspired mine: https://klushok.etv.tudelft.nl/projects/view?id=8
#include <Arduino.h>
#include <PubSubClient.h>
#include <ESP8266WiFi.h>
#include "dsmr.h"
// -----------------------------------------------------------------------
// Update these for your configuration
#define wifi_ssid "****"
#define wifi_password "*****"
#define mqtt_server "*****"
#define mqtt_port 1883
#define mqtt_user "*****"
#define mqtt_password "*****"
// When defined, no serial data are read, but instead the stub
// data from below is used. This is useful to test the WiFi and
// MQTT connection, while not being connected to an actual
// smart meter.
//#define TEST_DATA
// -----------------------------------------------------------------------
#ifdef TEST_DATA
const char test_msg[] =
"/KFM5KAIFA-METER\r\n"
"\r\n"
"1-3:0.2.8(40)\r\n"
"0-0:1.0.0(150117185916W)\r\n"
"0-0:96.1.1(0000000000000000000000000000000000)\r\n"
"1-0:1.8.1(000671.578*kWh)\r\n"
"1-0:1.8.2(000842.472*kWh)\r\n"
"1-0:2.8.1(000000.000*kWh)\r\n"
"1-0:2.8.2(000000.000*kWh)\r\n"
"0-0:96.14.0(0001)\r\n"
"1-0:1.7.0(00.333*kW)\r\n"
"1-0:2.7.0(00.000*kW)\r\n"
"0-0:17.0.0(999.9*kW)\r\n"
"0-0:96.3.10(1)\r\n"
"0-0:96.7.21(00008)\r\n"
"0-0:96.7.9(00007)\r\n"
"1-0:99.97.0(1)(0-0:96.7.19)(000101000001W)(2147483647*s)\r\n"
"1-0:32.32.0(00000)\r\n"
"1-0:32.36.0(00000)\r\n"
"0-0:96.13.1()\r\n"
"0-0:96.13.0()\r\n"
"1-0:31.7.0(001*A)\r\n"
"1-0:21.7.0(00.332*kW)\r\n"
"1-0:22.7.0(00.000*kW)\r\n"
"0-1:24.1.0(003)\r\n"
"0-1:96.1.0(0000000000000000000000000000000000)\r\n"
"0-1:24.2.1(150117180000W)(00473.789*m3)\r\n"
"0-1:24.4.0(1)\r\n"
"!6F4A\r\n";
#endif
// All possible items that can be received from the P1 port,
// depending on the DSMR version used. This sketch tries to
// fetch all of them, and publishes the items that actually
// were received. When desired, you can safely remove unneeded
// items from this definition by simply deleting their lines.
using MeterData = 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
>;
// My circuit has the request PIN always HIGH (because it is
// directly connected to the 5V power supply of the smart meter.
// PIN 3 (GPIO2) is not connected, so I'll feed that one as a
// stub here. Maybe it's an idea to hook up a LED + resistor
// to this pin for some visual feedback, making it an 'I am
// reading data' indicator.
P1Reader reader(&Serial, 3);
WiFiClient wifiClient;
PubSubClient pubsubClient;
void setup() {
Serial.begin(115200, SERIAL_8N1);
delay(1000); // Wait a bit for the serial port to wake up.
Serial.println("Setting up device ...");
setup_wifi();
setup_pubsub();
Serial.println("Starting the main loop ...");
}
void setup_wifi() {
// We start by connecting to a WiFi network
Serial.print("Setup connection to WiFi network '");
Serial.print(wifi_ssid);
Serial.print("' ");
WiFi.begin(wifi_ssid, wifi_password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("");
Serial.println("WiFi connected");
Serial.print("IP address: ");
Serial.println(WiFi.localIP());
}
void setup_pubsub() {
Serial.println("Setup MQTT broker connection");
pubsubClient.setClient(wifiClient);
pubsubClient.setServer(mqtt_server, mqtt_port);
}
void reconnect() {
// Loop until we're reconnected
while (!pubsubClient.connected()) {
Serial.print("Attempting MQTT connection ... ");
if (pubsubClient.connect("ESP8266Client", mqtt_user, mqtt_password)) {
Serial.println("connected");
} else {
Serial.print("failed, rc=");
Serial.print(pubsubClient.state());
Serial.println(", try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}
struct Printer {
template<typename Item>
void apply(Item &i) {
if (i.present()) {
if (!pubsubClient.connected()) {
reconnect();
}
String key = String("smartmeter/");
key.concat(Item::name);
String value = String(i.val());
value.concat(" ");
value.concat(Item::unit());
Serial.print("> ");
Serial.print(key);
Serial.print(" = ");
Serial.println(value);
pubsubClient.publish(key.c_str(), value.c_str(), true);
}
}
};
// Implementation of the main loop using test data.
#ifdef TEST_DATA
void loop() {
MeterData data;
String err;
Serial.println("Parsing test data");
ParseResult<void> res = P1Parser::parse(&data, test_msg, lengthof(test_msg));
if (res.err) {
Serial.println(res.fullError(test_msg, test_msg + lengthof(test_msg)));
if (!pubsubClient.connected()) {
reconnect();
}
if (pubsubClient.connected()) {
pubsubClient.publish("smartmeter/error", res.fullError(test_msg, test_msg + lengthof(test_msg)).c_str(), true);
}
// } else if (!data.all_present()) {
// Serial.println("Some fields are missing");
} else {
data.applyEach(Printer());
// Succesfully parsed, print results:
Serial.println(data.identification);
Serial.print(data.power_delivered.int_val());
Serial.println("W");
}
delay(10000);
}
// Implementation of the main loop using serial P1 data.
# else
long waitLoop = 0;
void loop() {
// Builds a complete telegram.
reader.loop();
// Every 4 seconds, start a new telegram reading operation.
// The smart meter will provide a telegram every 10 seconds.
// By restarting every 4 seconds, we know that we will be
// in sync with the P1 output at some point. A bit crude, but
// I don't have a real RTS request line in my current
// hardware design.
if (millis() > waitLoop) {
Serial.println("Start new telegram read operation");
waitLoop = millis() + 4000;
reader.enable(true);
}
// True when a new telegram is read.
if (reader.available()) {
MeterData data;
String err;
Serial.print("Parsing data from P1 ... ");
if (reader.parse(&data, &err)) {
Serial.println("OK");
data.applyEach(Printer());
} else {
// When the parser fails, we log the error to console
// and to an MQTT topic.
Serial.println("ERROR");
Serial.println(err);
if (!pubsubClient.connected()) {
reconnect();
}
if (pubsubClient.connected()) {
pubsubClient.publish("smartmeter/error", err.c_str(), true);
}
}
}
}
#endif // TEST OR PRODUCTION

1
README.md Normal file
View File

@ -0,0 +1 @@
# Smart Meter to MQTT, using an ESP-01

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

BIN
doc/ESP-01_pinout.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

BIN
doc/Slimme Meter specs.pdf Normal file

Binary file not shown.

BIN
doc/circuit.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 KiB