Передача звука с INMP441 на декодер PCM5102A с помощью ESP32

Практического применения этого проекта я не вижу (не мегафон же). Но, для меня это был довольно хороший опыт работы со звуком на ESP32-WROOM. Скетч разрабатывался в среде Arduino IDE 2.3.5. Заменил на Arduino IDE 2.3.6, скетч компилируется, загружается и работает.

Насчет железа. Дополнил предыдущий проект “ESP32 — проигрывание mp3 файлов с SD через декодер PCM5102A” (там и о распайке перемычек на PCM5102A)

https://conntest.ru/program/esp32-proigryvanie-mp3-fajlov-s-sd-cherez-dekoder-pcm5102a

модулем микрофона INMP441 и еще одной кнопкой, которая управляет изменением коэффициента усиления выходного сигнала от 0.5 до 3.0 через 0.5, по кругу. Еще добавил светодиод, который степенью яркости отражает величину этого коэффициента. Показания также выводятся в Serial Port после изменения (нажатия кнопки). Звук довольно чистый.

Подключение кнопки управления коэффициентом усиления: один контакт на GND, другой на P4. Светодиод индикации коэффициента подключен плюсом к 3,3 V через резистор 180 Ом, минусом к P2.

Таблица подключения модулей к ESP32-WROOM (обозначения контактов ESP32 относятся к переходной плате).

Микрофон INMP441 с интерфейсом I2S – моно (контакт L / R подключен к GND, наверное, можно подключить этот контакт к ESP32 и обрабатывать стерео), а декодер интерфейса I2S PCM5102A – стерео. В принципе, преобразование возможно, но я остановился на моно, работает только одна колонка.

Таблица подключения на https://conntest.ru/program/peredacha-zvuka-s-inmp441-na-dekoder-pcm5102a-s-pomoshhyu-esp32

Скетч:

#include <driver/i2s.h>
#include <driver/ledc.h>

// === Пины подключения INMP441 ===
#define I2S_WS   14
#define I2S_SD   33
#define I2S_SCK  32

// === Пины подключения PCM5102A ===
#define I2S_LRCK 25
#define I2S_DIN  22
#define I2S_BCK  26

#define I2S_MIC_PORT (I2S_NUM_0)
#define I2S_DAC_PORT (I2S_NUM_1)

#define bufferLen 128
int32_t sBuffer[bufferLen];

// === Кнопка ===
#define BUTTON_PIN 4
unsigned long lastDebounceTime = 0;
const unsigned long debounceDelay = 50;
bool lastButtonState = HIGH;
bool buttonPressed = false;

// === Усиление ===
float gainLevels[] = {0.5, 1.0, 1.5, 2.0, 2.5, 3.0};
int gainIndex = 0;

// === Светодиод (PWM) ===
#define LED_PIN 2
#define LEDC_CHANNEL LEDC_CHANNEL_0
#define LEDC_TIMER   LEDC_TIMER_0

void setup_pwm() {
  ledc_timer_config_t ledc_timer = {
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .duty_resolution = LEDC_TIMER_8_BIT,
    .timer_num = LEDC_TIMER,
    .freq_hz = 5000,
    .clk_cfg = LEDC_AUTO_CLK
  };
  ledc_timer_config(&ledc_timer);

  ledc_channel_config_t ledc_channel = {
    .gpio_num = LED_PIN,
    .speed_mode = LEDC_LOW_SPEED_MODE,
    .channel = LEDC_CHANNEL,
    .intr_type = LEDC_INTR_DISABLE,
    .timer_sel = LEDC_TIMER,
    .duty = 0,
    .hpoint = 0
  };
  ledc_channel_config(&ledc_channel);
}

void set_pwm_brightness(int brightness) {
  ledc_set_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL, brightness);
  ledc_update_duty(LEDC_LOW_SPEED_MODE, LEDC_CHANNEL);
}

