first commit
This commit is contained in:
commit
401777e1ee
|
@ -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
|
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.
Binary file not shown.
After Width: | Height: | Size: 92 KiB |
Binary file not shown.
Binary file not shown.
After Width: | Height: | Size: 965 KiB |
Loading…
Reference in New Issue