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