ESPHome 2026.10.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
9static const char *const 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 if (available())
46 this->last_transmission_ = now;
47 while (available()) {
48 uint8_t c;
49 read_byte(&c);
50 if (!this->receiving_) {
51 if (c != 0xa5)
52 continue;
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
65 if (this->trigger_next_) {
66 this->trigger_next_ = false;
67 switch (this->next_request_) {
68 case 0:
69 this->request_data_(DALY_REQUEST_BATTERY_LEVEL);
70 this->next_request_ = 1;
71 break;
72 case 1:
73 this->request_data_(DALY_REQUEST_MIN_MAX_VOLTAGE);
74 this->next_request_ = 2;
75 break;
76 case 2:
77 this->request_data_(DALY_REQUEST_MIN_MAX_TEMPERATURE);
78 this->next_request_ = 3;
79 break;
80 case 3:
81 this->request_data_(DALY_REQUEST_MOS);
82 this->next_request_ = 4;
83 break;
84 case 4:
85 this->request_data_(DALY_REQUEST_STATUS);
86 this->next_request_ = 5;
87 break;
88 case 5:
89 this->request_data_(DALY_REQUEST_CELL_VOLTAGE);
90 this->next_request_ = 6;
91 break;
92 case 6:
93 this->request_data_(DALY_REQUEST_TEMPERATURE);
94 this->next_request_ = 7;
95 break;
96 case 7:
97 default:
98 break;
99 }
100 }
101}
102
103void DalyBmsComponent::request_data_(uint8_t data_id) {
104 uint8_t request_message[DALY_FRAME_SIZE];
105
106 request_message[0] = 0xA5; // Start Flag
107 request_message[1] = this->addr_; // Communication Module Address
108 request_message[2] = data_id; // Data ID
109 request_message[3] = 0x08; // Data Length (Fixed)
110 request_message[4] = 0x00; // Empty Data
111 request_message[5] = 0x00; // |
112 request_message[6] = 0x00; // |
113 request_message[7] = 0x00; // |
114 request_message[8] = 0x00; // |
115 request_message[9] = 0x00; // |
116 request_message[10] = 0x00; // |
117 request_message[11] = 0x00; // Empty Data
118
119 request_message[12] = (uint8_t) (request_message[0] + request_message[1] + request_message[2] +
120 request_message[3]); // Checksum (Lower byte of the other bytes sum)
121
122 ESP_LOGV(TAG, "Request datapacket Nr %x", data_id);
123 this->write_array(request_message, sizeof(request_message));
124 this->flush();
125}
126
127void DalyBmsComponent::decode_data_(std::vector<uint8_t> data) {
128 auto it = data.begin();
129
130 while ((it = std::find(it, data.end(), 0xA5)) != data.end()) {
131 if (data.end() - it >= DALY_FRAME_SIZE && it[1] == 0x01) {
132 uint8_t checksum;
133 int sum = 0;
134 for (int i = 0; i < 12; i++) {
135 sum += it[i];
136 }
137 checksum = sum;
138
139 if (checksum == it[12]) {
140 switch (it[2]) {
141#ifdef USE_SENSOR
142 case DALY_REQUEST_BATTERY_LEVEL:
143 if (this->voltage_sensor_) {
144 this->voltage_sensor_->publish_state((float) encode_uint16(it[4], it[5]) / 10);
145 }
146 if (this->current_sensor_) {
147 this->current_sensor_->publish_state(((float) (encode_uint16(it[8], it[9]) - DALY_CURRENT_OFFSET) / 10));
148 }
149 if (this->battery_level_sensor_) {
150 this->battery_level_sensor_->publish_state((float) encode_uint16(it[10], it[11]) / 10);
151 }
152 break;
153
154 case DALY_REQUEST_MIN_MAX_VOLTAGE:
155 if (this->max_cell_voltage_sensor_) {
156 this->max_cell_voltage_sensor_->publish_state((float) encode_uint16(it[4], it[5]) / 1000);
157 }
158 if (this->max_cell_voltage_number_sensor_) {
159 this->max_cell_voltage_number_sensor_->publish_state(it[6]);
160 }
161 if (this->min_cell_voltage_sensor_) {
162 this->min_cell_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
163 }
164 if (this->min_cell_voltage_number_sensor_) {
165 this->min_cell_voltage_number_sensor_->publish_state(it[9]);
166 }
167 break;
168
169 case DALY_REQUEST_MIN_MAX_TEMPERATURE:
170 if (this->max_temperature_sensor_) {
171 this->max_temperature_sensor_->publish_state(it[4] - DALY_TEMPERATURE_OFFSET);
172 }
173 if (this->max_temperature_probe_number_sensor_) {
174 this->max_temperature_probe_number_sensor_->publish_state(it[5]);
175 }
176 if (this->min_temperature_sensor_) {
177 this->min_temperature_sensor_->publish_state(it[6] - DALY_TEMPERATURE_OFFSET);
178 }
179 if (this->min_temperature_probe_number_sensor_) {
180 this->min_temperature_probe_number_sensor_->publish_state(it[7]);
181 }
182 break;
183#endif
184 case DALY_REQUEST_MOS:
185#ifdef USE_TEXT_SENSOR
186 if (this->status_text_sensor_ != nullptr) {
187 switch (it[4]) {
188 case 0:
189 this->status_text_sensor_->publish_state("Stationary");
190 break;
191 case 1:
192 this->status_text_sensor_->publish_state("Charging");
193 break;
194 case 2:
195 this->status_text_sensor_->publish_state("Discharging");
196 break;
197 default:
198 break;
199 }
200 }
201#endif
202#ifdef USE_BINARY_SENSOR
203 if (this->charging_mos_enabled_binary_sensor_) {
204 this->charging_mos_enabled_binary_sensor_->publish_state(it[5]);
205 }
206 if (this->discharging_mos_enabled_binary_sensor_) {
207 this->discharging_mos_enabled_binary_sensor_->publish_state(it[6]);
208 }
209#endif
210#ifdef USE_SENSOR
211 if (this->remaining_capacity_sensor_) {
212 this->remaining_capacity_sensor_->publish_state((float) encode_uint32(it[8], it[9], it[10], it[11]) /
213 1000);
214 }
215#endif
216 break;
217
218#ifdef USE_SENSOR
219 case DALY_REQUEST_STATUS:
220 if (this->cells_number_sensor_) {
221 this->cells_number_sensor_->publish_state(it[4]);
222 }
223 break;
224
225 case DALY_REQUEST_TEMPERATURE:
226 if (it[4] == 1) {
227 if (this->temperature_1_sensor_) {
228 this->temperature_1_sensor_->publish_state(it[5] - DALY_TEMPERATURE_OFFSET);
229 }
230 if (this->temperature_2_sensor_) {
231 this->temperature_2_sensor_->publish_state(it[6] - DALY_TEMPERATURE_OFFSET);
232 }
233 }
234 break;
235
236 case DALY_REQUEST_CELL_VOLTAGE:
237 switch (it[4]) {
238 case 1:
239 if (this->cell_1_voltage_sensor_) {
240 this->cell_1_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
241 }
242 if (this->cell_2_voltage_sensor_) {
243 this->cell_2_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
244 }
245 if (this->cell_3_voltage_sensor_) {
246 this->cell_3_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
247 }
248 break;
249 case 2:
250 if (this->cell_4_voltage_sensor_) {
251 this->cell_4_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
252 }
253 if (this->cell_5_voltage_sensor_) {
254 this->cell_5_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
255 }
256 if (this->cell_6_voltage_sensor_) {
257 this->cell_6_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
258 }
259 break;
260 case 3:
261 if (this->cell_7_voltage_sensor_) {
262 this->cell_7_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
263 }
264 if (this->cell_8_voltage_sensor_) {
265 this->cell_8_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
266 }
267 if (this->cell_9_voltage_sensor_) {
268 this->cell_9_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
269 }
270 break;
271 case 4:
272 if (this->cell_10_voltage_sensor_) {
273 this->cell_10_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
274 }
275 if (this->cell_11_voltage_sensor_) {
276 this->cell_11_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
277 }
278 if (this->cell_12_voltage_sensor_) {
279 this->cell_12_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
280 }
281 break;
282 case 5:
283 if (this->cell_13_voltage_sensor_) {
284 this->cell_13_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
285 }
286 if (this->cell_14_voltage_sensor_) {
287 this->cell_14_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
288 }
289 if (this->cell_15_voltage_sensor_) {
290 this->cell_15_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
291 }
292 break;
293 case 6:
294 if (this->cell_16_voltage_sensor_) {
295 this->cell_16_voltage_sensor_->publish_state((float) encode_uint16(it[5], it[6]) / 1000);
296 }
297 if (this->cell_17_voltage_sensor_) {
298 this->cell_17_voltage_sensor_->publish_state((float) encode_uint16(it[7], it[8]) / 1000);
299 }
300 if (this->cell_18_voltage_sensor_) {
301 this->cell_18_voltage_sensor_->publish_state((float) encode_uint16(it[9], it[10]) / 1000);
302 }
303 break;
304 }
305 break;
306#endif
307 default:
308 break;
309 }
310 } else {
311 ESP_LOGW(TAG, "Checksum-Error on Packet %x", it[4]);
312 }
313 std::advance(it, DALY_FRAME_SIZE);
314 } else {
315 std::advance(it, 1);
316 }
317 }
318}
319
320} // 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:127
void request_data_(uint8_t data_id)
Definition daly_bms.cpp:103
UARTFlushResult flush()
Definition uart.h:49
bool read_byte(uint8_t *data)
Definition uart.h:35
void write_array(const uint8_t *data, size_t len)
Definition uart.h:27
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:892
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:884
Application App
Global storage of Application pointer - only one Application can exist.
static void uint32_t