31 size_t remaining = MBUS_MAX_FRAME_LENGTH * 2 - this->
receive_buffer_.size();
33 ESP_LOGW(TAG,
"Receive buffer full, dropping remaining bytes");
37 if (avail > remaining) {
42 size_t to_read = std::min(avail,
sizeof(buf));
58 uint16_t message_length;
59 uint8_t systitle_length;
60 uint16_t header_offset;
64 if (message_length < DECODER_START_OFFSET || message_length > MAX_MESSAGE_LENGTH) {
65 ESP_LOGE(TAG,
"DLMS: Message length invalid: %u", message_length);
78 ESP_LOGV(TAG,
"Parsing M-Bus frames");
79 uint16_t frame_offset = 0;
83 if (this->
receive_buffer_.size() - frame_offset < MBUS_HEADER_INTRO_LENGTH) {
84 ESP_LOGE(TAG,
"MBUS: Not enough data for frame header (need %d, have %d)", MBUS_HEADER_INTRO_LENGTH,
91 if (this->
receive_buffer_[frame_offset + MBUS_START1_OFFSET] != START_BYTE_LONG_FRAME ||
92 this->
receive_buffer_[frame_offset + MBUS_START2_OFFSET] != START_BYTE_LONG_FRAME) {
93 ESP_LOGE(TAG,
"MBUS: Start bytes do not match");
101 ESP_LOGE(TAG,
"MBUS: Length bytes do not match");
106 uint8_t frame_length = this->
receive_buffer_[frame_offset + MBUS_LENGTH1_OFFSET];
111 ESP_LOGE(TAG,
"MBUS: Frame too big for received data");
117 size_t required_total =
118 frame_length + MBUS_HEADER_INTRO_LENGTH + MBUS_FOOTER_LENGTH;
120 ESP_LOGE(TAG,
"MBUS: Incomplete frame (need %d, have %d)", (
unsigned int) required_total,
126 if (this->
receive_buffer_[frame_offset + frame_length + MBUS_HEADER_INTRO_LENGTH + MBUS_FOOTER_LENGTH - 1] !=
128 ESP_LOGE(TAG,
"MBUS: Invalid stop byte");
135 for (uint16_t i = 0; i < frame_length; i++) {
139 ESP_LOGE(TAG,
"MBUS: Invalid checksum: %x != %x",
checksum,
140 this->
receive_buffer_[frame_offset + frame_length + MBUS_HEADER_INTRO_LENGTH]);
145 mbus_payload.insert(mbus_payload.end(), &this->receive_buffer_[frame_offset + MBUS_FULL_HEADER_LENGTH],
146 &this->receive_buffer_[frame_offset + MBUS_HEADER_INTRO_LENGTH + frame_length]);
148 frame_offset += MBUS_HEADER_INTRO_LENGTH + frame_length + MBUS_FOOTER_LENGTH;
154 uint8_t &systitle_length, uint16_t &header_offset) {
155 ESP_LOGV(TAG,
"Parsing DLMS header");
156 if (mbus_payload.size() < DLMS_HEADER_LENGTH + DLMS_HEADER_EXT_OFFSET) {
157 ESP_LOGE(TAG,
"DLMS: Payload too short");
162 if (mbus_payload[DLMS_CIPHER_OFFSET] != GLO_CIPHERING) {
163 ESP_LOGE(TAG,
"DLMS: Unsupported cipher");
168 systitle_length = mbus_payload[DLMS_SYST_OFFSET];
170 if (systitle_length != 0x08) {
171 ESP_LOGE(TAG,
"DLMS: Unsupported system title length");
176 message_length = mbus_payload[DLMS_LENGTH_OFFSET];
182 if (message_length == NETZ_NOE_MAGIC_BYTE &&
183 mbus_payload[DLMS_LENGTH_OFFSET + 1] == NETZ_NOE_EXPECTED_MESSAGE_LENGTH &&
184 mbus_payload[DLMS_LENGTH_OFFSET + 2] == NETZ_NOE_EXPECTED_SECURITY_CONTROL_BYTE) {
185 message_length = mbus_payload[DLMS_LENGTH_OFFSET + 1];
188 ESP_LOGE(TAG,
"Wrong Length - Security Control Byte sequence detected for provider EVN");
191 if (message_length == TWO_BYTE_LENGTH) {
192 message_length =
encode_uint16(mbus_payload[DLMS_LENGTH_OFFSET + 1], mbus_payload[DLMS_LENGTH_OFFSET + 2]);
193 header_offset = DLMS_HEADER_EXT_OFFSET;
196 if (message_length < DLMS_LENGTH_CORRECTION) {
197 ESP_LOGE(TAG,
"DLMS: Message length too short: %u", message_length);
201 message_length -= DLMS_LENGTH_CORRECTION;
203 if (mbus_payload.size() - DLMS_HEADER_LENGTH - header_offset != message_length) {
204 ESP_LOGV(TAG,
"DLMS: Length mismatch - payload=%d, header=%d, offset=%d, message=%d", mbus_payload.size(),
205 DLMS_HEADER_LENGTH, header_offset, message_length);
206 ESP_LOGE(TAG,
"DLMS: Message has invalid length");
211 if (mbus_payload[header_offset + DLMS_SECBYTE_OFFSET] != 0x21 &&
212 mbus_payload[header_offset + DLMS_SECBYTE_OFFSET] !=
214 ESP_LOGE(TAG,
"DLMS: Unsupported security control byte");
223 uint16_t header_offset) {
224 ESP_LOGV(TAG,
"Decrypting payload");
228 memcpy(&iv[0], &mbus_payload[DLMS_SYST_OFFSET + 1], systitle_length);
229 memcpy(&iv[8], &mbus_payload[header_offset + DLMS_FRAMECOUNTER_OFFSET],
230 DLMS_FRAMECOUNTER_LENGTH);
232 uint8_t *payload_ptr = &mbus_payload[header_offset + DLMS_PAYLOAD_OFFSET];
234#if defined(USE_ESP8266_FRAMEWORK_ARDUINO)
235 br_gcm_context gcm_ctx;
236 br_aes_ct_ctr_keys bc;
237 br_aes_ct_ctr_init(&bc, this->
decryption_key_.data(), this->decryption_key_.size());
238 br_gcm_init(&gcm_ctx, &bc.vtable, br_ghash_ctmul32);
239 br_gcm_reset(&gcm_ctx, iv,
sizeof(iv));
240 br_gcm_flip(&gcm_ctx);
241 br_gcm_run(&gcm_ctx, 0, payload_ptr, message_length);
242#elif defined(USE_ESP32)
244 mbedtls_gcm_context gcm_ctx;
245 mbedtls_gcm_init(&gcm_ctx);
246 mbedtls_gcm_setkey(&gcm_ctx, MBEDTLS_CIPHER_ID_AES, this->
decryption_key_.data(), this->decryption_key_.size() * 8);
247 mbedtls_gcm_starts(&gcm_ctx, MBEDTLS_GCM_DECRYPT, iv,
sizeof(iv));
248 auto ret = mbedtls_gcm_update(&gcm_ctx, payload_ptr, message_length, payload_ptr, message_length, &outlen);
249 mbedtls_gcm_free(&gcm_ctx);
251 ESP_LOGE(TAG,
"Decryption failed with error: %d", ret);
256#error "Invalid Platform"
259 if (payload_ptr[0] != DATA_NOTIFICATION || payload_ptr[5] != TIMESTAMP_DATETIME) {
260 ESP_LOGE(TAG,
"OBIS: Packet was decrypted but data is invalid");
264 ESP_LOGV(TAG,
"Decrypted payload: %d bytes", message_length);
269 ESP_LOGV(TAG,
"Decoding payload");
271 uint16_t current_position = DECODER_START_OFFSET;
272 bool power_factor_found =
false;
274 while (current_position + OBIS_CODE_OFFSET <= message_length) {
276 ESP_LOGE(TAG,
"OBIS: Unsupported OBIS header type: %x", plaintext[current_position + OBIS_TYPE_OFFSET]);
281 uint8_t obis_code_length = plaintext[current_position + OBIS_LENGTH_OFFSET];
282 if (obis_code_length != OBIS_CODE_LENGTH_STANDARD && obis_code_length != OBIS_CODE_LENGTH_EXTENDED) {
283 ESP_LOGE(TAG,
"OBIS: Unsupported OBIS header length: %x", obis_code_length);
287 if (current_position + OBIS_CODE_OFFSET + obis_code_length > message_length) {
288 ESP_LOGE(TAG,
"OBIS: Buffer too short for OBIS code");
293 uint8_t *obis_code = &plaintext[current_position + OBIS_CODE_OFFSET];
294 uint8_t obis_medium = obis_code[OBIS_A];
295 uint16_t obis_cd =
encode_uint16(obis_code[OBIS_C], obis_code[OBIS_D]);
297 bool timestamp_found =
false;
298 bool meter_number_found =
false;
301 if ((obis_code_length == OBIS_CODE_LENGTH_EXTENDED) && (current_position == DECODER_START_OFFSET)) {
302 timestamp_found =
true;
303 }
else if (power_factor_found) {
304 meter_number_found =
true;
305 power_factor_found =
false;
307 current_position += obis_code_length + OBIS_CODE_OFFSET;
310 current_position += obis_code_length + OBIS_CODE_OFFSET;
314 ESP_LOGE(TAG,
"OBIS: Unsupported OBIS medium: %x", obis_medium);
319 if (current_position >= message_length) {
320 ESP_LOGE(TAG,
"OBIS: Buffer too short for data type");
326 uint8_t value_size = 0;
327 uint8_t data_type = plaintext[current_position];
333 if (current_position + value_size > message_length) {
334 ESP_LOGE(TAG,
"OBIS: Buffer too short for DOUBLE_LONG_UNSIGNED");
338 value =
encode_uint32(plaintext[current_position + 0], plaintext[current_position + 1],
339 plaintext[current_position + 2], plaintext[current_position + 3]);
340 current_position += value_size;
345 if (current_position + value_size > message_length) {
346 ESP_LOGE(TAG,
"OBIS: Buffer too short for LONG_UNSIGNED");
350 value =
encode_uint16(plaintext[current_position + 0], plaintext[current_position + 1]);
351 current_position += value_size;
355 uint8_t data_length = plaintext[current_position];
357 if (current_position + data_length > message_length) {
358 ESP_LOGE(TAG,
"OBIS: Buffer too short for OCTET_STRING");
363 if (obis_cd == OBIS_TIMESTAMP || timestamp_found) {
364 if (data_length < 8) {
365 ESP_LOGE(TAG,
"OBIS: Timestamp data too short: %u", data_length);
369 uint16_t
year =
encode_uint16(plaintext[current_position + 0], plaintext[current_position + 1]);
370 uint8_t
month = plaintext[current_position + 2];
371 uint8_t
day = plaintext[current_position + 3];
372 uint8_t
hour = plaintext[current_position + 5];
373 uint8_t
minute = plaintext[current_position + 6];
374 uint8_t
second = plaintext[current_position + 7];
381 snprintf(data.timestamp,
sizeof(data.timestamp),
"%04u-%02u-%02uT%02u:%02u:%02uZ",
year,
month,
day,
hour,
383 }
else if (meter_number_found) {
384 snprintf(data.meternumber,
sizeof(data.meternumber),
"%.*s", data_length, &plaintext[current_position]);
386 current_position += data_length;
390 ESP_LOGE(TAG,
"OBIS: Unsupported OBIS data type: %x", data_type);
398 if (!timestamp_found) {
399 current_position += 2;
402 current_position += 2;
406 if (current_position < message_length && plaintext[current_position] ==
DataType::INTEGER) {
408 if (current_position + 1 < message_length) {
409 int8_t scaler =
static_cast<int8_t
>(plaintext[current_position + 1]);
417 current_position += 4;
419 current_position += 6;
424 if (value_size > 0) {
426 case OBIS_VOLTAGE_L1:
427 data.voltage_l1 = value;
429 case OBIS_VOLTAGE_L2:
430 data.voltage_l2 = value;
432 case OBIS_VOLTAGE_L3:
433 data.voltage_l3 = value;
435 case OBIS_CURRENT_L1:
436 data.current_l1 = value;
438 case OBIS_CURRENT_L2:
439 data.current_l2 = value;
441 case OBIS_CURRENT_L3:
442 data.current_l3 = value;
444 case OBIS_ACTIVE_POWER_PLUS:
445 data.active_power_plus = value;
447 case OBIS_ACTIVE_POWER_MINUS:
448 data.active_power_minus = value;
450 case OBIS_ACTIVE_ENERGY_PLUS:
451 data.active_energy_plus = value;
453 case OBIS_ACTIVE_ENERGY_MINUS:
454 data.active_energy_minus = value;
456 case OBIS_REACTIVE_ENERGY_PLUS:
457 data.reactive_energy_plus = value;
459 case OBIS_REACTIVE_ENERGY_MINUS:
460 data.reactive_energy_minus = value;
462 case OBIS_POWER_FACTOR:
463 data.power_factor = value;
464 power_factor_found =
true;
467 ESP_LOGW(TAG,
"Unsupported OBIS code 0x%04X", obis_cd);
474 ESP_LOGI(TAG,
"Received valid data");