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