From dee5d639e2771606906b0bf94b94f0f3331a6132 Mon Sep 17 00:00:00 2001 From: Maurice Makaay Date: Wed, 17 Nov 2021 18:24:02 +0100 Subject: [PATCH] Add max_telegram_length option to dsmr (#2674) Co-authored-by: Maurice Makaay Co-authored-by: Oxan van Leeuwen --- esphome/components/dsmr/__init__.py | 3 ++ esphome/components/dsmr/dsmr.cpp | 63 +++++++++++++++++------------ esphome/components/dsmr/dsmr.h | 10 ++++- tests/test3.yaml | 1 + 4 files changed, 50 insertions(+), 27 deletions(-) diff --git a/esphome/components/dsmr/__init__.py b/esphome/components/dsmr/__init__.py index dd6e6051aa..1cfc21a4ac 100644 --- a/esphome/components/dsmr/__init__.py +++ b/esphome/components/dsmr/__init__.py @@ -15,6 +15,7 @@ CONF_DSMR_ID = "dsmr_id" CONF_DECRYPTION_KEY = "decryption_key" CONF_CRC_CHECK = "crc_check" CONF_GAS_MBUS_ID = "gas_mbus_id" +CONF_MAX_TELEGRAM_LENGTH = "max_telegram_length" # Hack to prevent compile error due to ambiguity with lib namespace dsmr_ns = cg.esphome_ns.namespace("esphome::dsmr") @@ -46,6 +47,7 @@ CONFIG_SCHEMA = cv.All( cv.Optional(CONF_DECRYPTION_KEY): _validate_key, cv.Optional(CONF_CRC_CHECK, default=True): cv.boolean, cv.Optional(CONF_GAS_MBUS_ID, default=1): cv.int_, + cv.Optional(CONF_MAX_TELEGRAM_LENGTH, default=1500): cv.int_, } ).extend(uart.UART_DEVICE_SCHEMA), cv.only_with_arduino, @@ -55,6 +57,7 @@ CONFIG_SCHEMA = cv.All( async def to_code(config): uart_component = await cg.get_variable(config[CONF_UART_ID]) var = cg.new_Pvariable(config[CONF_ID], uart_component, config[CONF_CRC_CHECK]) + cg.add(var.set_max_telegram_length(config[CONF_MAX_TELEGRAM_LENGTH])) if CONF_DECRYPTION_KEY in config: cg.add(var.set_decryption_key(config[CONF_DECRYPTION_KEY])) await cg.register_component(var, config) diff --git a/esphome/components/dsmr/dsmr.cpp b/esphome/components/dsmr/dsmr.cpp index ea852e626e..631b18a1f4 100644 --- a/esphome/components/dsmr/dsmr.cpp +++ b/esphome/components/dsmr/dsmr.cpp @@ -12,11 +12,15 @@ namespace dsmr { static const char *const TAG = "dsmr"; +void Dsmr::setup() { + telegram_ = new char[max_telegram_len_]; // NOLINT +} + void Dsmr::loop() { - if (this->decryption_key_.empty()) - this->receive_telegram_(); + if (decryption_key_.empty()) + receive_telegram_(); else - this->receive_encrypted_(); + receive_encrypted_(); } bool Dsmr::available_within_timeout_() { @@ -51,10 +55,10 @@ void Dsmr::receive_telegram_() { continue; // Check for buffer overflow. - if (telegram_len_ >= MAX_TELEGRAM_LENGTH) { + if (telegram_len_ >= max_telegram_len_) { header_found_ = false; footer_found_ = false; - ESP_LOGE(TAG, "Error: telegram larger than buffer (%d bytes)", MAX_TELEGRAM_LENGTH); + ESP_LOGE(TAG, "Error: telegram larger than buffer (%d bytes)", max_telegram_len_); return; } @@ -86,9 +90,7 @@ void Dsmr::receive_telegram_() { } void Dsmr::receive_encrypted_() { - // Encrypted buffer - uint8_t buffer[MAX_TELEGRAM_LENGTH]; - size_t buffer_length = 0; + encrypted_telegram_len_ = 0; size_t packet_size = 0; while (true) { @@ -114,39 +116,39 @@ void Dsmr::receive_encrypted_() { } // Check for buffer overflow. - if (buffer_length >= MAX_TELEGRAM_LENGTH) { + if (encrypted_telegram_len_ >= max_telegram_len_) { header_found_ = false; - ESP_LOGE(TAG, "Error: encrypted telegram larger than buffer (%d bytes)", MAX_TELEGRAM_LENGTH); + ESP_LOGE(TAG, "Error: encrypted telegram larger than buffer (%d bytes)", max_telegram_len_); return; } - buffer[buffer_length++] = c; + encrypted_telegram_[encrypted_telegram_len_++] = c; - if (packet_size == 0 && buffer_length > 20) { + if (packet_size == 0 && encrypted_telegram_len_ > 20) { // Complete header + data bytes - packet_size = 13 + (buffer[11] << 8 | buffer[12]); + packet_size = 13 + (encrypted_telegram_[11] << 8 | encrypted_telegram_[12]); ESP_LOGV(TAG, "Encrypted telegram size: %d bytes", packet_size); } - if (buffer_length == packet_size && packet_size > 0) { + if (encrypted_telegram_len_ == packet_size && packet_size > 0) { ESP_LOGV(TAG, "End of encrypted telegram found"); GCM *gcmaes128{new GCM()}; - gcmaes128->setKey(this->decryption_key_.data(), gcmaes128->keySize()); + gcmaes128->setKey(decryption_key_.data(), gcmaes128->keySize()); // the iv is 8 bytes of the system title + 4 bytes frame counter // system title is at byte 2 and frame counter at byte 15 for (int i = 10; i < 14; i++) - buffer[i] = buffer[i + 4]; + encrypted_telegram_[i] = encrypted_telegram_[i + 4]; constexpr uint16_t iv_size{12}; - gcmaes128->setIV(&buffer[2], iv_size); - gcmaes128->decrypt(reinterpret_cast(this->telegram_), + gcmaes128->setIV(&encrypted_telegram_[2], iv_size); + gcmaes128->decrypt(reinterpret_cast(telegram_), // the ciphertext start at byte 18 - &buffer[18], + &encrypted_telegram_[18], // cipher size - buffer_length - 17); + encrypted_telegram_len_ - 17); delete gcmaes128; // NOLINT(cppcoreguidelines-owning-memory) - telegram_len_ = strnlen(this->telegram_, sizeof(this->telegram_)); + telegram_len_ = strnlen(telegram_, max_telegram_len_); ESP_LOGV(TAG, "Decrypted telegram size: %d bytes", telegram_len_); - ESP_LOGVV(TAG, "Decrypted telegram: %s", this->telegram_); + ESP_LOGVV(TAG, "Decrypted telegram: %s", telegram_); parse_telegram(); @@ -162,7 +164,7 @@ bool Dsmr::parse_telegram() { ESP_LOGV(TAG, "Trying to parse telegram"); ::dsmr::ParseResult res = ::dsmr::P1Parser::parse(&data, telegram_, telegram_len_, false, - this->crc_check_); // Parse telegram according to data definition. Ignore unknown values. + crc_check_); // Parse telegram according to data definition. Ignore unknown values. if (res.err) { // Parsing error, show it auto err_str = res.fullError(telegram_, telegram_ + telegram_len_); @@ -177,6 +179,7 @@ bool Dsmr::parse_telegram() { void Dsmr::dump_config() { ESP_LOGCONFIG(TAG, "DSMR:"); + ESP_LOGCONFIG(TAG, " Max telegram length: %d", max_telegram_len_); #define DSMR_LOG_SENSOR(s) LOG_SENSOR(" ", #s, this->s_##s##_); DSMR_SENSOR_LIST(DSMR_LOG_SENSOR, ) @@ -188,7 +191,11 @@ void Dsmr::dump_config() { void Dsmr::set_decryption_key(const std::string &decryption_key) { if (decryption_key.length() == 0) { ESP_LOGI(TAG, "Disabling decryption"); - this->decryption_key_.clear(); + decryption_key_.clear(); + if (encrypted_telegram_ != nullptr) { + delete[] encrypted_telegram_; + encrypted_telegram_ = nullptr; + } return; } @@ -196,7 +203,7 @@ void Dsmr::set_decryption_key(const std::string &decryption_key) { ESP_LOGE(TAG, "Error, decryption key must be 32 character long"); return; } - this->decryption_key_.clear(); + decryption_key_.clear(); ESP_LOGI(TAG, "Decryption key is set"); // Verbose level prints decryption key @@ -207,8 +214,14 @@ void Dsmr::set_decryption_key(const std::string &decryption_key) { strncpy(temp, &(decryption_key.c_str()[i * 2]), 2); decryption_key_.push_back(std::strtoul(temp, nullptr, 16)); } + + if (encrypted_telegram_ == nullptr) { + encrypted_telegram_ = new uint8_t[max_telegram_len_]; // NOLINT + } } +void Dsmr::set_max_telegram_length(size_t length) { max_telegram_len_ = length; } + } // namespace dsmr } // namespace esphome diff --git a/esphome/components/dsmr/dsmr.h b/esphome/components/dsmr/dsmr.h index ca2c0f0877..5943e4d47f 100644 --- a/esphome/components/dsmr/dsmr.h +++ b/esphome/components/dsmr/dsmr.h @@ -16,7 +16,6 @@ namespace esphome { namespace dsmr { -static constexpr uint32_t MAX_TELEGRAM_LENGTH = 1500; static constexpr uint32_t READ_TIMEOUT_MS = 200; using namespace ::dsmr::fields; @@ -52,6 +51,8 @@ class Dsmr : public Component, public uart::UARTDevice { public: Dsmr(uart::UARTComponent *uart, bool crc_check) : uart::UARTDevice(uart), crc_check_(crc_check) {} + void setup() override; + void loop() override; bool parse_telegram(); @@ -72,6 +73,8 @@ class Dsmr : public Component, public uart::UARTDevice { void set_decryption_key(const std::string &decryption_key); + void set_max_telegram_length(size_t length); + // Sensor setters #define DSMR_SET_SENSOR(s) \ void set_##s(sensor::Sensor *sensor) { s_##s##_ = sensor; } @@ -97,8 +100,11 @@ class Dsmr : public Component, public uart::UARTDevice { bool available_within_timeout_(); // Telegram buffer - char telegram_[MAX_TELEGRAM_LENGTH]; + size_t max_telegram_len_; + char *telegram_{nullptr}; int telegram_len_{0}; + uint8_t *encrypted_telegram_{nullptr}; + int encrypted_telegram_len_{0}; // Serial parser bool header_found_{false}; diff --git a/tests/test3.yaml b/tests/test3.yaml index cf80c06aa8..0b9297a0b8 100644 --- a/tests/test3.yaml +++ b/tests/test3.yaml @@ -1308,6 +1308,7 @@ fingerprint_grow: dsmr: decryption_key: 00112233445566778899aabbccddeeff uart_id: uart6 + max_telegram_length: 1000 daly_bms: update_interval: 20s