ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
daly_bms.cpp
Go to the documentation of this file.
1#include "daly_bms.h"
2#include <vector>
5#include "esphome/core/log.h"
6
8
9ESPHOME_LOG_TAG(TAG, "daly_bms");
10
11static const uint8_t DALY_FRAME_SIZE = 13;
12static const uint8_t DALY_TEMPERATURE_OFFSET = 40;
13static const uint16_t DALY_CURRENT_OFFSET = 30000;
14
15static const uint8_t DALY_REQUEST_BATTERY_LEVEL = 0x90;
16static const uint8_t DALY_REQUEST_MIN_MAX_VOLTAGE = 0x91;
17static const uint8_t DALY_REQUEST_MIN_MAX_TEMPERATURE = 0x92;
18static const uint8_t DALY_REQUEST_MOS = 0x93;
19static const uint8_t DALY_REQUEST_STATUS = 0x94;
20static const uint8_t DALY_REQUEST_CELL_VOLTAGE = 0x95;
21static const uint8_t DALY_REQUEST_TEMPERATURE = 0x96;
22
23void DalyBmsComponent::setup() { this->next_request_ = 1; }
24
25void DalyBmsComponent::dump_config() { ESP_LOGCONFIG(TAG, "Daly BMS:"); }
26
28 this->trigger_next_ = true;
29 this->next_request_ = 0;
30}
31
34 if (this->receiving_ && (now - this->last_transmission_ >= 200)) {
35 // last transmission too long ago. Reset RX index.
36 ESP_LOGW(TAG, "Last transmission too long ago. Reset RX index.");
37 this->data_.clear();
38 this->receiving_ = false;
39 }
40 if ((now - this->last_transmission_ >= 250) && !this->trigger_next_) {
41 // last transmittion longer than 0.25s ago -> trigger next request
42 this->last_transmission_ = now;
43 this->trigger_next_ = true;
44 }
45 const size_t avail = this->available();
46 if (avail > 0)
47 this->last_transmission_ = now;
48 this->read_available(
49 [this](uint8_t c) {
50 if (!this->receiving_) {
51 if (c != 0xa5)
52 return;
53 this->receiving_ = true;
54 }
55 this->data_.push_back(c);
56 if (this->data_.size() == 4)
57 this->data_count_ = c;
58 if ((this->data_.size() > 4) and (data_.size() == this->data_count_ + 5)) {
59 this->decode_data_(this->data_);
60 this->data_.clear();
61 this->receiving_ = false;
62 }
63 },
64 avail);
65
66 if (this->trigger_next_) {
67 this->trigger_next_ = false;
68 switch (this->next_request_) {
69 case 0:
70 this->request_data_(DALY_REQUEST_BATTERY_LEVEL);
71 this->next_request_ = 1;
72 break;
73 case 1:
74 this->request_data_(DALY_REQUEST_MIN_MAX_VOLTAGE);
75 this->next_request_ = 2;
76 break;
77 case 2:
78 this->request_data_(DALY_REQUEST_MIN_MAX_TEMPERATURE);
79 this->next_request_ = 3;
80 break;
81 case 3:
82 this->request_data_(DALY_REQUEST_MOS);
83 this->next_request_ = 4;
84 break;
85 case 4:
86 this->request_data_(DALY_REQUEST_STATUS);
87 this->next_request_ = 5;
88 break;
89 case 5:
90 this->request_data_(DALY_REQUEST_CELL_VOLTAGE);
91 this->next_request_ = 6;
92 break;
93 case 6:
94 this->request_data_(DALY_REQUEST_TEMPERATURE);
95 this->next_request_ = 7;
96 break;
97 case 7:
98 default:
99 break;
100 }
101 }
102}
103
104void DalyBmsComponent::request_data_(uint8_t data_id) {
105 uint8_t request_message[DALY_FRAME_SIZE];
106
107 request_message[0] = 0xA5; // Start Flag
108 request_message[1] = this->addr_; // Communication Module Address
109 request_message[2] = data_id; // Data ID
110 request_message[3] = 0x08; // Data Length (Fixed)
111 request_message[4] = 0x00; // Empty Data
112 request_message[5] = 0x00; // |
113 request_message[6] = 0x00; // |
114 request_message[7] = 0x00; // |
115 request_message[8] = 0x00; // |
116 request_message[9] = 0x00; // |
117 request_message[10] = 0x00; // |
118 request_message[11] = 0x00; // Empty Data
119
120 request_message[12] = (uint8_t) (request_message[0] + request_message[1] + request_message[2] +
121 request_message[3]); // Checksum (Lower byte of the other bytes sum)
122
123 ESP_LOGV(TAG, "Request datapacket Nr %x", data_id);
124 this->write_array(request_message, sizeof(request_message));
125 this->flush();
126}
127
128void DalyBmsComponent::decode_data_(std::vector<uint8_t> data) {
129 auto it = data.begin();
130
131 while ((it = std::find(it, data.end(), 0xA5)) != data.end()) {
132 if (data.end() - it >= DALY_FRAME_SIZE && it[1] == 0x01) {
133 const uint8_t checksum = sum_bytes(&*it, 12);
134
135 if (checksum == it[12]) {
136 switch (it[2]) {
137#ifdef USE_SENSOR
138 case DALY_REQUEST_BATTERY_LEVEL:
139 if (this->voltage_sensor_) {
140 this->voltage_sensor_->publish_state((float) encode_uint16(it[4], it[5]) / 10);
141 }
142 if (this->current_sensor_) {
143 this->current_sensor_->publish_state(((float) (encode_uint16(it[8], it[9]) - DALY_CURRENT_OFFSET) / 10));
144 }
145 if (this->battery_level_sensor_) {
146 this->battery_level_sensor_->publish_state((float) encode_uint16(it[10], it[11]) / 10);
147 }
148 break;
149
150 case DALY_REQUEST_MIN_MAX_VOLTAGE:
151 if (this->max_cell_voltage_sensor_) {
152 this->max_cell_voltage_sensor_->publish_state((float) encode_uint16(it[4], it[5]) / 1000);
153 }
154 if (this->max_cell_voltage_number_sensor_) {
155 this->max_cell_voltage_number_sensor_->publish_state(it[6]);
156 }
157 if (this->min_cell_voltage_sensor_) {
158 this->min_cell_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
159 }
160 if (this->min_cell_voltage_number_sensor_) {
161 this->min_cell_voltage_number_sensor_->publish_state(it[9]);
162 }
163 break;
164
165 case DALY_REQUEST_MIN_MAX_TEMPERATURE:
166 if (this->max_temperature_sensor_) {
167 this->max_temperature_sensor_->publish_state(it[4] - DALY_TEMPERATURE_OFFSET);
168 }
169 if (this->max_temperature_probe_number_sensor_) {
170 this->max_temperature_probe_number_sensor_->publish_state(it[5]);
171 }
172 if (this->min_temperature_sensor_) {
173 this->min_temperature_sensor_->publish_state(it[6] - DALY_TEMPERATURE_OFFSET);
174 }
175 if (this->min_temperature_probe_number_sensor_) {
176 this->min_temperature_probe_number_sensor_->publish_state(it[7]);
177 }
178 break;
179#endif
180 case DALY_REQUEST_MOS:
181#ifdef USE_TEXT_SENSOR
182 if (this->status_text_sensor_ != nullptr) {
183 switch (it[4]) {
184 case 0:
185 this->status_text_sensor_->publish_state("Stationary");
186 break;
187 case 1:
188 this->status_text_sensor_->publish_state("Charging");
189 break;
190 case 2:
191 this->status_text_sensor_->publish_state("Discharging");
192 break;
193 default:
194 break;
195 }
196 }
197#endif
198#ifdef USE_BINARY_SENSOR
199 if (this->charging_mos_enabled_binary_sensor_) {
200 this->charging_mos_enabled_binary_sensor_->publish_state(it[5]);
201 }
202 if (this->discharging_mos_enabled_binary_sensor_) {
203 this->discharging_mos_enabled_binary_sensor_->publish_state(it[6]);
204 }
205#endif
206#ifdef USE_SENSOR
207 if (this->remaining_capacity_sensor_) {
208 this->remaining_capacity_sensor_->publish_state((float) encode_uint32(it[8], it[9], it[10], it[11]) /
209 1000);
210 }
211#endif
212 break;
213
214#ifdef USE_SENSOR
215 case DALY_REQUEST_STATUS:
216 if (this->cells_number_sensor_) {
217 this->cells_number_sensor_->publish_state(it[4]);
218 }
219 break;
220
221 case DALY_REQUEST_TEMPERATURE:
222 if (it[4] == 1) {
223 if (this->temperature_1_sensor_) {
224 this->temperature_1_sensor_->publish_state(it[5] - DALY_TEMPERATURE_OFFSET);
225 }
226 if (this->temperature_2_sensor_) {
227 this->temperature_2_sensor_->publish_state(it[6] - DALY_TEMPERATURE_OFFSET);
228 }
229 }
230 break;
231
232 case DALY_REQUEST_CELL_VOLTAGE:
233 switch (it[4]) {
234 case 1:
235 if (this->cell_1_voltage_sensor_) {
236 this->cell_1_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
237 }
238 if (this->cell_2_voltage_sensor_) {
239 this->cell_2_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
240 }
241 if (this->cell_3_voltage_sensor_) {
242 this->cell_3_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
243 }
244 break;
245 case 2:
246 if (this->cell_4_voltage_sensor_) {
247 this->cell_4_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
248 }
249 if (this->cell_5_voltage_sensor_) {
250 this->cell_5_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
251 }
252 if (this->cell_6_voltage_sensor_) {
253 this->cell_6_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
254 }
255 break;
256 case 3:
257 if (this->cell_7_voltage_sensor_) {
258 this->cell_7_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
259 }
260 if (this->cell_8_voltage_sensor_) {
261 this->cell_8_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
262 }
263 if (this->cell_9_voltage_sensor_) {
264 this->cell_9_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
265 }
266 break;
267 case 4:
268 if (this->cell_10_voltage_sensor_) {
269 this->cell_10_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
270 }
271 if (this->cell_11_voltage_sensor_) {
272 this->cell_11_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
273 }
274 if (this->cell_12_voltage_sensor_) {
275 this->cell_12_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
276 }
277 break;
278 case 5:
279 if (this->cell_13_voltage_sensor_) {
280 this->cell_13_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
281 }
282 if (this->cell_14_voltage_sensor_) {
283 this->cell_14_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
284 }
285 if (this->cell_15_voltage_sensor_) {
286 this->cell_15_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
287 }
288 break;
289 case 6:
290 if (this->cell_16_voltage_sensor_) {
291 this->cell_16_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
292 }
293 if (this->cell_17_voltage_sensor_) {
294 this->cell_17_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
295 }
296 if (this->cell_18_voltage_sensor_) {
297 this->cell_18_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
298 }
299 break;
300 }
301 break;
302#endif
303 default:
304 break;
305 }
306 } else {
307 ESP_LOGW(TAG, "Checksum-Error on Packet %x", it[4]);
308 }
309 std::advance(it, DALY_FRAME_SIZE);
310 } else {
311 std::advance(it, 1);
312 }
313 }
314}
315
316} // namespace esphome::daly_bms
uint8_t checksum
Definition bl0906.h:3
uint32_t IRAM_ATTR HOT get_loop_component_start_time() const
Get the cached time in milliseconds from when the current component started its loop execution.
virtual void setup()
Where the component's initialization should happen.
Definition component.cpp:84
std::vector< uint8_t > data_
Definition daly_bms.h:82
void decode_data_(std::vector< uint8_t > data)
Definition daly_bms.cpp:128
void request_data_(uint8_t data_id)
Definition daly_bms.cpp:104
UARTFlushResult flush()
Definition uart.h:96
bool read_available(F &&on_byte)
Same as read_available_chunks(), calling on_byte(uint8_t) once per byte.
Definition uart.h:77
void write_array(const uint8_t *data, size_t len)
Definition uart.h:30
ESPHOME_LOG_TAG(TAG, "daly_bms")
constexpr uint8_t sum_bytes(const uint8_t *data, size_t len, uint8_t sum=0)
Add up the bytes of data with size len, truncated to 8 bits: the checksum many serial and IR protocol...
Definition helpers.h:894
constexpr uint32_t encode_uint32(uint8_t byte1, uint8_t byte2, uint8_t byte3, uint8_t byte4)
Encode a 32-bit value given four bytes in most to least significant byte order.
Definition helpers.h:1006
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
Definition helpers.h:998
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t