ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
es8388.cpp
Go to the documentation of this file.
1#include "es8388.h"
2
3#include <cinttypes>
4#include "esphome/core/hal.h"
6#include "esphome/core/log.h"
7
8namespace esphome::es8388 {
9
10ESPHOME_LOG_TAG(TAG, "es8388");
11
12// Mark the component as failed; use only in setup
13#define ES8388_ERROR_FAILED(func) \
14 if (!(func)) { \
15 this->mark_failed(LOG_STR("Failed to initialize")); \
16 return; \
17 }
18
19// Return false; use outside of setup
20#define ES8388_ERROR_CHECK(func) \
21 if (!(func)) { \
22 return false; \
23 }
24
25void ES8388::setup() {
26 // mute DAC
27 this->set_mute_state_(true);
28
29 // I2S worker mode
30 ES8388_ERROR_FAILED(this->write_byte(ES8388_MASTERMODE, 0x00));
31
32 /* Chip Control and Power Management */
33 ES8388_ERROR_FAILED(this->write_byte(ES8388_CONTROL2, 0x50));
34 // normal all and power up all
35 ES8388_ERROR_FAILED(this->write_byte(ES8388_CHIPPOWER, 0x00));
36
37 // vmidsel/500k
38 // EnRef=0,Play&Record Mode,(0x17-both of mic&play)
39 ES8388_ERROR_FAILED(this->write_byte(ES8388_CONTROL1, 0x12));
40
41 // i2s 16 bits
42 ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL1, 0x18));
43 // sample freq 256
44 // DACFsMode,SINGLE SPEED; DACFsRatio,256
45 ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL2, 0x02));
46 // 0x00 audio on LIN1&RIN1, 0x09 LIN2&RIN2
47 ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL16, 0x00));
48 // only left DAC to left mixer enable 0db
49 ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL17, 0x90));
50 // only right DAC to right mixer enable 0db
51 ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL20, 0x90));
52 // set internal ADC and DAC use the same LRCK clock, ADC LRCK as internal LRCK
53 ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL21, 0x80));
54 // vroi=0 - 1.5k VREF to analog output resistance (default)
55 ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL23, 0x00));
56
57 // power down adc and line in
58 ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCPOWER, 0xFF));
59
60 //@nightdav
61 ES8388_ERROR_FAILED(
62 this->write_byte(ES8388_ADCCONTROL1, 0x00)); // +21dB : recommended value for ALC & voice recording
63
64 // set to Mono Right
65 ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL3, 0x02));
66
67 // I2S 16 Bits length and I2S serial audio data format
68 ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL4, 0x0d));
69 // ADCFsMode,singel SPEED,RATIO=256
70 ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL5, 0x02));
71
72 // ADC Volume
73 ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL8, 0x00));
74 ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL9, 0x00));
75
76 //@nightDav
77 // ALC Config (as recommended by ES8388 user guide for voice recording)
78
79 // Reg 0x12 = 0xe2 (ALC enable, PGA Max. Gain=23.5dB, Min. Gain=0dB)
80 ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL10, 0xe2));
81
82 // Reg 0x13 = 0xa0 (ALC Target=-1.5dB, ALC Hold time =0 mS)
83 ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL11, 0xa0));
84 // Reg 0x14 = 0x12(Decay time =820uS , Attack time = 416 uS)
85 ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL12, 0x12));
86
87 // Reg 0x15 = 0x06(ALC mode)
88 ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL13, 0x06));
89
90 // Reg 0x16 = 0xc3(nose gate = -40.5dB, NGG = 0x01(mute ADC))
91 ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL14, 0xc3));
92
93 // Power on ADC
94 ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL21, 0x80));
95
96 // Start state machine
97 ES8388_ERROR_FAILED(this->write_byte(ES8388_CHIPPOWER, 0xF0));
98 delay(1);
99 ES8388_ERROR_FAILED(this->write_byte(ES8388_CHIPPOWER, 0x00));
100
101 // DAC volume max
102 // Set initial volume
103 // this->set_volume(0.75); // 0.75 = 0xBF = 0dB
104
105 this->set_mute_state_(false);
106
107 // unmute ADC with fade in
108 ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCCONTROL7, 0x60));
109 // unmute DAC with fade in
110 ES8388_ERROR_FAILED(this->write_byte(ES8388_DACCONTROL3, 0x20));
111
112 // Power on ADC, Enable LIN&RIN, Power off MICBIAS, set int1lp to low power mode
113 ES8388_ERROR_FAILED(this->write_byte(ES8388_ADCPOWER, 0x09));
114
115#ifdef USE_ES8388_SELECT
116 if (this->dac_output_select_ != nullptr) {
117 if (this->dac_output_initial_.has_value()) {
118 const DacOutputLine line = this->dac_output_initial_.value();
119 ES8388_ERROR_FAILED(this->set_dac_output(line));
120 this->dac_output_select_->publish_state(static_cast<size_t>(line));
121 } else {
122 auto dac_power = this->get_dac_power();
123 if (dac_power.has_value()) {
124 if (this->dac_output_select_->has_index(dac_power.value())) {
125 this->dac_output_select_->publish_state(dac_power.value());
126 } else {
127 ESP_LOGW(TAG, "Unknown DAC output power value: %d", dac_power.value());
128 }
129 }
130 }
131 }
132 if (this->adc_input_mic_select_ != nullptr) {
133 if (this->adc_input_mic_initial_.has_value()) {
134 const AdcInputMicLine line = this->adc_input_mic_initial_.value();
135 ES8388_ERROR_FAILED(this->set_adc_input_mic(line));
136 this->adc_input_mic_select_->publish_state(static_cast<size_t>(line));
137 } else {
138 auto mic_input = this->get_mic_input();
139 if (mic_input.has_value()) {
140 if (this->adc_input_mic_select_->has_index(mic_input.value())) {
141 this->adc_input_mic_select_->publish_state(mic_input.value());
142 } else {
143 ESP_LOGW(TAG, "Unknown ADC input mic value: %d", mic_input.value());
144 }
145 }
146 }
147 }
148#endif
149}
150
151void ES8388::dump_config() {
152 ESP_LOGCONFIG(TAG, "ES8388 Audio Codec:");
153 LOG_I2C_DEVICE(this);
154#ifdef USE_ES8388_SELECT
155 LOG_SELECT(" ", "DacOutputSelect", this->dac_output_select_);
156 LOG_SELECT(" ", "ADCInputMicSelect", this->adc_input_mic_select_);
157#endif
158}
159
160bool ES8388::set_volume(float volume) {
161 volume = clamp(volume, 0.0f, 1.0f);
162 uint8_t value = remap<uint8_t, float>(volume, 0.0f, 1.0f, 192, 0);
163 ESP_LOGD(TAG, "Setting ES8388_DACCONTROL4 / ES8388_DACCONTROL5 to 0x%02X (volume: %f)", value, volume);
164 ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL4, value));
165 ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL5, value));
166
167 return true;
168}
169
170float ES8388::volume() {
171 uint8_t value;
172 ES8388_ERROR_CHECK(this->read_byte(ES8388_DACCONTROL4, &value));
173 return remap<float, uint8_t>(value, 192, 0, 0.0f, 1.0f);
174}
175
176bool ES8388::set_mute_state_(bool mute_state) {
177 uint8_t value = 0;
178
179 this->is_muted_ = mute_state;
180
181 ES8388_ERROR_CHECK(this->read_byte(ES8388_DACCONTROL3, &value));
182 ESP_LOGV(TAG, "Read ES8388_DACCONTROL3: 0x%02X", value);
183
184 // Only toggle the DACMute bit; the other bits of this register hold unrelated
185 // DAC settings that must be preserved. Previously muting overwrote the whole
186 // register with 0x3C and unmuting never cleared the bit, so once muted the DAC
187 // could not be unmuted again.
188 assign_bits(value, ES8388_DACCONTROL3_DAC_MUTE, mute_state);
189
190 ESP_LOGV(TAG, "Setting ES8388_DACCONTROL3 to 0x%02X (muted: %s)", value, YESNO(mute_state));
191 return this->write_byte(ES8388_DACCONTROL3, value);
192}
193
194// Set dac power output
195bool ES8388::set_dac_output(DacOutputLine line) {
196 uint8_t reg_out1 = 0;
197 uint8_t reg_out2 = 0;
198 uint8_t dac_power = 0;
199
200 // 0x00: -30dB , 0x1E: 0dB
201 switch (line) {
202 case DAC_OUTPUT_LINE1:
203 reg_out1 = 0x1E;
204 dac_power = ES8388_DAC_OUTPUT_LOUT1_ROUT1;
205 break;
206 case DAC_OUTPUT_LINE2:
207 reg_out2 = 0x1E;
208 dac_power = ES8388_DAC_OUTPUT_LOUT2_ROUT2;
209 break;
210 case DAC_OUTPUT_BOTH:
211 reg_out1 = 0x1E;
212 reg_out2 = 0x1E;
213 dac_power = ES8388_DAC_OUTPUT_BOTH;
214 break;
215 default:
216 ESP_LOGE(TAG, "Unknown DAC output line: %d", line);
217 return false;
218 };
219
220 ESP_LOGV(TAG,
221 "DAC output config:\n"
222 " DACPOWER: 0x%02X\n"
223 " DACCONTROL24/25: 0x%02X\n"
224 " DACCONTROL26/27: 0x%02X",
225 dac_power, reg_out1, reg_out2);
226
227 ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL24, reg_out1)); // LOUT1VOL
228 ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL25, reg_out1)); // ROUT1VOL
229 ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL26, reg_out2)); // LOUT2VOL
230 ES8388_ERROR_CHECK(this->write_byte(ES8388_DACCONTROL27, reg_out2)); // ROUT1VOL
231
232 return this->write_byte(ES8388_DACPOWER, dac_power);
233}
234
235optional<DacOutputLine> ES8388::get_dac_power() {
236 uint8_t dac_power;
237 if (!this->read_byte(ES8388_DACPOWER, &dac_power)) {
239 return {};
240 }
241 switch (dac_power) {
242 case ES8388_DAC_OUTPUT_LOUT1_ROUT1:
243 return DAC_OUTPUT_LINE1;
244 case ES8388_DAC_OUTPUT_LOUT2_ROUT2:
245 return DAC_OUTPUT_LINE2;
246 case ES8388_DAC_OUTPUT_BOTH:
247 return DAC_OUTPUT_BOTH;
248 default:
249 return {};
250 }
251}
252
253// Set ADC input MIC
254bool ES8388::set_adc_input_mic(AdcInputMicLine line) {
255 uint8_t mic_input = 0;
256
257 switch (line) {
259 mic_input = ES8388_ADC_INPUT_LINPUT1_RINPUT1;
260 break;
262 mic_input = ES8388_ADC_INPUT_LINPUT2_RINPUT2;
263 break;
265 mic_input = ES8388_ADC_INPUT_DIFFERENCE;
266 break;
267 default:
268 ESP_LOGE(TAG, "Unknown ADC input mic line: %d", line);
269 return false;
270 }
271
272 ESP_LOGV(TAG, "Setting ES8388_ADCCONTROL2 to 0x%02X", mic_input);
273 ES8388_ERROR_CHECK(this->write_byte(ES8388_ADCCONTROL2, mic_input));
274
275 return true;
276}
277
278optional<AdcInputMicLine> ES8388::get_mic_input() {
279 uint8_t mic_input;
280 if (!this->read_byte(ES8388_ADCCONTROL2, &mic_input)) {
282 return {};
283 }
284 switch (mic_input) {
285 case ES8388_ADC_INPUT_LINPUT1_RINPUT1:
286 return ADC_INPUT_MIC_LINE1;
287 case ES8388_ADC_INPUT_LINPUT2_RINPUT2:
288 return ADC_INPUT_MIC_LINE2;
289 case ES8388_ADC_INPUT_DIFFERENCE:
291 default:
292 return {};
293 };
294}
295
296} // namespace esphome::es8388
virtual void setup()
Where the component's initialization should happen.
Definition component.cpp:84
void status_momentary_warning(uint32_t length=5000)
Set warning status flag and clear it after length ms.
optional< DacOutputLine > dac_output_initial_
Definition es8388.h:84
bool set_mute_state_(bool mute_state)
Mutes or unmutes the DAC audio out.
Definition es8388.cpp:176
optional< AdcInputMicLine > adc_input_mic_initial_
Definition es8388.h:85
bool write_byte(uint8_t a_register, uint8_t data) const
Definition i2c.h:312
bool read_byte(uint8_t a_register, uint8_t *data)
Definition i2c.h:272
ESPHOME_LOG_TAG(TAG, "es8388")
@ ADC_INPUT_MIC_LINE1
Definition es8388.h:23
@ ADC_INPUT_MIC_DIFFERENCE
Definition es8388.h:25
@ ADC_INPUT_MIC_LINE2
Definition es8388.h:24
value[LAYOUT.day_index()]
ESPHOME_ALWAYS_INLINE constexpr void assign_bits(T &value, std::type_identity_t< T > mask, bool on)
Set the bits of mask in value when on is true, clear them otherwise.
Definition helpers.h:1072
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:887