New meter config

This commit is contained in:
Gunnar Skjold
2021-11-06 19:30:58 +01:00
parent 8e9da8f255
commit 6d26102b8e
27 changed files with 156 additions and 2370 deletions

View File

@@ -1,197 +0,0 @@
/*
* Simple sketch to simulate reading data from a Kamstrup
* AMS Meter.
*
* Created 24. October 2017 by Roar Fredriksen
* Modified 06. November 2017 by Ruben Andreassen
*/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <HanReader.h>
#include <Kamstrup.h>
// The HAN Port reader
HanReader hanReader;
// WiFi and MQTT endpoints
const char* ssid = "ssid";
const char* password = "password";
const char* mqtt_server = "ip or dns";
const char* mqtt_topic = "sensors/out/espams";
const char* device_name = "espams";
bool enableDebug = false;
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
//setupDebugPort(); //Comment out this line if you dont need debugging on Serial1
setupWiFi();
setupMqtt();
// initialize the HanReader
// (passing no han port, as we are feeding data manually, but provide Serial for debugging)
if (enableDebug) {
hanReader.setup(&Serial, 2400, SERIAL_8N1, &Serial1);
} else {
hanReader.setup(&Serial, 2400, SERIAL_8N1, NULL);
}
}
void setupMqtt()
{
client.setServer(mqtt_server, 1883);
}
void setupDebugPort()
{
enableDebug = true;
// Initialize the Serial port for debugging
Serial1.begin(115200);
while (!Serial1) {}
Serial1.setDebugOutput(true);
Serial1.println("Serial1");
Serial1.println("Serial debugging port initialized");
}
void setupWiFi()
{
// Initialize wifi
if (enableDebug) {
Serial1.print("Connecting to ");
Serial1.println(ssid);
}
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
if (enableDebug) Serial1.print(".");
}
if (enableDebug) {
Serial1.println("");
Serial1.println("WiFi connected");
Serial1.println("IP address: ");
Serial1.println(WiFi.localIP());
}
}
void loop() {
loopMqtt();
// Read one byte from the port, and see if we got a full package
if (hanReader.read())
{
// Get the list identifier
int listSize = hanReader.getListSize();
if (enableDebug) {
Serial1.println("");
Serial1.print("List size: ");
Serial1.print(listSize);
Serial1.print(": ");
}
// Only care for the ACtive Power Imported, which is found in the first list
if (listSize == (int)Kamstrup::List1 || listSize == (int)Kamstrup::List2)
{
// Define a json object to keep the data
StaticJsonBuffer<MQTT_MAX_PACKET_SIZE> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
// Any generic useful info here
root["dn"] = device_name;
root["up"] = millis();
// Add a sub-structure to the json object,
// to keep the data from the meter itself
JsonObject& data = root.createNestedObject("data");
data["ls"] = listSize;
data["lvi"] = hanReader.getString((int)Kamstrup_List1::ListVersionIdentifier);
data["mid"] = hanReader.getString((int)Kamstrup_List1::MeterID);
data["mt"] = hanReader.getString((int)Kamstrup_List1::MeterType);
data["t"] = hanReader.getPackageTime();
data["aip"] = hanReader.getInt((int)Kamstrup_List1::ActiveImportPower); //power
data["aep"] = hanReader.getInt((int)Kamstrup_List1::ActiveExportPower);
data["rip"] = hanReader.getInt((int)Kamstrup_List1::ReactiveImportPower);
data["rep"] = hanReader.getInt((int)Kamstrup_List1::ReactiveExportPower);
data["al1"] = (float)hanReader.getInt((int)Kamstrup_List1::CurrentL1) / 100.0;
data["al2"] = (float)hanReader.getInt((int)Kamstrup_List1::CurrentL2) / 100.0;
data["al3"] = (float)hanReader.getInt((int)Kamstrup_List1::CurrentL3) / 100.0;
data["vl1"] = hanReader.getInt((int)Kamstrup_List1::VoltageL1);
data["vl2"] = hanReader.getInt((int)Kamstrup_List1::VoltageL2);
data["vl3"] = hanReader.getInt((int)Kamstrup_List1::VoltageL3);
if (listSize == (int)Kamstrup::List2)
{
data["cl"] = hanReader.getTime((int)Kamstrup_List2::MeterClock);
data["caie"] = hanReader.getInt((int)Kamstrup_List2::CumulativeActiveImportEnergy);
data["caee"] = hanReader.getInt((int)Kamstrup_List2::CumulativeActiveExportEnergy);
data["crie"] = hanReader.getInt((int)Kamstrup_List2::CumulativeReactiveImportEnergy);
data["cree"] = hanReader.getInt((int)Kamstrup_List2::CumulativeReactiveExportEnergy);
}
if (enableDebug) {
root.printTo(Serial1);
Serial1.println("JSON length");
Serial1.println(root.measureLength());
Serial1.println("");
}
// Publish the json to the MQTT server
char msg[MQTT_MAX_PACKET_SIZE];
root.printTo(msg, MQTT_MAX_PACKET_SIZE);
bool result = client.publish(mqtt_topic, msg);
if (enableDebug) {
Serial1.println("MQTT publish result:");
Serial1.println(result);
}
}
}
}
// Ensure the MQTT lirary gets some attention too
void loopMqtt()
{
if (!client.connected()) {
reconnectMqtt();
}
client.loop();
}
void reconnectMqtt() {
// Loop until we're reconnected
while (!client.connected()) {
if (enableDebug) Serial1.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("ESP8266Client")) {
if (enableDebug) Serial1.println("connected");
// Once connected, publish an announcement...
// client.publish("sensors", "hello world");
// ... and resubscribe
// client.subscribe("inTopic");
}
else {
if (enableDebug) {
Serial1.print("failed, rc=");
Serial1.print(client.state());
Serial1.println(" try again in 5 seconds");
}
// Wait 5 seconds before retrying
delay(5000);
}
}
}

View File

@@ -1,95 +0,0 @@
# Setup
1. Copy AmsToMqttBridge\Code\Arduino\HanReader\src to Arduino\libraries
2. Download the following libraries and put them in Arduino\libraries
- ESP8266WiFi
- PubSubClient
- ArduinoJson
3. **Set MQTT_MAX_PACKET_SIZE in PubSubClient.h to at least 512 (i used 1024)**
4. Edit the following variables in the project:
- ssid
- password
- mqtt_server
- mqtt_topic
- device_name
## Output example:
### List 1
```
{
"dn": "espams",
"up": 1475902,
"data": {
"ls": 25,
"lvi": "Kamstrup_V0001",
"mid": "5706567274389702",
"mt": "6841121BN243101040",
"t": 1510088840,
"aip": 3499,
"aep": 0,
"rip": 0,
"rep": 424,
"al1": 10.27,
"al2": 6.37,
"al3": 11.79,
"vl1": 231,
"vl2": 226,
"vl3": 231
}
}
```
### List 2
```
{
"dn": "espams",
"up": 1041212,
"data": {
"ls": 35,
"lvi": "Kamstrup_V0001",
"mid": "5706567274389702",
"mt": "6841121BN243101040",
"t": 1510088405,
"aip": 4459,
"aep": 0,
"rip": 0,
"rep": 207,
"al1": 14.72,
"al2": 6.39,
"al3": 15.02,
"vl1": 231,
"vl2": 227,
"vl3": 231,
"cl": 1510088405,
"caie": 588500,
"caee": 0,
"crie": 93,
"cree": 80831
}
}
```
### List 1 and 2 fields
- dn = Device Name
- up = MS since last reboot
- ls = List Size
- lvi = List Version Identifier
- mid = Meter ID
- mt = Meter Type
- t = Time
- aie = Active Import Power
- aep = Active Export Power
- rip = Reactive Import Power
- rep = Reactive Export Power
- al1 = Current L1
- al2 = Current L2
- al3 = Current L3
- cl1 = Voltage L1
- cl2 = Voltage L2
- cl3 = Voltage L3
### List 2 additional fields
- cl = Meter Clock
- caie = Cumulative Active Import Energy
- caee = Cumulative Active Export Energy
- crie = Cumulative Reactive Import Energy
- cree = Cumulative Reactive Export Energy

