Skip to content

ESP32-LyraT I2S Microphone Capture on Jetson Orin Nano - Application Guide

Goal: Use an ESP32-LyraT audio dev kit as a practical I2S microphone/audio frontend for the Jetson Orin Nano 8GB / Super Developer Kit 40-pin header, so you can test Jetson I2S capture, AHUB routing, and JetPack audio debugging before designing a custom microphone board.

Hub: Multimedia
Audio foundation: Jetson Audio Setup and Development


1. What this application is testing

This is not a normal "plug in a USB mic" test.

This application tests the real embedded-audio path:

microphone frontend
  -> codec / ADC
  -> I2S serial audio
  -> Jetson 40-pin header I2S2
  -> AHUB / ADMAIF
  -> ALSA capture
  -> recording, ASR, wake-word, or voice pipeline

The ESP32-LyraT is useful here because it already has:

  • onboard microphones
  • an ES8388 audio codec
  • an exposed I2S header
  • ESP-ADF examples for configuring the board-side audio path

The important correction:

Audio is not carried over I2C. On the LyraT, I2C controls the codec registers. The audio samples move over I2S.

So if you say "I2C mic" in this project, usually you mean one of these:

  • I2S microphone / I2S audio stream: digital audio data path
  • I2C-controlled codec: control path for a codec such as ES8388

Use the LyraT as the audio frontend and let the ESP32 side configure the codec.

ESP32-LyraT
  onboard mics
      |
      v
  ES8388 codec / ADC
      |
      | I2S clocks + ADC data on JP4
      v
Jetson Orin Nano 40-pin header
  I2S2 DIN / SCLK / FS
      |
      v
Jetson AHUB -> ADMAIF1 -> ALSA arecord

This is the lowest-risk bring-up model because:

  • ESP32-LyraT already knows how to initialize the ES8388
  • Jetson only has to prove that it can receive I2S
  • you avoid fighting two hosts trying to control the same codec over I2C

Later, for a real product, replace the LyraT with a dedicated codec or ADC board that Jetson controls directly.


3. What not to do first

Do not start by trying to make Jetson directly control the LyraT ES8388 over I2C.

That path is possible in theory, but it is a poor first test because:

  • the ES8388 is already wired to the ESP32 on the LyraT
  • the ESP32 firmware normally owns codec initialization
  • Jetson would need an ASoC codec driver and machine-driver binding
  • two hosts on the same codec control path can conflict
  • you may need board rework or firmware changes to fully tri-state the ESP32 side

For first bring-up, treat the LyraT like a configured I2S audio source.


4. Hardware prerequisites

  • Jetson Orin Nano 8GB / Super Developer Kit
  • JetPack 6.x / L4T 36.x or newer
  • ESP32-LyraT V4.3 or similar LyraT variant
  • USB cable for powering and flashing the LyraT
  • jumper wires
  • logic analyzer or oscilloscope, strongly recommended
  • optional powered speakers or headphones for LyraT-side sanity tests

Voltage rule:

Jetson 40-pin header signals are 3.3 V logic. Do not connect 5 V UART/I2S/control signals.

The LyraT I2S header signals are ESP32-side 3.3 V logic, so direct signal wiring is reasonable. Still share ground.


5. Jetson 40-pin I2S2 pins

For the Orin Nano developer kit 40-pin header, the audio guide uses the header-exposed I2S2 path:

Jetson function 40-pin header pin Direction in this test
I2S2_SCLK / bit clock 12 LyraT -> Jetson if LyraT is master
I2S2_FS / LRCK / frame sync 35 LyraT -> Jetson if LyraT is master
I2S2_DIN 38 LyraT audio data -> Jetson
I2S2_DOUT 40 Jetson playback data -> LyraT, optional
AUD_MCLK 7 optional; do not connect for first capture test
GND 6, 9, 14, etc. common ground

For microphone capture, the minimum useful wiring is:

LyraT bit clock  -> Jetson pin 12
LyraT LRCK       -> Jetson pin 35
LyraT ADC data   -> Jetson pin 38
LyraT GND        -> Jetson GND

Do not connect both sides as clock masters. If LyraT drives SCLK and LRCK, Jetson must be configured as the I2S clock slave for that DAI link.


6. ESP32-LyraT JP4 I2S header

Espressif documents the LyraT V4.3 I2S header JP4 as exposing the board I2S signals:

