|
|
|
|
@@ -40,7 +40,9 @@ bool Dsmr::ready_to_request_data_() {
|
|
|
|
|
this->start_requesting_data_();
|
|
|
|
|
}
|
|
|
|
|
if (!this->requesting_data_) {
|
|
|
|
|
this->drain_rx_buffer_();
|
|
|
|
|
while (this->available()) {
|
|
|
|
|
this->read();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return this->requesting_data_;
|
|
|
|
|
@@ -113,18 +115,10 @@ void Dsmr::stop_requesting_data_() {
|
|
|
|
|
} else {
|
|
|
|
|
ESP_LOGV(TAG, "Stop reading data from P1 port");
|
|
|
|
|
}
|
|
|
|
|
this->drain_rx_buffer_();
|
|
|
|
|
this->requesting_data_ = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Dsmr::drain_rx_buffer_() {
|
|
|
|
|
uint8_t buf[64];
|
|
|
|
|
int avail;
|
|
|
|
|
while ((avail = this->available()) > 0) {
|
|
|
|
|
if (!this->read_array(buf, std::min(static_cast<size_t>(avail), sizeof(buf)))) {
|
|
|
|
|
break;
|
|
|
|
|
while (this->available()) {
|
|
|
|
|
this->read();
|
|
|
|
|
}
|
|
|
|
|
this->requesting_data_ = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@@ -139,144 +133,120 @@ void Dsmr::reset_telegram_() {
|
|
|
|
|
|
|
|
|
|
void Dsmr::receive_telegram_() {
|
|
|
|
|
while (this->available_within_timeout_()) {
|
|
|
|
|
// Read all available bytes in batches to reduce UART call overhead.
|
|
|
|
|
uint8_t buf[64];
|
|
|
|
|
int avail = this->available();
|
|
|
|
|
while (avail > 0) {
|
|
|
|
|
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
|
|
|
|
if (!this->read_array(buf, to_read))
|
|
|
|
|
return;
|
|
|
|
|
avail -= to_read;
|
|
|
|
|
const char c = this->read();
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < to_read; i++) {
|
|
|
|
|
const char c = static_cast<char>(buf[i]);
|
|
|
|
|
// Find a new telegram header, i.e. forward slash.
|
|
|
|
|
if (c == '/') {
|
|
|
|
|
ESP_LOGV(TAG, "Header of telegram found");
|
|
|
|
|
this->reset_telegram_();
|
|
|
|
|
this->header_found_ = true;
|
|
|
|
|
}
|
|
|
|
|
if (!this->header_found_)
|
|
|
|
|
continue;
|
|
|
|
|
|
|
|
|
|
// Find a new telegram header, i.e. forward slash.
|
|
|
|
|
if (c == '/') {
|
|
|
|
|
ESP_LOGV(TAG, "Header of telegram found");
|
|
|
|
|
this->reset_telegram_();
|
|
|
|
|
this->header_found_ = true;
|
|
|
|
|
}
|
|
|
|
|
if (!this->header_found_)
|
|
|
|
|
continue;
|
|
|
|
|
// Check for buffer overflow.
|
|
|
|
|
if (this->bytes_read_ >= this->max_telegram_len_) {
|
|
|
|
|
this->reset_telegram_();
|
|
|
|
|
ESP_LOGE(TAG, "Error: telegram larger than buffer (%d bytes)", this->max_telegram_len_);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for buffer overflow.
|
|
|
|
|
if (this->bytes_read_ >= this->max_telegram_len_) {
|
|
|
|
|
this->reset_telegram_();
|
|
|
|
|
ESP_LOGE(TAG, "Error: telegram larger than buffer (%d bytes)", this->max_telegram_len_);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Some v2.2 or v3 meters will send a new value which starts with '('
|
|
|
|
|
// in a new line, while the value belongs to the previous ObisId. For
|
|
|
|
|
// proper parsing, remove these new line characters.
|
|
|
|
|
if (c == '(') {
|
|
|
|
|
while (true) {
|
|
|
|
|
auto previous_char = this->telegram_[this->bytes_read_ - 1];
|
|
|
|
|
if (previous_char == '\n' || previous_char == '\r') {
|
|
|
|
|
this->bytes_read_--;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store the byte in the buffer.
|
|
|
|
|
this->telegram_[this->bytes_read_] = c;
|
|
|
|
|
this->bytes_read_++;
|
|
|
|
|
|
|
|
|
|
// Check for a footer, i.e. exclamation mark, followed by a hex checksum.
|
|
|
|
|
if (c == '!') {
|
|
|
|
|
ESP_LOGV(TAG, "Footer of telegram found");
|
|
|
|
|
this->footer_found_ = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// Check for the end of the hex checksum, i.e. a newline.
|
|
|
|
|
if (this->footer_found_ && c == '\n') {
|
|
|
|
|
// Parse the telegram and publish sensor values.
|
|
|
|
|
this->parse_telegram();
|
|
|
|
|
this->reset_telegram_();
|
|
|
|
|
return;
|
|
|
|
|
// Some v2.2 or v3 meters will send a new value which starts with '('
|
|
|
|
|
// in a new line, while the value belongs to the previous ObisId. For
|
|
|
|
|
// proper parsing, remove these new line characters.
|
|
|
|
|
if (c == '(') {
|
|
|
|
|
while (true) {
|
|
|
|
|
auto previous_char = this->telegram_[this->bytes_read_ - 1];
|
|
|
|
|
if (previous_char == '\n' || previous_char == '\r') {
|
|
|
|
|
this->bytes_read_--;
|
|
|
|
|
} else {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store the byte in the buffer.
|
|
|
|
|
this->telegram_[this->bytes_read_] = c;
|
|
|
|
|
this->bytes_read_++;
|
|
|
|
|
|
|
|
|
|
// Check for a footer, i.e. exclamation mark, followed by a hex checksum.
|
|
|
|
|
if (c == '!') {
|
|
|
|
|
ESP_LOGV(TAG, "Footer of telegram found");
|
|
|
|
|
this->footer_found_ = true;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
// Check for the end of the hex checksum, i.e. a newline.
|
|
|
|
|
if (this->footer_found_ && c == '\n') {
|
|
|
|
|
// Parse the telegram and publish sensor values.
|
|
|
|
|
this->parse_telegram();
|
|
|
|
|
this->reset_telegram_();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Dsmr::receive_encrypted_telegram_() {
|
|
|
|
|
while (this->available_within_timeout_()) {
|
|
|
|
|
// Read all available bytes in batches to reduce UART call overhead.
|
|
|
|
|
uint8_t buf[64];
|
|
|
|
|
int avail = this->available();
|
|
|
|
|
while (avail > 0) {
|
|
|
|
|
size_t to_read = std::min(static_cast<size_t>(avail), sizeof(buf));
|
|
|
|
|
if (!this->read_array(buf, to_read))
|
|
|
|
|
return;
|
|
|
|
|
avail -= to_read;
|
|
|
|
|
const char c = this->read();
|
|
|
|
|
|
|
|
|
|
for (size_t i = 0; i < to_read; i++) {
|
|
|
|
|
const char c = static_cast<char>(buf[i]);
|
|
|
|
|
|
|
|
|
|
// Find a new telegram start byte.
|
|
|
|
|
if (!this->header_found_) {
|
|
|
|
|
if ((uint8_t) c != 0xDB) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ESP_LOGV(TAG, "Start byte 0xDB of encrypted telegram found");
|
|
|
|
|
this->reset_telegram_();
|
|
|
|
|
this->header_found_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for buffer overflow.
|
|
|
|
|
if (this->crypt_bytes_read_ >= this->max_telegram_len_) {
|
|
|
|
|
this->reset_telegram_();
|
|
|
|
|
ESP_LOGE(TAG, "Error: encrypted telegram larger than buffer (%d bytes)", this->max_telegram_len_);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store the byte in the buffer.
|
|
|
|
|
this->crypt_telegram_[this->crypt_bytes_read_] = c;
|
|
|
|
|
this->crypt_bytes_read_++;
|
|
|
|
|
|
|
|
|
|
// Read the length of the incoming encrypted telegram.
|
|
|
|
|
if (this->crypt_telegram_len_ == 0 && this->crypt_bytes_read_ > 20) {
|
|
|
|
|
// Complete header + data bytes
|
|
|
|
|
this->crypt_telegram_len_ = 13 + (this->crypt_telegram_[11] << 8 | this->crypt_telegram_[12]);
|
|
|
|
|
ESP_LOGV(TAG, "Encrypted telegram length: %d bytes", this->crypt_telegram_len_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for the end of the encrypted telegram.
|
|
|
|
|
if (this->crypt_telegram_len_ == 0 || this->crypt_bytes_read_ != this->crypt_telegram_len_) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ESP_LOGV(TAG, "End of encrypted telegram found");
|
|
|
|
|
|
|
|
|
|
// Decrypt the encrypted telegram.
|
|
|
|
|
GCM<AES128> *gcmaes128{new GCM<AES128>()};
|
|
|
|
|
gcmaes128->setKey(this->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++)
|
|
|
|
|
this->crypt_telegram_[i] = this->crypt_telegram_[i + 4];
|
|
|
|
|
constexpr uint16_t iv_size{12};
|
|
|
|
|
gcmaes128->setIV(&this->crypt_telegram_[2], iv_size);
|
|
|
|
|
gcmaes128->decrypt(reinterpret_cast<uint8_t *>(this->telegram_),
|
|
|
|
|
// the ciphertext start at byte 18
|
|
|
|
|
&this->crypt_telegram_[18],
|
|
|
|
|
// cipher size
|
|
|
|
|
this->crypt_bytes_read_ - 17);
|
|
|
|
|
delete gcmaes128; // NOLINT(cppcoreguidelines-owning-memory)
|
|
|
|
|
|
|
|
|
|
this->bytes_read_ = strnlen(this->telegram_, this->max_telegram_len_);
|
|
|
|
|
ESP_LOGV(TAG, "Decrypted telegram size: %d bytes", this->bytes_read_);
|
|
|
|
|
ESP_LOGVV(TAG, "Decrypted telegram: %s", this->telegram_);
|
|
|
|
|
|
|
|
|
|
// Parse the decrypted telegram and publish sensor values.
|
|
|
|
|
this->parse_telegram();
|
|
|
|
|
this->reset_telegram_();
|
|
|
|
|
return;
|
|
|
|
|
// Find a new telegram start byte.
|
|
|
|
|
if (!this->header_found_) {
|
|
|
|
|
if ((uint8_t) c != 0xDB) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ESP_LOGV(TAG, "Start byte 0xDB of encrypted telegram found");
|
|
|
|
|
this->reset_telegram_();
|
|
|
|
|
this->header_found_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for buffer overflow.
|
|
|
|
|
if (this->crypt_bytes_read_ >= this->max_telegram_len_) {
|
|
|
|
|
this->reset_telegram_();
|
|
|
|
|
ESP_LOGE(TAG, "Error: encrypted telegram larger than buffer (%d bytes)", this->max_telegram_len_);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Store the byte in the buffer.
|
|
|
|
|
this->crypt_telegram_[this->crypt_bytes_read_] = c;
|
|
|
|
|
this->crypt_bytes_read_++;
|
|
|
|
|
|
|
|
|
|
// Read the length of the incoming encrypted telegram.
|
|
|
|
|
if (this->crypt_telegram_len_ == 0 && this->crypt_bytes_read_ > 20) {
|
|
|
|
|
// Complete header + data bytes
|
|
|
|
|
this->crypt_telegram_len_ = 13 + (this->crypt_telegram_[11] << 8 | this->crypt_telegram_[12]);
|
|
|
|
|
ESP_LOGV(TAG, "Encrypted telegram length: %d bytes", this->crypt_telegram_len_);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Check for the end of the encrypted telegram.
|
|
|
|
|
if (this->crypt_telegram_len_ == 0 || this->crypt_bytes_read_ != this->crypt_telegram_len_) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
ESP_LOGV(TAG, "End of encrypted telegram found");
|
|
|
|
|
|
|
|
|
|
// Decrypt the encrypted telegram.
|
|
|
|
|
GCM<AES128> *gcmaes128{new GCM<AES128>()};
|
|
|
|
|
gcmaes128->setKey(this->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++)
|
|
|
|
|
this->crypt_telegram_[i] = this->crypt_telegram_[i + 4];
|
|
|
|
|
constexpr uint16_t iv_size{12};
|
|
|
|
|
gcmaes128->setIV(&this->crypt_telegram_[2], iv_size);
|
|
|
|
|
gcmaes128->decrypt(reinterpret_cast<uint8_t *>(this->telegram_),
|
|
|
|
|
// the ciphertext start at byte 18
|
|
|
|
|
&this->crypt_telegram_[18],
|
|
|
|
|
// cipher size
|
|
|
|
|
this->crypt_bytes_read_ - 17);
|
|
|
|
|
delete gcmaes128; // NOLINT(cppcoreguidelines-owning-memory)
|
|
|
|
|
|
|
|
|
|
this->bytes_read_ = strnlen(this->telegram_, this->max_telegram_len_);
|
|
|
|
|
ESP_LOGV(TAG, "Decrypted telegram size: %d bytes", this->bytes_read_);
|
|
|
|
|
ESP_LOGVV(TAG, "Decrypted telegram: %s", this->telegram_);
|
|
|
|
|
|
|
|
|
|
// Parse the decrypted telegram and publish sensor values.
|
|
|
|
|
this->parse_telegram();
|
|
|
|
|
this->reset_telegram_();
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|