ESP32でEthernetライブラリのコンパイルエラーを回避する方法【Arduino使用】

Raspberry Pi/電子工作
スポンサーリンク

こんにちは、あろっちです。

ESP32でEthernetライブラリのEthernetServerクラスを使ったスケッチでコンパイルエラーが発生する問題が確認されました。

これを回避するためのEthernetライブラリの修正方法を記述します。

Ethernetモジュールについて

左: W5500 Lite 右: W5500

ESP32をEthernetに繋ぐには画像のようなSPI接続の汎用Ethernetモジュールを用いるのがお手軽でシンプルかと思います。

これらの製品のEthernet ICはいずれもW5500(WIZnet製)で制御プログラムを書くのにEhernetライブラリを使う感じです。

Ethernetライブラリ

https://github.com/WIZnet-ArduinoEthernet/Ethernet

EthernetライブラリはこちらのWIZnet Ethernet HATに対応したWIZnet fork版を使用することにします。

WIZnet Ethernet HAT(Raspberry Pi Pico用)

WIZnet Ethernet HATにRaspberry Pi Picoをセットしたところ

参考サイト:

Ethernetライブラリの修正

本記事の修正方法は他ボード(RP2040ボードなど)に影響が及ばないように配慮しています。
したがって、この修正を入れても他ボードでそのまま使えるかと思います。

ライブラリのソースファイル場所Arduinoスケッチ保存フォルダ内のlibraries/Ethernet/src(※)
修正ファイルEthernet.h
EthernetServer.cpp
※ライブラリ名はEthernetですがGitHubからzipをダウンロードしてインストールした場合、フォルダ名がブランチ名がついた形(Ethernet-master等)になっていることも想定されます。

Ethernet.h

EthernetServerの宣言の箇所

修正前

class EthernetServer : public Server {
private:
	uint16_t _port;
public:
	EthernetServer(uint16_t port) : _port(port) { }
	EthernetClient available();
	EthernetClient accept();
	virtual void begin();

begin関数の箇所を以下のように修正します。

修正後


class EthernetServer : public Server {
private:
	uint16_t _port;
public:
	EthernetServer(uint16_t port) : _port(port) { }
	EthernetClient available();
	EthernetClient accept();
#ifdef ARDUINO_ARCH_ESP32
	virtual void begin(uint16_t port=0);
#else
	virtual void begin();
#endif

EthernetServer.cpp

修正前

void EthernetServer::begin()
{

begin関数の箇所を以下のように修正します。

#ifdef ARDUINO_ARCH_ESP32
void EthernetServer::begin(uint16_t port)
#else
void EthernetServer::begin()
#endif
{

テスト

EthernetServerクラスがESP32以外のボードに影響がないか確認

この修正を実施した後、Raspberry Pi Pico(RP2040)にEthernetServerを使ったサンプルスケッチ(後述)を書いてESP32との対向通信を試してみました。

Raspberry Pi PicoがTCPSever、ESP32がTCPClientになっています

Ethernet同士を直結するためクロスLANケーブルを使っています。

Raspberry Pi Pico(TCPServer)のシリアルモニターを除いてみるとESP32からのパケットを受信できていることが確認できました。

このように上記修正を入れてもRaspberry Pi Pico(RP2040ボード)までEthernetServerクラスに影響が及んでいないことを確認できました。

ESP32とEthernetモジュールの配線は以下の通りです。

ESP32 (VSPI接続)Ethernetモジュール(W5500)
3.3V3.3V
GNDGND
18SCK
19MISO
23MOSI
5CS

TCPServerをM5Stamp C3で動かしてみる

ESP32でもESP32-C3を搭載しているM5Stamp C3で動くかテストしてみました。

M5Stamp C3がTCPServer、Raspberry Pi Pico(WIZnet Ethernet HAT)がTCPClient

Arduino IDEのシリアルモニター結果

問題なく対向通信できました。

M5Stamp C3とEthernetモジュールの配線は以下の通りです。

M5Stamp C3 (SPI接続)Ethernetモジュール(W5500)
3.3V3.3V
GNDGND
4SCK
5MISO
6MOSI
7CS

サンプルプログラム(Arduinoスケッチ)

対向通信用のサンプルプログラムを掲載します。

W5500汎用Ethernetモジュールに対応したESP32向けとWIZnet Ethernet HATに対応したRaspberry Pi Pico向けを掲載します。

対向通信をするためには片側をサーバー、もう一方をクライアントにします。

TCPServer

サーバーのプログラムです。

IPアドレス192.168.1.177/24
デフォルトゲートウェイ192.168.1.1
ポート番号1000

必要であれば適宜変更してください。

ESP32向け

M5Stamp C3もこちらのスケッチを使用できます。

/*
  W5500 Ethernet Module TcpServer example.
*/

#include <SPI.h>
#include <Ethernet.h>

#define SOCKET_PORT    1000             // You have to change
#define MODULE         "W5500"

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network.
// gateway and subnet are optional:
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEE
};
IPAddress ip(192, 168, 1, 177);
IPAddress myDns(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);

EthernetServer server(SOCKET_PORT);

void setup()
{
  // You can use Ethernet.init(pin) to configure the CS pin
  Ethernet.init(SS);  // Most Arduino shields

  // initialize the ethernet device
  Ethernet.begin(mac, ip, myDns, gateway, subnet);

  // Open serial communications and wait for port to open:
  Serial.begin(115200);

  // Check for Ethernet hardware present
  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    while (true) {
      delay(1); // do nothing, no point running without Ethernet hardware
    }
  }

  Serial.println("ok....");
  Serial.print("localIP: ");
  Serial.println(Ethernet.localIP());
  Serial.print("subnetMask: ");
  Serial.println(Ethernet.subnetMask());
  Serial.print("gatewayIP: ");
  Serial.println(Ethernet.gatewayIP());
  Serial.print("dnsServerIP: ");
  Serial.println(Ethernet.dnsServerIP());

  server.begin();
  Serial.println("Server Start");
}

void loop()
{
  size_t size;

  if (EthernetClient client = server.available()) {
    while ((size = client.available()) > 0) {
      Serial.print("[" + String(MODULE) + " Socket Server]Receive size=");
      Serial.println(size);
      uint8_t* msg = (uint8_t*)malloc(size + 1);
      size = client.read(msg, size);
      client.write(msg, size);
      Serial.write(msg, size);

      free(msg);
    }

    client.stop();
  }
}

Raspberry Pi Pico(WIZnet Ethernet HAT)向け

/*
  W5100S Ethernet Module TcpServer example.
*/

#include <SPI.h>
#include <Ethernet.h>

#define SOCKET_PORT    1000             // You have to change
#define MODULE         "W5100S"

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network.
// gateway and subnet are optional:
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEE
};
IPAddress ip(192, 168, 1, 177);
IPAddress myDns(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);

EthernetServer server(SOCKET_PORT);

void setup()
{
  // You can use Ethernet.init(pin) to configure the CS pin
  Ethernet.init(17);  // Most Arduino shields

  // initialize the ethernet device
  Ethernet.begin(mac, ip, myDns, gateway, subnet);

  // Open serial communications and wait for port to open:
  Serial.begin(115200);
 
/* 
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
  }
*/
  
  // Check for Ethernet hardware present
  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    while (true) {
      delay(1); // do nothing, no point running without Ethernet hardware
    }
  }