LyraT JP4 signal ESP32 pin Meaning for this test
MCLK GPIO0 master clock; usually leave unconnected for first Jetson capture test
SCLK GPIO5 I2S bit clock
LRCK GPIO25 I2S left/right frame clock
DSDIN GPIO26 data into ES8388 DAC, useful for playback into LyraT
ASDOUT GPIO35 ADC data out from ES8388, useful for mic capture into Jetson
GND GND common reference

For capture from LyraT microphones into Jetson:

LyraT signal Jetson signal Jetson pin
SCLK I2S2_SCLK 12
LRCK I2S2_FS 35
ASDOUT I2S2_DIN 38
GND GND 6 / 9 / 14

Optional playback direction:

Jetson signal LyraT signal Purpose
I2S2_DOUT pin 40 DSDIN send Jetson audio into LyraT DAC

Keep playback disconnected until capture works.


7. Bring-up plan

Use this order. It prevents chasing software routing while the wire-level clocks are still wrong.

  1. Prove Jetson internal audio routing.
  2. Prove LyraT microphones and codec work on the LyraT side.
  3. Make LyraT output a stable I2S mic stream — see §9 reference firmware lyrat_jp4_passthrough and §14 verified result for a known-good control.
  4. Verify SCLK, LRCK, and ASDOUT on a scope or logic analyzer.
  5. Enable Jetson 40-pin I2S2 pinmux.
  6. Configure Jetson audio route from I2S2 to ADMAIF1.
  7. Record raw audio with arecord.
  8. Only then add GStreamer, wake-word, ASR, or beamforming.

8. Jetson-side setup

Confirm JetPack and ALSA devices

cat /etc/nv_tegra_release
uname -a

aplay -l
arecord -l
amixer -c APE controls | grep -E 'I2S2|ADMAIF'

If APE does not exist, stop and fix the Jetson audio stack before wiring external hardware.

How to read a healthy JetPack 6 / R36 baseline

If your Jetson shows output like this:

  • card 1: APE [NVIDIA Jetson Orin Nano APE]
  • capture and playback devices for ADMAIF1 through ADMAIF20
  • mixer controls such as I2S2 Mux, I2S2 codec frame mode, I2S2 codec master mode, and ADMAIF1 Mux

that is a good sign.

It means:

  • the APE sound card is present
  • the AHUB / ADMAIF fabric is registered
  • the I2S2 driver is exposed to ALSA
  • hw:APE,0 maps to ADMAIF1, which is the first practical external capture endpoint

It does not mean the external header capture path is already working.

You still need:

  • I2S2 enabled on the 40-pin header
  • the correct master/slave clock relationship
  • the correct route from I2S2 into ADMAIF1
  • actual BCLK, LRCK, and data on the wires

Enable 40-pin I2S2 pinmux

Use Jetson-IO if your image supports it:

sudo /opt/nvidia/jetson-io/jetson-io.py

Select the 40-pin header configuration that enables I2S2, save the overlay, and reboot.

After reboot:

amixer -c APE controls | grep I2S2

Internal loopback first

Before using the LyraT, prove that Jetson-side routing is alive:

amixer -c APE cset name="I2S2 Mux" "ADMAIF1"
amixer -c APE cset name="ADMAIF1 Mux" "I2S2"
amixer -c APE cset name="I2S2 Loopback" "on"

aplay -D hw:APE,0 test.wav &
arecord -D hw:APE,0 -r 48000 -c 2 -f S16_LE jetson-i2s2-loopback.wav

This does not prove the external pins work. It proves the AHUB route is sane.


9. LyraT-side setup

On the LyraT, use ESP-ADF or ESP-IDF firmware that:

  • initializes the ES8388
  • selects the onboard microphone input
  • configures sample rate, usually 48000
  • configures stereo or mono capture
  • drives I2S clocks consistently
  • leaves the ADC data visible on ASDOUT

For the first Jetson test, choose a boring format:

Parameter Recommended first value
Sample rate 48000 Hz
Channels 2
Sample format S16_LE or 16-bit samples inside 32-bit I2S slots
Clock role LyraT master, Jetson slave
Data source LyraT onboard microphone path through ES8388 ADC

Expected wire-level clocks for a common stereo 48 kHz setup:

Signal Expected behavior
LRCK 48 kHz
SCLK commonly 1.536 MHz or 3.072 MHz depending on slot width
ASDOUT toggles when microphone signal is active

