yahboom
HomeLearn
Solution
Blog
Online Store
About Us
EN/中文
ESP32 Core Board📖
1. ESP32 Introduction
  • 1. ESP32S3 Overview
  • 2. Environment Setup Introduction
  • 3. Dev Board Introduction
2. ESP-IDF Development Tutorial
1. Before Development
  • 1. Environment Setup (Windows)
  • 2. Environment Setup (Linux)
  • 3. Build and Flash
2. Basic Peripherals
  • 1. LED Control
  • 2. Button Detection
  • 3. Peripheral Interrupts
  • 4. ESP_TIMER
  • 5. GPTIMER
  • 6. Watchdog Timer
  • 7. DMA
  • 8. ADC
  • 9. PWM Breathing LED
  • 10. RGB LED Effects
3. Serial Communication and Storage System
  • 1. Serial UART Communication Experiment
  • 2. I2C_OLED Display Experiment
  • 3. SPI Bus Communication Experiment
  • 4. PSRAM External Memory Experiment
  • 5. Flash Read and Write Experiment
  • 6. USB Emulated Mass Storage Experiment
4. FreeRTOS operating system
  • 1.FreeRTOS Core Concepts Theory
  • 2.Task Creation and Management
  • 3.Queue
  • 4.Binary Semaphore
  • 5.Mutex
  • 6.Event Groups
  • 7.Software Timer
  • 8.Task Scheduler
  • 9.Low Power Mode
  • 10.Deadlock
  • 11.Priority Inversion
5. Network Protocols and Wireless Communication
  • 1. WIFI Network Basics
  • 2. WIFI STA Mode
  • 3. WIFI AP Mode
  • 4. UDP Client Experiment
  • 5. UDP Server Experiment
  • 6. TCP Client Experiment
  • 7. TCP Server Experiment
  • 8. Socket Programming Basics
  • 9. HTTP Client Experiment
  • 10. HTTP Server Experiment
  • 11. MQTT IoT Communication Protocol
  • 12. WIFI OTA Upgrade
  • 13. Bluetooth Fundamentals
  • 14. Bluetooth Slave Mode
  • 15. Bluetooth Master Mode
6. Audio Applications
  • 1.I2S Microphone Recording and U-Disk Playback
  • 2.I2S Audio Playback Experiment
  • 3.Repeater Experiment
7. Xiaozhi AI Voice
  • 1. Burn Xiaozhi Firmware
  • 2. Network and Configuration
  • 3. AI Agent Settings
  • 4. Program Analysis
  • 5. Free Chat
3. Arduino Development Course
1. Before Development
  • 1. Environment Setup
  • 2. Build and Flash
2. Basic Peripherals
  • 1. LED Control
  • 2. Button Detection
  • 3. Peripheral Interrupts
  • 4. ESP_TIMER
  • 5. GPTIMER
  • 6. Watchdog Timer
  • 7. ADC
  • 8. PWM Breathing LED
  • 9. RGB LED Effects
3. Serial Communication and Storage System
  • 1. Serial UART Communication
  • 2. I2C OLED Display
  • 3. SPI Bus Communication
  • 4. Flash Read and Write
  • 5. USB Emulated Mass Storage
4. Network Protocols and Wireless Communication
  • 1. WIFI Network Basics
  • 2. WIFI STA Mode
  • 3. WIFI AP Mode
  • 4. UDP Server Experiment
  • 5. UDP Client Experiment
  • 6. TCP Client Experiment
  • 7. TCP Server Experiment
  • 8. HTTP Client Experiment
  • 9. HTTP Server Experiment
  • 10. MQTT IoT Communication Protocol
  • 11. WIFI OTA Upgrade
  • 12. Bluetooth Fundamentals
  • 13. Bluetooth Slave Mode
  • 14. Bluetooth Master Mode
5. Audio Applications
  • 1.I2S Microphone Recording and U-Disk Playback
  • 2.I2S Audio Playback Experiment
  • 3.Repeater Experiment
4. MicroPython Development Course
1. Before Development
  • 1. Environment Setup
  • 2. Build and Flash
  • 3. Thonny IDE
2. Basic Peripherals
  • 1. LED Control
  • 2. Button Detection
  • 3. Peripheral Interrupts
  • 4. TIMER_IT
  • 5. Watchdog Timer
  • 6. ADC
  • 7. PWM Breathing LED
  • 8. RGB LED Effects
3. Serial Communication and Storage System
  • 1. Serial UART Communication Experiment
  • 2. I2C_OLED Display Experiment
  • 3. SPI Bus Communication Experiment
  • 4. Flash Read and Write Experiment
  • 5. Print Storage
4. Network Protocols and Wireless Communication
  • 1. WIFI Network Basics
  • 2. WIFI STA Mode
  • 3. WIFI AP Mode
  • 4. UDP Client Experiment
  • 5. UDP Server Experiment
  • 6. TCP Client Experiment
  • 7. TCP Server Experiment
  • 8. HTTP Client Experiment
  • 9. HTTP Server Experiment
  • 10. MQTT
  • 11. Bluetooth Fundamentals
  • 12. Bluetooth Slave Mode
  • 13. Bluetooth Master Mode
5. Audio Applications
  • 1. I2S Audio Playback Experiment
  • 2. Repeater Experiment
Download
  • Code
  • Hardware_Info
  • 3D_Model_File
  • Software_Tools
  • VMware
  • VM_File
Download ZIP

Welcome to ESP32 Core Board repository

1.I2S Microphone Recording and U-Disk Playback

I2S Microphone Recording and U-Disk Playback (Requires Two Type-C Cables)

