mirror of
https://github.com/esphome/esphome.git
synced 2026-02-18 15:35:59 -07:00
202 lines
5.6 KiB
C++
202 lines
5.6 KiB
C++
#include "sy6970.h"
|
|
#include "esphome/core/hal.h"
|
|
#include "esphome/core/log.h"
|
|
|
|
namespace esphome::sy6970 {
|
|
|
|
static const char *const TAG = "sy6970";
|
|
|
|
bool SY6970Component::read_all_registers_() {
|
|
// Read all registers from 0x00 to 0x14 in one transaction (21 bytes)
|
|
// This includes unused registers 0x0F, 0x10 for performance
|
|
if (!this->read_bytes(SY6970_REG_INPUT_CURRENT_LIMIT, this->data_.registers, 21)) {
|
|
ESP_LOGW(TAG, "Failed to read registers 0x00-0x14");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool SY6970Component::write_register_(uint8_t reg, uint8_t value) {
|
|
if (!this->write_byte(reg, value)) {
|
|
ESP_LOGW(TAG, "Failed to write register 0x%02X", reg);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool SY6970Component::update_register_(uint8_t reg, uint8_t mask, uint8_t value) {
|
|
uint8_t reg_value;
|
|
if (!this->read_byte(reg, ®_value)) {
|
|
ESP_LOGW(TAG, "Failed to read register 0x%02X for update", reg);
|
|
return false;
|
|
}
|
|
reg_value = (reg_value & ~mask) | (value & mask);
|
|
return this->write_register_(reg, reg_value);
|
|
}
|
|
|
|
void SY6970Component::setup() {
|
|
ESP_LOGV(TAG, "Setting up SY6970...");
|
|
|
|
// Try to read chip ID
|
|
uint8_t reg_value;
|
|
if (!this->read_byte(SY6970_REG_DEVICE_ID, ®_value)) {
|
|
ESP_LOGE(TAG, "Failed to communicate with SY6970");
|
|
this->mark_failed();
|
|
return;
|
|
}
|
|
|
|
uint8_t chip_id = reg_value & 0x03;
|
|
if (chip_id != 0x00) {
|
|
ESP_LOGW(TAG, "Unexpected chip ID: 0x%02X (expected 0x00)", chip_id);
|
|
}
|
|
|
|
// Apply configuration options (all have defaults now)
|
|
ESP_LOGV(TAG, "Setting LED enabled to %s", ONOFF(this->led_enabled_));
|
|
this->set_led_enabled(this->led_enabled_);
|
|
|
|
ESP_LOGV(TAG, "Setting input current limit to %u mA", this->input_current_limit_);
|
|
this->set_input_current_limit(this->input_current_limit_);
|
|
|
|
ESP_LOGV(TAG, "Setting charge voltage to %u mV", this->charge_voltage_);
|
|
this->set_charge_target_voltage(this->charge_voltage_);
|
|
|
|
ESP_LOGV(TAG, "Setting charge current to %u mA", this->charge_current_);
|
|
this->set_charge_current(this->charge_current_);
|
|
|
|
ESP_LOGV(TAG, "Setting precharge current to %u mA", this->precharge_current_);
|
|
this->set_precharge_current(this->precharge_current_);
|
|
|
|
ESP_LOGV(TAG, "Setting charge enabled to %s", ONOFF(this->charge_enabled_));
|
|
this->set_charge_enabled(this->charge_enabled_);
|
|
|
|
ESP_LOGV(TAG, "Setting ADC measurements to %s", ONOFF(this->enable_adc_));
|
|
this->set_enable_adc_measure(this->enable_adc_);
|
|
|
|
ESP_LOGV(TAG, "SY6970 initialized successfully");
|
|
}
|
|
|
|
void SY6970Component::dump_config() {
|
|
ESP_LOGCONFIG(TAG,
|
|
"SY6970:\n"
|
|
" LED Enabled: %s\n"
|
|
" Input Current Limit: %u mA\n"
|
|
" Charge Voltage: %u mV\n"
|
|
" Charge Current: %u mA\n"
|
|
" Precharge Current: %u mA\n"
|
|
" Charge Enabled: %s\n"
|
|
" ADC Enabled: %s",
|
|
ONOFF(this->led_enabled_), this->input_current_limit_, this->charge_voltage_, this->charge_current_,
|
|
this->precharge_current_, ONOFF(this->charge_enabled_), ONOFF(this->enable_adc_));
|
|
LOG_I2C_DEVICE(this);
|
|
LOG_UPDATE_INTERVAL(this);
|
|
if (this->is_failed()) {
|
|
ESP_LOGE(TAG, "Communication with SY6970 failed!");
|
|
}
|
|
}
|
|
|
|
void SY6970Component::update() {
|
|
if (this->is_failed()) {
|
|
return;
|
|
}
|
|
|
|
// Read all registers in one transaction
|
|
if (!this->read_all_registers_()) {
|
|
ESP_LOGW(TAG, "Failed to read registers during update");
|
|
this->status_set_warning();
|
|
return;
|
|
}
|
|
|
|
this->status_clear_warning();
|
|
|
|
// Notify all listeners with the new data
|
|
for (auto *listener : this->listeners_) {
|
|
listener->on_data(this->data_);
|
|
}
|
|
}
|
|
|
|
void SY6970Component::set_input_current_limit(uint16_t milliamps) {
|
|
if (this->is_failed())
|
|
return;
|
|
|
|
if (milliamps < INPUT_CURRENT_MIN) {
|
|
milliamps = INPUT_CURRENT_MIN;
|
|
}
|
|
|
|
uint8_t val = (milliamps - INPUT_CURRENT_MIN) / INPUT_CURRENT_STEP;
|
|
if (val > 0x3F) {
|
|
val = 0x3F;
|
|
}
|
|
|
|
this->update_register_(SY6970_REG_INPUT_CURRENT_LIMIT, 0x3F, val);
|
|
}
|
|
|
|
void SY6970Component::set_charge_target_voltage(uint16_t millivolts) {
|
|
if (this->is_failed())
|
|
return;
|
|
|
|
if (millivolts < CHG_VOLTAGE_BASE) {
|
|
millivolts = CHG_VOLTAGE_BASE;
|
|
}
|
|
|
|
uint8_t val = (millivolts - CHG_VOLTAGE_BASE) / CHG_VOLTAGE_STEP;
|
|
if (val > 0x3F) {
|
|
val = 0x3F;
|
|
}
|
|
|
|
this->update_register_(SY6970_REG_CHARGE_VOLTAGE, 0xFC, val << 2);
|
|
}
|
|
|
|
void SY6970Component::set_precharge_current(uint16_t milliamps) {
|
|
if (this->is_failed())
|
|
return;
|
|
|
|
if (milliamps < PRE_CHG_BASE_MA) {
|
|
milliamps = PRE_CHG_BASE_MA;
|
|
}
|
|
|
|
uint8_t val = (milliamps - PRE_CHG_BASE_MA) / PRE_CHG_STEP_MA;
|
|
if (val > 0x0F) {
|
|
val = 0x0F;
|
|
}
|
|
|
|
this->update_register_(SY6970_REG_PRECHARGE_CURRENT, 0xF0, val << 4);
|
|
}
|
|
|
|
void SY6970Component::set_charge_current(uint16_t milliamps) {
|
|
if (this->is_failed())
|
|
return;
|
|
|
|
uint8_t val = milliamps / 64;
|
|
if (val > 0x7F) {
|
|
val = 0x7F;
|
|
}
|
|
|
|
this->update_register_(SY6970_REG_CHARGE_CURRENT, 0x7F, val);
|
|
}
|
|
|
|
void SY6970Component::set_charge_enabled(bool enabled) {
|
|
if (this->is_failed())
|
|
return;
|
|
|
|
this->update_register_(SY6970_REG_SYS_CONTROL, 0x10, enabled ? 0x10 : 0x00);
|
|
}
|
|
|
|
void SY6970Component::set_led_enabled(bool enabled) {
|
|
if (this->is_failed())
|
|
return;
|
|
|
|
// Bit 6: 0 = LED enabled, 1 = LED disabled
|
|
this->update_register_(SY6970_REG_TIMER_CONTROL, 0x40, enabled ? 0x00 : 0x40);
|
|
}
|
|
|
|
void SY6970Component::set_enable_adc_measure(bool enabled) {
|
|
if (this->is_failed())
|
|
return;
|
|
|
|
// Set bits to enable ADC conversion
|
|
this->update_register_(SY6970_REG_ADC_CONTROL, 0xC0, enabled ? 0xC0 : 0x00);
|
|
}
|
|
|
|
} // namespace esphome::sy6970
|