diff --git a/tasmota/berry/audio/es8311.be b/tasmota/berry/audio/es8311.be new file mode 100644 index 000000000..0e0006e39 --- /dev/null +++ b/tasmota/berry/audio/es8311.be @@ -0,0 +1,339 @@ +# ES8311 Audio Codec Driver for Berry +# HW: ESP32P4-EV +# pa_power pin GPIO 53 + + +class ES8311 + var i2c + + def init() + import gpio + self.i2c = tasmota.wire_scan(0x18) + if self.i2c == nil + log("ES8311 I2C initialization failed") + else + # var i2s_conf = tasmota.cmd('i2sconfig') + # var sample_freq = i2s_conf["I2SConfig"]["Tx"]["SampleRate"] + var sample_freq = 48000 # seems to work for every rate ??? + if self.init_codec(sample_freq, sample_freq * 128) + self.config_microphone() + log("ES8311 initialized successfully") + gpio.pin_mode(53, gpio.OUTPUT) + tasmota.add_driver(self) + else + log("ES8311 initialization failed") + end + end + end + + def audio(cmd, idx, payload, raw) + if cmd == "power" + self.pa_power(idx ? 1 : 0) + elif cmd == "rate" + # ??? seems to work with any rate + if raw[0] == 0 # tx + self.sample_frequency_config(idx * 256, idx) + print(f"ES8311: Set TX sample rate to {idx}Hz") + elif raw[0] == 1 # rx + self.sample_frequency_config(idx * 256, idx) + print(f"ES8311: Set RX sample rate to {idx}Hz") + end + end + end + + # PA Power Control + def pa_power(enable) + log(f"ES8311: Setting PA GPIO 53 to {enable}",4) + import gpio + if enable + gpio.digital_write(53, 1) + else + gpio.digital_write(53, 0) + end + end + + # I2C functions + def write_reg(reg_addr, data) + if self.i2c.write(0x18, reg_addr, data, 1) + return 0 + else + log(f"Write reg {reg_addr:#04x} failed", 4) + return -1 + end + end + + def read_reg(reg_addr) + var data = self.i2c.read(0x18, reg_addr, 1) + log(f"Read reg {reg_addr:#04x} = {data:#04x}",4) + return data != nil ? data : -1 + end + + # Coefficient table for clock divider + static coeff_div = [ + # mclk, rate, pre_div, pre_multi, adc_div, dac_div, fs_mode, lrck_h, lrck_l, bclk_div, adc_osr, dac_osr + [12288000, 8000, 0x06, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [18432000, 8000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x05, 0xff, 0x18, 0x10, 0x20], + [16384000, 8000, 0x08, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [8192000, 8000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [6144000, 8000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [4096000, 8000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [3072000, 8000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [2048000, 8000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [1536000, 8000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [1024000, 8000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + + # 11.025k + [11289600, 11025, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [5644800, 11025, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [2822400, 11025, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [1411200, 11025, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + + # 12k + [12288000, 12000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [6144000, 12000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [3072000, 12000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [1536000, 12000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + + # 16k + [12288000, 16000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [18432000, 16000, 0x03, 0x02, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x20], + [16384000, 16000, 0x04, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [8192000, 16000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [6144000, 16000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [4096000, 16000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [3072000, 16000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [2048000, 16000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [1536000, 16000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + [1024000, 16000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x20], + + # 22.05k + [11289600, 22050, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [5644800, 22050, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [2822400, 22050, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [1411200, 22050, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + + # 24k + [12288000, 24000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [18432000, 24000, 0x03, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [6144000, 24000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [3072000, 24000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [1536000, 24000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + + # 32k + [12288000, 32000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [18432000, 32000, 0x03, 0x04, 0x03, 0x03, 0x00, 0x02, 0xff, 0x0c, 0x10, 0x10], + [16384000, 32000, 0x02, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [8192000, 32000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [6144000, 32000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [4096000, 32000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [3072000, 32000, 0x03, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [2048000, 32000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [1536000, 32000, 0x03, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10], + [1024000, 32000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + + # 44.1k + [11289600, 44100, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [5644800, 44100, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [2822400, 44100, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [1411200, 44100, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + + # 48k + [12288000, 48000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [18432000, 48000, 0x03, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [6144000, 48000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [3072000, 48000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [1536000, 48000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + + # 64k + [12288000, 64000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [18432000, 64000, 0x03, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10], + [16384000, 64000, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [8192000, 64000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [6144000, 64000, 0x01, 0x04, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10], + [4096000, 64000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [3072000, 64000, 0x01, 0x08, 0x03, 0x03, 0x01, 0x01, 0x7f, 0x06, 0x10, 0x10], + [2048000, 64000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [1536000, 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0xbf, 0x03, 0x18, 0x18], + [1024000, 64000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10], + + # 88.2k + [11289600, 88200, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [5644800, 88200, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [2822400, 88200, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [1411200, 88200, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10], + + # 96k + [12288000, 96000, 0x01, 0x02, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [18432000, 96000, 0x03, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [6144000, 96000, 0x01, 0x04, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [3072000, 96000, 0x01, 0x08, 0x01, 0x01, 0x00, 0x00, 0xff, 0x04, 0x10, 0x10], + [1536000, 96000, 0x01, 0x08, 0x01, 0x01, 0x01, 0x00, 0x7f, 0x02, 0x10, 0x10] + ] + + # Find coefficients + def get_coeff(mclk, rate) + for i: 0..size(self.coeff_div)-1 + if self.coeff_div[i][0] == mclk && self.coeff_div[i][1] == rate + return i + end + end + return -1 + end + + # Configure sample frequency + def sample_frequency_config(mclk_frequency, sample_frequency) + var coeff = self.get_coeff(mclk_frequency, sample_frequency) + if coeff < 0 + log(f"Error: Cannot configure sample rate: {sample_frequency}",4) + return false + end + + var selected_coeff = self.coeff_div[coeff] + + # Register 0x02 + var regv = self.read_reg(0x02) + regv &= 0x07 + regv |= (selected_coeff[2] - 1) << 5 + regv |= selected_coeff[3] << 3 + self.write_reg(0x02, regv) + + # Register 0x03 + var reg03 = (selected_coeff[6] << 6) | selected_coeff[10] + self.write_reg(0x03, reg03) + + # Register 0x04 + self.write_reg(0x04, selected_coeff[11]) + + # Register 0x05 + var reg05 = ((selected_coeff[4] - 1) << 4) | (selected_coeff[5] - 1) + self.write_reg(0x05, reg05) + + # Register 0x06 + regv = self.read_reg(0x06) + regv &= 0xE0 + if selected_coeff[9] < 19 + regv |= (selected_coeff[9] - 1) << 0 + else + regv |= selected_coeff[9] << 0 + end + self.write_reg(0x06, regv) + + # Register 0x07 + regv = self.read_reg(0x07) + regv &= 0xC0 + regv |= selected_coeff[7] << 0 + self.write_reg(0x07, regv) + + # Register 0x08 + self.write_reg(0x08, selected_coeff[8]) + log(f"Sample frequency configured: MCLK={mclk_frequency}Hz, Sample Rate={sample_frequency}Hz, Coeff Index={coeff}",4) + return true + end + + # Initialize ES8311 + def init_codec(sample_freq, mclk_freq) + log(f"Initializing ES8311 codec with sample frequency: {sample_freq}Hz and MCLK frequency: {mclk_freq}Hz",2) + # Reset ES8311 + self.write_reg(0x00, 0x1F) + tasmota.delay(20) + self.write_reg(0x00, 0x00) + self.write_reg(0x00, 0x80) + + # Clock configuration + var reg01 = 0x3F # Enable all clocks + self.write_reg(0x01, reg01) + + # Configure sample frequency + if !self.sample_frequency_config(mclk_freq, sample_freq) + return false + end + + # Configure audio format (I2S, 16-bit) + var regv = self.read_reg(0x00) + regv &= 0xBF; + self.write_reg(0x00, regv) + self.write_reg(0x09, 0x0C) # SDP In: I2S, 16-bit + self.write_reg(0x0A, 0x0C) # SDP Out: I2S, 16-bit + + # Enable analog circuits + self.write_reg(0x0D, 0x01) # Enable analog circuits + self.write_reg(0x0E, 0x02) # Enable PGA and ADC modulator + self.write_reg(0x12, 0x00) # Enable DAC + self.write_reg(0x13, 0x10) # Enable headphone output + + # ADC/DAC configuration + self.write_reg(0x1C, 0x6A) # Bypass ADC equalizer + self.write_reg(0x37, 0x08) # Bypass DAC equalizer + + return true + end + + # Set volume (0-100) + def set_volume(volume) + if volume < 0 + volume = 0 + elif volume > 100 + volume = 100 + end + + var reg32 + if volume == 0 + reg32 = 0 + else + reg32 = ((volume * 256) / 100) - 1 + end + + self.write_reg(0x32, reg32) + return volume + end + + # Read volume + def get_volume() + var reg32 = self.read_reg(0x32) + if reg32 == 0 + return 0 + else + return ((reg32 * 100) / 256) + 1 + end + end + + # Mute + def mute(enable) + var reg31 = self.read_reg(0x31) + if enable + reg31 |= 0x60 # Set bits 6 and 5 + else + reg31 &= ~0x60 # Clear bits 6 and 5 + end + self.write_reg(0x31, reg31) + end + + # Set microphone gain + def set_mic_gain(gain_db) # gain_db is 0x00 to 0x07 (0dB to 42dB) + self.write_reg(0x16, gain_db) + end + + # Configure microphone + def config_microphone() + var reg14 = 0x1A # Enable analog microphone, max PGA gain + self.write_reg(0x17, 0xC8) # Set ADC gain + self.write_reg(0x14, reg14) + self.set_mic_gain(0x01) # Set microphone gain to 6dB + end + + # Register dump for debugging + def register_dump() + log("ES8311 Register Dump:",2) + for reg: 0..0x49 + var value = self.read_reg(reg) + log(f"REG:{reg:02X}: {value:02X}",2) + end + + end +end + +# Example +codec = ES8311() +codec.set_volume(75) +codec.set_mic_gain(0x04) # Set microphone gain to (6*4 =) 24dB +tasmota.cmd('i2sconfig {"Tx":{"SampleRate":48000,"SlotMask":1,"APLL":0,"SlotConfig":2},"Rx":{"SampleRate":32000,"Channels":1, "Mode":0}}')