Last active
June 26, 2025 19:28
-
-
Save Staars/2fcf90a1a1610e97621c978a5ae9f7ab to your computer and use it in GitHub Desktop.
ESP8311 Codec fro upcoming i2s driver of Tasmota
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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