こんにちは、あろっちです。
ESP32でEthernetライブラリのEthernetServerクラスを使ったスケッチでコンパイルエラーが発生する問題が確認されました。
これを回避するためのEthernetライブラリの修正方法を記述します。
Ethernetモジュールについて
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用)
参考サイト:
Ethernetライブラリの修正
本記事の修正方法は他ボード(RP2040ボードなど)に影響が及ばないように配慮しています。
したがって、この修正を入れても他ボードでそのまま使えるかと思います。
ライブラリのソースファイル場所 | Arduinoスケッチ保存フォルダ内のlibraries/Ethernet/src(※) |
修正ファイル | Ethernet.h EthernetServer.cpp |
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との対向通信を試してみました。
Ethernet同士を直結するためクロスLANケーブルを使っています。
Raspberry Pi Pico(TCPServer)のシリアルモニターを除いてみるとESP32からのパケットを受信できていることが確認できました。
このように上記修正を入れてもRaspberry Pi Pico(RP2040ボード)までEthernetServerクラスに影響が及んでいないことを確認できました。
ESP32とEthernetモジュールの配線は以下の通りです。
ESP32 (VSPI接続) | Ethernetモジュール(W5500) |
---|---|
3.3V | 3.3V |
GND | GND |
18 | SCK |
19 | MISO |
23 | MOSI |
5 | CS |
TCPServerをM5Stamp C3で動かしてみる
ESP32でもESP32-C3を搭載しているM5Stamp C3で動くかテストしてみました。
Arduino IDEのシリアルモニター結果
問題なく対向通信できました。
M5Stamp C3とEthernetモジュールの配線は以下の通りです。
M5Stamp C3 (SPI接続) | Ethernetモジュール(W5500) |
---|---|
3.3V | 3.3V |
GND | GND |
4 | SCK |
5 | MISO |
6 | MOSI |
7 | CS |
サンプルプログラム(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
}
関連記事
当ブログのマイコン記事です。ぜひご覧ください。
コメント