インターフォンの製作(その8)

前回各部品は機能している様だが綺麗な音に成らない(製作には殆ど再生されない)の原因が分かりました。ネットに問題が有った様です。

最初にESP32のUDPについてネット検索している時、見つかった記事ではESP32をAP接続しているものがほとんでした。今回はそれをルータを介した接続にし、かつDNSを使用しています。シリアルプロッタでサイン波が時々止まるのはこの接続方法に問題があるのでは無いかと予想しました。そこで、クライアントとサーバーのスケッチを以下の様に書き換えました。

先ずはサーバー側


#include <WiFiUdp.h>
#include "WiFi.h"
#include <driver/i2s.h>

const char * ssid = "ESP32UDP";             // SSID
const char * password = "12345678";         // password

static const int RmoteUdpPort = 9000;      
static const int LocalPort = 9000;         

// AMP Port 
#define I2S_NUM_AMP                 I2S_NUM_0
#define I2S_PIN_WS_AMP              4
#define I2S_PIN_CLK_AMP             16
#define I2S_PIN_DOUT_AMP            17
#define I2S_PIN_DIN_AMP             I2S_PIN_NO_CHANGE

#define I2S_SAMPLE_RATE             32000
#define I2S_BUFFER_COUNT            4
#define I2S_BUFFER_SIZE             512
 
//DMA Buffer
uint8_t recBuffer[I2S_BUFFER_SIZE];

WiFiUDP UDP;

// Initiate AMP
void i2s_AMP_Init() 
{
  i2s_config_t i2s_config_AMP = {
    .mode                     = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
    .sample_rate              = I2S_SAMPLE_RATE,
    .bits_per_sample          = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format           = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format     = I2S_COMM_FORMAT_I2S,
    .intr_alloc_flags         = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count            = I2S_BUFFER_COUNT,
    .dma_buf_len              = I2S_BUFFER_SIZE,
    .use_apll                 = false,
    .tx_desc_auto_clear       = false,
    .fixed_mclk               = 0
  };
  
  i2s_pin_config_t pin_config_AMP = {
    .bck_io_num                   = I2S_PIN_CLK_AMP,
    .ws_io_num                    = I2S_PIN_WS_AMP,
    .data_out_num                 = I2S_PIN_DOUT_AMP,
    .data_in_num                  = I2S_PIN_DIN_AMP,
  };
 
  i2s_driver_install(I2S_NUM_AMP, &i2s_config_AMP, 0, NULL);
  i2s_set_pin(I2S_NUM_AMP, &pin_config_AMP);
}

void setup()
{
  Serial.begin(115200);

  WiFi.softAP(ssid, password); 
  delay(1000);

  Serial.print("AP IP address: ");            //    192.168.4.1
  Serial.println(WiFi.softAPIP());
  
  UDP.begin(LocalPort);
  i2s_AMP_Init();
  Serial.println("Server OK");
}

//DMA Buffer
#define DMA_BUFFER_SIZE             1024

void loop()
{
  uint8_t rv_buf[DMA_BUFFER_SIZE];

  if (UDP.parsePacket() > 0)
  {
    Serial.println("Recieve");
    UDP.read(rv_buf , DMA_BUFFER_SIZE);
    i2s_write_bytes(I2S_NUM_AMP, rv_buf, sizeof(rv_buf), portMAX_DELAY);
    UDP.flush();
  }
}
  • 5,6行: AP接続なので、SSIDとパスワードを設定します。
  • 59行: ここで、AP接続開始
    • AP接続の場合、特別に設定しない限り、IPアドレスは、”192.168.4.1”になります。
  • 62行: ここでIPアドレスがシリアルモニタに表示されます。アドレスを確認して下さい。

次はクライアント側


#include "WiFi.h"
#include <WiFiUdp.h>
#include <driver/i2s.h>

const char * ssid = "ESP32UDP";         // SSID
const char * password = "12345678";     // password

WiFiUDP wifiUdp;
 
static const int RmoteUdpPort = 9000;  
static const int LocalPort = 9000;     

// MIC Port 
#define I2S_NUM_MIC                 I2S_NUM_1
#define I2S_PIN_CLK_MIC             27
#define I2S_PIN_WS_MIC              25
#define I2S_PIN_DOUT_MIC            I2S_PIN_NO_CHANGE
#define I2S_PIN_DIN_MIC             26
 
#define I2S_SAMPLE_RATE             32000
#define I2S_BUFFER_COUNT            4
#define I2S_BUFFER_SIZE             512

//DMA Buffer
#define DMA_BUFFER_SIZE             1024
uint8_t recBuffer[DMA_BUFFER_SIZE];