View File

@@ -1,61 +0,0 @@
/*
* Simple sketch to read MBus data from electrical meter
* As the protocol requires "Even" parity, and this is
* only supported on the hardware port of the ESP8266,
* we'll have to use Serial1 for debugging.
*
* This means you'll have to program the ESP using the
* regular RX/TX port, and then you must remove the FTDI
* and connect the MBus signal from the meter to the
* RS pin. The FTDI/RX can be moved to Pin2 for debugging
*
* Created 14. september 2017 by Roar Fredriksen
*/
#include "HanReader.h"
#include "Kaifa.h"
// The HAN Port reader
HanReader hanReader;
void setup() {
setupDebugPort();
// initialize the HanReader
// (passing Serial as the HAN port and Serial1 for debugging)
hanReader.setup(&Serial, &Serial1);
}
void setupDebugPort()
{
// Initialize the Serial1 port for debugging
// (This port is fixed to Pin2 of the ESP8266)
Serial1.begin(115200);
while (!Serial1) {}
Serial1.setDebugOutput(true);
Serial1.println("Serial1");
Serial1.println("Serial debugging port initialized");
}
void loop() {
// Read one byte from the port, and see if we got a full package
if (hanReader.read())
{
// Get the list identifier
int listSize = hanReader.getListSize();
Serial1.println("");
Serial1.print("List size: ");
Serial1.print(listSize);
Serial1.print(": ");
// Only care for the ACtive Power Imported, which is found in the first list
if (listSize == (int)Kaifa::List1)
{
int power = hanReader.getInt((int)Kaifa_List1::ActivePowerImported);
Serial1.print("Power consumtion is right now: ");
Serial1.print(power);
Serial1.println(" W");
}
}
}

View File

@@ -1,202 +0,0 @@
/*
* Simple sketch to read MBus data from electrical meter
* As the protocol requires "Even" parity, and this is
* only supported on the hardware port of the ESP8266,
* we'll have to use Serial1 for debugging.
*
* This means you'll have to program the ESP using the
* regular RX/TX port, and then you must remove the FTDI
* and connect the MBus signal from the meter to the
* RS pin. The FTDI/RX can be moved to Pin2 for debugging
*
* Created 14. september 2017 by Roar Fredriksen
*/
#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <ArduinoJson.h>
#include "HanReader.h"
#include "Kaifa.h"
// The HAN Port reader
HanReader hanReader;
// WiFi and MQTT endpoints
const char* ssid = "Roar_Etne";
const char* password = "**********";
const char* mqtt_server = "192.168.10.203";
WiFiClient espClient;
PubSubClient client(espClient);
void setup() {
setupDebugPort();
setupWiFi();
setupMqtt();
// initialize the HanReader
// (passing Serial as the HAN port and Serial1 for debugging)
hanReader.setup(&Serial, &Serial1);
}
void setupMqtt()
{
client.setServer(mqtt_server, 1883);
}
void setupDebugPort()
{
// Initialize the Serial1 port for debugging
// (This port is fixed to Pin2 of the ESP8266)
Serial1.begin(115200);
while (!Serial1) {}
Serial1.setDebugOutput(true);
Serial1.println("Serial1");
Serial1.println("Serial debugging port initialized");
}
void setupWiFi()
{
// Initialize wifi
Serial1.print("Connecting to ");
Serial1.println(ssid);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial1.print(".");
}
Serial1.println("");
Serial1.println("WiFi connected");
Serial1.println("IP address: ");
Serial1.println(WiFi.localIP());
}
void loop() {
loopMqtt();
// Read one byt from the port, and see if we got a full package
if (hanReader.read())
{
// Get the list identifier
int listSize = hanReader.getListSize();
Serial1.println("");
Serial1.print("List size: ");
Serial1.print(listSize);
Serial1.print(": ");
// Only care for the ACtive Power Imported, which is found in the first list
if (listSize == (int)Kaifa::List1 || listSize == (int)Kaifa::List2 || listSize == (int)Kaifa::List3)
{
if (listSize == (int)Kaifa::List1)
{
Serial1.println(" (list #1 has no ID)");
}
else
{
String id = hanReader.getString((int)Kaifa_List2::ListVersionIdentifier);
Serial1.println(id);
}
// Get the timestamp (as unix time) from the package
time_t time = hanReader.getPackageTime();
Serial.print("Time of the package is: ");
Serial.println(time);
// Define a json object to keep the data
StaticJsonBuffer<500> jsonBuffer;
JsonObject& root = jsonBuffer.createObject();
// Any generic useful info here
root["id"] = "espdebugger";
root["up"] = millis();
root["t"] = time;
// Add a sub-structure to the json object,
// to keep the data from the meter itself
JsonObject& data = root.createNestedObject("data");
// Based on the list number, get all details
// according to OBIS specifications for the meter
if (listSize == (int)Kaifa::List1)
{
data["P"] = hanReader.getInt((int)Kaifa_List1::ActivePowerImported);
}
else if (listSize == (int)Kaifa::List2)
{
data["lv"] = hanReader.getString((int)Kaifa_List2::ListVersionIdentifier);
data["id"] = hanReader.getString((int)Kaifa_List2::MeterID);
data["type"] = hanReader.getString((int)Kaifa_List2::MeterType);
data["P"] = hanReader.getInt((int)Kaifa_List2::ActiveImportPower);
data["Q"] = hanReader.getInt((int)Kaifa_List2::ReactiveImportPower);
data["I1"] = hanReader.getInt((int)Kaifa_List2::CurrentL1);
data["I2"] = hanReader.getInt((int)Kaifa_List2::CurrentL2);
data["I3"] = hanReader.getInt((int)Kaifa_List2::CurrentL3);
data["U1"] = hanReader.getInt((int)Kaifa_List2::VoltageL1);
data["U2"] = hanReader.getInt((int)Kaifa_List2::VoltageL2);
data["U3"] = hanReader.getInt((int)Kaifa_List2::VoltageL3);
}
else if (listSize == (int)Kaifa::List3)
{
data["lv"] = hanReader.getString((int)Kaifa_List3::ListVersionIdentifier);;
data["id"] = hanReader.getString((int)Kaifa_List3::MeterID);
data["type"] = hanReader.getString((int)Kaifa_List3::MeterType);
data["P"] = hanReader.getInt((int)Kaifa_List3::ActiveImportPower);
data["Q"] = hanReader.getInt((int)Kaifa_List3::ReactiveImportPower);
data["I1"] = hanReader.getInt((int)Kaifa_List3::CurrentL1);
data["I2"] = hanReader.getInt((int)Kaifa_List3::CurrentL2);
data["I3"] = hanReader.getInt((int)Kaifa_List3::CurrentL3);
data["U1"] = hanReader.getInt((int)Kaifa_List3::VoltageL1);
data["U2"] = hanReader.getInt((int)Kaifa_List3::VoltageL2);
data["U3"] = hanReader.getInt((int)Kaifa_List3::VoltageL3);
data["tPI"] = hanReader.getInt((int)Kaifa_List3::CumulativeActiveImportEnergy);
data["tPO"] = hanReader.getInt((int)Kaifa_List3::CumulativeActiveExportEnergy);
data["tQI"] = hanReader.getInt((int)Kaifa_List3::CumulativeReactiveImportEnergy);
data["tQO"] = hanReader.getInt((int)Kaifa_List3::CumulativeReactiveExportEnergy);
}
// Write the json to the debug port
root.printTo(Serial1);
Serial1.println();
// Publish the json to the MQTT server
char msg[1024];
root.printTo(msg, 1024);
client.publish("sensors/out/espdebugger", msg);
}
}
}
// Ensure the MQTT lirary gets some attention too
void loopMqtt()
{
if (!client.connected()) {
reconnectMqtt();
}
client.loop();
}
void reconnectMqtt() {
// Loop until we're reconnected
while (!client.connected()) {
Serial1.print("Attempting MQTT connection...");
// Attempt to connect
if (client.connect("ESP8266Client")) {
Serial1.println("connected");
// Once connected, publish an announcement...
// client.publish("sensors", "hello world");
// ... and resubscribe
// client.subscribe("inTopic");
}
else {
Serial1.print("failed, rc=");
Serial1.print(client.state());
Serial1.println(" try again in 5 seconds");
// Wait 5 seconds before retrying
delay(5000);
}
}
}

