【ESP32音视频传输】②通过I2S采集SPH0645麦克风音频数据并上传到服务端实时播放

04-10 1577阅读

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • 一、I2S型麦克风SPH0645
  • 二、使用步骤
    • 1.连线图
    • 2.Arduino主文件代码
    • 3.服务端利用UDP接收音频代码
    • Arduino完整程序

      前言

      本文章基于Arduino ESP32 2.07版本,因为2.04版本开始I2S驱动被更改了,所以相同代码可能效果不太同 本文主要参考了:https://atomic14.com/2020/09/12/esp32-audio-input.html


      一、I2S型麦克风SPH0645

      ESP32有多种方式从外置麦克风中读取数据:

      ①直接从内置模数转换器 (ADC) 读取数据

      这对于一次性读数很有用,但不适用于高采样率。

      ②使用 I2S 通过 DMA 读取内置 ADC

      适用于模拟麦克风,例如 MAX4466 和 MAX9814

      ③使用 I2S 直接读取 I2S 兼容外设

      适用于 SPH0645LM4H、INPM441、ICS43432 和 ICS43434 等麦克风

      【ESP32音视频传输】②通过I2S采集SPH0645麦克风音频数据并上传到服务端实时播放

      二、使用步骤

      1.连线图

      【ESP32音视频传输】②通过I2S采集SPH0645麦克风音频数据并上传到服务端实时播放

      【ESP32音视频传输】②通过I2S采集SPH0645麦克风音频数据并上传到服务端实时播放

      (上图均来自:https://diyi0t.com/i2s-sound-tutorial-for-esp32/)

      ①位时钟线

      正式名称为“连续串行时钟 (SCK)”。通常写作“位时钟(BCLK)”。

      ② 字时钟线

      正式的“单词选择(WS)”。通常称为“左右时钟 (LRCLK)”或“帧同步 (FS)”。

      0 = 左声道,1 = 右声道

      ③数据线

      正式名称为“串行数据 (SD)”,但可以称为 SDATA、SDIN、SDOUT、DACDAT、ADCDAT 等。

      我的连线图是这样的

      【ESP32音视频传输】②通过I2S采集SPH0645麦克风音频数据并上传到服务端实时播放

      引脚对应为:

      // i2s pins
      i2s_pin_config_t i2sPins = {
          .bck_io_num = GPIO_NUM_15,
          .ws_io_num = GPIO_NUM_16,
          .data_out_num = I2S_PIN_NO_CHANGE,
          .data_in_num = GPIO_NUM_21};
      

      2.Arduino主文件代码

      工程代码的I2S操作SPH0645参考了:https://github.com/atomic14/esp32-walkie-talkie,原工程用的是HTTP stream,本例改成了AsyncUDP

      主文件代码如下:

      #include 
      #include 
      #include "soc/soc.h"
      #include "soc/rtc_cntl_reg.h"
      #include "esp_camera.h"
      #include "configuration.h"
      #include "I2SMEMSSampler.h"
      #include "AsyncUDP.h"
      const char * ssid = "your-ssid"; //WiFi名称
      const char * password = "password";//wifi密码
      IPAddress serverip = IPAddress(192,168,1,108);    //服务端的静态IP地址
      uint16_t toport_sound = 8085;              //服务端的开放端口
      const int SAMPLE_SIZE = 700;//udp一次最大发送的数据包大小,UDP最大发生有限制
      AsyncUDP SoundUDP;
      I2SSampler *i2sSampler = NULL;
      void InitUDP(IPAddress ip, uint16_t toport_sound){
        if(SoundUDP.connect(ip, toport_sound)) {
            Serial.println("UDP connected");
            SoundUDP.onPacket([](AsyncUDPPacket packet) {
                Serial.print("UDP Packet Type: ");
                Serial.print(packet.isBroadcast()?"Broadcast":packet.isMulticast()?"Multicast":"Unicast");
                Serial.print(", From: ");
                Serial.print(packet.remoteIP());
                Serial.print(":");
                Serial.print(packet.remotePort());
                Serial.print(", To: ");
                Serial.print(packet.localIP());
                Serial.print(":");
                Serial.print(packet.localPort());
                Serial.print(", Length: ");
                Serial.print(packet.length());
                Serial.print(", Data: ");
                Serial.write(packet.data(), packet.length());
                Serial.println();
                //reply to the client
                packet.printf("S2 Got %u bytes of data", packet.length());
            });
            //Send unicast
            //SoundUDP.print("This is S2 Server");
        }  
      }
      // i2s config for reading from left channel of I2S
      i2s_config_t i2sMemsConfigLeftChannel = {
          .mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
          .sample_rate = 16000,
          .bits_per_sample = I2S_BITS_PER_SAMPLE_32BIT,
          .channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT,
          .communication_format = i2s_comm_format_t(I2S_COMM_FORMAT_STAND_I2S),
          .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
          .dma_buf_count = 4,
          .dma_buf_len = 1024,
          .use_apll = false,
          .tx_desc_auto_clear = false,
          .fixed_mclk = 0};
      // i2s pins
      i2s_pin_config_t i2sPins = {
          .bck_io_num = GPIO_NUM_15,
          .ws_io_num = GPIO_NUM_16,
          .data_out_num = I2S_PIN_NO_CHANGE,
          .data_in_num = GPIO_NUM_21};
      //      发送音频数据
      void sendSoundData(uint8_t *bytes, size_t count, AsyncUDP* UDPClient = &SoundUDP, uint16_t the_port = toport_sound)
      {
        // send them off to the server
        digitalWrite(2, HIGH);
        /*
        httpClient->begin(*wifiClient, url);
        httpClient->addHeader("content-type", "application/octet-stream");
        httpClient->POST(bytes, count);
        httpClient->end();
        */
       //这是向局域网内所有用户广播,若只发给连接的用户,要用UDPClient.write()
        UDPClient->broadcastTo(bytes, count,the_port);
        digitalWrite(2, LOW);
      }
      // Task to write samples to our server
      void i2sMemsWriterTask(void *param)
      {
        I2SSampler *sampler = (I2SSampler *)param;
        int16_t *samples = (int16_t *)malloc(sizeof(uint16_t) * SAMPLE_SIZE);
        if (!samples)
        {
          Serial.println("Failed to allocate memory for samples");
          return;
        }
        while (true)
        {
          int samples_read = sampler->read(samples, SAMPLE_SIZE);
          //sendSoundData((uint8_t *)samples, samples_read * sizeof(uint16_t));
          SoundUDP.write((uint8_t *)samples, samples_read * sizeof(uint16_t));
          //SoundUDP.broadcastTo((uint8_t *)samples, samples_read * sizeof(uint16_t),toport_sound);
        }
      }
      void setup()
      {
        WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); //disable brownout detector
        Serial.begin(115200);
        //Serial.setDebugOutput(false);
        // launch WiFi
          WiFi.mode(WIFI_STA);
          WiFi.begin(ssid, password);
          if (WiFi.waitForConnectResult() != WL_CONNECTED) {
              Serial.println("WiFi Failed");
              while(1) {
                  delay(1000);
              }
          }
        // indicator LED
        pinMode(2, OUTPUT);
        InitUDP(serverip,toport_sound);
        // Direct i2s input from INMP441 or the SPH0645
        i2sSampler = new I2SMEMSSampler(I2S_NUM_1, i2sPins, i2sMemsConfigLeftChannel, false);
        i2sSampler->start();
        // set up the i2s sample writer task
        TaskHandle_t i2sMemsWriterTaskHandle;
        xTaskCreatePinnedToCore(i2sMemsWriterTask, "I2S Writer Task", 4096, i2sSampler, 1, &i2sMemsWriterTaskHandle, 1);
        // // start sampling from i2s device
      }
      void loop()
      {
        // nothing to do here - everything is taken care of by tasks
      }
      

      该处使用的url网络请求的数据。

      3.服务端利用UDP接收音频代码

      import pyaudio
      import socket
      # 配置参数
      chunk = 1400
      sample_rate = 16000
      duration = 5
      udp_port = 8085
      p = pyaudio.PyAudio()
      # 打开音频流
      stream = p.open(format=pyaudio.paInt16,
                      channels=1,
                      rate=sample_rate,
                      output=True)
      # 打开UDP套接字
      sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
      sock.bind(('0.0.0.0', udp_port))
      # 播放PCM音频数据
      while True:
          data, addr = sock.recvfrom(chunk)
          print(f"Received message ")
          stream.write(data)
      # 关闭音频流和UDP套接字
      stream.stop_stream()
      stream.close()
      sock.close()
      p.terminate()
      

      Arduino完整程序

      https://download.csdn.net/download/loveliveoil/87762126

VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]