[ESP32 audio and video transmission] ②Collect audio data of SPH0645 microphone through I2S and upload it to the server for real-time playback

Tip: After the article is written, the table of contents can be automatically generated. How to generate it can refer to the help document on the right


foreword

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


1. I2S Microphone SPH0645

ESP32 has several ways to read data from external microphone:

①Read data directly from the built-in analog-to-digital converter (ADC)
This is useful for one-time readings, but not suitable for high sampling rates.
②Using I2S to read the built-in ADC via DMA
is suitable for analog microphones, such as MAX4466 and MAX9814
③Using I2S to directly read I2S compatible peripherals
for microphones such as SPH0645LM4H, INPM441, ICS43432 and ICS43434
SPH0645

2. Use steps

1. Connection diagram

The code is as follows (example):
insert image description here
(The above pictures are all from: https://diyi0t.com/i2s-sound-tutorial-for-esp32/ )

①Bit clock line

The official name is "Continuous Serial Clock (SCK)". Usually written "bit clock (BCLK)".

② Word clock line

Formally "Word Choice (WS)". Often referred to as "Left-Right Clock (LRCLK)" or "Frame Sync (FS)".

0 = left channel, 1 = right channel

③Data cable

The official name is "Serial Data (SD)", but may be called SDATA, SDIN, SDOUT, DACDAT, ADCDAT, etc.

My wiring diagram is like this.
insert image description here
The pins correspond to:

// 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 main file code

The I2S operation SPH0645 of the project code refers to: https://github.com/atomic14/esp32-walkie-talkie , the original project uses HTTP stream, this example is changed to AsyncUDP
main file code as follows:

#include <Arduino.h>
#include <WiFi.h>

#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

}

The data requested by the url network used here.

3. The server uses UDP to receive the audio code

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 complete program

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

Guess you like

Origin blog.csdn.net/loveliveoil/article/details/130542231