ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
tas2780.cpp
Go to the documentation of this file.
1#include "tas2780.h"
2
3#include <cmath>
4
5#include "esphome/core/hal.h"
7#include "esphome/core/log.h"
8
9namespace esphome::tas2780 {
10
11ESPHOME_LOG_TAG(TAG, "tas2780");
12
13static constexpr uint8_t TAS2780_PAGE_SELECT = 0x00; // Page Select
14static constexpr uint8_t TAS2780_PAGE_0 = 0x00; // Page 0
15static constexpr uint8_t TAS2780_PAGE_1 = 0x01; // Page 1
16static constexpr uint8_t TAS2780_PAGE_FD = 0xFD; // Page 0xFD
17
18/* PAGE 0 */
19static constexpr uint8_t TAS2780_SW_RESET = 0x01; // Software Reset
20static constexpr uint8_t TAS2780_SW_RESET_CMD = 0x01; // Trigger software reset
21static constexpr uint8_t TAS2780_MODE_CTRL = 0x02; // Device operational mode
22static constexpr uint8_t TAS2780_MODE_CTRL_MODE_MASK = 0x07;
23static constexpr uint8_t TAS2780_MODE_CTRL_MODE_ACTIVE = 0x00;
24static constexpr uint8_t TAS2780_MODE_CTRL_MODE_ACTIVE_MUTED = 0x01;
25static constexpr uint8_t TAS2780_MODE_CTRL_MODE_SFTW_SHTDWN = 0x02;
26
27static constexpr uint8_t TAS2780_CHNL_0 = 0x03; // Y Bridge and Channel settings
28static constexpr uint8_t TAS2780_CHNL_0_CDS_MODE_SHIFT = 6;
29static constexpr uint8_t TAS2780_CHNL_0_CDS_MODE_MASK = (0x03 << TAS2780_CHNL_0_CDS_MODE_SHIFT);
30static constexpr uint8_t TAS2780_CHNL_0_AMP_LEVEL_SHIFT = 1;
31static constexpr uint8_t TAS2780_CHNL_0_AMP_LEVEL_MASK = (0x1F) << TAS2780_CHNL_0_AMP_LEVEL_SHIFT;
32static constexpr uint8_t TAS2780_AMP_LEVEL_MAX = 0x14; // Codes above 20 are reserved
33
34static constexpr uint8_t TAS2780_DC_BLK0 = 0x04; // SAR Filter and DC Path Blocker
35static constexpr uint8_t TAS2780_DC_BLK0_VBAT1S_MODE_MASK = (1 << 7);
36static constexpr uint8_t TAS2780_DC_BLK1 = 0x05; // Record DC Blocker
37static constexpr uint8_t TAS2780_DC_BLK1_RESET_VAL = 0x41; // Presence check, there is no WHO_AM_I register
38
39static constexpr uint8_t TAS2780_TDM_CFG2 = 0x0A; // TDM Configuration 2
40static constexpr uint8_t TAS2780_TDM_CFG2_RX_SCFG_SHIFT = 4;
41static constexpr uint8_t TAS2780_TDM_CFG2_RX_SCFG_MASK = (3 << TAS2780_TDM_CFG2_RX_SCFG_SHIFT);
42static constexpr uint8_t TAS2780_TDM_CFG2_RX_WLEN_SHIFT = 2;
43static constexpr uint8_t TAS2780_TDM_CFG2_RX_WLEN_MASK = (3 << TAS2780_TDM_CFG2_RX_WLEN_SHIFT);
44static constexpr uint8_t TAS2780_TDM_CFG2_RX_WLEN_32BIT = (3 << TAS2780_TDM_CFG2_RX_WLEN_SHIFT);
45static constexpr uint8_t TAS2780_TDM_CFG2_RX_SLEN_MASK = (3 << 0);
46static constexpr uint8_t TAS2780_TDM_CFG2_RX_SLEN_32BIT = 2;
47
48static constexpr uint8_t TAS2780_TDM_CFG5 = 0x0E; // TDM Configuration 5
49static constexpr uint8_t TAS2780_TDM_CFG5_TX_VSNS_EN_SLOT4 = 0x44; // vsns TX enable, slot 4
50static constexpr uint8_t TAS2780_TDM_CFG6 = 0x0F; // TDM Configuration 6
51static constexpr uint8_t TAS2780_TDM_CFG6_TX_ISNS_EN_SLOT0 = 0x40; // isns TX enable, slot 0
52
53static constexpr uint8_t TAS2780_DVC = 0x1A; // Digital Volume Control
54static constexpr uint8_t TAS2780_DVC_MAX_ATTEN = 200; // 0 dB (0x00) to -100 dB (0xC8) in 0.5 dB steps
55
56/* Interrupts */
57static constexpr uint8_t TAS2780_INT_MASK_ALL = 0xFF; // Mask all interrupts
58static constexpr uint8_t TAS2780_INT_MASK0 = 0x3B; // Interrupt Mask 0
59static constexpr uint8_t TAS2780_INT_MASK1 = 0x3C; // Interrupt Mask 1
60static constexpr uint8_t TAS2780_INT_MASK1_0 = 0x3D; // Interrupt Mask 1_0 (INT_LTCH1_0 group)
61static constexpr uint8_t TAS2780_INT_MASK2 = 0x40; // Interrupt Mask 2
62static constexpr uint8_t TAS2780_INT_MASK3 = 0x41; // Interrupt Mask 3
63static constexpr uint8_t TAS2780_INT_LTCH0 = 0x49; // Latched Interrupt Read-back 0, 1 and 1_0 follow
64static constexpr uint8_t TAS2780_INT_LTCH2 = 0x4F; // Latched Interrupt Read-back 2
65
66static constexpr uint8_t TAS2780_INT_CLK_CFG = 0x5C; // Clock Setting and IRQZ
67static constexpr uint8_t TAS2780_INT_CLK_CFG_CLR_LATCH = (1 << 2); // Clear interrupt latches
68static constexpr uint8_t TAS2780_INT_CLK_CFG_MODE_MASK = 0x03; // Trigger mode field mask
69static constexpr uint8_t TAS2780_INT_CLK_CFG_MODE_LIVE = 0x00; // Trigger on any unmasked live interrupt
70static constexpr uint8_t TAS2780_PVDD_UVLO = 0x71; // UVLO Threshold
71static constexpr uint8_t TAS2780_PVDD_UVLO_2V76 = 0x03; // PVDD UVLO threshold = 2.76V
72
73/* PAGE 0x01 */
74static constexpr uint8_t TAS2780_INIT_0 = 0x17; // Initialization
75static constexpr uint8_t TAS2780_INIT_0_VAL = 0xC8; // SARBurstMask=0, CMP_HYST_LP=1
76static constexpr uint8_t TAS2780_LSR = 0x19; // Modulation
77static constexpr uint8_t TAS2780_LSR_PWM_MODE = 0x00; // PWM modulation mode
78static constexpr uint8_t TAS2780_INIT_1 = 0x21; // Initialization
79static constexpr uint8_t TAS2780_INIT_1_VAL = 0x00; // Disable comparator hysteresis
80static constexpr uint8_t TAS2780_INIT_2 = 0x35; // Initialization
81static constexpr uint8_t TAS2780_INIT_2_VAL = 0x74; // Noise minimized
82
83/* PAGE 0xFD */
84static constexpr uint8_t TAS2780_PAGE_FD_ACCESS = 0x0D; // Page 0xFD access unlock/lock register
85static constexpr uint8_t TAS2780_PAGE_FD_ACCESS_UNLOCK = 0x0D; // Unlock page 0xFD access
86static constexpr uint8_t TAS2780_PAGE_FD_ACCESS_LOCK = 0x00; // Lock page 0xFD access
87static constexpr uint8_t TAS2780_INIT_3 = 0x3E; // Initialization
88static constexpr uint8_t TAS2780_INIT_3_VAL = 0x4A; // Optimal Dmin
89
90// CDS_MODE (2 bits) and VBAT1S_MODE (1 bit) per power mode 0..3, packed so nothing lands in RAM on ESP8266:
91// PWR_MODE0: 2/0, PWR_MODE1: 0/0, PWR_MODE2: 3/1, PWR_MODE3: 1/0
92static constexpr uint8_t POWER_MODE_CDS_MODES = 0x72;
93static constexpr uint8_t POWER_MODE_VBAT1S_MODES = 0x04;
94
95// Latched interrupt bits per register (INT_LTCH0, 1, 1_0, 2, one byte each, low to high), split into faults
96// and informational events; bits outside both masks are undefined.
97static constexpr uint32_t TAS2780_INT_LTCH_ERROR_MASKS = 0x0FA058C7;
98static constexpr uint32_t TAS2780_INT_LTCH_INFO_MASKS = 0x00002138;
99
100// An if chain rather than a switch: a switch table would land in rodata, which is RAM on ESP8266.
101static const LogString *fault_name(uint8_t reg, uint8_t bit) {
102 const uint8_t key = (reg << 3) | bit;
103 if (key == 0x00)
104 return LOG_STR("Over temperature error");
105 if (key == 0x01)
106 return LOG_STR("Over current error");
107 if (key == 0x02)
108 return LOG_STR("TDM Clock Error");
109 if (key == 0x03)
110 return LOG_STR("Limiter active");
111 if (key == 0x04)
112 return LOG_STR("PVDD below limiter inflection point");
113 if (key == 0x05)
114 return LOG_STR("Limiter max attenuation");
115 if (key == 0x06)
116 return LOG_STR("BOP infinite hold");
117 if (key == 0x07)
118 return LOG_STR("BOP Mute");
119 if (key == 0x08)
120 return LOG_STR("Gain limiter active");
121 if (key == 0x0B)
122 return LOG_STR("Load Diagnostic mode fault status");
123 if (key == 0x0D)
124 return LOG_STR("Load diagnostic complete");
125 if (key == 0x0E)
126 return LOG_STR("OTP CRC error flag");
127 if (key == 0x15)
128 return LOG_STR("VBAT1S Under Voltage");
129 if (key == 0x17)
130 return LOG_STR("Internal PLL Clock Error");
131 if (key == 0x18)
132 return LOG_STR("PVDD UVLO");
133 if (key == 0x19)
134 return LOG_STR("Internal VBAT1S LDO Over Load");
135 if (key == 0x1A)
136 return LOG_STR("Internal VBAT1S LDO Over Voltage");
137 if (key == 0x1B)
138 return LOG_STR("Internal VBAT1S LDO Under Voltage");
139 return nullptr;
140}
141
143 if (!this->init_()) {
144 this->mark_failed();
145 return;
146 }
147 this->write_mode_ctrl_(TAS2780_MODE_CTRL_MODE_SFTW_SHTDWN);
148}
149
150bool TAS2780::select_page_(uint8_t page) {
151 if (this->current_page_ == page)
152 return true;
153 if (!this->write_byte(TAS2780_PAGE_SELECT, page)) {
154 this->current_page_ = 0xFF;
155 return false;
156 }
157 this->current_page_ = page;
158 return true;
159}
160
161bool TAS2780::update_bits_(uint8_t reg, uint8_t mask, uint8_t value) {
162 uint8_t current;
163 if (!this->select_page_(TAS2780_PAGE_0) || !this->read_byte(reg, &current)) {
164 ESP_LOGE(TAG, "Failed to read register 0x%02X", reg);
165 return false;
166 }
167 return this->write_byte(reg, (current & ~mask) | (value & mask));
168}
169
171 // Software reset (must select page 0 first; reset invalidates page cache)
172 if (!this->select_page_(TAS2780_PAGE_0)) {
173 ESP_LOGE(TAG, "I2C write failed during init");
174 return false;
175 }
176 this->current_page_ = 0xFF;
177 this->applied_power_mode_ = 0xFF; // only a complete init makes the chip trustworthy again
178 this->reg(TAS2780_SW_RESET) = TAS2780_SW_RESET_CMD;
179 delay(1);
180
181 uint8_t chd1;
182 if (!this->read_byte(TAS2780_DC_BLK1, &chd1)) {
183 ESP_LOGE(TAG, "I2C read failed during init");
184 return false;
185 }
186 if (chd1 != TAS2780_DC_BLK1_RESET_VAL) {
187 ESP_LOGE(TAG, "Init failed (DC_BLK1=0x%02X, expected 0x%02X)", chd1, TAS2780_DC_BLK1_RESET_VAL);
188 return false;
189 }
190
191 if (!this->select_page_(TAS2780_PAGE_0)) {
192 return false;
193 }
194 this->reg(TAS2780_TDM_CFG5) = TAS2780_TDM_CFG5_TX_VSNS_EN_SLOT4;
195 this->reg(TAS2780_TDM_CFG6) = TAS2780_TDM_CFG6_TX_ISNS_EN_SLOT0;
196
197 if (!this->select_page_(TAS2780_PAGE_1)) {
198 return false;
199 }
200 this->reg(TAS2780_LSR) = TAS2780_LSR_PWM_MODE;
201 this->reg(TAS2780_INIT_0) = TAS2780_INIT_0_VAL;
202 this->reg(TAS2780_INIT_1) = TAS2780_INIT_1_VAL;
203 this->reg(TAS2780_INIT_2) = TAS2780_INIT_2_VAL;
204
205 if (!this->select_page_(TAS2780_PAGE_FD)) {
206 return false;
207 }
208 this->reg(TAS2780_PAGE_FD_ACCESS) = TAS2780_PAGE_FD_ACCESS_UNLOCK;
209 this->reg(TAS2780_INIT_3) = TAS2780_INIT_3_VAL;
210 this->reg(TAS2780_PAGE_FD_ACCESS) = TAS2780_PAGE_FD_ACCESS_LOCK;
211
212 if (!this->select_page_(TAS2780_PAGE_0)) {
213 return false;
214 }
215 if (!this->set_power_mode_(this->power_mode_))
216 return false;
217
218 // When Y bridge is used (eg. PWR_MODE1) PVDD UVLO threshold needs to be set 2.5 V above VBAT1S level.
219 // UVLO = 1.753V + val * 0.332V
220 this->reg(TAS2780_PVDD_UVLO) = TAS2780_PVDD_UVLO_2V76;
221
222 // Mask all interrupt groups on the IRQZ pin, events are polled via update()
223 this->reg(TAS2780_INT_MASK0) = TAS2780_INT_MASK_ALL;
224 this->reg(TAS2780_INT_MASK1) = TAS2780_INT_MASK_ALL;
225 this->reg(TAS2780_INT_MASK1_0) = TAS2780_INT_MASK_ALL;
226 this->reg(TAS2780_INT_MASK2) = TAS2780_INT_MASK_ALL;
227 this->reg(TAS2780_INT_MASK3) = TAS2780_INT_MASK_ALL;
228
229 if (!this->update_bits_(TAS2780_INT_CLK_CFG, TAS2780_INT_CLK_CFG_MODE_MASK, TAS2780_INT_CLK_CFG_MODE_LIVE))
230 return false;
231
232 // Software reset sets DVC back to 0 dB (full volume)
233 if (!this->apply_config())
234 return false;
235 this->applied_power_mode_ = this->power_mode_;
236 return true;
237}
238
240 if (this->is_failed())
241 return;
242 ESP_LOGD(TAG, "Activating (PWR_MODE:%u)", this->power_mode_);
243 this->clear_latches_();
244 if (this->power_mode_ != this->applied_power_mode_ && !this->reinit_())
245 return;
246 this->active_ = this->write_mode_ctrl_(this->active_mode_());
247}
248
250 if (this->is_failed())
251 return;
252 ESP_LOGD(TAG, "Deactivating");
253 this->active_ = false;
254 this->write_mode_ctrl_(TAS2780_MODE_CTRL_MODE_SFTW_SHTDWN);
255}
256
258 if (this->is_failed())
259 return;
260 if (this->reinit_() && this->active_)
261 this->activate();
262}
263
265 if (!this->init_()) {
266 ESP_LOGE(TAG, "Re-initialization failed");
267 this->status_set_error(LOG_STR("Init failed"));
268 return false;
269 }
270 this->status_clear_error();
271 return true;
272}
273
275 // Lambda-supplied values bypass schema validation; refuse before anything touches the chip
276 if (power_mode >= 4) {
277 ESP_LOGE(TAG, "Invalid power mode %u, must be 0-3", power_mode);
278 return;
279 }
280 this->power_mode_ = power_mode;
281}
282
284 uint8_t cds_mode = (POWER_MODE_CDS_MODES >> (power_mode * 2)) & 0x03;
285 uint8_t vbat1s_mode = (POWER_MODE_VBAT1S_MODES >> power_mode) & 0x01;
286 return this->update_bits_(TAS2780_CHNL_0, TAS2780_CHNL_0_CDS_MODE_MASK, cds_mode << TAS2780_CHNL_0_CDS_MODE_SHIFT) &&
287 this->update_bits_(TAS2780_DC_BLK0, TAS2780_DC_BLK0_VBAT1S_MODE_MASK, vbat1s_mode ? 0xFF : 0);
288}
289
291 this->update_bits_(TAS2780_INT_CLK_CFG, TAS2780_INT_CLK_CFG_CLR_LATCH, TAS2780_INT_CLK_CFG_CLR_LATCH);
292}
293
294// Returns true if any latched interrupt flag is set
296 uint8_t latched[4];
297 if (!this->select_page_(TAS2780_PAGE_0) || !this->read_bytes(TAS2780_INT_LTCH0, latched, 3) ||
298 !this->read_byte(TAS2780_INT_LTCH2, &latched[3])) {
299 return false;
300 }
301 // LDMODE is a two-bit field in INT_LTCH1; report it once
302 if (latched[1] & (1 << 4))
303 latched[1] = (latched[1] & ~(1 << 4)) | (1 << 3);
304 for (uint8_t reg = 0; reg < 4; reg++) {
305 uint8_t errors = latched[reg] & (TAS2780_INT_LTCH_ERROR_MASKS >> (reg * 8));
306 uint8_t infos = latched[reg] & (TAS2780_INT_LTCH_INFO_MASKS >> (reg * 8));
307 for (uint8_t bit = 0; bit < 8; bit++) {
308 if (errors & (1 << bit)) {
309 ESP_LOGE(TAG, "%s", LOG_STR_ARG(fault_name(reg, bit)));
310 } else if (infos & (1 << bit)) {
311 ESP_LOGD(TAG, "%s", LOG_STR_ARG(fault_name(reg, bit)));
312 }
313 }
314 }
315 return (latched[0] | latched[1] | latched[2] | latched[3]) != 0;
316}
317
319 // Latches hold until cleared; without this the same events are logged on every update
320 if (this->log_error_states_())
321 this->clear_latches_();
322}
323
325 ESP_LOGCONFIG(TAG, "Audio Amplifier:");
326 LOG_I2C_DEVICE(this);
327 LOG_UPDATE_INTERVAL(this);
328 ESP_LOGCONFIG(TAG,
329 " Power Mode: %u\n"
330 " Amp Level: %u\n"
331 " Volume Range: %.2f - %.2f\n"
332 " Channel: %s",
333 this->power_mode_, this->amp_level_, this->vol_range_min_, this->vol_range_max_,
334 this->selected_channel_ == LEFT_CHANNEL ? LOG_STR_LITERAL("Left")
335 : this->selected_channel_ == RIGHT_CHANNEL ? LOG_STR_LITERAL("Right")
336 : LOG_STR_LITERAL("Mono Downmix"));
337}
338
340 return this->update_bits_(TAS2780_MODE_CTRL, TAS2780_MODE_CTRL_MODE_MASK, mode);
341}
342
343uint8_t TAS2780::active_mode_() const {
344 return this->is_muted_ ? TAS2780_MODE_CTRL_MODE_ACTIVE_MUTED : TAS2780_MODE_CTRL_MODE_ACTIVE;
345}
346
347bool TAS2780::set_mute_(bool muted) {
348 bool previous = this->is_muted_;
349 this->is_muted_ = muted;
350 uint8_t mode_ctrl;
351 if (!this->select_page_(TAS2780_PAGE_0) || !this->read_byte(TAS2780_MODE_CTRL, &mode_ctrl)) {
352 ESP_LOGE(TAG, "Failed to read MODE_CTRL");
353 this->is_muted_ = previous;
354 return false;
355 }
356 uint8_t current_mode = mode_ctrl & TAS2780_MODE_CTRL_MODE_MASK;
357 // Only switch between active/muted if device is active; don't wake from shutdown
358 if ((current_mode == TAS2780_MODE_CTRL_MODE_ACTIVE || current_mode == TAS2780_MODE_CTRL_MODE_ACTIVE_MUTED) &&
359 !this->write_byte(TAS2780_MODE_CTRL, (mode_ctrl & ~TAS2780_MODE_CTRL_MODE_MASK) | this->active_mode_())) {
360 ESP_LOGE(TAG, "Failed to write MODE_CTRL");
361 this->is_muted_ = previous;
362 return false;
363 }
364 return true;
365}
366
367bool TAS2780::set_volume(float volume) {
368 float previous = this->volume_;
369 this->volume_ = clamp(volume, 0.0f, 1.0f);
370 if (!this->write_volume_()) {
371 this->volume_ = previous;
372 return false;
373 }
374 return true;
375}
376
378 // Lambda-supplied volume ranges are not bounded or ordered
379 float range_min = std::min(this->vol_range_min_, this->vol_range_max_);
380 float range_max = std::max(this->vol_range_min_, this->vol_range_max_);
381 float volume = clamp(std::lerp(range_min, range_max, this->volume_), 0.0f, 1.0f);
382 uint8_t dvc = remap<uint8_t, float>(volume, 0.0f, 1.0f, TAS2780_DVC_MAX_ATTEN, 0);
383 ESP_LOGD(TAG, "Setting attenuation to: %u", dvc);
384 return this->select_page_(TAS2780_PAGE_0) && this->write_byte(TAS2780_DVC, dvc);
385}
386
388
390 // Lambda-supplied values bypass schema validation
391 if (this->amp_level_ > TAS2780_AMP_LEVEL_MAX) {
392 ESP_LOGW(TAG, "Amp level %u out of range, using %u", this->amp_level_, TAS2780_AMP_LEVEL_MAX);
393 this->amp_level_ = TAS2780_AMP_LEVEL_MAX;
394 }
395 ESP_LOGD(TAG, "Update amp to level idx: %u", this->amp_level_);
396 if (!this->update_bits_(TAS2780_CHNL_0, TAS2780_CHNL_0_AMP_LEVEL_MASK,
397 this->amp_level_ << TAS2780_CHNL_0_AMP_LEVEL_SHIFT)) {
398 return false;
399 }
400 return this->update_bits_(
401 TAS2780_TDM_CFG2, TAS2780_TDM_CFG2_RX_SCFG_MASK | TAS2780_TDM_CFG2_RX_WLEN_MASK | TAS2780_TDM_CFG2_RX_SLEN_MASK,
402 (this->selected_channel_ << TAS2780_TDM_CFG2_RX_SCFG_SHIFT) | TAS2780_TDM_CFG2_RX_WLEN_32BIT |
403 TAS2780_TDM_CFG2_RX_SLEN_32BIT);
404}
405
406} // namespace esphome::tas2780
BedjetMode mode
BedJet operating mode.
void mark_failed()
Mark this component as failed.
bool is_failed() const
Definition component.h:274
void status_clear_error()
Definition component.h:297
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 activate()
Activate with the configured power mode; re-initializes first when the mode changed.
Definition tas2780.cpp:239
void update() override
Definition tas2780.cpp:318
bool select_page_(uint8_t page)
Definition tas2780.cpp:150
void reset()
Software reset and re-initialization, leaving the amplifier on or off as last requested.
Definition tas2780.cpp:257
void setup() override
Definition tas2780.cpp:142
bool set_volume(float volume) override
Definition tas2780.cpp:367
void dump_config() override
Definition tas2780.cpp:324
void set_power_mode(uint8_t power_mode)
Definition tas2780.cpp:274
bool update_bits_(uint8_t reg, uint8_t mask, uint8_t value)
Definition tas2780.cpp:161
bool set_mute_(bool muted)
Definition tas2780.cpp:347
bool apply_config()
Write the amp level, channel and volume range to the device.
Definition tas2780.cpp:387
uint8_t active_mode_() const
Definition tas2780.cpp:343
float volume() override
Definition tas2780.h:32
bool set_power_mode_(uint8_t power_mode)
Definition tas2780.cpp:283
ChannelSelect selected_channel_
Definition tas2780.h:61
bool write_mode_ctrl_(uint8_t mode)
Definition tas2780.cpp:339
PowerMode power_mode
Definition msa3xx.h:3
ESPHOME_LOG_TAG(TAG, "tas2780")
void HOT delay(uint32_t ms)
Definition hal.cpp:85
T remap(U value, U min, U max, T min_out, T max_out)
Remap value from the range (min, max) to (min_out, max_out).
Definition helpers.h:879
static void uint32_t