// Initiate MIC
void i2s_MIC_Init() 
{
  i2s_config_t i2s_config_MIC = {
    .mode                     = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
    .sample_rate              = I2S_SAMPLE_RATE,
    .bits_per_sample          = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format           = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format     = I2S_COMM_FORMAT_I2S,
    .intr_alloc_flags         = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count            = I2S_BUFFER_COUNT,
    .dma_buf_len              = I2S_BUFFER_SIZE,
    .use_apll                 = false,
    .tx_desc_auto_clear       = false,
    .fixed_mclk               = 0
  };
  
  i2s_pin_config_t pin_config_MIC = {
    .bck_io_num                   = I2S_PIN_CLK_MIC,
    .ws_io_num                    = I2S_PIN_WS_MIC,
    .data_out_num                 = I2S_PIN_DOUT_MIC,
    .data_in_num                  = I2S_PIN_DIN_MIC,
  };
 
  i2s_driver_install(I2S_NUM_MIC, &i2s_config_MIC, 0, NULL);
  i2s_set_pin(I2S_NUM_MIC, &pin_config_MIC);
}

void setup()
{
  Serial.begin(115200);
    
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  // Try forever
  while (WiFi.status() != WL_CONNECTED) 
  {
    Serial.println("...Connecting to WiFi");
    delay(1000);
  }
  Serial.println("Connected");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  wifiUdp.begin(LocalPort);
  i2s_MIC_Init();
  Serial.println("Client OK");
}

void loop()
{
  size_t transBytes;

  Serial.println("Write");
  i2s_read(I2S_NUM_MIC, (char*)recBuffer, DMA_BUFFER_SIZE, &transBytes, portMAX_DELAY);
  wifiUdp.beginPacket("192.168.4.1", RmoteUdpPort);
  wifiUdp.write(recBuffer,DMA_BUFFER_SIZE);
  wifiUdp.endPacket(); 
}
  • こちらは殆ど変えていません
  • 85行: サーバーのアドレスを、”192.168.4.1”と直接指定しています。変更箇所はここのみ。

両者をコンパイル後実行して下さい。クライアント側のマイクの音声サーバー側で綺麗に再生されました。この環境なら問題は無いようです。

今回は、

  • ルータを返したUDP通信から、一方をサーバー(AP)、他方をクライント方式に変更
  • DNSの使用を廃止

の2つを行って上手く音データを送る事が出来ました。不具合を確認する為にもう少し確認を進めます。

AP接続でDNAを使う場合の確認

AP接続でDNSを使う場合を確認します。DNSを使用したAP接続サーバー側のスケッチ。


#include <WiFiUdp.h>
#include "WiFi.h"
#include <driver/i2s.h>
#include <ESPmDNS.h>

const char * ssid = "ESP32UDP";             // SSID
const char * password = "12345678";         // password

static const int RmoteUdpPort = 9000;      
static const int LocalPort = 9000;         

// AMP Port 
#define I2S_NUM_AMP                 I2S_NUM_0
#define I2S_PIN_WS_AMP              4
#define I2S_PIN_CLK_AMP             16
#define I2S_PIN_DOUT_AMP            17
#define I2S_PIN_DIN_AMP             I2S_PIN_NO_CHANGE

#define I2S_SAMPLE_RATE             32000
#define I2S_BUFFER_COUNT            4
#define I2S_BUFFER_SIZE             512
 
//DMA Buffer
uint8_t recBuffer[I2S_BUFFER_SIZE];

WiFiUDP UDP;

// Initiate AMP
void i2s_AMP_Init() 
{
  i2s_config_t i2s_config_AMP = {
    .mode                     = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX),
    .sample_rate              = I2S_SAMPLE_RATE,
    .bits_per_sample          = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format           = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format     = I2S_COMM_FORMAT_I2S,
    .intr_alloc_flags         = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count            = I2S_BUFFER_COUNT,
    .dma_buf_len              = I2S_BUFFER_SIZE,
    .use_apll                 = false,
    .tx_desc_auto_clear       = false,
    .fixed_mclk               = 0
  };
  
  i2s_pin_config_t pin_config_AMP = {
    .bck_io_num                   = I2S_PIN_CLK_AMP,
    .ws_io_num                    = I2S_PIN_WS_AMP,
    .data_out_num                 = I2S_PIN_DOUT_AMP,
    .data_in_num                  = I2S_PIN_DIN_AMP,
  };
 
  i2s_driver_install(I2S_NUM_AMP, &i2s_config_AMP, 0, NULL);
  i2s_set_pin(I2S_NUM_AMP, &pin_config_AMP);
}

