diff --git a/components/ip5306/README.md b/components/ip5306/README.md index 2c71790..7c8e0f9 100644 --- a/components/ip5306/README.md +++ b/components/ip5306/README.md @@ -25,4 +25,14 @@ ip5306: on_release: then: - lambda: ESP_LOGD("TEST", "still charging"); + charger_active: # binary_sensor + id: charger_active + power_boost_on: false # default: false + power_boost_set: false # default: false + power_vin: false # default: false + power_btn: false # default: false + power_boost_keep_on: false # default: false + auto_boot_on_load: false # default: false + enable_power_btn: false # default: false + low_power_shutdown_time: false # default: 64 ``` diff --git a/components/ip5306/__init__.py b/components/ip5306/__init__.py index 91212d4..5b03fd7 100644 --- a/components/ip5306/__init__.py +++ b/components/ip5306/__init__.py @@ -11,7 +11,16 @@ IP5306 = ip5306_ns.class_('IP5306', i2c.I2CDevice, cg.Component) CONF_CHARGER_CONNECTED = "charger_connected" +CONF_CHARGER_ACTIVE = "charger_active" CONF_CHARGE_FULL = "charge_full" +CONF_POWER_BOOST_ON = "power_boost_on" +CONF_POWER_BOOST_SET = "power_boost_set" +CONF_POWER_VIN = "power_vin" +CONF_POWER_BTN = "power_btn" +CONF_POWER_BOOST_KEEP_ON = "power_boost_keep_on" +CONF_AUTO_BOOT_ON_LOAD = "auto_boot_on_load" +CONF_ENABLE_POWER_BTN = "enable_power_btn" +CONF_LOW_POWER_SHUTDOWN_TIME = "low_power_shutdown_time" CONFIG_SCHEMA = cv.COMPONENT_SCHEMA.extend( { @@ -23,9 +32,35 @@ ), cv.Optional(CONF_CHARGER_CONNECTED): binary_sensor.binary_sensor_schema(), cv.Optional(CONF_CHARGE_FULL): binary_sensor.binary_sensor_schema(), + cv.Optional(CONF_CHARGER_ACTIVE): binary_sensor.binary_sensor_schema(), + cv.Optional(CONF_POWER_BOOST_ON, default=True): cv.boolean, + cv.Optional(CONF_POWER_BOOST_SET, default=True): cv.boolean, + cv.Optional(CONF_POWER_VIN, default=True): cv.boolean, + cv.Optional(CONF_POWER_BTN, default=True): cv.boolean, + cv.Optional(CONF_POWER_BOOST_KEEP_ON, default=True): cv.boolean, + cv.Optional(CONF_AUTO_BOOT_ON_LOAD, default=False): cv.boolean, + cv.Optional(CONF_ENABLE_POWER_BTN, default=True): cv.boolean, + cv.Optional(CONF_LOW_POWER_SHUTDOWN_TIME, default=64): cv.uint8_t, } ).extend(i2c.i2c_device_schema(0x75)) +def keys_to_code(config, var, types): + for key in types: + if key in config: + conf = config[key] + cg.add(getattr(var, f"set_{key}")(conf)) + +IP5306_KEYS = { + CONF_POWER_BOOST_ON, + CONF_POWER_BOOST_SET, + CONF_POWER_VIN, + CONF_POWER_BTN, + CONF_POWER_BOOST_KEEP_ON, + CONF_AUTO_BOOT_ON_LOAD, + CONF_ENABLE_POWER_BTN, + CONF_LOW_POWER_SHUTDOWN_TIME +} + async def to_code(config): var = cg.new_Pvariable(config[CONF_ID]) await cg.register_component(var, config) @@ -39,7 +74,12 @@ async def to_code(config): sens = await binary_sensor.new_binary_sensor(config[CONF_CHARGER_CONNECTED]) cg.add(var.set_charger_connected(sens)) + if CONF_CHARGER_ACTIVE in config: + sens = await binary_sensor.new_binary_sensor(config[CONF_CHARGER_ACTIVE]) + cg.add(var.set_charger_active(sens)) + if CONF_CHARGE_FULL in config: sens = await binary_sensor.new_binary_sensor(config[CONF_CHARGE_FULL]) cg.add(var.set_charge_full(sens)) + keys_to_code(config, var, IP5306_KEYS) diff --git a/components/ip5306/ip5306.cpp b/components/ip5306/ip5306.cpp index 23e9986..4843333 100644 --- a/components/ip5306/ip5306.cpp +++ b/components/ip5306/ip5306.cpp @@ -8,12 +8,43 @@ namespace ip5306 { static const char *const TAG = "ip5306"; static const uint8_t IP5306_DEFAULT_SETUP = 0x37; // set power boost keep on by default +static const uint8_t IP5306_REG_CHARGE_OUT_BIT = 0x10; // charge out bit + +static const uint8_t IP5306_REG_CHG_DIG = 0x24; // charge current +static const uint8_t IP5306_REG_CHG_CTL0 = 0x20; // charge voltage +static const uint8_t IP5306_REG_CHG_CTL1 = 0x21; // charge control +static const uint8_t IP5306_REG_CHG_CTL2 = 0x22; // charge control +static const uint8_t IP5306_REG_CHG_CTL3 = 0x23; // charge control static const uint8_t IP5306_REG_SYS_CTL0 = 0x00; // initialize +static const uint8_t IP5306_REG_SYS_CTL1 = 0x01; // sys control 1 +static const uint8_t IP5306_REG_SYS_CTL2 = 0x02; // sys control 2 static const uint8_t IP5306_REG_READ0 = 0x70; // charge en static const uint8_t IP5306_REG_READ1 = 0x71; // charge full static const uint8_t IP5306_REG_LEVEL = 0x78; // bat level +static const uint8_t CURRENT_400MA = 0x01 << 2; +static const uint8_t BAT_4_2V = 0x00; + +//- REG_CTL0 +static const uint8_t BOOST_ENABLE_BIT = 0x20; +static const uint8_t CHARGE_OUT_BIT = 0x10; +static const uint8_t BOOT_ON_LOAD_BIT = 0x04; +static const uint8_t BOOST_OUT_BIT = 0x02; +static const uint8_t BOOST_BUTTON_EN_BIT = 0x01; + +//- REG_CTL1 +static const uint8_t BOOST_SET_BIT = 0x80; +static const uint8_t WLED_SET_BIT = 0x40; +static const uint8_t SHORT_BOOST_BIT = 0x20; +static const uint8_t VIN_ENABLE_BIT = 0x04; + +//- REG_CTL2 +static const uint8_t SHUTDOWNTIME_MASK = 0x0c; +static const uint8_t SHUTDOWNTIME_64S = 0x0c; +static const uint8_t SHUTDOWNTIME_32S = 0x04; +static const uint8_t SHUTDOWNTIME_16S = 0x08; +static const uint8_t SHUTDOWNTIME_8S = 0x00; float IP5306::get_setup_priority() const { return setup_priority::IO; } @@ -22,36 +53,269 @@ void IP5306::setup() { if (this->write_register(IP5306_REG_SYS_CTL0, &IP5306_DEFAULT_SETUP, 1) != i2c::ERROR_OK) { ESP_LOGE(TAG, "setup failed"); this->mark_failed(); + return; + } + ESP_LOGI(TAG, "ip5306 initial setup done"); + + if (!setVinMaxCurrent(CURRENT_400MA)) { + ESP_LOGE(TAG, "setVinMaxCurrent failed"); + this->mark_failed(); + return; + } + ESP_LOGI(TAG, "ip5306 setVinMaxCurrent done"); + if (!setChargeVolt(BAT_4_2V)) { + ESP_LOGE(TAG, "setChargeVolt failed"); + this->mark_failed(); + return; + } + ESP_LOGI(TAG, "ip5306 setChargeVolt done"); + + if (!setPowerBoostOnOff(powerBoostOn_)) { + ESP_LOGE(TAG, "setPowerBoostOnOff failed"); + this->mark_failed(); + return; + } + ESP_LOGI(TAG, "ip5306 setPowerBoostOnOff done"); + + if (!setPowerBoostSet(powerBoostSet_)) { + ESP_LOGE(TAG, "setPowerBoostSet failed"); + this->mark_failed(); + return; + } + ESP_LOGI(TAG, "ip5306 setPowerBoostSet done"); + + if (!setPowerVin(powerVin_)) { + ESP_LOGE(TAG, "setPowerVin failed"); + this->mark_failed(); + return; + } + ESP_LOGI(TAG, "ip5306 setPowerVin done"); + + if (!enablePowerBtn(enablePowerBtn_)) { + ESP_LOGE(TAG, "enablePowerBtn failed"); + this->mark_failed(); + return; + } + ESP_LOGI(TAG, "ip5306 enablePowerBtn done"); + + if (!setPowerBoostKeepOn(powerBoostKeepOn_)) { + ESP_LOGE(TAG, "setPowerBoostKeepOn failed"); + this->mark_failed(); + return; } + ESP_LOGI(TAG, "ip5306 setPowerBoostKeepOn done"); + + if (!setAutoBootOnLoad(autoBootOnLoad_)) { + ESP_LOGE(TAG, "setAutoBootOnLoad failed"); + this->mark_failed(); + return; + } + ESP_LOGI(TAG, "ip5306 setAutoBootOnLoad done"); + + if (!setLowPowerShutdownTime(lowPowerShutdownTime_)) { + ESP_LOGE(TAG, "setLowPowerShutdownTime failed"); + this->mark_failed(); + return; + } + completeChargingSetup(); } -void IP5306::loop() { - uint8_t data[2]; - if (this->battery_level_ != nullptr) { - if (this->read_register(IP5306_REG_LEVEL, data, 1) != i2c::ERROR_OK) { - ESP_LOGE(TAG, "unable to read level"); - this->mark_failed(); - return; +void IP5306::completeChargingSetup() { + uint8_t data[1]; + uint8_t value; + // End charge current 200ma + if (this->read_register(IP5306_REG_CHG_CTL1, data, 1) == i2c::ERROR_OK) { + value = (data[0] & 0x3f) | 0x00; + this->write_register(IP5306_REG_CHG_CTL1, &value, 1); + } else { + ESP_LOGE(TAG, "completeChargingSetup read 1 failed"); + } + + // Add volt 28mv + if (this->read_register(IP5306_REG_CHG_CTL2, data, 1) == i2c::ERROR_OK) { + value = (data[0] & 0xfc) | 0x02; + this->write_register(IP5306_REG_CHG_CTL2, &value, 1); + } else { + ESP_LOGE(TAG, "completeChargingSetup read 2 failed"); + } + + // Vin charge CC + if (this->read_register(IP5306_REG_CHG_CTL3, data, 1) == i2c::ERROR_OK) { + value = (data[0] & 0xdf) | 0x20; + this->write_register(IP5306_REG_CHG_CTL3, &value, 1); + } else { + ESP_LOGE(TAG, "completeChargingSetup read 3 failed"); + } +} + +bool IP5306::setVinMaxCurrent(uint8_t cur) { + uint8_t data[1]; + if (this->read_register(IP5306_REG_CHG_DIG, data, 1) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "setVinMaxCurrent read failed"); + return false; + } + uint8_t value = (data[0] & 0xE0) | cur; + return this->write_register(IP5306_REG_CHG_DIG, &value, 1) == i2c::ERROR_OK; +} + +bool IP5306::setChargeVolt(uint8_t volt) { + uint8_t data[1]; + if (this->read_register(IP5306_REG_CHG_CTL0, data, 1) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "setChargeVolt read failed"); + return false; } - float value = 0; - switch (data[0] & 0xF0) { - case 0xE0: value = 25; break; - case 0xC0: value = 50; break; - case 0x80: value = 75; break; - case 0x00: value = 100; break; + uint8_t value = (data[0] & 0xFC) | volt; + return this->write_register(IP5306_REG_CHG_CTL0, &value, 1) == i2c::ERROR_OK; +} + +bool IP5306::setPowerBoostOnOff(bool enabled) { + uint8_t data[1]; + if (this->read_register(IP5306_REG_SYS_CTL1, data, 1) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "setPowerBoostOnOff read failed"); + return false; + } + uint8_t value = enabled ? (data[0] | BOOST_SET_BIT) : (data[0] & (~BOOST_SET_BIT)); + return this->write_register(IP5306_REG_SYS_CTL1, &value, 1) == i2c::ERROR_OK; +} + +bool IP5306::setPowerBoostSet(bool enabled) { + uint8_t data[1]; + if (this->read_register(IP5306_REG_SYS_CTL1, data, 1) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "setPowerBoostSet read failed"); + return false; } - if (!this->battery_level_->has_state() || (this->battery_level_->state != value)) - this->battery_level_->publish_state(value); + uint8_t value = enabled ? (data[0] | SHORT_BOOST_BIT) : (data[0] & (~SHORT_BOOST_BIT)); + return this->write_register(IP5306_REG_SYS_CTL1, &value, 1) == i2c::ERROR_OK; +} + +bool IP5306::setPowerVin(bool enabled) { + uint8_t data[1]; + if (this->read_register(IP5306_REG_SYS_CTL1, data, 1) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "setPowerVin read failed"); + return false; + } + uint8_t value = enabled ? (data[0] | VIN_ENABLE_BIT) : (data[0] & (~VIN_ENABLE_BIT)); + return this->write_register(IP5306_REG_SYS_CTL1, &value, 1) == i2c::ERROR_OK; +} + +bool IP5306::enablePowerBtn(bool enabled) { + uint8_t data[1]; + if (this->read_register(IP5306_REG_SYS_CTL0, data, 1) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "enablePowerBtn read failed"); + return false; } + uint8_t value = enabled ? (data[0] | BOOST_BUTTON_EN_BIT) : (data[0] & (~BOOST_BUTTON_EN_BIT)); + return this->write_register(IP5306_REG_SYS_CTL0, &value, 1) == i2c::ERROR_OK; +} + +bool IP5306::setPowerBoostKeepOn(bool enabled) { + uint8_t data[1]; + if (this->read_register(IP5306_REG_SYS_CTL0, data, 1) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "setPowerBoostKeepOn read failed"); + return false; + } + uint8_t value = enabled ? (data[0] | BOOST_OUT_BIT) : (data[0] & (~BOOST_OUT_BIT)); + return this->write_register(IP5306_REG_SYS_CTL0, &value, 1) == i2c::ERROR_OK; +} + +/* + default: true + true: If enough load is connected, the power will turn on. +*/ +bool IP5306::setAutoBootOnLoad(bool enabled) { + uint8_t data[1]; + if (this->read_register(IP5306_REG_SYS_CTL0, data, 1) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "setAutoBootOnLoad read failed"); + return false; + } + uint8_t value = enabled ? (data[0] | BOOT_ON_LOAD_BIT) : (data[0] & (~BOOT_ON_LOAD_BIT)); + return this->write_register(IP5306_REG_SYS_CTL0, &value, 1) == i2c::ERROR_OK; +} + +// if charge full,try set charge enable->disable->enable,can be recharged +// https://github.com/m5stack/M5Stack/blob/1e81176b2dd9cb7839f53f5a71a8143b04794de7/src/utility/Power.cpp#L270 +void IP5306::setCharge(bool enabled) { + if (this->charger_active_ == nullptr) return; + if (this->charger_active_->has_state() && this->charger_active_->state == enabled) return; + + if (this->write_register(IP5306_REG_SYS_CTL0, &IP5306_REG_CHARGE_OUT_BIT, enabled ? 1 : 0) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "set charge failed %d", enabled); + this->mark_failed(); + } + ESP_LOGE(TAG, "set charge worked, %d", enabled); + if (!this->charger_active_->has_state() || (this->charger_active_->state != enabled)) + this->charger_active_->publish_state(enabled); +} + +bool IP5306::setLowPowerShutdownTime(int time) { + uint8_t data[1]; + if (this->read_register(IP5306_REG_SYS_CTL2, data, 1) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "setLowPowerShutdownTime read failed"); + return false; + } + uint8_t value; + switch (time) { + case 8: + value = ((data[0] & (~SHUTDOWNTIME_MASK)) | SHUTDOWNTIME_8S); + break; + case 16: + value = ((data[0] & (~SHUTDOWNTIME_MASK)) | SHUTDOWNTIME_16S); + break; + case 32: + value = ((data[0] & (~SHUTDOWNTIME_MASK)) | SHUTDOWNTIME_32S); + break; + case 64: + value = ((data[0] & (~SHUTDOWNTIME_MASK)) | SHUTDOWNTIME_64S); + break; + default: + ESP_LOGE(TAG, "setLowPowerShutdownTime invalid time"); + return false; + } + return this->write_register(IP5306_REG_SYS_CTL2, &value, 1) == i2c::ERROR_OK; +} + +bool IP5306::readBatteryLevel(uint8_t* data) { + if (this->battery_level_ == nullptr) return false; + if (this->read_register(IP5306_REG_LEVEL, data, 1) != i2c::ERROR_OK) { + ESP_LOGE(TAG, "unable to read level"); + this->mark_failed(); + return false; + } + float value = 0; + switch (data[0] & 0xF0) { + case 0xE0: value = 25; break; + case 0xC0: value = 50; break; + case 0x80: value = 75; break; + case 0x00: value = 100; break; + } + if (!this->battery_level_->has_state() || (this->battery_level_->state != value)) + this->battery_level_->publish_state(value); + return true; +} + +bool IP5306::readBatteryStatus(uint8_t* data) { if (this->read_register(IP5306_REG_READ0, data, 2) != i2c::ERROR_OK) { ESP_LOGE(TAG, "unable to read status"); this->mark_failed(); - return; + return false; } if (this->charger_connected_ != nullptr) this->charger_connected_->publish_state(data[0] & 0x08); if (this->charge_full_ != nullptr) this->charge_full_->publish_state(data[1] & 0x08); + + if (this->charger_connected_->state && !this->charge_full_->state) { + this->setCharge(true); + } else { + this->setCharge(false); + } + return true; +} + +void IP5306::loop() { + uint8_t data[2]; + if (!readBatteryLevel(data)) return; + if (!readBatteryStatus(data)) return; } } // namespace ip5306 diff --git a/components/ip5306/ip5306.h b/components/ip5306/ip5306.h index 7557b9b..d3c3ab1 100644 --- a/components/ip5306/ip5306.h +++ b/components/ip5306/ip5306.h @@ -17,12 +17,47 @@ class IP5306 : public i2c::I2CDevice, public Component { void set_battery_level(sensor::Sensor *sensor) { this->battery_level_ = sensor; } void set_charger_connected(binary_sensor::BinarySensor *sensor) { this->charger_connected_ = sensor; } + void set_charger_active(binary_sensor::BinarySensor *sensor) { this->charger_active_ = sensor; } void set_charge_full(binary_sensor::BinarySensor *sensor) { this->charge_full_ = sensor; } + void set_power_boost_on(bool enabled) { this->powerBoostOn_ = enabled; } + void set_power_boost_set(bool enabled) { this->powerBoostSet_ = enabled; } + void set_power_vin(bool enabled) { this->powerVin_ = enabled; } + void set_power_btn(bool enabled) { this->powerBtn_ = enabled; } + void set_power_boost_keep_on(bool enabled) { this->powerBoostKeepOn_ = enabled; } + void set_auto_boot_on_load(bool enabled) { this->autoBootOnLoad_ = enabled; } + void set_enable_power_btn(bool enabled) { this->enablePowerBtn_ = enabled; } + void set_low_power_shutdown_time(int time) { this->lowPowerShutdownTime_ = time; } + protected: sensor::Sensor *battery_level_{nullptr}; binary_sensor::BinarySensor *charger_connected_{nullptr}; binary_sensor::BinarySensor *charge_full_{nullptr}; + binary_sensor::BinarySensor *charger_active_{nullptr}; + + void setCharge(bool enabled); + bool readBatteryLevel(uint8_t* data); + bool readBatteryStatus(uint8_t* data); + bool setVinMaxCurrent(uint8_t cur); + bool setChargeVolt(uint8_t volt); + bool setPowerBoostOnOff(bool enabled); + bool setPowerBoostSet(bool enabled); + bool setPowerVin(bool enabled); + bool enablePowerBtn(bool enabled); + bool setPowerBoostKeepOn(bool enabled); + bool setAutoBootOnLoad(bool enabled); + bool setLowPowerShutdownTime(int time); + void completeChargingSetup(); + + private: + bool powerBoostOn_{false}; + bool powerBoostSet_{false}; + bool powerVin_{false}; + bool powerBtn_{false}; + bool powerBoostKeepOn_{false}; + bool autoBootOnLoad_{false}; + bool enablePowerBtn_{false}; + int lowPowerShutdownTime_{0}; }; } // namespace ip5306