Here is the code (I have no time for github sry) //Arduino IDE 2.3.7
/*
RIIO_TINETZ – ESP32 – M-Bus (TINETZ Kundenschnittstelle) -> MQTT
- Reads segmented M-Bus long frames (0x68 ... 0x16), reassembles DLMS payload
- Parses General-GLO-Ciphering APDU (0xDB 0x08 ...)
- Handles sec_ctrl = 0x21 (Encryption-only, Auth not applied) by decrypting via AES-CTR (GCM-CTR)
- Extracts COSEM Data-Notification (0x0F) and publishes OBIS values to MQTT
- MQTT Watchdog toggles true/false every 30s
- Adds SSD1306 128x64 I2C OLED debug screen
- Sends M-Bus ACK (0xE5) after each valid long frame (CRC ok)
*/
#include <WiFi.h>
#include <MQTT.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include "mbedtls/aes.h"
#include "mbedtls/gcm.h"
#include <math.h>
// ---------- Forward declarations (fix Arduino auto-prototype issues) ----------
struct AxdrValue;
class AxdrReader;
// ------------------------- USER CONFIG -------------------------
#define WIFI_SSID "wifissd"
#define WIFI_PASSWORD "wifipwd"
static const char* MQTT_HOST = "mqtt host ip";
static const uint16_t MQTT_PORT = mqtt host port ;
// Base topics (KEEP EXACT)
static const char* TOPIC_BASE = "tinetz/smartmeter";
static const char* TOPIC_STATUS_WD = "tinetz/smartmeter/status/watchdog";
static const char* TOPIC_DEBUG_SYS = "tinetz/smartmeter/debug/sys_title";
static const char* TOPIC_DEBUG_SC = "tinetz/smartmeter/debug/sec_ctrl";
static const char* TOPIC_DEBUG_IC = "tinetz/smartmeter/debug/invocation_counter";
static const char* TOPIC_DEBUG_DECFAIL = "tinetz/smartmeter/debug/decrypt_fail";
static const char* TOPIC_DEBUG_CIPHER = "tinetz/smartmeter/debug/cipher_hex";
// Your DML key (hex, 16 bytes / 32 hex chars)
static const char* DML_KEY_HEX = "Benutzerschnittstelle key from TINETZ ";
// ------------------------- PIN CONFIG -------------------------
// ESP32 UART2 pins
static const int MBUS_RX_PIN = 16;
static const int MBUS_TX_PIN = 17; // used for ACK 0xE5
static const uint32_t MBUS_BAUD = 2400;
// OLED I2C (SSD1306 128x64)
static const int OLED_SDA = 21;
static const int OLED_SCL = 22;
static const uint8_t OLED_ADDR = 0x3C; // change to 0x3D if needed
static const int OLED_RESET = -1;
// ------------------------- MQTT/WIFI -------------------------
WiFiClient net;
MQTTClient mqtt(4096);
// OLED object
Adafruit_SSD1306 display(128, 64, &Wire, OLED_RESET);
// ------------------------- RUNTIME / DEBUG STATE -------------------------
static uint8_t g_dmlKey[16];
static unsigned long g_lastMbusFrameMs = 0;
static unsigned long g_lastMbusByteMs = 0;
static uint32_t g_mbusFramesOk = 0;
static uint32_t g_mbusBadCrc = 0;
static uint32_t g_mbusResets = 0;
static uint32_t g_decryptFailCount = 0;
static unsigned long g_lastDecryptOkMs = 0;
static uint8_t g_lastSysTitle[8] = {0};
static uint8_t g_lastSecCtrl = 0;
static uint8_t g_lastInvCtr[4] = {0};
static String g_lastObis = "";
static String g_lastObisVal = "";
static unsigned long g_lastOledMs = 0;
static unsigned long g_lastSerialRestartMs = 0;
// ------------------------- HELPERS -------------------------
static bool hexNibble(char c, uint8_t& out) {
if (c >= '0' && c <= '9') { out = (uint8_t)(c - '0'); return true; }
if (c >= 'a' && c <= 'f') { out = (uint8_t)(10 + (c - 'a')); return true; }
if (c >= 'A' && c <= 'F') { out = (uint8_t)(10 + (c - 'A')); return true; }
return false;
}
static bool hexToBytes(const char* hex, uint8_t* out, size_t outLen) {
size_t n = strlen(hex);
if (n != outLen * 2) return false;
for (size_t i = 0; i < outLen; i++) {
uint8_t hi, lo;
if (!hexNibble(hex[2*i], hi) || !hexNibble(hex[2*i+1], lo)) return false;
out[i] = (uint8_t)((hi << 4) | lo);
}
return true;
}
static String...
Read more »
stefan.schnitzer