View File

@@ -1,10 +0,0 @@
name=HanReader
version=1.0.1
author=roarfred
maintainer=roarfred <not@important.com>
sentence=HAN support
paragraph=HAN support
category=Sensors
url=https://github.com/roarfred/AmsToMqttBridge
architectures=*
depends=Timezone

View File

@@ -1,19 +0,0 @@
Arduino Compatible Cross Platform C++ Library Project : For more information see http://www.visualmicro.com
This project works exactly the same way as an Arduino library.
Add this project to any solution that contains an Arduino project and #include <headers.h> in code as you would any normal Arduino library headers.
To enable intellisense and to support live build discovery outside of the "standard" Arduino library locations, ensure that the library is added as a shared project reference to the master Arduino project. To do this, right click the master project "References" node and then click "Add Reference". A window will open and the library will appear on the "Shared Projects" tab. Click the checkbox next to the library name to add the reference. If this library is moved the shared referencemust be removed and re-added.
VS2017 has a bug, workround: After moving existing source code within a "library or shared project", close and re-open the solution.
Visual Studio will display intellisense for libraries based on the platform/board that has been specified for the currently active "Startup Project" of the current solution.
IMPORTANT: The arduino.cc Library Rules must be followed when adding code or restructing libraries.
blog: http://www.visualmicro.com/post/2017/01/16/Arduino-Cross-Platform-Library-Development.aspx

View File

@@ -1,304 +0,0 @@
// Aidon.h
#ifndef _AIDON_h
#define _AIDON_h
enum class Aidon
{
List1 = 0x01,
List1PhaseShort = 0x09,
List1PhaseLong = 0x0E,
List3PhaseShort = 0x0D,
List3PhaseLong = 0x12,
List3PhaseITShort = 0x0C,
List3PhaseITLong = 0x11,
};
enum class Aidon_List1
{
ListSize,
IGN_0,
ActiveImportPower_OBIS,
ActiveImportPower,
IGN_1,
ActiveImportPowerInt8,
ActiveImportPowerEnum
};
enum class Aidon_List1Phase
{
ListSize,
IGN_0,
ListVersionIdentifier_OBIS,
ListVersionIdentifier,
IGN_1,
MeterID_OBIS,
MeterID,
IGN_2,
MeterType_OBIS,
MeterType,
IGN_3,
ActiveImportPower_OBIS,
ActiveImportPower,
IGN_4,
ActiveImportPowerInt8,
ActiveImportPowerEnum,
IGN_5,
ActiveExportPower_OBIS,
ActiveExportPower,
IGN_6,
ActiveExportPowerInt8,
ActiveExportPowerEnum,
IGN_7,
ReactiveImportPower_OBIS,
ReactiveImportPower,
IGN_8,
ReactiveImportPowerInt8,
ReactiveImportPowerEnum,
IGN_9,
ReactiveExportPower_OBIS,
ReactiveExportPower,
IGN_10,
ReactiveExportPowerInt8,
ReactiveExportPowerEnum,
IGN_11,
CurrentL1_OBIS,
CurrentL1,
IGN_12,
CurrentL1Int8,
CurrentL1Enum,
IGN_13,
VoltageL1_OBIS,
VoltageL1,
IGN_14,
VoltageL1Int8,
VoltageL1Enum,
IGN_15,
Timestamp_OBIS,
Timestamp,
IGN_16,
CumulativeActiveImportEnergy_OBIS,
CumulativeActiveImportEnergy,
IGN_17,
CumulativeActiveImportEnergyInt8,
CumulativeActiveImportEnergyEnum,
IGN_18,
CumulativeActiveExportEnergy_OBIS,
CumulativeActiveExportEnergy,
IGN_19,
CumulativeActiveExportEnergyInt8,
CumulativeActiveExportEnergyEnum,
IGN_20,
CumulativeReactiveImportEnergy_OBIS,
CumulativeReactiveImportEnergy,
IGN_21,
CumulativeReactiveImportEnergyInt8,
CumulativeReactiveImportEnergyEnum,
IGN_22,
CumulativeReactiveExportEnergy_OBIS,
CumulativeReactiveExportEnergy,
IGN_23,
CumulativeReactiveExportEnergyInt8,
CumulativeReactiveExportEnergyEnum
};
enum class Aidon_List3Phase
{
ListSize,
IGN_0,
ListVersionIdentifier_OBIS,
ListVersionIdentifier,
IGN_1,
MeterID_OBIS,
MeterID,
IGN_2,
MeterType_OBIS,
MeterType,
IGN_3,
ActiveImportPower_OBIS,
ActiveImportPower,
IGN_4,
ActiveImportPowerInt8,
ActiveImportPowerEnum,
IGN_5,
ActiveExportPower_OBIS,
ActiveExportPower,
IGN_6,
ActiveExportPowerInt8,
ActiveExportPowerEnum,
IGN_7,
ReactiveImportPower_OBIS,
ReactiveImportPower,
IGN_8,
ReactiveImportPowerInt8,
ReactiveImportPowerEnum,
IGN_9,
ReactiveExportPower_OBIS,
ReactiveExportPower,
IGN_10,
ReactiveExportPowerInt8,
ReactiveExportPowerEnum,
IGN_11,
CurrentL1_OBIS,
CurrentL1,
IGN_12,
CurrentL1Int8,
CurrentL1Enum,
IGN_13,
CurrentL2_OBIS,
CurrentL2,
IGN_14,
CurrentL2Int8,
CurrentL2Enum,
IGN_15,
CurrentL3_OBIS,
CurrentL3,
IGN_16,
CurrentL3Int8,
CurrentL3Enum,
IGN_17,
VoltageL1_OBIS,
VoltageL1,
IGN_18,
VoltageL1Int8,
VoltageL1Enum,
IGN_19,
VoltageL2_OBIS,
VoltageL2,
IGN_20,
VoltageL2Int8,
VoltageL2Enum,
IGN_21,
VoltageL3_OBIS,
VoltageL3,
IGN_22,
VoltageL3Int8,
VoltageL3Enum,
IGN_23,
Timestamp_OBIS,
Timestamp,
IGN_24,
CumulativeActiveImportEnergy_OBIS,
CumulativeActiveImportEnergy,
IGN_25,
CumulativeActiveImportEnergyInt8,
CumulativeActiveImportEnergyEnum,
IGN_26,
CumulativeActiveExportEnergy_OBIS,
CumulativeActiveExportEnergy,
IGN_27,
CumulativeActiveExportEnergyInt8,
CumulativeActiveExportEnergyEnum,
IGN_28,
CumulativeReactiveImportEnergy_OBIS,
CumulativeReactiveImportEnergy,
IGN_29,
CumulativeReactiveImportEnergyInt8,
CumulativeReactiveImportEnergyEnum,
IGN_30,
CumulativeReactiveExportEnergy_OBIS,
CumulativeReactiveExportEnergy,
IGN_31,
CumulativeReactiveExportEnergyInt8,
CumulativeReactiveExportEnergyEnum
};
enum class Aidon_List3PhaseIT
{
ListSize,
IGN_0,
ListVersionIdentifier_OBIS,
ListVersionIdentifier,
IGN_1,
MeterID_OBIS,
MeterID,
IGN_2,
MeterType_OBIS,
MeterType,
IGN_3,
ActiveImportPower_OBIS,
ActiveImportPower,
IGN_4,
ActiveImportPowerInt8,
ActiveImportPowerEnum,
IGN_5,
ActiveExportPower_OBIS,
ActiveExportPower,
IGN_6,
ActiveExportPowerInt8,
ActiveExportPowerEnum,
IGN_7,
ReactiveImportPower_OBIS,
ReactiveImportPower,
IGN_8,
ReactiveImportPowerInt8,
ReactiveImportPowerEnum,
IGN_9,
ReactiveExportPower_OBIS,
ReactiveExportPower,
IGN_10,
ReactiveExportPowerInt8,
ReactiveExportPowerEnum,
IGN_11,
CurrentL1_OBIS,
CurrentL1,
IGN_12,
CurrentL1Int8,
CurrentL1Enum,
IGN_13,
CurrentL3_OBIS,
CurrentL3,
IGN_14,
CurrentL3Int8,
CurrentL3Enum,
IGN_15,
VoltageL1_OBIS,
VoltageL1,
IGN_16,
VoltageL1Int8,
VoltageL1Enum,
IGN_17,
VoltageL2_OBIS,
VoltageL2,
IGN_18,
VoltageL2Int8,
VoltageL2Enum,
IGN_19,
VoltageL3_OBIS,
VoltageL3,
IGN_20,
VoltageL3Int8,
VoltageL3Enum,
IGN_21,
Timestamp_OBIS,
Timestamp,
IGN_22,
CumulativeActiveImportEnergy_OBIS,
CumulativeActiveImportEnergy,
IGN_23,
CumulativeActiveImportEnergyInt8,
CumulativeActiveImportEnergyEnum,
IGN_24,
CumulativeActiveExportEnergy_OBIS,
CumulativeActiveExportEnergy,
IGN_25,
CumulativeActiveExportEnergyInt8,
CumulativeActiveExportEnergyEnum,
IGN_26,
CumulativeReactiveImportEnergy_OBIS,
CumulativeReactiveImportEnergy,
IGN_27,
CumulativeReactiveImportEnergyInt8,
CumulativeReactiveImportEnergyEnum,
IGN_28,
CumulativeReactiveExportEnergy_OBIS,
CumulativeReactiveExportEnergy,
IGN_29,
CumulativeReactiveExportEnergyInt8,
CumulativeReactiveExportEnergyEnum
};
#endif

