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