void setup()
{
  Serial.begin(115200);

  WiFi.softAP(ssid, password); 
  delay(1000);

  Serial.print("AP IP address: ");            //    192.168.4.1
  Serial.println(WiFi.softAPIP());
  
  if (MDNS.begin("U_Server")) 
    Serial.println("MDNS responder started");

  UDP.begin(LocalPort);
  i2s_AMP_Init();
  Serial.println("Server OK");
}

//DMA Buffer
#define DMA_BUFFER_SIZE             1024

void loop()
{
  uint8_t rv_buf[DMA_BUFFER_SIZE];

  if (UDP.parsePacket() > 0)
  {
    Serial.println("Recieve");
    UDP.read(rv_buf , DMA_BUFFER_SIZE);
    i2s_write_bytes(I2S_NUM_AMP, rv_buf, sizeof(rv_buf), portMAX_DELAY);
    UDP.flush();
  }
}
  • 4行: DNAを使うので、ESPmDNS.hを読み込む。
  • 66行: ここで、ドメイン名を、”U_Srever”としてDNSを開始

クライアント側は


#include "WiFi.h"
#include <WiFiUdp.h>
#include <driver/i2s.h>
#include <ESPmDNS.h>

const char * ssid = "ESP32UDP";         // SSID
const char * password = "12345678";     // password

WiFiUDP wifiUdp;
 
static const int RmoteUdpPort = 9000;  
static const int LocalPort = 9000;     

// MIC Port 
#define I2S_NUM_MIC                 I2S_NUM_1
#define I2S_PIN_CLK_MIC             27
#define I2S_PIN_WS_MIC              25
#define I2S_PIN_DOUT_MIC            I2S_PIN_NO_CHANGE
#define I2S_PIN_DIN_MIC             26
 
#define I2S_SAMPLE_RATE             32000
#define I2S_BUFFER_COUNT            4
#define I2S_BUFFER_SIZE             512

//DMA Buffer
#define DMA_BUFFER_SIZE             1024
uint8_t recBuffer[DMA_BUFFER_SIZE];

// Initiate MIC
void i2s_MIC_Init() 
{
  i2s_config_t i2s_config_MIC = {
    .mode                     = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
    .sample_rate              = I2S_SAMPLE_RATE,
    .bits_per_sample          = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format           = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format     = I2S_COMM_FORMAT_I2S,
    .intr_alloc_flags         = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count            = I2S_BUFFER_COUNT,
    .dma_buf_len              = I2S_BUFFER_SIZE,
    .use_apll                 = false,
    .tx_desc_auto_clear       = false,
    .fixed_mclk               = 0
  };
  
  i2s_pin_config_t pin_config_MIC = {
    .bck_io_num                   = I2S_PIN_CLK_MIC,
    .ws_io_num                    = I2S_PIN_WS_MIC,
    .data_out_num                 = I2S_PIN_DOUT_MIC,
    .data_in_num                  = I2S_PIN_DIN_MIC,
  };
 
  i2s_driver_install(I2S_NUM_MIC, &i2s_config_MIC, 0, NULL);
  i2s_set_pin(I2S_NUM_MIC, &pin_config_MIC);
}

void setup()
{
  Serial.begin(115200);
    
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  // Try forever
  while (WiFi.status() != WL_CONNECTED) 
  {
    Serial.println("...Connecting to WiFi");
    delay(1000);
  }
  Serial.println("Connected");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("U_Client")) 
    Serial.println("MDNS responder started");

  wifiUdp.begin(LocalPort);
  i2s_MIC_Init();
  Serial.println("Client OK");
}

void loop()
{
  size_t transBytes;

  Serial.println("Write");
  i2s_read(I2S_NUM_MIC, (char*)recBuffer, DMA_BUFFER_SIZE, &transBytes, portMAX_DELAY);
  wifiUdp.beginPacket(MDNS.queryHost("U_Server"), RmoteUdpPort);
  wifiUdp.write(recBuffer,DMA_BUFFER_SIZE);
  wifiUdp.endPacket(); 
}
  • 4行: DNAを使うので、ESPmDNS.hを読み込む。
  • 75行: ここで、ドメイン名を、”U_Client”としてDNSを開始
  • 89行: wifiUdp.beginPacket(MDNS.queryHost(“U_Server”), RmoteUdpPort);
    • サーバーのIPアドレスを、MDNS.queryHost(“U_Server”)で取得

これでコンパイル実行して下さい。なんと前回と同じブツブツとエコーが再現しました。MDNS.queryHost(“U_Server”)でIPアドレスを取る事に問題がある様です。

ルータを介してIPアドレスを固定する場合

次に、ルータを介してIPアドレスを固定する場合に付いて確認して見ます。スケッチは載せませんが、結果は若干エコーが掛かった再生となりました。音質的にはAP接続のDNS使用よりは良かったです。

