diff --git a/esphome/components/mcp4461/mcp4461.cpp b/esphome/components/mcp4461/mcp4461.cpp index b5ce591547..6b0774a664 100644 --- a/esphome/components/mcp4461/mcp4461.cpp +++ b/esphome/components/mcp4461/mcp4461.cpp @@ -542,20 +542,88 @@ bool Mcp4461Component::set_eeprom_value(Mcp4461EepromLocation location, uint16_t return true; } -bool Mcp4461Component::is_writing_() { return static_cast((this->get_status_register() >> 4) & 0x01); } +/** + * @brief Checks if the EEPROM is currently writing. + * + * This function reads the MCP4461 status register to determine if an EEPROM write operation is in progress. + * If the EEPROM is no longer writing, the timeout flag (`last_eeprom_write_timed_out_`) + * is reset to allow normal operation in future calls of `is_eeprom_ready_for_writing_()`. + * + * Behavior: + * - If the EEPROM is **writing**, the function returns `true`. + * - If the EEPROM is **not writing**, the function returns `false` and resets `last_eeprom_write_timed_out_`. + * + * @return `true` if the EEPROM is currently writing. + * @return `false` if the EEPROM is not writing (also resets the timeout flag). + */ +bool Mcp4461Component::is_writing_() { + /* Read the EEPROM write-active status from the status register */ + bool writing = static_cast((this->get_status_register() >> 4) & 0x01); -bool Mcp4461Component::is_eeprom_busy_() { - while (this->is_writing_() && this->previous_write_exec_time_ != 0) { - if ((millis() - this->previous_write_exec_time_) > EEPROM_WRITE_TIMEOUT_MS) { - this->previous_write_exec_time_ = millis(); - ESP_LOGE(TAG, - "EEPROM write timeout exceeded (%" PRIu8 " ms), aborting read/write from/to nonvolatile wiper/eeprom", - EEPROM_WRITE_TIMEOUT_MS); + /* If EEPROM is no longer writing, reset the timeout flag */ + if (!writing) { + /* This is protected boolean flag in Mcp4461Component class */ + this->last_eeprom_write_timed_out_ = false; + } + + return writing; +} + +/** + * @brief Checks if the EEPROM is ready for a new write operation. + * + * This function ensures that the EEPROM is not actively writing. + * It can either return the current status immediately or wait until the EEPROM becomes ready. + * + * Behavior: + * - If `wait_if_not_ready` is `false`, the function returns the current readiness status immediately. + * - If `wait_if_not_ready` is `true`: + * - The function waits up to `EEPROM_WRITE_TIMEOUT_MS` for the EEPROM to become ready. + * - If the EEPROM remains busy after the timeout, the `last_eeprom_write_timed_out_` flag is set to `true`, + * preventing unnecessary waits in future calls. + * - If the EEPROM becomes ready within the timeout, the function returns `true`. + * + * @param[in] wait_if_not_ready Specifies whether to wait for EEPROM readiness if it is currently busy. + * - `true` → Waits for completion (up to `EEPROM_WRITE_TIMEOUT_MS`). + * - `false` → Returns the current readiness status without waiting. + * + * @return `true` if the EEPROM is ready for a new write. + * @return `false` if: + * - The last write attempt **timed out** (`wait_if_not_ready = true`). + * - The EEPROM is still busy (`wait_if_not_ready = false`). + */ +bool Mcp4461Component::is_eeprom_ready_for_writing_(bool wait_if_not_ready) { + /* Check initial write status */ + bool ready_for_write = !this->is_writing_(); + + /* Return early if no waiting is required or EEPROM is already ready */ + if (ready_for_write || !wait_if_not_ready || this->last_eeprom_write_timed_out_) { + return ready_for_write; + } + + /* Timestamp before starting the loop */ + const uint32_t start_millis = millis(); + + ESP_LOGV(TAG, "Waiting until EEPROM is ready for write, start_millis = %" PRIu32, start_millis); + + /* Loop until EEPROM is ready or timeout is reached */ + while (!ready_for_write && ((millis() - start_millis) < EEPROM_WRITE_TIMEOUT_MS)) { + ready_for_write = !this->is_writing_(); + + /* If ready, exit early */ + if (ready_for_write) { + ESP_LOGV(TAG, "EEPROM is ready for new write, elapsed_millis = %" PRIu32, millis() - start_millis); return true; } - ESP_LOGV(TAG, "Waiting while eeprom is busy"); + + /* Not ready yet, yield before checking again */ yield(); } + + /* If still not ready after timeout, log error and mark the timeout */ + ESP_LOGE(TAG, "EEPROM write timeout exceeded (%" PRIu8 " ms)", EEPROM_WRITE_TIMEOUT_MS); + this->last_eeprom_write_timed_out_ = true; + return false; }