// === Усиление сигнала ===
void applyGain(int32_t* buffer, int samples, float gain) {
  for (int i = 0; i < samples; ++i) {
    float amplified = buffer[i] * gain;
    if (amplified > INT32_MAX) amplified = INT32_MAX;
    if (amplified < INT32_MIN) amplified = INT32_MIN;
    buffer[i] = (int32_t)amplified;
  }
}

// === I2S: вход ===
void i2s_install_in() {
  const i2s_config_t i2s_config_in = {
    .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_RX),
    .sample_rate = 48000,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
    .communication_format = I2S_COMM_FORMAT_STAND_I2S,
    .intr_alloc_flags = 0,
    .dma_buf_count = 16,
    .dma_buf_len = bufferLen,
    .use_apll = false
  };
  i2s_driver_install(I2S_MIC_PORT, &i2s_config_in, 0, NULL);
}

void i2s_setpin_in() {
  const i2s_pin_config_t pin_config_in = {
    .mck_io_num = -1,
    .bck_io_num = I2S_SCK,
    .ws_io_num = I2S_WS,
    .data_out_num = -1,
    .data_in_num = I2S_SD,
  };
  i2s_set_pin(I2S_MIC_PORT, &pin_config_in);
}

// === I2S: выход ===
void i2s_install_out() {
  const i2s_config_t i2s_config_out = {
    .mode = i2s_mode_t(I2S_MODE_MASTER | I2S_MODE_TX),
    .sample_rate = 48000,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
    .communication_format = I2S_COMM_FORMAT_STAND_I2S,
    .intr_alloc_flags = 0,
    .dma_buf_count = 16,
    .dma_buf_len = bufferLen,
    .use_apll = false
  };
  i2s_driver_install(I2S_DAC_PORT, &i2s_config_out, 0, NULL);
}

void i2s_setpin_out() {
  const i2s_pin_config_t pin_config_out = {
    .mck_io_num = -1,
    .bck_io_num = I2S_BCK,
    .ws_io_num = I2S_LRCK,
    .data_out_num = I2S_DIN,
    .data_in_num = -1,
  };
  i2s_set_pin(I2S_DAC_PORT, &pin_config_out);
}

// === Проверка кнопки ===
void checkButton() {
  static bool lastStableState = HIGH;
  static bool lastReadState = HIGH;
  static unsigned long lastChangeTime = 0;

  bool currentRead = digitalRead(BUTTON_PIN);

  if (currentRead != lastReadState) {
    lastChangeTime = millis();
  }

  if ((millis() - lastChangeTime) > debounceDelay) {
    if (currentRead != lastStableState) {
      lastStableState = currentRead;
      if (lastStableState == LOW) {
        buttonPressed = true;
      }
    }
  }

  lastReadState = currentRead;
}

void setup() {
  Serial.begin(115200);
  Serial.println("Старт...");

  pinMode(BUTTON_PIN, INPUT_PULLUP);

  i2s_install_in();
  i2s_setpin_in();
  i2s_start(I2S_MIC_PORT);

  i2s_install_out();
  i2s_setpin_out();
  i2s_start(I2S_DAC_PORT);

  setup_pwm();
  set_pwm_brightness(map(gainIndex, 0, 5, 255, 0)); 

  delay(500);
}

void loop() {
  checkButton();

  if (buttonPressed) {
    gainIndex = (gainIndex + 1) % (sizeof(gainLevels) / sizeof(gainLevels[0]));
    int brightness = map(gainIndex, 0, 5, 255, 0);
    set_pwm_brightness(brightness);

    Serial.print("Нажата кнопка! Новое усиление: ");
    Serial.println(gainLevels[gainIndex]);
    buttonPressed = false;
  }

  size_t bytesIn;
  esp_err_t result = i2s_read(I2S_MIC_PORT, &sBuffer, sizeof(sBuffer), &bytesIn, portMAX_DELAY);

  if (result == ESP_OK && bytesIn > 0) {
    int samples = bytesIn / sizeof(int32_t);
    float gain = gainLevels[gainIndex];

    applyGain(sBuffer, samples, gain);

    size_t bytesOut;
    esp_err_t res = i2s_write(I2S_DAC_PORT, sBuffer, bytesIn, &bytesOut, portMAX_DELAY);

    if (res != ESP_OK || bytesOut != bytesIn) {
      Serial.println("Ошибка записи в I2S DAC");
    }
  } else {
    Serial.println("Ошибка чтения из I2S микрофона");
  }
}

