mirror of
https://github.com/esphome/esphome.git
synced 2026-10-14 02:02:51 +00:00
[bq76972] Add BQ76972 BMS chip monitoring component (#17720)
Co-authored-by: pre-commit-ci-lite[bot] <117423508+pre-commit-ci-lite[bot]@users.noreply.github.com> Co-authored-by: J. Nick Koston <nick@koston.org> Co-authored-by: J. Nick Koston <nick@home-assistant.io>
This commit is contained in:
co-authored by
pre-commit-ci-lite[bot]
J. Nick Koston
J. Nick Koston
parent
f829447b2e
commit
8acf885241
@@ -106,6 +106,7 @@ esphome/components/bp1658cj/* @Cossid
|
||||
esphome/components/bp5758d/* @Cossid
|
||||
esphome/components/bq25186/* @Rapsssito
|
||||
esphome/components/bq27220/* @rggammon
|
||||
esphome/components/bq76972/* @limpkin
|
||||
esphome/components/bridge/* @kbx81
|
||||
esphome/components/bthome_mithermometer/* @nagyrobi
|
||||
esphome/components/button/* @esphome/core
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
from esphome import automation
|
||||
import esphome.codegen as cg
|
||||
from esphome.components import i2c, sensor
|
||||
import esphome.config_validation as cv
|
||||
from esphome.const import (
|
||||
CONF_ADDRESS,
|
||||
CONF_ID,
|
||||
DEVICE_CLASS_TEMPERATURE,
|
||||
DEVICE_CLASS_VOLTAGE,
|
||||
STATE_CLASS_MEASUREMENT,
|
||||
UNIT_CELSIUS,
|
||||
UNIT_VOLT,
|
||||
)
|
||||
from esphome.types import ConfigType
|
||||
|
||||
AUTO_LOAD = ["sensor"]
|
||||
CODEOWNERS = ["@limpkin"]
|
||||
DEPENDENCIES = ["i2c"]
|
||||
DOMAIN = "bq76972"
|
||||
MULTI_CONF = True
|
||||
|
||||
bq76972_ns = cg.esphome_ns.namespace("bq76972")
|
||||
BQ76972Component = bq76972_ns.class_(
|
||||
"BQ76972Component", cg.PollingComponent, i2c.I2CDevice
|
||||
)
|
||||
|
||||
CONF_REG_DISABLED = "reg_disabled"
|
||||
CONF_CRC_ENABLED = "crc_enabled"
|
||||
CONF_STACK_VOLTAGE = "stack_voltage"
|
||||
|
||||
# Ordered as the chip reports them, starting at command 0x68
|
||||
CONF_TEMPERATURES = [
|
||||
"internal_temperature",
|
||||
"cfetoff_temperature",
|
||||
"dfetoff_temperature",
|
||||
"alert_temperature",
|
||||
"ts1_temperature",
|
||||
"ts2_temperature",
|
||||
"ts3_temperature",
|
||||
"hdq_temperature",
|
||||
"dchg_temperature",
|
||||
"ddsg_temperature",
|
||||
]
|
||||
|
||||
CONF_CELL_VOLTAGES = [f"cell_{i}_voltage" for i in range(1, 17)]
|
||||
|
||||
voltage_sensor_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_VOLT,
|
||||
accuracy_decimals=3,
|
||||
device_class=DEVICE_CLASS_VOLTAGE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
|
||||
temp_sensor_schema = sensor.sensor_schema(
|
||||
unit_of_measurement=UNIT_CELSIUS,
|
||||
accuracy_decimals=1,
|
||||
device_class=DEVICE_CLASS_TEMPERATURE,
|
||||
state_class=STATE_CLASS_MEASUREMENT,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = (
|
||||
cv.Schema(
|
||||
{
|
||||
cv.GenerateID(): cv.declare_id(BQ76972Component),
|
||||
cv.Optional(CONF_CRC_ENABLED, default=False): cv.boolean,
|
||||
cv.Optional(CONF_REG_DISABLED, default=False): cv.boolean,
|
||||
cv.Optional(CONF_STACK_VOLTAGE): voltage_sensor_schema,
|
||||
**{cv.Optional(key): voltage_sensor_schema for key in CONF_CELL_VOLTAGES},
|
||||
**{cv.Optional(key): temp_sensor_schema for key in CONF_TEMPERATURES},
|
||||
}
|
||||
)
|
||||
.extend(i2c.i2c_device_schema(0x08))
|
||||
.extend(cv.polling_component_schema("10s"))
|
||||
)
|
||||
|
||||
|
||||
async def to_code(config: ConfigType) -> None:
|
||||
var = cg.new_Pvariable(config[CONF_ID])
|
||||
await cg.register_component(var, config)
|
||||
await i2c.register_i2c_device(var, config)
|
||||
|
||||
cg.add(var.set_crc_mode(config[CONF_CRC_ENABLED]))
|
||||
cg.add(var.set_reg_disable(config[CONF_REG_DISABLED]))
|
||||
|
||||
sensors = sensor.sub_sensors(config)
|
||||
await sensors(CONF_STACK_VOLTAGE, var.set_stack_voltage_sensor)
|
||||
|
||||
for idx, key in enumerate(CONF_TEMPERATURES):
|
||||
if (conf := config.get(key)) is not None:
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_temperature_sensor(sens, idx))
|
||||
|
||||
for idx, key in enumerate(CONF_CELL_VOLTAGES):
|
||||
if (conf := config.get(key)) is not None:
|
||||
sens = await sensor.new_sensor(conf)
|
||||
cg.add(var.set_cell_sensor(sens, idx))
|
||||
|
||||
|
||||
BQ76972_PROGRAM_I2C_ADDRESS_SCHEMA = cv.maybe_simple_value(
|
||||
{
|
||||
cv.GenerateID(): cv.use_id(BQ76972Component),
|
||||
cv.Required(CONF_ADDRESS): cv.templatable(cv.i2c_address),
|
||||
},
|
||||
key=CONF_ADDRESS,
|
||||
)
|
||||
|
||||
automation.register_apply_action(
|
||||
"bq76972.program_i2c_address",
|
||||
BQ76972_PROGRAM_I2C_ADDRESS_SCHEMA,
|
||||
automation.ApplyField(CONF_ADDRESS, "program_i2c_address", cg.uint8),
|
||||
)
|
||||
@@ -0,0 +1,298 @@
|
||||
#include "bq76972.h"
|
||||
#include "esphome/core/hal.h"
|
||||
#include "esphome/core/helpers.h"
|
||||
#include "esphome/core/log.h"
|
||||
|
||||
namespace esphome::bq76972 {
|
||||
|
||||
ESPHOME_LOG_TAG(TAG, "bq76972");
|
||||
|
||||
static constexpr uint8_t BQ76972_REG_SUBCOMMAND = 0x3E; // 0x3E low byte, 0x3F high byte
|
||||
static constexpr uint8_t BQ76972_REG_TRANSFER_BUFFER = 0x40;
|
||||
static constexpr uint8_t BQ76972_REG_CHECKSUM = 0x60; // 0x60 checksum, 0x61 length
|
||||
static constexpr uint8_t BQ76972_CMD_BAT_STATUS = 0x12;
|
||||
static constexpr uint8_t BQ76972_CMD_CELL_START = 0x14;
|
||||
static constexpr uint8_t BQ76972_CMD_STACK_VOLTAGE = 0x34;
|
||||
static constexpr uint8_t BQ76972_CMD_TEMP_START = 0x68;
|
||||
static constexpr uint16_t BQ76972_SUBCMD_ENTER_CFG_UPDATE = 0x0090;
|
||||
static constexpr uint16_t BQ76972_SUBCMD_EXIT_CFG_UPDATE = 0x0092;
|
||||
static constexpr uint16_t BQ76972_MEM_REG12_CONFIG = 0x9236;
|
||||
static constexpr uint16_t BQ76972_MEM_REG0_CONFIG = 0x9237;
|
||||
static constexpr uint16_t BQ76972_MEM_I2C_ADDRESS = 0x923A;
|
||||
static constexpr uint16_t BQ76972_MEM_SWAP_COMM_MODE = 0x29BC;
|
||||
// Pin config of temperature reading N (1 = CFETOFF ... 9 = DDSG) is at this base + N
|
||||
static constexpr uint16_t BQ76972_MEM_TEMP_PIN_CONFIG_BASE = 0x92F9;
|
||||
static constexpr size_t BQ76972_TS1_INDEX = 4; // a thermistor input by default
|
||||
static constexpr uint8_t BQ76972_PIN_CONFIG_THERMISTOR = 0x07;
|
||||
|
||||
// 16 words with a CRC byte after every data byte
|
||||
static constexpr size_t BQ76972_MAX_TRANSFER = 64;
|
||||
static constexpr uint8_t BQ76972_MAX_POLLS = 50;
|
||||
|
||||
static float raw_to_celsius(uint16_t raw) { return static_cast<int16_t>(raw) * 0.1f - 273.15f; }
|
||||
|
||||
bool BQ76972Component::read_block_(uint8_t start_register, uint8_t *data, size_t len) {
|
||||
if (!this->crc_mode_)
|
||||
return this->read_bytes(start_register, data, len);
|
||||
|
||||
// With CRC enabled, every data byte is followed by a CRC byte
|
||||
std::array<uint8_t, BQ76972_MAX_TRANSFER> buffer;
|
||||
if (len == 0 || len * 2 > buffer.size())
|
||||
return false;
|
||||
if (!this->read_bytes(start_register, buffer.data(), len * 2))
|
||||
return false;
|
||||
|
||||
// The first CRC also covers the write address, register and read address
|
||||
const uint8_t crc0_input[4] = {static_cast<uint8_t>(this->address_ << 1), start_register,
|
||||
static_cast<uint8_t>((this->address_ << 1) | 1), buffer[0]};
|
||||
if (crc8(crc0_input, 4, 0x00, 0x07, true) != buffer[1]) {
|
||||
ESP_LOGD(TAG, "CRC mismatch at register 0x%02X byte 0", start_register);
|
||||
return false;
|
||||
}
|
||||
data[0] = buffer[0];
|
||||
for (size_t i = 1; i < len; ++i) {
|
||||
if (crc8(&buffer[i * 2], 1, 0x00, 0x07, true) != buffer[i * 2 + 1]) {
|
||||
ESP_LOGD(TAG, "CRC mismatch at register 0x%02X byte %u", start_register, static_cast<unsigned>(i));
|
||||
return false;
|
||||
}
|
||||
data[i] = buffer[i * 2];
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BQ76972Component::write_block_(uint8_t start_register, const uint8_t *data, size_t len) {
|
||||
if (!this->crc_mode_)
|
||||
return this->write_bytes(start_register, data, len);
|
||||
|
||||
std::array<uint8_t, BQ76972_MAX_TRANSFER> buffer;
|
||||
if (len == 0 || len * 2 > buffer.size())
|
||||
return false;
|
||||
|
||||
// The first CRC also covers the write address and register
|
||||
const uint8_t crc0_input[3] = {static_cast<uint8_t>(this->address_ << 1), start_register, data[0]};
|
||||
buffer[0] = data[0];
|
||||
buffer[1] = crc8(crc0_input, 3, 0x00, 0x07, true);
|
||||
for (size_t i = 1; i < len; ++i) {
|
||||
buffer[i * 2] = data[i];
|
||||
buffer[i * 2 + 1] = crc8(&data[i], 1, 0x00, 0x07, true);
|
||||
}
|
||||
return this->write_bytes(start_register, buffer.data(), len * 2);
|
||||
}
|
||||
|
||||
bool BQ76972Component::read_words_(uint8_t start_register, uint16_t *data, size_t num_words) {
|
||||
std::array<uint8_t, BQ76972_MAX_TRANSFER / 2> bytes;
|
||||
if (num_words * 2 > bytes.size() || !this->read_block_(start_register, bytes.data(), num_words * 2))
|
||||
return false;
|
||||
for (size_t i = 0; i < num_words; i++)
|
||||
data[i] = encode_uint16(bytes[2 * i + 1], bytes[2 * i]);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BQ76972Component::send_subcommand_(uint16_t subcommand) {
|
||||
const uint8_t bytes[2] = {static_cast<uint8_t>(subcommand & 0xFF), static_cast<uint8_t>(subcommand >> 8)};
|
||||
return this->write_block_(BQ76972_REG_SUBCOMMAND, bytes, 2);
|
||||
}
|
||||
|
||||
bool BQ76972Component::wait_for_subcommand_() {
|
||||
uint8_t bytes[2];
|
||||
for (uint8_t i = 0; i < BQ76972_MAX_POLLS; i++) {
|
||||
if (!this->read_block_(BQ76972_REG_SUBCOMMAND, bytes, 2))
|
||||
return false;
|
||||
if (bytes[0] != 0xFF && bytes[1] != 0xFF)
|
||||
return true;
|
||||
delay(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BQ76972Component::wait_for_cfgupdate_() {
|
||||
uint16_t status;
|
||||
for (uint8_t i = 0; i < BQ76972_MAX_POLLS; i++) {
|
||||
if (!this->read_words_(BQ76972_CMD_BAT_STATUS, &status, 1))
|
||||
return false;
|
||||
if (status & 0x0001)
|
||||
return true;
|
||||
delay(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool BQ76972Component::read_subcommand_(uint16_t subcommand, uint8_t *data, size_t len) {
|
||||
return this->send_subcommand_(subcommand) && this->wait_for_subcommand_() &&
|
||||
this->read_block_(BQ76972_REG_TRANSFER_BUFFER, data, len);
|
||||
}
|
||||
|
||||
bool BQ76972Component::write_subcommand_(uint16_t subcommand, const uint8_t *data, size_t len) {
|
||||
if (!this->send_subcommand_(subcommand))
|
||||
return false;
|
||||
if (len == 0)
|
||||
return true;
|
||||
if (!this->write_block_(BQ76972_REG_TRANSFER_BUFFER, data, len))
|
||||
return false;
|
||||
|
||||
// Inverted 8-bit sum of 0x3E, 0x3F and the data; length counts 0x3E, 0x3F, 0x60, 0x61 and the data
|
||||
uint8_t sum = static_cast<uint8_t>(subcommand & 0xFF) + static_cast<uint8_t>(subcommand >> 8);
|
||||
for (size_t i = 0; i < len; ++i)
|
||||
sum += data[i];
|
||||
const uint8_t tail[2] = {static_cast<uint8_t>(~sum), static_cast<uint8_t>(4 + len)};
|
||||
return this->write_block_(BQ76972_REG_CHECKSUM, tail, 2);
|
||||
}
|
||||
|
||||
bool BQ76972Component::enter_cfg_update_() {
|
||||
if (!this->write_subcommand_(BQ76972_SUBCMD_ENTER_CFG_UPDATE, nullptr, 0))
|
||||
return false;
|
||||
delay(10);
|
||||
return this->wait_for_cfgupdate_();
|
||||
}
|
||||
|
||||
bool BQ76972Component::exit_cfg_update_() {
|
||||
return this->write_subcommand_(BQ76972_SUBCMD_EXIT_CFG_UPDATE, nullptr, 0);
|
||||
}
|
||||
|
||||
bool BQ76972Component::regs_disabled_(bool &out) {
|
||||
uint8_t reg12;
|
||||
uint8_t reg0;
|
||||
if (!this->read_subcommand_(BQ76972_MEM_REG12_CONFIG, ®12, 1) ||
|
||||
!this->read_subcommand_(BQ76972_MEM_REG0_CONFIG, ®0, 1))
|
||||
return false;
|
||||
out = reg12 == 0 && (reg0 & 0x01) == 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
const LogString *BQ76972Component::configure_() {
|
||||
uint16_t internal_temp;
|
||||
if (!this->read_words_(BQ76972_CMD_TEMP_START, &internal_temp, 1))
|
||||
return LOG_STR(ESP_LOG_MSG_COMM_FAIL);
|
||||
|
||||
// Only write the regulator config when it differs, see
|
||||
// https://e2e.ti.com/support/power-management-group/power-management/f/power-management-forum/1583718/bq76972-undocumented-4-minute-config_update-auto-exit-timeout
|
||||
bool regs_disabled = true;
|
||||
if (this->reg_disable_ && !this->regs_disabled_(regs_disabled))
|
||||
return LOG_STR("Reading regulator config failed");
|
||||
const bool write_regs = !regs_disabled;
|
||||
|
||||
bool write_thermistors = false;
|
||||
for (size_t i = 1; i < BQ76972_NUM_TEMPERATURES; i++) {
|
||||
if (i != BQ76972_TS1_INDEX && this->temperature_sensors_[i] != nullptr)
|
||||
write_thermistors = true;
|
||||
}
|
||||
|
||||
if (write_regs || write_thermistors) {
|
||||
if (!this->enter_cfg_update_())
|
||||
return LOG_STR("Entering config update mode failed");
|
||||
const LogString *error = this->write_config_(write_regs);
|
||||
// The chip stops measuring in config update mode, so leave it even after a failed write
|
||||
if (!this->exit_cfg_update_() && error == nullptr)
|
||||
error = LOG_STR("Exiting config update mode failed");
|
||||
if (error != nullptr)
|
||||
return error;
|
||||
if (write_regs && (!this->regs_disabled_(regs_disabled) || !regs_disabled))
|
||||
return LOG_STR("Disabling regulators failed");
|
||||
}
|
||||
|
||||
ESP_LOGD(TAG, "Internal temperature: %.1f°C", raw_to_celsius(internal_temp));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const LogString *BQ76972Component::write_config_(bool write_regs) {
|
||||
if (write_regs) {
|
||||
const uint8_t zero = 0;
|
||||
if (!this->write_subcommand_(BQ76972_MEM_REG12_CONFIG, &zero, 1) ||
|
||||
!this->write_subcommand_(BQ76972_MEM_REG0_CONFIG, &zero, 1))
|
||||
return LOG_STR("Writing regulator config failed");
|
||||
}
|
||||
for (size_t i = 1; i < BQ76972_NUM_TEMPERATURES; i++) {
|
||||
if (i == BQ76972_TS1_INDEX || this->temperature_sensors_[i] == nullptr)
|
||||
continue;
|
||||
if (!this->write_subcommand_(BQ76972_MEM_TEMP_PIN_CONFIG_BASE + i, &BQ76972_PIN_CONFIG_THERMISTOR, 1))
|
||||
return LOG_STR("Writing thermistor pin config failed");
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void BQ76972Component::setup() {
|
||||
if (const LogString *error = this->configure_(); error != nullptr)
|
||||
this->mark_failed(error);
|
||||
}
|
||||
|
||||
void BQ76972Component::dump_config() {
|
||||
ESP_LOGCONFIG(TAG, "BQ76972:");
|
||||
LOG_I2C_DEVICE(this);
|
||||
LOG_UPDATE_INTERVAL(this);
|
||||
ESP_LOGCONFIG(TAG,
|
||||
" CRC Mode: %s\n"
|
||||
" Regulators disabled: %s",
|
||||
this->crc_mode_ ? LOG_STR_LITERAL("Enabled") : LOG_STR_LITERAL("Disabled"), YESNO(this->reg_disable_));
|
||||
}
|
||||
|
||||
void BQ76972Component::program_i2c_address(uint8_t address) {
|
||||
if (this->is_failed())
|
||||
return;
|
||||
const uint8_t shifted = address << 1;
|
||||
if (!this->enter_cfg_update_()) {
|
||||
this->status_set_warning(LOG_STR("Programming I2C address failed"));
|
||||
return;
|
||||
}
|
||||
// The new address only takes effect with the swap, so after a failed write the chip still answers at the
|
||||
// old one and must still be taken out of config update mode
|
||||
const bool written = this->write_subcommand_(BQ76972_MEM_I2C_ADDRESS, &shifted, 1) &&
|
||||
this->write_subcommand_(BQ76972_MEM_SWAP_COMM_MODE, nullptr, 0);
|
||||
if (written) {
|
||||
delay(10);
|
||||
this->set_i2c_address(address);
|
||||
}
|
||||
if (!this->exit_cfg_update_() || !written) {
|
||||
this->status_set_warning(LOG_STR("Programming I2C address failed"));
|
||||
return;
|
||||
}
|
||||
ESP_LOGI(TAG, "I2C address set to 0x%02X", address);
|
||||
}
|
||||
|
||||
template<size_t N> static bool any_set(const std::array<sensor::Sensor *, N> &sensors) {
|
||||
for (auto *sensor : sensors) {
|
||||
if (sensor != nullptr)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void BQ76972Component::update() {
|
||||
// Each group publishes as soon as its read succeeds, so one failed read does not hold back the others
|
||||
bool ok = true;
|
||||
uint16_t words[BQ76972_NUM_CELLS];
|
||||
if (this->stack_voltage_sensor_ != nullptr) {
|
||||
if (this->read_words_(BQ76972_CMD_STACK_VOLTAGE, words, 1)) {
|
||||
if (words[0] != 0)
|
||||
this->stack_voltage_sensor_->publish_state(words[0] * 0.01f); // 10 mV units
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (any_set(this->cell_sensors_)) {
|
||||
if (this->read_words_(BQ76972_CMD_CELL_START, words, BQ76972_NUM_CELLS)) {
|
||||
for (size_t i = 0; i < BQ76972_NUM_CELLS; i++) {
|
||||
if (this->cell_sensors_[i] != nullptr && words[i] != 0)
|
||||
this->cell_sensors_[i]->publish_state(words[i] * 0.001f); // mV
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (any_set(this->temperature_sensors_)) {
|
||||
if (this->read_words_(BQ76972_CMD_TEMP_START, words, BQ76972_NUM_TEMPERATURES)) {
|
||||
for (size_t i = 0; i < BQ76972_NUM_TEMPERATURES; i++) {
|
||||
if (this->temperature_sensors_[i] != nullptr)
|
||||
this->temperature_sensors_[i]->publish_state(raw_to_celsius(words[i]));
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
if (ok) {
|
||||
this->status_clear_warning();
|
||||
} else {
|
||||
this->status_set_warning(LOG_STR(ESP_LOG_MSG_COMM_FAIL));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace esphome::bq76972
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include "esphome/components/sensor/sensor.h"
|
||||
#include "esphome/components/i2c/i2c.h"
|
||||
#include "esphome/core/component.h"
|
||||
|
||||
#include <array>
|
||||
|
||||
namespace esphome::bq76972 {
|
||||
|
||||
static constexpr size_t BQ76972_NUM_CELLS = 16;
|
||||
static constexpr size_t BQ76972_NUM_TEMPERATURES = 10;
|
||||
|
||||
class BQ76972Component final : public PollingComponent, public i2c::I2CDevice {
|
||||
public:
|
||||
void setup() override;
|
||||
void update() override;
|
||||
void dump_config() override;
|
||||
|
||||
void set_crc_mode(bool crc_mode) { this->crc_mode_ = crc_mode; }
|
||||
void set_reg_disable(bool reg_disable) { this->reg_disable_ = reg_disable; }
|
||||
void set_stack_voltage_sensor(sensor::Sensor *s) { this->stack_voltage_sensor_ = s; }
|
||||
void set_cell_sensor(sensor::Sensor *s, size_t index) { this->cell_sensors_[index] = s; }
|
||||
void set_temperature_sensor(sensor::Sensor *s, size_t index) { this->temperature_sensors_[index] = s; }
|
||||
|
||||
void program_i2c_address(uint8_t address);
|
||||
|
||||
protected:
|
||||
bool read_block_(uint8_t start_register, uint8_t *data, size_t len);
|
||||
bool write_block_(uint8_t start_register, const uint8_t *data, size_t len);
|
||||
bool read_words_(uint8_t start_register, uint16_t *data, size_t num_words);
|
||||
bool send_subcommand_(uint16_t subcommand);
|
||||
bool read_subcommand_(uint16_t subcommand, uint8_t *data, size_t len);
|
||||
bool write_subcommand_(uint16_t subcommand, const uint8_t *data, size_t len);
|
||||
bool wait_for_subcommand_();
|
||||
bool wait_for_cfgupdate_();
|
||||
bool enter_cfg_update_();
|
||||
bool exit_cfg_update_();
|
||||
bool regs_disabled_(bool &out);
|
||||
const LogString *configure_();
|
||||
const LogString *write_config_(bool write_regs);
|
||||
|
||||
bool reg_disable_{false};
|
||||
bool crc_mode_{false};
|
||||
|
||||
sensor::Sensor *stack_voltage_sensor_{nullptr};
|
||||
std::array<sensor::Sensor *, BQ76972_NUM_CELLS> cell_sensors_{};
|
||||
std::array<sensor::Sensor *, BQ76972_NUM_TEMPERATURES> temperature_sensors_{};
|
||||
};
|
||||
|
||||
} // namespace esphome::bq76972
|
||||
@@ -0,0 +1,59 @@
|
||||
bq76972:
|
||||
id: bq76972_id
|
||||
address: 0x08
|
||||
crc_enabled: true
|
||||
reg_disabled: true
|
||||
update_interval: 10s
|
||||
stack_voltage:
|
||||
name: "Pack Total Voltage"
|
||||
internal_temperature:
|
||||
name: "Temperature - Internal"
|
||||
ts1_temperature:
|
||||
name: "Temperature - Pin TS1"
|
||||
ts2_temperature:
|
||||
name: "Temperature - Pin TS2"
|
||||
ts3_temperature:
|
||||
name: "Temperature - Pin TS3"
|
||||
hdq_temperature:
|
||||
name: "Temperature - Pin HDQ"
|
||||
cell_1_voltage:
|
||||
name: "Cell 1 Voltage"
|
||||
cell_2_voltage:
|
||||
name: "Cell 2 Voltage"
|
||||
cell_3_voltage:
|
||||
name: "Cell 3 Voltage"
|
||||
cell_4_voltage:
|
||||
name: "Cell 4 Voltage"
|
||||
cell_5_voltage:
|
||||
name: "Cell 5 Voltage"
|
||||
cell_6_voltage:
|
||||
name: "Cell 6 Voltage"
|
||||
cell_7_voltage:
|
||||
name: "Cell 7 Voltage"
|
||||
cell_8_voltage:
|
||||
name: "Cell 8 Voltage"
|
||||
cell_9_voltage:
|
||||
name: "Cell 9 Voltage"
|
||||
cell_10_voltage:
|
||||
name: "Cell 10 Voltage"
|
||||
cell_11_voltage:
|
||||
name: "Cell 11 Voltage"
|
||||
cell_12_voltage:
|
||||
name: "Cell 12 Voltage"
|
||||
cell_13_voltage:
|
||||
name: "Cell 13 Voltage"
|
||||
cell_14_voltage:
|
||||
name: "Cell 14 Voltage"
|
||||
cell_15_voltage:
|
||||
name: "Cell 15 Voltage"
|
||||
cell_16_voltage:
|
||||
name: "Cell 16 Voltage"
|
||||
|
||||
button:
|
||||
- platform: template
|
||||
name: "Program BQ I2C Address"
|
||||
on_press:
|
||||
- bq76972.program_i2c_address:
|
||||
id: bq76972_id
|
||||
address: 0x09
|
||||
- bq76972.program_i2c_address: !lambda "return 0x08;"
|
||||
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp32-idf.yaml
|
||||
bq76972: !include common.yaml
|
||||
@@ -0,0 +1,3 @@
|
||||
packages:
|
||||
i2c: !include ../../test_build_components/common/i2c/esp8266-ard.yaml
|
||||
bq76972: !include common.yaml
|
||||
Reference in New Issue
Block a user