diff --git a/.clang-format b/.clang-format index 079c44e..42cff79 100644 --- a/.clang-format +++ b/.clang-format @@ -1,5 +1,3 @@ ---- -Language: Cpp BasedOnStyle: Google AccessModifierOffset: -4 AlignAfterOpenBracket: Align @@ -164,4 +162,3 @@ StatementMacros: TabWidth: 4 UseCRLF: false UseTab: Never -... diff --git a/.github/workflows/Arduino-Lint-Check.yml b/.github/workflows/Arduino-Lint-Check.yml index ee49e2f..de06bf0 100644 --- a/.github/workflows/Arduino-Lint-Check.yml +++ b/.github/workflows/Arduino-Lint-Check.yml @@ -1,17 +1,27 @@ name: Arduino Lint Check on: push: - branches: [ master ] + branches: [ master, main ] pull_request: - branches: [ master ] + branches: [ master, main ] + workflow_dispatch: + +defaults: + run: + shell: bash + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: lint: name: Lint Check - runs-on: ubuntu-latest + runs-on: [self-hosted, Linux, X64] steps: - - uses: actions/checkout@v2 - - uses: arduino/arduino-lint-action@v1 + - uses: actions/checkout@v4 + - uses: arduino/arduino-lint-action@v2 with: library-manager: update compliance: strict - project-type: all \ No newline at end of file + project-type: all diff --git a/.github/workflows/clang-format-check.yml b/.github/workflows/clang-format-check.yml index ecc4a96..bb38931 100644 --- a/.github/workflows/clang-format-check.yml +++ b/.github/workflows/clang-format-check.yml @@ -35,7 +35,7 @@ on: jobs: formatting-check: name: Formatting Check - runs-on: ubuntu-latest + runs-on: [self-hosted, Linux, X64] strategy: matrix: path: diff --git a/examples/M5Core/i2sloopback/i2sloopback.ino b/examples/M5Core/i2sloopback/i2sloopback.ino deleted file mode 100644 index 116cb5e..0000000 --- a/examples/M5Core/i2sloopback/i2sloopback.ino +++ /dev/null @@ -1,120 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD - * - * SPDX-License-Identifier: MIT - * - * @Hardwares: M5Core Basic + Module Audio - * @Dependent Library: - * M5Unified@^0.2.5: https://github.com/m5stack/M5Unified - * Module Audio: https://github.com/m5stack/M5Module-Audio - * - * Please rollback these libraries if you encounter I2S initialization issues. - * Board Manager M5Stack@2.0.7; - * M5Unified@0.1.17; - */ - -#include "M5Unified.h" -#include "audio_i2c.hpp" -#include "es8388.hpp" -#include "driver/i2s.h" -AudioI2c device; - -#define SYS_I2C_SDA_PIN 21 -#define SYS_I2C_SCL_PIN 22 -#define SYS_I2S_MCLK_PIN 0 -#define SYS_I2S_SCLK_PIN 13 -#define SYS_I2S_LRCK_PIN 12 -#define SYS_I2S_DOUT_PIN 15 -#define SYS_I2S_DIN_PIN 34 -#define SYS_SPI_MISO_PIN 19 -#define SYS_SPI_MOSI_PIN 23 -#define SYS_SPI_CLK_PIN 18 -#define SYS_SPI_CS_PIN 4 - -ES8388 es8388(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); - -uint16_t rxbuf[256], txbuf[256]; -size_t readsize = 0; -byte error, address; -const uint32_t color[] = {0xFF0000, 0xFF0000, 0xFF0000, 0x00FF00, 0xFFFF00, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}; -i2s_config_t i2s_config = {.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX), - .sample_rate = 44100, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = I2S_COMM_FORMAT_STAND_I2S, - .intr_alloc_flags = 0, - .dma_buf_count = 8, - .dma_buf_len = 512, - .use_apll = false, - .tx_desc_auto_clear = true, - .fixed_mclk = 0}; - -i2s_pin_config_t pin_config = { - .mck_io_num = SYS_I2S_MCLK_PIN, - .bck_io_num = SYS_I2S_SCLK_PIN, - .ws_io_num = SYS_I2S_LRCK_PIN, - .data_out_num = SYS_I2S_DOUT_PIN, - .data_in_num = SYS_I2S_DIN_PIN, -}; - -void setup() -{ - M5.begin(); - Serial.begin(115200); - for (int address = 1; address < 127; address++) { - Wire.beginTransmission(address); - error = Wire.endTransmission(); - if (error == 0) { - Serial.print("Found I2C device at address 0x"); - Serial.print(address, HEX); - Serial.print(" \n"); - } else if (error == 4) { - Serial.print("Unknown error at address 0x"); - Serial.println(address, HEX); - } - } - device.begin(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); - device.setHPMode(AUDIO_HPMODE_NATIONAL); - device.setMICStatus(AUDIO_MIC_OPEN); - device.setRGBBrightness(100); - Serial.printf("getHPMode:%d\n", device.getHPMode()); - Serial.printf("getMICStatus:%d\n", device.getMICStatus()); - for (int i = 0; i <= 2; i++) { - device.setRGBLED(i, color[i + 3]); - // Output the hexadecimal value of the current color - Serial.printf("Set RGB to %06X\n", (unsigned int)color[i + 3]); - Serial.printf("get RGB to %06X\n", device.getRGBLED(i)); - } - Serial.println("Read Reg ES8388 : "); - if (!es8388.init()) Serial.println("Init Fail"); - es8388.setADCInput(ADC_INPUT_LINPUT1_RINPUT1); - es8388.setMicGain(MIC_GAIN_24DB); - es8388.setADCVolume(100); - // The volume output should not exceed 40, otherwise there will be noise or current sounds - es8388.setDACVolume(40); - es8388.setDACOutput(DAC_OUTPUT_OUT1); - es8388.setBitsSample(ES_MODULE_ADC, BIT_LENGTH_16BITS); - es8388.setSampleRate(SAMPLE_RATE_44K); - uint8_t *reg; - for (uint8_t i = 0; i < 53; i++) { - reg = es8388.readAllReg(); - Serial.printf("Reg-%02d = 0x%02x\r\n", i, reg[i]); - } - // i2s - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - WRITE_PERI_REG(PIN_CTRL, 0xFFF0); - i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); - i2s_set_pin(I2S_NUM_0, &pin_config); -} - -void loop() -{ - i2s_read(I2S_NUM_0, &rxbuf[0], 256 * 2, &readsize, 1000); - for (int i = 0; i < 256; i++) { - // direct transfer too txbuff - txbuf[i] = rxbuf[i]; - // txbuf[i] = 0; //mute - } - // play received buffer - i2s_write(I2S_NUM_0, &txbuf[0], 256 * 2, &readsize, 1000); -} \ No newline at end of file diff --git a/examples/M5Core/player_sd/player_sd.ino b/examples/M5Core/player_sd/player_sd.ino deleted file mode 100644 index 90fc179..0000000 --- a/examples/M5Core/player_sd/player_sd.ino +++ /dev/null @@ -1,166 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD - * - * SPDX-License-Identifier: MIT - * - * @Hardwares: M5Core Basic + Module Audio - * @Dependent Library: - * M5Unified@^0.2.5: https://github.com/m5stack/M5Unified - * Module Audio: https://github.com/m5stack/M5Module-Audio - * - * Please rollback these libraries if you encounter I2S initialization issues. - * Board Manager M5Stack@2.0.7; - * M5Unified@0.1.17; - */ - -#include "M5Unified.h" -#include "audio_i2c.hpp" -#include "es8388.hpp" -#include "driver/i2s.h" -#include -#include -AudioI2c device; - -#define SYS_I2C_SDA_PIN 21 -#define SYS_I2C_SCL_PIN 22 -#define SYS_I2S_MCLK_PIN 0 -#define SYS_I2S_SCLK_PIN 13 -#define SYS_I2S_LRCK_PIN 12 -#define SYS_I2S_DOUT_PIN 15 -#define SYS_I2S_DIN_PIN 34 -#define SYS_SPI_MISO_PIN 19 -#define SYS_SPI_MOSI_PIN 23 -#define SYS_SPI_CLK_PIN 18 -#define SYS_SPI_CS_PIN 4 - -ES8388 es8388(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); -void i2s_write_task(void *arg); -uint16_t rxbuf[256], txbuf[256]; -size_t readsize = 0; -byte error, address; -const uint32_t color[] = {0xFF0000, 0xFF0000, 0xFF0000, 0x00FF00, 0xFFFF00, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}; -i2s_config_t i2s_config = {.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX), - .sample_rate = 44100, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = I2S_COMM_FORMAT_STAND_I2S, - .intr_alloc_flags = 0, - .dma_buf_count = 8, - .dma_buf_len = 512, - .use_apll = false, - .tx_desc_auto_clear = true, - .fixed_mclk = 0}; - -i2s_pin_config_t pin_config = { - .mck_io_num = SYS_I2S_MCLK_PIN, - .bck_io_num = SYS_I2S_SCLK_PIN, - .ws_io_num = SYS_I2S_LRCK_PIN, - .data_out_num = SYS_I2S_DOUT_PIN, - .data_in_num = SYS_I2S_DIN_PIN, -}; - -void setup() -{ - M5.begin(); - Serial.begin(115200); - for (int address = 1; address < 127; address++) { - Wire.beginTransmission(address); - error = Wire.endTransmission(); - if (error == 0) { - Serial.print("Found I2C device at address 0x"); - Serial.print(address, HEX); - Serial.print(" \n"); - } else if (error == 4) { - Serial.print("Unknown error at address 0x"); - Serial.println(address, HEX); - } - } - device.begin(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); - device.setHPMode(AUDIO_HPMODE_NATIONAL); - device.setMICStatus(AUDIO_MIC_OPEN); - device.setRGBBrightness(100); - Serial.printf("getHPMode:%d\n", device.getHPMode()); - Serial.printf("getMICStatus:%d\n", device.getMICStatus()); - for (int i = 0; i <= 2; i++) { - device.setRGBLED(i, color[i + 3]); - // Output the hexadecimal value of the current color - Serial.printf("Set RGB to %06X\n", (unsigned int)color[i + 3]); - Serial.printf("get RGB to %06X\n", device.getRGBLED(i)); - } - Serial.println("Read Reg ES8388 : "); - if (!es8388.init()) Serial.println("Init Fail"); - es8388.setADCVolume(100); - es8388.setDACVolume(80); - es8388.setDACOutput(DAC_OUTPUT_OUT1); - es8388.setBitsSample(ES_MODULE_ADC, BIT_LENGTH_16BITS); - es8388.setSampleRate(SAMPLE_RATE_44K); - uint8_t *reg; - for (uint8_t i = 0; i < 53; i++) { - reg = es8388.readAllReg(); - Serial.printf("Reg-%02d = 0x%02x\r\n", i, reg[i]); - } - // i2s - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - WRITE_PERI_REG(PIN_CTRL, 0xFFF0); - i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); - i2s_set_pin(I2S_NUM_0, &pin_config); - if (SD.begin(SYS_SPI_CS_PIN)) { - Serial.println("SD card initialized successfully"); - - } else { - Serial.println("Failed to initialize SD card. Retrying..."); - } - xTaskCreate(i2s_write_task, "i2s_write_task", 1024 * 8, NULL, 6, NULL); -} - -void loop() -{ -} - -void i2s_write_task(void *arg) -{ - // Open the WAV file - // Replace with the path of your WAV file - File file = SD.open("/hello.wav", "r"); - if (!file) { - Serial.println("Failed to open WAV file for reading"); - vTaskDelete(NULL); - } - - // Obtain the file size and print - size_t fileSize = file.size(); - Serial.printf("File size: %d bytes\n", fileSize); - - // Skip the WAV file header (44 bytes) - file.seek(44); - - uint8_t txbuf[1024]; - - while (1) { - // Check whether the end of the file has been reached - if (file.available() == 0) { - // Go back to the beginning of the file in order to replay it - file.seek(44); - } - - // Read data - size_t bytesRead = file.read(txbuf, sizeof(txbuf)); - - // If data is read - if (bytesRead > 0) { - size_t bytesWritten = 0; - esp_err_t result = i2s_write(I2S_NUM_0, txbuf, bytesRead, &bytesWritten, portMAX_DELAY); - // Check the writing result - if (result != ESP_OK) { - Serial.printf("I2S write error: %d\n", result); - } - } else { - // If there is no more data, exit the loop - break; - } - } - - // Close the file - file.close(); - vTaskDelete(NULL); -} \ No newline at end of file diff --git a/examples/M5Core/player_spiffs/player_spiffs.ino b/examples/M5Core/player_spiffs/player_spiffs.ino deleted file mode 100644 index beb9cb1..0000000 --- a/examples/M5Core/player_spiffs/player_spiffs.ino +++ /dev/null @@ -1,165 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD - * - * SPDX-License-Identifier: MIT - * - * @Hardwares: M5Core Basic + Module Audio - * @Dependent Library: - * M5Unified@^0.2.5: https://github.com/m5stack/M5Unified - * Module Audio: https://github.com/m5stack/M5Module-Audio - * - * Please rollback these libraries if you encounter I2S initialization issues. - * Board Manager M5Stack@2.0.7; - * M5Unified@0.1.17; - */ - -#include "M5Unified.h" -#include "audio_i2c.hpp" -#include "es8388.hpp" -#include "driver/i2s.h" -#include -#include -AudioI2c device; - -#define SYS_I2C_SDA_PIN 21 -#define SYS_I2C_SCL_PIN 22 -#define SYS_I2S_MCLK_PIN 0 -#define SYS_I2S_SCLK_PIN 13 -#define SYS_I2S_LRCK_PIN 12 -#define SYS_I2S_DOUT_PIN 15 -#define SYS_I2S_DIN_PIN 34 -#define SYS_SPI_MISO_PIN 19 -#define SYS_SPI_MOSI_PIN 23 -#define SYS_SPI_CLK_PIN 18 -#define SYS_SPI_CS_PIN 4 - -ES8388 es8388(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); -void i2s_write_task(void *arg); -uint16_t rxbuf[256], txbuf[256]; -size_t readsize = 0; -byte error, address; -const uint32_t color[] = {0xFF0000, 0xFF0000, 0xFF0000, 0x00FF00, 0xFFFF00, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}; -i2s_config_t i2s_config = {.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX), - .sample_rate = 44100, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = I2S_COMM_FORMAT_STAND_I2S, - .intr_alloc_flags = 0, - .dma_buf_count = 8, - .dma_buf_len = 512, - .use_apll = false, - .tx_desc_auto_clear = true, - .fixed_mclk = 0}; - -i2s_pin_config_t pin_config = { - .mck_io_num = SYS_I2S_MCLK_PIN, - .bck_io_num = SYS_I2S_SCLK_PIN, - .ws_io_num = SYS_I2S_LRCK_PIN, - .data_out_num = SYS_I2S_DOUT_PIN, - .data_in_num = SYS_I2S_DIN_PIN, -}; - -void setup() -{ - M5.begin(); - Serial.begin(115200); - for (int address = 1; address < 127; address++) { - Wire.beginTransmission(address); - error = Wire.endTransmission(); - if (error == 0) { - Serial.print("Found I2C device at address 0x"); - Serial.print(address, HEX); - Serial.print(" \n"); - } else if (error == 4) { - Serial.print("Unknown error at address 0x"); - Serial.println(address, HEX); - } - } - device.begin(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); - device.setHPMode(AUDIO_HPMODE_NATIONAL); - device.setMICStatus(AUDIO_MIC_OPEN); - device.setRGBBrightness(100); - Serial.printf("getHPMode:%d\n", device.getHPMode()); - Serial.printf("getMICStatus:%d\n", device.getMICStatus()); - for (int i = 0; i <= 2; i++) { - device.setRGBLED(i, color[i + 3]); - // Output the hexadecimal value of the current color - Serial.printf("Set RGB to %06X\n", (unsigned int)color[i + 3]); - Serial.printf("get RGB to %06X\n", device.getRGBLED(i)); - } - Serial.println("Read Reg ES8388 : "); - if (!es8388.init()) Serial.println("Init Fail"); - es8388.setADCVolume(100); - es8388.setDACVolume(80); - es8388.setDACOutput(DAC_OUTPUT_OUT1); - es8388.setBitsSample(ES_MODULE_ADC, BIT_LENGTH_16BITS); - es8388.setSampleRate(SAMPLE_RATE_44K); - uint8_t *reg; - for (uint8_t i = 0; i < 53; i++) { - reg = es8388.readAllReg(); - Serial.printf("Reg-%02d = 0x%02x\r\n", i, reg[i]); - } - // i2s - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - WRITE_PERI_REG(PIN_CTRL, 0xFFF0); - i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); - i2s_set_pin(I2S_NUM_0, &pin_config); - // init SPIFFS - if (!SPIFFS.begin(true)) { - Serial.println("Failed to mount SPIFFS"); - return; - } - xTaskCreate(i2s_write_task, "i2s_write_task", 1024 * 8, NULL, 6, NULL); -} - -void loop() -{ -} - -void i2s_write_task(void *arg) -{ - // Open the WAV file - // Replace with the path of your WAV file - File file = SPIFFS.open("/hello.wav", "r"); - if (!file) { - Serial.println("Failed to open WAV file for reading"); - vTaskDelete(NULL); - } - - // Obtain the file size and print - size_t fileSize = file.size(); - Serial.printf("File size: %d bytes\n", fileSize); - - // Skip the WAV file header (44 bytes) - file.seek(44); - - uint8_t txbuf[1024]; - - while (1) { - // Check whether the end of the file has been reached - if (file.available() == 0) { - // Go back to the beginning of the file in order to replay it - file.seek(44); - } - - // Read data - size_t bytesRead = file.read(txbuf, sizeof(txbuf)); - - // If data is read - if (bytesRead > 0) { - size_t bytesWritten = 0; - esp_err_t result = i2s_write(I2S_NUM_0, txbuf, bytesRead, &bytesWritten, portMAX_DELAY); - // Check the writing result - if (result != ESP_OK) { - Serial.printf("I2S write error: %d\n", result); - } - } else { - // If there is no more data, exit the loop - break; - } - } - - // Close the file - file.close(); - vTaskDelete(NULL); -} \ No newline at end of file diff --git a/examples/M5Core2/i2sloopback/i2sloopback.ino b/examples/M5Core2/i2sloopback/i2sloopback.ino deleted file mode 100644 index 55cfcff..0000000 --- a/examples/M5Core2/i2sloopback/i2sloopback.ino +++ /dev/null @@ -1,120 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD - * - * SPDX-License-Identifier: MIT - * - * @Hardwares: M5Core2 + Module Audio - * @Dependent Library: - * M5Unified@^0.2.5: https://github.com/m5stack/M5Unified - * Module Audio: https://github.com/m5stack/M5Module-Audio - * - * Please rollback these libraries if you encounter I2S initialization issues. - * Board Manager M5Stack@2.0.7; - * M5Unified@0.1.17; - */ - -#include "M5Unified.h" -#include "audio_i2c.hpp" -#include "es8388.hpp" -#include "driver/i2s.h" -AudioI2c device; - -#define SYS_I2C_SDA_PIN 21 -#define SYS_I2C_SCL_PIN 22 -#define SYS_I2S_MCLK_PIN 0 -#define SYS_I2S_SCLK_PIN 19 -#define SYS_I2S_LRCK_PIN 27 -#define SYS_I2S_DOUT_PIN 2 -#define SYS_I2S_DIN_PIN 34 -#define SYS_SPI_MISO_PIN 38 -#define SYS_SPI_MOSI_PIN 23 -#define SYS_SPI_CLK_PIN 18 -#define SYS_SPI_CS_PIN 4 - -ES8388 es8388(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); - -uint16_t rxbuf[256], txbuf[256]; -size_t readsize = 0; -byte error, address; -const uint32_t color[] = {0xFF0000, 0xFF0000, 0xFF0000, 0x00FF00, 0xFFFF00, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}; -i2s_config_t i2s_config = {.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX), - .sample_rate = 44100, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = I2S_COMM_FORMAT_STAND_I2S, - .intr_alloc_flags = 0, - .dma_buf_count = 8, - .dma_buf_len = 512, - .use_apll = false, - .tx_desc_auto_clear = true, - .fixed_mclk = 0}; - -i2s_pin_config_t pin_config = { - .mck_io_num = SYS_I2S_MCLK_PIN, - .bck_io_num = SYS_I2S_SCLK_PIN, - .ws_io_num = SYS_I2S_LRCK_PIN, - .data_out_num = SYS_I2S_DOUT_PIN, - .data_in_num = SYS_I2S_DIN_PIN, -}; - -void setup() -{ - M5.begin(); - Serial.begin(115200); - for (int address = 1; address < 127; address++) { - Wire.beginTransmission(address); - error = Wire.endTransmission(); - if (error == 0) { - Serial.print("Found I2C device at address 0x"); - Serial.print(address, HEX); - Serial.print(" \n"); - } else if (error == 4) { - Serial.print("Unknown error at address 0x"); - Serial.println(address, HEX); - } - } - device.begin(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); - device.setHPMode(AUDIO_HPMODE_NATIONAL); - device.setMICStatus(AUDIO_MIC_OPEN); - device.setRGBBrightness(100); - Serial.printf("getHPMode:%d\n", device.getHPMode()); - Serial.printf("getMICStatus:%d\n", device.getMICStatus()); - for (int i = 0; i <= 2; i++) { - device.setRGBLED(i, color[i + 3]); - // Output the hexadecimal value of the current color - Serial.printf("Set RGB to %06X\n", (unsigned int)color[i + 3]); - Serial.printf("get RGB to %06X\n", device.getRGBLED(i)); - } - Serial.println("Read Reg ES8388 : "); - if (!es8388.init()) Serial.println("Init Fail"); - es8388.setADCInput(ADC_INPUT_LINPUT1_RINPUT1); - es8388.setMicGain(MIC_GAIN_24DB); - es8388.setADCVolume(100); - // The volume output should not exceed 40, otherwise there will be noise or current sounds - es8388.setDACVolume(40); - es8388.setDACOutput(DAC_OUTPUT_OUT1); - es8388.setBitsSample(ES_MODULE_ADC, BIT_LENGTH_16BITS); - es8388.setSampleRate(SAMPLE_RATE_44K); - uint8_t *reg; - for (uint8_t i = 0; i < 53; i++) { - reg = es8388.readAllReg(); - Serial.printf("Reg-%02d = 0x%02x\r\n", i, reg[i]); - } - // i2s - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - WRITE_PERI_REG(PIN_CTRL, 0xFFF0); - i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); - i2s_set_pin(I2S_NUM_0, &pin_config); -} - -void loop() -{ - i2s_read(I2S_NUM_0, &rxbuf[0], 256 * 2, &readsize, 1000); - for (int i = 0; i < 256; i++) { - // direct transfer too txbuff - txbuf[i] = rxbuf[i]; - // txbuf[i] = 0; //mute - } - // play received buffer - i2s_write(I2S_NUM_0, &txbuf[0], 256 * 2, &readsize, 1000); -} \ No newline at end of file diff --git a/examples/M5Core2/player_sd/player_sd.ino b/examples/M5Core2/player_sd/player_sd.ino deleted file mode 100644 index 5588e36..0000000 --- a/examples/M5Core2/player_sd/player_sd.ino +++ /dev/null @@ -1,166 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD - * - * SPDX-License-Identifier: MIT - * - * @Hardwares: M5Core2 + Module Audio - * @Dependent Library: - * M5Unified@^0.2.5: https://github.com/m5stack/M5Unified - * Module Audio: https://github.com/m5stack/M5Module-Audio - * - * Please rollback these libraries if you encounter I2S initialization issues. - * Board Manager M5Stack@2.0.7; - * M5Unified@0.1.17; - */ - -#include "M5Unified.h" -#include "audio_i2c.hpp" -#include "es8388.hpp" -#include "driver/i2s.h" -#include -#include -AudioI2c device; - -#define SYS_I2C_SDA_PIN 21 -#define SYS_I2C_SCL_PIN 22 -#define SYS_I2S_MCLK_PIN 0 -#define SYS_I2S_SCLK_PIN 19 -#define SYS_I2S_LRCK_PIN 27 -#define SYS_I2S_DOUT_PIN 2 -#define SYS_I2S_DIN_PIN 34 -#define SYS_SPI_MISO_PIN 38 -#define SYS_SPI_MOSI_PIN 23 -#define SYS_SPI_CLK_PIN 18 -#define SYS_SPI_CS_PIN 4 - -ES8388 es8388(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); -void i2s_write_task(void *arg); -uint16_t rxbuf[256], txbuf[256]; -size_t readsize = 0; -byte error, address; -const uint32_t color[] = {0xFF0000, 0xFF0000, 0xFF0000, 0x00FF00, 0xFFFF00, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}; -i2s_config_t i2s_config = {.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX), - .sample_rate = 44100, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = I2S_COMM_FORMAT_STAND_I2S, - .intr_alloc_flags = 0, - .dma_buf_count = 8, - .dma_buf_len = 512, - .use_apll = false, - .tx_desc_auto_clear = true, - .fixed_mclk = 0}; - -i2s_pin_config_t pin_config = { - .mck_io_num = SYS_I2S_MCLK_PIN, - .bck_io_num = SYS_I2S_SCLK_PIN, - .ws_io_num = SYS_I2S_LRCK_PIN, - .data_out_num = SYS_I2S_DOUT_PIN, - .data_in_num = SYS_I2S_DIN_PIN, -}; - -void setup() -{ - M5.begin(); - Serial.begin(115200); - for (int address = 1; address < 127; address++) { - Wire.beginTransmission(address); - error = Wire.endTransmission(); - if (error == 0) { - Serial.print("Found I2C device at address 0x"); - Serial.print(address, HEX); - Serial.print(" \n"); - } else if (error == 4) { - Serial.print("Unknown error at address 0x"); - Serial.println(address, HEX); - } - } - device.begin(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); - device.setHPMode(AUDIO_HPMODE_NATIONAL); - device.setMICStatus(AUDIO_MIC_OPEN); - device.setRGBBrightness(100); - Serial.printf("getHPMode:%d\n", device.getHPMode()); - Serial.printf("getMICStatus:%d\n", device.getMICStatus()); - for (int i = 0; i <= 2; i++) { - device.setRGBLED(i, color[i + 3]); - // Output the hexadecimal value of the current color - Serial.printf("Set RGB to %06X\n", (unsigned int)color[i + 3]); - Serial.printf("get RGB to %06X\n", device.getRGBLED(i)); - } - Serial.println("Read Reg ES8388 : "); - if (!es8388.init()) Serial.println("Init Fail"); - es8388.setADCVolume(100); - es8388.setDACVolume(80); - es8388.setDACOutput(DAC_OUTPUT_OUT1); - es8388.setBitsSample(ES_MODULE_ADC, BIT_LENGTH_16BITS); - es8388.setSampleRate(SAMPLE_RATE_44K); - uint8_t *reg; - for (uint8_t i = 0; i < 53; i++) { - reg = es8388.readAllReg(); - Serial.printf("Reg-%02d = 0x%02x\r\n", i, reg[i]); - } - // i2s - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - WRITE_PERI_REG(PIN_CTRL, 0xFFF0); - i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); - i2s_set_pin(I2S_NUM_0, &pin_config); - if (SD.begin(SYS_SPI_CS_PIN)) { - Serial.println("SD card initialized successfully"); - - } else { - Serial.println("Failed to initialize SD card. Retrying..."); - } - xTaskCreate(i2s_write_task, "i2s_write_task", 1024 * 8, NULL, 6, NULL); -} - -void loop() -{ -} - -void i2s_write_task(void *arg) -{ - // Open the WAV file - // Replace with the path of your WAV file - File file = SD.open("/hello.wav", "r"); - if (!file) { - Serial.println("Failed to open WAV file for reading"); - vTaskDelete(NULL); - } - - // Obtain the file size and print - size_t fileSize = file.size(); - Serial.printf("File size: %d bytes\n", fileSize); - - // Skip the WAV file header (44 bytes) - file.seek(44); - - uint8_t txbuf[1024]; - - while (1) { - // Check whether the end of the file has been reached - if (file.available() == 0) { - // Go back to the beginning of the file in order to replay it - file.seek(44); - } - - // Read data - size_t bytesRead = file.read(txbuf, sizeof(txbuf)); - - // If data is read - if (bytesRead > 0) { - size_t bytesWritten = 0; - esp_err_t result = i2s_write(I2S_NUM_0, txbuf, bytesRead, &bytesWritten, portMAX_DELAY); - // Check the writing result - if (result != ESP_OK) { - Serial.printf("I2S write error: %d\n", result); - } - } else { - // If there is no more data, exit the loop - break; - } - } - - // Close the file - file.close(); - vTaskDelete(NULL); -} \ No newline at end of file diff --git a/examples/M5Core2/player_spiffs/player_spiffs.ino b/examples/M5Core2/player_spiffs/player_spiffs.ino deleted file mode 100644 index 0ceba9d..0000000 --- a/examples/M5Core2/player_spiffs/player_spiffs.ino +++ /dev/null @@ -1,165 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD - * - * SPDX-License-Identifier: MIT - * - * @Hardwares: M5Core2 + Module Audio - * @Dependent Library: - * M5Unified@^0.2.5: https://github.com/m5stack/M5Unified - * Module Audio: https://github.com/m5stack/M5Module-Audio - * - * Please rollback these libraries if you encounter I2S initialization issues. - * Board Manager M5Stack@2.0.7; - * M5Unified@0.1.17; - */ - -#include "M5Unified.h" -#include "audio_i2c.hpp" -#include "es8388.hpp" -#include "driver/i2s.h" -#include -#include -AudioI2c device; - -#define SYS_I2C_SDA_PIN 21 -#define SYS_I2C_SCL_PIN 22 -#define SYS_I2S_MCLK_PIN 0 -#define SYS_I2S_SCLK_PIN 19 -#define SYS_I2S_LRCK_PIN 27 -#define SYS_I2S_DOUT_PIN 2 -#define SYS_I2S_DIN_PIN 34 -#define SYS_SPI_MISO_PIN 38 -#define SYS_SPI_MOSI_PIN 23 -#define SYS_SPI_CLK_PIN 18 -#define SYS_SPI_CS_PIN 4 - -ES8388 es8388(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); -void i2s_write_task(void *arg); -uint16_t rxbuf[256], txbuf[256]; -size_t readsize = 0; -byte error, address; -const uint32_t color[] = {0xFF0000, 0xFF0000, 0xFF0000, 0x00FF00, 0xFFFF00, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}; -i2s_config_t i2s_config = {.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX), - .sample_rate = 44100, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = I2S_COMM_FORMAT_STAND_I2S, - .intr_alloc_flags = 0, - .dma_buf_count = 8, - .dma_buf_len = 512, - .use_apll = false, - .tx_desc_auto_clear = true, - .fixed_mclk = 0}; - -i2s_pin_config_t pin_config = { - .mck_io_num = SYS_I2S_MCLK_PIN, - .bck_io_num = SYS_I2S_SCLK_PIN, - .ws_io_num = SYS_I2S_LRCK_PIN, - .data_out_num = SYS_I2S_DOUT_PIN, - .data_in_num = SYS_I2S_DIN_PIN, -}; - -void setup() -{ - M5.begin(); - Serial.begin(115200); - for (int address = 1; address < 127; address++) { - Wire.beginTransmission(address); - error = Wire.endTransmission(); - if (error == 0) { - Serial.print("Found I2C device at address 0x"); - Serial.print(address, HEX); - Serial.print(" \n"); - } else if (error == 4) { - Serial.print("Unknown error at address 0x"); - Serial.println(address, HEX); - } - } - device.begin(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); - device.setHPMode(AUDIO_HPMODE_NATIONAL); - device.setMICStatus(AUDIO_MIC_OPEN); - device.setRGBBrightness(100); - Serial.printf("getHPMode:%d\n", device.getHPMode()); - Serial.printf("getMICStatus:%d\n", device.getMICStatus()); - for (int i = 0; i <= 2; i++) { - device.setRGBLED(i, color[i + 3]); - // Output the hexadecimal value of the current color - Serial.printf("Set RGB to %06X\n", (unsigned int)color[i + 3]); - Serial.printf("get RGB to %06X\n", device.getRGBLED(i)); - } - Serial.println("Read Reg ES8388 : "); - if (!es8388.init()) Serial.println("Init Fail"); - es8388.setADCVolume(100); - es8388.setDACVolume(80); - es8388.setDACOutput(DAC_OUTPUT_OUT1); - es8388.setBitsSample(ES_MODULE_ADC, BIT_LENGTH_16BITS); - es8388.setSampleRate(SAMPLE_RATE_44K); - uint8_t *reg; - for (uint8_t i = 0; i < 53; i++) { - reg = es8388.readAllReg(); - Serial.printf("Reg-%02d = 0x%02x\r\n", i, reg[i]); - } - // i2s - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - WRITE_PERI_REG(PIN_CTRL, 0xFFF0); - i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); - i2s_set_pin(I2S_NUM_0, &pin_config); - // init SPIFFS - if (!SPIFFS.begin(true)) { - Serial.println("Failed to mount SPIFFS"); - return; - } - xTaskCreate(i2s_write_task, "i2s_write_task", 1024 * 8, NULL, 6, NULL); -} - -void loop() -{ -} - -void i2s_write_task(void *arg) -{ - // Open the WAV file - // Replace with the path of your WAV file - File file = SPIFFS.open("/hello.wav", "r"); - if (!file) { - Serial.println("Failed to open WAV file for reading"); - vTaskDelete(NULL); - } - - // Obtain the file size and print - size_t fileSize = file.size(); - Serial.printf("File size: %d bytes\n", fileSize); - - // Skip the WAV file header (44 bytes) - file.seek(44); - - uint8_t txbuf[1024]; - - while (1) { - // Check whether the end of the file has been reached - if (file.available() == 0) { - // Go back to the beginning of the file in order to replay it - file.seek(44); - } - - // Read data - size_t bytesRead = file.read(txbuf, sizeof(txbuf)); - - // If data is read - if (bytesRead > 0) { - size_t bytesWritten = 0; - esp_err_t result = i2s_write(I2S_NUM_0, txbuf, bytesRead, &bytesWritten, portMAX_DELAY); - // Check the writing result - if (result != ESP_OK) { - Serial.printf("I2S write error: %d\n", result); - } - } else { - // If there is no more data, exit the loop - break; - } - } - - // Close the file - file.close(); - vTaskDelete(NULL); -} \ No newline at end of file diff --git a/examples/M5Cores3/i2sloopback/i2sloopback.ino b/examples/M5Cores3/i2sloopback/i2sloopback.ino deleted file mode 100644 index 185b966..0000000 --- a/examples/M5Cores3/i2sloopback/i2sloopback.ino +++ /dev/null @@ -1,200 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD - * - * SPDX-License-Identifier: MIT - * - * @Hardwares: M5CoreS3 + Module Audio - * @Dependent Library: - * M5Unified@^0.2.5: https://github.com/m5stack/M5Unified - * Module Audio: https://github.com/m5stack/M5Module-Audio - * - * Please rollback these libraries if you encounter I2S initialization issues. - * Board Manager M5Stack@2.0.7; - * M5Unified@0.1.17; - */ - -#include "M5Unified.h" -#include "audio_i2c.hpp" -#include "es8388.hpp" - -AudioI2c device; - -#define USE_NEW_I2S_API 1 -#define RECORD_SIZE (44100 * 2 * 2 * 2) - -#if USE_NEW_I2S_API -#include -I2SClass I2S; -#else -#include "driver/i2s.h" -#endif - -#define SYS_I2C_SDA_PIN 12 -#define SYS_I2C_SCL_PIN 11 -#define SYS_I2S_MCLK_PIN 0 -#define SYS_I2S_SCLK_PIN 7 -#define SYS_I2S_LRCK_PIN 6 -#define SYS_I2S_DOUT_PIN 13 -#define SYS_I2S_DIN_PIN 14 -#define SYS_SPI_MISO_PIN 35 -#define SYS_SPI_MOSI_PIN 37 -#define SYS_SPI_CLK_PIN 36 -#define SYS_SPI_CS_PIN 4 - -ES8388 es8388(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); -static uint8_t *buffer = nullptr; -uint16_t rxbuf[1024], txbuf[1024]; -size_t readsize = 0; -byte error, address; -const uint32_t color[] = {0xFF0000, 0xFF0000, 0xFF0000, 0x00FF00, 0xFFFF00, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}; - -void setup() -{ - M5.begin(); - Serial.begin(115200); - for (int address = 1; address < 127; address++) { - Wire.beginTransmission(address); - error = Wire.endTransmission(); - if (error == 0) { - Serial.print("Found I2C device at address 0x"); - Serial.print(address, HEX); - Serial.print(" \n"); - } else if (error == 4) { - Serial.print("Unknown error at address 0x"); - Serial.println(address, HEX); - } - } - device.begin(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); - device.setHPMode(AUDIO_HPMODE_NATIONAL); - device.setMICStatus(AUDIO_MIC_OPEN); - device.setRGBBrightness(100); - Serial.printf("getHPMode:%d\n", device.getHPMode()); - Serial.printf("getMICStatus:%d\n", device.getMICStatus()); - for (int i = 0; i <= 2; i++) { - device.setRGBLED(i, color[i + 3]); - // Output the hexadecimal value of the current color - Serial.printf("Set RGB to %06X\n", (unsigned int)color[i + 3]); - Serial.printf("get RGB to %06X\n", device.getRGBLED(i)); - } - Serial.println("Read Reg ES8388 : "); - if (!es8388.init()) Serial.println("Init Fail"); - es8388.setADCInput(ADC_INPUT_LINPUT1_RINPUT1); - es8388.setMicGain(MIC_GAIN_24DB); - es8388.setADCVolume(100); - // The volume output should not exceed 40, otherwise there will be noise or current sounds - es8388.setDACVolume(40); - es8388.setDACOutput(DAC_OUTPUT_OUT1); - es8388.setBitsSample(ES_MODULE_ADC, BIT_LENGTH_16BITS); - es8388.setSampleRate(SAMPLE_RATE_44K); - uint8_t *reg; - for (uint8_t i = 0; i < 53; i++) { - reg = es8388.readAllReg(); - Serial.printf("Reg-%02d = 0x%02x\r\n", i, reg[i]); - } -#if USE_NEW_I2S_API - uint32_t sample_rate = 44100; - uint8_t i2s_bck = SYS_I2S_SCLK_PIN; - uint8_t i2s_ws = SYS_I2S_LRCK_PIN; - uint8_t i2s_do = SYS_I2S_DOUT_PIN; - uint8_t i2s_di = SYS_I2S_DIN_PIN; - uint8_t i2s_mc = SYS_I2S_MCLK_PIN; - I2S.setPins(i2s_bck, i2s_ws, i2s_do, i2s_di, i2s_mc); // SCK, WS, SDOUT, SDIN, MCLK - I2S.begin(I2S_MODE_STD, 44100, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO); - I2S.configureRX(44100, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO); - - I2S.configureTX(44100, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO); -#else - i2s_config_t i2s_config = {.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX), - .sample_rate = 44100, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = I2S_COMM_FORMAT_STAND_I2S, - .intr_alloc_flags = 0, - .dma_buf_count = 8, - .dma_buf_len = 512, - .use_apll = false, - .tx_desc_auto_clear = true, - .fixed_mclk = 0}; - - i2s_pin_config_t pin_config = { - .mck_io_num = SYS_I2S_MCLK_PIN, - .bck_io_num = SYS_I2S_SCLK_PIN, - .ws_io_num = SYS_I2S_LRCK_PIN, - .data_out_num = SYS_I2S_DOUT_PIN, - .data_in_num = SYS_I2S_DIN_PIN, - }; - - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - WRITE_PERI_REG(PIN_CTRL, 0xFFF0); - i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); - i2s_set_pin(I2S_NUM_0, &pin_config); - -#endif - buffer = (uint8_t *)malloc(RECORD_SIZE); // Allocate memory for the record buffer. - // Check if memory allocation was successful. - if (buffer == nullptr) { - // If memory allocation fails, enter an infinite loop. - while (true) { - Serial.println("Failed to allocate memory :("); - delay(1000); - } - } -} -void loop() -{ - size_t bytes_to_record = RECORD_SIZE; // sample_rate * bytes_per_sample * channels - - size_t bytes_read = 0; - -#if USE_NEW_I2S_API - Serial.println("Start recording"); - bytes_read = I2S.readBytes((char *)buffer, bytes_to_record); - if (bytes_read < 0) { - ESP_LOGI(TAG, "Recording failed during I2S read"); - } - -#else - esp_err_t err = i2s_read(I2S_NUM_0, &rxbuf[0], 1024 * 96, &readsize, 1000); - - if (err != ESP_OK) { - ESP_LOGI(TAG, "Recording failed during I2S read"); - } else - ESP_LOGI(TAG, "Recording"); -#endif - delay(100); - size_t samples = bytes_read / sizeof(int16_t); - int16_t *pcm = (int16_t *)buffer; - - int16_t peak = 0; - - for (size_t i = 0; i < samples; i++) { - int16_t v = pcm[i]; - if (v < 0) v = -v; - if (v > peak) peak = v; - } - - float level = (float)peak / 32767.0f; - - Serial.printf("Level: %.3f\n", level); - for (int i = 0; i < 1024; i++) { - // direct transfer too txbuff - txbuf[i] = rxbuf[i]; - // txbuf[i] = 0; //mute - } - // play received buffer - size_t bytes_written = 0; -#if USE_NEW_I2S_API - Serial.println("Start playing"); - bytes_written = I2S.write(buffer, RECORD_SIZE); - if (bytes_written < 0) { - ESP_LOGI(TAG, "Playback failed"); - } - -#else - err = i2s_write(I2S_NUM_0, &txbuf[0], 1024 * 96, &readsize, 1000); - if (err != ESP_OK) { - ESP_LOGI(TAG, "Playback failed during I2S write"); - } -#endif - delay(100); -} diff --git a/examples/M5Cores3/player_sd/player_sd.ino b/examples/M5Cores3/player_sd/player_sd.ino deleted file mode 100644 index 95926d0..0000000 --- a/examples/M5Cores3/player_sd/player_sd.ino +++ /dev/null @@ -1,166 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD - * - * SPDX-License-Identifier: MIT - * - * @Hardwares: M5CoreS3 + Module Audio - * @Dependent Library: - * M5Unified@^0.2.5: https://github.com/m5stack/M5Unified - * Module Audio: https://github.com/m5stack/M5Module-Audio - * - * Please rollback these libraries if you encounter I2S initialization issues. - * Board Manager M5Stack@2.0.7; - * M5Unified@0.1.17; - */ - -#include "M5Unified.h" -#include "audio_i2c.hpp" -#include "es8388.hpp" -#include "driver/i2s.h" -#include -#include -AudioI2c device; - -#define SYS_I2C_SDA_PIN 12 -#define SYS_I2C_SCL_PIN 11 -#define SYS_I2S_MCLK_PIN 0 -#define SYS_I2S_SCLK_PIN 7 -#define SYS_I2S_LRCK_PIN 6 -#define SYS_I2S_DOUT_PIN 13 -#define SYS_I2S_DIN_PIN 14 -#define SYS_SPI_MISO_PIN 35 -#define SYS_SPI_MOSI_PIN 37 -#define SYS_SPI_CLK_PIN 36 -#define SYS_SPI_CS_PIN 4 - -ES8388 es8388(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); -void i2s_write_task(void *arg); -uint16_t rxbuf[256], txbuf[256]; -size_t readsize = 0; -byte error, address; -const uint32_t color[] = {0xFF0000, 0xFF0000, 0xFF0000, 0x00FF00, 0xFFFF00, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}; -i2s_config_t i2s_config = {.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX), - .sample_rate = 44100, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = I2S_COMM_FORMAT_STAND_I2S, - .intr_alloc_flags = 0, - .dma_buf_count = 8, - .dma_buf_len = 512, - .use_apll = false, - .tx_desc_auto_clear = true, - .fixed_mclk = 0}; - -i2s_pin_config_t pin_config = { - .mck_io_num = SYS_I2S_MCLK_PIN, - .bck_io_num = SYS_I2S_SCLK_PIN, - .ws_io_num = SYS_I2S_LRCK_PIN, - .data_out_num = SYS_I2S_DOUT_PIN, - .data_in_num = SYS_I2S_DIN_PIN, -}; - -void setup() -{ - M5.begin(); - Serial.begin(115200); - for (int address = 1; address < 127; address++) { - Wire.beginTransmission(address); - error = Wire.endTransmission(); - if (error == 0) { - Serial.print("Found I2C device at address 0x"); - Serial.print(address, HEX); - Serial.print(" \n"); - } else if (error == 4) { - Serial.print("Unknown error at address 0x"); - Serial.println(address, HEX); - } - } - device.begin(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); - device.setHPMode(AUDIO_HPMODE_NATIONAL); - device.setMICStatus(AUDIO_MIC_OPEN); - device.setRGBBrightness(100); - Serial.printf("getHPMode:%d\n", device.getHPMode()); - Serial.printf("getMICStatus:%d\n", device.getMICStatus()); - for (int i = 0; i <= 2; i++) { - device.setRGBLED(i, color[i + 3]); - // Output the hexadecimal value of the current color - Serial.printf("Set RGB to %06X\n", (unsigned int)color[i + 3]); - Serial.printf("get RGB to %06X\n", device.getRGBLED(i)); - } - Serial.println("Read Reg ES8388 : "); - if (!es8388.init()) Serial.println("Init Fail"); - es8388.setADCVolume(100); - es8388.setDACVolume(80); - es8388.setDACOutput(DAC_OUTPUT_OUT1); - es8388.setBitsSample(ES_MODULE_ADC, BIT_LENGTH_16BITS); - es8388.setSampleRate(SAMPLE_RATE_44K); - uint8_t *reg; - for (uint8_t i = 0; i < 53; i++) { - reg = es8388.readAllReg(); - Serial.printf("Reg-%02d = 0x%02x\r\n", i, reg[i]); - } - // i2s - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - WRITE_PERI_REG(PIN_CTRL, 0xFFF0); - i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); - i2s_set_pin(I2S_NUM_0, &pin_config); - if (SD.begin(SYS_SPI_CS_PIN)) { - Serial.println("SD card initialized successfully"); - - } else { - Serial.println("Failed to initialize SD card. Retrying..."); - } - xTaskCreate(i2s_write_task, "i2s_write_task", 1024 * 8, NULL, 6, NULL); -} - -void loop() -{ -} - -void i2s_write_task(void *arg) -{ - // Open the WAV file - // Replace with the path of your WAV file - File file = SD.open("/hello.wav", "r"); - if (!file) { - Serial.println("Failed to open WAV file for reading"); - vTaskDelete(NULL); - } - - // Obtain the file size and print - size_t fileSize = file.size(); - Serial.printf("File size: %d bytes\n", fileSize); - - // Skip the WAV file header (44 bytes) - file.seek(44); - - uint8_t txbuf[1024]; - - while (1) { - // Check whether the end of the file has been reached - if (file.available() == 0) { - // Go back to the beginning of the file in order to replay it - file.seek(44); - } - - // Read data - size_t bytesRead = file.read(txbuf, sizeof(txbuf)); - - // If data is read - if (bytesRead > 0) { - size_t bytesWritten = 0; - esp_err_t result = i2s_write(I2S_NUM_0, txbuf, bytesRead, &bytesWritten, portMAX_DELAY); - // Check the writing result - if (result != ESP_OK) { - Serial.printf("I2S write error: %d\n", result); - } - } else { - // If there is no more data, exit the loop - break; - } - } - - // Close the file - file.close(); - vTaskDelete(NULL); -} \ No newline at end of file diff --git a/examples/M5Cores3/player_spiffs/player_spiffs.ino b/examples/M5Cores3/player_spiffs/player_spiffs.ino deleted file mode 100644 index 0bdc903..0000000 --- a/examples/M5Cores3/player_spiffs/player_spiffs.ino +++ /dev/null @@ -1,165 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD - * - * SPDX-License-Identifier: MIT - * - * @Hardwares: M5CoreS3 + Module Audio - * @Dependent Library: - * M5Unified@^0.2.5: https://github.com/m5stack/M5Unified - * Module Audio: https://github.com/m5stack/M5Module-Audio - * - * Please rollback these libraries if you encounter I2S initialization issues. - * Board Manager M5Stack@2.0.7; - * M5Unified@0.1.17; - */ - -#include "M5Unified.h" -#include "audio_i2c.hpp" -#include "es8388.hpp" -#include "driver/i2s.h" -#include -#include -AudioI2c device; - -#define SYS_I2C_SDA_PIN 12 -#define SYS_I2C_SCL_PIN 11 -#define SYS_I2S_MCLK_PIN 0 -#define SYS_I2S_SCLK_PIN 7 -#define SYS_I2S_LRCK_PIN 6 -#define SYS_I2S_DOUT_PIN 13 -#define SYS_I2S_DIN_PIN 14 -#define SYS_SPI_MISO_PIN 35 -#define SYS_SPI_MOSI_PIN 37 -#define SYS_SPI_CLK_PIN 36 -#define SYS_SPI_CS_PIN 4 - -ES8388 es8388(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); -void i2s_write_task(void *arg); -uint16_t rxbuf[256], txbuf[256]; -size_t readsize = 0; -byte error, address; -const uint32_t color[] = {0xFF0000, 0xFF0000, 0xFF0000, 0x00FF00, 0xFFFF00, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF}; -i2s_config_t i2s_config = {.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX), - .sample_rate = 44100, - .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT, - .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT, - .communication_format = I2S_COMM_FORMAT_STAND_I2S, - .intr_alloc_flags = 0, - .dma_buf_count = 8, - .dma_buf_len = 512, - .use_apll = false, - .tx_desc_auto_clear = true, - .fixed_mclk = 0}; - -i2s_pin_config_t pin_config = { - .mck_io_num = SYS_I2S_MCLK_PIN, - .bck_io_num = SYS_I2S_SCLK_PIN, - .ws_io_num = SYS_I2S_LRCK_PIN, - .data_out_num = SYS_I2S_DOUT_PIN, - .data_in_num = SYS_I2S_DIN_PIN, -}; - -void setup() -{ - M5.begin(); - Serial.begin(115200); - for (int address = 1; address < 127; address++) { - Wire.beginTransmission(address); - error = Wire.endTransmission(); - if (error == 0) { - Serial.print("Found I2C device at address 0x"); - Serial.print(address, HEX); - Serial.print(" \n"); - } else if (error == 4) { - Serial.print("Unknown error at address 0x"); - Serial.println(address, HEX); - } - } - device.begin(&Wire, SYS_I2C_SDA_PIN, SYS_I2C_SCL_PIN); - device.setHPMode(AUDIO_HPMODE_NATIONAL); - device.setMICStatus(AUDIO_MIC_OPEN); - device.setRGBBrightness(100); - Serial.printf("getHPMode:%d\n", device.getHPMode()); - Serial.printf("getMICStatus:%d\n", device.getMICStatus()); - for (int i = 0; i <= 2; i++) { - device.setRGBLED(i, color[i + 3]); - // Output the hexadecimal value of the current color - Serial.printf("Set RGB to %06X\n", (unsigned int)color[i + 3]); - Serial.printf("get RGB to %06X\n", device.getRGBLED(i)); - } - Serial.println("Read Reg ES8388 : "); - if (!es8388.init()) Serial.println("Init Fail"); - es8388.setADCVolume(100); - es8388.setDACVolume(80); - es8388.setDACOutput(DAC_OUTPUT_OUT1); - es8388.setBitsSample(ES_MODULE_ADC, BIT_LENGTH_16BITS); - es8388.setSampleRate(SAMPLE_RATE_44K); - uint8_t *reg; - for (uint8_t i = 0; i < 53; i++) { - reg = es8388.readAllReg(); - Serial.printf("Reg-%02d = 0x%02x\r\n", i, reg[i]); - } - // i2s - PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); - WRITE_PERI_REG(PIN_CTRL, 0xFFF0); - i2s_driver_install(I2S_NUM_0, &i2s_config, 0, NULL); - i2s_set_pin(I2S_NUM_0, &pin_config); - // init SPIFFS - if (!SPIFFS.begin(true)) { - Serial.println("Failed to mount SPIFFS"); - return; - } - xTaskCreate(i2s_write_task, "i2s_write_task", 1024 * 8, NULL, 6, NULL); -} - -void loop() -{ -} - -void i2s_write_task(void *arg) -{ - // Open the WAV file - // Replace with the path of your WAV file - File file = SPIFFS.open("/hello.wav", "r"); - if (!file) { - Serial.println("Failed to open WAV file for reading"); - vTaskDelete(NULL); - } - - // Obtain the file size and print - size_t fileSize = file.size(); - Serial.printf("File size: %d bytes\n", fileSize); - - // Skip the WAV file header (44 bytes) - file.seek(44); - - uint8_t txbuf[1024]; - - while (1) { - // Check whether the end of the file has been reached - if (file.available() == 0) { - // Go back to the beginning of the file in order to replay it - file.seek(44); - } - - // Read data - size_t bytesRead = file.read(txbuf, sizeof(txbuf)); - - // If data is read - if (bytesRead > 0) { - size_t bytesWritten = 0; - esp_err_t result = i2s_write(I2S_NUM_0, txbuf, bytesRead, &bytesWritten, portMAX_DELAY); - // Check the writing result - if (result != ESP_OK) { - Serial.printf("I2S write error: %d\n", result); - } - } else { - // If there is no more data, exit the loop - break; - } - } - - // Close the file - file.close(); - vTaskDelete(NULL); -} \ No newline at end of file diff --git a/examples/i2sloopback/i2sloopback.ino b/examples/i2sloopback/i2sloopback.ino new file mode 100644 index 0000000..520f9aa --- /dev/null +++ b/examples/i2sloopback/i2sloopback.ino @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + * + * @Hardwares: M5Core/M5Core2/M5CoreS3 + Module Audio + * @Dependent Library: + * M5Unified: https://github.com/m5stack/M5Unified + * Module Audio: https://github.com/m5stack/M5Module-Audio + * + * @Note: If use CoreS3, please switch to the B Pins. + */ + +#include "M5Unified.h" +#include "M5Module_Audio.h" + +M5ModuleAudio device; + +static uint8_t audio_buf[4096]; + +void setup() +{ + delay(1000); + auto cfg = M5.config(); + cfg.serial_baudrate = 115200; + M5.begin(cfg); + + if (!device.begin(Wire)) { + while (1) { + Serial.println("Module Audio not found"); + delay(1000); + } + } + device.setHPMode(AUDIO_HPMODE_NATIONAL); + device.setMICStatus(AUDIO_MIC_OPEN); + device.setRGBBrightness(100); + Serial.printf("getHPMode:%d\n", device.getHPMode()); + Serial.printf("getMICStatus:%d\n", device.getMICStatus()); + for (int i = 0; i <= 2; i++) { + device.setRGBLED(i, 0xFFFFFF); + } + Serial.println("Read Reg ES8388 :"); + uint8_t *reg = device.readAllReg(); + if (reg != nullptr) { + for (uint8_t i = 0; i < 53; i++) { + Serial.printf("Reg-%02d = 0x%02x\r\n", i, reg[i]); + } + } + device.setMicInputLine(ADC_INPUT_LINPUT2_RINPUT2); + device.setMicGain(MIC_GAIN_12DB); + device.setMicAdcVolume(80); + device.setSpeakerVolume(50); + device.setSpeakerOutput(DAC_OUTPUT_OUT1); + device.setBitsSample(ES_MODULE_ADC_DAC, BIT_LENGTH_16BITS); + device.setSampleRate(SAMPLE_RATE_32K); +} + +void loop() +{ + if (!device.record(audio_buf, sizeof(audio_buf))) { + Serial.println("record failed"); + return; + } + + if (!device.play(audio_buf, sizeof(audio_buf))) { + Serial.println("play failed"); + return; + } +} diff --git a/examples/player_sd/player_sd.ino b/examples/player_sd/player_sd.ino new file mode 100644 index 0000000..5bc331e --- /dev/null +++ b/examples/player_sd/player_sd.ino @@ -0,0 +1,69 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + * + * @Hardwares: M5Core/M5Core2/M5CoreS3 + Module Audio + * @Dependent Library: + * M5Unified: https://github.com/m5stack/M5Unified + * Module Audio: https://github.com/m5stack/M5Module-Audio + * + * @Note: If use CoreS3, please switch to the B Pins. + * @Note: Please place hello.wav in the root directory of the SD card. + */ + +#include "M5Unified.h" +#include "M5Module_Audio.h" +#include +#include + +M5ModuleAudio device; + +void setup() +{ + delay(1000); + auto cfg = M5.config(); + cfg.serial_baudrate = 115200; + M5.begin(cfg); + + if (!device.begin(Wire)) { + while (1) { + Serial.println("Module Audio not found"); + delay(1000); + } + } + device.setHPMode(AUDIO_HPMODE_NATIONAL); + device.setMICStatus(AUDIO_MIC_OPEN); + device.setRGBBrightness(100); + Serial.printf("getHPMode:%d\n", device.getHPMode()); + Serial.printf("getMICStatus:%d\n", device.getMICStatus()); + for (int i = 0; i <= 2; i++) { + device.setRGBLED(i, 0xFFFFFF); + } + Serial.println("Read Reg ES8388 :"); + device.setMicAdcVolume(100); + device.setSpeakerVolume(40); + device.setSpeakerOutput(DAC_OUTPUT_OUT1); + device.setBitsSample(ES_MODULE_ADC, BIT_LENGTH_16BITS); + device.setSampleRate(SAMPLE_RATE_44K); + uint8_t *reg = device.readAllReg(); + if (reg != nullptr) { + for (uint8_t i = 0; i < 53; i++) { + Serial.printf("Reg-%02d = 0x%02x\r\n", i, reg[i]); + } + } + // The SPI_CS pin on the M5Core, M5Core2, and M5CoreS3 is GPIO4 + if (SD.begin(GPIO_NUM_4)) { + Serial.println("SD card initialized successfully"); + } else { + while (1) { + Serial.println("Failed to initialize SD card. Please check the SD card and try again."); + delay(1000); + } + } +} + +void loop() +{ + device.play(SD, "/hello.wav"); +} \ No newline at end of file diff --git a/examples/player_spiffs/player_spiffs.ino b/examples/player_spiffs/player_spiffs.ino new file mode 100644 index 0000000..a1a2129 --- /dev/null +++ b/examples/player_spiffs/player_spiffs.ino @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: 2026 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + * + * @Hardwares: M5Core/M5Core2/M5CoreS3 + Module Audio + * @Dependent Library: + * M5Unified: https://github.com/m5stack/M5Unified + * Module Audio: https://github.com/m5stack/M5Module-Aud@Note: If use CoreS3, please switch to the B Pins. + */ + +#include "M5Unified.h" +#include "M5Module_Audio.h" +#include +M5ModuleAudio device; + +void setup() +{ + delay(1000); + auto cfg = M5.config(); + cfg.serial_baudrate = 115200; + M5.begin(cfg); + + if (!device.begin(Wire)) { + while (1) { + Serial.println("Module Audio not found"); + delay(1000); + } + } + device.setHPMode(AUDIO_HPMODE_NATIONAL); + device.setMICStatus(AUDIO_MIC_OPEN); + device.setRGBBrightness(100); + Serial.printf("getHPMode:%d\n", device.getHPMode()); + Serial.printf("getMICStatus:%d\n", device.getMICStatus()); + for (int i = 0; i <= 2; i++) { + device.setRGBLED(i, 0xFFFFFF); + } + Serial.println("Read Reg ES8388 :"); + device.setMicAdcVolume(100); + device.setSpeakerVolume(80); + device.setSpeakerOutput(DAC_OUTPUT_OUT1); + device.setBitsSample(ES_MODULE_ADC, BIT_LENGTH_16BITS); + device.setSampleRate(SAMPLE_RATE_44K); + uint8_t *reg = device.readAllReg(); + if (reg != nullptr) { + for (uint8_t i = 0; i < 53; i++) { + Serial.printf("Reg-%02d = 0x%02x\r\n", i, reg[i]); + } + } + if (!SPIFFS.begin(true)) { + Serial.println("Failed to mount SPIFFS"); + return; + oc hbnmhgfghjkl; + ; + l + } +} + +void loop() +{ + device.play(SPIFFS, "/hello.wav"); +} \ No newline at end of file diff --git a/src/M5Module_Audio.cpp b/src/M5Module_Audio.cpp new file mode 100644 index 0000000..b370dd8 --- /dev/null +++ b/src/M5Module_Audio.cpp @@ -0,0 +1,726 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ + +#include "M5Module_Audio.h" + +#define TAG "EchoBase" + +namespace { + +uint32_t sample_rate_to_hz(es_sample_rate_t rate) +{ + switch (rate) { + case SAMPLE_RATE_8K: + return 8000; + case SAMPLE_RATE_11K: + return 11025; + case SAMPLE_RATE_16K: + return 16000; + case SAMPLE_RATE_24K: + return 24000; + case SAMPLE_RATE_32K: + return 32000; + case SAMPLE_RATE_44K: + return 44100; + case SAMPLE_RATE_48K: + return 48000; + default: + return 0; + } +} + +} // namespace + +#if USE_NEW_I2S_API +M5ModuleAudio::M5ModuleAudio() : _wire(nullptr), _sda(0), _scl(0), _addr(I2C_ADDR), es8388(nullptr) +{ +} +#else +M5ModuleAudio::M5ModuleAudio(i2s_port_t i2s_num) + : _wire(nullptr), _sda(0), _scl(0), _addr(I2C_ADDR), es8388(nullptr), i2s_num(i2s_num) +{ +} +#endif + +M5ModuleAudio::~M5ModuleAudio() +{ + if (es8388 != nullptr) { + delete es8388; + es8388 = nullptr; + } +} + +bool M5ModuleAudio::begin(TwoWire &wire, uint8_t addr) +{ + uint8_t sda = M5.getPin(m5::pin_name_t::in_i2c_sda); + uint8_t scl = M5.getPin(m5::pin_name_t::in_i2c_scl); +#if defined(CONFIG_IDF_TARGET_ESP32S3) + uint8_t bck = M5.getPin(m5::pin_name_t::mbus_pin24); // SCLK + uint8_t mck = M5.getPin(m5::pin_name_t::mbus_pin22); // MCLK +#else + uint8_t bck = M5.getPin(m5::pin_name_t::mbus_pin22); // SCLK + uint8_t mck = M5.getPin(m5::pin_name_t::mbus_pin24); // MCLK +#endif + uint8_t di = M5.getPin(m5::pin_name_t::mbus_pin26); + uint8_t ws = M5.getPin(m5::pin_name_t::mbus_pin21); // LRCK + uint8_t do_ = M5.getPin(m5::pin_name_t::mbus_pin23); + printf("I2C SDA: %d, SCL: %d\n", sda, scl); + printf("I2S MCK: %d, DI: %d, WS: %d, DO: %d, BCK: %d\n", mck, di, ws, do_, bck); + return begin(wire, sda, scl, addr, 400000, 44100, mck, di, ws, do_, bck); +} + +bool M5ModuleAudio::begin(TwoWire &wire, uint8_t sda, uint8_t scl, uint8_t addr, uint32_t speed) +{ + _i2s_initialized = false; + _wire = &wire; + _sda = sda; + _scl = scl; + _addr = addr; + + ESP_LOGI(TAG, "I2C initialized"); + + _wire->begin(_sda, _scl, speed); + delay(10); + _wire->beginTransmission(addr); + uint8_t error = _wire->endTransmission(); + if (error != 0) { + return false; + } + + if (!es8388_codec_init()) { + ESP_LOGI(TAG, "ES8388 codec initialization failed"); + return false; + } + + return setMicGain(MIC_GAIN_0DB); +} + +bool M5ModuleAudio::begin(TwoWire &wire, uint8_t sda, uint8_t scl, uint8_t addr, uint32_t speed, int sample_rate, + int i2s_mck, int i2s_di, int i2s_ws, int i2s_do, int i2s_bck) +{ + _wire = &wire; + _sda = sda; + _scl = scl; + _addr = addr; + + // Store IO pins + _i2s_mck = i2s_mck; + _i2s_bck = i2s_bck; + _i2s_ws = i2s_ws; + _i2s_do = i2s_do; + _i2s_di = i2s_di; + + // Initialize I2C + ESP_LOGI(TAG, "I2C initialized"); + + _wire->begin(_sda, _scl, speed); + delay(10); + _wire->beginTransmission(addr); + uint8_t error = _wire->endTransmission(); + if (error != 0) { + return false; + } + + // Initialize I2S driver + if (!i2s_driver_init(sample_rate)) { + ESP_LOGI(TAG, "I2S driver initialization failed"); + return false; + } + + // Initialize ES8388 codec + if (!es8388_codec_init()) { + ESP_LOGI(TAG, "ES8388 codec initialization failed"); + return false; + } + + setMicGain(MIC_GAIN_0DB); + + return true; +} + +void M5ModuleAudio::setMICStatus(audio_mic_t status) +{ + uint8_t reg = MICROPHONE_STATUS; + writeBytes(_addr, reg, (uint8_t *)&status, 1); +} + +void M5ModuleAudio::setHPMode(audio_hpmode_t mode) +{ + uint8_t reg = HEADPHONE_MODE; + writeBytes(_addr, reg, (uint8_t *)&mode, 1); +} + +void M5ModuleAudio::setRGBBrightness(uint8_t brightness) +{ + if (brightness > 100) brightness = 100; + uint8_t reg = RGB_LED_BRIGHTNESS; + writeBytes(_addr, reg, (uint8_t *)&brightness, 1); +} + +void M5ModuleAudio::setRGBLED(uint8_t num, uint32_t color) +{ + if (num > 2) num = 2; + color = ((color & 0xFF) << 16) | (color & 0xFF00) | ((color >> 16) & 0xFF); + uint8_t reg = RGB_LED + num * 3; + writeBytes(_addr, reg, (uint8_t *)&color, 3); +} + +uint8_t M5ModuleAudio::setI2CAddress(uint8_t newAddr) +{ + newAddr = constrain(newAddr, I2C_ADDR_MIN, I2C_ADDR_MAX); + uint8_t reg = I2C_ADDRESS; + writeBytes(_addr, reg, (uint8_t *)&newAddr, 1); + _addr = newAddr; + delay(20); + return _addr; +} + +void M5ModuleAudio::setFlashWriteBack() +{ + uint8_t data = 1; + uint8_t reg = FLASH_WRITE; + writeBytes(_addr, reg, (uint8_t *)&data, 1); + delay(20); +} + +audio_mic_t M5ModuleAudio::getMICStatus() +{ + uint8_t data; + uint8_t reg = MICROPHONE_STATUS; + readBytes(_addr, reg, (uint8_t *)&data, 1); + return (audio_mic_t)data; +} + +audio_hpmode_t M5ModuleAudio::getHPMode() +{ + uint8_t data; + uint8_t reg = HEADPHONE_MODE; + readBytes(_addr, reg, (uint8_t *)&data, 1); + return (audio_hpmode_t)data; +} + +uint8_t M5ModuleAudio::getHPInsertStatus() +{ + uint8_t data; + uint8_t reg = HEADPHONE_INSERT_STATUS; + readBytes(_addr, reg, (uint8_t *)&data, 1); + return data; +} + +uint8_t M5ModuleAudio::getRGBBrightness() +{ + uint8_t data; + uint8_t reg = RGB_LED_BRIGHTNESS; + readBytes(_addr, reg, (uint8_t *)&data, 1); + return data; +} + +uint32_t M5ModuleAudio::getRGBLED(uint8_t num) +{ + uint8_t rgb[3] = {0}; + uint8_t reg = RGB_LED + num * 3; + readBytes(_addr, reg, rgb, 3); + return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; +} + +uint8_t M5ModuleAudio::getFirmwareVersion() +{ + uint8_t version; + uint8_t reg = FIRMWARE_VERSION; + readBytes(_addr, reg, (uint8_t *)&version, 1); + return version; +} + +uint8_t M5ModuleAudio::getI2CAddress() +{ + uint8_t I2CAddress; + uint8_t reg = I2C_ADDRESS; + readBytes(_addr, reg, (uint8_t *)&I2CAddress, 1); + return I2CAddress; +} + +bool M5ModuleAudio::es8388_codec_init() +{ + if (es8388 != nullptr) { + delete es8388; + es8388 = nullptr; + } + + es8388 = new ES8388(_wire, _sda, _scl); + if (!es8388->init()) { + ESP_LOGI(TAG, "Failed to initialize ES8388 codec"); + return false; + } + + ESP_LOGI(TAG, "ES8388 codec initialized successfully"); + return true; +} + +bool M5ModuleAudio::i2s_driver_init(int sample_rate) +{ + _i2s_initialized = false; +#if USE_NEW_I2S_API + I2S.setPins(_i2s_bck, _i2s_ws, _i2s_do, _i2s_di, _i2s_mck); // SCK, WS, SDOUT, SDIN, MCLK + if (!I2S.begin(I2S_MODE_STD, sample_rate, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO)) { + ESP_LOGI(TAG, "Failed to initialize I2S (new API)"); + return false; + } + _i2s_initialized = true; + return true; + +#else + // Initialize I2S driver + i2s_cfg.mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX | I2S_MODE_RX); + i2s_cfg.sample_rate = (uint32_t)sample_rate; + i2s_cfg.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT; + i2s_cfg.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT; + i2s_cfg.communication_format = I2S_COMM_FORMAT_STAND_I2S; + i2s_cfg.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1; + i2s_cfg.dma_buf_count = 8; + i2s_cfg.dma_buf_len = 512; + i2s_cfg.use_apll = false; + i2s_cfg.tx_desc_auto_clear = true; + i2s_cfg.fixed_mclk = 0; + +#if defined(CONFIG_IDF_TARGET_ESP32) + // ESP-IDF 4.x on ESP32 still needs CLK_OUT1 routed explicitly for MCLK. + PIN_FUNC_SELECT(PERIPHS_IO_MUX_GPIO0_U, FUNC_GPIO0_CLK_OUT1); + WRITE_PERI_REG(PIN_CTRL, 0xFFF0); +#endif + + // I2S pin configuration + i2s_pin_cfg.mck_io_num = _i2s_mck; + i2s_pin_cfg.bck_io_num = _i2s_bck; + i2s_pin_cfg.ws_io_num = _i2s_ws; + i2s_pin_cfg.data_out_num = _i2s_do; + i2s_pin_cfg.data_in_num = _i2s_di; + + if (i2s_driver_install(i2s_num, &i2s_cfg, 0, NULL) != ESP_OK) { + ESP_LOGI(TAG, "Failed to install I2S driver"); + return false; + } + + if (i2s_set_pin(i2s_num, &i2s_pin_cfg) != ESP_OK) { + ESP_LOGI(TAG, "Failed to set I2S pins"); + return false; + } + + if (i2s_set_clk(i2s_num, i2s_cfg.sample_rate, i2s_cfg.bits_per_sample, I2S_CHANNEL_STEREO) != ESP_OK) { + ESP_LOGI(TAG, "Failed to set I2S clock"); + return false; + } + + i2s_zero_dma_buffer(i2s_num); + // idf 4.x + i2s_start(i2s_num); + _i2s_initialized = true; + + return true; +#endif +} + +bool M5ModuleAudio::setSpeakerVolume(int volume) +{ + if (volume < 0 || volume > 100) { + ESP_LOGI(TAG, "Volume out of range (0-100)"); + return false; + } + + if (es8388 == nullptr) { + ESP_LOGI(TAG, "ES8388 codec is not initialized"); + return false; + } + + if (!es8388->setDACVolume(volume)) { + ESP_LOGI(TAG, "Failed to set speaker volume"); + return false; + } + return true; +} + +bool M5ModuleAudio::setMicGain(es_mic_gain_t gain) +{ + if (es8388 == nullptr) { + ESP_LOGI(TAG, "ES8388 codec is not initialized"); + return false; + } + + if (!es8388->setMicGain(gain)) { + ESP_LOGI(TAG, "Failed to set microphone gain"); + return false; + } + return true; +} + +bool M5ModuleAudio::setMicInputLine(es_adc_input_t input) +{ + if (es8388 == nullptr) { + ESP_LOGI(TAG, "ES8388 codec is not initialized"); + return false; + } + + if (!es8388->setADCInput(input)) { + ESP_LOGI(TAG, "Failed to set ADC input"); + return false; + } + return true; +} + +bool M5ModuleAudio::setMicPGAGain(es_mic_gain_t gain) +{ + if (es8388 == nullptr) { + ESP_LOGI(TAG, "ES8388 codec is not initialized"); + return false; + } + + if (!es8388->setMicGain(gain)) { + ESP_LOGI(TAG, "Failed to configure ES8388 PGA gain"); + return false; + } + return true; +} + +bool M5ModuleAudio::setMicAdcVolume(uint8_t volume) +{ + if (volume > 100) { + ESP_LOGI(TAG, "ADC Volume out of range (0-100)"); + return false; + } + + if (es8388 == nullptr) { + ESP_LOGI(TAG, "ES8388 codec is not initialized"); + return false; + } + + if (!es8388->setADCVolume(volume)) { + ESP_LOGI(TAG, "Failed to set ADC volume"); + return false; + } + return true; +} + +bool M5ModuleAudio::setMute(bool mute) +{ + if (es8388 == nullptr) { + ESP_LOGI(TAG, "ES8388 codec is not initialized"); + return false; + } + + if (!es8388->setDACmute(mute)) { + ESP_LOGI(TAG, "Failed to set mute state"); + return false; + } + return true; +} + +bool M5ModuleAudio::setSpeakerOutput(es_dac_output_t output) +{ + if (es8388 == nullptr) { + ESP_LOGI(TAG, "ES8388 codec is not initialized"); + return false; + } + + if (!es8388->setDACOutput(output)) { + ESP_LOGI(TAG, "Failed to set DAC output"); + return false; + } + return true; +} + +bool M5ModuleAudio::setMixSourceSelect(es_mixsel_t lmixsel, es_mixsel_t rmixsel) +{ + if (es8388 == nullptr) { + ESP_LOGI(TAG, "ES8388 codec is not initialized"); + return false; + } + + if (!es8388->setMixSourceSelect(lmixsel, rmixsel)) { + ESP_LOGI(TAG, "Failed to set mix source"); + return false; + } + return true; +} + +bool M5ModuleAudio::setBitsSample(es_module_t mode, es_bits_length_t bits_len) +{ + if (es8388 == nullptr) { + ESP_LOGI(TAG, "ES8388 codec is not initialized"); + return false; + } + + if (!es8388->setBitsSample(mode, bits_len)) { + ESP_LOGI(TAG, "Failed to set sample bits"); + return false; + } + return true; +} + +bool M5ModuleAudio::setSampleRate(es_sample_rate_t rate) +{ + if (es8388 == nullptr) { + ESP_LOGI(TAG, "ES8388 codec is not initialized"); + return false; + } + + uint32_t sample_rate_hz = sample_rate_to_hz(rate); + if (sample_rate_hz == 0) { + ESP_LOGI(TAG, "Invalid sample rate"); + return false; + } + + if (_i2s_initialized) { +#if USE_NEW_I2S_API + if (!I2S.configureRX(sample_rate_hz, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO)) { + ESP_LOGI(TAG, "Failed to set I2S RX sample rate"); + return false; + } + if (!I2S.configureTX(sample_rate_hz, I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO)) { + ESP_LOGI(TAG, "Failed to set I2S TX sample rate"); + return false; + } +#else + if (i2s_set_clk(i2s_num, sample_rate_hz, i2s_cfg.bits_per_sample, I2S_CHANNEL_STEREO) != ESP_OK) { + ESP_LOGI(TAG, "Failed to set I2S sample rate"); + return false; + } + i2s_cfg.sample_rate = sample_rate_hz; +#endif + } + + if (!es8388->setSampleRate(rate)) { + ESP_LOGI(TAG, "Failed to set sample rate"); + return false; + } + return true; +} + +uint8_t M5ModuleAudio::getMicGain() +{ + if (es8388 == nullptr) { + ESP_LOGI(TAG, "ES8388 codec is not initialized"); + return 0; + } + + return es8388->getMicGain(); +} + +uint8_t *M5ModuleAudio::readAllReg() +{ + if (es8388 == nullptr) { + ESP_LOGI(TAG, "ES8388 codec is not initialized"); + return nullptr; + } + + return es8388->readAllReg(); +} + +int M5ModuleAudio::getBufferSize(int duration, int sample_rate) +{ + if (!sample_rate) { +#if USE_NEW_I2S_API + sample_rate = I2S.txSampleRate(); +#else + sample_rate = i2s_cfg.sample_rate; +#endif + } + return duration * sample_rate * 2 * 2; // sample_rate * bytes_per_sample * channels +} + +int M5ModuleAudio::getDuration(int size, int sample_rate) +{ + if (!sample_rate) { +#if USE_NEW_I2S_API + sample_rate = I2S.txSampleRate(); +#else + sample_rate = i2s_cfg.sample_rate; +#endif + } + return size / (sample_rate * 2 * 2); // sample_rate * bytes_per_sample * channels +} + +bool M5ModuleAudio::record(FS &fs, const char *filename, int size) +{ + // Open file for writing + File file = fs.open(filename, FILE_WRITE); + if (!file) { + ESP_LOGI(TAG, "Failed to open file for recording"); + return false; + } + + // Define chunk size (e.g., 1024 bytes) + const size_t CHUNK_SIZE = 1024; + uint8_t buffer[CHUNK_SIZE]; + size_t total_bytes_to_record = size; // sample_rate * bytes_per_sample * channels + size_t bytes_recorded = 0; + + while (bytes_recorded < total_bytes_to_record) { + size_t bytes_to_read = CHUNK_SIZE; + if (bytes_recorded + CHUNK_SIZE > total_bytes_to_record) { + bytes_to_read = total_bytes_to_record - bytes_recorded; + } + + size_t bytes_read = 0; +#if USE_NEW_I2S_API + bytes_read = I2S.readBytes((char *)buffer, bytes_to_read); + if (bytes_read != bytes_to_read) { + ESP_LOGI(TAG, "Recording failed during I2S read"); + file.close(); + return false; + } +#else + esp_err_t err = i2s_read(i2s_num, buffer, bytes_to_read, &bytes_read, portMAX_DELAY); + if (err != ESP_OK || bytes_read != bytes_to_read) { + ESP_LOGI(TAG, "Recording failed during I2S read"); + file.close(); + return false; + } +#endif + + file.write(buffer, bytes_read); + bytes_recorded += bytes_read; + } + + file.close(); + return true; +} + +bool M5ModuleAudio::record(uint8_t *buffer, int size) +{ + // Calculate the number of bytes to record + size_t bytes_to_record = size; // sample_rate * bytes_per_sample * channels + memset(buffer, 0, bytes_to_record); + + size_t bytes_read = 0; +#if USE_NEW_I2S_API + bytes_read = I2S.readBytes((char *)buffer, bytes_to_record); + if (bytes_read != bytes_to_record) { + ESP_LOGI(TAG, "Recording failed during I2S read"); + return false; + } +#else + esp_err_t err = i2s_read(i2s_num, buffer, bytes_to_record, &bytes_read, getDuration(size) * 1000 + 1000); + if (err != ESP_OK || bytes_read != bytes_to_record) { + ESP_LOGI(TAG, "Recording failed"); + return false; + } +#endif + return true; +} + +bool M5ModuleAudio::play(FS &fs, const char *filename) +{ + // Open file for reading + File file = fs.open(filename, FILE_READ); + if (!file) { + ESP_LOGI(TAG, "Failed to open file for playback"); + return false; + } + + // Define chunk size (e.g., 1024 bytes) + const size_t CHUNK_SIZE = 1024; + uint8_t buffer[CHUNK_SIZE]; + + while (file.available()) { + size_t bytes_to_read = CHUNK_SIZE; + if (file.available() < CHUNK_SIZE) { + bytes_to_read = file.available(); + } + + size_t bytes_read = file.read(buffer, bytes_to_read); + if (bytes_read > 0) { + size_t bytes_written = 0; +#if USE_NEW_I2S_API + bytes_written = I2S.write(buffer, bytes_read); + if (bytes_written < 0) { + ESP_LOGI(TAG, "Playback failed during I2S write"); + file.close(); + return false; + } +#else + esp_err_t err = i2s_write(i2s_num, buffer, bytes_read, &bytes_written, portMAX_DELAY); + if (err != ESP_OK) { + ESP_LOGI(TAG, "Playback failed during I2S write"); + file.close(); + return false; + } +#endif + } + } + + file.close(); + return true; +} + +bool M5ModuleAudio::play(const uint8_t *buffer, int size) +{ + size_t bytes_written = 0; +#if USE_NEW_I2S_API + bytes_written = I2S.write(buffer, size); + if (bytes_written < 0) { + ESP_LOGI(TAG, "Playback failed"); + return false; + } +#else + esp_err_t err = i2s_write(i2s_num, buffer, size, &bytes_written, portMAX_DELAY); + if (err != ESP_OK) { + ESP_LOGI(TAG, "Playback failed"); + return false; + } +#endif + return true; +} + +void M5ModuleAudio::writeBytes(uint8_t addr, uint8_t reg, uint8_t *buffer, uint8_t length) +{ + _wire->beginTransmission(addr); + _wire->write(reg); + for (int i = 0; i < length; i++) { + _wire->write(*(buffer + i)); + } + _wire->endTransmission(); +#if AUDIO_I2C_DEBUG + Serial.print("Write bytes: ["); + Serial.print(addr); + Serial.print(","); + Serial.print(reg); + Serial.print(","); + for (int i = 0; i < length; i++) { + Serial.print(buffer[i]); + if (i < length - 1) { + Serial.print(","); + } + } + Serial.println("]"); +#else +#endif +} + +void M5ModuleAudio::readBytes(uint8_t addr, uint8_t reg, uint8_t *buffer, uint8_t length) +{ + uint8_t index = 0; + _wire->beginTransmission(addr); + _wire->write(reg); + _wire->endTransmission(false); + _wire->requestFrom(addr, length); + for (int i = 0; i < length; i++) { + buffer[index++] = _wire->read(); + } +#if AUDIO_I2C_DEBUG + Serial.print("Read bytes: ["); + Serial.print(addr); + Serial.print(","); + Serial.print(reg); + Serial.print(","); + for (int i = 0; i < length; i++) { + Serial.print(buffer[i]); + if (i < length - 1) { + Serial.print(","); + } + } + Serial.println("]"); +#else +#endif +} diff --git a/src/M5Module_Audio.h b/src/M5Module_Audio.h new file mode 100644 index 0000000..df8d4aa --- /dev/null +++ b/src/M5Module_Audio.h @@ -0,0 +1,410 @@ +/* + * SPDX-FileCopyrightText: 2024 M5Stack Technology CO LTD + * + * SPDX-License-Identifier: MIT + */ + +#ifndef M5MODULE_AUDIO_H +#define M5MODULE_AUDIO_H + +#include +#include +#include +#include "es8388.hpp" +#include "M5Unified.h" + +#ifdef ESP_IDF_VERSION +#if (ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)) +#define USE_NEW_I2S_API 1 +#include +#else +#define USE_NEW_I2S_API 0 +#include "driver/i2s.h" +#endif +#else +#define USE_NEW_I2S_API 0 +#endif + +// #define AUDIO_I2C_DEBUG Serial +#if defined AUDIO_I2C_DEBUG +#define serialPrint(...) AUDIO_I2C_DEBUG.print(__VA_ARGS__) +#define serialPrintln(...) AUDIO_I2C_DEBUG.println(__VA_ARGS__) +#define serialPrintf(...) AUDIO_I2C_DEBUG.printf(__VA_ARGS__) +#define serialFlush() AUDIO_I2C_DEBUG.flush() +#else +#endif + +#define I2C_ADDR (0x33) +#define I2C_ADDR_MIN (0x08) +#define I2C_ADDR_MAX (0x77) +#define MICROPHONE_STATUS (0x00) +#define HEADPHONE_MODE (0x10) +#define HEADPHONE_INSERT_STATUS (0x20) +#define RGB_LED_BRIGHTNESS (0x30) +#define RGB_LED (0x40) +#define FLASH_WRITE (0xF0) +#define FIRMWARE_VERSION (0xFE) +#define I2C_ADDRESS (0xFF) + +/** + * @enum audio_mic_t + * @brief Microphone/LINE input configuration options + * @details Corresponds to register 0x00 (R/W) + * + * @var AUDIO_MIC_CLOSE + * Disable MIC/LINE input (value: 0) + * @var AUDIO_MIC_OPEN + * Enable MIC/LINE input (value: 1, default) + */ +typedef enum { AUDIO_MIC_CLOSE = 0, AUDIO_MIC_OPEN } audio_mic_t; + +/** + * @enum audio_hpmode_t + * @brief Headphone output mode configuration options + * @details Corresponds to register 0x10 (R/W) + * + * @var AUDIO_HPMODE_NATIONAL + * National Standard audio mode (value: 0, default) + * @var AUDIO_HPMODE_AMERICAN + * American Standard audio mode (value: 1) + */ +typedef enum { AUDIO_HPMODE_NATIONAL = 0, AUDIO_HPMODE_AMERICAN } audio_hpmode_t; + +/** + * @brief Class to manage M5ModuleAudio operations including audio input/output and codec control. + */ +class M5ModuleAudio { +public: +#if USE_NEW_I2S_API + M5ModuleAudio(); +#else + M5ModuleAudio(i2s_port_t i2s_num = I2S_NUM_0); +#endif + ~M5ModuleAudio(); + + /** + * @brief Initializes the M5ModuleAudio with specific audio settings. + * + * @param sample_rate Sampling rate in Hz (defaults to 32000 Hz). + * @param i2c_sda I2C data line pin number. + * @param i2c_scl I2C clock line pin number. + * @param i2s_di I2S data input pin. + * @param i2s_ws I2S word select pin. + * @param i2s_do I2S data output pin. + * @param i2s_bck I2S bit clock pin. + * @param wire Reference to a TwoWire object (defaults to Wire). + * @return bool True if initialization was successful, false otherwise. + */ + bool begin(TwoWire& wire, uint8_t sda, uint8_t scl, uint8_t addr, uint32_t speed, int sample_rate, int i2s_mck, + int i2s_di, int i2s_ws, int i2s_do, int i2s_bck); + + bool begin(TwoWire& wire, uint8_t sda, uint8_t scl, uint8_t addr = I2C_ADDR, uint32_t speed = 400000); + + bool begin(TwoWire& wire, uint8_t addr = I2C_ADDR); + + /** + * @brief Set microphone/LINE input status + * @param status Audio input state (AUDIO_MIC_CLOSE/AUDIO_MIC_OPEN) + * @note Controls register 0x00 (R/W) + * @see audio_mic_t + */ + void setMICStatus(audio_mic_t status); + + /** + * @brief Set headphone output standard mode + * @param mode Audio output standard (AUDIO_HPMODE_NATIONAL/AUDIO_HPMODE_AMERICAN) + * @note Controls register 0x10 (R/W) + * @see audio_hpmode_t + */ + void setHPMode(audio_hpmode_t mode); + + /** + * @brief Set global brightness for all RGB LEDs + * @param brightness Brightness value (0-100) + * @note Writes to register 0x30 (R/W), default:10 + */ + void setRGBBrightness(uint8_t brightness); + + /** + * @brief Set color for specific RGB LED + * @param num LED number (1-3) + * @param color 24-bit color value (0xRRGGBB) + * @note Writes to register 0x40 (R/W). Color components are automatically + * decomposed into 3 bytes (R, G, B) + */ + void setRGBLED(uint8_t num, uint32_t color); + + /** + * @brief Trigger configuration save to internal Flash + * @note Writes 1 to register 0xF0 (Write-Only). Requires 20ms for Flash erase. + * @warning Avoid frequent writes to extend Flash lifespan + */ + void setFlashWriteBack(); + + /** + * @brief Set device I2C address + * @param newAddr New I2C address (0x08-0x77) + * @return Actual set address (may differ if invalid input) + * @note Writes to register 0xFF (R/W). Requires reboot to take effect. + */ + uint8_t setI2CAddress(uint8_t newAddr); + + /** + * @brief Get current microphone/LINE input status + * @return Current audio input state + * @note Reads from register 0x00 (R/W) + */ + audio_mic_t getMICStatus(); + + /** + * @brief Get current headphone output standard mode + * @return Current audio output standard + * @note Reads from register 0x10 (R/W) + */ + audio_hpmode_t getHPMode(); + + /** + * @brief Get headphone insertion status + * @return 0=Not inserted, 1=Inserted + * @note Reads from register 0x20 (Read-Only) + */ + uint8_t getHPInsertStatus(); + + /** + * @brief Get current RGB brightness value + * @return Brightness level (0-100) + * @note Reads from register 0x30 (R/W) + */ + uint8_t getRGBBrightness(); + + /** + * @brief Get current color for specific RGB LED + * @param num LED number (1-3) + * @return 24-bit color value (0xRRGGBB) + * @note Reads from register 0x40 (R/W). Combines 3 bytes (R, G, B) into color + */ + uint32_t getRGBLED(uint8_t num); + + /** + * @brief Get firmware version + * @return 4-byte version code (e.g., 0x01020304 = v1.2.3.4) + * @note Reads from register 0xFE (Read-Only) + */ + uint8_t getFirmwareVersion(); + + /** + * @brief Get current I2C device address + * @return Current I2C address + * @note Reads from register 0xFF (R/W). Default:0x33 + */ + uint8_t getI2CAddress(); + + /** + * @brief Sets the speaker volume. + * + * @param volume Volume level (0-100). + * @return bool True if volume was set successfully, false if the operation failed. + */ + bool setSpeakerVolume(int volume); + + /** + * @brief Sets the microphone gain. + * + * @param gain Microphone gain level. + * @return bool True if gain was set successfully, false if the operation failed. + */ + bool setMicGain(es_mic_gain_t gain); + + /** + * @brief Selects the ES8388 ADC input source. + * + * @param input ADC input routing. + * @return bool True if the source was set successfully, false otherwise. + */ + bool setMicInputLine(es_adc_input_t input); + + /** + * @brief Sets the microphone PGA gain. + * + * @param gain PGA gain value. + * @return bool True if PGA gain was set successfully, false if the operation failed. + */ + bool setMicPGAGain(es_mic_gain_t gain); + + /** + * @brief Sets the microphone ADC volume. + * + * @param volume Volume level (0-100). + * @return bool True if volume was set successfully, false if the operation failed. + */ + bool setMicAdcVolume(uint8_t volume); + + /** + * @brief Mutes or unmutes the speaker. + * + * @param mute Set true to mute, false to unmute. + * @return bool True if the mute state was set successfully, false if the operation failed. + */ + bool setMute(bool mute); + + /** + * @brief Selects the ES8388 DAC output path. + * + * @param output Output routing mask. + * @return bool True if the output path was set successfully, false otherwise. + */ + bool setSpeakerOutput(es_dac_output_t output); + + /** + * @brief Configures the ES8388 mixer input sources. + * + * @param lmixsel Left mixer source. + * @param rmixsel Right mixer source. + * @return bool True if the mixer was configured successfully, false otherwise. + */ + bool setMixSourceSelect(es_mixsel_t lmixsel, es_mixsel_t rmixsel); + + /** + * @brief Configures ES8388 sample width. + * + * @param mode Codec path to configure. + * @param bits_len Sample bit width. + * @return bool True if the setting was applied successfully, false otherwise. + */ + bool setBitsSample(es_module_t mode, es_bits_length_t bits_len); + + /** + * @brief Configures ES8388 sample rate. + * + * @param rate Target sample rate. + * @return bool True if the setting was applied successfully, false otherwise. + */ + bool setSampleRate(es_sample_rate_t rate); + + /** + * @brief Reads back the configured ES8388 microphone gain. + * + * @return uint8_t Current microphone gain register value. + */ + uint8_t getMicGain(); + + /** + * @brief Reads all ES8388 registers. + * + * @return uint8_t* Pointer to a 53-byte register snapshot, or nullptr if unavailable. + */ + uint8_t* readAllReg(); + + /** + * @brief Calculates the buffer size needed for a recording of the specified duration. + * + * @param duration Recording duration in seconds. + * @param sample_rate Sampling rate in Hz (defaults to 0). + * @return int Buffer size in bytes required for the recording. + */ + int getBufferSize(int duration, int sample_rate = 0); + + /** + * @brief Determines the duration of a recording for a specified buffer size. + * + * @param size Buffer size in bytes. + * @param sample_rate Sampling rate in Hz (defaults to 0). + * @return int Duration in seconds of the recording. + */ + int getDuration(int size, int sample_rate = 0); + + /** + * @brief Records audio to a file. + * + * @param fs Reference to the filesystem to use. + * @param filename Name of the file to save the recording to. + * @param size Buffer size in bytes (should be pre-calculated using getBufferSize()). + * @return bool True if the recording was successful, false if it failed. + */ + bool record(FS& fs, const char* filename, int size); + + /** + * @brief Records audio to a buffer. + * + * @param buffer Pointer to the buffer where audio data will be stored. + * @param size Buffer size in bytes (should be pre-calculated using getBufferSize()). + * @return bool True if the recording was successful, false if it failed. + */ + bool record(uint8_t* buffer, int size); + + /** + * @brief Plays audio from a file. + * + * @param fs Reference to the filesystem from which to play the audio. + * @param filename Name of the file containing the audio to play. + * @return bool True if playback was successful, false if it failed. + */ + bool play(FS& fs, const char* filename); + + /** + * @brief Plays audio from a buffer. + * + * @param buffer Pointer to the audio buffer. + * @param size Size of the audio buffer in bytes. + * @return bool True if playback was successful, false if it failed. + */ + bool play(const uint8_t* buffer, int size); + +private: + TwoWire* _wire; + uint8_t _sda, _scl; + uint8_t _addr; + + ES8388* es8388; + + // I2S configuration +#if USE_NEW_I2S_API + I2SClass I2S; +#else + i2s_port_t i2s_num; + i2s_config_t i2s_cfg; + i2s_pin_config_t i2s_pin_cfg; +#endif + bool _i2s_initialized = false; + + // I2S pin numbers + int _i2s_mck = -1; + int _i2s_bck = -1; + int _i2s_ws = -1; + int _i2s_do = -1; + int _i2s_di = -1; + + /** + * @brief Writes multiple bytes to a specified register. + * + * This function writes a sequence of bytes from the provided buffer + * to the device located at the specified I2C address and register. + * + * @param addr The I2C address of the device. + * @param reg The register address where the data will be written. + * @param buffer A pointer to the data buffer that contains the bytes to be written. + * @param length The number of bytes to write from the buffer. + */ + void writeBytes(uint8_t addr, uint8_t reg, uint8_t* buffer, uint8_t length); + + /** + * @brief Reads multiple bytes from a specified register. + * + * This function reads a sequence of bytes from the device located at + * the specified I2C address and register into the provided buffer. + * + * @param addr The I2C address of the device. + * @param reg The register address from which the data will be read. + * @param buffer A pointer to the data buffer where the read bytes will be stored. + * @param length The number of bytes to read into the buffer. + */ + void readBytes(uint8_t addr, uint8_t reg, uint8_t* buffer, uint8_t length); + + // Initializes the ES8388 codec + bool es8388_codec_init(); + + // Initializes the I2S driver + bool i2s_driver_init(int sample_rate); +}; + +#endif // M5ModuleAudio_H diff --git a/src/audio_i2c.cpp b/src/audio_i2c.cpp deleted file mode 100644 index ac8185f..0000000 --- a/src/audio_i2c.cpp +++ /dev/null @@ -1,178 +0,0 @@ -/* - *SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD - * - *SPDX-License-Identifier: MIT - */ - -#include "audio_i2c.hpp" - -void AudioI2c::writeBytes(uint8_t addr, uint8_t reg, uint8_t *buffer, uint8_t length) -{ - _wire->beginTransmission(addr); - _wire->write(reg); - for (int i = 0; i < length; i++) { - _wire->write(*(buffer + i)); - } - _wire->endTransmission(); -#if AUDIO_I2C_DEBUG - Serial.print("Write bytes: ["); - Serial.print(addr); - Serial.print(", "); - Serial.print(reg); - Serial.print(", "); - for (int i = 0; i < length; i++) { - Serial.print(buffer[i]); - if (i < length - 1) { - Serial.print(", "); - } - } - Serial.println("]"); -#else -#endif -} - -void AudioI2c::readBytes(uint8_t addr, uint8_t reg, uint8_t *buffer, uint8_t length) -{ - uint8_t index = 0; - _wire->beginTransmission(addr); - _wire->write(reg); - _wire->endTransmission(false); - _wire->requestFrom(addr, length); - for (int i = 0; i < length; i++) { - buffer[index++] = _wire->read(); - } -#if AUDIO_I2C_DEBUG - Serial.print("Read bytes: ["); - Serial.print(addr); - Serial.print(", "); - Serial.print(reg); - Serial.print(", "); - for (int i = 0; i < length; i++) { - Serial.print(buffer[i]); - if (i < length - 1) { - Serial.print(", "); - } - } - Serial.println("]"); -#else -#endif -} - -bool AudioI2c::begin(TwoWire *wire, uint8_t sda, uint8_t scl, uint32_t speed, uint8_t addr) -{ - _wire = wire; - _sda = sda; - _scl = scl; - _addr = addr; - _speed = speed; - _wire->begin(_sda, _scl, _speed); - delay(10); - _wire->beginTransmission(_addr); - uint8_t error = _wire->endTransmission(); - if (error == 0) { - return true; - } else { - return false; - } -} - -void AudioI2c::setMICStatus(audio_mic_t status) -{ - uint8_t reg = MICROPHONE_STATUS; - writeBytes(_addr, reg, (uint8_t *)&status, 1); -} - -void AudioI2c::setHPMode(audio_hpmode_t mode) -{ - uint8_t reg = HEADPHONE_MODE; - writeBytes(_addr, reg, (uint8_t *)&mode, 1); -} - -void AudioI2c::setRGBBrightness(uint8_t brightness) -{ - if (brightness > 100) brightness = 100; - uint8_t reg = RGB_LED_BRIGHTNESS; - writeBytes(_addr, reg, (uint8_t *)&brightness, 1); -} - -void AudioI2c::setRGBLED(uint8_t num, uint32_t color) -{ - if (num > 2) num = 2; - color = ((color & 0xFF) << 16) | (color & 0xFF00) | ((color >> 16) & 0xFF); - uint8_t reg = RGB_LED + num * 3; - writeBytes(_addr, reg, (uint8_t *)&color, 3); -} - -uint8_t AudioI2c::setI2CAddress(uint8_t newAddr) -{ - newAddr = constrain(newAddr, I2C_ADDR_MIN, I2C_ADDR_MAX); - uint8_t reg = I2C_ADDRESS; - writeBytes(_addr, reg, (uint8_t *)&newAddr, 1); - _addr = newAddr; - delay(20); - return _addr; -} - -void AudioI2c::setFlashWriteBack() -{ - uint8_t data = 1; - uint8_t reg = FLASH_WRITE; - writeBytes(_addr, reg, (uint8_t *)&data, 1); - delay(20); -} - -audio_mic_t AudioI2c::getMICStatus() -{ - uint8_t data; - uint8_t reg = MICROPHONE_STATUS; - readBytes(_addr, reg, (uint8_t *)&data, 1); - return (audio_mic_t)data; -} - -audio_hpmode_t AudioI2c::getHPMode() -{ - uint8_t data; - uint8_t reg = HEADPHONE_MODE; - readBytes(_addr, reg, (uint8_t *)&data, 1); - return (audio_hpmode_t)data; -} - -uint8_t AudioI2c::getHPInsertStatus() -{ - uint8_t data; - uint8_t reg = HEADPHONE_INSERT_STATUS; - readBytes(_addr, reg, (uint8_t *)&data, 1); - return data; -} - -uint8_t AudioI2c::getRGBBrightness() -{ - uint8_t data; - uint8_t reg = RGB_LED_BRIGHTNESS; - readBytes(_addr, reg, (uint8_t *)&data, 1); - return data; -} - -uint32_t AudioI2c::getRGBLED(uint8_t num) -{ - uint8_t rgb[3] = {0}; - uint8_t reg = RGB_LED + num * 3; - readBytes(_addr, reg, rgb, 3); - return (rgb[0] << 16) | (rgb[1] << 8) | rgb[2]; -} - -uint8_t AudioI2c::getFirmwareVersion() -{ - uint8_t version; - uint8_t reg = FIRMWARE_VERSION; - readBytes(_addr, reg, (uint8_t *)&version, 1); - return version; -} - -uint8_t AudioI2c::getI2CAddress() -{ - uint8_t I2CAddress; - uint8_t reg = I2C_ADDRESS; - readBytes(_addr, reg, (uint8_t *)&I2CAddress, 1); - return I2CAddress; -} \ No newline at end of file diff --git a/src/audio_i2c.hpp b/src/audio_i2c.hpp deleted file mode 100644 index 1b1fb88..0000000 --- a/src/audio_i2c.hpp +++ /dev/null @@ -1,251 +0,0 @@ -/* - *SPDX-FileCopyrightText: 2025 M5Stack Technology CO LTD - * - *SPDX-License-Identifier: MIT - */ - -#ifndef __AUDIO_I2C_HPP -#define __AUDIO_I2C_HPP - -#include "Arduino.h" -#include "Wire.h" - -// #define AUDIO_I2C_DEBUG Serial // This macro definition can be annotated without sending and receiving data prints -// Define the serial port you want to use, e.g., Serial1 or Serial2 -#if defined AUDIO_I2C_DEBUG -#define serialPrint(...) AUDIO_I2C_DEBUG.print(__VA_ARGS__) -#define serialPrintln(...) AUDIO_I2C_DEBUG.println(__VA_ARGS__) -#define serialPrintf(...) AUDIO_I2C_DEBUG.printf(__VA_ARGS__) -#define serialFlush() AUDIO_I2C_DEBUG.flush() -#else -#endif - -/** - * @brief I2C Address Indicates the default address. - */ -#define I2C_ADDR (0x33) - -/** - * @brief Minimum valid I2C address. - */ -#define I2C_ADDR_MIN (0x08) - -/** - * @brief Maximum valid I2C address. - */ -#define I2C_ADDR_MAX (0x77) - -/** - * @brief Microphone/LINE Input Configuration Register (R/W) - */ -#define MICROPHONE_STATUS (0x00) - -/** - * @brief Headphone Mode Configuration Register (R/W) - */ -#define HEADPHONE_MODE (0x10) - -/** - * @brief Headphone Insertion Detection Register (Read-Only) - */ -#define HEADPHONE_INSERT_STATUS (0x20) - -/**S - * @brief RGB LED Brightness Control Register (R/W) - */ -#define RGB_LED_BRIGHTNESS (0x30) - -/** - * @brief RGB LED Color Control Register (R/W) - */ -#define RGB_LED (0x40) - -/** - * @brief Configuration Save Register (Write-Only) - */ -#define FLASH_WRITE (0xF0) - -/** - * @brief Firmware Version Register (Read-Only) - */ -#define FIRMWARE_VERSION (0xFE) - -/** - * @brief I2C Device Address Configuration Register (R/W) - */ -#define I2C_ADDRESS (0xFF) - -/** - * @enum audio_mic_t - * @brief Microphone/LINE input configuration options - * @details Corresponds to register 0x00 (R/W) - * - * @var AUDIO_MIC_CLOSE - * Disable MIC/LINE input (value: 0) - * @var AUDIO_MIC_OPEN - * Enable MIC/LINE input (value: 1, default) - */ -typedef enum { AUDIO_MIC_CLOSE = 0, AUDIO_MIC_OPEN } audio_mic_t; - -/** - * @enum audio_hpmode_t - * @brief Headphone output mode configuration options - * @details Corresponds to register 0x10 (R/W) - * - * @var AUDIO_HPMODE_NATIONAL - * National Standard audio mode (value: 0, default) - * @var AUDIO_HPMODE_AMERICAN - * American Standard audio mode (value: 1) - */ -typedef enum { AUDIO_HPMODE_NATIONAL = 0, AUDIO_HPMODE_AMERICAN } audio_hpmode_t; - -class AudioI2c { -public: - /** - * @brief Initializes the I2C communication with specified parameters. - * - * This function sets up the I2C bus using the provided SDA and SCL pins, - * as well as the desired communication speed. It can also specify a default - * I2C address for the device. - * - * @param wire Pointer to a TwoWire object that represents the I2C bus. - * @param sda The pin number for the SDA line (default is -1). - * @param scl The pin number for the SCL line (default is -1). - * @param speed The I2C clock speed in Hertz (default is 400000L). - * @param addr The I2C address of the device (default is I2C_ADDR). - * - * @return True if the initialization was successful, false otherwise. - */ - bool begin(TwoWire* wire, uint8_t sda = -1, uint8_t scl = -1, uint32_t speed = 400000L, uint8_t addr = I2C_ADDR); - - /** - * @brief Set microphone/LINE input status - * @param status Audio input state (AUDIO_MIC_CLOSE/AUDIO_MIC_OPEN) - * @note Controls register 0x00 (R/W) - * @see audio_mic_t - */ - void setMICStatus(audio_mic_t status); - - /** - * @brief Set headphone output standard mode - * @param mode Audio output standard (AUDIO_HPMODE_NATIONAL/AUDIO_HPMODE_AMERICAN) - * @note Controls register 0x10 (R/W) - * @see audio_hpmode_t - */ - void setHPMode(audio_hpmode_t mode); - - /** - * @brief Set global brightness for all RGB LEDs - * @param brightness Brightness value (0-100) - * @note Writes to register 0x30 (R/W), default:10 - */ - void setRGBBrightness(uint8_t brightness); - - /** - * @brief Set color for specific RGB LED - * @param num LED number (1-3) - * @param color 24-bit color value (0xRRGGBB) - * @note Writes to register 0x40 (R/W). Color components are automatically - * decomposed into 3 bytes (R, G, B) - */ - void setRGBLED(uint8_t num, uint32_t color); - - /** - * @brief Trigger configuration save to internal Flash - * @note Writes 1 to register 0xF0 (Write-Only). Requires 20ms for Flash erase. - * @warning Avoid frequent writes to extend Flash lifespan - */ - void setFlashWriteBack(); - - /** - * @brief Set device I2C address - * @param newAddr New I2C address (0x08-0x77) - * @return Actual set address (may differ if invalid input) - * @note Writes to register 0xFF (R/W). Requires reboot to take effect. - */ - uint8_t setI2CAddress(uint8_t newAddr); - - /** - * @brief Get current microphone/LINE input status - * @return Current audio input state - * @note Reads from register 0x00 (R/W) - */ - audio_mic_t getMICStatus(); - - /** - * @brief Get current headphone output standard mode - * @return Current audio output standard - * @note Reads from register 0x10 (R/W) - */ - audio_hpmode_t getHPMode(); - - /** - * @brief Get headphone insertion status - * @return 0=Not inserted, 1=Inserted - * @note Reads from register 0x20 (Read-Only) - */ - uint8_t getHPInsertStatus(); - - /** - * @brief Get current RGB brightness value - * @return Brightness level (0-100) - * @note Reads from register 0x30 (R/W) - */ - uint8_t getRGBBrightness(); - - /** - * @brief Get current color for specific RGB LED - * @param num LED number (1-3) - * @return 24-bit color value (0xRRGGBB) - * @note Reads from register 0x40 (R/W). Combines 3 bytes (R, G, B) into color - */ - uint32_t getRGBLED(uint8_t num); - - /** - * @brief Get firmware version - * @return 4-byte version code (e.g., 0x01020304 = v1.2.3.4) - * @note Reads from register 0xFE (Read-Only) - */ - uint8_t getFirmwareVersion(); - - /** - * @brief Get current I2C device address - * @return Current I2C address - * @note Reads from register 0xFF (R/W). Default:0x33 - */ - uint8_t getI2CAddress(); - -private: - TwoWire* _wire = &Wire; - uint8_t _addr; - uint8_t _scl; - uint8_t _sda; - uint32_t _speed; - - /** - * @brief Writes multiple bytes to a specified register. - * - * This function writes a sequence of bytes from the provided buffer - * to the device located at the specified I2C address and register. - * - * @param addr The I2C address of the device. - * @param reg The register address where the data will be written. - * @param buffer A pointer to the data buffer that contains the bytes to be written. - * @param length The number of bytes to write from the buffer. - */ - void writeBytes(uint8_t addr, uint8_t reg, uint8_t* buffer, uint8_t length); - - /** - * @brief Reads multiple bytes from a specified register. - * - * This function reads a sequence of bytes from the device located at - * the specified I2C address and register into the provided buffer. - * - * @param addr The I2C address of the device. - * @param reg The register address from which the data will be read. - * @param buffer A pointer to the data buffer where the read bytes will be stored. - * @param length The number of bytes to read into the buffer. - */ - void readBytes(uint8_t addr, uint8_t reg, uint8_t* buffer, uint8_t length); -}; -#endif \ No newline at end of file diff --git a/src/es8388.cpp b/src/es8388.cpp index aa7c9f6..0974800 100644 --- a/src/es8388.cpp +++ b/src/es8388.cpp @@ -24,11 +24,11 @@ bool ES8388::writeBytes(uint8_t reg, uint8_t data) #if ES8388_DEBUG Serial.print("Write bytes: ["); Serial.print(ES8388_ADDR); - Serial.print(", "); + Serial.print(","); Serial.print(reg); - Serial.print(", "); + Serial.print(","); Serial.print(data); - Serial.print(", "); + Serial.print(","); #else #endif } @@ -48,11 +48,11 @@ bool ES8388::readBytes(uint8_t reg, uint8_t& data) #if AUDIO_I2C_DEBUG Serial.print("Read bytes: ["); Serial.print(ES8388_ADDR); - Serial.print(", "); + Serial.print(","); Serial.print(reg); - Serial.print(", "); + Serial.print(","); Serial.print(data); - Serial.print(", "); + Serial.print(","); #else #endif } diff --git a/src/es8388.hpp b/src/es8388.hpp index b8e4945..84be599 100644 --- a/src/es8388.hpp +++ b/src/es8388.hpp @@ -303,22 +303,22 @@ * @note Mixer configuration affects audio path routing */ typedef enum { - MIXLIN1, /**< Direct line input 1 (unprocessed signal path) */ - MIXLIN2, /**< Direct line input 2 (alternative signal path) */ - MIXRES, /**< Reserved mixer source (do not use in normal operation) */ - MIXADC /**< Processed signal from ADC/ALC (with automatic level control) */ + MIXLIN1, /**