View File

@@ -1,14 +0,0 @@
#include "Crc16.h"
Crc16Class::Crc16Class() { }
unsigned short Crc16Class::ComputeChecksum(byte *data, int start, int length) {
ushort fcs = 0xffff;
for (int i = start; i < (start + length); i++)
{
byte index = (fcs ^ data[i]) & 0xff;
fcs = (ushort)((fcs >> 8) ^ crc16_ccitt_table_reverse[index]);
}
fcs ^= 0xffff;
return fcs;
}

View File

@@ -1,57 +0,0 @@
#ifndef _CRC16_h
#define _CRC16_h
#include "Arduino.h"
const int crc16_ccitt_table_reverse [256] PROGMEM = {
0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF,
0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7,
0x1081, 0x0108, 0x3393, 0x221A, 0x56A5, 0x472C, 0x75B7, 0x643E,
0x9CC9, 0x8D40, 0xBFDB, 0xAE52, 0xDAED, 0xCB64, 0xF9FF, 0xE876,
0x2102, 0x308B, 0x0210, 0x1399, 0x6726, 0x76AF, 0x4434, 0x55BD,
0xAD4A, 0xBCC3, 0x8E58, 0x9FD1, 0xEB6E, 0xFAE7, 0xC87C, 0xD9F5,
0x3183, 0x200A, 0x1291, 0x0318, 0x77A7, 0x662E, 0x54B5, 0x453C,
0xBDCB, 0xAC42, 0x9ED9, 0x8F50, 0xFBEF, 0xEA66, 0xD8FD, 0xC974,
0x4204, 0x538D, 0x6116, 0x709F, 0x0420, 0x15A9, 0x2732, 0x36BB,
0xCE4C, 0xDFC5, 0xED5E, 0xFCD7, 0x8868, 0x99E1, 0xAB7A, 0xBAF3,
0x5285, 0x430C, 0x7197, 0x601E, 0x14A1, 0x0528, 0x37B3, 0x263A,
0xDECD, 0xCF44, 0xFDDF, 0xEC56, 0x98E9, 0x8960, 0xBBFB, 0xAA72,
0x6306, 0x728F, 0x4014, 0x519D, 0x2522, 0x34AB, 0x0630, 0x17B9,
0xEF4E, 0xFEC7, 0xCC5C, 0xDDD5, 0xA96A, 0xB8E3, 0x8A78, 0x9BF1,
0x7387, 0x620E, 0x5095, 0x411C, 0x35A3, 0x242A, 0x16B1, 0x0738,
0xFFCF, 0xEE46, 0xDCDD, 0xCD54, 0xB9EB, 0xA862, 0x9AF9, 0x8B70,
0x8408, 0x9581, 0xA71A, 0xB693, 0xC22C, 0xD3A5, 0xE13E, 0xF0B7,
0x0840, 0x19C9, 0x2B52, 0x3ADB, 0x4E64, 0x5FED, 0x6D76, 0x7CFF,
0x9489, 0x8500, 0xB79B, 0xA612, 0xD2AD, 0xC324, 0xF1BF, 0xE036,
0x18C1, 0x0948, 0x3BD3, 0x2A5A, 0x5EE5, 0x4F6C, 0x7DF7, 0x6C7E,
0xA50A, 0xB483, 0x8618, 0x9791, 0xE32E, 0xF2A7, 0xC03C, 0xD1B5,
0x2942, 0x38CB, 0x0A50, 0x1BD9, 0x6F66, 0x7EEF, 0x4C74, 0x5DFD,
0xB58B, 0xA402, 0x9699, 0x8710, 0xF3AF, 0xE226, 0xD0BD, 0xC134,
0x39C3, 0x284A, 0x1AD1, 0x0B58, 0x7FE7, 0x6E6E, 0x5CF5, 0x4D7C,
0xC60C, 0xD785, 0xE51E, 0xF497, 0x8028, 0x91A1, 0xA33A, 0xB2B3,
0x4A44, 0x5BCD, 0x6956, 0x78DF, 0x0C60, 0x1DE9, 0x2F72, 0x3EFB,
0xD68D, 0xC704, 0xF59F, 0xE416, 0x90A9, 0x8120, 0xB3BB, 0xA232,
0x5AC5, 0x4B4C, 0x79D7, 0x685E, 0x1CE1, 0x0D68, 0x3FF3, 0x2E7A,
0xE70E, 0xF687, 0xC41C, 0xD595, 0xA12A, 0xB0A3, 0x8238, 0x93B1,
0x6B46, 0x7ACF, 0x4854, 0x59DD, 0x2D62, 0x3CEB, 0x0E70, 0x1FF9,
0xF78F, 0xE606, 0xD49D, 0xC514, 0xB1AB, 0xA022, 0x92B9, 0x8330,
0x7BC7, 0x6A4E, 0x58D5, 0x495C, 0x3DE3, 0x2C6A, 0x1EF1, 0x0F78
};
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
class Crc16Class
{
public:
Crc16Class();
unsigned short ComputeChecksum(byte *data, int start, int length);
};
#endif

View File

