# i2s_mic Wi-Fi audio sender Captures audio with `i2s_mic` and streams it over Wi-Fi, as UDP datagrams sent with the `udp_link` component, to one statically configured IPv4 address. This is the sending half of a Wi-Fi walkie-talkie. The receiver is either the `i2s_spk` Wi-Fi example (plays the audio on a speaker) or `tools/udp_audio_rx.py` on a PC (plays or records it). A later version makes both boards send and receive. ``` INMP441 ──I2S──▶ ESP32 ──Wi-Fi / UDP (udp_link)──▶ receiver board or PC ``` --- ## How it works Every 30 ms: 1. `i2s_mic_read()` returns the next DMA buffer: 480 stereo frames of 32-bit samples. 2. One slot of each frame is kept and shifted to 16-bit: 480 mono samples, 960 bytes. 3. `udp_link_send()` sends those 960 bytes as one UDP datagram (about 33 per second, 256 kbit/s of audio). udp_link adds a header with a sequence number, so the receiver can detect lost or reordered packets. The audio task never waits for the network. If `udp_link_send()` reports no buffer or no route, for example during a Wi-Fi reconnect, that frame is dropped and counted, and capture continues. Late audio is useless for live speech, so frames are never resent. ### Stream format `main/audio_stream_format.h` is the contract between sender and receiver: | Property | Value | |---|---| | Sample rate | 16 kHz | | Samples | signed 16-bit, little-endian, mono | | Frame | 480 samples = 30 ms = 960 bytes, one frame per datagram | | Payload header | none (udp_link's own header carries sequence and session) | | udp_link stream_id | `0xA1` (identifies this format) | | UDP port | 5004 by default, same on both sides | The receiver uses an identical copy of this header. If the format ever changes, change `AUDIO_STREAM_ID` too, so mismatched builds reject each other's packets instead of playing noise. --- ## Dependencies | Component | Where it comes from | |---|---| | `i2s_mic` | this repository (`EXTRA_COMPONENT_DIRS ../../`) | | `udp_link` | local path in `main/idf_component.yml`; set it to your checkout | | `protocol_examples_common` | ESP-IDF's `examples/common_components` (`example_connect()`) | **Set the udp_link path before building.** Open `main/idf_component.yml` and point `udp_link: path:` at your udp_link component folder (relative to that file). Once udp_link is published, replace the entry with the registry version, e.g. `embedblocks/udp_link: "^0.1.0"`. --- ## Wiring | INMP441 | GPIO (Kconfig) | ESP32 | ESP32-C3 | ESP32-S3 | |---|---|---|---|---| | SCK | `AUDIO_TX_MIC_GPIO_BCK` | 16 | 7 | 4 | | WS | `AUDIO_TX_MIC_GPIO_WS` | 17 | 8 | 5 | | SD | `AUDIO_TX_MIC_GPIO_DATA` | 18 | 9 | 6 | | L/R | GND | | | | | VDD | 3.3V | | | | | GND | GND | | | | On ESP32 WROVER modules, GPIO 16/17 are used by PSRAM; pick other pins there. --- ## Configure ```bash idf.py set-target esp32 # or esp32c3, esp32s3 idf.py menuconfig ``` - **Example Connection Configuration** → WiFi SSID and WiFi Password. - **i2s_mic Wi-Fi audio sender** → Receiver IPv4 address (the speaker board or your PC), UDP port (default 5004, must match the receiver), mic pins. Wi-Fi connection type is preset to Wi-Fi only, IPv4 only (`sdkconfig.defaults`). --- ## Build, flash, check ```bash idf.py build flash monitor ``` ``` I (...) example_connect: Connected to example_netif_sta I (...) audio_tx: sending to 192.168.1.100:5004, stream 0xA1, 960-byte frames (30 ms of 16000 Hz 16-bit mono) I (...) audio_tx: sent 66 frames (33/s, 253 kbit/s) | dropped 0 | send errors 0 | mic lost 0 ``` On a healthy link: about **33 frames/s**, and **dropped**, **send errors** and **mic lost** all stay at **0**. | Counter | Rising means | |---|---| | dropped | udp_link had no buffer or no route; the network is momentarily unavailable | | send errors | an unexpected udp_link error, logged with its name | | mic lost | the audio task fell more than ~150 ms behind; capture data was lost before sending | --- ## Test without a receiver board `tools/udp_audio_rx.py` receives the stream on a PC: ```bash python tools/udp_audio_rx.py --wav out.wav # record pip install sounddevice python tools/udp_audio_rx.py --play # listen live ``` Set the board's receiver address to the PC's IP and allow UDP port 5004 through the PC firewall. The script removes udp_link's 12-byte header and skips any datagram that isn't exactly one frame. It assumes the header comes before the payload and does not check the header fields. --- ## Not in this version - One direction only; the full-duplex walkie-talkie comes later. - No jitter buffer or packet-loss concealment: that belongs on the receiving side. - No push-to-talk: audio streams continuously. - Static peer address; no discovery.
To create a project from this example, run:
idf.py create-project-from-example "embedblocks/i2s_mic=0.2.1:wifi_audio_tx"