ESPHome 2025.9.0
Loading...
Searching...
No Matches
mcp4461.cpp
Go to the documentation of this file.
1#include "mcp4461.h"
2
3#include "esphome/core/hal.h"
5
6namespace esphome {
7namespace mcp4461 {
8
9static const char *const TAG = "mcp4461";
10constexpr uint8_t EEPROM_WRITE_TIMEOUT_MS = 10;
11
13 auto err = this->write(nullptr, 0);
14 if (err != i2c::ERROR_OK) {
15 this->error_code_ = MCP4461_STATUS_I2C_ERROR;
16 this->mark_failed();
17 return;
18 }
19 // save WP/WL status
21 for (uint8_t i = 0; i < 8; i++) {
22 if (this->reg_[i].initial_value.has_value()) {
23 uint16_t initial_state = static_cast<uint16_t>(*this->reg_[i].initial_value * 256.0f);
24 this->write_wiper_level_(i, initial_state);
25 }
26 if (this->reg_[i].enabled) {
27 this->reg_[i].state = this->read_wiper_level_(i);
28 } else {
29 // only volatile wipers can be set disabled on hw level
30 if (i < 4) {
31 this->reg_[i].state = 0;
32 Mcp4461WiperIdx wiper_idx = static_cast<Mcp4461WiperIdx>(i);
33 this->disable_wiper_(wiper_idx);
34 }
35 }
36 }
37}
38
39void Mcp4461Component::set_initial_value(Mcp4461WiperIdx wiper, float initial_value) {
40 uint8_t wiper_idx = static_cast<uint8_t>(wiper);
41 this->reg_[wiper_idx].initial_value = initial_value;
42}
43
45 uint8_t wiper_idx = static_cast<uint8_t>(wiper);
46 switch (terminal) {
47 case 'a':
48 this->reg_[wiper_idx].terminal_a = false;
49 break;
50 case 'b':
51 this->reg_[wiper_idx].terminal_b = false;
52 break;
53 case 'w':
54 this->reg_[wiper_idx].terminal_w = false;
55 break;
56 }
57}
58
60 uint8_t status_register_value = this->get_status_register_();
61 this->write_protected_ = static_cast<bool>((status_register_value >> 0) & 0x01);
62 this->reg_[0].wiper_lock_active = static_cast<bool>((status_register_value >> 2) & 0x01);
63 this->reg_[1].wiper_lock_active = static_cast<bool>((status_register_value >> 3) & 0x01);
64 this->reg_[2].wiper_lock_active = static_cast<bool>((status_register_value >> 5) & 0x01);
65 this->reg_[3].wiper_lock_active = static_cast<bool>((status_register_value >> 6) & 0x01);
66}
67
69 ESP_LOGCONFIG(TAG, "mcp4461:");
70 LOG_I2C_DEVICE(this);
71 if (this->is_failed()) {
72 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
73 }
74 // log wiper status
75 for (uint8_t i = 0; i < 8; ++i) {
76 // terminals only valid for volatile wipers 0-3 - enable/disable is terminal hw
77 // so also invalid for nonvolatile. For these, only print current level.
78 // reworked to be a one-line intentionally, as output would not be in order
79 if (i < 4) {
80 ESP_LOGCONFIG(TAG, " ├── Volatile wiper [%u] level: %u, Status: %s, HW: %s, A: %s, B: %s, W: %s", i,
81 this->reg_[i].state, ONOFF(this->reg_[i].terminal_hw), ONOFF(this->reg_[i].terminal_a),
82 ONOFF(this->reg_[i].terminal_b), ONOFF(this->reg_[i].terminal_w), ONOFF(this->reg_[i].enabled));
83 } else {
84 ESP_LOGCONFIG(TAG, " ├── Nonvolatile wiper [%u] level: %u", i, this->reg_[i].state);
85 }
86 }
87}
88
90 if (this->status_has_warning()) {
92 }
93 for (uint8_t i = 0; i < 8; i++) {
94 if (this->reg_[i].update_level) {
95 // set wiper i state if changed
96 if (this->reg_[i].state != this->read_wiper_level_(i)) {
97 this->write_wiper_level_(i, this->reg_[i].state);
98 }
99 }
100 this->reg_[i].update_level = false;
101 // can be true only for wipers 0-3
102 // setting changes for terminals of nonvolatile wipers
103 // is prohibited in public methods
104 if (this->reg_[i].update_terminal) {
105 // set terminal register changes
106 Mcp4461TerminalIdx terminal_connector =
108 uint8_t new_terminal_value = this->calc_terminal_connector_byte_(terminal_connector);
109 ESP_LOGV(TAG, "updating terminal %u to new value %u", static_cast<uint8_t>(terminal_connector),
110 new_terminal_value);
111 this->set_terminal_register_(terminal_connector, new_terminal_value);
112 }
113 this->reg_[i].update_terminal = false;
114 }
115}
116
118 if (this->is_failed()) {
119 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
120 return 0;
121 }
122 uint8_t addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_STATUS);
123 uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::READ);
124 uint16_t buf;
125 if (!this->read_16_(reg, &buf)) {
126 this->error_code_ = MCP4461_STATUS_REGISTER_ERROR;
127 this->mark_failed();
128 return 0;
129 }
130 uint8_t msb = buf >> 8;
131 uint8_t lsb = static_cast<uint8_t>(buf & 0x00ff);
132 if (msb != 1 || ((lsb >> 7) & 0x01) != 1 || ((lsb >> 1) & 0x01) != 1) {
133 // D8, D7 and R1 bits are hardlocked to 1 -> a status msb bit 0 (bit 9 of status register) of 0 or lsb bit 1/7 = 0
134 // indicate device/communication issues, therefore mark component failed
135 this->error_code_ = MCP4461_STATUS_REGISTER_INVALID;
136 this->mark_failed();
137 return 0;
138 }
139 this->status_clear_warning();
140 return lsb;
141}
142
144 uint8_t status_register_value = this->get_status_register_();
145 ESP_LOGI(TAG, "D7: %u, WL3: %u, WL2: %u, EEWA: %u, WL1: %u, WL0: %u, R1: %u, WP: %u",
146 ((status_register_value >> 7) & 0x01), ((status_register_value >> 6) & 0x01),
147 ((status_register_value >> 5) & 0x01), ((status_register_value >> 4) & 0x01),
148 ((status_register_value >> 3) & 0x01), ((status_register_value >> 2) & 0x01),
149 ((status_register_value >> 1) & 0x01), ((status_register_value >> 0) & 0x01));
150}
151bool Mcp4461Component::read_16_(uint8_t address, uint16_t *buf) {
152 // read 16 bits and convert from big endian to host,
153 // Do this as two separate operations to ensure a stop condition between the write and read
154 i2c::ErrorCode err = this->write(&address, 1);
155 if (err != i2c::ERROR_OK) {
156 return false;
157 }
158 err = this->read(reinterpret_cast<uint8_t *>(buf), 2);
159 if (err != i2c::ERROR_OK) {
160 return false;
161 }
162 *buf = convert_big_endian(*buf);
163 return true;
164}
165
167 uint8_t addr;
168 bool nonvolatile = false;
169 if (wiper > 3) {
170 nonvolatile = true;
171 wiper = wiper - 4;
172 }
173 switch (wiper) {
174 case 0:
175 addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_VW0);
176 break;
177 case 1:
178 addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_VW1);
179 break;
180 case 2:
181 addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_VW2);
182 break;
183 case 3:
184 addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_VW3);
185 break;
186 default:
187 ESP_LOGW(TAG, "unknown wiper specified");
188 return 0;
189 }
190 if (nonvolatile) {
191 addr = addr + 0x20;
192 }
193 return addr;
194}
195
197 if (this->is_failed()) {
198 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
199 return 0;
200 }
201 uint8_t wiper_idx = static_cast<uint8_t>(wiper);
202 if (!(this->reg_[wiper_idx].enabled)) {
203 ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
204 return 0;
205 }
206 if (!(this->reg_[wiper_idx].enabled)) {
207 ESP_LOGW(TAG, "reading from disabled volatile wiper %u, returning 0", wiper_idx);
208 return 0;
209 }
210 return this->read_wiper_level_(wiper_idx);
211}
212
213uint16_t Mcp4461Component::read_wiper_level_(uint8_t wiper_idx) {
214 uint8_t addr = this->get_wiper_address_(wiper_idx);
215 uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::READ);
216 if (wiper_idx > 3) {
217 if (!this->is_eeprom_ready_for_writing_(true)) {
218 return 0;
219 }
220 }
221 uint16_t buf = 0;
222 if (!(this->read_16_(reg, &buf))) {
223 this->error_code_ = MCP4461_STATUS_I2C_ERROR;
224 this->status_set_warning();
225 ESP_LOGW(TAG, "Error fetching %swiper %u value", (wiper_idx > 3) ? "nonvolatile " : "", wiper_idx);
226 return 0;
227 }
228 return buf;
229}
230
232 if (this->is_failed()) {
233 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
234 return false;
235 }
236 uint8_t wiper_idx = static_cast<uint8_t>(wiper);
237 if (!(this->reg_[wiper_idx].enabled)) {
238 ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
239 return false;
240 }
241 uint16_t data = this->get_wiper_level_(wiper);
242 ESP_LOGV(TAG, "Got value %u from wiper %u", data, wiper_idx);
243 this->reg_[wiper_idx].state = data;
244 return true;
245}
246
248 if (this->is_failed()) {
249 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
250 return false;
251 }
252 uint8_t wiper_idx = static_cast<uint8_t>(wiper);
253 if (value > 0x100) {
254 ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_VALUE_INVALID)));
255 return false;
256 }
257 if (!(this->reg_[wiper_idx].enabled)) {
258 ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
259 return false;
260 }
261 if (this->reg_[wiper_idx].wiper_lock_active) {
262 ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_LOCKED)));
263 return false;
264 }
265 ESP_LOGV(TAG, "Setting MCP4461 wiper %u to %u", wiper_idx, value);
266 this->reg_[wiper_idx].state = value;
267 this->reg_[wiper_idx].update_level = true;
268 return true;
269}
270
271void Mcp4461Component::write_wiper_level_(uint8_t wiper, uint16_t value) {
272 bool nonvolatile = wiper > 3;
273 if (!(this->mcp4461_write_(this->get_wiper_address_(wiper), value, nonvolatile))) {
274 this->error_code_ = MCP4461_STATUS_I2C_ERROR;
275 this->status_set_warning();
276 ESP_LOGW(TAG, "Error writing %swiper %u level %u", (wiper > 3) ? "nonvolatile " : "", wiper, value);
277 }
278}
279
281 if (this->is_failed()) {
282 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
283 return;
284 }
285 uint8_t wiper_idx = static_cast<uint8_t>(wiper);
286 if ((this->reg_[wiper_idx].enabled)) {
287 ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_ENABLED)));
288 return;
289 }
290 if (this->reg_[wiper_idx].wiper_lock_active) {
291 ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_LOCKED)));
292 return;
293 }
294 ESP_LOGV(TAG, "Enabling wiper %u", wiper_idx);
295 this->reg_[wiper_idx].enabled = true;
296 if (wiper_idx < 4) {
297 this->reg_[wiper_idx].terminal_hw = true;
298 this->reg_[wiper_idx].update_terminal = true;
299 }
300}
301
303 if (this->is_failed()) {
304 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
305 return;
306 }
307 uint8_t wiper_idx = static_cast<uint8_t>(wiper);
308 if (!(this->reg_[wiper_idx].enabled)) {
309 ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
310 return;
311 }
312 if (this->reg_[wiper_idx].wiper_lock_active) {
313 ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_LOCKED)));
314 return;
315 }
316 ESP_LOGV(TAG, "Disabling wiper %u", wiper_idx);
317 this->reg_[wiper_idx].enabled = true;
318 if (wiper_idx < 4) {
319 this->reg_[wiper_idx].terminal_hw = true;
320 this->reg_[wiper_idx].update_terminal = true;
321 }
322}
323
325 if (this->is_failed()) {
326 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
327 return false;
328 }
329 uint8_t wiper_idx = static_cast<uint8_t>(wiper);
330 if (!(this->reg_[wiper_idx].enabled)) {
331 ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
332 return false;
333 }
334 if (this->reg_[wiper_idx].wiper_lock_active) {
335 ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_LOCKED)));
336 return false;
337 }
338 if (this->reg_[wiper_idx].state == 256) {
339 ESP_LOGV(TAG, "Maximum wiper level reached, further increase of wiper %u prohibited", wiper_idx);
340 return false;
341 }
342 ESP_LOGV(TAG, "Increasing wiper %u", wiper_idx);
343 uint8_t addr = this->get_wiper_address_(wiper_idx);
344 uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::INCREMENT);
345 auto err = this->write(&this->address_, reg);
346 if (err != i2c::ERROR_OK) {
347 this->error_code_ = MCP4461_STATUS_I2C_ERROR;
348 this->status_set_warning();
349 return false;
350 }
351 this->reg_[wiper_idx].state++;
352 return true;
353}
354
356 if (this->is_failed()) {
357 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
358 return false;
359 }
360 uint8_t wiper_idx = static_cast<uint8_t>(wiper);
361 if (!(this->reg_[wiper_idx].enabled)) {
362 ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
363 return false;
364 }
365 if (this->reg_[wiper_idx].wiper_lock_active) {
366 ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_LOCKED)));
367 return false;
368 }
369 if (this->reg_[wiper_idx].state == 0) {
370 ESP_LOGV(TAG, "Minimum wiper level reached, further decrease of wiper %u prohibited", wiper_idx);
371 return false;
372 }
373 ESP_LOGV(TAG, "Decreasing wiper %u", wiper_idx);
374 uint8_t addr = this->get_wiper_address_(wiper_idx);
375 uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::DECREMENT);
376 auto err = this->write(&this->address_, reg);
377 if (err != i2c::ERROR_OK) {
378 this->error_code_ = MCP4461_STATUS_I2C_ERROR;
379 this->status_set_warning();
380 return false;
381 }
382 this->reg_[wiper_idx].state--;
383 return true;
384}
385
387 uint8_t i = static_cast<uint8_t>(terminal_connector) <= 1 ? 0 : 2;
388 uint8_t new_value_byte = 0;
389 new_value_byte += static_cast<uint8_t>(this->reg_[i].terminal_b);
390 new_value_byte += static_cast<uint8_t>(this->reg_[i].terminal_w) << 1;
391 new_value_byte += static_cast<uint8_t>(this->reg_[i].terminal_a) << 2;
392 new_value_byte += static_cast<uint8_t>(this->reg_[i].terminal_hw) << 3;
393 new_value_byte += static_cast<uint8_t>(this->reg_[(i + 1)].terminal_b) << 4;
394 new_value_byte += static_cast<uint8_t>(this->reg_[(i + 1)].terminal_w) << 5;
395 new_value_byte += static_cast<uint8_t>(this->reg_[(i + 1)].terminal_a) << 6;
396 new_value_byte += static_cast<uint8_t>(this->reg_[(i + 1)].terminal_hw) << 7;
397 return new_value_byte;
398}
399
401 if (this->is_failed()) {
402 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
403 return 0;
404 }
405 uint8_t reg = static_cast<uint8_t>(terminal_connector) == 0 ? static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON0)
406 : static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON1);
407 reg |= static_cast<uint8_t>(Mcp4461Commands::READ);
408 uint16_t buf;
409 if (this->read_16_(reg, &buf)) {
410 return static_cast<uint8_t>(buf & 0x00ff);
411 } else {
412 this->error_code_ = MCP4461_STATUS_I2C_ERROR;
413 this->status_set_warning();
414 ESP_LOGW(TAG, "Error fetching terminal register value");
415 return 0;
416 }
417}
418
420 if (this->is_failed()) {
421 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
422 return;
423 }
424 if ((static_cast<uint8_t>(terminal_connector) != 0 && static_cast<uint8_t>(terminal_connector) != 1)) {
425 return;
426 }
427 uint8_t terminal_data = this->get_terminal_register_(terminal_connector);
428 if (terminal_data == 0) {
429 return;
430 }
431 ESP_LOGV(TAG, "Got terminal register %u data 0x%02X", static_cast<uint8_t>(terminal_connector), terminal_data);
432 uint8_t wiper_index = 0;
433 if (static_cast<uint8_t>(terminal_connector) == 1) {
434 wiper_index = 2;
435 }
436 this->reg_[wiper_index].terminal_b = ((terminal_data >> 0) & 0x01);
437 this->reg_[wiper_index].terminal_w = ((terminal_data >> 1) & 0x01);
438 this->reg_[wiper_index].terminal_a = ((terminal_data >> 2) & 0x01);
439 this->reg_[wiper_index].terminal_hw = ((terminal_data >> 3) & 0x01);
440 this->reg_[(wiper_index + 1)].terminal_b = ((terminal_data >> 4) & 0x01);
441 this->reg_[(wiper_index + 1)].terminal_w = ((terminal_data >> 5) & 0x01);
442 this->reg_[(wiper_index + 1)].terminal_a = ((terminal_data >> 6) & 0x01);
443 this->reg_[(wiper_index + 1)].terminal_hw = ((terminal_data >> 7) & 0x01);
444}
445
446bool Mcp4461Component::set_terminal_register_(Mcp4461TerminalIdx terminal_connector, uint8_t data) {
447 if (this->is_failed()) {
448 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
449 return false;
450 }
451 uint8_t addr;
452 if (static_cast<uint8_t>(terminal_connector) == 0) {
453 addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON0);
454 } else if (static_cast<uint8_t>(terminal_connector) == 1) {
455 addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON1);
456 } else {
457 ESP_LOGW(TAG, "Invalid terminal connector id %u specified", static_cast<uint8_t>(terminal_connector));
458 return false;
459 }
460 if (!(this->mcp4461_write_(addr, data))) {
461 this->error_code_ = MCP4461_STATUS_I2C_ERROR;
462 this->status_set_warning();
463 return false;
464 }
465 return true;
466}
467
469 if (this->is_failed()) {
470 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
471 return;
472 }
473 uint8_t wiper_idx = static_cast<uint8_t>(wiper);
474 ESP_LOGV(TAG, "Enabling terminal %c of wiper %u", terminal, wiper_idx);
475 switch (terminal) {
476 case 'h':
477 this->reg_[wiper_idx].terminal_hw = true;
478 break;
479 case 'a':
480 this->reg_[wiper_idx].terminal_a = true;
481 break;
482 case 'b':
483 this->reg_[wiper_idx].terminal_b = true;
484 break;
485 case 'w':
486 this->reg_[wiper_idx].terminal_w = true;
487 break;
488 default:
489 ESP_LOGW(TAG, "Unknown terminal %c specified", terminal);
490 return;
491 }
492 this->reg_[wiper_idx].update_terminal = false;
493}
494
496 if (this->is_failed()) {
497 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
498 return;
499 }
500 uint8_t wiper_idx = static_cast<uint8_t>(wiper);
501 ESP_LOGV(TAG, "Disabling terminal %c of wiper %u", terminal, wiper_idx);
502 switch (terminal) {
503 case 'h':
504 this->reg_[wiper_idx].terminal_hw = false;
505 break;
506 case 'a':
507 this->reg_[wiper_idx].terminal_a = false;
508 break;
509 case 'b':
510 this->reg_[wiper_idx].terminal_b = false;
511 break;
512 case 'w':
513 this->reg_[wiper_idx].terminal_w = false;
514 break;
515 default:
516 ESP_LOGW(TAG, "Unknown terminal %c specified", terminal);
517 return;
518 }
519 this->reg_[wiper_idx].update_terminal = false;
520}
521
523 if (this->is_failed()) {
524 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
525 return 0;
526 }
527 uint8_t reg = 0;
528 reg |= static_cast<uint8_t>(Mcp4461EepromLocation::MCP4461_EEPROM_1) + (static_cast<uint8_t>(location) * 0x10);
529 reg |= static_cast<uint8_t>(Mcp4461Commands::READ);
530 uint16_t buf;
531 if (!this->is_eeprom_ready_for_writing_(true)) {
532 return 0;
533 }
534 if (!this->read_16_(reg, &buf)) {
535 this->error_code_ = MCP4461_STATUS_I2C_ERROR;
536 this->status_set_warning();
537 ESP_LOGW(TAG, "Error fetching EEPROM location value");
538 return 0;
539 }
540 return buf;
541}
542
544 if (this->is_failed()) {
545 ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
546 return false;
547 }
548 uint8_t addr = 0;
549 if (value > 511) {
550 return false;
551 }
552 if (value > 256) {
553 addr = 1;
554 }
555 addr |= static_cast<uint8_t>(Mcp4461EepromLocation::MCP4461_EEPROM_1) + (static_cast<uint8_t>(location) * 0x10);
556 if (!(this->mcp4461_write_(addr, value, true))) {
557 this->error_code_ = MCP4461_STATUS_I2C_ERROR;
558 this->status_set_warning();
559 ESP_LOGW(TAG, "Error writing EEPROM value");
560 return false;
561 }
562 return true;
563}
564
566 /* Read the EEPROM write-active status from the status register */
567 bool writing = static_cast<bool>((this->get_status_register_() >> 4) & 0x01);
568
569 /* If EEPROM is no longer writing, reset the timeout flag */
570 if (!writing) {
571 /* This is protected boolean flag in Mcp4461Component class */
572 this->last_eeprom_write_timed_out_ = false;
573 }
574
575 return writing;
576}
577
579 /* Check initial write status */
580 bool ready_for_write = !this->is_writing_();
581
582 /* Return early if no waiting is required or EEPROM is already ready */
583 if (ready_for_write || !wait_if_not_ready || this->last_eeprom_write_timed_out_) {
584 return ready_for_write;
585 }
586
587 /* Timestamp before starting the loop */
588 const uint32_t start_millis = millis();
589
590 ESP_LOGV(TAG, "Waiting until EEPROM is ready for write, start_millis = %" PRIu32, start_millis);
591
592 /* Loop until EEPROM is ready or timeout is reached */
593 while (!ready_for_write && ((millis() - start_millis) < EEPROM_WRITE_TIMEOUT_MS)) {
594 ready_for_write = !this->is_writing_();
595
596 /* If ready, exit early */
597 if (ready_for_write) {
598 ESP_LOGV(TAG, "EEPROM is ready for new write, elapsed_millis = %" PRIu32, millis() - start_millis);
599 return true;
600 }
601
602 /* Not ready yet, yield before checking again */
603 yield();
604 }
605
606 /* If still not ready after timeout, log error and mark the timeout */
607 ESP_LOGE(TAG, "EEPROM write timeout exceeded (%u ms)", EEPROM_WRITE_TIMEOUT_MS);
608 this->last_eeprom_write_timed_out_ = true;
609
610 return false;
611}
612
613bool Mcp4461Component::mcp4461_write_(uint8_t addr, uint16_t data, bool nonvolatile) {
614 uint8_t reg = data > 0xff ? 1 : 0;
615 uint8_t value_byte = static_cast<uint8_t>(data & 0x00ff);
616 ESP_LOGV(TAG, "Writing value %u to address %u", data, addr);
617 reg |= addr;
618 reg |= static_cast<uint8_t>(Mcp4461Commands::WRITE);
619 if (nonvolatile) {
620 if (this->write_protected_) {
621 ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WRITE_PROTECTED)));
622 return false;
623 }
624 if (!this->is_eeprom_ready_for_writing_(true)) {
625 return false;
626 }
627 }
628 return this->write_byte(reg, value_byte);
629}
630} // namespace mcp4461
631} // namespace esphome
uint8_t address
Definition bl0906.h:4
virtual void mark_failed()
Mark this component as failed.
bool is_failed() const
void status_set_warning(const char *message=nullptr)
bool status_has_warning() const
void status_clear_warning()
ErrorCode write(const uint8_t *data, size_t len) const
writes an array of bytes to a device using an I2CBus
Definition i2c.h:184
ErrorCode read(uint8_t *data, size_t len) const
reads an array of bytes from the device using an I2CBus
Definition i2c.h:164
uint8_t address_
store the address of the device on the bus
Definition i2c.h:302
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:266
I2CRegister reg(uint8_t a_register)
calls the I2CRegister constructor
Definition i2c.h:153
void set_initial_value(Mcp4461WiperIdx wiper, float initial_value)
public function used to set initial value
Definition mcp4461.cpp:39
bool increase_wiper_(Mcp4461WiperIdx wiper)
Definition mcp4461.cpp:324
uint8_t calc_terminal_connector_byte_(Mcp4461TerminalIdx terminal_connector)
Definition mcp4461.cpp:386
uint8_t get_terminal_register_(Mcp4461TerminalIdx terminal_connector)
Definition mcp4461.cpp:400
void update_terminal_register_(Mcp4461TerminalIdx terminal_connector)
Definition mcp4461.cpp:419
uint16_t get_eeprom_value(Mcp4461EepromLocation location)
get eeprom value from location
Definition mcp4461.cpp:522
bool set_eeprom_value(Mcp4461EepromLocation location, uint16_t value)
set eeprom value at specified location
Definition mcp4461.cpp:543
void disable_wiper_(Mcp4461WiperIdx wiper)
Definition mcp4461.cpp:302
void initialize_terminal_disabled(Mcp4461WiperIdx wiper, char terminal)
public function used to set disable terminal config
Definition mcp4461.cpp:44
void enable_wiper_(Mcp4461WiperIdx wiper)
Definition mcp4461.cpp:280
uint16_t read_wiper_level_(uint8_t wiper)
Definition mcp4461.cpp:213
bool mcp4461_write_(uint8_t addr, uint16_t data, bool nonvolatile=false)
Definition mcp4461.cpp:613
bool read_16_(uint8_t address, uint16_t *buf)
Definition mcp4461.cpp:151
void enable_terminal_(Mcp4461WiperIdx wiper, char terminal)
Definition mcp4461.cpp:468
bool is_eeprom_ready_for_writing_(bool wait_if_not_ready)
Definition mcp4461.cpp:578
uint16_t get_wiper_level_(Mcp4461WiperIdx wiper)
Definition mcp4461.cpp:196
static const LogString * get_message_string(int status)
Definition mcp4461.h:123
bool update_wiper_level_(Mcp4461WiperIdx wiper)
Definition mcp4461.cpp:231
uint8_t get_wiper_address_(uint8_t wiper)
Definition mcp4461.cpp:166
void disable_terminal_(Mcp4461WiperIdx, char terminal)
Definition mcp4461.cpp:495
void write_wiper_level_(uint8_t wiper, uint16_t value)
Definition mcp4461.cpp:271
bool decrease_wiper_(Mcp4461WiperIdx wiper)
Definition mcp4461.cpp:355
bool set_terminal_register_(Mcp4461TerminalIdx terminal_connector, uint8_t data)
Definition mcp4461.cpp:446
bool set_wiper_level_(Mcp4461WiperIdx wiper, uint16_t value)
Definition mcp4461.cpp:247
void read_status_register_to_log()
read status register to log
Definition mcp4461.cpp:143
bool state
Definition fan.h:0
ErrorCode
Error codes returned by I2CBus and I2CDevice methods.
Definition i2c_bus.h:31
@ ERROR_OK
No error found during execution of method.
Definition i2c_bus.h:33
constexpr uint8_t EEPROM_WRITE_TIMEOUT_MS
Definition mcp4461.cpp:10
Providing packet encoding functions for exchanging data with a remote host.
Definition a01nyub.cpp:7
constexpr T convert_big_endian(T val)
Convert a value between host byte order and big endian (most significant byte first) order.
Definition helpers.h:230
void IRAM_ATTR HOT yield()
Definition core.cpp:27
uint32_t IRAM_ATTR HOT millis()
Definition core.cpp:28
optional< float > initial_value
Definition mcp4461.h:13