@@ -1,224 +0,0 @@
#include "DlmsReader.h"
DlmsReader::DlmsReader()
{
//this->Clear();
}
void DlmsReader::Clear()
{
this->position = 0;
this->dataLength = 0;
this->destinationAddressLength = 0;
this->sourceAddressLength = 0;
this->frameFormatType = 0;
}
bool DlmsReader::Read(byte data, Print* debugger)
{
if (position == 0 && data != 0x7E)
{
// we haven't started yet, wait for the start flag (no need to capture any data yet)
return false;
}
else
{
// We have completed reading of one package, so clear and be ready for the next
if (dataLength > 0 && position >= dataLength + 2) {
if(debugger != NULL) {
debugger->printf("Preparing for next frame\n");
}
Clear();
}
// Check if we're about to run into a buffer overflow
if (position >= DLMS_READER_BUFFER_SIZE) {
if(debugger != NULL) {
debugger->printf("Buffer overflow\n");
debugPrint(buffer, 0, position, debugger);
}
Clear();
}
// Check if this is a second start flag, which indicates the previous one was a stop from the last package
if (position == 1 && data == 0x7E)
{
// just return, we can keep the one byte we had in the buffer
return false;
}
// We have started, so capture every byte
buffer[position++] = data;
if (position == 1)
{
// This was the start flag, we're not done yet
return false;
}
else if (position == 2)
{
// Capture the Frame Format Type
frameFormatType = (byte)(data & 0xF0);
if (!IsValidFrameFormat(frameFormatType)) {
if(debugger != NULL) {
debugger->printf("Incorrect frame format %02X\n", frameFormatType);
debugPrint(buffer, 0, position, debugger);
}
Clear();
}
return false;
}
else if (position == 3)
{
// Capture the length of the data package
dataLength = ((buffer[1] & 0x0F) << 8) | buffer[2];
return false;
}
else if (destinationAddressLength == 0)
{
// Capture the destination address
destinationAddressLength = GetAddress(3, destinationAddress, 0, DLMS_READER_MAX_ADDRESS_SIZE);
if (destinationAddressLength > 3) {
if(debugger != NULL) {
debugger->printf("Destination address length incorrect\n");
debugPrint(buffer, 0, position, debugger);
}
Clear();
}
return false;
}
else if (sourceAddressLength == 0)
{
// Capture the source address
sourceAddressLength = GetAddress(3 + destinationAddressLength, sourceAddress, 0, DLMS_READER_MAX_ADDRESS_SIZE);
if (sourceAddressLength > 3) {
if(debugger != NULL) {
debugger->printf("Source address length incorrect\n");
debugPrint(buffer, 0, position, debugger);
}
Clear();
}
return false;
}
else if (position == 4 + destinationAddressLength + sourceAddressLength + 2)
{
// Verify the header checksum
ushort headerChecksum = GetChecksum(position - 3);
if (headerChecksum != Crc16.ComputeChecksum(buffer, 1, position - 3)) {
if(debugger != NULL) {
debugger->printf("Header checksum is incorrect %02X\n", headerChecksum);
debugPrint(buffer, 0, position, debugger);
}
Clear();
}
return false;
}
else if (position == dataLength + 1)
{
// Verify the data package checksum
ushort checksum = this->GetChecksum(position - 3);
if (checksum != Crc16.ComputeChecksum(buffer, 1, position - 3)) {
if(debugger != NULL) {
debugger->printf("Frame checksum is incorrect %02X\n", checksum);
debugPrint(buffer, 0, position, debugger);
}
Clear();
}
return false;
}
else if (position == dataLength + 2)
{
// We're done, check the stop flag and signal we're done
if (data == 0x7E)
return true;
else
{
if(debugger != NULL) {
debugger->printf("Received incorrect end marker %02X\n", data);
debugPrint(buffer, 0, position, debugger);
}
Clear();
return false;
}
}
}
return false;
}
bool DlmsReader::IsValidFrameFormat(byte frameFormatType)
{
return frameFormatType == 0xA0;
}
int DlmsReader::GetRawData(byte *dataBuffer, int start, int length)
{
if (dataLength > 0 && position == dataLength + 2)
{
int headerLength = 3 + destinationAddressLength + sourceAddressLength + 2;
int bytesWritten = 0;
for (int i = headerLength + 1; i < dataLength - 1; i++)
{
dataBuffer[i + start - headerLength - 1] = buffer[i];
bytesWritten++;
}
return bytesWritten;
}
else
return 0;
}
int DlmsReader::getBytesRead() {
return dataLength - (destinationAddressLength + sourceAddressLength + 7);
}
byte* DlmsReader::getBuffer() {
return buffer + (3 + destinationAddressLength + sourceAddressLength + 2 + 1);
}
byte* DlmsReader::getFullBuffer() {
return buffer;
}
int DlmsReader::getFullBufferLength() {
return dataLength;
}
int DlmsReader::GetAddress(int addressPosition, byte* addressBuffer, int start, int length)
{
int addressBufferPos = start;
for (int i = addressPosition; i < position; i++)
{
addressBuffer[addressBufferPos++] = buffer[i];
// LSB=1 means this was the last address byte
if ((buffer[i] & 0x01) == 0x01)
break;
// See if we've reached last byte, try again when we've got more data
else if (i == position - 1)
return 0;
}
return addressBufferPos - start;
}
ushort DlmsReader::GetChecksum(int checksumPosition)
{
return (ushort)(buffer[checksumPosition + 2] << 8 |
buffer[checksumPosition + 1]);
}
void DlmsReader::debugPrint(byte *buffer, int start, int length, Print* debugger) {
for (int i = start; i < start + length; i++) {
if (buffer[i] < 0x10)
debugger->print("0");
debugger->print(buffer[i], HEX);
debugger->print(" ");
if ((i - start + 1) % 16 == 0)
debugger->println("");
else if ((i - start + 1) % 4 == 0)
debugger->print(" ");
yield(); // Let other get some resources too
}
debugger->println("");
}

View File

@@ -1,47 +0,0 @@
#ifndef _DLMSREADER_h
#define _DLMSREADER_h
#include "Crc16.h"
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#define DLMS_READER_BUFFER_SIZE 512
#define DLMS_READER_MAX_ADDRESS_SIZE 5
class DlmsReader
{
public:
DlmsReader();
bool Read(byte data, Print* Debug);
int GetRawData(byte *buffer, int start, int length);
int getBytesRead();
byte* getBuffer();
byte* getFullBuffer();
int getFullBufferLength();
protected:
Crc16Class Crc16;
private:
byte buffer[DLMS_READER_BUFFER_SIZE];
int position;
int dataLength;
byte frameFormatType;
byte destinationAddress[DLMS_READER_MAX_ADDRESS_SIZE];
byte destinationAddressLength;
byte sourceAddress[DLMS_READER_MAX_ADDRESS_SIZE];
byte sourceAddressLength;
void Clear();
int GetAddress(int addressPosition, byte* buffer, int start, int length);
unsigned short GetChecksum(int checksumPosition);
bool IsValidFrameFormat(byte frameFormatType);
void WriteBuffer();
void debugPrint(byte *buffer, int start, int length, Print* debugger);
};
#endif

View File