ここまでで、

  • ルーターを介さない、AP接続
  • DNSを使用しない

が綺麗な音声を再生する為に必要な事と分かったのですが、やっぱり、いちいちIPアドレスを固定するのは面倒です。そこで、DNSを使うが、setup()で、MDNS.queryHost()を使用して予めIPアドレスを取得し、それを、wifiUdp.beginPacket()で使えば遅れは生じないのでは無いかと予想。

確認するために、サーバー側は、”AP接続でDNSを使う場合スケッチ”と同じスケッチを、クライアント側を以下の様に修正しました。


#include "WiFi.h"
#include <WiFiUdp.h>
#include <driver/i2s.h>
#include <ESPmDNS.h>

const char * ssid = "ESP32UDP";             // SSID
const char * password = "12345678";         // password

WiFiUDP wifiUdp;
 
static const int RmoteUdpPort = 9000;  
static const int LocalPort = 9000;     

// MIC Port 
#define I2S_NUM_MIC                 I2S_NUM_1
#define I2S_PIN_CLK_MIC             27
#define I2S_PIN_WS_MIC              25
#define I2S_PIN_DOUT_MIC            I2S_PIN_NO_CHANGE
#define I2S_PIN_DIN_MIC             26
 
#define I2S_SAMPLE_RATE             32000
#define I2S_BUFFER_COUNT            4
#define I2S_BUFFER_SIZE             512

//DMA Buffer
#define DMA_BUFFER_SIZE             1024
uint8_t recBuffer[DMA_BUFFER_SIZE];

// Initiate MIC
void i2s_MIC_Init() 
{
  i2s_config_t i2s_config_MIC = {
    .mode                     = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_RX),
    .sample_rate              = I2S_SAMPLE_RATE,
    .bits_per_sample          = I2S_BITS_PER_SAMPLE_32BIT,
    .channel_format           = I2S_CHANNEL_FMT_ONLY_LEFT,
    .communication_format     = I2S_COMM_FORMAT_I2S,
    .intr_alloc_flags         = ESP_INTR_FLAG_LEVEL1,
    .dma_buf_count            = I2S_BUFFER_COUNT,
    .dma_buf_len              = I2S_BUFFER_SIZE,
    .use_apll                 = false,
    .tx_desc_auto_clear       = false,
    .fixed_mclk               = 0
  };
  
  i2s_pin_config_t pin_config_MIC = {
    .bck_io_num                   = I2S_PIN_CLK_MIC,
    .ws_io_num                    = I2S_PIN_WS_MIC,
    .data_out_num                 = I2S_PIN_DOUT_MIC,
    .data_in_num                  = I2S_PIN_DIN_MIC,
  };
 
  i2s_driver_install(I2S_NUM_MIC, &i2s_config_MIC, 0, NULL);
  i2s_set_pin(I2S_NUM_MIC, &pin_config_MIC);
}

IPAddress ip_server; 

void setup()
{
  Serial.begin(115200);
    
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);

  // Try forever
  while (WiFi.status() != WL_CONNECTED) 
  {
    Serial.println("...Connecting to WiFi");
    delay(1000);
  }
  Serial.println("Connected");
  Serial.println(ssid);
  Serial.print("IP address: ");
  Serial.println(WiFi.localIP());

  if (MDNS.begin("U_Client")) 
    Serial.println("MDNS responder started");

  ip_server = MDNS.queryHost("U_Server");
  
  wifiUdp.begin(LocalPort);
  i2s_MIC_Init();

  Serial.println("Client OK");
}

void loop()
{
  size_t transBytes;

  Serial.println("Write");
  i2s_read(I2S_NUM_MIC, (char*)recBuffer, DMA_BUFFER_SIZE, &transBytes, portMAX_DELAY);
  wifiUdp.beginPacket(ip_server, RmoteUdpPort);
  wifiUdp.write(recBuffer,DMA_BUFFER_SIZE);
  wifiUdp.endPacket(); 
}
  • 80行: ip_server = MDNS.queryHost(“U_Server”);
    • ここで、サーバーのIPアドレスを取得しています。
  • 94行: wifiUdp.beginPacket(ip_server, RmoteUdpPort);
    • 第一引数に、”MDNS.queryHost(“U_Server”)”を指定していたのでが、ここに、予め取得した、”ip_server”を指定しています。

これで実行して下さい。問題無く再生出来ました。これから、”DNSを使用しない”では無く、”wifiUdp.beginPacket()”関数の第一引数に、”MDNS.queryHost(“U_Server”)”を使わない。が正しい様です。

今回はここまで。次回はクライアント、サーバー共にマイクとアンプを実装してみたいと思います。