I2S Microphone Recording and U-Disk Playback (Requires Two Type-C Cables)1. Overview1.1 Experiment Objective2. Core Concepts2.1 USB MSC Protocol2.2 PSRAM FAT16 Virtual Disk2.3 ES8311 Codec ADC Path2.4 WAV File Format2.5 Button Debounce State Machine3. Hardware Features and Dependencies3.1 ESP32-S3 USB OTG Features3.2 ES8311 Audio Codec Specifications3.3 Pin Assignment Table3.4 Arduino IDE Configuration3.5 Required Arduino Libraries4. Program Flow4.1 Overall Flowchart4.2 Audio System Initialization Flow (audioSystemInit)4.3 Recording State Machine4.4 PC Interaction After USB Connection5. Source Code Analysis5.1 Directory Structure5.2 Main Program: Arduino_Recorder_USB_MSC.ino5.2.1 File Header and Includes5.2.2 Constants and Global Variables5.2.3 MSC Callback Functions5.2.4 setup() -- System Initialization5.2.5 loop() -- Main Loop5.3 Audio System: AudioSystem.h / AudioSystem.cpp5.3.1 Audio Parameters and Initialization5.3.2 I2C Bus Initialization and Scan5.3.3 ES8311 Hardware Reset Sequence (es8311HardReset)5.3.4 ADC Path Start (es8311StartADC)5.3.5 Mic Gain Setting5.4 FAT16 Virtual Disk: Fat16Disk.h / Fat16Disk.cpp5.4.1 Class Interface5.4.2 begin -- Create FAT16 Virtual Disk5.4.3 resetForRecord -- Prepare for Recording5.4.4 publishFile -- Register Data as File5.5 Recorder: Recorder.h / Recorder.cpp5.5.1 Class Interface5.5.2 WAV Header Template5.5.3 start -- Begin Recording5.5.4 tick -- Main Loop Recording5.5.5 stop -- Stop Recording5.6 Button: Button.h / Button.cpp5.6.1 Complete tick() Implementation5.7 LED: Led.h / Led.cpp5.7.1 Class Interface5.7.2 Rainbow Effect6. Operation Flow6.1 IDE Configuration6.2 Compile and Flash6.3 Operation Steps6.4 Long Press Force Stop7. FAQ7.1 Version Compatibility Quick ReferenceAppendix A: Complete Code IndexAppendix B: ES8311 Register Quick Reference


1. Overview

1.1 Experiment Objective

This experiment implements a ESP32-S3-based I2S microphone recorder that writes recording data directly to a FAT16 virtual disk constructed in PSRAM, then simulates the ESP32-S3 as a USB flash drive (U disk) through the USB MSC (Mass Storage Class) protocol. When the user connects the computer via USB cable, the PC recognizes a FAT16-formatted U disk containing a playable .WAV audio file.

Complete Signal Chain

[MIC] --> [ES8311 ADC] --> [I2S DIN] --> [ESP32-S3 I2S RX]
                                            |
                                      [PSRAM FAT16 Disk]
                                            |
                               [USB MSC] --> [PC recognizes as USB drive]

2. Core Concepts

2.1 USB MSC Protocol

USB MSC is a USB device class protocol that allows embedded devices to simulate standard storage devices (U disk, external hard drive, etc.). The PC communicates with the device using the standard SCSI command set.

SCSI Command Mapping for This Project:

SCSI CommandCallbackThis Project Implementation
READ(10)onRead(lba, offset, buf, size)Copy data from PSRAM disk image via memcpy
WRITE(10)onWrite(lba, offset, buf, size)Copy data to PSRAM disk image via memcpy
START STOP UNITonStartStop(power, start, eject)Returns true

MSC devices don't need real Flash chips - just build a FAT image in memory and expose it to the PC via onRead/onWrite callbacks.

2.2 PSRAM FAT16 Virtual Disk

This experiment builds a FAT16 filesystem image (8MB / 16384 sectors) in ESP32-S3 PSRAM. Layout: boot(1) + FAT(64) + root(1) + data(16318). During recording, I2S data is written directly to data sectors, exposed to PC via USB MSC callbacks - no external TF card or SPI Flash required.

FAT16 is chosen over FAT32 because the FAT table is smaller, saving PSRAM; the root directory is fixed at 1 sector (16 directory entries), and this project only needs 1 WAV file, which is sufficient.

2.3 ES8311 Codec ADC Path

The ES8311 is a low-power audio codec from Sinx. This experiment uses only its ADC recording path:

xxxxxxxxxx
[MIC analog signal] --> [ES8311 PGA programmable gain amplifier]
                           |
                      [Sigma-Delta ADC modulator]
                           |
                      [Digital decimation filter]
                           |
                      [I2S serial output SDOUT] --> [ESP32-S3 I2S DIN]

Clock Tree: MCLK 4.096MHz → pre_div=1 → ADC_MOD_CLK=4.096MHz → adc_osr=256 → Fs=16kHz. BCLK = 4.096MHz/4 = 1.024MHz, WS period 64 BCLK, per channel 32 BCLK (16bit data + 16bit padding), I2S rate 64KB/s.

Why choose 16kHz sample rate?

  • Voice frequency coverage: 16kHz sample rate can capture 8kHz frequency (main voice energy is 300-3400Hz)

  • Storage efficiency: 16kHz/16bit/mono = 32KB/s, 120 seconds ~ 3.84MB, 8MB PSRAM is more than enough

  • WAV format compatibility: All players support 16kHz sample rate

2.4 WAV File Format

WAV is based on RIFF specification, 44-byte header + PCM data. This project's WAV header definition:

xxxxxxxxxx
#pragma pack(push, 1)     // 1-byte alignment, no padding
struct WavHdr {
    char     riff[4];     // "RIFF"
    uint32_t fileLen;     // Total file length - 8
    char     wave[4];     // "WAVE"
    char     fmt[4];      // "fmt "
    uint32_t fmtLen;      // 16
    uint16_t audioFormat; // 1 = PCM
    uint16_t channels;    // 1 = mono
    uint32_t sampleRate;  // 16000
    uint32_t byteRate;    // 16000 x 1 x 2 = 32000
    uint16_t blockAlign;  // 1 x 2 = 2
    uint16_t bitsPerSample; // 16
    char     data[4];     // "data"
    uint32_t dataLen;     // Total PCM data bytes
};
#pragma pack(pop)         // Restore default alignment

Key Technique: Before recording starts, write a "skeleton" WAV header (fileLen=0, dataLen=0). After recording ends, backfill the real file length and data length. This ensures the PC always sees a correct WAV file header during MSC callbacks.

2.5 Button Debounce State Machine

