Skip to content

Instantly share code, notes, and snippets.

@Staars
Last active June 26, 2025 19:28
Show Gist options
  • Save Staars/2fcf90a1a1610e97621c978a5ae9f7ab to your computer and use it in GitHub Desktop.
Save Staars/2fcf90a1a1610e97621c978a5ae9f7ab to your computer and use it in GitHub Desktop.
ESP8311 Codec fro upcoming i2s driver of Tasmota
# ES8311 Audio Codec Driver for Berry
# HW: ESP32P4-Nano, may work with the ESP32P4-EV too
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(50)
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}}')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment