Custom hardware support: ES8311 (DAC) + ES7243E (ADC) on shared I2C/I2S bus — init sequence incompatibility and dual-codec guidance needed

LiveKit SDK Version

v0.3.6

IDF Version

v5.4.1

Issue Description

Summary

I have a working ESP32-S3 voice bot firmware using WebSockets and am attempting to migrate to the
LiveKit ESP32 SDK (v0.3.6). My hardware uses two separate audio codecs — ES8311 for speaker output
and ES7243E for microphone input — both sharing the same I2C bus and I2S channels. I’ve run into
a fundamental incompatibility between the initialization sequence my hardware requires and the
sequence assumed by esp_capture / av_render as used in the SDK examples.


Hardware Configuration

  • Board: ESP32-S3N16R8
  • Speaker codec: ES8311 (DAC) via I2C address 0x18
  • Microphone codec: ES7243E (ADC) via I2C address 0x10
  • Shared I2C bus: SDA=GPIO18, SCL=GPIO17 (both codecs on I2C_NUM_0)
  • Shared I2S: MCLK=GPIO16, BCLK=GPIO9, WS=GPIO7, DOUT=GPIO8, DIN=GPIO10
  • Sample rate: 16kHz mono, 16-bit, Philips/I2S standard mode
  • Power Amplifier: GPIO21

Working Init Sequence (Current Firmware)

In my working WebSocket-based firmware, the correct hardware initialization order is:

  1. i2c_bus_init() — Initialize I2C master bus
  2. es7243_init() — Configure ES7243E ADC mic via I2C (sets sample rate, gain, etc.)
  3. es8311_codec_init() — Configure ES8311 DAC via I2C
  4. i2s_init() — Initialize I2S channels (TX + RX, stereo, 16kHz)
  5. pa_enable(true) — Enable power amplifier

The critical point: both codecs must be configured over I2C before I2S is started, because
both codecs depend on the MCLK signal coming from the I2S master to lock their PLLs and internal
clocks. If I2S starts before codec init, the codecs receive clock signals they haven’t been
configured to handle yet. In practice, the ES7243E is particularly sensitive to this ordering.


Issue with LiveKit SDK’s esp_capture / av_render Pattern

Looking at the SDK examples (media.c), the implied initialization pattern through esp_capture
and av_render appears to be:

  1. i2c init (inside codec driver, triggered by esp_capture setup)
  2. i2s init (inside esp_capture/av_render pipeline setup)
  3. codec init (after I2S is running)

This ordering does not work for my hardware. Specifically:

  1. Dual-codec topology is undocumented : There are no examples or guidance for boards where the mic ADC and speaker DAC are separate chips sharing a single I2S bus.
  2. Shared I2S bus conflict: esp_capture initializes an I2S RX channel and av_render initializes an I2S TX channel. On my board both TX and RX are on the same I2S peripheral (I2S_NUM_0, full-duplex). It is unclear whether these two components can share a single I2S peripheral or whether they each try to claim it independently, causing a double-init conflict.
  3. No hook for pre-I2S codec configuration: There is no apparent way to run custom I2C codec setup before esp_capture triggers I2S initialization internally.

Questions / Feature Requests

  1. Is there a supported way to use ES7243E (record-only) with esp_capture alongside ES8311 for playback? If not, can a custom capture source be provided that bypasses the codec driver layer but still feeds raw audio into the LiveKit pipeline?
  2. Can esp_capture and av_render share a single full-duplex I2S peripheral? If yes, how should this be configured?
  3. Is there a lifecycle hook to perform codec-specific I2C configuration before I2S channels are enabled? A pre_i2s_init callback in the capture/render config structs would solve this cleanly.
  4. What is the recommended migration path for custom-hardware boards that don’t match the reference board (e.g. ESP32-S3-BOX) and have non-standard dual-codec topologies?

Is this your own custom board or are you using an of the shelf ESP32 kit? The reason I ask is if it is off the self I would like to have a look at that kits datasheets and example code.

Hi, this is a custom board, not an off-the-shelf ESP32 kit.

Do you need the datasheets of the codecs used?
If yes please find attached.
Datasheets

I’ve used that ES7210 and ES8311 a lot with the LiveKit libraries. I will have to see look through my boards to see if I ever used the ES7243E. I am not sure the difference in the two codecs off hand.

Did you start with the basic example. I’ve had most luck starting with that on on new boards.

Hi thanks for response.
I have the both the codecs working but with my own init sequence without using the esp_codec/livekit API’s.
I tried with using the minimal example from the livekit. But the example inlcudes board_cfg.txt for limited boards which does not have the include support for custom board.
Also it uses esp_codec_dev which does not use the shared i2s channel. it creates a new i2s channel each time.

I’ve used that example on my own custom boards so it should be possible with the source code found here:

I find it hard to help troubleshoot this without the physical hardware to test on.

Agreed.Thanks! Could you share how you structured the custom board definition? Specifically:

  1. Did you replace board.c/media.c directly in main/, or did you create a separate component?

  2. Did you pass an existing I2S channel handle to audio_codec_new_i2s_data() or let the SDK create its own

Let me see if I can whip something up

1 Like

The examples in the ESP32 SDK repository use the codec_board component to initialize capture and render peripherals based on the settings in board_cfg.txt.

While this approach is convenient and flexible for example applications, production applications should typically use a pre-made or custom board support package (BSP) to initialize the target hardware. BSPs for common dev boards can be found in this repo, and they can be easily modified to support custom initialization—unlike codec_board. This gives you full control over peripheral setup and board-specific behavior.

The LiveKit SDK itself is agnostic to the initialization method—it only requires properly configured esp_capture_handle_t and av_render_handle_t to be provided when the room is initialized.

2 Likes