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