English
English
简体中文
日本語

Arduino Quick Start

2. Devices & Examples

5. Extensions

6. Applications

Module Audio Arduino Tutorial

1. Prerequisites

2. Initialization

Module Audio integrates an STM32 controller for headset interface standard switching, LED control, headphone insertion detection (channel 2 only), microphone input mode configuration, and other functions.

Interface Standard Selection
Headset interface standards use different pin assignments. Call audio.setHPMode(AUDIO_HPMODE_NATIONAL); to select the appropriate standard for compatibility: OMTP (AUDIO_HPMODE_NATIONAL) or CTIA (AUDIO_HPMODE_AMERICAN).
cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
#include <M5Unified.h>
#include <M5Module_Audio.h>

M5ModuleAudio audio;

void setup()
{
    // Initialize M5Unified without the built-in audio devices.
    auto cfg = M5.config();
    cfg.serial_baudrate = 115200;
    cfg.internal_mic = false;
    cfg.internal_spk = false;

    M5.begin(cfg);

    if (!audio.begin(Wire)) {
        Serial.println("Module Audio not found.");

        while (true) {
            delay(1000);
        }
    }

    audio.setHPMode(AUDIO_HPMODE_NATIONAL);
    // audio.setHPMode(AUDIO_HPMODE_AMERICAN);
    audio.setMICStatus(AUDIO_MIC_OPEN);
    audio.setRGBBrightness(100);

    for (uint8_t i = 0; i < 3; ++i) {
        audio.setRGBLED(i, 0x0000FF);
    }
}

void loop()
{
    M5.update();

    bool is_hp_inserted = audio.getHPInsertStatus();

    if (is_hp_inserted) {
        Serial.println("Headphone Inserted");
    } else {
        Serial.println("Headphone Removed");
    }
    delay(500);
}

3. Input Interface Configuration

Interface Usage
Module Audio provides two 3.5mm audio interfaces (channel 1 and channel 2). Channel 1 accepts microphone input from a 3.5mm TRS audio connector. Channel 2 supports 3.5mm TRRS combined audio devices and is also compatible with TRS audio connectors.
  • TRS audio wiring: Connect the MIC input to channel 1 and the audio output to channel 2. This configuration is suitable for devices with separate microphone and audio plugs, such as PC headsets.
  • TRRS combined audio wiring: Connect the combined audio input/output device to channel 2. This configuration supports wired headsets with microphones and is also compatible with TRS audio devices.

After initialization, call the following APIs to configure the input/output channels, volume, gain, and other parameters.

cpp
1 2 3 4 5 6 7 8 9 10 11 12
// Use input 1 for both channels.
audio.setMicInputLine(ADC_INPUT_LINPUT1_RINPUT1);

// Use input 2 for both channels.
// audio.setMicInputLine(ADC_INPUT_LINPUT2_RINPUT2);

audio.setSpeakerOutput(DAC_OUTPUT_OUT1);
audio.setMicAdcVolume(100);
audio.setSpeakerVolume(40);
audio.setMicGain(MIC_GAIN_24DB);
audio.setBitsSample(ES_MODULE_ADC, BIT_LENGTH_16BITS);
audio.setSampleRate(SAMPLE_RATE_44K);

4. Recording and Playback

Using the TRS wiring configuration, connect the MIC input to channel 1 and the audio output to channel 2 to capture and play microphone data in real time.

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
#include <M5Unified.h>
#include <M5Module_Audio.h>

M5ModuleAudio audio;

static constexpr size_t AUDIO_BUFFER_SIZE = 512;

static uint8_t audio_buffer[AUDIO_BUFFER_SIZE];

