ESP32实现类似对讲机功能

好嘞!我给你写一个用ESP32-S3实现的最简易UDP对讲机示例,包含:

  • WiFi连接
  • I2S采集麦克风音频
  • UDP发送采集的音频数据
  • UDP接收对方音频数据并播放
  • 按键控制“按住说话,松开听”

硬件前提

  • 麦克风接ESP32-S3的I2S输入(I2S麦克风或模拟麦克风接ADC也行,这里用I2S麦克风示范)
  • 扬声器接I2S或DAC输出(示例用I2S)
  • 按键接GPIO(带下拉,按下为低)

代码说明

  • 采样率16kHz,16bit单声道
  • UDP包大小为256字节PCM数据(128个16bit样本)
  • 按键GPIO0,按下开始采集发送,松开接收播放

cpp复制编辑#include <WiFi.h>
#include <WiFiUdp.h>
#include "driver/i2s.h"

#define I2S_NUM           (0)     // 使用I2S0
#define SAMPLE_RATE       (16000)
#define I2S_BUFFER_SIZE   (256)   // 256字节 = 128个16bit采样
#define UDP_LOCAL_PORT    (12345)
#define UDP_REMOTE_PORT   (12345)
#define BUTTON_PIN        (0)     // 按键GPIO

const char* WIFI_SSID = "your_ssid";
const char* WIFI_PASS = "your_password";
const char* REMOTE_IP = "192.168.1.100";  // 对方IP

WiFiUDP udp;

uint16_t i2s_read_buffer[I2S_BUFFER_SIZE / 2];
uint16_t i2s_write_buffer[I2S_BUFFER_SIZE / 2];

void setupWiFi() {
  Serial.print("Connecting to WiFi...");
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println(" connected!");
  Serial.print("IP: ");
  Serial.println(WiFi.localIP());
  udp.begin(UDP_LOCAL_PORT);
}

void setupI2S() {
  // 配置I2S作为麦克风输入
  i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX | I2S_MODE_PDM),
    .sample_rate = SAMPLE_RATE,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format = I2S_COMM_FORMAT_STAND_MSB,
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 4,
    .dma_buf_len = 128,
    .use_apll = false,
    .tx_desc_auto_clear = false,
    .fixed_mclk = 0
  };

  i2s_pin_config_t pin_config = {
    .bck_io_num = 14,    // I2S BCK (bit clock)
    .ws_io_num = 15,     // I2S WS (word select)
    .data_out_num = -1,  // 不用输出
    .data_in_num = 13    // I2S DATA_IN (麦克风数据)
  };

  i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
  i2s_set_pin(I2S_NUM, &pin_config);
  i2s_zero_dma_buffer(I2S_NUM);
}

void setupI2SOut() {
  // 配置I2S作为扬声器输出
  i2s_config_t i2s_config = {
    .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
    .sample_rate = SAMPLE_RATE,
    .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
    .channel_format = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format = I2S_COMM_FORMAT_STAND_MSB,
    .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count = 4,
    .dma_buf_len = 128,
    .use_apll = false,
    .tx_desc_auto_clear = true,
    .fixed_mclk = 0
  };

  i2s_pin_config_t pin_config = {
    .bck_io_num = 14,
    .ws_io_num = 15,
    .data_out_num = 27,  // I2S数据输出(扬声器)
    .data_in_num = -1
  };

  i2s_driver_install(I2S_NUM, &i2s_config, 0, NULL);
  i2s_set_pin(I2S_NUM, &pin_config);
  i2s_zero_dma_buffer(I2S_NUM);
}

void sendAudio() {
  size_t bytes_read = 0;
  // 从麦克风读数据
  i2s_read(I2S_NUM, (void*)i2s_read_buffer, I2S_BUFFER_SIZE, &bytes_read, portMAX_DELAY);
  if (bytes_read == I2S_BUFFER_SIZE) {
    udp.beginPacket(REMOTE_IP, UDP_REMOTE_PORT);
    udp.write((uint8_t*)i2s_read_buffer, I2S_BUFFER_SIZE);
    udp.endPacket();
  }
}

void receiveAudio() {
  int packetSize = udp.parsePacket();
  if (packetSize >= I2S_BUFFER_SIZE) {
    int len = udp.read((uint8_t*)i2s_write_buffer, I2S_BUFFER_SIZE);
    if (len == I2S_BUFFER_SIZE) {
      size_t bytes_written = 0;
      i2s_write(I2S_NUM, (const void*)i2s_write_buffer, I2S_BUFFER_SIZE, &bytes_written, portMAX_DELAY);
    }
  }
}

void setup() {
  Serial.begin(115200);
  pinMode(BUTTON_PIN, INPUT_PULLUP);

  setupWiFi();

  // I2S初始化为输入采集(先)
  setupI2S();

  Serial.println("Setup complete. Press button to talk.");
}

void loop() {
  if (digitalRead(BUTTON_PIN) == LOW) {
    // 按键按下,采集并发送音频
    sendAudio();
  } else {
    // 松开按键,接收并播放音频
    // 先卸载I2S输入,切换成输出模式
    i2s_driver_uninstall(I2S_NUM);
    setupI2SOut();

    receiveAudio();

    // 再切回输入模式,准备下一次按键说话
    i2s_driver_uninstall(I2S_NUM);
    setupI2S();
  }
}

说明和改进

  • 代码中setupI2SsetupI2SOut反复切换输入输出,是最简方案,实际可以用双I2S或者更优的音频框架避免切换延迟。
  • 麦克风型号和接线请根据实际I2S麦克风调整。
  • 音质和延迟需调试缓冲区大小和采样率。
  • 可进一步添加音频压缩、VAD和多播。
  • 如果用ADC麦克风或内置ADC,采样部分需改写。
上一篇
下一篇