  Serial.println("ok....");
  Serial.print("localIP: ");
  Serial.println(Ethernet.localIP());
  Serial.print("subnetMask: ");
  Serial.println(Ethernet.subnetMask());
  Serial.print("gatewayIP: ");
  Serial.println(Ethernet.gatewayIP());
  Serial.print("dnsServerIP: ");
  Serial.println(Ethernet.dnsServerIP());

  server.begin();
  Serial.println("Server Start");
}

void loop()
{
  size_t size;

  if (EthernetClient client = server.available()) {
    while ((size = client.available()) > 0) {
      Serial.print("[" + String(MODULE) + " Socket Server]Receive size=");
      Serial.println(size);
      uint8_t* msg = (uint8_t*)malloc(size + 1);
      size = client.read(msg, size);
      client.write(msg, size);
      Serial.write(msg, size);

      free(msg);
    }

    client.stop();
  }
}

TCPClient

クライアントのプログラムです。

IPアドレス192.168.1.178/24
通信先サーバー(TCPServer) IPアドレス192.168.1.177/24
デフォルトゲートウェイ192.168.1.1
ポート番号1000 (※クライアント、サーバー共)

必要であれば適宜変更してください。

ESP32向け

/*
    W5500 Ethernet Module Socket Client example.
*/

#include <SPI.h>
#include <Ethernet.h>

#define INTERVAL       5000
#define SOCKET_HOST    "192.168.1.177" // You have to change
#define SOCKET_PORT    1000             // You have to change
#define TIME_OUT       10000

#define MODULE         "W5500"

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network.
// gateway and subnet are optional:
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF
};
IPAddress ip(192, 168, 1, 178);
IPAddress myDns(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);

EthernetClient client;

unsigned long nextMillis;

void setup() {
  // You can use Ethernet.init(pin) to configure the CS pin
  Ethernet.init(SS);  // Most Arduino shields

  // initialize the ethernet device
  Ethernet.begin(mac, ip, myDns, gateway, subnet);

  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  /*
    while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
    }
  */

  // Check for Ethernet hardware present
  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    while (true) {
      delay(1); // do nothing, no point running without Ethernet hardware
    }
  }

  Serial.println("ok....");
  Serial.print("localIP: ");
  Serial.println(Ethernet.localIP());
  Serial.print("subnetMask: ");
  Serial.println(Ethernet.subnetMask());
  Serial.print("gatewayIP: ");
  Serial.println(Ethernet.gatewayIP());
  Serial.print("dnsServerIP: ");
  Serial.println(Ethernet.dnsServerIP());
  nextMillis = millis();
}

