-
-
Save aldadic/31abea0001c0a231c59814a3e0c68146 to your computer and use it in GitHub Desktop.
ESP8266 Smart Meter Reader
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "config.h" | |
#include <AES.h> | |
#include <CTR.h> | |
#include <FastCRC.h> | |
#include <ArduinoJson.h> | |
#include <ESP8266WiFi.h> | |
#include <PubSubClient.h> | |
// --------------- WIFI --------------- | |
void SetupWifi() { | |
delay(10); | |
console->println(); | |
console->print("Connecting to "); | |
console->println(WIFI_SSID); | |
WiFi.begin(WIFI_SSID, WIFI_PASS); | |
while (WiFi.status() != WL_CONNECTED) { | |
delay(500); | |
console->print("."); | |
} | |
console->println(""); | |
console->println("WiFi connected"); | |
console->println("IP address: "); | |
console->println(WiFi.localIP()); | |
} | |
// --------------- MQTT --------------- | |
WiFiClient wifi_client; | |
PubSubClient mqtt_client(wifi_client); | |
void ReconnectMQTT() { | |
while (!mqtt_client.connected()) { | |
console->print("Attempting MQTT connection..."); | |
if (mqtt_client.connect("ESPClient", MQTT_USER, MQTT_PASS)) { | |
console->println("connected"); | |
} else { | |
console->print("failed, rc="); | |
console->print(mqtt_client.state()); | |
console->println(" try again in 5 seconds"); | |
delay(5000); | |
} | |
} | |
} | |
// --------------- SERIAL READER --------------- | |
const unsigned int MESSAGE_LENGTH = 105; | |
byte received_data[MESSAGE_LENGTH]; | |
void ReadSerialData() { | |
static byte previous_byte = 0; | |
static bool receiving = false; | |
static unsigned int pos; | |
while (smart_meter->available() > 0) { | |
byte current_byte = smart_meter->read(); | |
if (!receiving) { | |
// Starting sequence is 7E A0 | |
if (previous_byte == 0x7E && current_byte == 0xA0) { | |
receiving = true; | |
received_data[0] = 0x7E; | |
received_data[1] = 0xA0; | |
pos = 2; | |
} | |
} else { | |
if (pos < MESSAGE_LENGTH) { | |
received_data[pos] = current_byte; | |
pos++; | |
} else { | |
ParseReceivedData(); | |
receiving = false; | |
} | |
} | |
previous_byte = current_byte; | |
} | |
} | |
// -------------- DATA PARSER --------------- | |
int BytesToInt(byte bytes[], unsigned int left, unsigned int right) { | |
int result = 0; | |
for (unsigned int i = left; i < right; i++) { | |
result = result * 256 + bytes[i]; | |
} | |
return result; | |
} | |
void ParseReceivedData() { | |
// Discard message if CRC check fails | |
if (!ValidateCRC()) { | |
return; | |
} | |
// Decrypt message | |
byte decrypted_message[74]; | |
DecryptMessage(decrypted_message); | |
// Extract time and date from decrypted message | |
int year = BytesToInt(decrypted_message, 22, 24); | |
int month = BytesToInt(decrypted_message, 24, 25); | |
int day = BytesToInt(decrypted_message, 25, 26); | |
int hour = BytesToInt(decrypted_message, 27, 28); | |
int minute = BytesToInt(decrypted_message, 28, 29); | |
int second = BytesToInt(decrypted_message, 29, 30); | |
char timestamp[19]; | |
sprintf(timestamp, "%02d.%02d.%04d %02d:%02d:%02d", day, month, year, hour, minute, second); | |
// Create JSON | |
StaticJsonDocument<256> doc; | |
doc["timestamp"] = timestamp; | |
doc["+A"] = BytesToInt(decrypted_message, 35, 39)/1000.0; | |
doc["-A"] = BytesToInt(decrypted_message, 40, 44)/1000.0; | |
doc["+R"] = BytesToInt(decrypted_message, 45, 49)/1000.0; | |
doc["-R"] = BytesToInt(decrypted_message, 50, 54)/1000.0; | |
doc["+P"] = BytesToInt(decrypted_message, 55, 59); | |
doc["-P"] = BytesToInt(decrypted_message, 60, 64); | |
doc["+Q"] = BytesToInt(decrypted_message, 65, 69); | |
doc["-Q"] = BytesToInt(decrypted_message, 70, 74); | |
char payload[256]; | |
serializeJson(doc, payload); | |
// Publish JSON | |
console->println(payload); | |
if (MQTT_ENABLED) { | |
mqtt_client.publish(MQTT_TOPIC, payload, false); | |
} | |
} | |
// --------------- CRC VALIDATOR --------------- | |
FastCRC16 CRC16; | |
bool ValidateCRC() { | |
byte message[101]; | |
memcpy(message, received_data + 1, 101); | |
int crc = CRC16.x25(message, 101); | |
int expected_crc = received_data[103] * 256 + received_data[102]; | |
if (crc != expected_crc) { | |
console->println("WARNING: CRC check failed"); | |
return false; | |
} | |
return true; | |
} | |
// --------------- DECRYPTOR --------------- | |
CTR<AES128> ctr; | |
void DecryptMessage(byte decrypted_message[74]) { | |
// Extract message and nonce from received data | |
byte encrypted_message[74] = {0}; | |
memcpy(encrypted_message, received_data + 28, 74); | |
byte iv[16] = {0}; | |
memcpy(iv, received_data + 14, 8); | |
memcpy(iv + 8, received_data + 24, 4); | |
iv[15] = 0x02; | |
// Decrypt message | |
ctr.setIV(iv, sizeof(iv)); | |
ctr.decrypt(decrypted_message, encrypted_message, sizeof(encrypted_message)); | |
} | |
// --------------- SETUP & LOOP --------------- | |
void setup() { | |
console->begin(CONSOLE_BAUD_RATE); | |
smart_meter->begin(SMARTMETER_BAUD_RATE); | |
ctr.setKey(KEY, sizeof(KEY)); | |
if (MQTT_ENABLED) { | |
SetupWifi(); | |
mqtt_client.setServer(MQTT_SERVER, MQTT_PORT); | |
} | |
} | |
void loop() { | |
if (!mqtt_client.connected()) { | |
ReconnectMQTT(); | |
} | |
mqtt_client.loop(); | |
ReadSerialData(); | |
} |
Hoppla, das wäre mir wahrscheinlich nie aufgefallen 😂 Danke für den Hinweis!
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Da haben sich wohl noch ein paar Tippfehler eingeschlichen ;-)
decrpyted_message, encrpyted_message