본문 바로가기

RaspberryPi/W55RP20

[W55RP20-4032] W55RP20 Arduino - 네트웍을 통한 오디오 출력 테스트

 

 

 

[W55RP20-4032] 보드에서 WAV 파일 출력은 정상적으로 확인 되었으므로 이제 네트웍을 통해 WAV 파일을 전송해서 출력해보자.

네트웍을 통해 실시간으로 WAV파일을 받기 위해 W55RP20의 lwIP 이더넷 기능을 활성화 하고 서버를 추가 하였다.

#include <Wire.h>
#include <I2S.h>
#include "audio_data2.h"

#include <W55RP20lwIP.h>
EthernetServer server(5000);

#define I2S_DATA_PIN 10
#define I2S_CK_PIN 11
#define I2S_WS_PIN 12

void setup() {
  Serial.begin(115200);
  while (!Serial) {
   // wait for serial port to connect. Needed for Leonardo only
  }  

  Serial.println("Starting TAS5825P");

  i2s.setDATA(I2S_DATA_PIN);
  i2s.setBCLK(I2S_CK_PIN);
  i2s.setBitsPerSample(32); 
  
  if (!i2s.begin(SAMPLE_RATE)) { 
    Serial.println("I2S Initialization Failed!");
    while (true);
  }

  for (int i = 0; i < (SAMPLE_RATE / 20); i++) {
    i2s.write((int32_t)0); // Left 무음
    i2s.write((int32_t)0); // Right 무음
  }

  delay(10);
  
  // I2C 초기화
  Wire.begin();
  Wire.setClock(100000);  
  initTAS5825P();
  Serial.println("Starting WAV File Output...");
  
  // Start the Ethernet port
  if (!eth.begin()) {
    Serial.println("No wired Ethernet hardware detected. Check pinouts, wiring.");
    while (1) {
      delay(1000);
    }
  }
  while (!eth.connected()) {
    Serial.print(".");
    delay(500);
  }  

  Serial.println("");
  Serial.println("Ethernet connected");
  Serial.println("IP address: ");
  Serial.println(eth.localIP());  

  // 서버 대기 시작
  server.begin();  
}

 

 

메일 루프에서는 TCP 서버를 생성하고 연결되면 WAV 데이터를 수신하여 출력하는 코드로 작성했다.

#define NET_RX_BUF_SIZE 2048
uint8_t rx_buf[NET_RX_BUF_SIZE];

void loop() {
  EthernetClient client = server.available();
  
  if (client) {
    Serial.println("클라이언트 연결됨! 스트리밍 재생을 시작합니다.");
    
    digitalWrite(LED_NET, 0); 
    
    while (client.connected()) {
      int freeWords = i2s.availableForWrite();
      int freeSamples = freeWords / 2; 
      
      if (freeSamples > 0) {
        int maxBytesToRead = freeSamples * 4;
        int netAvailable = client.available();
        
        int bytesToRead = netAvailable;
        if (bytesToRead > maxBytesToRead) bytesToRead = maxBytesToRead;
        if (bytesToRead > NET_RX_BUF_SIZE) bytesToRead = NET_RX_BUF_SIZE;
        
        bytesToRead = (bytesToRead / 4) * 4;
        
        if (bytesToRead >= 4) {
          int actualRead = client.read(rx_buf, bytesToRead);
          actualRead = (actualRead / 4) * 4; 

          if (actualRead > 0) {
            unsigned long currentMillis = millis();

            if (currentMillis - lastBlinkTime >= blinkInterval) {
              lastBlinkTime = currentMillis;
              digitalWrite(LED_STREAM, !digitalRead(LED_STREAM)); 
            }
          }

          // 오디오 데이터 변환 및 I2S 출력 루프
          for (int i = 0; i < actualRead; i += 4) {
            int16_t sample16_L = rx_buf[i]     | (rx_buf[i + 1] << 8);
            int16_t sample16_R = rx_buf[i + 2] | (rx_buf[i + 3] << 8);

            float l_scaled = (float)sample16_L * volume;
            float r_scaled = (float)sample16_R * volume;
            
            if (l_scaled > 32767.0f) l_scaled = 32767.0f;
            if (l_scaled < -32768.0f) l_scaled = -32768.0f;
            if (r_scaled > 32767.0f) r_scaled = 32767.0f;
            if (r_scaled < -32768.0f) r_scaled = -32768.0f;

            int32_t sample32_L = ((int32_t)((int16_t)l_scaled)) << 16;
            int32_t sample32_R = ((int32_t)((int16_t)r_scaled)) << 16;

            i2s.write(sample32_L);
            i2s.write(sample32_R);
          }
        } else {
          delayMicroseconds(100);
        }
      } else {
        delayMicroseconds(100);
      }
    }
    
    Serial.println("스트리밍 종료. 대기 상태로 돌아갑니다.");
    
    digitalWrite(LED_NET, 1);    
    digitalWrite(LED_STREAM, 1); 
    
    for (int i = 0; i < 500; i++) {
      i2s.write((int32_t)0);
      i2s.write((int32_t)0);
    }
    client.stop();
  }
}

 

 

 

