ESPHome 2025.5.0
Loading...
Searching...
No Matches
sml.cpp
Go to the documentation of this file.
1#include "sml.h"
2#include "esphome/core/log.h"
4#include "sml_parser.h"
5
6namespace esphome {
7namespace sml {
8
9static const char *const TAG = "sml";
10
11const char START_BYTES_DETECTED = 1;
12const char END_BYTES_DETECTED = 2;
13
14SmlListener::SmlListener(std::string server_id, std::string obis_code)
15 : server_id(std::move(server_id)), obis_code(std::move(obis_code)) {}
16
17char 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)
22 if ((this->incoming_mask_ >> 6) == END_MASK)
23 return END_BYTES_DETECTED;
24 return 0;
25}
26
27void 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)) {
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
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
64void Sml::add_on_data_callback(std::function<void(std::vector<uint8_t>, bool)> &&callback) {
65 this->data_callbacks_.add(std::move(callback));
66}
67
68void 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
76void 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
89void 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
95void 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
106void Sml::dump_config() { ESP_LOGCONFIG(TAG, "SML:"); }
107
108void Sml::register_sml_listener(SmlListener *listener) { sml_listeners_.emplace_back(listener); }
109
110bool 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
134uint8_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
std::string code_repr() const
std::vector< ObisInfo > get_obis_info()
uint16_t incoming_mask_
Definition sml.h:38
void process_sml_file_(const BytesView &sml_data)
Definition sml.cpp:68
char check_start_end_bytes_(uint8_t byte)
Definition sml.cpp:17
void loop() override
Definition sml.cpp:27
std::vector< SmlListener * > sml_listeners_
Definition sml.h:26
CallbackManager< void(const std::vector< uint8_t > &, bool)> data_callbacks_
Definition sml.h:41
void add_on_data_callback(std::function< void(std::vector< uint8_t >, bool)> &&callback)
Definition sml.cpp:64
void register_sml_listener(SmlListener *listener)
Definition sml.cpp:108
void dump_config() override
Definition sml.cpp:106
bytes sml_data_
Definition sml.h:39
void log_obis_info_(const std::vector< ObisInfo > &obis_info_vec)
Definition sml.cpp:76
void publish_value_(const ObisInfo &obis_info)
Definition sml.cpp:95
void publish_obis_info_(const std::vector< ObisInfo > &obis_info_vec)
Definition sml.cpp:89
bool record_
Definition sml.h:37
SmlListener(std::string server_id, std::string obis_code)
Definition sml.cpp:14
std::string bytes_repr(const BytesView &buffer)
const std::vector< uint8_t > START_SEQ
Definition constants.h:24
const char END_BYTES_DETECTED
Definition sml.cpp:12
bool check_sml_data(const bytes &buffer)
Definition sml.cpp:110
uint8_t get_code(uint8_t byte)
Definition sml.cpp:134
const uint16_t START_MASK
Definition constants.h:21
std::vector< uint8_t > bytes
Definition sml_parser.h:13
const uint16_t END_MASK
Definition constants.h:22
const char START_BYTES_DETECTED
Definition sml.cpp:11
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
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