ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
sy6970.cpp
Go to the documentation of this file.
1#include "sy6970.h"
2#include "esphome/core/hal.h"
3#include "esphome/core/log.h"
4
5namespace esphome::sy6970 {
6
7ESPHOME_LOG_TAG(TAG, "sy6970");
8
9static const LogString *i2c_watchdog_timeout_to_string(I2CWatchdogTimeout timeout) {
10 switch (timeout) {
12 return LOG_STR("Disabled");
14 return LOG_STR("40s");
16 return LOG_STR("80s");
18 return LOG_STR("160s");
19 default:
20 return LOG_STR("Unknown");
21 }
22}
23
24static constexpr uint32_t I2C_WATCHDOG_KICK_INTERVAL_ID = 0;
25// Well under the 40 s default, so a failed kick or a timeout the chip did not accept still leaves margin
26static constexpr uint32_t I2C_WATCHDOG_KICK_INTERVAL_MS = 10000;
27
29 // Read all registers from 0x00 to 0x14 in one transaction (21 bytes)
30 // This includes unused registers 0x0F, 0x10 for performance
31 if (!this->read_bytes(SY6970_REG_INPUT_CURRENT_LIMIT, this->data_.registers, 21)) {
32 ESP_LOGW(TAG, "Failed to read registers 0x00-0x14");
33 return false;
34 }
35
36 return true;
37}
38
39bool SY6970Component::write_register_(uint8_t reg, uint8_t value) {
40 if (!this->write_byte(reg, value)) {
41 ESP_LOGW(TAG, "Failed to write register 0x%02X", reg);
42 return false;
43 }
44 return true;
45}
46
47bool SY6970Component::update_register_(uint8_t reg, uint8_t mask, uint8_t value) {
48 uint8_t reg_value;
49 if (!this->read_byte(reg, &reg_value)) {
50 ESP_LOGW(TAG, "Failed to read register 0x%02X for update", reg);
51 return false;
52 }
53 reg_value = (reg_value & ~mask) | (value & mask);
54 return this->write_register_(reg, reg_value);
55}
56
58 ESP_LOGV(TAG, "Setting up SY6970...");
59
60 // Try to read chip ID
61 uint8_t reg_value;
62 if (!this->read_byte(SY6970_REG_DEVICE_ID, &reg_value)) {
63 ESP_LOGE(TAG, "Failed to communicate with SY6970");
64 this->mark_failed();
65 return;
66 }
67
68 uint8_t chip_id = reg_value & 0x03;
69 if (chip_id != 0x00) {
70 ESP_LOGW(TAG, "Unexpected chip ID: 0x%02X (expected 0x00)", chip_id);
71 }
72
73 // Service the watchdog first, so the settings below are not reverted while they are written
74 ESP_LOGV(TAG, "Setting I2C watchdog timeout to %s",
75 LOG_STR_ARG(i2c_watchdog_timeout_to_string(this->i2c_watchdog_timeout_)));
77
78 // Apply configuration options (all have defaults now)
79 ESP_LOGV(TAG, "Setting LED enabled to %s", ONOFF(this->led_enabled_));
80 this->set_led_enabled(this->led_enabled_);
81
82 ESP_LOGV(TAG, "Setting input current limit to %u mA", this->input_current_limit_);
84
85 ESP_LOGV(TAG, "Setting charge voltage to %u mV", this->charge_voltage_);
87
88 ESP_LOGV(TAG, "Setting charge current to %u mA", this->charge_current_);
90
91 ESP_LOGV(TAG, "Setting precharge current to %u mA", this->precharge_current_);
93
94 ESP_LOGV(TAG, "Setting charge enabled to %s", ONOFF(this->charge_enabled_));
96
97 ESP_LOGV(TAG, "Setting ADC measurements to %s", ONOFF(this->enable_adc_));
99
100 ESP_LOGV(TAG, "SY6970 initialized successfully");
101}
102
104 ESP_LOGCONFIG(TAG,
105 "SY6970:\n"
106 " LED Enabled: %s\n"
107 " Input Current Limit: %u mA\n"
108 " Charge Voltage: %u mV\n"
109 " Charge Current: %u mA\n"
110 " Precharge Current: %u mA\n"
111 " Charge Enabled: %s\n"
112 " ADC Enabled: %s\n"
113 " I2C Watchdog Timeout: %s",
114 ONOFF(this->led_enabled_), this->input_current_limit_, this->charge_voltage_, this->charge_current_,
115 this->precharge_current_, ONOFF(this->charge_enabled_), ONOFF(this->enable_adc_),
116 LOG_STR_ARG(i2c_watchdog_timeout_to_string(this->i2c_watchdog_timeout_)));
117 LOG_I2C_DEVICE(this);
118 LOG_UPDATE_INTERVAL(this);
119 if (this->is_failed()) {
120 ESP_LOGE(TAG, "Communication with SY6970 failed!");
121 }
122}
123
125 if (this->is_failed()) {
126 return;
127 }
128
129 // Read all registers in one transaction
130 if (!this->read_all_registers_()) {
131 ESP_LOGW(TAG, "Failed to read registers during update");
132 this->status_set_warning();
133 return;
134 }
135
136 this->status_clear_warning();
137
138 // Notify all listeners with the new data
139 for (auto *listener : this->listeners_) {
140 listener->on_data(this->data_);
141 }
142}
143
145 if (this->is_failed())
146 return;
147
148 if (milliamps < INPUT_CURRENT_MIN) {
149 milliamps = INPUT_CURRENT_MIN;
150 }
151
152 uint8_t val = (milliamps - INPUT_CURRENT_MIN) / INPUT_CURRENT_STEP;
153 if (val > 0x3F) {
154 val = 0x3F;
155 }
156
157 this->update_register_(SY6970_REG_INPUT_CURRENT_LIMIT, 0x3F, val);
158}
159
161 if (this->is_failed())
162 return;
163
164 if (millivolts < CHG_VOLTAGE_BASE) {
165 millivolts = CHG_VOLTAGE_BASE;
166 }
167
168 uint8_t val = (millivolts - CHG_VOLTAGE_BASE) / CHG_VOLTAGE_STEP;
169 if (val > 0x3F) {
170 val = 0x3F;
171 }
172
173 this->update_register_(SY6970_REG_CHARGE_VOLTAGE, 0xFC, val << 2);
174}
175
177 if (this->is_failed())
178 return;
179
180 if (milliamps < PRE_CHG_BASE_MA) {
181 milliamps = PRE_CHG_BASE_MA;
182 }
183
184 uint8_t val = (milliamps - PRE_CHG_BASE_MA) / PRE_CHG_STEP_MA;
185 if (val > 0x0F) {
186 val = 0x0F;
187 }
188
189 this->update_register_(SY6970_REG_PRECHARGE_CURRENT, 0xF0, val << 4);
190}
191
192void SY6970Component::set_charge_current(uint16_t milliamps) {
193 if (this->is_failed())
194 return;
195
196 uint8_t val = milliamps / 64;
197 if (val > 0x7F) {
198 val = 0x7F;
199 }
200
201 this->update_register_(SY6970_REG_CHARGE_CURRENT, 0x7F, val);
202}
203
205 if (this->is_failed())
206 return;
207
208 this->update_register_(SY6970_REG_SYS_CONTROL, 0x10, enabled ? 0x10 : 0x00);
209}
210
212 if (this->is_failed())
213 return;
214
215 // Bit 6: 0 = LED enabled, 1 = LED disabled
216 this->update_register_(SY6970_REG_TIMER_CONTROL, 0x40, enabled ? 0x00 : 0x40);
217}
218
220 if (this->is_failed())
221 return;
222
223 // Set bits to enable ADC conversion
224 this->update_register_(SY6970_REG_ADC_CONTROL, 0xC0, enabled ? 0xC0 : 0x00);
225}
226
228 if (this->is_failed())
229 return;
230
231 if (timeout != I2C_WATCHDOG_DISABLED) {
232 // Changing the timeout does not restart the chip's timer, which may be close to expiring
233 this->reset_i2c_watchdog();
234 }
235
236 // REG07 bits 5:4 (WATCHDOG[1:0])
237 bool written = this->update_register_(SY6970_REG_TIMER_CONTROL, 0x30, static_cast<uint8_t>(timeout) << 4);
238 if (!written) {
239 ESP_LOGW(TAG, "Failed to set I2C watchdog timeout to %s", LOG_STR_ARG(i2c_watchdog_timeout_to_string(timeout)));
240 }
241
242 if (timeout == I2C_WATCHDOG_DISABLED && written) {
243 this->i2c_watchdog_timeout_ = timeout;
244 this->cancel_interval(I2C_WATCHDOG_KICK_INTERVAL_ID);
245 return;
246 }
247 if (timeout != I2C_WATCHDOG_DISABLED) {
248 this->i2c_watchdog_timeout_ = timeout;
249 }
250 // Keeps kicking after a failed write too: the chip may still be on its 40 s default
251 this->set_interval(I2C_WATCHDOG_KICK_INTERVAL_ID, I2C_WATCHDOG_KICK_INTERVAL_MS,
252 [this]() { this->reset_i2c_watchdog(); });
253}
254
256 if (this->is_failed())
257 return false;
258
259 // WD_RST, self-clearing
260 if (!this->update_register_(SY6970_REG_SYS_CONTROL, 0x40, 0x40)) {
261 ESP_LOGW(TAG, "I2C watchdog reset failed, settings may revert");
262 return false;
263 }
264 return true;
265}
266
267} // namespace esphome::sy6970
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:274
bool cancel_interval(const char *name)
Cancel an interval function.
Definition component.cpp:92
void set_interval(const char *name, uint32_t interval, std::function< void()> &&f)
Set an interval function with a const char* name.
Definition component.cpp:88
void status_clear_warning()
Definition component.h:291
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:265
bool read_byte(uint8_t a_register, uint8_t *data)
Definition i2c.h:240
I2CRegister reg(uint8_t a_register)
calls the I2CRegister constructor
Definition i2c.h:152
bool read_bytes(uint8_t a_register, uint8_t *data, uint8_t len)
Compat APIs All methods below have been added for compatibility reasons.
Definition i2c.h:217
void set_charge_target_voltage(uint16_t millivolts)
Definition sy6970.cpp:160
void set_enable_adc_measure(bool enabled=true)
Definition sy6970.cpp:219
void set_input_current_limit(uint16_t milliamps)
Definition sy6970.cpp:144
void set_charge_enabled(bool enabled)
Definition sy6970.cpp:204
I2CWatchdogTimeout i2c_watchdog_timeout_
Definition sy6970.h:134
void set_precharge_current(uint16_t milliamps)
Definition sy6970.cpp:176
bool write_register_(uint8_t reg, uint8_t value)
Definition sy6970.cpp:39
void set_led_enabled(bool enabled)
Definition sy6970.cpp:211
bool reset_i2c_watchdog()
Kick the I2C watchdog (REG03 bit 6); runs on its own interval while enabled. Returns false when not w...
Definition sy6970.cpp:255
void set_charge_current(uint16_t milliamps)
Definition sy6970.cpp:192
void set_i2c_watchdog_timeout(I2CWatchdogTimeout timeout)
Definition sy6970.cpp:227
std::vector< SY6970Listener * > listeners_
Definition sy6970.h:124
bool update_register_(uint8_t reg, uint8_t mask, uint8_t value)
Definition sy6970.cpp:47
mopeka_std_values val[3]
@ I2C_WATCHDOG_160S
Definition sy6970.h:71
@ I2C_WATCHDOG_DISABLED
Definition sy6970.h:68
ESPHOME_LOG_TAG(TAG, "sy6970")
int written
Definition helpers.h:1186
static void uint32_t