mirror of
https://github.com/esphome/esphome.git
synced 2025-03-28 21:48:16 +00:00
commit
a73149d22f
@ -8,6 +8,7 @@ namespace esphome {
|
||||
namespace mcp4461 {
|
||||
|
||||
static const char *const TAG = "mcp4461";
|
||||
constexpr uint8_t EEPROM_WRITE_TIMEOUT_MS = 10;
|
||||
|
||||
void Mcp4461Component::setup() {
|
||||
ESP_LOGCONFIG(TAG, "Setting up mcp4461 (0x%02" PRIX8 ")...", this->address_);
|
||||
@ -20,34 +21,90 @@ void Mcp4461Component::setup() {
|
||||
}
|
||||
|
||||
void Mcp4461Component::begin_() {
|
||||
// save WP/WL status
|
||||
this->set_write_protection_status_();
|
||||
this->previous_write_exec_time_ = 0;
|
||||
for (uint8_t i = 0; i < 8; i++) {
|
||||
if (this->reg_[i].enabled) {
|
||||
this->reg_[i].state = this->read_wiper_level_(i);
|
||||
} else {
|
||||
// only volatile wipers can be disabled
|
||||
if (i < 4) {
|
||||
this->reg_[i].state = 0;
|
||||
Mcp4461WiperIdx wiper_idx;
|
||||
wiper_idx = static_cast<Mcp4461WiperIdx>(i);
|
||||
this->disable_wiper(wiper_idx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Mcp4461Component::set_write_protection_status_() {
|
||||
uint8_t status_register_value;
|
||||
status_register_value = this->get_status_register();
|
||||
this->write_protected_ = static_cast<bool>((status_register_value >> 0) & 0x01);
|
||||
this->reg_[0].wiper_lock_active = static_cast<bool>((status_register_value >> 2) & 0x01);
|
||||
this->reg_[1].wiper_lock_active = static_cast<bool>((status_register_value >> 3) & 0x01);
|
||||
this->reg_[2].wiper_lock_active = static_cast<bool>((status_register_value >> 5) & 0x01);
|
||||
this->reg_[3].wiper_lock_active = static_cast<bool>((status_register_value >> 6) & 0x01);
|
||||
}
|
||||
|
||||
void Mcp4461Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "mcp4461:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
// log wiper status
|
||||
for (uint8_t i = 0; i < 8; ++i) {
|
||||
ESP_LOGCONFIG(TAG, "Wiper [%" PRIu8 "] level: %" PRIu16, i, this->reg_[i].state);
|
||||
// terminals only valid for volatile wipers 0-3 - enable/disable is terminal hw
|
||||
// so also invalid for nonvolatile. For these, only print current level.
|
||||
// reworked to be a one-line intentionally, as output would not be in order
|
||||
if (i < 4) {
|
||||
ESP_LOGCONFIG(TAG, " ├── Status: %s", ONOFF(this->reg_[i].enabled));
|
||||
ESP_LOGCONFIG(TAG, " ├── Terminal A: %s", ONOFF(this->reg_[i].terminal_a));
|
||||
ESP_LOGCONFIG(TAG, " ├── Terminal B: %s", ONOFF(this->reg_[i].terminal_b));
|
||||
ESP_LOGCONFIG(TAG, " ├── Terminal W: %s", ONOFF(this->reg_[i].terminal_w));
|
||||
ESP_LOGCONFIG(TAG, " └── Terminal HW: %s", ONOFF(this->reg_[i].terminal_hw));
|
||||
ESP_LOGCONFIG(TAG, " ├── Volatile wiper [%" PRIu8 "] level: %" PRIu16 ", Status: %s, HW: %s, A: %s, B: %s, W: %s",
|
||||
i,
|
||||
this->reg_[i].state,
|
||||
ONOFF(this->reg_[i].terminal_hw),
|
||||
ONOFF(this->reg_[i].terminal_a),
|
||||
ONOFF(this->reg_[i].terminal_b),
|
||||
ONOFF(this->reg_[i].terminal_w),
|
||||
ONOFF(this->reg_[i].enabled));
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, " ├── Nonvolatile wiper [%" PRIu8 "] level: %" PRIu16 "", i, this->reg_[i].state);
|
||||
}
|
||||
}
|
||||
// log current device status register at start
|
||||
// from datasheet:
|
||||
// (1) means, bit is hard-locked to value 1
|
||||
// Bit 0 is WP status (=>pin)
|
||||
// Bit 1 is named "R-1"-pin in datasheet an declared "reserved" and forced/locked to 1
|
||||
// Bit 2 is WiperLock-Status resistor-network 0
|
||||
// Bit 3 is WiperLock-Status resistor-network 1
|
||||
// Bit 4 is EEPROM-Write-Active-Status bit
|
||||
// Bit 5 is WiperLock-Status resistor-network 2
|
||||
// Bit 6 is WiperLock-Status resistor-network 3
|
||||
// Bit 7+8 are referenced in datasheet as D7 + D8 and both locked to 1
|
||||
// Default status register reading should be 0x182h or 386 decimal
|
||||
// "Default" means without any WiperLocks or WriteProtection enabled and EEPRom not active writing
|
||||
// get_status_register() will automatically check, if D8, D7 & R1 bits (locked to 1) are 1 and bail out using error-routine otherwise
|
||||
uint8_t status_register_value;
|
||||
status_register_value = this->get_status_register();
|
||||
ESP_LOGCONFIG(TAG, " └── Status register: D7: %" PRIu8 ", WL3: %" PRIu8 ", WL2: %" PRIu8 ", EEWA: %" PRIu8 ", WL1: %" PRIu8 ", WL0: %" PRIu8 ", R1: %" PRIu8 ", WP: %" PRIu8 "",
|
||||
((status_register_value >> 7) & 0x01),
|
||||
((status_register_value >> 6) & 0x01),
|
||||
((status_register_value >> 5) & 0x01),
|
||||
((status_register_value >> 4) & 0x01),
|
||||
((status_register_value >> 3) & 0x01),
|
||||
((status_register_value >> 2) & 0x01),
|
||||
((status_register_value >> 1) & 0x01),
|
||||
((status_register_value >> 0) & 0x01)
|
||||
);
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGE(TAG, "Communication with mcp4461 failed!");
|
||||
}
|
||||
}
|
||||
|
||||
void Mcp4461Component::loop() {
|
||||
if (status_has_warning()) {
|
||||
this->get_status_register();
|
||||
}
|
||||
if (this->update_) {
|
||||
uint8_t i;
|
||||
for (i = 0; i < 8; i++) {
|
||||
@ -76,21 +133,32 @@ void Mcp4461Component::loop() {
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t Mcp4461Component::get_status_register() {
|
||||
uint8_t Mcp4461Component::get_status_register() {
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Returning 0");
|
||||
return 0;
|
||||
}
|
||||
uint8_t reg = 0;
|
||||
reg |= static_cast<uint8_t>(Mcp4461Addresses::MCP4461_STATUS);
|
||||
reg |= static_cast<uint8_t>(Mcp4461Commands::READ);
|
||||
uint16_t buf;
|
||||
if (!this->read_byte_16(reg, &buf)) {
|
||||
this->status_set_warning();
|
||||
ESP_LOGW(TAG, "Error fetching status register value");
|
||||
this->mark_failed();
|
||||
ESP_LOGE(TAG, "Error fetching status register value");
|
||||
return 0;
|
||||
}
|
||||
return buf;
|
||||
uint8_t msb = buf >> 8;
|
||||
uint8_t lsb;
|
||||
lsb = static_cast<uint8_t>(buf & 0x00ff);
|
||||
if (msb != 1 || ((lsb >> 7) & 0x01) != 1 || ((lsb >> 1) & 0x01) != 1) {
|
||||
// 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 indicate device/communication issues, therefore mark component failed
|
||||
this->mark_failed();
|
||||
return 0;
|
||||
}
|
||||
this->status_clear_warning();
|
||||
return lsb;
|
||||
}
|
||||
|
||||
bool Mcp4461Component::is_writing_() { return static_cast<bool>((this->get_status_register() >> 4) & 0x01); }
|
||||
|
||||
uint8_t Mcp4461Component::get_wiper_address_(uint8_t wiper) {
|
||||
uint8_t addr;
|
||||
bool nonvolatile = false;
|
||||
@ -112,7 +180,7 @@ uint8_t Mcp4461Component::get_wiper_address_(uint8_t wiper) {
|
||||
addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_VW3);
|
||||
break;
|
||||
default:
|
||||
ESP_LOGE(TAG, "unknown wiper specified");
|
||||
ESP_LOGW(TAG, "unknown wiper specified");
|
||||
return 0;
|
||||
}
|
||||
if (nonvolatile) {
|
||||
@ -122,6 +190,10 @@ uint8_t Mcp4461Component::get_wiper_address_(uint8_t wiper) {
|
||||
}
|
||||
|
||||
uint16_t Mcp4461Component::get_wiper_level(Mcp4461WiperIdx wiper) {
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Returning 0");
|
||||
return 0;
|
||||
}
|
||||
uint8_t wiper_idx = static_cast<uint8_t>(wiper);
|
||||
if (!(this->reg_[wiper_idx].enabled)) {
|
||||
ESP_LOGW(TAG, "reading from disabled volatile wiper %" PRIu8 ", returning 0", wiper_idx);
|
||||
@ -136,12 +208,11 @@ uint16_t Mcp4461Component::read_wiper_level_(uint8_t wiper) {
|
||||
reg |= this->get_wiper_address_(wiper);
|
||||
reg |= static_cast<uint8_t>(Mcp4461Commands::READ);
|
||||
if (wiper > 3) {
|
||||
while (this->is_writing_()) {
|
||||
ESP_LOGVV(TAG, "delaying during eeprom write");
|
||||
yield();
|
||||
if(this->is_eeprom_busy_()) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (!this->read_byte_16(reg, &buf)) {
|
||||
if (!(this->read_byte_16(reg, &buf))) {
|
||||
this->status_set_warning();
|
||||
ESP_LOGW(TAG, "Error fetching %swiper %" PRIu8 " value", (wiper > 3) ? "nonvolatile " : "", wiper);
|
||||
return 0;
|
||||
@ -158,26 +229,44 @@ void Mcp4461Component::update_wiper_level(Mcp4461WiperIdx wiper) {
|
||||
}
|
||||
|
||||
void Mcp4461Component::set_wiper_level(Mcp4461WiperIdx wiper, uint16_t value) {
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Aborting write.");
|
||||
return;
|
||||
}
|
||||
uint8_t wiper_idx = static_cast<uint8_t>(wiper);
|
||||
if (this->reg_[wiper_idx].wiper_lock_active) {
|
||||
ESP_LOGW(TAG, "Ignoring request to set the state for wiper %" PRIu8 " as it is locked by WiperLock", wiper_idx);
|
||||
return;
|
||||
}
|
||||
if (value > 0x100) {
|
||||
ESP_LOGW(TAG, "ignoring invalid wiper level %" PRIu16 "!");
|
||||
return;
|
||||
}
|
||||
if (!(this->reg_[wiper_idx].enabled)) {
|
||||
ESP_LOGW(TAG, "writing to disabled volatile wiper %" PRIu8 " is prohibited", wiper_idx);
|
||||
return;
|
||||
}
|
||||
ESP_LOGV(TAG, "Setting MCP4461 wiper %" PRIu8 " to %" PRIu16 "!", wiper_idx, value);
|
||||
this->reg_[wiper_idx].state = value;
|
||||
this->update_ = true;
|
||||
}
|
||||
|
||||
void Mcp4461Component::write_wiper_level_(uint8_t wiper, uint16_t value) {
|
||||
uint8_t wiper_idx = static_cast<uint8_t>(wiper);
|
||||
bool nonvolatile = false;
|
||||
if (wiper_idx > 3) {
|
||||
if (wiper > 3) {
|
||||
nonvolatile = true;
|
||||
}
|
||||
this->mcp4461_write_(this->get_wiper_address_(wiper_idx), value, nonvolatile);
|
||||
if (!(this->mcp4461_write_(this->get_wiper_address_(wiper), value, nonvolatile))) {
|
||||
ESP_LOGW(TAG, "Error writing %swiper %" PRIu8 " level %" PRIu16 "", (wiper > 3) ? "nonvolatile " : "", wiper, value);
|
||||
this->status_set_warning();
|
||||
}
|
||||
}
|
||||
|
||||
void Mcp4461Component::enable_wiper(Mcp4461WiperIdx wiper) {
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Aborting");
|
||||
return;
|
||||
}
|
||||
uint8_t wiper_idx = static_cast<uint8_t>(wiper);
|
||||
ESP_LOGV(TAG, "Enabling wiper %" PRIu8, wiper_idx);
|
||||
this->reg_[wiper_idx].terminal_hw = true;
|
||||
@ -185,34 +274,72 @@ void Mcp4461Component::enable_wiper(Mcp4461WiperIdx wiper) {
|
||||
}
|
||||
|
||||
void Mcp4461Component::disable_wiper(Mcp4461WiperIdx wiper) {
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Aborting");
|
||||
return;
|
||||
}
|
||||
uint8_t wiper_idx = static_cast<uint8_t>(wiper);
|
||||
ESP_LOGV(TAG, "Disabling wiper %" PRIu8, wiper_idx);
|
||||
this->reg_[wiper_idx].terminal_hw = false;
|
||||
this->update_ = true;
|
||||
}
|
||||
|
||||
void Mcp4461Component::increase_wiper(Mcp4461WiperIdx wiper) {
|
||||
bool Mcp4461Component::increase_wiper(Mcp4461WiperIdx wiper) {
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Aborting");
|
||||
return false;
|
||||
}
|
||||
uint8_t wiper_idx = static_cast<uint8_t>(wiper);
|
||||
if (!(this->reg_[wiper_idx].enabled)) {
|
||||
ESP_LOGW(TAG, "increasing disabled volatile wiper %" PRIu8 " is prohibited", wiper_idx);
|
||||
return false;
|
||||
}
|
||||
if (this->reg_[wiper_idx].wiper_lock_active) {
|
||||
ESP_LOGW(TAG, "Ignoring request to increase wiper %" PRIu8 " as it is locked by WiperLock", wiper_idx);
|
||||
return false;
|
||||
}
|
||||
ESP_LOGV(TAG, "Increasing wiper %" PRIu8 "", wiper_idx);
|
||||
uint8_t reg = 0;
|
||||
uint8_t addr;
|
||||
addr = this->get_wiper_address_(wiper_idx);
|
||||
reg |= addr;
|
||||
reg |= static_cast<uint8_t>(Mcp4461Commands::INCREMENT);
|
||||
this->write(&this->address_, reg, sizeof(reg));
|
||||
auto err = this->write(&this->address_, reg, sizeof(reg));
|
||||
if (err != i2c::ERROR_OK) {
|
||||
this->status_set_warning();
|
||||
return false;
|
||||
}
|
||||
this->reg_[wiper_idx].state++;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mcp4461Component::decrease_wiper(Mcp4461WiperIdx wiper) {
|
||||
bool Mcp4461Component::decrease_wiper(Mcp4461WiperIdx wiper) {
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Aborting");
|
||||
return false;
|
||||
}
|
||||
uint8_t wiper_idx = static_cast<uint8_t>(wiper);
|
||||
if (!(this->reg_[wiper_idx].enabled)) {
|
||||
ESP_LOGW(TAG, "decreasing disabled volatile wiper %" PRIu8 " is prohibited", wiper_idx);
|
||||
return false;
|
||||
}
|
||||
if (this->reg_[wiper_idx].wiper_lock_active) {
|
||||
ESP_LOGW(TAG, "Ignoring request to decrease wiper %" PRIu8 " as it is locked by WiperLock", wiper_idx);
|
||||
return false;
|
||||
}
|
||||
ESP_LOGV(TAG, "Decreasing wiper %" PRIu8 "", wiper_idx);
|
||||
uint8_t reg = 0;
|
||||
uint8_t addr;
|
||||
addr = this->get_wiper_address_(wiper_idx);
|
||||
reg |= addr;
|
||||
reg |= static_cast<uint8_t>(Mcp4461Commands::DECREMENT);
|
||||
this->write(&this->address_, reg, sizeof(reg));
|
||||
auto err = this->write(&this->address_, reg, sizeof(reg));
|
||||
if (err != i2c::ERROR_OK) {
|
||||
this->status_set_warning();
|
||||
return false;
|
||||
}
|
||||
this->reg_[wiper_idx].state--;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint8_t Mcp4461Component::calc_terminal_connector_byte_(Mcp4461TerminalIdx terminal_connector) {
|
||||
@ -240,6 +367,10 @@ uint8_t Mcp4461Component::calc_terminal_connector_byte_(Mcp4461TerminalIdx termi
|
||||
}
|
||||
|
||||
uint8_t Mcp4461Component::get_terminal_register(Mcp4461TerminalIdx terminal_connector) {
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Returning 0");
|
||||
return 0;
|
||||
}
|
||||
uint8_t reg = 0;
|
||||
if (static_cast<uint8_t>(terminal_connector) == 0) {
|
||||
reg |= static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON0);
|
||||
@ -248,12 +379,13 @@ uint8_t Mcp4461Component::get_terminal_register(Mcp4461TerminalIdx terminal_conn
|
||||
}
|
||||
reg |= static_cast<uint8_t>(Mcp4461Commands::READ);
|
||||
uint16_t buf;
|
||||
if (!this->read_byte_16(reg, &buf)) {
|
||||
if (this->read_byte_16(reg, &buf)) {
|
||||
return static_cast<uint8_t>(buf & 0x00ff);
|
||||
} else {
|
||||
this->status_set_warning();
|
||||
ESP_LOGW(TAG, "Error fetching terminal register value");
|
||||
return 0;
|
||||
}
|
||||
return static_cast<uint8_t>(buf & 0x00ff);
|
||||
}
|
||||
|
||||
void Mcp4461Component::update_terminal_register(Mcp4461TerminalIdx terminal_connector) {
|
||||
@ -280,7 +412,11 @@ void Mcp4461Component::update_terminal_register(Mcp4461TerminalIdx terminal_conn
|
||||
this->reg_[(wiper_index + 1)].terminal_hw = ((terminal_data >> 7) & 0x01);
|
||||
}
|
||||
|
||||
void Mcp4461Component::set_terminal_register(Mcp4461TerminalIdx terminal_connector, uint8_t data) {
|
||||
bool Mcp4461Component::set_terminal_register(Mcp4461TerminalIdx terminal_connector, uint8_t data) {
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Aborting");
|
||||
return false;
|
||||
}
|
||||
uint8_t addr;
|
||||
if (static_cast<uint8_t>(terminal_connector) == 0) {
|
||||
addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON0);
|
||||
@ -288,12 +424,20 @@ void Mcp4461Component::set_terminal_register(Mcp4461TerminalIdx terminal_connect
|
||||
addr = static_cast<uint8_t>(Mcp4461Addresses::MCP4461_TCON1);
|
||||
} else {
|
||||
ESP_LOGW(TAG, "Invalid terminal connector id %" PRIu8 " specified", static_cast<uint8_t>(terminal_connector));
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
this->mcp4461_write_(addr, data);
|
||||
if (!(this->mcp4461_write_(addr, data))) {
|
||||
this->status_set_warning();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mcp4461Component::enable_terminal(Mcp4461WiperIdx wiper, char terminal) {
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Aborting");
|
||||
return;
|
||||
}
|
||||
uint8_t wiper_idx = static_cast<uint8_t>(wiper);
|
||||
if (wiper_idx > 3) {
|
||||
return;
|
||||
@ -313,7 +457,6 @@ void Mcp4461Component::enable_terminal(Mcp4461WiperIdx wiper, char terminal) {
|
||||
this->reg_[wiper_idx].terminal_w = true;
|
||||
break;
|
||||
default:
|
||||
this->status_set_warning();
|
||||
ESP_LOGW(TAG, "Unknown terminal %c specified", terminal);
|
||||
return;
|
||||
}
|
||||
@ -321,6 +464,10 @@ void Mcp4461Component::enable_terminal(Mcp4461WiperIdx wiper, char terminal) {
|
||||
}
|
||||
|
||||
void Mcp4461Component::disable_terminal(Mcp4461WiperIdx wiper, char terminal) {
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Aborting");
|
||||
return;
|
||||
}
|
||||
uint8_t wiper_idx = static_cast<uint8_t>(wiper);
|
||||
if (wiper_idx > 3) {
|
||||
return;
|
||||
@ -340,7 +487,6 @@ void Mcp4461Component::disable_terminal(Mcp4461WiperIdx wiper, char terminal) {
|
||||
this->reg_[wiper_idx].terminal_w = false;
|
||||
break;
|
||||
default:
|
||||
this->status_set_warning();
|
||||
ESP_LOGW(TAG, "Unknown terminal %c specified", terminal);
|
||||
return;
|
||||
}
|
||||
@ -348,10 +494,17 @@ void Mcp4461Component::disable_terminal(Mcp4461WiperIdx wiper, char terminal) {
|
||||
}
|
||||
|
||||
uint16_t Mcp4461Component::get_eeprom_value(Mcp4461EepromLocation location) {
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Returning 0");
|
||||
return 0;
|
||||
}
|
||||
uint8_t reg = 0;
|
||||
reg |= static_cast<uint8_t>(Mcp4461EepromLocation::MCP4461_EEPROM_1) + (static_cast<uint8_t>(location) * 0x10);
|
||||
reg |= static_cast<uint8_t>(Mcp4461Commands::READ);
|
||||
uint16_t buf;
|
||||
if(this->is_eeprom_busy_()) {
|
||||
return 0;
|
||||
}
|
||||
if (!this->read_byte_16(reg, &buf)) {
|
||||
this->status_set_warning();
|
||||
ESP_LOGW(TAG, "Error fetching EEPRom location value");
|
||||
@ -360,19 +513,42 @@ uint16_t Mcp4461Component::get_eeprom_value(Mcp4461EepromLocation location) {
|
||||
return buf;
|
||||
}
|
||||
|
||||
void Mcp4461Component::set_eeprom_value(Mcp4461EepromLocation location, uint16_t value) {
|
||||
bool Mcp4461Component::set_eeprom_value(Mcp4461EepromLocation location, uint16_t value) {
|
||||
if (this->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Aborting");
|
||||
return false;
|
||||
}
|
||||
uint8_t addr = 0;
|
||||
if (value > 511) {
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
if (value > 256) {
|
||||
addr = 1;
|
||||
}
|
||||
addr |= static_cast<uint8_t>(Mcp4461EepromLocation::MCP4461_EEPROM_1) + (static_cast<uint8_t>(location) * 0x10);
|
||||
this->mcp4461_write_(addr, value, true);
|
||||
if (!(this->mcp4461_write_(addr, value, true))) {
|
||||
this->status_set_warning();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void Mcp4461Component::mcp4461_write_(uint8_t addr, uint16_t data, bool nonvolatile) {
|
||||
bool Mcp4461Component::is_writing_() { return static_cast<bool>((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);
|
||||
return true;
|
||||
}
|
||||
ESP_LOGV(TAG, "Waiting while eeprom is busy");
|
||||
yield();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Mcp4461Component::mcp4461_write_(uint8_t addr, uint16_t data, bool nonvolatile) {
|
||||
uint8_t reg = 0;
|
||||
if (data > 0xFF) {
|
||||
reg = 1;
|
||||
@ -383,12 +559,16 @@ void Mcp4461Component::mcp4461_write_(uint8_t addr, uint16_t data, bool nonvolat
|
||||
reg |= addr;
|
||||
reg |= static_cast<uint8_t>(Mcp4461Commands::WRITE);
|
||||
if (nonvolatile) {
|
||||
while (this->is_writing_()) {
|
||||
ESP_LOGV(TAG, "Waiting while eeprom busy");
|
||||
yield();
|
||||
if (this->write_protected_) {
|
||||
ESP_LOGW(TAG, "Ignoring write to write protected chip");
|
||||
return false;
|
||||
}
|
||||
if (this->is_eeprom_busy_()) {
|
||||
return false;
|
||||
}
|
||||
this->previous_write_exec_time_ = millis();
|
||||
}
|
||||
this->write_byte(reg, value_byte);
|
||||
return this->write_byte(reg, value_byte);
|
||||
}
|
||||
} // namespace mcp4461
|
||||
} // namespace esphome
|
||||
|
@ -11,8 +11,9 @@ struct WiperState {
|
||||
bool terminal_b = true;
|
||||
bool terminal_w = true;
|
||||
bool terminal_hw = true;
|
||||
uint16_t state;
|
||||
uint16_t state = 0;
|
||||
bool enabled = true;
|
||||
bool wiper_lock_active = false;
|
||||
};
|
||||
|
||||
enum class Mcp4461Defaults : uint8_t { WIPER_VALUE = 0x80 };
|
||||
@ -56,11 +57,11 @@ class Mcp4461Wiper;
|
||||
class Mcp4461Component : public Component, public i2c::I2CDevice {
|
||||
public:
|
||||
Mcp4461Component(bool disable_wiper_0, bool disable_wiper_1, bool disable_wiper_2, bool disable_wiper_3)
|
||||
: wiper_0_enabled_(false), wiper_1_enabled_(false), wiper_2_enabled_(false), wiper_3_enabled_(false) {
|
||||
this->reg_[0].enabled = this->wiper_0_enabled_;
|
||||
this->reg_[1].enabled = this->wiper_1_enabled_;
|
||||
this->reg_[2].enabled = this->wiper_2_enabled_;
|
||||
this->reg_[3].enabled = this->wiper_3_enabled_;
|
||||
: wiper_0_disabled_(disable_wiper_0), wiper_1_disabled_(disable_wiper_1), wiper_2_disabled_(disable_wiper_2), wiper_3_disabled_(disable_wiper_3) {
|
||||
this->reg_[0].enabled = !wiper_0_disabled_;
|
||||
this->reg_[1].enabled = !wiper_1_disabled_;
|
||||
this->reg_[2].enabled = !wiper_2_disabled_;
|
||||
this->reg_[3].enabled = !wiper_3_disabled_;
|
||||
}
|
||||
|
||||
void setup() override;
|
||||
@ -68,38 +69,42 @@ class Mcp4461Component : public Component, public i2c::I2CDevice {
|
||||
float get_setup_priority() const override { return setup_priority::HARDWARE; }
|
||||
void loop() override;
|
||||
|
||||
uint16_t get_status_register();
|
||||
uint8_t get_status_register();
|
||||
uint16_t get_wiper_level(Mcp4461WiperIdx wiper);
|
||||
void set_wiper_level(Mcp4461WiperIdx wiper, uint16_t value);
|
||||
void update_wiper_level(Mcp4461WiperIdx wiper);
|
||||
void enable_wiper(Mcp4461WiperIdx wiper);
|
||||
void disable_wiper(Mcp4461WiperIdx wiper);
|
||||
void increase_wiper(Mcp4461WiperIdx wiper);
|
||||
void decrease_wiper(Mcp4461WiperIdx wiper);
|
||||
bool increase_wiper(Mcp4461WiperIdx wiper);
|
||||
bool decrease_wiper(Mcp4461WiperIdx wiper);
|
||||
void enable_terminal(Mcp4461WiperIdx wiper, char terminal);
|
||||
void disable_terminal(Mcp4461WiperIdx, char terminal);
|
||||
void update_terminal_register(Mcp4461TerminalIdx terminal_connector);
|
||||
uint8_t get_terminal_register(Mcp4461TerminalIdx terminal_connector);
|
||||
void set_terminal_register(Mcp4461TerminalIdx terminal_connector, uint8_t data);
|
||||
bool set_terminal_register(Mcp4461TerminalIdx terminal_connector, uint8_t data);
|
||||
uint16_t get_eeprom_value(Mcp4461EepromLocation location);
|
||||
void set_eeprom_value(Mcp4461EepromLocation location, uint16_t value);
|
||||
bool set_eeprom_value(Mcp4461EepromLocation location, uint16_t value);
|
||||
|
||||
protected:
|
||||
friend class Mcp4461Wiper;
|
||||
void set_write_protection_status_();
|
||||
bool is_writing_();
|
||||
bool is_eeprom_busy_();
|
||||
uint8_t get_wiper_address_(uint8_t wiper);
|
||||
uint16_t read_wiper_level_(uint8_t wiper);
|
||||
void write_wiper_level_(uint8_t wiper, uint16_t value);
|
||||
void mcp4461_write_(uint8_t addr, uint16_t data, bool nonvolatile = false);
|
||||
bool mcp4461_write_(uint8_t addr, uint16_t data, bool nonvolatile = false);
|
||||
uint8_t calc_terminal_connector_byte_(Mcp4461TerminalIdx terminal_connector);
|
||||
|
||||
WiperState reg_[8];
|
||||
void begin_();
|
||||
bool update_{false};
|
||||
bool wiper_0_enabled_{true};
|
||||
bool wiper_1_enabled_{true};
|
||||
bool wiper_2_enabled_{true};
|
||||
bool wiper_3_enabled_{true};
|
||||
uint32_t previous_write_exec_time_;
|
||||
bool write_protected_{false};
|
||||
bool wiper_0_disabled_{false};
|
||||
bool wiper_1_disabled_{false};
|
||||
bool wiper_2_disabled_{false};
|
||||
bool wiper_3_disabled_{false};
|
||||
};
|
||||
} // namespace mcp4461
|
||||
} // namespace esphome
|
||||
|
@ -1,7 +1,7 @@
|
||||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome.components import output
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID
|
||||
from esphome.const import CONF_CHANNEL, CONF_ID, CONF_INITIAL_VALUE
|
||||
from .. import Mcp4461Component, CONF_MCP4461_ID, mcp4461_ns
|
||||
|
||||
DEPENDENCIES = ["mcp4461"]
|
||||
@ -24,6 +24,7 @@ CONF_ENABLE = "enable"
|
||||
CONF_TERMINAL_A = "terminal_a"
|
||||
CONF_TERMINAL_B = "terminal_b"
|
||||
CONF_TERMINAL_W = "terminal_w"
|
||||
CONF_INITIAL_VALUE = "initial_value"
|
||||
|
||||
CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
{
|
||||
@ -34,6 +35,7 @@ CONFIG_SCHEMA = output.FLOAT_OUTPUT_SCHEMA.extend(
|
||||
cv.Optional(CONF_TERMINAL_A, default=True): cv.boolean,
|
||||
cv.Optional(CONF_TERMINAL_B, default=True): cv.boolean,
|
||||
cv.Optional(CONF_TERMINAL_W, default=True): cv.boolean,
|
||||
cv.Optional(CONF_INITIAL_VALUE): cv.float_range(min=0.000, max=0.256),
|
||||
}
|
||||
)
|
||||
|
||||
@ -50,3 +52,6 @@ async def to_code(config):
|
||||
config[CONF_TERMINAL_W],
|
||||
)
|
||||
await output.register_output(var, config)
|
||||
await cg.register_parented(var, config[CONF_MCP4461_ID])
|
||||
if CONF_INITIAL_VALUE in config:
|
||||
cg.add(var.set_initial_value(config[CONF_INITIAL_VALUE]))
|
||||
|
@ -10,6 +10,10 @@ namespace mcp4461 {
|
||||
static const char *const TAG = "mcp4461.output";
|
||||
|
||||
void Mcp4461Wiper::write_state(float state) {
|
||||
if (this->parent_->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Aborting");
|
||||
return;
|
||||
}
|
||||
uint8_t wiper_idx = static_cast<uint8_t>(this->wiper_);
|
||||
ESP_LOGV(TAG, "Got value %02f from frontend", state);
|
||||
const float max_taps = 256.0;
|
||||
@ -25,20 +29,46 @@ void Mcp4461Wiper::write_state(float state) {
|
||||
this->parent_->set_wiper_level(this->wiper_, taps);
|
||||
}
|
||||
|
||||
void Mcp4461Wiper::set_initial_value(float initial_value) {
|
||||
if (this->parent_->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Aborting");
|
||||
return;
|
||||
}
|
||||
if (initial_value >= 0.000 && initial_value <= 0.256) {
|
||||
float state = initial_value * 1000;
|
||||
this->initial_value_ = static_cast<uint16_t>(state);
|
||||
// Use the value
|
||||
ESP_LOGCONFIG(TAG, "Setting initial value %" PRIu16 "", *this->initial_value_);
|
||||
this->state_ = state;
|
||||
this->parent_->set_wiper_level(this->wiper_, *this->initial_value_);
|
||||
} else {
|
||||
ESP_LOGCONFIG(TAG, "Invalid initial value set, retaining previous wiper level.");
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t Mcp4461Wiper::get_wiper_level() { return this->parent_->get_wiper_level(this->wiper_); }
|
||||
|
||||
void Mcp4461Wiper::save_level() {
|
||||
if (this->parent_->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Aborting");
|
||||
return;
|
||||
}
|
||||
uint8_t wiper_idx = static_cast<uint8_t>(this->wiper_);
|
||||
if (wiper_idx > 3) {
|
||||
ESP_LOGW(TAG, "Cannot save level for nonvolatile wiper %" PRIu8 " !", wiper_idx);
|
||||
return;
|
||||
}
|
||||
uint8_t nonvolatile_wiper_idx = wiper_idx + 4;
|
||||
this->parent_->reg_[nonvolatile_wiper_idx].state = this->parent_->reg_[wiper_idx].state;
|
||||
Mcp4461WiperIdx nonvolatile_wiper = static_cast<Mcp4461WiperIdx>(nonvolatile_wiper_idx);
|
||||
this->parent_->set_wiper_level(nonvolatile_wiper, this->state_);
|
||||
}
|
||||
|
||||
void Mcp4461Wiper::enable_wiper() {
|
||||
if (this->parent_->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Aborting");
|
||||
return;
|
||||
}
|
||||
uint8_t wiper_idx = static_cast<uint8_t>(this->wiper_);
|
||||
if (wiper_idx > 3) {
|
||||
ESP_LOGW(TAG, "Cannot enable nonvolatile wiper %" PRIu8 " !", wiper_idx);
|
||||
@ -57,21 +87,33 @@ void Mcp4461Wiper::disable_wiper() {
|
||||
}
|
||||
|
||||
void Mcp4461Wiper::increase_wiper() {
|
||||
if (this->parent_->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Aborting");
|
||||
return;
|
||||
}
|
||||
uint8_t wiper_idx = static_cast<uint8_t>(this->wiper_);
|
||||
if (wiper_idx > 3) {
|
||||
ESP_LOGW(TAG, "Cannot increase nonvolatile wiper %" PRIu8 " !", wiper_idx);
|
||||
return;
|
||||
}
|
||||
this->parent_->increase_wiper(this->wiper_);
|
||||
if (this->parent_->increase_wiper(this->wiper_)) {
|
||||
this->state_ = this->state_ + 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
void Mcp4461Wiper::decrease_wiper() {
|
||||
if (this->parent_->is_failed()) {
|
||||
ESP_LOGW(TAG, "Parent MCP4461 component has failed! Aborting");
|
||||
return;
|
||||
}
|
||||
uint8_t wiper_idx = static_cast<uint8_t>(this->wiper_);
|
||||
if (wiper_idx > 3) {
|
||||
ESP_LOGW(TAG, "Cannot decrease nonvolatile wiper %" PRIu8 " !", wiper_idx);
|
||||
return;
|
||||
}
|
||||
this->parent_->decrease_wiper(this->wiper_);
|
||||
if (this->parent_->decrease_wiper(this->wiper_)) {
|
||||
this->state_ = this->state_ - 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
void Mcp4461Wiper::enable_terminal(char terminal) {
|
||||
|
@ -8,7 +8,7 @@
|
||||
namespace esphome {
|
||||
namespace mcp4461 {
|
||||
|
||||
class Mcp4461Wiper : public output::FloatOutput {
|
||||
class Mcp4461Wiper : public output::FloatOutput, public Parented<Mcp4461Component> {
|
||||
public:
|
||||
Mcp4461Wiper(Mcp4461Component *parent, Mcp4461WiperIdx wiper, bool enable, bool terminal_a, bool terminal_b,
|
||||
bool terminal_w)
|
||||
@ -20,7 +20,7 @@ class Mcp4461Wiper : public output::FloatOutput {
|
||||
terminal_w_(terminal_w) {
|
||||
uint8_t wiper_idx = static_cast<uint8_t>(wiper);
|
||||
// update wiper connection state
|
||||
if (!enable && wiper_idx < 4) {
|
||||
if (!(this->enable_) && wiper_idx < 4) {
|
||||
parent->reg_[wiper_idx].enabled = false;
|
||||
parent->disable_terminal(wiper, 'h');
|
||||
}
|
||||
@ -39,6 +39,7 @@ class Mcp4461Wiper : public output::FloatOutput {
|
||||
void decrease_wiper();
|
||||
void enable_terminal(char terminal);
|
||||
void disable_terminal(char terminal);
|
||||
void set_initial_value(float initial_value);
|
||||
|
||||
protected:
|
||||
void write_state(float state) override;
|
||||
@ -46,6 +47,7 @@ class Mcp4461Wiper : public output::FloatOutput {
|
||||
Mcp4461WiperIdx wiper_;
|
||||
bool enable_;
|
||||
uint16_t state_;
|
||||
optional<uint16_t> initial_value_;
|
||||
bool terminal_a_;
|
||||
bool terminal_b_;
|
||||
bool terminal_w_;
|
||||
|
Loading…
x
Reference in New Issue
Block a user