If LRCK or SCLK is not present, Jetson cannot capture anything.

Reference firmware: lyrat_jp4_passthrough (verified)

A minimal, capture-only ESP-ADF firmware that satisfies all of the requirements above is available in the local esp-adf working tree:

esp-adf/examples/recorder/lyrat_jp4_passthrough/
├── CMakeLists.txt
├── Makefile
├── main/
│   ├── CMakeLists.txt
│   └── lyrat_jp4_passthrough.c
├── partitions_passthrough_example.csv
├── sdkconfig.defaults
└── sdkconfig.defaults.esp32

What it does, in 90 lines of C:

  1. audio_board_init() — uses the in-tree lyrat_v4_3 board config; pin map at components/audio_board/lyrat_v4_3/board_pins_config.c (no overrides needed).
  2. audio_hal_ctrl_codec(... ENCODE, START) — puts ES8388 into ADC mode driving ASDOUT/GPIO35.
  3. Builds an i2s_stream reader at 48 kHz / 16-bit / stereo / Philips, master role.
  4. raw_stream sink + a small drain task discards bytes — the firmware exists only to keep BCLK/LRCK alive and the codec ADC pushing data on the JP4 bus. Jetson is the actual consumer.
  5. Logs rate=N B/s (expected=192000 B/s) every 2 s as a runtime canary.

Why the drain sink matters: ESP-ADF's i2s_stream reader will stall (and stop the I2S clocks) if no one consumes the ringbuffer. A tiny bytes-to-/dev/null task is the simplest way to guarantee the bus stays live.

Upstream tracking: espressif/esp-adf#1607 (feature request to merge this example upstream).

Build (macOS / Linux):

# One-time host prereqs (macOS)
brew install cmake ninja dfu-util
cd esp-adf/esp-idf && ./install.sh esp32

# Build
cd esp-adf/esp-idf && . ./export.sh
export ADF_PATH=$(realpath ..)
cd ../examples/recorder/lyrat_jp4_passthrough
idf.py set-target esp32 build

