ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
bq25186.cpp
Go to the documentation of this file.
1#include "bq25186.h"
2#ifdef USE_BINARY_SENSOR
4#endif
5#ifdef USE_TEXT_SENSOR
7#endif
8
9#include "esphome/core/hal.h"
10#include "esphome/core/log.h"
11
13
14ESPHOME_LOG_TAG(TAG, "bq25186");
15
16// Bits the configuration owns in each register; the rest are the SYS_REG GPO level, reset
17// triggers (SHIP_RST) or the device id (MASK_ID) and are kept as read
18static const uint8_t BQ25186_CONFIG_MASKS[BQ25186_CONFIG_REGISTER_COUNT] PROGMEM = {
19 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x1F, 0xEF, 0xFF, 0xF0,
20};
21
22bool BQ25186Component::write_register_(uint8_t reg, uint8_t value) {
23 if (!this->write_byte(reg, value)) {
24 ESP_LOGW(TAG, "Failed to write register 0x%02X", reg);
25 return false;
26 }
27 return true;
28}
29
30bool BQ25186Component::update_register_(uint8_t reg, uint8_t mask, uint8_t value) {
31 uint8_t reg_value;
32 if (!this->read_byte(reg, &reg_value)) {
33 ESP_LOGW(TAG, "Failed to read register 0x%02X", reg);
34 return false;
35 }
36 reg_value = (reg_value & ~mask) | (value & mask);
37 return this->write_register_(reg, reg_value);
38}
39
40uint8_t BQ25186Component::expected_register_(size_t index) const {
41 uint8_t value = this->regs_[index];
42#ifdef USE_BQ25186_PG_GPO
43 if (index == 0 && this->pg_gpo_switch_ != nullptr)
44 value |= BQ25186_VBAT_CTRL_PG_MODE;
45#endif
46 return value;
47}
48
50 for (uint8_t reg = BQ25186_REG_VBAT_CTRL; reg <= BQ25186_REG_MASK_ID; reg++) {
51 const size_t index = reg - BQ25186_REG_VBAT_CTRL;
52 const uint8_t mask = progmem_read_byte(&BQ25186_CONFIG_MASKS[index]);
53 if (((regs[reg] ^ this->expected_register_(index)) & mask) != 0)
54 return false;
55 }
56 return true;
57}
58
60 // Setup rejects an unknown chip; the action must not program it before that check or after it failed
61 if (!this->is_ready() || this->is_failed()) {
62 ESP_LOGW(TAG, "Not applying configuration, setup has not succeeded");
63 return false;
64 }
65 this->regs_ = regs;
66 this->configured_ = true;
67 for (size_t i = 0; i < regs.size(); i++) {
68 const uint8_t reg = BQ25186_REG_VBAT_CTRL + i;
69 const uint8_t mask = progmem_read_byte(&BQ25186_CONFIG_MASKS[i]);
70 const uint8_t value = this->expected_register_(i);
71 const bool ok = mask == 0xFF ? this->write_register_(reg, value) : this->update_register_(reg, mask, value);
72 if (!ok) {
73 this->status_set_warning(LOG_STR("Configuration partly applied"));
74 return false;
75 }
76 }
78 return true;
79}
80
82 uint8_t mask_id;
83 if (!this->read_byte(BQ25186_REG_MASK_ID, &mask_id)) {
84 this->mark_failed(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
85 return;
86 }
87
88 // Another charger at this address could lay its registers out differently, so do not write into it
89 const uint8_t device_id = mask_id & 0x0F;
90 if (device_id != 0x01) {
91 ESP_LOGE(TAG, "Unexpected device id 0x%X (expected 0x1)", device_id);
92 this->mark_failed(LOG_STR("Not a BQ25186"));
93 return;
94 }
95
96 // A failed write only sets the warning status: the chip has identified itself, so a later
97 // apply_configuration may retry
98 if (this->configure_on_boot_)
99 this->apply_config(this->regs_);
100#ifdef USE_BQ25186_PG_GPO
101 if (this->pg_gpo_switch_ != nullptr) {
102 // The poll below puts the pin in output mode; without a restore mode the switch takes its level from it
104 if (initial.has_value())
105 this->pg_gpo_switch_->publish_state(*initial); // the first poll writes it to the pin
106 }
107#endif
108 // The poller first runs after a random share of the interval; the entities get a state now
109 this->update();
110}
111
112#ifdef USE_BQ25186_PG_GPO
113// The pin mode and level come from the switch, so every poll puts back whatever the chip lost
114void BQ25186Component::check_pg_gpo_(const BQ25186StatusRegisters &status, bool &pending) {
115 if (this->pg_gpo_switch_ == nullptr)
116 return;
117 if ((status[BQ25186_REG_VBAT_CTRL] & BQ25186_VBAT_CTRL_PG_MODE) == 0 &&
118 !this->update_register_(BQ25186_REG_VBAT_CTRL, BQ25186_VBAT_CTRL_PG_MODE, BQ25186_VBAT_CTRL_PG_MODE)) {
119 this->status_set_warning(LOG_STR("PG/GPO pin not in output mode"));
120 pending = true;
121 }
122 const bool level = (status[BQ25186_REG_SYS_REG] & BQ25186_SYS_REG_PG_GPO_LEVEL) != 0;
123 if (!this->pg_gpo_switch_->has_state()) {
124 this->pg_gpo_switch_->publish_state(level);
125 } else if (level != this->pg_gpo_switch_->state && !this->write_pg_gpo_level(this->pg_gpo_switch_->state)) {
126 this->status_set_warning(LOG_STR("PG/GPO level write failed"));
127 pending = true;
128 }
129}
130#endif
131
132// Every poll also serves the I2C watchdog, which wants to see traffic within its period
135 if (!this->read_bytes(BQ25186_REG_STAT0, status.data(), status.size())) {
136 this->status_set_warning(LOG_STR("Status read failed"));
137 return;
138 }
139 // Whatever failed is tried again here, so the warning only stays while something is still wrong
140 bool pending = false;
141 // A watchdog or hardware reset puts the power-on values back, and a failed write leaves a register
142 // behind; either way the configuration is written again
143 if (this->configured_ && !this->config_matches_(status)) {
144 ESP_LOGW(TAG, "Configuration registers differ from the values applied, writing them again");
145 if (this->apply_config(this->regs_)) {
146 status[BQ25186_REG_VBAT_CTRL] |= BQ25186_VBAT_CTRL_PG_MODE; // part of the write when a switch is configured
147 } else {
148 pending = true;
149 }
150 }
151#ifdef USE_BQ25186_PG_GPO
152 this->check_pg_gpo_(status, pending);
153#endif
154 if (!pending)
155 this->status_clear_warning();
156#ifdef BQ25186_BINARY_SENSOR_COUNT
157 for (auto *sensor : this->binary_sensors_)
158 sensor->on_status(status);
159#endif
160#ifdef BQ25186_TEXT_SENSOR_COUNT
161 for (auto *sensor : this->text_sensors_)
162 sensor->on_status(status);
163#endif
164}
165
167 ESP_LOGCONFIG(TAG, "BQ25186:");
168 LOG_I2C_DEVICE(this);
169 LOG_UPDATE_INTERVAL(this);
170#ifdef USE_BQ25186_PG_GPO
171 LOG_SWITCH(" ", "PG/GPO", this->pg_gpo_switch_);
172#endif
173}
174
175} // namespace esphome::bq25186
uint8_t status
Definition bl0942.h:8
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:274
bool is_ready() const
void status_clear_warning()
Definition component.h:291
bool has_state() const
StaticVector< BQ25186TextSensor *, BQ25186_TEXT_SENSOR_COUNT > text_sensors_
Definition bq25186.h:85
BQ25186ConfigRegisters regs_
Definition bq25186.h:90
void check_pg_gpo_(const BQ25186StatusRegisters &status, bool &pending)
Definition bq25186.cpp:114
switch_::Switch * pg_gpo_switch_
Definition bq25186.h:88
StaticVector< BQ25186BinarySensor *, BQ25186_BINARY_SENSOR_COUNT > binary_sensors_
Definition bq25186.h:82
bool write_register_(uint8_t reg, uint8_t value)
Definition bq25186.cpp:22
bool config_matches_(const BQ25186StatusRegisters &regs) const
Definition bq25186.cpp:49
bool apply_config(const BQ25186ConfigRegisters &regs)
Definition bq25186.cpp:59
uint8_t expected_register_(size_t index) const
Definition bq25186.cpp:40
bool update_register_(uint8_t reg, uint8_t mask, uint8_t value)
Definition bq25186.cpp:30
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:265
bool read_byte(uint8_t a_register, uint8_t *data)
Definition i2c.h:240
I2CRegister reg(uint8_t a_register)
calls the I2CRegister constructor
Definition i2c.h:152
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
Compat APIs All methods below have been added for compatibility reasons.
Definition i2c.h:217
bool state
The current reported state of the binary sensor.
Definition switch.h:55
void publish_state(bool state)
Publish a state to the front-end from the back-end.
Definition switch.cpp:56
optional< bool > get_initial_state_with_restore_mode()
Returns the initial state of the switch, after applying restore mode rules.
Definition switch.cpp:42
ESPHOME_LOG_TAG(TAG, "bq25186")
std::array< uint8_t, BQ25186_CONFIG_REGISTER_COUNT > BQ25186ConfigRegisters
Definition bq25186.h:38
std::array< uint8_t, BQ25186_STATUS_REGISTER_COUNT > BQ25186StatusRegisters
Definition bq25186.h:39
uint8_t progmem_read_byte(const uint8_t *addr)
Definition hal.h:51