Waveshare ESP32-S3-LCD-1.85

Edit: I updated the example to include both mic and speaker that have been confirmed to work. It should also be a complete example with UDP send and receive code.

It’s a bit of a hack, using the microphone event to read and feed the speaker data, but it works.

I have been able to stream audio out of the microphone using this configuration:

i2s_audio:
  - id: i2s_in
    i2s_lrclk_pin: GPIO2
    i2s_bclk_pin: GPIO15
  - id: i2s_out
    i2s_lrclk_pin: GPIO38
    i2s_bclk_pin: GPIO48
    #i2s_mclk_pin: GPIO21

microphone:
  - platform: i2s_audio
    id: i2s_microphone
    i2s_audio_id: i2s_in
    i2s_din_pin: GPIO39
    adc_type: external
    pdm: false
    channel: right
    sample_rate: 16000
    bits_per_sample: 16bit
    on_data:
      - lambda: |-
          static std::vector<int16_t> send_buffer;
          static std::vector<uint8_t> recv_buffer(1024);
          static struct sockaddr_in destination = {
            .sin_family = AF_INET,
            .sin_port = htons(12345),
            .sin_addr = { .s_addr = inet_addr("192.168.1.165") }
          };
          static struct sockaddr_in source = {
            .sin_family = AF_INET,
            .sin_port = htons(12346),
            .sin_addr = { .s_addr = INADDR_ANY }
          };
          static int send_sock = ::socket(AF_INET, SOCK_DGRAM, 0);
          static int recv_sock = ::socket(AF_INET, SOCK_DGRAM, 0);
          static bool bound = false;
          if (!bound) {
            int flags = fcntl(recv_sock, F_GETFL, 0);
            fcntl(recv_sock, F_SETFL, flags | O_NONBLOCK);
            bind(recv_sock, (const sockaddr*)&source, sizeof(source));
            bound = true;
          }
          for (uint16_t byte : x) {
            send_buffer.push_back(byte);
          }
          int send_cnt = send_buffer.size();
          if(send_cnt >= 256) {
            ::sendto(send_sock, send_buffer.data(), send_cnt * 2, 0, reinterpret_cast<sockaddr*>(&destination), sizeof(destination));
            send_buffer.clear();
          }
          socklen_t fromlen = sizeof(source);
          int recv_cnt = ::recvfrom(recv_sock, recv_buffer.data(), recv_buffer.size(), 0, reinterpret_cast<sockaddr*>(&source), &fromlen);
          if (recv_cnt > 0) {
            id(i2s_speaker).play(recv_buffer.data(), recv_cnt, 0);
          }

button:
  - platform: template
    name: "Enable Microphone"
    on_press:
      - microphone.capture: i2s_microphone
      - delay: 10s
      - microphone.stop_capture: i2s_microphone
        
speaker:
  - platform: i2s_audio
    id: i2s_speaker
    i2s_audio_id: i2s_out
    i2s_dout_pin: GPIO47
    dac_type: external
    sample_rate: 16000
    bits_per_sample: 16bit
    channel: mono

I suspect the problem that I was having previously was not knowing that I needed to start capture, which is what the button is for.

I got most of this from here: Is there a way to stream audio from one ESPHome to another? - #9 by haforum

I’m currently trying to figure out how to do something similar for the speaker to test it.

1 Like