ESPHome  2025.4.0
sml.cpp
Go to the documentation of this file.
1 #include "sml.h"
2 #include "esphome/core/log.h"
3 #include "esphome/core/helpers.h"
4 #include "sml_parser.h"
5 
6 namespace esphome {
7 namespace sml {
8 
9 static const char *const TAG = "sml";
10 
11 const char START_BYTES_DETECTED = 1;
12 const char END_BYTES_DETECTED = 2;
13 
14 SmlListener::SmlListener(std::string server_id, std::string obis_code)
15  : server_id(std::move(server_id)), obis_code(std::move(obis_code)) {}
16 
17 char Sml::check_start_end_bytes_(uint8_t byte) {
18  this->incoming_mask_ = (this->incoming_mask_ << 2) | get_code(byte);
19 
20  if (this->incoming_mask_ == START_MASK)
21  return START_BYTES_DETECTED;
22  if ((this->incoming_mask_ >> 6) == END_MASK)
23  return END_BYTES_DETECTED;
24  return 0;
25 }
26 
27 void Sml::loop() {
28  while (available()) {
29  const char c = read();
30 
31  if (this->record_)
32  this->sml_data_.emplace_back(c);
33 
34  switch (this->check_start_end_bytes_(c)) {
35  case START_BYTES_DETECTED: {
36  this->record_ = true;
37  this->sml_data_.clear();
38  // add start sequence (for callbacks)
39  this->sml_data_.insert(this->sml_data_.begin(), START_SEQ.begin(), START_SEQ.end());
40  break;
41  };
42  case END_BYTES_DETECTED: {
43  if (this->record_) {
44  this->record_ = false;
45 
46  bool valid = check_sml_data(this->sml_data_);
47 
48  // call callbacks
49  this->data_callbacks_.call(this->sml_data_, valid);
50 
51  if (!valid)
52  break;
53 
54  // remove start/end sequence
55  this->process_sml_file_(
56  BytesView(this->sml_data_).subview(START_SEQ.size(), this->sml_data_.size() - START_SEQ.size() - 8));
57  }
58  break;
59  };
60  };
61  }
62 }
63 
64 void Sml::add_on_data_callback(std::function<void(std::vector<uint8_t>, bool)> &&callback) {
65  this->data_callbacks_.add(std::move(callback));
66 }
67 
68 void Sml::process_sml_file_(const BytesView &sml_data) {
69  SmlFile sml_file(sml_data);
70  std::vector<ObisInfo> obis_info = sml_file.get_obis_info();
71  this->publish_obis_info_(obis_info);
72 
73  this->log_obis_info_(obis_info);
74 }
75 
76 void Sml::log_obis_info_(const std::vector<ObisInfo> &obis_info_vec) {
77 #ifdef ESPHOME_LOG_HAS_DEBUG
78  ESP_LOGD(TAG, "OBIS info:");
79  for (auto const &obis_info : obis_info_vec) {
80  std::string info;
81  info += " (" + bytes_repr(obis_info.server_id) + ") ";
82  info += obis_info.code_repr();
83  info += " [0x" + bytes_repr(obis_info.value) + "]";
84  ESP_LOGD(TAG, "%s", info.c_str());
85  }
86 #endif
87 }
88 
89 void Sml::publish_obis_info_(const std::vector<ObisInfo> &obis_info_vec) {
90  for (auto const &obis_info : obis_info_vec) {
91  this->publish_value_(obis_info);
92  }
93 }
94 
95 void Sml::publish_value_(const ObisInfo &obis_info) {
96  const auto obis_code = obis_info.code_repr();
97  for (auto const &sml_listener : sml_listeners_) {
98  if ((!sml_listener->server_id.empty()) && (bytes_repr(obis_info.server_id) != sml_listener->server_id))
99  continue;
100  if (obis_code != sml_listener->obis_code)
101  continue;
102  sml_listener->publish_val(obis_info);
103  }
104 }
105 
106 void Sml::dump_config() { ESP_LOGCONFIG(TAG, "SML:"); }
107 
108 void Sml::register_sml_listener(SmlListener *listener) { sml_listeners_.emplace_back(listener); }
109 
110 bool check_sml_data(const bytes &buffer) {
111  if (buffer.size() < 2) {
112  ESP_LOGW(TAG, "Checksum error in received SML data.");
113  return false;
114  }
115 
116  uint16_t crc_received = (buffer.at(buffer.size() - 2) << 8) | buffer.at(buffer.size() - 1);
117  uint16_t crc_calculated = crc16(buffer.data() + 8, buffer.size() - 10, 0x6e23, 0x8408, true, true);
118  crc_calculated = (crc_calculated >> 8) | (crc_calculated << 8);
119  if (crc_received == crc_calculated) {
120  ESP_LOGV(TAG, "Checksum verification successful with CRC16/X25.");
121  return true;
122  }
123 
124  crc_calculated = crc16(buffer.data() + 8, buffer.size() - 10, 0xed50, 0x8408);
125  if (crc_received == crc_calculated) {
126  ESP_LOGV(TAG, "Checksum verification successful with CRC16/KERMIT.");
127  return true;
128  }
129 
130  ESP_LOGW(TAG, "Checksum error in received SML data.");
131  return false;
132 }
133 
134 uint8_t get_code(uint8_t byte) {
135  switch (byte) {
136  case 0x1b:
137  return 1;
138  case 0x01:
139  return 2;
140  case 0x1a:
141  return 3;
142  default:
143  return 0;
144  }
145 }
146 
147 } // namespace sml
148 } // namespace esphome
const uint16_t START_MASK
Definition: constants.h:21
void log_obis_info_(const std::vector< ObisInfo > &obis_info_vec)
Definition: sml.cpp:76
void loop() override
Definition: sml.cpp:27
STL namespace.
const std::vector< uint8_t > START_SEQ
Definition: constants.h:24
SmlListener(std::string server_id, std::string obis_code)
Definition: sml.cpp:14
uint16_t crc16(const uint8_t *data, uint16_t len, uint16_t crc, uint16_t reverse_poly, bool refin, bool refout)
Calculate a CRC-16 checksum of data with size len.
Definition: helpers.cpp:112
void publish_value_(const ObisInfo &obis_info)
Definition: sml.cpp:95
void register_sml_listener(SmlListener *listener)
Definition: sml.cpp:108
const char END_BYTES_DETECTED
Definition: sml.cpp:12
const uint16_t END_MASK
Definition: constants.h:22
const char START_BYTES_DETECTED
Definition: sml.cpp:11
char check_start_end_bytes_(uint8_t byte)
Definition: sml.cpp:17
uint8_t get_code(uint8_t byte)
Definition: sml.cpp:134
void add_on_data_callback(std::function< void(std::vector< uint8_t >, bool)> &&callback)
Definition: sml.cpp:64
std::vector< ObisInfo > get_obis_info()
Definition: sml_parser.cpp:85
std::string obis_code
Definition: sml.h:16
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
std::vector< uint8_t > bytes
Definition: sml_parser.h:13
void publish_obis_info_(const std::vector< ObisInfo > &obis_info_vec)
Definition: sml.cpp:89
bool check_sml_data(const bytes &buffer)
Definition: sml.cpp:110
void dump_config() override
Definition: sml.cpp:106
std::string code_repr() const
Definition: sml_parser.cpp:148
std::string bytes_repr(const BytesView &buffer)
Definition: sml_parser.cpp:104
void process_sml_file_(const BytesView &sml_data)
Definition: sml.cpp:68