15#include "driver/gpio.h"
16#include "freertos/FreeRTOS.h"
17#include "freertos/task.h"
19static const char*
TAG =
"BQ25895";
95 bool start()
override;
98 const char*
getName()
const override {
return "power"; }
121 bool readReg(uint8_t reg, uint8_t* value)
const;
122 bool writeReg(uint8_t reg, uint8_t value);
123 bool updateRegBits(uint8_t reg, uint8_t mask, uint8_t value,
const char* label);
126 void readChargerStatus();
127 bool setChargeCurrentMa(uint16_t currentMa);
140 bool fastChargeEnabled_ =
false;
141 uint32_t lastWdtKickMs_ = 0;
144 bool powerButtonHeld_ =
false;
145 uint32_t powerButtonHoldStartMs_ = 0;
148 mutable uint16_t cachedBatteryMv_ = 0;
150 mutable bool cachedUsbConnected_ =
false;
151 mutable bool cachedBatteryPresent_ =
false;
155 bool prevUsbConnected_ =
false;
156 bool prevBatteryPresent_ =
false;
165bool BQ25895Power::readReg(uint8_t reg, uint8_t* value)
const {
166 if (!device_ || !value)
return false;
167 return bus_->
readReg(device_, reg, value, 1) == ESP_OK;
176bool BQ25895Power::writeReg(uint8_t reg, uint8_t value) {
177 if (!device_)
return false;
178 return bus_->
writeReg(device_, reg, &value, 1) == ESP_OK;
189bool BQ25895Power::updateRegBits(uint8_t reg, uint8_t mask, uint8_t value,
const char* label) {
191 if (!readReg(reg, ¤t)) {
192 LOG_E(
TAG,
"Read failed: %s", label);
196 uint8_t newVal = (current & ~mask) | (value & mask);
197 if (newVal == current) {
198 LOG_D(
TAG,
"%s already set (0x%02X)", label, current);
202 if (!writeReg(reg, newVal)) {
203 LOG_E(
TAG,
"Write failed: %s", label);
207 LOG_D(
TAG,
"%s: 0x%02X -> 0x%02X", label, current, newVal);
221 LOG_I(
TAG,
"Initializing BQ25895 power management");
226 LOG_E(
TAG,
"I2C bus not initialized");
232 if (bus_->addDevice(
BQ25895_ADDR, &device_) != ESP_OK) {
233 LOG_E(
TAG,
"Failed to add BQ25895 to I2C0");
241 LOG_E(
TAG,
"Failed to read vendor register");
245 uint8_t partNumber = (vendor >> 3) & 0x07;
246 if (partNumber != 7) {
247 LOG_E(
TAG,
"BQ25895 not detected (got part=%d)", partNumber);
251 LOG_I(
TAG,
"BQ25895 detected (REG14=0x%02X)", vendor);
263 if (!updateRegBits(
BQ_REG_CHG_CTRL, 0x0E, (uint8_t)(sysMinCode << 1),
"SYS_MIN=3.3V")) {
284 gpio_config_t io_conf = {};
286 io_conf.mode = GPIO_MODE_INPUT;
287 io_conf.pull_up_en = GPIO_PULLUP_ENABLE;
288 io_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
289 io_conf.intr_type = GPIO_INTR_NEGEDGE;
290 gpio_config(&io_conf);
293 esp_err_t err = gpio_install_isr_service(0);
294 if (err != ESP_OK && err != ESP_ERR_INVALID_STATE) {
295 LOG_E(
TAG,
"Failed to install ISR service: %s", esp_err_to_name(err));
303 gpio_config_t btn_conf = {};
305 btn_conf.mode = GPIO_MODE_INPUT;
306 btn_conf.pull_up_en = GPIO_PULLUP_ENABLE;
307 btn_conf.pull_down_en = GPIO_PULLDOWN_DISABLE;
308 btn_conf.intr_type = GPIO_INTR_DISABLE;
309 gpio_config(&btn_conf);
344void BQ25895Power::readChargerStatus() {
345 uint8_t reg0b = 0, reg0c = 0;
346 uint8_t chrgStat = 0;
349 chrgStat = (reg0b >> 3) & 0x03;
350 bool pgStat = (reg0b >> 2) & 0x01;
353 bool vsysMinRegulation = (reg0b & 0x01) != 0;
356 cachedUsbConnected_ = pgStat;
375 cachedBatteryPresent_ =
true;
376 }
else if (cachedUsbConnected_) {
380 cachedBatteryPresent_ = !vsysMinRegulation;
383 cachedBatteryPresent_ = !vsysMinRegulation &&
393 bool statusChanged = (cachedChargeStatus_ != prevChargeStatus_) ||
394 (cachedUsbConnected_ != prevUsbConnected_) ||
395 (cachedBatteryPresent_ != prevBatteryPresent_);
398 const char* chrgText[] = {
"Not charging",
"Pre-charge",
"Fast charging",
"Charge done"};
399 uint16_t chargeMa = (ichgr & 0x7F) * 50;
400 if (!cachedBatteryPresent_ && cachedUsbConnected_) {
401 LOG_I(
TAG,
"USB connected, no battery (VBAT=%dmV, ICHG=%dmA)", vbat, chargeMa);
403 LOG_I(
TAG,
"Status: %s, USB=%d, VBAT=%dmV, ICHG=%dmA",
404 chrgText[
static_cast<int>(cachedChargeStatus_)],
405 cachedUsbConnected_, vbat, chargeMa);
408 prevChargeStatus_ = cachedChargeStatus_;
409 prevUsbConnected_ = cachedUsbConnected_;
410 prevBatteryPresent_ = cachedBatteryPresent_;
416 if (reg0c == 0x80 && chrgStat == 3 && cachedBatteryPresent_) {
418 }
else if (reg0c == 0x80) {
420 LOG_D(
TAG,
"WDT expired (post-sleep), kicking");
435bool BQ25895Power::setChargeCurrentMa(uint16_t currentMa) {
449 LOG_I(
TAG,
"Charge current set to %dmA (code=%d)", currentChargeMa_, code);
462 vTaskDelay(pdMS_TO_TICKS(10));
471 uint8_t code = batv & 0x7F;
472 cachedBatteryMv_ = 2304 + (code * 20);
473 return cachedBatteryMv_;
482 if (mv == 0)
return 0;
498 return cachedUsbConnected_;
514 return cachedChargeStatus_;
538 return cachedBatteryPresent_;
552 LOG_I(
TAG,
"Charging %s", enabled ?
"enabled" :
"disabled");
569 LOG_E(
TAG,
"Failed to set BATFET_DIS");
573 LOG_I(
TAG,
"Entered shipping mode");
579void BQ25895Power::kickWatchdog() {
593 uint32_t nowMs = (uint32_t)(esp_timer_get_time() / 1000);
594 if ((nowMs - lastWdtKickMs_) >= 30000) {
595 lastWdtKickMs_ = nowMs;
601 if (!powerButtonHeld_) {
602 powerButtonHeld_ =
true;
603 powerButtonHoldStartMs_ = nowMs;
605 powerButtonHeld_ =
false;
606 LOG_W(
TAG,
"Power button long-press, entering ship mode");
610 powerButtonHeld_ =
false;
CDC Log: logging over TinyUSB CDC and UART.
#define LOG_W(tag, fmt,...)
#define LOG_D(tag, fmt,...)
#define LOG_I(tag, fmt,...)
#define LOG_E(tag, fmt,...)
PowerSource getPowerSource() const override
Returns current active power source.
ChargeStatus getChargeStatus() const override
Returns cached charge-state machine value.
void stop() override
Stops power manager service state.
uint8_t getBatteryPercent() const override
Estimates battery percentage from measured voltage.
bool isBatteryPresent() const override
Returns whether a battery is considered present.
bool init() override
Initializes charger hardware and applies safe boot defaults.
void refresh() override
Forces a synchronous re-read of charger status registers.
bool isBatteryLow() const override
Indicates low-battery threshold state.
void update() override
Periodic power-manager update handling watchdog and IRQ-driven refresh.
uint16_t getBatteryVoltage() const override
Returns measured battery voltage in millivolts.
bool isUsbConnected() const override
Returns cached USB power presence.
const char * getName() const override
bool isBatteryCritical() const override
Indicates critical-battery threshold state.
void setChargingEnabled(bool enabled) override
Enables or effectively disables charging current.
void enterShipMode() override
Requests battery ship mode via BATFET disconnect.
bool start() override
Starts power manager service state.
core::ServiceState getState() const override
virtual esp_err_t readReg(I2cDeviceHandle dev, uint8_t reg, uint8_t *data, size_t len)=0
virtual esp_err_t writeReg(I2cDeviceHandle dev, uint8_t reg, const uint8_t *data, size_t len)=0
static constexpr uint8_t BQ_REG_VBUS
static constexpr uint8_t BQ_REG_ADC_CTRL
static constexpr uint32_t kPowerButtonLongPressMs
Hold duration on the power/flash button that triggers ship mode.
static BQ25895Power g_powerManager
Singleton power manager instance.
II2cBus * getI2cBus0()
Returns singleton instance of I2C bus 0.
static constexpr uint16_t CHARGE_CURRENT_MIN
static constexpr uint8_t BQ_REG_SYSV
static constexpr uint16_t BATTERY_MIN_MV
Battery voltage thresholds for state estimation. LiPo battery characteristics (3.7V nominal,...
static constexpr uint8_t BQ_REG_FAULT
static constexpr uint8_t BQ_REG_SYS_STATUS
static constexpr uint16_t BATTERY_FULL_MV
static constexpr uint16_t BATTERY_USB_PASSTHRU_MIN_MV
static constexpr uint8_t BQ_ICHG_STEP_MA
Charge current and safety threshold constants.
static constexpr uint8_t BQ_REG_TS
static constexpr uint8_t BQ_REG_CHG_CTRL
static constexpr uint8_t BQ_REG_MISC
IPowerManager * getPowerManagerInstance()
Returns the singleton power manager instance.
static constexpr uint16_t BATTERY_MAX_MV
static constexpr uint16_t CHARGE_CURRENT_FAST
static constexpr uint16_t CHARGE_CURRENT_MAX
static constexpr uint8_t BQ_REG_INPUT_CTRL
BQ25895 register map constants.
static constexpr uint8_t BQ_REG_FAST_CHG
static constexpr uint16_t BATTERY_EMPTY_MV
static void charger_isr(void *arg)
GPIO interrupt handler for the charger IRQ pin.
static volatile bool charger_irq_pending
Charger IRQ flag set by ISR and consumed in update().
static constexpr uint16_t CHARGE_CURRENT_SLOW
static constexpr uint8_t BQ_REG_TIMER
static constexpr uint16_t BQ_SYS_MIN_MV
static constexpr uint8_t BQ_REG_ICHG
static constexpr uint8_t BQ_REG_VINDPM
static constexpr uint8_t BQ_REG_VENDOR
static constexpr uint16_t BATTERY_USB_PASSTHRU_MAX_MV
static constexpr uint8_t BQ_REG_BATV