@@ -1,424 +0,0 @@
#include "HanReader.h"
#if defined(ESP32)
#include "mbedtls/gcm.h"
#endif
HanReader::HanReader() {
// Central European Time (Frankfurt, Paris)
TimeChangeRule CEST = {"CEST", Last, Sun, Mar, 2, 120}; // Central European Summer Time
TimeChangeRule CET = {"CET ", Last, Sun, Oct, 3, 60}; // Central European Standard Time
localZone = new Timezone(CEST, CET);
}
void HanReader::setup(Stream *hanPort, RemoteDebug *debug)
{
han = hanPort;
bytesRead = 0;
debugger = debug;
if (debug) debug->println("MBUS serial setup complete");
}
void HanReader::setup(Stream *hanPort){
setup(hanPort, NULL);
}
void HanReader::setEncryptionKey(uint8_t* encryption_key) {
this->encryption_key = encryption_key;
}
void HanReader::setAuthenticationKey(uint8_t* authentication_key) {
this->authentication_key = authentication_key;
}
bool HanReader::read(byte data) {
if (reader.Read(data, debugger->isActive(RemoteDebug::DEBUG) ? debugger : NULL)) {
bytesRead = reader.getBytesRead();
buffer = reader.getBuffer();
if (debugger->isActive(RemoteDebug::INFO)) {
printI("Got valid DLMS data (%d bytes)", bytesRead);
if (debugger->isActive(RemoteDebug::DEBUG)) {
byte* full = reader.getFullBuffer();
int size = reader.getFullBufferLength();
printI("Full DLMS frame (%d bytes)", size);
debugPrint(full, 0, size);
}
}
/*
Data should start with E6 E7 00 0F
and continue with four bytes for the InvokeId
*/
if (bytesRead < 9) {
printW("Invalid HAN data: Less than 9 bytes received");
return false;
}
else if (
buffer[0] != 0xE6
|| buffer[1] != 0xE7
|| buffer[2] != 0x00
)
{
printW("Invalid HAN data: Start should be E6 E7 00");
return false;
}
// Have not found any documentation supporting this, but 0x0F for all norwegian meters.
// Danish meters with encryption has 0xDB, so lets assume this has something to do with that.
switch(buffer[3]) {
case 0x0F:
dataHeader = 8;
break;
case 0xDB:
printI("Decrypting frame");
if(!decryptFrame()) return false;
if (debugger->isActive(RemoteDebug::DEBUG)) {
printD("Data after decryption:");
debugPrint(buffer, 0, bytesRead);
}
dataHeader = 26;
break;
}
listSize = getInt(0, buffer, 0, bytesRead);
printI("HAN data is valid, listSize: %d", listSize);
return true;
}
return false;
}
const size_t headersize = 3;
const size_t footersize = 0;
bool HanReader::decryptFrame() {
uint8_t system_title[8];
memcpy(system_title, buffer + headersize + 2, 8);
if (debugger->isActive(RemoteDebug::DEBUG)) {
printD("System title:");
debugPrint(system_title, 0, 8);
}
uint8_t initialization_vector[12];
memcpy(initialization_vector, system_title, 8);
memcpy(initialization_vector + 8, buffer + headersize + 14, 4);
if (debugger->isActive(RemoteDebug::DEBUG)) {
printD("Initialization vector:");
debugPrint(initialization_vector, 0, 12);
}
uint8_t additional_authenticated_data[17];
memcpy(additional_authenticated_data, buffer + headersize + 13, 1);
memcpy(additional_authenticated_data + 1, authentication_key, 16);
if (debugger->isActive(RemoteDebug::DEBUG)) {
printD("Additional authenticated data:");
debugPrint(additional_authenticated_data, 0, 17);
}
uint8_t authentication_tag[12];
memcpy(authentication_tag, buffer + headersize + bytesRead - headersize - footersize - 12, 12);
if (debugger->isActive(RemoteDebug::DEBUG)) {
printD("Authentication tag:");
debugPrint(authentication_tag, 0, 12);
}
if (debugger->isActive(RemoteDebug::DEBUG)) {
printD("Encryption key:");
debugPrint(encryption_key, 0, 16);
}
#if defined(ESP8266)
br_gcm_context gcmCtx;
br_aes_ct_ctr_keys bc;
br_aes_ct_ctr_init(&bc, encryption_key, 16);
br_gcm_init(&gcmCtx, &bc.vtable, br_ghash_ctmul32);
br_gcm_reset(&gcmCtx, initialization_vector, sizeof(initialization_vector));
br_gcm_aad_inject(&gcmCtx, additional_authenticated_data, sizeof(additional_authenticated_data));
br_gcm_flip(&gcmCtx);
br_gcm_run(&gcmCtx, 0, buffer + headersize + 18, bytesRead - headersize - footersize - 18 - 12);
if(br_gcm_check_tag_trunc(&gcmCtx, authentication_tag, 12) != 1) {
printE("authdecrypt failed");
return false;
}
#elif defined(ESP32)
uint8_t cipher_text[bytesRead - headersize - footersize - 18 - 12];
memcpy(cipher_text, buffer + headersize + 18, bytesRead - headersize - footersize - 12 - 18);
mbedtls_gcm_context m_ctx;
mbedtls_gcm_init(&m_ctx);
int success = mbedtls_gcm_setkey(&m_ctx, MBEDTLS_CIPHER_ID_AES, encryption_key, 128);
if (0 != success ) {
printE("Setkey failed: " + String(success));
return false;
}
success = mbedtls_gcm_auth_decrypt(&m_ctx, sizeof(cipher_text), initialization_vector, sizeof(initialization_vector),
additional_authenticated_data, sizeof(additional_authenticated_data), authentication_tag, sizeof(authentication_tag),
cipher_text, buffer + headersize + 18);
if (0 != success) {
printE("authdecrypt failed: " + String(success));
return false;
}
mbedtls_gcm_free(&m_ctx);
#endif
return true;
}
void HanReader::debugPrint(byte *buffer, int start, int length) {
for (int i = start; i < start + length; i++) {
if (buffer[i] < 0x10)
debugger->print("0");
debugger->print(buffer[i], HEX);
debugger->print(" ");
if ((i - start + 1) % 16 == 0)
debugger->println("");
else if ((i - start + 1) % 4 == 0)
debugger->print(" ");
yield(); // Let other get some resources too
}
debugger->println("");
}
bool HanReader::read() {
while(han->available()) {
if(read(han->read())) {
return true;
}
}
return false;
}
int HanReader::getListSize() {
return listSize;
}
time_t HanReader::getPackageTime(bool respectTimezone, bool respectDsc) {
int packageTimePosition = dataHeader
+ (compensateFor09HeaderBug ? 1 : 0);
return getTime(buffer, packageTimePosition, bytesRead, respectTimezone, respectDsc);
}
time_t HanReader::getTime(uint8_t objectId, bool respectTimezone, bool respectDsc) {
return getTime(objectId, respectTimezone, respectDsc, buffer, 0, bytesRead);
}
int32_t HanReader::getInt(uint8_t objectId) {
return getInt(objectId, buffer, 0, bytesRead);
}
uint32_t HanReader::getUint(uint8_t objectId) {
return getUint32(objectId, buffer, 0, bytesRead);
}
String HanReader::getString(uint8_t objectId) {
return getString(objectId, buffer, 0, bytesRead);
}
int HanReader::getBuffer(byte* buf) {
for (int i = 0; i < bytesRead; i++) {
buf[i] = buffer[i];
}
return bytesRead;
}
int HanReader::findValuePosition(uint8_t dataPosition, byte *buffer, int start, int length) {
// The first byte after the header gives the length
// of the extended header information (variable)
int headerSize = dataHeader + (compensateFor09HeaderBug ? 1 : 0);
int firstData = headerSize + buffer[headerSize] + 1;
for (int i = start + firstData; i<length; i++) {
if (dataPosition-- == 0)
return i;
else if (buffer[i] == 0x00) // null
i += 0;
else if (buffer[i] == 0x0A) // String
i += buffer[i + 1] + 1;
else if (buffer[i] == 0x09) // byte array
i += buffer[i + 1] + 1;
else if (buffer[i] == 0x01) // array (1 byte for reading size)
i += 1;
else if (buffer[i] == 0x02) // struct (1 byte for reading size)
i += 1;
else if (buffer[i] == 0x10) // int16 value (2 bytes)
i += 2;
else if (buffer[i] == 0x12) // uint16 value (2 bytes)
i += 2;
else if (buffer[i] == 0x06) // uint32 value (4 bytes)
i += 4;
else if (buffer[i] == 0x0F) // int8 value (1 bytes)
i += 1;
else if (buffer[i] == 0x16) // enum (1 bytes)
i += 1;
else {
printW("Unknown data type found: 0x%s", String(buffer[i], HEX).c_str());
return 0; // unknown data type found
}
}
printD("Passed the end of the data. Length was: %d", length);
return 0;
}
time_t HanReader::getTime(uint8_t dataPosition, bool respectTimezone, bool respectDsc, byte *buffer, int start, int length) {
// TODO: check if the time is represented always as a 12 byte string (0x09 0x0C)
int timeStart = findValuePosition(dataPosition, buffer, start, length);
timeStart += 1;
return getTime(buffer, start + timeStart, length - timeStart, respectTimezone, respectDsc);
}
time_t HanReader::getTime(byte *buffer, int start, int length, bool respectTimezone, bool respectDsc) {
int pos = start;
int dataLength = buffer[pos++];
if (dataLength == 0x0C) {
int year = buffer[pos] << 8 |
buffer[pos + 1];
int month = buffer[pos + 2];
int day = buffer[pos + 3];
// 4: Day of week
int hour = buffer[pos + 5];
int minute = buffer[pos + 6];
int second = buffer[pos + 7];
// 8: Hundredths
int16_t tzMinutes = buffer[pos + 9] << 8 | buffer[pos + 10];
bool dsc = (buffer[pos + 11] & 0x80) == 0x80;
tmElements_t tm;
tm.Year = year - 1970;
tm.Month = month;
tm.Day = day;
tm.Hour = hour;
tm.Minute = minute;
tm.Second = second;
time_t time = makeTime(tm);
if(respectTimezone && (tzMinutes | 0x8000) != 0x8000 && tzMinutes <= 720 && tzMinutes >= -720) {
time -= tzMinutes * 60;
if(respectDsc && dsc)
time += 3600;
} else {
if(respectDsc && dsc)
time += 3600;
time = localZone->toUTC(time);
}
return time;
} else if(dataLength == 0) {
return (time_t)0L;
} else {
printW("Unknown time length: %d", dataLength);
// Date format not supported
return (time_t)0L;
}
}
int HanReader::getInt(uint8_t dataPosition, byte *buffer, int start, int length) {
int valuePosition = findValuePosition(dataPosition, buffer, start, length);
if (valuePosition > 0) {
switch (buffer[valuePosition++]) {
case 0x01:
case 0x02:
case 0x16:
return getUint8(dataPosition, buffer, start, length);
case 0x0F:
return getInt8(dataPosition, buffer, start, length);
case 0x12:
return getUint16(dataPosition, buffer, start, length);
case 0x10:
return getInt16(dataPosition, buffer, start, length);
case 0x06:
return getUint32(dataPosition, buffer, start, length);
}
}
return 0;
}
int8_t HanReader::getInt8(uint8_t dataPosition, byte *buffer, int start, int length) {
int valuePosition = findValuePosition(dataPosition, buffer, start, length);
if (valuePosition > 0 && buffer[valuePosition++] == 0x0F) {
return buffer[valuePosition];
}
return 0;
}
int16_t HanReader::getInt16(uint8_t dataPosition, byte *buffer, int start, int length) {
int valuePosition = findValuePosition(dataPosition, buffer, start, length);
if (valuePosition > 0 && buffer[valuePosition++] == 0x10) {
return buffer[valuePosition] << 8 | buffer[valuePosition+1];
}
return 0;
}
uint8_t HanReader::getUint8(uint8_t dataPosition, byte *buffer, int start, int length) {
int valuePosition = findValuePosition(dataPosition, buffer, start, length);
if (valuePosition > 0) {
switch(buffer[valuePosition++]) {
case 0x01:
case 0x02:
case 0x16:
return buffer[valuePosition];
}
}
return 0;
}
uint16_t HanReader::getUint16(uint8_t dataPosition, byte *buffer, int start, int length) {
int valuePosition = findValuePosition(dataPosition, buffer, start, length);
if (valuePosition > 0 && buffer[valuePosition++] == 0x12) {
return buffer[valuePosition] << 8 | buffer[valuePosition+1];
}
return 0;
}
uint32_t HanReader::getUint32(uint8_t dataPosition, byte *buffer, int start, int length) {
int valuePosition = findValuePosition(dataPosition, buffer, start, length);
if (valuePosition > 0) {
if(buffer[valuePosition++] != 0x06)
return 0;
uint32_t value = 0;
for (int i = valuePosition; i < valuePosition + 4; i++) {
value = value << 8 | buffer[i];
}
return value;
}
return 0;
}
String HanReader::getString(uint8_t dataPosition, byte *buffer, int start, int length) {
int valuePosition = findValuePosition(dataPosition, buffer, start, length);
if (valuePosition > 0) {
String value = String("");
for (int i = valuePosition + 2; i < valuePosition + buffer[valuePosition + 1] + 2; i++) {
value += String((char)buffer[i]);
}
return value;
}
return String("");
}
void HanReader::printD(String fmt, int arg) {
if(debugger->isActive(RemoteDebug::DEBUG)) debugger->printf(String("(HanReader)" + fmt + "\n").c_str(), arg);
}
void HanReader::printI(String fmt, int arg) {
if(debugger->isActive(RemoteDebug::INFO)) debugger->printf(String("(HanReader)" + fmt + "\n").c_str(), arg);
}
void HanReader::printW(String fmt, int arg) {
if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf(String("(HanReader)" + fmt + "\n").c_str(), arg);
}
void HanReader::printW(String fmt, const char* arg) {
if(debugger->isActive(RemoteDebug::WARNING)) debugger->printf(String("(HanReader)" + fmt + "\n").c_str(), arg);
}
void HanReader::printE(String fmt, int arg) {
if(debugger->isActive(RemoteDebug::ERROR)) debugger->printf(String("(HanReader)" + fmt + "\n").c_str(), arg);
}

