33 size_t remaining = MBUS_MAX_FRAME_LENGTH * 2 - this->
receive_buffer_.size();
35 ESP_LOGW(TAG,
"Receive buffer full, dropping remaining bytes");
39 if (avail > remaining) {
44 size_t to_read = std::min(avail,
sizeof(buf));
60 uint16_t message_length;
61 uint8_t systitle_length;
62 uint16_t header_offset;
66 if (message_length < DECODER_START_OFFSET || message_length > MAX_MESSAGE_LENGTH) {
67 ESP_LOGE(TAG,
"DLMS: Message length invalid: %u", message_length);
80 ESP_LOGV(TAG,
"Parsing M-Bus frames");
81 uint16_t frame_offset = 0;
85 if (this->
receive_buffer_.size() - frame_offset < MBUS_HEADER_INTRO_LENGTH) {
86 ESP_LOGE(TAG,
"MBUS: Not enough data for frame header (need %d, have %d)", MBUS_HEADER_INTRO_LENGTH,
93 if (this->
receive_buffer_[frame_offset + MBUS_START1_OFFSET] != START_BYTE_LONG_FRAME ||
94 this->
receive_buffer_[frame_offset + MBUS_START2_OFFSET] != START_BYTE_LONG_FRAME) {
95 ESP_LOGE(TAG,
"MBUS: Start bytes do not match");
103 ESP_LOGE(TAG,
"MBUS: Length bytes do not match");
108 uint8_t frame_length = this->
receive_buffer_[frame_offset + MBUS_LENGTH1_OFFSET];
113 ESP_LOGE(TAG,
"MBUS: Frame too big for received data");
119 size_t required_total =
120 frame_length + MBUS_HEADER_INTRO_LENGTH + MBUS_FOOTER_LENGTH;
122 ESP_LOGE(TAG,
"MBUS: Incomplete frame (need %d, have %d)", (
unsigned int) required_total,
128 if (this->
receive_buffer_[frame_offset + frame_length + MBUS_HEADER_INTRO_LENGTH + MBUS_FOOTER_LENGTH - 1] !=
130 ESP_LOGE(TAG,
"MBUS: Invalid stop byte");
137 for (uint16_t i = 0; i < frame_length; i++) {
141 ESP_LOGE(TAG,
"MBUS: Invalid checksum: %x != %x",
checksum,
142 this->
receive_buffer_[frame_offset + frame_length + MBUS_HEADER_INTRO_LENGTH]);
147 mbus_payload.insert(mbus_payload.end(), &this->receive_buffer_[frame_offset + MBUS_FULL_HEADER_LENGTH],
148 &this->receive_buffer_[frame_offset + MBUS_HEADER_INTRO_LENGTH + frame_length]);
150 frame_offset += MBUS_HEADER_INTRO_LENGTH + frame_length + MBUS_FOOTER_LENGTH;
156 uint8_t &systitle_length, uint16_t &header_offset) {
157 ESP_LOGV(TAG,
"Parsing DLMS header");
158 if (mbus_payload.size() < DLMS_HEADER_LENGTH + DLMS_HEADER_EXT_OFFSET) {
159 ESP_LOGE(TAG,
"DLMS: Payload too short");
164 if (mbus_payload[DLMS_CIPHER_OFFSET] != GLO_CIPHERING) {
165 ESP_LOGE(TAG,
"DLMS: Unsupported cipher");
170 systitle_length = mbus_payload[DLMS_SYST_OFFSET];
172 if (systitle_length != 0x08) {
173 ESP_LOGE(TAG,
"DLMS: Unsupported system title length");
178 message_length = mbus_payload[DLMS_LENGTH_OFFSET];
184 if (message_length == NETZ_NOE_MAGIC_BYTE &&
185 mbus_payload[DLMS_LENGTH_OFFSET + 1] == NETZ_NOE_EXPECTED_MESSAGE_LENGTH &&
186 mbus_payload[DLMS_LENGTH_OFFSET + 2] == NETZ_NOE_EXPECTED_SECURITY_CONTROL_BYTE) {
187 message_length = mbus_payload[DLMS_LENGTH_OFFSET + 1];
190 ESP_LOGE(TAG,
"Wrong Length - Security Control Byte sequence detected for provider EVN");
193 if (message_length == TWO_BYTE_LENGTH) {
194 message_length =
encode_uint16(mbus_payload[DLMS_LENGTH_OFFSET + 1], mbus_payload[DLMS_LENGTH_OFFSET + 2]);
195 header_offset = DLMS_HEADER_EXT_OFFSET;
198 if (message_length < DLMS_LENGTH_CORRECTION) {
199 ESP_LOGE(TAG,
"DLMS: Message length too short: %u", message_length);
203 message_length -= DLMS_LENGTH_CORRECTION;
205 if (mbus_payload.size() - DLMS_HEADER_LENGTH - header_offset != message_length) {
206 ESP_LOGV(TAG,
"DLMS: Length mismatch - payload=%d, header=%d, offset=%d, message=%d", mbus_payload.size(),
207 DLMS_HEADER_LENGTH, header_offset, message_length);
208 ESP_LOGE(TAG,
"DLMS: Message has invalid length");
213 if (mbus_payload[header_offset + DLMS_SECBYTE_OFFSET] != 0x21 &&
214 mbus_payload[header_offset + DLMS_SECBYTE_OFFSET] !=
216 ESP_LOGE(TAG,
"DLMS: Unsupported security control byte");
225 uint16_t header_offset) {
226 ESP_LOGV(TAG,
"Decrypting payload");
230 memcpy(&iv[0], &mbus_payload[DLMS_SYST_OFFSET + 1], systitle_length);
231 memcpy(&iv[8], &mbus_payload[header_offset + DLMS_FRAMECOUNTER_OFFSET],
232 DLMS_FRAMECOUNTER_LENGTH);
234 uint8_t *payload_ptr = &mbus_payload[header_offset + DLMS_PAYLOAD_OFFSET];
236#if defined(USE_ESP8266_FRAMEWORK_ARDUINO)
237 br_gcm_context gcm_ctx;
238 br_aes_ct_ctr_keys bc;
239 br_aes_ct_ctr_init(&bc, this->
decryption_key_.data(), this->decryption_key_.size());
240 br_gcm_init(&gcm_ctx, &bc.vtable, br_ghash_ctmul32);
241 br_gcm_reset(&gcm_ctx, iv,
sizeof(iv));
242 br_gcm_flip(&gcm_ctx);
243 br_gcm_run(&gcm_ctx, 0, payload_ptr, message_length);
244#elif defined(USE_ESP32)
246 mbedtls_gcm_context gcm_ctx;
247 mbedtls_gcm_init(&gcm_ctx);
248 mbedtls_gcm_setkey(&gcm_ctx, MBEDTLS_CIPHER_ID_AES, this->
decryption_key_.data(), this->decryption_key_.size() * 8);
249 mbedtls_gcm_starts(&gcm_ctx, MBEDTLS_GCM_DECRYPT, iv,
sizeof(iv));
250 auto ret = mbedtls_gcm_update(&gcm_ctx, payload_ptr, message_length, payload_ptr, message_length, &outlen);
251 mbedtls_gcm_free(&gcm_ctx);
253 ESP_LOGE(TAG,
"Decryption failed with error: %d", ret);
258#error "Invalid Platform"
261 if (payload_ptr[0] != DATA_NOTIFICATION || payload_ptr[5] != TIMESTAMP_DATETIME) {
262 ESP_LOGE(TAG,
"OBIS: Packet was decrypted but data is invalid");
266 ESP_LOGV(TAG,
"Decrypted payload: %d bytes", message_length);
271 ESP_LOGV(TAG,
"Decoding payload");
273 uint16_t current_position = DECODER_START_OFFSET;
274 bool power_factor_found =
false;
276 while (current_position + OBIS_CODE_OFFSET <= message_length) {
278 ESP_LOGE(TAG,
"OBIS: Unsupported OBIS header type: %x", plaintext[current_position + OBIS_TYPE_OFFSET]);
283 uint8_t obis_code_length = plaintext[current_position + OBIS_LENGTH_OFFSET];
284 if (obis_code_length != OBIS_CODE_LENGTH_STANDARD && obis_code_length != OBIS_CODE_LENGTH_EXTENDED) {
285 ESP_LOGE(TAG,
"OBIS: Unsupported OBIS header length: %x", obis_code_length);
289 if (current_position + OBIS_CODE_OFFSET + obis_code_length > message_length) {
290 ESP_LOGE(TAG,
"OBIS: Buffer too short for OBIS code");
295 uint8_t *obis_code = &plaintext[current_position + OBIS_CODE_OFFSET];
296 uint8_t obis_medium = obis_code[OBIS_A];
297 uint16_t obis_cd =
encode_uint16(obis_code[OBIS_C], obis_code[OBIS_D]);
299 bool timestamp_found =
false;
300 bool meter_number_found =
false;
303 if ((obis_code_length == OBIS_CODE_LENGTH_EXTENDED) && (current_position == DECODER_START_OFFSET)) {
304 timestamp_found =
true;
305 }
else if (power_factor_found) {
306 meter_number_found =
true;
307 power_factor_found =
false;
309 current_position += obis_code_length + OBIS_CODE_OFFSET;
312 current_position += obis_code_length + OBIS_CODE_OFFSET;
316 ESP_LOGE(TAG,
"OBIS: Unsupported OBIS medium: %x", obis_medium);
321 if (current_position >= message_length) {
322 ESP_LOGE(TAG,
"OBIS: Buffer too short for data type");
328 uint8_t value_size = 0;
329 uint8_t data_type = plaintext[current_position];
335 if (current_position + value_size > message_length) {
336 ESP_LOGE(TAG,
"OBIS: Buffer too short for DOUBLE_LONG_UNSIGNED");
340 value =
encode_uint32(plaintext[current_position + 0], plaintext[current_position + 1],
341 plaintext[current_position + 2], plaintext[current_position + 3]);
342 current_position += value_size;
347 if (current_position + value_size > message_length) {
348 ESP_LOGE(TAG,
"OBIS: Buffer too short for LONG_UNSIGNED");
352 value =
encode_uint16(plaintext[current_position + 0], plaintext[current_position + 1]);
353 current_position += value_size;
357 uint8_t data_length = plaintext[current_position];
359 if (current_position + data_length > message_length) {
360 ESP_LOGE(TAG,
"OBIS: Buffer too short for OCTET_STRING");
365 if (obis_cd == OBIS_TIMESTAMP || timestamp_found) {
366 if (data_length < 8) {
367 ESP_LOGE(TAG,
"OBIS: Timestamp data too short: %u", data_length);
371 uint16_t
year =
encode_uint16(plaintext[current_position + 0], plaintext[current_position + 1]);
372 uint8_t
month = plaintext[current_position + 2];
373 uint8_t
day = plaintext[current_position + 3];
374 uint8_t
hour = plaintext[current_position + 5];
375 uint8_t
minute = plaintext[current_position + 6];
376 uint8_t
second = plaintext[current_position + 7];
383 snprintf(data.timestamp,
sizeof(data.timestamp),
"%04u-%02u-%02uT%02u:%02u:%02uZ",
year,
month,
day,
hour,
385 }
else if (meter_number_found) {
386 snprintf(data.meternumber,
sizeof(data.meternumber),
"%.*s", data_length, &plaintext[current_position]);
388 current_position += data_length;
392 ESP_LOGE(TAG,
"OBIS: Unsupported OBIS data type: %x", data_type);
400 if (!timestamp_found) {
401 current_position += 2;
404 current_position += 2;
408 if (current_position < message_length && plaintext[current_position] ==
DataType::INTEGER) {
410 if (current_position + 1 < message_length) {
411 int8_t scaler =
static_cast<int8_t
>(plaintext[current_position + 1]);
413 value *= powf(10.0f, scaler);
419 current_position += 4;
421 current_position += 6;
426 if (value_size > 0) {
428 case OBIS_VOLTAGE_L1:
429 data.voltage_l1 = value;
431 case OBIS_VOLTAGE_L2:
432 data.voltage_l2 = value;
434 case OBIS_VOLTAGE_L3:
435 data.voltage_l3 = value;
437 case OBIS_CURRENT_L1:
438 data.current_l1 = value;
440 case OBIS_CURRENT_L2:
441 data.current_l2 = value;
443 case OBIS_CURRENT_L3:
444 data.current_l3 = value;
446 case OBIS_ACTIVE_POWER_PLUS:
447 data.active_power_plus = value;
449 case OBIS_ACTIVE_POWER_MINUS:
450 data.active_power_minus = value;
452 case OBIS_ACTIVE_ENERGY_PLUS:
453 data.active_energy_plus = value;
455 case OBIS_ACTIVE_ENERGY_MINUS:
456 data.active_energy_minus = value;
458 case OBIS_REACTIVE_ENERGY_PLUS:
459 data.reactive_energy_plus = value;
461 case OBIS_REACTIVE_ENERGY_MINUS:
462 data.reactive_energy_minus = value;
464 case OBIS_POWER_FACTOR:
465 data.power_factor = value;
466 power_factor_found =
true;
469 ESP_LOGW(TAG,
"Unsupported OBIS code 0x%04X", obis_cd);
476 ESP_LOGI(TAG,
"Received valid data");