Ты в двух темах решил похвалиться, одной мало?

1 лайк

Ожидается еще третья и четвертая, по мере решения. Третья уже решена, но не оформлена. Для тебя анонс: “Запись звука с INMP441 на SD карту с помощью ESP32”

ооо интересное чтиво))) ждемс, мне удалось сделать и запись через веб сервер, но код дать не могу… однако есть просто код записи на sd первой версии, пойдет на ядре 2.0.17 на которой не идут большинство библиотек аудио

#include <WiFi.h>
#include <ESPAsyncWebServer.h>
#include <SD.h>
#include <SPI.h>

const char* ssid = "ESP32-Access-Point"; // Имя точки доступа
const char* password = "123456789"; // Пароль точки доступа

AsyncWebServer server(80); // Создание асинхронного веб-сервера на порту 80

void setup() {
  Serial.begin(115200); // Инициализация последовательного порта
  
  // Инициализация точки доступа
  WiFi.softAP(ssid, password);
  Serial.println("Access Point started");

  // Получение IP-адреса
  IPAddress myIP = WiFi.softAPIP();
  Serial.print("ESP32 AP IP address: ");
  Serial.println(myIP); // Вывод IP-адреса в последовательный порт
  
  // Инициализация SD-карты
  if (!SD.begin()) {
    Serial.println("Ошибка инициализации SD-карты!");
    return;
  }
  Serial.println("SD-карта инициализирована успешно.");

  // Установка обработчика для корневого URL
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    String html = "<html><body>";
    html += "<h1>Upload MP3 File</h1>";
    html += "<form action=\"/upload\" method=\"POST\" enctype=\"multipart/form-data\">";
    html += "<input type=\"file\" name=\"file\">";
    html += "<input type=\"submit\" value=\"Upload\">";
    html += "</form>";
    html += "</body></html>";
    request->send(200, "text/html", html); // Отправка HTML-страницы
  });

  // Установка обработчика для загрузки файла
  server.on("/upload", HTTP_POST, [](AsyncWebServerRequest *request){
    // Обработка загрузки файла
  }, handleFileUpload);

  // Запуск сервера
  server.begin();
  Serial.println("HTTP server started");
}

void loop() {
  // Обработка клиентов не требуется, так как используется асинхронный сервер
}

void handleFileUpload(AsyncWebServerRequest *request, String filename, size_t index, uint8_t *data, size_t len, bool final) {
  static File file; // Объявляем переменную file как статическую, чтобы она сохраняла свое значение между вызовами

  if (!index) {
    Serial.printf("Upload Start: %s\n", filename.c_str());
    
    // Открываем файл для записи на SD-карту
    file = SD.open("/" + filename, FILE_WRITE);
    if (!file) {
      Serial.println("Не удалось открыть файл для записи");
      request->send(500, "text/plain", "Не удалось открыть файл для записи");
      return;
    } else {
      Serial.printf("Файл %s успешно открыт для записи.\n", filename.c_str());
    }
  }

  // Записываем данные в файл
  if (len) {
    file.write(data, len);
    Serial.printf("Записано %d байт в файл %s\n", len, filename.c_str());
  }

  if (final) {
    file.close();
    Serial.printf("Файл %s успешно загружен\n", filename.c_str());
    request->send(200, "text/plain", "Файл загружен успешно");
  }
}

в общем интересно как у вас успехи и как вы решали задачу)))

Спасибо, что заинтересовались, остальное после публикации.