void loop() {
  static int num = 0;
  char smsg[30];
  int rflag;
  unsigned long timeout;
  unsigned long now;

  now = millis();
  if ( (long)(now - nextMillis) > 0) {
    nextMillis = millis() + INTERVAL;
    Serial.print("Client connect....");
    if (!client.connect(SOCKET_HOST, SOCKET_PORT)) {
      Serial.println("failed");
    } else {
      Serial.println("ok");
      sprintf(smsg, "data from arduino %05d", num);
      num++;
      client.write(smsg, strlen(smsg));

      // wait for responce
      rflag = 1;
      timeout = millis();
      while (client.available() == 0) {
        now = millis();
        if ((long)(now - timeout) > TIME_OUT) {
          rflag = 0;
        }
      } // end while

      Serial.print("Server response....");
      if (rflag == 0) {
        Serial.println("failed");
      } else {
        Serial.println("ok");
        int size;
        while ((size = client.available()) > 0) {
          Serial.print("[" + String(MODULE) + " Socket Client]Receive Size=");
          Serial.println(size);
          uint8_t* msg = (uint8_t*)malloc(size + 1);
          memset(msg, 0, size + 1);
          size = client.read(msg, size);
          Serial.write(smsg, size);
          Serial.write("->");
          Serial.write(msg, size);
          Serial.println("");
          free(msg);
        } // end while
      }

      //disconnect client
      Serial.println("Client disconnect");
      client.stop();
    } // end if
  } // end if
}

Raspberry Pi Pico(WIZnet Ethernet HAT)向け

/*
    W5100S Ethernet Module Socket Client example.
*/

#include <SPI.h>
#include <Ethernet.h>

#define INTERVAL       5000
#define SOCKET_HOST    "192.168.1.177" // You have to change
#define SOCKET_PORT    1000             // You have to change
#define TIME_OUT       10000

#define MODULE         "W5100S"

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network.
// gateway and subnet are optional:
byte mac[] = {
  0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xEF
};
IPAddress ip(192, 168, 1, 178);
IPAddress myDns(192, 168, 1, 1);
IPAddress gateway(192, 168, 1, 1);
IPAddress subnet(255, 255, 255, 0);

EthernetClient client;

unsigned long nextMillis;

void setup() {
  // You can use Ethernet.init(pin) to configure the CS pin
  Ethernet.init(17);  // Most Arduino shields

  // initialize the ethernet device
  Ethernet.begin(mac, ip, myDns, gateway, subnet);

  // Open serial communications and wait for port to open:
  Serial.begin(115200);
  /*
    while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB port only
    }
  */

  // Check for Ethernet hardware present
  if (Ethernet.hardwareStatus() == EthernetNoHardware) {
    Serial.println("Ethernet shield was not found.  Sorry, can't run without hardware. :(");
    while (true) {
      delay(1); // do nothing, no point running without Ethernet hardware
    }
  }

  Serial.println("ok....");
  Serial.print("localIP: ");
  Serial.println(Ethernet.localIP());
  Serial.print("subnetMask: ");
  Serial.println(Ethernet.subnetMask());
  Serial.print("gatewayIP: ");
  Serial.println(Ethernet.gatewayIP());
  Serial.print("dnsServerIP: ");
  Serial.println(Ethernet.dnsServerIP());
  nextMillis = millis();
}

void loop() {
  static int num = 0;
  char smsg[30];
  int rflag;
  unsigned long timeout;
  unsigned long now;

  now = millis();
  if ( (long)(now - nextMillis) > 0) {
    nextMillis = millis() + INTERVAL;
    Serial.print("Client connect....");
    if (!client.connect(SOCKET_HOST, SOCKET_PORT)) {
      Serial.println("failed");
    } else {
      Serial.println("ok");
      sprintf(smsg, "data from arduino %05d", num);
      num++;
      client.write(smsg, strlen(smsg));

      // wait for responce
      rflag = 1;
      timeout = millis();
      while (client.available() == 0) {
        now = millis();
        if ((long)(now - timeout) > TIME_OUT) {
          rflag = 0;
        }
      } // end while

      Serial.print("Server response....");
      if (rflag == 0) {
        Serial.println("failed");
      } else {
        Serial.println("ok");
        int size;
        while ((size = client.available()) > 0) {
          Serial.print("[" + String(MODULE) + " Socket Client]Receive Size=");
          Serial.println(size);
          uint8_t* msg = (uint8_t*)malloc(size + 1);
          memset(msg, 0, size + 1);
          size = client.read(msg, size);
          Serial.write(smsg, size);
          Serial.write("->");
          Serial.write(msg, size);
          Serial.println("");
          free(msg);
        } // end while
      }

      //disconnect client
      Serial.println("Client disconnect");
      client.stop();
    } // end if
  } // end if
}

関連記事

当ブログのマイコン記事です。ぜひご覧ください。

コメント

タイトルとURLをコピーしました