void setup()
{
    // Initialize M5Unified without the built-in audio devices.
    auto cfg = M5.config();
    cfg.serial_baudrate = 115200;
    cfg.internal_mic = false;
    cfg.internal_spk = false;

    M5.begin(cfg);

    // Initialize Module Audio on the internal I2C bus.
    if (!audio.begin(M5.In_I2C)) {
        Serial.println("Module Audio not found.");

        while (true) {
            delay(1000);
        }
    }

    // Configure the Module Audio controller.
    audio.setHPMode(AUDIO_HPMODE_NATIONAL);
    audio.setMICStatus(AUDIO_MIC_OPEN);
    audio.setRGBBrightness(100);

    Serial.printf("HP mode: %d\n", audio.getHPMode());
    Serial.printf("MIC status: %d\n", audio.getMICStatus());

    // Set the status LEDs.
    const uint32_t colors[] = {
        0x00FF00,
        0xFFFF00,
        0xFFFFFF,
    };

    for (uint8_t i = 0; i < 3; ++i) {
        audio.setRGBLED(i, colors[i]);

        Serial.printf(
            "RGB-%u: set=%06X, read=%06X\n",
            i,
            static_cast<unsigned>(colors[i]),
            static_cast<unsigned>(audio.getRGBLED(i)));
    }

    // Configure ES8388 for microphone loopback.
    audio.setMicInputLine(ADC_INPUT_LINPUT2_RINPUT2);
    audio.setMicGain(MIC_GAIN_24DB);
    audio.setMicAdcVolume(100);
    audio.setSpeakerVolume(40);
    audio.setSpeakerOutput(DAC_OUTPUT_OUT1);
    audio.setBitsSample(
        ES_MODULE_ADC_DAC,
        BIT_LENGTH_16BITS);
    audio.setSampleRate(SAMPLE_RATE_44K);

    // Print the ES8388 register values.
    Serial.println("Read ES8388 registers:");

    uint8_t* registers = audio.readAllReg();

    if (registers != nullptr) {
        for (uint8_t i = 0; i < 53; ++i) {
            Serial.printf(
                "Reg-%02u = 0x%02X\r\n",
                i,
                registers[i]);
        }
    }

    Serial.println("Audio loopback started.");
}

void loop()
{
    M5.update();

    // Capture one audio buffer.
    if (!audio.record(audio_buffer, sizeof(audio_buffer))) {
        Serial.println("Audio record failed.");
        delay(100);
        return;
    }

    // Play the captured audio buffer.
    if (!audio.play(audio_buffer, sizeof(audio_buffer))) {
        Serial.println("Audio playback failed.");
        delay(100);
    }
}

5. Playing a WAV File from microSD

Configure channel 2 as the audio output, load a WAV file stored on the microSD card, and play it. Before use, place the WAV file on the microSD card and insert the card into the controller's card slot.

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
#include <M5Unified.h>
#include <M5Module_Audio.h>
#include <SPI.h>
#include <SD.h>

M5ModuleAudio audio;
File wav_file;

static constexpr char WAV_PATH[] = "/hello.wav";
static constexpr size_t WAV_HEADER_SIZE = 44;
static constexpr size_t AUDIO_BUFFER_SIZE = 1024;

static uint8_t audio_buffer[AUDIO_BUFFER_SIZE];

bool openWavFile()
{
    if (wav_file) {
        wav_file.close();
    }

    wav_file = SD.open(WAV_PATH, FILE_READ);

    if (!wav_file) {
        Serial.printf("Failed to open %s\n", WAV_PATH);
        return false;
    }

    if (wav_file.size() <= WAV_HEADER_SIZE) {
        Serial.println("Invalid or empty WAV file.");
        wav_file.close();
        return false;
    }

    Serial.printf(
        "WAV file size: %u bytes\n",
        static_cast<unsigned>(wav_file.size()));

    if (!wav_file.seek(WAV_HEADER_SIZE)) {
        Serial.println("Failed to skip WAV header.");
        wav_file.close();
        return false;
    }

    return true;
}

