10static constexpr uint8_t BQ76972_REG_SUBCOMMAND = 0x3E;
11static constexpr uint8_t BQ76972_REG_TRANSFER_BUFFER = 0x40;
12static constexpr uint8_t BQ76972_REG_CHECKSUM = 0x60;
13static constexpr uint8_t BQ76972_CMD_BAT_STATUS = 0x12;
14static constexpr uint8_t BQ76972_CMD_CELL_START = 0x14;
15static constexpr uint8_t BQ76972_CMD_STACK_VOLTAGE = 0x34;
16static constexpr uint8_t BQ76972_CMD_TEMP_START = 0x68;
17static constexpr uint16_t BQ76972_SUBCMD_ENTER_CFG_UPDATE = 0x0090;
18static constexpr uint16_t BQ76972_SUBCMD_EXIT_CFG_UPDATE = 0x0092;
19static constexpr uint16_t BQ76972_MEM_REG12_CONFIG = 0x9236;
20static constexpr uint16_t BQ76972_MEM_REG0_CONFIG = 0x9237;
21static constexpr uint16_t BQ76972_MEM_I2C_ADDRESS = 0x923A;
22static constexpr uint16_t BQ76972_MEM_SWAP_COMM_MODE = 0x29BC;
24static constexpr uint16_t BQ76972_MEM_TEMP_PIN_CONFIG_BASE = 0x92F9;
25static constexpr size_t BQ76972_TS1_INDEX = 4;
26static constexpr uint8_t BQ76972_PIN_CONFIG_THERMISTOR = 0x07;
29static constexpr size_t BQ76972_MAX_TRANSFER = 64;
30static constexpr uint8_t BQ76972_MAX_POLLS = 50;
32static float raw_to_celsius(uint16_t
raw) {
return static_cast<int16_t
>(
raw) * 0.1f - 273.15f; }
39 std::array<uint8_t, BQ76972_MAX_TRANSFER>
buffer;
46 const uint8_t crc0_input[4] = {
static_cast<uint8_t
>(this->
address_ << 1), start_register,
47 static_cast<uint8_t
>((this->
address_ << 1) | 1), buffer[0]};
48 if (
crc8(crc0_input, 4, 0x00, 0x07,
true) !=
buffer[1]) {
49 ESP_LOGD(TAG,
"CRC mismatch at register 0x%02X byte 0", start_register);
53 for (
size_t i = 1; i <
len; ++i) {
55 ESP_LOGD(TAG,
"CRC mismatch at register 0x%02X byte %u", start_register,
static_cast<unsigned>(i));
67 std::array<uint8_t, BQ76972_MAX_TRANSFER>
buffer;
72 const uint8_t crc0_input[3] = {
static_cast<uint8_t
>(this->
address_ << 1), start_register, data[0]};
74 buffer[1] =
crc8(crc0_input, 3, 0x00, 0x07,
true);
75 for (
size_t i = 1; i <
len; ++i) {
77 buffer[i * 2 + 1] =
crc8(&data[i], 1, 0x00, 0x07,
true);
83 std::array<uint8_t, BQ76972_MAX_TRANSFER / 2> bytes;
84 if (num_words * 2 > bytes.size() || !this->read_block_(start_register, bytes.data(), num_words * 2))
86 for (
size_t i = 0; i < num_words; i++)
92 const uint8_t bytes[2] = {
static_cast<uint8_t
>(subcommand & 0xFF),
static_cast<uint8_t
>(subcommand >> 8)};
93 return this->
write_block_(BQ76972_REG_SUBCOMMAND, bytes, 2);
98 for (uint8_t i = 0; i < BQ76972_MAX_POLLS; i++) {
99 if (!this->
read_block_(BQ76972_REG_SUBCOMMAND, bytes, 2))
101 if (bytes[0] != 0xFF && bytes[1] != 0xFF)
110 for (uint8_t i = 0; i < BQ76972_MAX_POLLS; i++) {
134 uint8_t sum =
static_cast<uint8_t
>(subcommand & 0xFF) +
static_cast<uint8_t
>(subcommand >> 8);
135 for (
size_t i = 0; i <
len; ++i)
137 const uint8_t tail[2] = {
static_cast<uint8_t
>(~sum),
static_cast<uint8_t
>(4 +
len)};
138 return this->
write_block_(BQ76972_REG_CHECKSUM, tail, 2);
158 out = reg12 == 0 && (reg0 & 0x01) == 0;
163 uint16_t internal_temp;
164 if (!this->
read_words_(BQ76972_CMD_TEMP_START, &internal_temp, 1))
165 return LOG_STR(ESP_LOG_MSG_COMM_FAIL);
169 bool regs_disabled =
true;
171 return LOG_STR(
"Reading regulator config failed");
172 const bool write_regs = !regs_disabled;
174 bool write_thermistors =
false;
175 for (
size_t i = 1; i < BQ76972_NUM_TEMPERATURES; i++) {
177 write_thermistors =
true;
180 if (write_regs || write_thermistors) {
182 return LOG_STR(
"Entering config update mode failed");
186 error = LOG_STR(
"Exiting config update mode failed");
187 if (error !=
nullptr)
189 if (write_regs && (!this->
regs_disabled_(regs_disabled) || !regs_disabled))
190 return LOG_STR(
"Disabling regulators failed");
193 ESP_LOGD(TAG,
"Internal temperature: %.1f°C", raw_to_celsius(internal_temp));
199 const uint8_t zero = 0;
202 return LOG_STR(
"Writing regulator config failed");
204 for (
size_t i = 1; i < BQ76972_NUM_TEMPERATURES; i++) {
207 if (!this->
write_subcommand_(BQ76972_MEM_TEMP_PIN_CONFIG_BASE + i, &BQ76972_PIN_CONFIG_THERMISTOR, 1))
208 return LOG_STR(
"Writing thermistor pin config failed");
214 if (
const LogString *error = this->
configure_(); error !=
nullptr)
219 ESP_LOGCONFIG(TAG,
"BQ76972:");
220 LOG_I2C_DEVICE(
this);
221 LOG_UPDATE_INTERVAL(
this);
224 " Regulators disabled: %s",
225 this->
crc_mode_ ? LOG_STR_LITERAL(
"Enabled") : LOG_STR_LITERAL(
"Disabled"), YESNO(this->
reg_disable_));
231 const uint8_t shifted =
address << 1;
248 ESP_LOGI(TAG,
"I2C address set to 0x%02X",
address);
251template<
size_t N>
static bool any_set(
const std::array<sensor::Sensor *, N> &sensors) {
252 for (
auto *sensor : sensors) {
253 if (sensor !=
nullptr)
262 uint16_t words[BQ76972_NUM_CELLS];
264 if (this->
read_words_(BQ76972_CMD_STACK_VOLTAGE, words, 1)) {
272 if (this->
read_words_(BQ76972_CMD_CELL_START, words, BQ76972_NUM_CELLS)) {
273 for (
size_t i = 0; i < BQ76972_NUM_CELLS; i++) {
282 if (this->
read_words_(BQ76972_CMD_TEMP_START, words, BQ76972_NUM_TEMPERATURES)) {
283 for (
size_t i = 0; i < BQ76972_NUM_TEMPERATURES; i++) {
void mark_failed()
Mark this component as failed.
void status_set_warning()
void status_clear_warning()
bool read_block_(uint8_t start_register, uint8_t *data, size_t len)
bool wait_for_subcommand_()
void dump_config() override
bool read_subcommand_(uint16_t subcommand, uint8_t *data, size_t len)
bool wait_for_cfgupdate_()
bool read_words_(uint8_t start_register, uint16_t *data, size_t num_words)
const LogString * write_config_(bool write_regs)
std::array< sensor::Sensor *, BQ76972_NUM_CELLS > cell_sensors_
bool write_subcommand_(uint16_t subcommand, const uint8_t *data, size_t len)
bool write_block_(uint8_t start_register, const uint8_t *data, size_t len)
void program_i2c_address(uint8_t address)
const LogString * configure_()
bool regs_disabled_(bool &out)
std::array< sensor::Sensor *, BQ76972_NUM_TEMPERATURES > temperature_sensors_
sensor::Sensor * stack_voltage_sensor_
bool send_subcommand_(uint16_t subcommand)
uint8_t address_
store the address of the device on the bus
void set_i2c_address(uint8_t address)
We store the address of the device on the bus.
bool write_bytes(uint8_t a_register, const uint8_t *data, uint8_t len) const
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
Compat APIs All methods below have been added for compatibility reasons.
void publish_state(float state)
Publish a new state to the front-end.
std::vector< uint8_t > & out
ESPHOME_LOG_TAG(TAG, "bq76972")
uint8_t crc8(const uint8_t *data, uint8_t len, uint8_t crc, uint8_t poly, bool msb_first)
Calculate a CRC-8 checksum of data with size len.
constexpr uint16_t encode_uint16(uint8_t msb, uint8_t lsb)
Encode a 16-bit value given the most and least significant byte.
void HOT delay(uint32_t ms)