테스트를 위해 PC 에서 실시간 WAV파일을 전송하는 프로그램은 간단히 테스트를 위해 Python으로 작성 했다.

import socket
import time

BOARD_IP = '192.168.1.104' 

PORT = 5000
FILE_PATH = 'ElectricGuitar_Mood 1_F.wav'

# 전송 속도 계산 (44.1kHz, 16bit, Stereo 기준)
BYTES_PER_SECOND = 44100 * 2 * 2
CHUNK_SIZE = 4096
SLEEP_TIME = (CHUNK_SIZE / BYTES_PER_SECOND)/16


def send_audio_to_board():
    # IPv4, TCP 소켓 생성
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_sock:
        print(f"🔄 보드({BOARD_IP}:{PORT})에 연결을 시도합니다...")
        
        try:
            # 보드(서버)로 접속
            client_sock.connect((BOARD_IP, PORT))
            print("✅ 보드와 연결되었습니다. 오디오 전송을 시작합니다.")

            with open(FILE_PATH, 'rb') as f:
                f.seek(44) # WAV 헤더 44바이트 스킵

                while True:
                    data = f.read(CHUNK_SIZE)
                    if not data:
                        print("🎵 재생이 완료되었습니다.")
                        break
                    
                    # 보드로 데이터 전송
                    client_sock.sendall(data)
                    
                    # 수신 버퍼 오버플로우 방지를 위한 전송 속도 조절
                    time.sleep(SLEEP_TIME)

        except ConnectionRefusedError:
            print("❌ 오류: 보드에 연결할 수 없습니다. 보드의 전원과 IP 주소를 확인하세요.")
        except FileNotFoundError:
            print(f"❌ 오류: '{FILE_PATH}' 파일을 찾을 수 없습니다.")
        except Exception as e:
            print(f"⚠️ 전송 중 오류가 발생했습니다: {e}")

if __name__ == '__main__':
    send_audio_to_board()

 

컴파일 후 적용하면 보드 접속도 되고 WAV 파일 전송 까지 되지만 출력 속도가 너무 느리다

이더넷 전송 속도가 문제인가?

 

이전에 테스트 했던 RP2040의 iperf 전송률 테스트에서는 25Mbps 정도는 되었는데... Arduino 환경에서 이더넷 전송율 확인이 필요하다.


W55RP20에서 Arduino 환경의 lwIP 기반의 iperf 전송률 측정 결과는 2Mbps 정도로 측정된다.

이정도면 일반적인 CD음질 44.1khz 16bit stereo 로 전송시 1.4Mbps(44100 x 16 x2) 면 되므로 문제가 없어야 하는데...

 

물론 이경우 24bit 고음질 WAV 전송에는 문제가 있을것 같다. 

아무래도 고음질 오디오 송출을 위해서는 pi pico C/C++ SDK를 이용하여 테스트가 필요할것 같다.