ESPHome  2025.4.0
mcp4461.cpp
Go to the documentation of this file.
1 #include "mcp4461.h"
2 
3 #include "esphome/core/helpers.h"
4 #include "esphome/core/hal.h"
5 
6 namespace esphome {
7 namespace mcp4461 {
8 
9 static const char *const TAG = "mcp4461";
10 constexpr uint8_t EEPROM_WRITE_TIMEOUT_MS = 10;
11 
13  ESP_LOGCONFIG(TAG, "Setting up mcp4461 using address (0x%02X)...", this->address_);
14  auto err = this->write(nullptr, 0);
15  if (err != i2c::ERROR_OK) {
16  this->error_code_ = MCP4461_STATUS_I2C_ERROR;
17  this->mark_failed();
18  return;
19  }
20  // save WP/WL status
22  for (uint8_t i = 0; i < 8; i++) {
23  if (this->reg_[i].initial_value.has_value()) {
24  uint16_t initial_state = static_cast<uint16_t>(*this->reg_[i].initial_value * 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 
40 void 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].terminal_hw), ONOFF(this->reg_[i].terminal_a),
83  ONOFF(this->reg_[i].terminal_b), ONOFF(this->reg_[i].terminal_w), ONOFF(this->reg_[i].enabled));
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()) {
92  this->get_status_register_();
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_byte_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 }
152 
153 uint8_t Mcp4461Component::get_wiper_address_(uint8_t wiper) {
154  uint8_t addr;
155  bool nonvolatile = false;
156  if (wiper > 3) {
157  nonvolatile = true;
158  wiper = wiper - 4;
159  }
160  switch (wiper) {
161  case 0:
162  addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_VW0);
163  break;
164  case 1:
165  addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_VW1);
166  break;
167  case 2:
168  addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_VW2);
169  break;
170  case 3:
171  addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_VW3);
172  break;
173  default:
174  ESP_LOGW(TAG, "unknown wiper specified");
175  return 0;
176  }
177  if (nonvolatile) {
178  addr = addr + 0x20;
179  }
180  return addr;
181 }
182 
184  if (this->is_failed()) {
185  ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
186  return 0;
187  }
188  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
189  if (!(this->reg_[wiper_idx].enabled)) {
190  ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
191  return 0;
192  }
193  if (!(this->reg_[wiper_idx].enabled)) {
194  ESP_LOGW(TAG, "reading from disabled volatile wiper %u, returning 0", wiper_idx);
195  return 0;
196  }
197  return this->read_wiper_level_(wiper_idx);
198 }
199 
200 uint16_t Mcp4461Component::read_wiper_level_(uint8_t wiper_idx) {
201  uint8_t addr = this->get_wiper_address_(wiper_idx);
202  uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::INCREMENT);
203  if (wiper_idx > 3) {
204  if (!this->is_eeprom_ready_for_writing_(true)) {
205  return 0;
206  }
207  }
208  uint16_t buf = 0;
209  if (!(this->read_byte_16(reg, &buf))) {
210  this->error_code_ = MCP4461_STATUS_I2C_ERROR;
211  this->status_set_warning();
212  ESP_LOGW(TAG, "Error fetching %swiper %u value", (wiper_idx > 3) ? "nonvolatile " : "", wiper_idx);
213  return 0;
214  }
215  return buf;
216 }
217 
219  if (this->is_failed()) {
220  ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
221  return false;
222  }
223  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
224  if (!(this->reg_[wiper_idx].enabled)) {
225  ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
226  return false;
227  }
228  uint16_t data = this->get_wiper_level_(wiper);
229  ESP_LOGV(TAG, "Got value %u from wiper %u", data, wiper_idx);
230  this->reg_[wiper_idx].state = data;
231  return true;
232 }
233 
235  if (this->is_failed()) {
236  ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
237  return false;
238  }
239  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
240  if (value > 0x100) {
241  ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_VALUE_INVALID)));
242  return false;
243  }
244  if (!(this->reg_[wiper_idx].enabled)) {
245  ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
246  return false;
247  }
248  if (this->reg_[wiper_idx].wiper_lock_active) {
249  ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_LOCKED)));
250  return false;
251  }
252  ESP_LOGV(TAG, "Setting MCP4461 wiper %u to %u", wiper_idx, value);
253  this->reg_[wiper_idx].state = value;
254  this->reg_[wiper_idx].update_level = true;
255  return true;
256 }
257 
258 void Mcp4461Component::write_wiper_level_(uint8_t wiper, uint16_t value) {
259  bool nonvolatile = wiper > 3;
260  if (!(this->mcp4461_write_(this->get_wiper_address_(wiper), value, nonvolatile))) {
261  this->error_code_ = MCP4461_STATUS_I2C_ERROR;
262  this->status_set_warning();
263  ESP_LOGW(TAG, "Error writing %swiper %u level %u", (wiper > 3) ? "nonvolatile " : "", wiper, value);
264  }
265 }
266 
268  if (this->is_failed()) {
269  ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
270  return;
271  }
272  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
273  if ((this->reg_[wiper_idx].enabled)) {
274  ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_ENABLED)));
275  return;
276  }
277  if (this->reg_[wiper_idx].wiper_lock_active) {
278  ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_LOCKED)));
279  return;
280  }
281  ESP_LOGV(TAG, "Enabling wiper %u", wiper_idx);
282  this->reg_[wiper_idx].enabled = true;
283  if (wiper_idx < 4) {
284  this->reg_[wiper_idx].terminal_hw = true;
285  this->reg_[wiper_idx].update_terminal = true;
286  }
287 }
288 
290  if (this->is_failed()) {
291  ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
292  return;
293  }
294  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
295  if (!(this->reg_[wiper_idx].enabled)) {
296  ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
297  return;
298  }
299  if (this->reg_[wiper_idx].wiper_lock_active) {
300  ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_LOCKED)));
301  return;
302  }
303  ESP_LOGV(TAG, "Disabling wiper %u", wiper_idx);
304  this->reg_[wiper_idx].enabled = true;
305  if (wiper_idx < 4) {
306  this->reg_[wiper_idx].terminal_hw = true;
307  this->reg_[wiper_idx].update_terminal = true;
308  }
309 }
310 
312  if (this->is_failed()) {
313  ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
314  return false;
315  }
316  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
317  if (!(this->reg_[wiper_idx].enabled)) {
318  ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
319  return false;
320  }
321  if (this->reg_[wiper_idx].wiper_lock_active) {
322  ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_LOCKED)));
323  return false;
324  }
325  if (this->reg_[wiper_idx].state == 256) {
326  ESP_LOGV(TAG, "Maximum wiper level reached, further increase of wiper %u prohibited", wiper_idx);
327  return false;
328  }
329  ESP_LOGV(TAG, "Increasing wiper %u", wiper_idx);
330  uint8_t addr = this->get_wiper_address_(wiper_idx);
331  uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::INCREMENT);
332  auto err = this->write(&this->address_, reg, sizeof(reg));
333  if (err != i2c::ERROR_OK) {
334  this->error_code_ = MCP4461_STATUS_I2C_ERROR;
335  this->status_set_warning();
336  return false;
337  }
338  this->reg_[wiper_idx].state++;
339  return true;
340 }
341 
343  if (this->is_failed()) {
344  ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
345  return false;
346  }
347  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
348  if (!(this->reg_[wiper_idx].enabled)) {
349  ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_DISABLED)));
350  return false;
351  }
352  if (this->reg_[wiper_idx].wiper_lock_active) {
353  ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WIPER_LOCKED)));
354  return false;
355  }
356  if (this->reg_[wiper_idx].state == 0) {
357  ESP_LOGV(TAG, "Minimum wiper level reached, further decrease of wiper %u prohibited", wiper_idx);
358  return false;
359  }
360  ESP_LOGV(TAG, "Decreasing wiper %u", wiper_idx);
361  uint8_t addr = this->get_wiper_address_(wiper_idx);
362  uint8_t reg = addr | static_cast<uint8_t>(Mcp4461Commands::DECREMENT);
363  auto err = this->write(&this->address_, reg, sizeof(reg));
364  if (err != i2c::ERROR_OK) {
365  this->error_code_ = MCP4461_STATUS_I2C_ERROR;
366  this->status_set_warning();
367  return false;
368  }
369  this->reg_[wiper_idx].state--;
370  return true;
371 }
372 
374  uint8_t i = static_cast<uint8_t>(terminal_connector) <= 1 ? 0 : 2;
375  uint8_t new_value_byte = 0;
376  new_value_byte += static_cast<uint8_t>(this->reg_[i].terminal_b);
377  new_value_byte += static_cast<uint8_t>(this->reg_[i].terminal_w) << 1;
378  new_value_byte += static_cast<uint8_t>(this->reg_[i].terminal_a) << 2;
379  new_value_byte += static_cast<uint8_t>(this->reg_[i].terminal_hw) << 3;
380  new_value_byte += static_cast<uint8_t>(this->reg_[(i + 1)].terminal_b) << 4;
381  new_value_byte += static_cast<uint8_t>(this->reg_[(i + 1)].terminal_w) << 5;
382  new_value_byte += static_cast<uint8_t>(this->reg_[(i + 1)].terminal_a) << 6;
383  new_value_byte += static_cast<uint8_t>(this->reg_[(i + 1)].terminal_hw) << 7;
384  return new_value_byte;
385 }
386 
388  if (this->is_failed()) {
389  ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
390  return 0;
391  }
392  uint8_t reg = static_cast<uint8_t>(terminal_connector) == 0 ? static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON0)
393  : static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON1);
394  reg |= static_cast<uint8_t>(Mcp4461Commands::READ);
395  uint16_t buf;
396  if (this->read_byte_16(reg, &buf)) {
397  return static_cast<uint8_t>(buf & 0x00ff);
398  } else {
399  this->error_code_ = MCP4461_STATUS_I2C_ERROR;
400  this->status_set_warning();
401  ESP_LOGW(TAG, "Error fetching terminal register value");
402  return 0;
403  }
404 }
405 
407  if (this->is_failed()) {
408  ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
409  return;
410  }
411  if ((static_cast<uint8_t>(terminal_connector) != 0 && static_cast<uint8_t>(terminal_connector) != 1)) {
412  return;
413  }
414  uint8_t terminal_data = this->get_terminal_register_(terminal_connector);
415  if (terminal_data == 0) {
416  return;
417  }
418  ESP_LOGV(TAG, "Got terminal register %u data 0x%02X", static_cast<uint8_t>(terminal_connector), terminal_data);
419  uint8_t wiper_index = 0;
420  if (static_cast<uint8_t>(terminal_connector) == 1) {
421  wiper_index = 2;
422  }
423  this->reg_[wiper_index].terminal_b = ((terminal_data >> 0) & 0x01);
424  this->reg_[wiper_index].terminal_w = ((terminal_data >> 1) & 0x01);
425  this->reg_[wiper_index].terminal_a = ((terminal_data >> 2) & 0x01);
426  this->reg_[wiper_index].terminal_hw = ((terminal_data >> 3) & 0x01);
427  this->reg_[(wiper_index + 1)].terminal_b = ((terminal_data >> 4) & 0x01);
428  this->reg_[(wiper_index + 1)].terminal_w = ((terminal_data >> 5) & 0x01);
429  this->reg_[(wiper_index + 1)].terminal_a = ((terminal_data >> 6) & 0x01);
430  this->reg_[(wiper_index + 1)].terminal_hw = ((terminal_data >> 7) & 0x01);
431 }
432 
433 bool Mcp4461Component::set_terminal_register_(Mcp4461TerminalIdx terminal_connector, uint8_t data) {
434  if (this->is_failed()) {
435  ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
436  return false;
437  }
438  uint8_t addr;
439  if (static_cast<uint8_t>(terminal_connector) == 0) {
440  addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON0);
441  } else if (static_cast<uint8_t>(terminal_connector) == 1) {
442  addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON1);
443  } else {
444  ESP_LOGW(TAG, "Invalid terminal connector id %u specified", static_cast<uint8_t>(terminal_connector));
445  return false;
446  }
447  if (!(this->mcp4461_write_(addr, data))) {
448  this->error_code_ = MCP4461_STATUS_I2C_ERROR;
449  this->status_set_warning();
450  return false;
451  }
452  return true;
453 }
454 
456  if (this->is_failed()) {
457  ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
458  return;
459  }
460  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
461  ESP_LOGV(TAG, "Enabling terminal %c of wiper %u", terminal, wiper_idx);
462  switch (terminal) {
463  case 'h':
464  this->reg_[wiper_idx].terminal_hw = true;
465  break;
466  case 'a':
467  this->reg_[wiper_idx].terminal_a = true;
468  break;
469  case 'b':
470  this->reg_[wiper_idx].terminal_b = true;
471  break;
472  case 'w':
473  this->reg_[wiper_idx].terminal_w = true;
474  break;
475  default:
476  ESP_LOGW(TAG, "Unknown terminal %c specified", terminal);
477  return;
478  }
479  this->reg_[wiper_idx].update_terminal = false;
480 }
481 
483  if (this->is_failed()) {
484  ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
485  return;
486  }
487  uint8_t wiper_idx = static_cast<uint8_t>(wiper);
488  ESP_LOGV(TAG, "Disabling terminal %c of wiper %u", terminal, wiper_idx);
489  switch (terminal) {
490  case 'h':
491  this->reg_[wiper_idx].terminal_hw = false;
492  break;
493  case 'a':
494  this->reg_[wiper_idx].terminal_a = false;
495  break;
496  case 'b':
497  this->reg_[wiper_idx].terminal_b = false;
498  break;
499  case 'w':
500  this->reg_[wiper_idx].terminal_w = false;
501  break;
502  default:
503  ESP_LOGW(TAG, "Unknown terminal %c specified", terminal);
504  return;
505  }
506  this->reg_[wiper_idx].update_terminal = false;
507 }
508 
510  if (this->is_failed()) {
511  ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
512  return 0;
513  }
514  uint8_t reg = 0;
515  reg |= static_cast<uint8_t>(Mcp4461EepromLocation::MCP4461_EEPROM_1) + (static_cast<uint8_t>(location) * 0x10);
516  reg |= static_cast<uint8_t>(Mcp4461Commands::READ);
517  uint16_t buf;
518  if (!this->is_eeprom_ready_for_writing_(true)) {
519  return 0;
520  }
521  if (!this->read_byte_16(reg, &buf)) {
522  this->error_code_ = MCP4461_STATUS_I2C_ERROR;
523  this->status_set_warning();
524  ESP_LOGW(TAG, "Error fetching EEPROM location value");
525  return 0;
526  }
527  return buf;
528 }
529 
531  if (this->is_failed()) {
532  ESP_LOGE(TAG, "%s", LOG_STR_ARG(this->get_message_string(this->error_code_)));
533  return false;
534  }
535  uint8_t addr = 0;
536  if (value > 511) {
537  return false;
538  }
539  if (value > 256) {
540  addr = 1;
541  }
542  addr |= static_cast<uint8_t>(Mcp4461EepromLocation::MCP4461_EEPROM_1) + (static_cast<uint8_t>(location) * 0x10);
543  if (!(this->mcp4461_write_(addr, value, true))) {
544  this->error_code_ = MCP4461_STATUS_I2C_ERROR;
545  this->status_set_warning();
546  ESP_LOGW(TAG, "Error writing EEPROM value");
547  return false;
548  }
549  return true;
550 }
551 
553  /* Read the EEPROM write-active status from the status register */
554  bool writing = static_cast<bool>((this->get_status_register_() >> 4) & 0x01);
555 
556  /* If EEPROM is no longer writing, reset the timeout flag */
557  if (!writing) {
558  /* This is protected boolean flag in Mcp4461Component class */
559  this->last_eeprom_write_timed_out_ = false;
560  }
561 
562  return writing;
563 }
564 
566  /* Check initial write status */
567  bool ready_for_write = !this->is_writing_();
568 
569  /* Return early if no waiting is required or EEPROM is already ready */
570  if (ready_for_write || !wait_if_not_ready || this->last_eeprom_write_timed_out_) {
571  return ready_for_write;
572  }
573 
574  /* Timestamp before starting the loop */
575  const uint32_t start_millis = millis();
576 
577  ESP_LOGV(TAG, "Waiting until EEPROM is ready for write, start_millis = %" PRIu32, start_millis);
578 
579  /* Loop until EEPROM is ready or timeout is reached */
580  while (!ready_for_write && ((millis() - start_millis) < EEPROM_WRITE_TIMEOUT_MS)) {
581  ready_for_write = !this->is_writing_();
582 
583  /* If ready, exit early */
584  if (ready_for_write) {
585  ESP_LOGV(TAG, "EEPROM is ready for new write, elapsed_millis = %" PRIu32, millis() - start_millis);
586  return true;
587  }
588 
589  /* Not ready yet, yield before checking again */
590  yield();
591  }
592 
593  /* If still not ready after timeout, log error and mark the timeout */
594  ESP_LOGE(TAG, "EEPROM write timeout exceeded (%u ms)", EEPROM_WRITE_TIMEOUT_MS);
595  this->last_eeprom_write_timed_out_ = true;
596 
597  return false;
598 }
599 
600 bool Mcp4461Component::mcp4461_write_(uint8_t addr, uint16_t data, bool nonvolatile) {
601  uint8_t reg = data > 0xff ? 1 : 0;
602  uint8_t value_byte = static_cast<uint8_t>(data & 0x00ff);
603  ESP_LOGV(TAG, "Writing value %u to address %u", data, addr);
604  reg |= addr;
605  reg |= static_cast<uint8_t>(Mcp4461Commands::WRITE);
606  if (nonvolatile) {
607  if (this->write_protected_) {
608  ESP_LOGW(TAG, "%s", LOG_STR_ARG(this->get_message_string(MCP4461_WRITE_PROTECTED)));
609  return false;
610  }
611  if (!this->is_eeprom_ready_for_writing_(true)) {
612  return false;
613  }
614  }
615  return this->write_byte(reg, value_byte);
616 }
617 } // namespace mcp4461
618 } // namespace esphome
bool state
Definition: fan.h:34
bool read_byte_16(uint8_t a_register, uint16_t *data)
Definition: i2c.h:246
uint8_t get_wiper_address_(uint8_t wiper)
Definition: mcp4461.cpp:153
void write_wiper_level_(uint8_t wiper, uint16_t value)
Definition: mcp4461.cpp:258
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
bool status_has_warning() const
Definition: component.cpp:149
uint16_t read_wiper_level_(uint8_t wiper)
Definition: mcp4461.cpp:200
I2CRegister reg(uint8_t a_register)
calls the I2CRegister constructor
Definition: i2c.h:149
bool set_wiper_level_(Mcp4461WiperIdx wiper, uint16_t value)
Definition: mcp4461.cpp:234
bool is_failed() const
Definition: component.cpp:143
uint16_t get_eeprom_value(Mcp4461EepromLocation location)
get eeprom value from location
Definition: mcp4461.cpp:509
void initialize_terminal_disabled(Mcp4461WiperIdx wiper, char terminal)
public function used to set disable terminal config
Definition: mcp4461.cpp:45
optional< float > initial_value
Definition: mcp4461.h:13
bool update_wiper_level_(Mcp4461WiperIdx wiper)
Definition: mcp4461.cpp:218
uint8_t get_terminal_register_(Mcp4461TerminalIdx terminal_connector)
Definition: mcp4461.cpp:387
void update_terminal_register_(Mcp4461TerminalIdx terminal_connector)
Definition: mcp4461.cpp:406
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
ErrorCode write(const uint8_t *data, size_t len, bool stop=true)
writes an array of bytes to a device using an I2CBus
Definition: i2c.h:186
bool set_eeprom_value(Mcp4461EepromLocation location, uint16_t value)
set eeprom value at specified location
Definition: mcp4461.cpp:530
bool is_eeprom_ready_for_writing_(bool wait_if_not_ready)
Definition: mcp4461.cpp:565
uint8_t calc_terminal_connector_byte_(Mcp4461TerminalIdx terminal_connector)
Definition: mcp4461.cpp:373
constexpr uint8_t EEPROM_WRITE_TIMEOUT_MS
Definition: mcp4461.cpp:10
No error found during execution of method.
Definition: i2c_bus.h:13
uint16_t get_wiper_level_(Mcp4461WiperIdx wiper)
Definition: mcp4461.cpp:183
void disable_terminal_(Mcp4461WiperIdx, char terminal)
Definition: mcp4461.cpp:482
void status_clear_warning()
Definition: component.cpp:168
void enable_wiper_(Mcp4461WiperIdx wiper)
Definition: mcp4461.cpp:267
static const LogString * get_message_string(int status)
Definition: mcp4461.h:122
bool increase_wiper_(Mcp4461WiperIdx wiper)
Definition: mcp4461.cpp:311
bool set_terminal_register_(Mcp4461TerminalIdx terminal_connector, uint8_t data)
Definition: mcp4461.cpp:433
uint8_t address_
store the address of the device on the bus
Definition: i2c.h:269
void IRAM_ATTR HOT yield()
Definition: core.cpp:24
void disable_wiper_(Mcp4461WiperIdx wiper)
Definition: mcp4461.cpp:289
bool write_byte(uint8_t a_register, uint8_t data, bool stop=true)
Definition: i2c.h:262
void read_status_register_to_log()
read status register to log
Definition: mcp4461.cpp:144
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
void set_initial_value(Mcp4461WiperIdx wiper, float initial_value)
public function used to set initial value
Definition: mcp4461.cpp:40
bool mcp4461_write_(uint8_t addr, uint16_t data, bool nonvolatile=false)
Definition: mcp4461.cpp:600
bool decrease_wiper_(Mcp4461WiperIdx wiper)
Definition: mcp4461.cpp:342
void enable_terminal_(Mcp4461WiperIdx wiper, char terminal)
Definition: mcp4461.cpp:455