ESPはIOTゲートウェイにふさわしいマイコン
趣味の範囲でsmart home用のiotゲートウェイ基板を作っています。無線LANで各センサからデータを収集して、有線LANでPCに送信するという画策をしています。有線無線でethrnet通信できる基板を趣味の範囲で作るには、低予算で入手性も良いものは、今のところESP32の初代しかありません。S3からはmacが削除されてしまいました。
有線LANモジュールを試す
PHYの定番チップで入手性良く、安いものにLAN8720があります。8720はRMIIのみ対応しています。今回は、amazonで以下のモジュールを購入しました。500円ほどでした。

gitで公開しています
ESPで使うにはちょっとクセがある
AN8720のRMIIは50MHzの基準クロック(REF_CLK)が必要です。
ESP32側は、ピンのペリフェラルが決まっており、GPIO0 がRMIIクロック(EMAC_TX_CLK/REF_CLK)になります。
ところが GPIO0はブートストラップピンも兼ねており、起動時にここがクロックで暴れると、書き込みモードに入ったりして起動が不安定になります
そこでモジュールの改造が必要になります。改造方法は、50MHz振動子の1pin(クロックイネーブルで改造前N.C)をモジュールのNCに接続して、ESP32から50MHzクロックを操作してやります。これで起動時は、クロックを出さず、起動後の安定したタイミングでクロックを出すことで、ブートストラップに干渉する問題を防ぐことができます。
詳しい改造方法は、先駆者の記事に書かれています。GP0(ブートストラップ/CLK)は3.6kでプルアップ、GP17(クロックイネーブル)はプルダウンします。
ピンアサイン
ピンアサインは以下の通りです
| 信号 | GPIO |
|---|---|
| MDC | 23 |
| MDIO | 18 |
| TXD0 | 19 |
| TXD1 | 22 |
| TX_EN | 21 |
| RXD0 | 25 |
| RXD1 | 26 |
| CRS_DV | 27 |
| REF_CLK | 0 |
| OSC_EN | 17 |
API紹介
gitで公開しています
以下が、RMIIのAPIの説明になります
UDP通信を使っています
start_enet_rmii_lan8720_task()
Ethernetタスクを起動
enet_rmii_task()
メイン処理
- 発振器ON
- netif初期化
- Ethernet初期化
- netif接続
- IP設定
- Ethernet開始
- 監視ループ
enable_oscillator()
外部50MHz発振器を有効化
GPIO17制御:
LOW → 待機 → HIGH → 安定待ち
eth_init_lan8720()
Ethernet初期化
- MAC生成(esp32内蔵)
- PHY生成(LAN8720)
- RMII設定
- ドライバインストール
- MACアドレス設定
eth_event_handler()
Ethernetイベント処理
- LINK UP
- LINK DOWN
- START
- STOP
got_ip_event_handler()
IP取得イベント
IP / MASK / GW をログ出力
enet_set_ip(host_id)
IP変更
ネットワーク初期化
esp_netif_init()
esp_event_loop_create_default()
netif接続
esp_netif_new()
esp_eth_new_netif_glue()
esp_netif_attach()
DHCP停止
esp_netif_dhcpc_stop()
Ethernet開始
esp_eth_start()
状態取得
esp_netif_is_netif_up()
esp_netif_get_ip_info()
UDPソケット生成
int sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
送信先アドレス設定
struct sockaddr_in dest = {
.sin_family = AF_INET,
.sin_port = htons(54321),
.sin_addr.s_addr = inet_addr("192.168.1.10"),
};
UDPデータ送信
const char *msg = "ESP32-ETH-TX-TEST";
int tx_ret = sendto(sock,
msg,
strlen(msg),
0,
(struct sockaddr *)&dest,
sizeof(dest));
エラーチェック
if (tx_ret < 0) {
// 送信失敗
} else {
// 送信成功(バイト数返却)
}
送信周期
vTaskDelay(pdMS_TO_TICKS(5000));で調整