void setup()
{
    auto cfg = M5.config();
    cfg.serial_baudrate = 115200;
    cfg.internal_mic = false;
    cfg.internal_spk = false;

    M5.begin(cfg);

    Serial.printf(
        "Detected board: %d\n",
        static_cast<int>(M5.getBoard()));

    if (!audio.begin(M5.In_I2C)) {
        Serial.println("Module Audio not found.");

        while (true) {
            delay(1000);
        }
    }

    audio.setHPMode(AUDIO_HPMODE_NATIONAL);
    audio.setMICStatus(AUDIO_MIC_OPEN);
    audio.setRGBBrightness(100);

    Serial.printf("HP mode: %d\n", audio.getHPMode());
    Serial.printf("MIC status: %d\n", audio.getMICStatus());

    const uint32_t colors[] = {
        0x00FF00,
        0xFFFF00,
        0xFFFFFF,
    };

    for (uint8_t i = 0; i < 3; ++i) {
        audio.setRGBLED(i, colors[i]);

        Serial.printf(
            "RGB-%u: set=%06X, read=%06X\n",
            i,
            static_cast<unsigned>(colors[i]),
            static_cast<unsigned>(audio.getRGBLED(i)));
    }

    // M5ModuleAudio has already initialized ES8388.
    audio.setMicAdcVolume(100);
    audio.setSpeakerVolume(80);
    audio.setSpeakerOutput(DAC_OUTPUT_OUT1);
    audio.setBitsSample(
        ES_MODULE_ADC_DAC,
        BIT_LENGTH_16BITS);
    audio.setSampleRate(SAMPLE_RATE_44K);

    Serial.println("Read ES8388 registers:");

    uint8_t* registers = audio.readAllReg();

    if (registers != nullptr) {
        for (uint8_t i = 0; i < 53; ++i) {
            Serial.printf(
                "Reg-%02u = 0x%02X\r\n",
                i,
                registers[i]);
        }
    }

    // Get the SD SPI pins for the detected board.
    int sd_sclk = M5.getPin(m5::pin_name_t::sd_spi_sclk);
    int sd_miso = M5.getPin(m5::pin_name_t::sd_spi_miso);
    int sd_mosi = M5.getPin(m5::pin_name_t::sd_spi_mosi);
    int sd_cs   = M5.getPin(m5::pin_name_t::sd_spi_cs);

    Serial.printf("SD SPI: SCLK=%d, MISO=%d, MOSI=%d, CS=%d\n", sd_sclk, sd_miso, sd_mosi, sd_cs);

    SPI.begin(sd_sclk, sd_miso, sd_mosi, sd_cs);

    if (!SD.begin(sd_cs, SPI, 25000000)) {
        Serial.println("Failed to initialize SD card.");

        while (true) {
            delay(1000);
        }
    }

    Serial.println("SD card initialized successfully.");

    if (!openWavFile()) {
        while (true) {
            delay(1000);
        }
    }

    Serial.println("WAV playback started.");
}

void loop()
{
    M5.update();

    if (!wav_file) {
        delay(100);
        return;
    }

    // Restart playback at the beginning of the audio data.
    if (wav_file.available() == 0) {
        if (!wav_file.seek(WAV_HEADER_SIZE)) {
            Serial.println("Failed to restart WAV file.");
            delay(100);
        }

        return;
    }

    size_t bytes_read =
        wav_file.read(audio_buffer, sizeof(audio_buffer));

    if (bytes_read == 0) {
        return;
    }

    if (!audio.play(audio_buffer, bytes_read)) {
        Serial.println("I2S write failed.");
        delay(100);
    }
}

6. RGB LED Control

Module Audio integrates three programmable RGB LEDs on its side. Use the following example to control them.

cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
#include <M5Unified.h>
#include <M5Module_Audio.h>

M5ModuleAudio audio;

void setup()
{
    auto cfg = M5.config();
    cfg.serial_baudrate = 115200;
    cfg.internal_mic = false;
    cfg.internal_spk = false;

    M5.begin(cfg);

    if (!audio.begin(M5.In_I2C)) {
        Serial.println("Module Audio not found.");

        while (true) {
            delay(1000);
        }
    }

    audio.setRGBBrightness(100);
    Serial.println("Module Audio initialized successfully.");
}

void loop()
{
    M5.update();

    // Cycle all LEDs through red, green, and blue.
    for (uint8_t i = 0; i < 3; ++i) {
        audio.setRGBLED(i, 0xFF0000);
    }

    delay(1000);

    for (uint8_t i = 0; i < 3; ++i) {
        audio.setRGBLED(i, 0x00FF00);
    }

    delay(1000);

    for (uint8_t i = 0; i < 3; ++i) {
        audio.setRGBLED(i, 0x0000FF);
    }

    delay(1000);
}
Page Tools
PDF
On This Page