diff --git a/idf_component.yml b/idf_component.yml index 101c27e..25ea460 100644 --- a/idf_component.yml +++ b/idf_component.yml @@ -1,4 +1,3 @@ -version: "1.2.0" description: "A C++20 library for parsing DLMS/COSEM push telegrams from electricity meters. It is designed for embedded and integration-heavy environments such as ESPHome" url: "https://github.com/esphome-libs/dlms_parser" files: diff --git a/library.json b/library.json index 12aedb5..34fe3be 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "dlms_parser", - "version": "1.2.0", + "version": "1.3.0", "description": "A C++20 library for parsing DLMS/COSEM push telegrams from electricity meters. It is designed for embedded and integration-heavy environments such as ESPHome", "keywords": [ "dlms", diff --git a/src/dlms_parser/dlms_parser.cpp b/src/dlms_parser/dlms_parser.cpp index c941570..6263bea 100644 --- a/src/dlms_parser/dlms_parser.cpp +++ b/src/dlms_parser/dlms_parser.cpp @@ -22,6 +22,11 @@ static void log_span_as_hex(const LogLevel level, const std::span } } +static bool is_mbus_short_frame(const std::span data) { + if (data.size() < 5 || data[0] != 0x10 || data[4] != 0x16) return false; + return static_cast(data[1] + data[2]) == data[3]; +} + DlmsParser::DlmsParser(Aes128GcmDecryptor* decryptor) : decryptor_(decryptor) {} void DlmsParser::set_skip_crc_check(const bool skip) { @@ -72,6 +77,12 @@ ParseResult DlmsParser::parse(std::span buf, const DlmsDataCallback& co log_span_as_hex(LogLevel::VERY_VERBOSE, buf); Logger::log(LogLevel::VERY_VERBOSE, "============"); + if (is_mbus_short_frame(buf)) { + Logger::log(LogLevel::VERBOSE, "Skipping M-Bus short frame prefix"); + buf = buf.subspan(5); + if (buf.empty()) return {}; + } + std::span decoded; // Step 1: Frame decode (auto-detect HDLC / MBus / RAW from first byte) diff --git a/tests/test_meter_dumps.cpp b/tests/test_meter_dumps.cpp index 8fd5326..7914330 100644 --- a/tests/test_meter_dumps.cpp +++ b/tests/test_meter_dumps.cpp @@ -191,6 +191,17 @@ TEST_CASE("Integration: HDLC") { ); } + SUBCASE("Iskra 550 (3 segmented frames) with leading M-Bus short frame") { + std::vector frame{0x10, 0x40, 0x01, 0x41, 0x16}; + frame.insert(frame.end(), std::begin(dlms::test_data::iskra550_raw_frame), std::end(dlms::test_data::iskra550_raw_frame)); + run_meter_test( + frame, + dlms::test_data::iskra550_expected_count, + dlms::test_data::iskra550_expected_strings, + dlms::test_data::iskra550_expected_floats + ); + } + SUBCASE("Iskra 550 (3 segmented frames) and the same data at the end. Should ignore the duplicated part") { std::vector duplicated_frame(std::begin(dlms::test_data::iskra550_raw_frame), std::end(dlms::test_data::iskra550_raw_frame)); duplicated_frame.insert(duplicated_frame.end(), std::begin(dlms::test_data::iskra550_raw_frame), std::end(dlms::test_data::iskra550_raw_frame));