View File

@@ -1,74 +0,0 @@
#ifndef _HANREADER_h
#define _HANREADER_h
#if defined(ARDUINO) && ARDUINO >= 100
#include "Arduino.h"
#else
#include "WProgram.h"
#endif
#include "DlmsReader.h"
#include <Timezone.h>
#include "RemoteDebug.h"
class HanReader
{
public:
uint dataHeader = 8;
bool compensateFor09HeaderBug = false;
HanReader();
void setup(Stream *hanPort);
void setup(Stream *hanPort, RemoteDebug *debug);
bool read();
bool read(byte data);
int getListSize();
time_t getPackageTime(bool respectTimezone, bool respectDsc);
int32_t getInt(uint8_t objectId); // Use this for uint8, int8, uint16, int16
uint32_t getUint(uint8_t objectId); // Only for uint32
String getString(uint8_t objectId);
time_t getTime(uint8_t objectId, bool respectTimezone, bool respectDsc);
int getBuffer(byte* buf);
void setEncryptionKey(uint8_t* encryption_key);
void setAuthenticationKey(uint8_t* authentication_key);
private:
RemoteDebug* debugger;
Stream *han;
byte* buffer;
int bytesRead;
DlmsReader reader;
int listSize;
Timezone *localZone;
uint8_t* encryption_key;
uint8_t* authentication_key;
int findValuePosition(uint8_t dataPosition, byte *buffer, int start, int length);
time_t getTime(uint8_t dataPosition, bool respectTimezone, bool respectDsc, byte *buffer, int start, int length);
time_t getTime(byte *buffer, int start, int length, bool respectTimezone, bool respectDsc);
int getInt(uint8_t dataPosition, byte *buffer, int start, int length);
int8_t getInt8(uint8_t dataPosition, byte *buffer, int start, int length);
uint8_t getUint8(uint8_t dataPosition, byte *buffer, int start, int length);
int16_t getInt16(uint8_t dataPosition, byte *buffer, int start, int length);
uint16_t getUint16(uint8_t dataPosition, byte *buffer, int start, int length);
uint32_t getUint32(uint8_t dataPosition, byte *buffer, int start, int length);
String getString(uint8_t dataPosition, byte *buffer, int start, int length);
time_t toUnixTime(uint16_t year, uint8_t month, uint8_t day, uint8_t hour, uint8_t minute, uint8_t second);
bool decryptFrame();
void debugPrint(byte *buffer, int start, int length);
void printD(String fmt, int arg=0);
void printI(String fmt, int arg=0);
void printW(String fmt, int arg=0);
void printW(String fmt, const char* arg);
void printE(String fmt, int arg=0);
};
#endif