Button FSM handles mechanical bounce (5-30ms) and distinguishes short/long presses. IDLE → DEBOUNCE(30ms) → PRESS: release <800ms = short press, ≥800ms or held to threshold = long press (triggers early, doesn't wait for release).


3. Hardware Features and Dependencies

3.1 ESP32-S3 USB OTG Features

FeatureParameter
USB ControllerUSB 2.0 OTG Full-Speed (12Mbps)
Built-in PHYYes (no external USB transceiver needed)
USB PinsGPIO19 (D-), GPIO20 (D+)
TinyUSB SupportBuilt-in (integrated by default in ESP32 Arduino Core v2.0+)
Power ModeBus-powered, max 500mA

ESP32-S3's USB OTG is an independent peripheral, separate from the UART download port. When USB MSC mode is enabled, USB CDC (Serial) cannot be used simultaneously - debugging must use UART0 (Serial0).

3.2 ES8311 Audio Codec Specifications

FeatureParameter
ADC SNR93dBA
ADC Sample Rate8kHz ~ 96kHz
Supported FormatsI2S, Left-Justified, Right-Justified, DSP/PCM
Microphone Gain0/6/12/18/24/30/36/42 dB (8 programmable levels)
Operating Voltage1.8V ~ 3.3V (analog), 1.8V (digital)
I2C Control Interface7-bit address 0x18 (AD0=0, AD1=0)
PackageQFN-32 (4x4mm)

3.3 Pin Assignment Table

FunctionESP32-S3 GPIODirectionDescription
I2C SCLGPIO 42OutputES8311 register configuration clock (100kHz)
I2C SDAGPIO 41BidirectionalES8311 register configuration data
I2S MCLKGPIO 15OutputMaster clock 4.096MHz -> ES8311
I2S BCLKGPIO 16OutputI2S bit clock 1.024MHz
I2S WS (LRCLK)GPIO 18OutputI2S word select/LR channel 16kHz
I2S DOUTGPIO 8OutputI2S data output (DAC playback, not used in this experiment)
I2S DINGPIO 17InputI2S data input (ADC recording)
PA_ENGPIO 7OutputES8311 enable (HIGH = power on/active)
BOOT ButtonGPIO 0Input (pull-up)User operation button (active low)
WS2812 LEDGPIO 48OutputStatus indicator RGB LED
USB D-GPIO 19BidirectionalUSB OTG differential data negative
USB D+GPIO 20BidirectionalUSB OTG differential data positive

3.4 Arduino IDE Configuration

This is the most critical configuration step!

ConfigurationValueDescription
BoardESP32S3 Dev Module 
USB ModeTinyUSBMSC depends on TinyUSB protocol stack
USB CDC On BootDisabledUSB OTG occupied by MSC, CDC cannot be used simultaneously
PSRAMOPI PSRAMEnable 8MB/16MB Octal PSRAM
Flash Size16MBBased on onboard Flash
Partition SchemeDefault 4MB with spiffs 
Upload ModeUART0 / Hardware CDCUse onboard USB-UART for download

Why must USB CDC On Boot be disabled?

ESP32-S3 has only one USB OTG peripheral (GPIO19/20) - USB Mode = TinyUSB hands OTG to TinyUSB protocol stack (required for MSC). At this point Serial is unavailable, debugging must go through onboard USB-UART bridge chip's Serial0.

3.5 Required Arduino Libraries

LibraryVersionPurposeReference Tutorial
ESP32 Arduino Corev3.3.8+Basic framework, built-in TinyUSB + SPIFFS + Wire + ESP_I2SArduino → 1. Before Development → 1. Before Development → Environment Setup (Arduino-ESP32)
Adafruit NeoPixel^1.12.0WS2812 RGB LED driver2. Basic Peripherals → 9.RGB LED Experiment → RGB LED Experiment
USB/USBMSC (built-in)—TinyUSB USB MSC simulated U disk3. Serial Communication and Storage System → 5.USB Mass Storage Simulation → USB Mass Storage

4. Program Flow

4.1 Overall Flowchart

​x
setup():
  1. Serial0.begin(115200) → Debug serial
  2. led.begin() → WS2812 LED
  3. audioSystemInit() → ES8311 + I2S audio chain
  4. disk.begin(16384) → PSRAM FAT16 virtual disk
  5. msc.onRead/onWrite/onStartStop registration → USB MSC callbacks
  6. USB.begin() → USB OTG MSC device stack
  7. btn.begin(0) → BOOT button (long press 800ms/debounce 30ms)

loop():
  rec.tick() → I2S read → PSRAM write
  btn.tick() → SHORT? Start/stop recording : LONG? Force stop

4.2 Audio System Initialization Flow (audioSystemInit)

  1. codecPowerOn() → GPIO7 HIGH, ES8311 power on

  2. i2cBusInit() + i2cBusScan() → I2C bus + scan confirm 0x18

  3. I2S.setPins() + I2S.begin() + I2S.configureRX() → Start MCLK (4.096MHz) + RX stereo→mono

  4. es8311HardReset() + es8311InitADC() → Clock tree + ADC path + Mic gain (24dB)

Step 3 must come before step 4: ES8311 initialization requires MCLK signal as clock source.

4.3 Recording State Machine

xxxxxxxxxx
IDLE (LED OFF) --short press--> RECORDING (LED GREEN, I2S→PSRAM) --short press/full--> SAVING (LED RED, patch WAV header) --> READY (LED RAINBOW, file readable)

4.4 PC Interaction After USB Connection

USB enumeration → INQUIRY/READ_CAPACITY → PC reads boot sector+FAT+root directory → Recognize RECORD.WAV → User double-click to play → onRead() copies data from PSRAM.


5. Source Code Analysis

Full source code: Source code → Arduino → 4.Audio → Arduino_Recorder_USB_MSC → Arduino_Recorder_USB_MSC.ino

5.1 Directory Structure

Arduino_Recorder_USB_MSC.ino (main program) / AudioSystem (I2C+I2S+ES8311) / Recorder (WAV+I2S read) / Fat16Disk (FAT16 virtual disk) / Button (debounce state machine) / Led (WS2812)

5.2 Main Program: Arduino_Recorder_USB_MSC.ino

5.2.1 File Header and Includes

The main program includes 6 header files, covering all subsystems: audio system, FAT16 virtual disk, USB MSC, button, and LED.

xxxxxxxxxx
#include "USB.h"          // TinyUSB device framework
#include "USBMSC.h"       // MSC class driver
#include "AudioSystem.h"   // Audio system (I2C + I2S + ES8311)
#include "Fat16Disk.h"     // PSRAM FAT16 virtual disk
#include "Button.h"        // Debounced button
#include "Led.h"           // WS2812 RGB LED
#include "Recorder.h"      // Recorder state machine

5.2.2 Constants and Global Variables

Global constants define disk size (8MB / 16384 sectors) and max recording duration (120s). Global variables hold instances of each subsystem. The Recorder constructor lambda simulates USB drive re-plug after recording completes to notify PC to refresh file list.

xxxxxxxxxx
/* ===== Constants ===== */
#define DISK_SECTORS  16384   // 8MB (16384 x 512B)
#define REC_SEC       120     // Max record seconds
#define PIN_LED       48      // WS2812 data pin

/* ===== Globals ===== */
static Fat16Disk disk;        // FAT16 virtual disk instance
static USBMSC    msc;         // USB MSC device instance
static Button    btn;         // Button instance
static Led       led(PIN_LED); // LED instance (GPIO48)
static Recorder  rec(disk, led, REC_SEC, []() {
    msc.mediaPresent(false); delay(80); msc.mediaPresent(true);
    // Simulate USB drive re-plug to notify PC to refresh file list
});

5.2.3 MSC Callback Functions

The three callbacks are the core of MSC U disk simulation. PC calls onRead() when reading sectors (memcpy from PSRAM); onWrite() when writing (memcpy back); onStartStop accepts all mount/eject operations.

xxxxxxxxxx
/* ===== MSC Callbacks ===== */
static int32_t onRead(uint32_t lba, uint32_t offset, void *buf, uint32_t size) {
    // lba=sector number, offset=byte offset within sector
    // buf=PC read destination, size=bytes to read

    // Bounds check
    if (!disk.ready() || lba >= disk.sectorCount()) return 0;
    // Copy data from PSRAM to PC buffer
    memcpy(buf, disk.data() + (size_t)lba * 512 + offset, size);
    return size;  // Return bytes transferred
}

static int32_t onWrite(uint32_t lba, uint32_t offset, uint8_t *buf, uint32_t size) {
    // Parameters symmetric to onRead, buf is data from PC

    // Bounds check
    if (!disk.ready() || lba >= disk.sectorCount()) return 0;
    // Copy PC data to PSRAM
    memcpy(disk.data() + (size_t)lba * 512 + offset, buf, size);
    return size;
}

static bool onStartStop(uint8_t power, bool start, bool eject) {
    // start=load media, eject=eject media
    return true;  // Always accept
}

5.2.4 setup() -- System Initialization

setup() initializes 7 subsystems in strict order: serial→LED→audio→virtual disk→MSC→USB→button. Each step has clear dependencies - audio must be ready before disk creation (PSRAM available), MSC callbacks must be registered before USB starts.

xxxxxxxxxx
void setup() {
    // 1. Debug serial (UART0, NOT USB CDC)
    Serial0.begin(115200); delay(300);
    Serial0.println("\n=== Rec+USB MSC ===");

    // 2. LED init
    led.begin();

    // 3. Audio system init
    audioSystemInit();

    // 4. Create FAT16 virtual disk (8MB, label="AUDIODISK")
    if (!disk.begin(DISK_SECTORS, "AUDIODISK")) {
        Serial0.println("[FATAL] No PSRAM"); while (1);
    }

    // 5. Configure MSC device info & callbacks
    msc.vendorID("ESP32");   msc.productID("Recorder");   msc.productRevision("1.0");
    msc.onStartStop(onStartStop); msc.onRead(onRead); msc.onWrite(onWrite);
    msc.mediaPresent(true);  // Declare media present
    msc.begin(DISK_SECTORS, 512);  // Configure sector count & size

    // 6. Start USB device stack
    USB.begin();

    // 7. Print hint + button init
    Serial0.printf("[USB] FAT16 %luKB MSC *Plug USB cable*\n", (unsigned long)DISK_SECTORS / 2);
    btn.begin(PIN_BUTTON);  // GPIO0, long press 800ms / debounce 30ms
}

5.2.5 loop() -- Main Loop

loop() is extremely concise - each frame drives recorder tick() (returns immediately if not recording) and button state machine, dispatching start/stop based on events. All complex logic is encapsulated in modules, main program only routes.

xxxxxxxxxx
void loop() {
    rec.tick();                    // 1. Recorder tick (returns immediately if not recording)

    Button::Event e = btn.tick();  // 2. Button FSM tick
    if (e == Button::SHORT) {      // 3. Short press: start/stop recording
        if (rec.busy()) rec.stop(); else rec.start();
    } else if (e == Button::LONG) { // 4. Long press: force stop recording
        if (rec.busy()) rec.stop();
    }
}

5.3 Audio System: AudioSystem.h / AudioSystem.cpp

5.3.1 Audio Parameters and Initialization

audioSystemInit() drives the entire recording chain in sequence: power on → I2C → I2S → ES8311 reset → ADC init. I2S must start before ES8311 (ES8311 is slave, depends on ESP32 for MCLK). I2S.configureRX() enables STEREO_TO_MONO hardware conversion, automatically averaging left/right channels - no software processing needed.

xxxxxxxxxx
#define SAMPLE_RATE     16000  // Sample rate
#define MCLK_MULTIPLE   256    // MCLK = Fs x 256 = 4.096MHz
#define MIC_GAIN_DB     24     // Microphone gain

void audioSystemInit() {
    Serial0.println("===== Audio System (ADC Only) =====");

    // 1. Power on codec
    codecPowerOn();

    // 2 & 3. I2C bus init + device scan
    i2cBusInit();  i2cBusScan();

    // 4, 5 & 6. I2S start, provides MCLK to ES8311
    I2S.setPins(PIN_I2S_BCLK, PIN_I2S_WS, PIN_I2S_DOUT, PIN_I2S_DIN, PIN_I2S_MCLK);
    if (!I2S.begin(I2S_MODE_STD, SAMPLE_RATE, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO)) {
        Serial0.println("[I2S] ERROR");
        return;
    }
    delay(100);
    I2S.configureRX(SAMPLE_RATE, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO,
                    I2S_RX_TRANSFORM_16_STEREO_TO_MONO);  // Stereo to mono conversion

    // 7 & 8. ES8311 hardware reset + ADC path init
    es8311HardReset();  delay(50);  es8311InitADC();

    Serial0.println("===== Recorder Ready =====");
}

5.3.2 I2C Bus Initialization and Scan

I2C uses Arduino standard Wire library. After Wire.setPins() specifies pins and Wire.begin() starts, scan addresses 1~126 to confirm ES8311 responds at 0x18.

xxxxxxxxxx
void i2cBusInit() {
    Wire.setPins(PIN_I2C_SDA, PIN_I2C_SCL);  // Set I2C pins
    Wire.begin();                              // Start I2C master mode
    delay(50);
    i2c_ready = true;
    Serial0.printf("[I2C] SDA:%d SCL:%d\n", PIN_I2C_SDA, PIN_I2C_SCL);
}

void i2cBusScan() {
    if (!i2c_ready) return;
    Serial0.println("===== I2C Scan =====");
    int found = 0;
    for (uint8_t a = 1; a < 127; a++) {
        Wire.beginTransmission(a);              // Send start condition + device address
        if (Wire.endTransmission() == 0) {      // Returns 0 = device ACK
            Serial0.printf("  [OK] 0x%02X\n", a);
            found++;
        }
        delay(3);
    }
    Serial0.printf("===== %d device(s) =====\n", found);
}

ES8311 register read/write foundation - write uses Wire.beginTransmission + Wire.write + Wire.endTransmission; read writes register address first (no STOP), then issues read request for 1 byte.

xxxxxxxxxx
uint8_t es8311ReadReg(uint8_t reg) {
    Wire.beginTransmission(ES8311_ADDR);           // START + device address(W)
    Wire.write(reg);                               // Send register address
    Wire.endTransmission(false);                   // No STOP → RSTART
    Wire.requestFrom((uint8_t)ES8311_ADDR, (uint8_t)1); // RSTART + device address(R) + read 1 byte
    return Wire.available() ? Wire.read() : 0;
}

5.3.3 ES8311 Hardware Reset Sequence (es8311HardReset)

Complete hardware reset sequence must be executed after ES8311 power on. Register writes are divided into 4 groups, strictly following chip manual order: analog power→clock tree→slave mode→ADC filter.

xxxxxxxxxx
void es8311HardReset() {
    // -- 1. Analog power + GPIO reference --
    es8311WriteReg(ES8311_REG0D_SYSTEM3, 0xFA);  // Enable analog LDO & reference
    es8311WriteReg(ES8311_REG44_GPIO1,   0x08);  // Set GPIO reference = 1.8V
    es8311WriteReg(ES8311_REG44_GPIO1,   0x08);  // Double-write anti-I2C noise

    // -- 2. Clock tree --
    es8311WriteReg(ES8311_REG01_CLK_MGR1, 0x30);  // Master clock config
    es8311WriteReg(ES8311_REG02_CLK_MGR2, 0x00);  // pre_div=1, multi=x1
    es8311WriteReg(ES8311_REG03_CLK_MGR3, 0x10);  // fs_mode=0, adc_osr=16
    es8311WriteReg(ES8311_REG04_CLK_MGR4, 0x10);  // dac_osr initial
    es8311WriteReg(ES8311_REG05_CLK_MGR5, 0x00);  // adc_div=1, dac_div=1
    es8311WriteReg(ES8311_REG16_ADC2,    0x24);  // ADC initial gain
    es8311WriteReg(ES8311_REG0B_SYSTEM1, 0x00);  // System control 1
    es8311WriteReg(ES8311_REG0C_SYSTEM2, 0x00);  // System control 2
    es8311WriteReg(ES8311_REG10_SYSTEM6, 0x1F);  // System control 6
    es8311WriteReg(ES8311_REG11_SYSTEM7, 0x7F);  // System control 7
    es8311WriteReg(ES8311_REG00_RESET,   0x80);  // Release reset (bit7=1)

    // -- 3. Slave mode + external MCLK --
    es8311WriteReg(ES8311_REG00_RESET, es8311ReadReg(ES8311_REG00_RESET) & 0xBF);  // Clear bit6
    es8311WriteReg(ES8311_REG01_CLK_MGR1, 0x3F & 0x7F);   // MCLK from external
    es8311WriteReg(ES8311_REG06_CLK_MGR6, es8311ReadReg(ES8311_REG06_CLK_MGR6) & ~0x20); // No BCLK invert

    // -- 4. ADC analog + filters --
    es8311WriteReg(ES8311_REG13_SYSTEM9, 0x10);  // System control 9
    es8311WriteReg(ES8311_REG1B_ADC7,    0x0A);  // High-pass filter (HPF)
    es8311WriteReg(ES8311_REG1C_ADC8,    0x6A);  // Equalizer (EQ)
    es8311WriteReg(ES8311_REG44_GPIO1,   0x58);  // Enable internal DAC reference

    delay(10);
    codec_ready = true;
    Serial0.println("[ES8311] HW init done");
}

5.3.4 ADC Path Start (es8311StartADC)

Called after hardware reset completes to start ADC path: release clock gating, enable ADC clock, power on digital/analog power, start ADC/DAC ramp, configure PGA gain and input channel.

xxxxxxxxxx
static void es8311StartADC() {
    // Release clock gating
    es8311WriteReg(ES8311_REG00_RESET, es8311ReadReg(ES8311_REG00_RESET) & 0xBF);

    uint8_t dac = es8311ReadReg(ES8311_REG09_SDP_IN);   // Read DAC I2S format
    uint8_t adc = es8311ReadReg(ES8311_REG0A_SDP_OUT);  // Read ADC I2S format
    dac &= ~(0x40);                                        // Enable DAC clock
    adc &= ~(0x40);                                        // Enable ADC clock
    es8311WriteReg(ES8311_REG09_SDP_IN,  dac);
    es8311WriteReg(ES8311_REG0A_SDP_OUT, adc);

    es8311WriteReg(ES8311_REG17_ADC3, 0xBF);              // ADC digital volume max
    es8311WriteReg(ES8311_REG0E_SYSTEM4, 0x02);           // Digital power enable
    es8311WriteReg(ES8311_REG12_SYSTEM8, 0x00);           // DAC enable
    es8311WriteReg(ES8311_REG14_SYSTEM10, 0x1A);          // PGA gain + ADC input enable
    es8311WriteReg(ES8311_REG0D_SYSTEM3, 0x01);           // Analog power final config
    es8311WriteReg(ES8311_REG15_ADC1, 0x40);              // ADC ramp start
    es8311WriteReg(ES8311_REG37_DAC7, 0x08);              // DAC ramp start
    es8311WriteReg(ES8311_REG45_GPIO2, 0x00);             // GPIO2 output control
}

5.3.5 Mic Gain Setting

Mic gain supports 8 levels from 0~42dB. es8311SetMicGain() converts dB value to register value 0~7 via lookup table, writes to REG16_ADC2. Default is 24dB, suitable for close-range voice acquisition.

xxxxxxxxxx
/*
 * Mic gain mapping table:
 * +----------+-----+-----------+
 * | Input dB | Reg | Actual    |
 * +----------+-----+-----------+
 * |  <  6    |  0  |   0dB    |
 * |  6~11    |  1  |   6dB    |
 * | 12~17    |  2  |  12dB    |
 * | 18~23    |  3  |  18dB    |
 * | 24~29    |  4  |  24dB    |
 * | 30~35    |  5  |  30dB    |
 * | 36~41    |  6  |  36dB    |
 * |  >= 42   |  7  |  42dB    |
 * +----------+-----+-----------+
 */
void es8311SetMicGain(uint8_t db) {
    uint8_t g;
    if      (db <  6) g = 0;
    else if (db < 12) g = 1;
    else if (db < 18) g = 2;
    else if (db < 24) g = 3;
    else if (db < 30) g = 4;
    else if (db < 36) g = 5;
    else if (db < 42) g = 6;
    else              g = 7;
    es8311WriteReg(ES8311_REG16_ADC2, g);
}

5.4 FAT16 Virtual Disk: Fat16Disk.h / Fat16Disk.cpp

5.4.1 Class Interface

Fat16Disk encapsulates all logic for building FAT16 filesystem image in PSRAM, exposing begin()/resetForRecord()/publishFile() core methods and data()/ready() status query interfaces.

xxxxxxxxxx
class Fat16Disk {
public:
    bool     begin(uint32_t sectors, const char* label);    // Alloc PSRAM + format FAT16
    void     resetForRecord(const char* label);              // Reset to recording-ready state
    void     publishFile(uint8_t entry, const char* name,    // Register data area as FAT16 file
                        const char* ext, uint32_t size);

    uint8_t* data()        const { return _disk; }           // PSRAM disk base pointer
    uint32_t sectorCount() const { return _sectors; }        // Total sectors
    uint16_t dataOffset()  const { return _dataOfs; }        // Data area start sector
    bool     ready()       const { return _disk != nullptr; } // PSRAM allocation success?

private:
    uint8_t* _disk    = nullptr;  // PSRAM disk image base address
    uint32_t _sectors = 0;        // Total sectors
    uint16_t _fatSecs = 0;        // FAT table sectors
    uint16_t _dataOfs = 0;        // Cluster-2 physical sector
};

5.4.2 begin -- Create FAT16 Virtual Disk

calloc 8MB disk image from PSRAM, calculate FAT sectors and data area offset (boot(1) + FAT + root(1)), then write boot sector BPB, FAT table (all clusters marked FREE), and root directory label in sequence.

xxxxxxxxxx
bool Fat16Disk::begin(uint32_t sectors, const char* label) {
    // 1. Alloc disk image from PSRAM (calloc ensures zeroed)
    _disk = (uint8_t*)calloc(1, (size_t)sectors * 512);
    if (!_disk) return false;  // PSRAM allocation failed
    _sectors = sectors;

    // 2. Calculate FAT16 structure parameters
    _fatSecs = fat_sectors_per_alloc_table(sectors, true); // FAT16, 16-bit entries
    _dataOfs = 1 + _fatSecs + 1;  // boot(1) + FAT + root(1)

    // 3. Write boot sector BPB
    fat_add_boot_sector(_disk, sectors, _fatSecs, "FAT16", label, 0x1234);

    // 4. Write FAT table (all clusters FREE)
    fat_add_table(_disk, (fat_boot_sector_t*)_disk, true);

    // 5. Write root directory label
    fat_add_label(_disk, label);

    Serial0.printf("[DISK] FAT16 %luKB | boot(1)+FAT(%d)+root(1)+data(%d sectors)\n",
                   (unsigned long)sectors / 2, _fatSecs, sectors - _dataOfs);
    return true;
}

5.4.3 resetForRecord -- Prepare for Recording

Called before each new recording, rebuilds FAT table (all clusters FREE), rewrites root directory label, clears remaining directory entries. Ensures disk is clean with no residue, PC only sees new file.

xxxxxxxxxx
void Fat16Disk::resetForRecord(const char* label) {
    if (!_disk) return;

    // 1. Rebuild FAT table (all clusters FREE)
    fat_add_table(_disk, (fat_boot_sector_t*)_disk, true);

    // 2. Rebuild label
    fat_add_label(_disk, label);

    // 3. Clear remaining directory entries
    memset(_disk + (size_t)(1 + _fatSecs) * 512 + 32, 0, 15 * 32);
}

5.4.4 publishFile -- Register Data as File

Called after recording ends. Uses TinyUSB FAT helper to create 8.3 format file entry in root directory (filename + extension + size), internally writes 32-byte directory entry and marks cluster allocation chain in FAT table. PC can now see the file.

xxxxxxxxxx
void Fat16Disk::publishFile(uint8_t entry, const char* name,
                            const char* ext, uint32_t size) {
    if (!_disk) return;

    // TinyUSB FAT helper: create root dir file entry
    //   entry=dir index, name=8.3 filename
    //   ext=extension, size=file size
    //   2=starting cluster, true=FAT16
    fat_add_root_file(_disk, entry, name, ext, size, 2, true);
    // Internally: write 32-byte dir entry + FAT chain
}

5.5 Recorder: Recorder.h / Recorder.cpp

5.5.1 Class Interface

Recorder class encapsulates recording state machine, providing start()/stop()/tick() core methods and busy() status query. Constructor receives disk, LED, max duration, and completion callback four dependencies.

xxxxxxxxxx
using RecorderCallback = void (*)();  // Recording complete callback type

class Recorder {
public:
    Recorder(Fat16Disk& d, Led& l, uint16_t maxSec, RecorderCallback onPublished = nullptr);
    void start();    // Start recording
    void stop();     // Stop recording, commit file, call onPublished
    void tick();     // Called by main loop, read I2S -> write PSRAM
    bool busy() const { return _recording; }  // Is recording?

private:
    Fat16Disk&       _disk;
    Led&             _led;
    uint16_t         _maxSec;
    RecorderCallback _onPublished;
    bool             _recording = false;
    uint32_t         _wavTotal  = 0;   // Recorded PCM total bytes
    uint32_t         _wavMax    = 0;   // Max recording bytes
};

5.5.2 WAV Header Template

WAV header template - written to data area beginning before recording starts, fileLen and dataLen fields initially 0. Real values backfilled after recording ends, ensuring PC always sees correct WAV header when reading file.

xxxxxxxxxx
static const WavHdr kHdr = {
    {'R','I','F','F'}, 0,    // RIFF ID + file length (patch later)
    {'W','A','V','E'},       // WAVE format
    {'f','m','t',' '}, 16,   // fmt chunk, len=16
    1, 1, SAMPLE_RATE,       // PCM, 1 channel, 16kHz
    SAMPLE_RATE * 1 * 2,     // Byte rate: 16000*1*2 = 32000 B/s
    (uint16_t)(1 * 2),       // Block align: 1ch * 2B = 2
    16,                      // Bits per sample: 16
    {'d','a','t','a'}, 0     // data chunk + data length (patch later)
};

5.5.3 start -- Begin Recording

First reset disk to clean state, then write WAV header skeleton to data area start, finally initialize PCM counter and light green LED. _wavMax = maxSec × byteRate calculates max recording bytes.

xxxxxxxxxx
void Recorder::start() {
    // Already recording or disk not ready
    if (_recording || !_disk.ready()) return;

    // 1. Reset FAT16 disk to clean state
    _disk.resetForRecord("AUDIODISK");

    // 2. Write WAV header template to data area start
    memcpy(_disk.data() + (size_t)_disk.dataOffset() * 512, &kHdr, sizeof(kHdr));

    // 3. Initialize counters
    _wavTotal  = 0;
    _wavMax    = (uint32_t)_maxSec * kHdr.byteRate;  // 120s × 32000B/s = 3,840,000 bytes
    _recording = true;

    // 4. LED green indicates recording
    _led.on(0, 150, 0);  // R=0, G=150, B=0
    Serial0.printf("[REC] START (max %ds, %luB)\n", _maxSec, _wavMax);
}

5.5.4 tick -- Main Loop Recording

Called by loop() each frame, returns immediately if not recording. Reads 640 bytes PCM from I2S (~20ms audio), appends to PSRAM disk image after WAV header. Automatically calls stop() when max duration reached.

xxxxxxxxxx
void Recorder::tick() {
    if (!_recording) return;  // Not recording, return

    // 1. Read PCM from I2S (640B = 320 samples = 20ms)
    uint8_t buf[640];
    size_t  rd = I2S.readBytes((char*)buf, sizeof(buf));
    if (rd == 0) return;  // No data, read next time

    // 2. Append PCM after WAV header
    memcpy(_disk.data() + (size_t)_disk.dataOffset() * 512 + sizeof(kHdr) + _wavTotal, buf, rd);
    _wavTotal += rd;

    // 3. Check max recording duration
    if (_wavTotal >= _wavMax) stop();
}

5.5.5 stop -- Stop Recording

After stopping recording, completes three things in order: (1) Backfill WAV header fileLen and dataLen fields (memcpy to offset 4 and 40); (2) Register data area as FAT16 file via publishFile(); (3) Call _onPublished callback to simulate USB drive re-plug. LED red→rainbow sequence prompts user that save is complete.

xxxxxxxxxx
void Recorder::stop() {
    if (!_recording) return;
    _recording = false;

    // 1. LED red = saving
    _led.on(150, 0, 0);  // R=150, G=0, B=0

    // 2. Backfill WAV header file length and data length
    uint32_t fileSize = _wavTotal + sizeof(kHdr);// Total file size
    uint32_t riffLen  = fileSize - 8;  // RIFF length
    uint8_t* base     = _disk.data() + (size_t)_disk.dataOffset() * 512;
    memcpy(base + 4,  &riffLen,  4);   // Offset 0x04: fileLen field
    memcpy(base + 40, &_wavTotal, 4);  // Offset 0x28: dataLen field

    // 3. Register data area as FAT16 file
    _disk.publishFile(1, "RECORD", "WAV", fileSize);

    // 4. Call completion callback → notify PC media change
    if (_onPublished) _onPublished();

    Serial0.printf("[REC] DONE: %luB WAV, %.1fs\n",
                   fileSize, (float)_wavTotal / kHdr.byteRate);

    // 5. LED rainbow = done
    delay(300);
    _led.rainbow();
}

5.6 Button: Button.h / Button.cpp

5.6.1 Complete tick() Implementation

Button state machine handles mechanical bounce and short/long press distinction, with three paths: (1) Press edge→debounce 30ms then confirm; (2) Release edge→distinguish short/long by <800ms/≥800ms; (3) Hold to threshold→trigger long press early without waiting for release.

xxxxxxxxxx
Button::Event Button::tick() {
    bool pressed = !digitalRead(_pin);  // LOW=pressed

    // -- Press edge detect --
    if (pressed && !_down) {
        delay(_debounceMs);        // Debounce wait 30ms
        if (digitalRead(_pin)) return NONE;      // Read back HIGH=bounce, ignore
        _down       = true;        // Confirm press
        _handled    = false;       // Event pending
        _pressStart = millis();    // Record press time
        return NONE;
    }

    // -- Release edge detect --
    if (!pressed && _down) {
        _down = false;
        if (_handled) return NONE;// Long already sent, skip
        _handled = true;
        return (millis() - _pressStart < _longPressMs) ? SHORT : LONG; // <800ms=short/>=800ms=long
    }

    // -- Long press hold detect (fire early, don't wait for release) --
    if (_down && !_handled && millis() - _pressStart >= _longPressMs) {
        _handled = true;           // Mark handled
        return LONG;
    }

    return NONE;
}

5.7 LED: Led.h / Led.cpp

5.7.1 Class Interface

Led class wraps Adafruit_NeoPixel library, constructor specifies 1 LED, GRB byte order, 800kHz timing. Only exposes on()/off()/rainbow() three methods, simplifying LED state switching.

xxxxxxxxxx
#include <Adafruit_NeoPixel.h>  // WS2812 driver

class Led {
public:
    Led(uint8_t pin) : _neo(1, pin, NEO_GRB + NEO_KHZ800) {} // 1 LED, GRB, 800kHz

    void begin();        // Init
    void on(uint8_t r, uint8_t g, uint8_t b);  // Set color
    void off();          // Turn off
    void rainbow();      // Rainbow cycle

private:
    Adafruit_NeoPixel _neo;
};

5.7.2 Rainbow Effect

Simple 7-color cycle effect, each color lasts 200ms (~1.4s total). Called after recording completes as visual hint for "file ready". Auto-off after cycle ends.

xxxxxxxxxx
void Led::rainbow() {
    static const uint8_t colors[][3] = {
        {  0, 150,   0}, // Green
        {150,  60,   0}, // Orange
        {150, 150,   0}, // Yellow
        {  0, 150,   0}, // Green
        {  0, 150, 150}, // Cyan
        {  0,   0, 150}, // Blue
        {120,   0, 150}, // Purple
    };
    for (int i = 0; i < 7; i++) {
        _neo.setPixelColor(0, _neo.Color(colors[i][0], colors[i][1], colors[i][2]));
        _neo.show();
        delay(200);  // 200ms per color
    }
    off();
}

6. Operation Flow

6.1 IDE Configuration

After opening the project, configure the following in the Tools menu:

ConfigurationValueDescription
BoardESP32S3 Dev ModuleSelect ESP32-S3 development board
USB ModeTinyUSBMSC requires TinyUSB protocol stack
USB CDC On BootDisabled (required!)USB OTG reserved for MSC
PSRAMOPI PSRAMEnable 8MB Octal PSRAM
Flash Size16MB (128Mb)Match onboard 16MB SPI Flash
Partition Scheme16M Flash (3MB APP/9.9MB FATFS)Or custom partition
Upload ModeUART0 / Hardware CDCUse onboard USB-UART for download

ESP32-S3 has only one USB OTG peripheral (GPIO19/20) - USB Mode = TinyUSB hands OTG to TinyUSB protocol stack (required for MSC). At this point Serial is unavailable, USB CDC On Boot = Disabled prevents USB CDC conflict, debugging through onboard USB-UART bridge chip's Serial0.

Enable PSRAM:

Enable PSRAM

Enable TinyUSB:

Enable TinyUSB

Enable Large FATFS Partition:

Enable large FATFS partition

6.2 Compile and Flash

For library installation, see section 3.5. Only need to additionally install Adafruit NeoPixel, reference 2.Basic Peripherals → 9.RGB LED Experiment → RGB LED Experiment.

Arduino Flash Process: See Arduino → 1. Before Development → 2. Build and Flash → Build and Flash.

6.3 Operation Steps

Step 1: Confirm Hardware Connection

This development board has ES8311 audio codec and MEMS microphone onboard, no external modules needed, ready to use after power on.

Step 2: Serial Output

After flashing completes, open serial monitor (115200 baud), should see:

Ready, waiting for recording

xxxxxxxxxx
=== Rec+USB MSC ===
===== Audio System (ADC Only) =====
[PWR] GPIO7 HIGH
[I2C] SDA:41 SCL:42
===== I2C Scan =====
  [OK] 0x18
  [OK] 0x3C
===== 2 device(s) =====
[I2S] Ready MCLK:15 BCLK:16 WS:18 DIN:17
[ES8311] HW init done
[ES8311] ADC ready (FS:16000Hz Mic:24dB)
===== Recorder Ready =====
[DISK] FAT16 4096KB | boot(1)+FAT(32)+root(1)+data(8158 sectors)
[USB] FAT16 4096KB MSC *Plug USB cable*

Step 3: Start Recording

Short press BOOT button (GPIO0), LED turns green, recording starts. Speak towards onboard MEMS microphone to capture audio.

Recording in progress

Serial output:

xxxxxxxxxx
[REC] START (max 120s, 3840000B)

Step 4: Stop Recording, Save and Export

Short press BOOT button again to stop recording. LED first turns red (saving), then lights rainbow (7-color cycle), indicating save complete.

Downloading

Serial output:

xxxxxxxxxx
[REC] DONE: 390444B WAV, 12.2s

Recording successful, saved

  1. After seeing rainbow light, continue to keep USB-UART port powered. (During first run, do not insert USB OTG port before rainbow light appears, otherwise you'll get an empty U disk. If not first run, simply re-plug the USB OTG data cable)

  2. After rainbow light appears, connect USB OTG port to PC with another Type-C data cable. PC will pop up a U disk (volume label "AUDIODISK"), open to see RECORD.WAV, double-click to play and verify recording effect.

USB drive front

USB Drive

Saved WAV file

 

Recording data is stored in PSRAM, lost on power failure. Unplugging USB-UART will cause board power loss and data cleared. Must keep powered and read file via USB OTG port.

To record again, unplug Type-C cable from USB OTG port, keep USB-UART powered, short press BOOT button (new recording will automatically overwrite old file).

6.4 Long Press Force Stop

If emergency stop is needed during recording (e.g., excessive noise), long press BOOT button >= 0.8 seconds to immediately stop recording and save file.

OperationButton ActionResult
Normal stopShort press < 0.8sStop recording + save
Emergency force stopLong press >= 0.8sImmediately stop recording + save
120s limit reachedAutoAuto stop + save

7. FAQ

SymptomCauseSolution
PC cannot recognize U diskUSB CDC On Boot not disabled, USB OTG occupied by CDCIDE → Tools → "USB CDC On Boot" → Disabled; debug using Serial0
WAV file cannot playWAV header not backfilled / player not supported / Mic gain insufficientCheck file size >44B; use VLC player; increase MIC_GAIN_DB
Recording time inaccuratePSRAM disk space insufficient / I2S DMA data lossConfirm DISK_SECTORS setting correct; avoid delay() in loop()
Button insensitiveDebounce time mismatch / GPIO0 conflictAdjust Button::begin(pin, 800, 30) debounce parameters
Insufficient PSRAMPSRAM not enabled or capacity insufficientTools → PSRAM → "OPI PSRAM"; need >=8MB PSRAM
Filename garbledFAT16 only supports 8.3 short filename formatUse English uppercase filenames (RECORD, AUDIO, etc.)
Serial conflicts with MSCESP32-S3 has only one USB OTG, cannot support CDC+MSC simultaneouslyUse Serial0 (UART0), disable USB CDC On Boot

7.1 Version Compatibility Quick Reference

ComponentMinimum VersionRecommended VersionNotes
Arduino IDE2.0.02.3.x+ESP32 board management support
ESP32 Arduino Core2.0.63.3.8+TinyUSB MSC support starts from 2.0.6
Adafruit NeoPixel1.12.01.12.3+WS2812 driver
Windows710/11FAT16 native support

Appendix A: Complete Code Index

FileLinesFunction
Arduino_Recorder_USB_MSC.ino79Main program: setup/loop/MSC callbacks
AudioSystem.h192Audio system header: pin macros/register addresses/function declarations
AudioSystem.cpp307Audio system implementation: I2C driver/ES8311 init/I2S config
Recorder.h33Recorder header: state machine interface declarations
Recorder.cpp96Recorder implementation: WAV header build/I2S read/disk write/backfill
Fat16Disk.h31FAT16 disk header: virtual disk interface
Fat16Disk.cpp35FAT16 disk implementation: format/reset/file publish
Button.h25Button header: debounce state machine interface
Button.cpp42Button implementation: short/long press detection logic
Led.h22LED header: WS2812 control interface
Led.cpp39LED implementation: brightness/color/rainbow effect

Appendix B: ES8311 Register Quick Reference

RegisterAddressThis Project ValueFunction
RESET0x000x80 -> 0x3FGlobal reset release -> clear I2S reset
CLK_MGR10x010x30 -> 0x3FMaster clock config -> MCLK external input
CLK_MGR20x020x00pre_div=1, multi=x1
CLK_MGR30x030x10fs_mode=0, adc_osr=16
CLK_MGR40x040x20dac_osr=32
CLK_MGR50x050x00adc_div=1, dac_div=1
CLK_MGR60x06bclk_div=4BCLK = MCLK / 4 = 1.024MHz
CLK_MGR70x070x00lrck_h=0
CLK_MGR80x080xFFlrck_l=0xFF -> WS period = 256 BCLK
SDP_IN0x090x0CDAC I2S standard mode, 16bit
SDP_OUT0x0A0x0CADC I2S standard mode, 16bit
SYSTEM30x0D0xFA -> 0x01Analog power config
SYSTEM40x0E0x02Digital power enable
SYSTEM100x140x1APGA gain + ADC input enable
ADC10x150x40ADC ramp start
ADC20x160x04 (24dB)ADC analog gain select (0~7)
ADC30x170xBFADC digital volume (max)
ADC70x1B0x0AADC high-pass filter (HPF)
ADC80x1C0x6AADC equalizer (EQ)
GPIO10x440x08 -> 0x58GPIO reference -> enable internal DAC reference
Bluetooth Master Mode
I2S Audio Playback Experiment

COMPANY

  • About us
  • Team Style
  • Blog

LEARNING & EXPLORATION

  • Tutorials
  • Edge AI hardware
  • Educational robots
  • Embedded intelligent hardware

WHERE TO BUY

  • Yahboom Online Store
  • Amazon
  • Alibaba
  • AliExpress
  • Mercado

TECHNOLOGY SUPPORT

  • Email: support@yahboom.com

BE A DISTRIBUTOR

  • Email: sales@yahboom.com
QR Code

Copyright © 2017-2026 Yahboom. All Rights Reserved.

EN/中文