Flash artifacts produced (offsets fixed by the project's partitions_passthrough_example.csv):

File Offset
build/bootloader/bootloader.bin 0x1000
build/partition_table/partition-table.bin 0x8000
build/lyrat_jp4_passthrough.bin 0x10000

Flash from a Windows host (one USB micro-B cable to LyraT — auto-bootloader works; the CP2102N enumerates as COMx):

pip install esptool
python -m esptool --chip esp32 -p COM3 -b 460800 erase_flash
python -m esptool --chip esp32 -p COM3 -b 460800 write_flash --flash_mode dio --flash_size detect --flash_freq 80m 0x1000 build\bootloader\bootloader.bin 0x8000 build\partition_table\partition-table.bin 0x10000 build\lyrat_jp4_passthrough.bin

Espressif's "Flash Download Tool" GUI works too, but two gotchas have bitten this exact bring-up:

  • Each file row has a checkbox on the left — if unchecked, the row is silently skipped. Symptom: START finishes in milliseconds and the board boots an unrelated firmware (e.g. the factory Bluetooth speaker demo).
  • Stale offsets autofill from previous sessions. If partition-table.bin is missing or its offset is wrong, the bootloader loops with flash_parts: partition 0 invalid magic number 0x... / Failed to verify partition table. Re-typing all three offsets and using EraseAll once recovers it.

esptool from PowerShell makes both classes of mistake impossible — the offsets are explicit on the command line.


10. External capture on Jetson

Once the LyraT is generating clocks and ADC data, route Jetson capture:

amixer -c APE cset name="I2S2 Loopback" "off"
amixer -c APE cset name="I2S2 codec frame mode" "i2s"
amixer -c APE cset name="I2S2 codec master mode" "cbm-cfm"
amixer -c APE cset name="I2S2 Sample Rate" "48000"
amixer -c APE cset name="I2S2 Capture Audio Channels" "2"
amixer -c APE cset name="I2S2 Client Channels" "2"
amixer -c APE cset name="I2S2 Capture Audio Bit Format" "16"
amixer -c APE cset name="I2S2 Client Bit Format" "16"
amixer -c APE cset name="ADMAIF1 Capture Audio Channels" "2"
amixer -c APE cset name="ADMAIF1 Capture Client Channels" "2"
amixer -c APE cset name="ADMAIF1 Mux" "I2S2"

Try a conservative capture:

arecord -D hw:APE,0 -r 48000 -c 2 -f S16_LE lyrat-i2s-capture.wav

Inspect the file:

file lyrat-i2s-capture.wav
aplay lyrat-i2s-capture.wav

If playback is silent, inspect signal level:

sox lyrat-i2s-capture.wav -n stat

Why these settings matter:

  • I2S2 codec frame mode = i2s matches the normal LyraT ES8388 I2S framing
  • I2S2 codec master mode = cbm-cfm means the external board is clock master and Jetson is the slave
  • ADMAIF1 Mux = I2S2 routes external I2S2 receive data into hw:APE,0
  • the channel and bit-format settings keep the I2S CIF side and ADMAIF side aligned

If your LyraT firmware sends 32-bit slots with 16-bit valid microphone samples, try this second variant:

amixer -c APE cset name="I2S2 Capture Audio Bit Format" "32"
amixer -c APE cset name="I2S2 Client Bit Format" "32"
arecord -D hw:APE,0 -r 48000 -c 2 -f S32_LE lyrat-i2s-capture-32.wav

If you intentionally redesign the test so Jetson drives BCLK and LRCK, reverse the clock-role assumption and change:

amixer -c APE cset name="I2S2 codec master mode" "cbs-cfs"

Useful quick checks:

  • silence with no clocks: wiring or LyraT firmware problem
  • silence with clocks: I2S data line, codec gain, or route problem
  • distorted audio: bit depth, slot width, or master/slave mismatch
  • one channel dead: codec input route or channel ordering problem

11. Important limitation: this may need a real ASoC binding

Jetson audio is not just "GPIO plus arecord."

For a robust external I2S capture card, Jetson normally needs:

  • I2S2 pinmux as SFIO
  • an enabled i2s2 controller
  • a sound-card DAI link
  • codec or dummy-codec binding
  • correct master/slave clock configuration
  • correct sample format and TDM/I2S settings

If you only use mixer commands and arecord, you may reach the limit of what the default Jetson sound card exposes. That is normal.

For a production board, use the full path from the audio deep dive:

device tree overlay
  -> codec or dummy-codec node
  -> DAI link for I2S2
  -> AHUB route
  -> ALSA PCM

12. Direct Jetson control of LyraT ES8388

This is the advanced path, not the first bring-up path.

It would look like this:

Jetson I2C -> ES8388 control registers
Jetson I2S2 <-> ES8388 I2S audio
ESP32 side disabled or kept from driving the same bus

Why it is hard:

  • the LyraT was designed for ESP32 to own the codec
  • the ESP32 and ES8388 are already connected
  • Jetson and ESP32 must not both drive I2C/I2S
  • Linux must have a working ES8388 codec driver and DAI binding
  • board-level pull-ups and strap behavior may matter

Use this path only if you intentionally turn the LyraT into a codec breakout board.

For a real Jetson audio product, it is usually cleaner to design or buy a small ES7210, ES8388, TLV320, or similar codec board intended to be controlled by the Jetson.


13. Debug checklist

Jetson checks

arecord -l
aplay -l
amixer -c APE controls | grep I2S2
amixer -c APE controls | grep ADMAIF
dmesg | grep -i -E 'asoc|tegra|i2s|audio'

Wire checks

Use a scope or logic analyzer:

  • LRCK toggles at the expected sample rate
  • SCLK toggles continuously during capture
  • ASDOUT changes when sound reaches the microphone
  • no two devices are driving the same clock line
  • ground is shared

Format checks

Try common capture variants:

arecord -D hw:APE,0 -r 48000 -c 1 -f S16_LE test-mono.wav
arecord -D hw:APE,0 -r 48000 -c 2 -f S16_LE test-stereo.wav
arecord -D hw:APE,0 -r 48000 -c 2 -f S32_LE test-stereo-32.wav

Use the one that matches the LyraT firmware's I2S slot format.


14. What success looks like

Minimum success:

  • LyraT outputs stable I2S clocks
  • Jetson I2S2 is enabled on the 40-pin header
  • ADMAIF1 routes from I2S2
  • arecord creates a non-silent WAV file
  • channel count and sample rate are correct

Better success:

  • waveforms show clean clocks and data
  • spoken audio is intelligible
  • gain is not clipped
  • left/right channels are understood
  • recording works repeatedly after reboot

Product-level success:

  • proper codec or ADC driver
  • stable device-tree overlay
  • reproducible ALSA route setup
  • GStreamer pipeline
  • application-level audio health checks
  • wake-word or ASR pipeline using the captured audio

Verified test result — LyraT side (2026-05-11)

Flashed lyrat_jp4_passthrough.bin (built from esp-adf release/v2.x, ESP-IDF v5.5.3) to an ESP32-LyraT V4.3. Boot log confirms the codec/I2S path is live and DMA is moving real data:

I (172) boot: Loaded app from partition at offset 0x10000
I (197) app_init: Project name:     lyrat_jp4_passthrough
I (215) app_init: ESP-IDF:          v5.5.3
I (298) JP4_PASSTHROUGH: [1] Init audio board (LyraT v4.3) + ES8388 codec
I (329) JP4_PASSTHROUGH:     codec mode=ENCODE (ADC), input=LINPUT1/RINPUT1 (onboard mic)
I (334) JP4_PASSTHROUGH: [3] I2S: rate=48000 Hz, bits=16, channels=2, format=Philips, role=master
I (340) JP4_PASSTHROUGH:     JP4 pins: BCLK=GPIO5  LRCK=GPIO25  ASDOUT=GPIO35  MCLK=GPIO0
I (349) JP4_PASSTHROUGH: [4] Capture started — JP4 I2S bus is live. Samples drained on-chip.
I (2381) JP4_PASSTHROUGH: rate=194560 B/s (expected=192000 B/s)  total=389120 B
I (4389) JP4_PASSTHROUGH: rate=192512 B/s (expected=192000 B/s)  total=774144 B
I (6411) JP4_PASSTHROUGH: rate=194560 B/s (expected=192000 B/s)  total=1163264 B

What this confirms about the LyraT side of the bring-up plan (Section 7, steps 1–4):

  • ✅ ES8388 enumerated and put in ENCODE/ADC mode
  • ✅ Onboard mics (LINPUT1/RINPUT1) selected
  • ✅ I2S peripheral running as master, BCLK/LRCK clocking on JP4
  • ✅ DMA is moving 192 ± 1.3 % kB/s = 48000 Hz × 2 ch × 2 B worth of samples per second (the small overshoot is xTaskGetTickCount() granularity in the 2 s reporting window, not clock drift)

The Jetson side (steps 5–7) is verified separately below — see §14 verified result — Jetson side.

The LyraT-side artifact is a known-good control: if Jetson capture ever regresses after this firmware is flashed, the failure is on the Jetson software path (pinmux, ASoC routing, master/slave config), not the bus.

Verified test result — Jetson side (2026-05-11)

After the LyraT bring-up above, all four JP4 jumpers wired to the Jetson Orin Nano 40-pin header (SCLK→12, LRCK→35, ASDOUT→38, GND→6) on a single shared ground. No JetPack rebuild, no out-of-tree driver — only Jetson-IO overlay + amixer route.

Step 1 — confirm overlay applied

amixer -c APE controls | grep I2S2          # expect ~19 I2S2 controls present
amixer -c APE cget name="ADMAIF1 Mux"       # expect 'I2S2' in items list
arecord -l                                  # expect "card 1: APE ... device 0: XBAR-ADMAIF1-0"

If I2S2 Mux, I2S2 codec frame mode, and I2S2 codec master mode are not in the control list, the 40-pin overlay didn't take — re-run sudo /opt/nvidia/jetson-io/jetson-io.py, save, reboot.

Step 2 — set the AHUB route and I2S format

amixer -c APE cset name="ADMAIF1 Mux" "I2S2"
amixer -c APE cset name="I2S2 codec master mode" "cbm-cfm"     # LyraT drives BCLK + LRCK
amixer -c APE cset name="I2S2 codec frame mode" "i2s"          # Philips I2S framing
amixer -c APE cset name="I2S2 Sample Rate" 48000
amixer -c APE cset name="I2S2 Capture Audio Channels" 2
amixer -c APE cset name="I2S2 Capture Audio Bit Format" 16
amixer -c APE cset name="I2S2 Client Channels" 2
amixer -c APE cset name="I2S2 Client Bit Format" 16
amixer -c APE cset name="ADMAIF1 Capture Audio Channels" 2
amixer -c APE cset name="ADMAIF1 Capture Client Channels" 2

Why each control:

  • cbm-cfm = "codec-bit-master, codec-frame-master" → Jetson is the I2S slave; LyraT drives both clocks. Matches the firmware's I2S_ROLE_MASTER.
  • i2s framing = standard Philips I2S. Matches the ES8388 / our I2S_COMM_FORMAT_STAND_I2S.
  • 48000 / 2 ch / 16-bit everywhere → matches the firmware's SAMPLE_RATE_HZ / SAMPLE_CHANNELS / SAMPLE_BITS. Mismatch on any side here is the most common cause of distortion or silence.

Step 3 — record and inspect

arecord -D hw:APE,0 -r 48000 -c 2 -f S16_LE -d 5 lyrat.wav
ls -la lyrat.wav
sox lyrat.wav -n stat
aplay lyrat.wav

Observed result

-rw-r--r-- 1 aihpc aihpc 960044 May 10 20:14 lyrat.wav

Samples read:            480000
Length (seconds):      5.000000
Maximum amplitude:     0.233582
Minimum amplitude:    -0.178314
Midline amplitude:     0.027634
Mean    amplitude:    -0.000075
RMS     amplitude:     0.021088
Rough   frequency:         1356
Volume adjustment:        4.281

Speech ("one two three test") was clearly audible on aplay — bit-accurate capture.

What each number proves:

  • File size 960,044 bytes is exact: 5.000 s × 48000 Hz × 2 ch × 2 B + 44 B WAV header = 960044. Zero DMA underruns; LyraT-master / Jetson-slave clocking is rock solid.
  • RMS amplitude 0.021 ≈ –33 dBFS — normal speech range, well above noise floor.
  • Mean amplitude -0.000075 ≈ 0 — no DC offset; ES8388 input biasing is clean.
  • Volume adjustment 4.281 → ~12 dB of headroom before clipping. If louder capture is desired, bump ES8388 PGA gain in firmware (es8388_set_mic_gain()) rather than amplifying digitally.
  • Rough frequency 1356 — dominant pitch in the speech band; consistent with voice content.

Bring-up plan §7 — final state

  • ✅ Step 5: 40-pin I2S2 pinmux applied (via Jetson-IO overlay)
  • ✅ Step 6: AHUB route from I2S2ADMAIF1 configured; clock direction set to Jetson-slave
  • ✅ Step 7: arecord -D hw:APE,0 produces a bit-accurate, byte-exact, audible WAV

Known gap — settings do not survive reboot

The 9 amixer cset commands are not persistent. alsactl store captures most of them but the NVIDIA APE card's Mux / codec master mode controls have historically not been restored cleanly by alsa-restore.service. For a reproducible bring-up across reboots, wrap the commands in a small systemd unit that runs after sound.target (or after the Jetson-IO overlay loads), or save with alsactl store -f /var/lib/alsa/asound.state and verify with alsactl restore before relying on it.

Next steps anchored to this verified baseline

Now that capture is bit-accurate, the next layers (Section 15) are unblocked:

  • GStreamer alsasrc to replace arecord and fan-out to multiple consumers
  • Streaming ASR (whisper.cpp with CUDA on Orin) consuming the alsasrc tap
  • Two-mic delay-and-sum beamforming, taking advantage of LyraT's L+R mics (physical spacing measured 3.5 cm — note this is below Espressif's recommended 4–6.5 cm and the default MASE_MIC_DISTANCE = 65 mm in esp-sr/include/.../esp_mase.h, so any use of Espressif's esp_afe_doa / MASE APIs needs mic_distance explicitly overridden to 0.035)
  • Eventually, replacing the LyraT with a Jetson-controlled 4-mic codec board (e.g. ES7210) for a real product

15. Next application steps

Once the raw capture path works:

  • convert the arecord path into a GStreamer capture pipeline
  • add WebRTC VAD or wake-word detection
  • test local ASR such as Whisper or a smaller streaming ASR model
  • replace LyraT with a dedicated Jetson-controlled codec board
  • design a real microphone frontend for the smart-speaker product path

Example GStreamer capture starting point:

gst-launch-1.0 alsasrc device=hw:APE,0 ! \
  audio/x-raw,rate=48000,channels=2,format=S16LE ! \
  wavenc ! filesink location=lyrat-i2s-gst.wav

16. References