View File

@@ -1,57 +0,0 @@
#ifndef _KAIFA_h
#define _KAIFA_h
enum class Kaifa {
List1 = 0x01,
List1PhaseShort = 0x09,
List3PhaseShort = 0x0D,
List1PhaseLong = 0x0E,
List3PhaseLong = 0x12
};
enum class Kaifa_List1 {
ListSize,
ActivePowerImported
};
enum class Kaifa_List3Phase {
ListSize,
ListVersionIdentifier,
MeterID,
MeterType,
ActiveImportPower,
ActiveExportPower,
ReactiveImportPower,
ReactiveExportPower,
CurrentL1,
CurrentL2,
CurrentL3,
VoltageL1,
VoltageL2,
VoltageL3,
MeterClock,
CumulativeActiveImportEnergy,
CumulativeActiveExportEnergy,
CumulativeReactiveImportEnergy,
CumulativeReactiveExportEnergy
};
enum class Kaifa_List1Phase {
ListSize,
ListVersionIdentifier,
MeterID,
MeterType,
ActiveImportPower,
ActiveExportPower,
ReactiveImportPower,
ReactiveExportPower,
CurrentL1,
VoltageL1,
MeterClock,
CumulativeActiveImportEnergy,
CumulativeActiveExportEnergy,
CumulativeReactiveImportEnergy,
CumulativeReactiveExportEnergy
};
#endif

View File

@@ -1,127 +0,0 @@
// Kamstrup.h
#ifndef _KAMSTRUP_h
#define _KAMSTRUP_h
enum class Kamstrup
{
List1PhaseShort = 0x11,
List1PhaseLong = 0x1B,
List3PhaseShort = 0x19,
List3PhaseLong = 0x23,
List3PhaseITShort = 0x17,
List3PhaseITLong = 0x21
};
enum class Kamstrup_List3Phase
{
ListSize,
ListVersionIdentifier,
MeterID_OBIS,
MeterID,
MeterType_OBIS,
MeterType,
ActiveImportPower_OBIS,
ActiveImportPower,
ActiveExportPower_OBIS,
ActiveExportPower,
ReactiveImportPower_OBIS,
ReactiveImportPower,
ReactiveExportPower_OBIS,
ReactiveExportPower,
CurrentL1_OBIS,
CurrentL1,
CurrentL2_OBIS,
CurrentL2,
CurrentL3_OBIS,
CurrentL3,
VoltageL1_OBIS,
VoltageL1,
VoltageL2_OBIS,
VoltageL2,
VoltageL3_OBIS,
VoltageL3,
MeterClock_OBIS,
MeterClock,
CumulativeActiveImportEnergy_OBIS,
CumulativeActiveImportEnergy,
CumulativeActiveExportEnergy_OBIS,
CumulativeActiveExportEnergy,
CumulativeReactiveImportEnergy_OBIS,
CumulativeReactiveImportEnergy,
CumulativeReactiveExportEnergy_OBIS,
CumulativeReactiveExportEnergy
};
enum class Kamstrup_List1Phase
{
ListSize,
ListVersionIdentifier,
MeterID_OBIS,
MeterID,
MeterType_OBIS,
MeterType,
ActiveImportPower_OBIS,
ActiveImportPower,
ActiveExportPower_OBIS,
ActiveExportPower,
ReactiveImportPower_OBIS,
ReactiveImportPower,
ReactiveExportPower_OBIS,
ReactiveExportPower,
CurrentL1_OBIS,
CurrentL1,
VoltageL1_OBIS,
VoltageL1,
MeterClock_OBIS,
MeterClock,
CumulativeActiveImportEnergy_OBIS,
CumulativeActiveImportEnergy,
CumulativeActiveExportEnergy_OBIS,
CumulativeActiveExportEnergy,
CumulativeReactiveImportEnergy_OBIS,
CumulativeReactiveImportEnergy,
CumulativeReactiveExportEnergy_OBIS,
CumulativeReactiveExportEnergy
};
enum class Kamstrup_List3PhaseIT
{
ListSize,
ListVersionIdentifier,
MeterID_OBIS,
MeterID,
MeterType_OBIS,
MeterType,
ActiveImportPower_OBIS,
ActiveImportPower,
ActiveExportPower_OBIS,
ActiveExportPower,
ReactiveImportPower_OBIS,
ReactiveImportPower,
ReactiveExportPower_OBIS,
ReactiveExportPower,
CurrentL1_OBIS,
CurrentL1,
CurrentL3_OBIS,
CurrentL3,
VoltageL1_OBIS,
VoltageL1,
VoltageL2_OBIS,
VoltageL2,
VoltageL3_OBIS,
VoltageL3,
MeterClock_OBIS,
MeterClock,
CumulativeActiveImportEnergy_OBIS,
CumulativeActiveImportEnergy,
CumulativeActiveExportEnergy_OBIS,
CumulativeActiveExportEnergy,
CumulativeReactiveImportEnergy_OBIS,
CumulativeReactiveImportEnergy,
CumulativeReactiveExportEnergy_OBIS,
CumulativeReactiveExportEnergy
};
#endif

View File

@@ -1,77 +0,0 @@
#ifndef _OMNIPOWER_h
#define _OMNIPOWER_h
enum class Omnipower {
DLMS = 0x41
};
enum class Omnipower_DLMS {
ListSize,
ListVersionIdentifier,
CumulativeActiveImportEnergy_OBIS,
CumulativeActiveImportEnergy,
CumulativeActiveExportEnergy_OBIS,
CumulativeActiveExportEnergy,
CumulativeReactiveImportEnergy_OBIS,
CumulativeReactiveImportEnergy,
CumulativeReactiveExportEnergy_OBIS,
CumulativeReactiveExportEnergy,
MeterNumber_OBIS,
MeterNumber,
ActiveImportPower_OBIS,
ActiveImportPower,
ActiveExportPower_OBIS,
ActiveExportPower,
ReactiveImportPower_OBIS,
ReactiveImportPower,
ReactiveExportPower_OBIS,
ReactiveExportPower,
MeterClock_OBIS,
MeterClock,
VoltageL1_OBIS,
VoltageL1,
VoltageL2_OBIS,
VoltageL2,
VoltageL3_OBIS,
VoltageL3,
CurrentL1_OBIS,
CurrentL1,
CurrentL2_OBIS,
CurrentL2,
CurrentL3_OBIS,
CurrentL3,
ActiveImportPowerL1_OBIS,
ActiveImportPowerL1,
ActiveImportPowerL2_OBIS,
ActiveImportPowerL2,
ActiveImportPowerL3_OBIS,
ActiveImportPowerL3,
PowerFactorL1_OBIS,
PowerFactorL1,
PowerFactorL2_OBIS,
PowerFactorL2,
PowerFactorL3_OBIS,
PowerFactorL3,
PowerFactor_OBIS,
PowerFactor,
ActiveExportPowerL1_OBIS,
ActiveExportPowerL1,
ActiveExportPowerL2_OBIS,
ActiveExportPowerL2,
ActiveExportPowerL3_OBIS,
ActiveExportPowerL3,
CumulativeActiveExportEnergyL1_OBIS,
CumulativeActiveExportEnergyL1,
CumulativeActiveExportEnergyL2_OBIS,
CumulativeActiveExportEnergyL2,
CumulativeActiveExportEnergyL3_OBIS,
CumulativeActiveExportEnergyL3,
CumulativeActiveImportEnergyL1_OBIS,
CumulativeActiveImportEnergyL1,
CumulativeActiveImportEnergyL2_OBIS,
CumulativeActiveImportEnergyL2,
CumulativeActiveImportEnergyL3_OBIS,
CumulativeActiveImportEnergyL3
};
#endif