ESP32 实战教程:接入 LAN8720 以太网并做有线 Web 配置

Wi-Fi 很方便,但工业现场和固定设备常常更喜欢网线:延迟稳定、抗干扰能力强,设备地址也更容易规划。本文用 ESP32 加 LAN8720,做一个有线网络节点,并把启动过程、RMII 时钟和常见的“灯不亮、IP 不来”问题讲清楚。

ESP32 + LAN8720 有线网络架构

一、先把 PHY 和 MAC 分清楚

以太网链路里,LAN8720 是 PHY 芯片,负责把数字数据转换成网线上的电气信号;ESP32 内部的 Ethernet MAC 负责帧收发、地址过滤和与 TCP/IP 协议栈衔接。两者之间通过 RMII(Reduced Media Independent Interface)连接,常见信号包括 TX、RX、TX_EN、CRS_DV、MDC、MDIO 和 50 MHz 参考时钟。

很多初学者把 LAN8720 当成“USB 网卡”,只要插上就应该自动有 IP。实际情况是:PHY 地址、MDC/MDIO 引脚、时钟来源、供电控制脚都可能因模块而不同。以太网项目第一件要做的事不是写 WebServer,而是找到模块原理图,确认这些参数。

常见的 ESP32 + LAN8720 模块会从 GPIO0 输入 50 MHz 时钟,也有模块由 ESP32 输出时钟;不同方案不能互换。下面这段配置以官方示例中的经典 ESP32 RMII 组合为参考,真正接线时必须以开发板文档为准。

二、硬件和接线检查表

建议准备 ESP32 WROOM 开发板、LAN8720 模块、RJ45 带网络变压器接口的网线模块,以及一根可靠的网线。检查点如下:

  1. PHY 地址是否为 0 或 1,模块上的跳线/电阻有没有改变它;
  2. MDC、MDIO 是否和代码中的 GPIO 一致;
  3. 50 MHz 时钟是 GPIO0 输入,还是由某个 GPIO 输出;
  4. PHY 的复位脚、电源脚是否需要 ESP32 控制;
  5. ESP32 与 PHY 必须共地,3.3 V 电源要能承受网口初始化时的电流变化。

不要把 RMII 信号随意换到“看起来空闲”的 GPIO。经典 ESP32 的以太网外设对部分引脚和时钟路径有硬件约束,换线之前先看芯片和板卡资料。

三、官方 Arduino-ESP32 初始化方式

Arduino-ESP32 当前的 Ethernet 示例要求在包含 ETH.h 前定义 PHY 宏。这样做的好处是库可以根据目标芯片和配置选择正确的默认初始化路径。

#include <Arduino.h>

#define ETH_PHY_TYPE  ETH_PHY_LAN8720
#define ETH_PHY_ADDR  0
#define ETH_PHY_MDC   23
#define ETH_PHY_MDIO  18
#define ETH_PHY_POWER -1
#define ETH_CLK_MODE  ETH_CLOCK_GPIO0_IN

#include <ETH.h>
#include <Network.h>

volatile bool ethReady = false;

void onNetworkEvent(arduino_event_id_t event) {
  switch (event) {
    case ARDUINO_EVENT_ETH_START:
      Serial.println("ETH started");
      ETH.setHostname("esp32-lan8720");
      break;
    case ARDUINO_EVENT_ETH_CONNECTED:
      Serial.println("ETH link up");
      break;
    case ARDUINO_EVENT_ETH_GOT_IP:
      ethReady = true;
      Serial.print("IP: ");
      Serial.println(ETH.localIP());
      break;
    case ARDUINO_EVENT_ETH_LOST_IP:
    case ARDUINO_EVENT_ETH_DISCONNECTED:
    case ARDUINO_EVENT_ETH_STOP:
      ethReady = false;
      Serial.println("ETH unavailable");
      break;
    default:
      break;
  }
}

void setup() {
  Serial.begin(115200);
  Network.begin();
  Network.onEvent(onNetworkEvent);
  ETH.begin(ETH_PHY_TYPE, ETH_PHY_ADDR, ETH_PHY_MDC,
            ETH_PHY_MDIO, ETH_PHY_POWER, ETH_CLK_MODE);
}

void loop() {
  delay(1000);
}

这里有一个很重要的工程细节:网络事件回调可能运行在另一个任务里,不要在回调中做很重的文件操作或长时间等待。回调里只更新状态,真正启动 Web 服务、读取配置、刷新 UI 放到主循环或独立任务中,问题会少很多。

四、启动 Web 配置页面

以太网和 Wi-Fi 在 Arduino Core 3.x 中共享很多网络类,WebServer、NetworkClient 的使用方式基本一致。最小的页面可以先显示 IP、链路状态和设备版本,后面再加传感器配置。

#include <WebServer.h>

WebServer server(80);

void handleRoot() {
  String html = "<meta charset='utf-8'><h1>ESP32 有线节点</h1>";
  html += "<p>IP: " + ETH.localIP().toString() + "</p>";
  html += "<p>链路: " + String(ETH.linkUp() ? "UP" : "DOWN") + "</p>";
  html += "<form action='/config' method='post'>";
  html += "采样周期(ms):<input name='period' value='1000'>";
  html += "<button>保存</button></form>";
  server.send(200, "text/html; charset=utf-8", html);
}

void handleConfig() {
  if (!server.hasArg("period")) {
    server.send(400, "text/plain; charset=utf-8", "missing period");
    return;
  }
  int period = server.arg("period").toInt();
  if (period < 100 || period > 60000) {
    server.send(400, "text/plain; charset=utf-8", "周期必须在 100~60000 ms");
    return;
  }
  // 这里可以调用 Preferences 保存配置
  server.send(200, "text/plain; charset=utf-8", "配置已保存");
}

void startWeb() {
  server.on("/", HTTP_GET, handleRoot);
  server.on("/config", HTTP_POST, handleConfig);
  server.begin();
}

只有在 ARDUINO_EVENT_ETH_GOT_IP 之后再打开依赖局域网访问的页面,会更容易向用户说明“设备已经准备好”。如果设备需要固定 IP,可以使用 ETH.config(local_ip, gateway, subnet, dns1, dns2),但要注意局域网网段和网关必须匹配,不能随手填一个看起来很专业的地址。

五、启动流程和排错顺序

LAN8720 启动与故障定位顺序

推荐顺序是:先看 PHY 电源和 Link 灯,再看串口里是否出现 ETH_START,然后确认是否收到 ETH_GOT_IP。不要一上来就调 Web 页面,因为没有 IP 时,浏览器报错是必然结果。

优先检查网线、RJ45 磁性器件、PHY 电源和 50 MHz 时钟。若 PHY 地址写错,MAC 可能无法读到 PHY 状态;若时钟模式写反,初始化也会失败。用示波器测时钟,比盯着串口猜测有效得多。

这通常说明物理层已经工作,问题转移到了 DHCP、网段或路由器端口。可以先用静态 IP 验证 TCP/IP 路径,再回头查 DHCP。确认路由器没有把该端口放到隔离 VLAN,也不要忘记 Network.begin() 与事件注册。

页面偶尔打不开

检查事件回调是否误把 ethReady 置回 false;避免在 loop() 里每次都重复 server.begin();处理请求时不要长时间阻塞,例如在网页请求里同步读取传感器 10 秒。以太网本身很稳定,但阻塞代码一样能把它写成“看起来不稳定”。

六、什么时候应该选有线而不是 Wi-Fi

固定安装、供电充足、设备数量少但可靠性要求高的场景,很适合 LAN8720:工业柜、机房环境监测、门禁旁的状态采集、家庭弱电箱。移动设备、临时传感器、需要电池供电的节点则更适合 Wi-Fi 或 BLE。

有线并不意味着天然安全。Web 配置页仍然要限制修改权限,至少给危险操作加口令,并对输入做范围校验。设备接在办公网里时,默认监听端口相当于把一个小服务器放进了局域网,不能因为“只有内网”就完全不设防。

七、延伸实验

可以把本项目继续拓展为:以太网优先、Wi-Fi 备用的双接口节点;使用 mDNS 让用户访问 esp32-lan8720.local;通过 WebSocket 实时推送传感器曲线;给每个设备生成唯一序列号并在 DHCP 租约表里固定地址;或者使用 MQTT 把有线节点接到现有的 Home Assistant。

本次最值得记住的不是某一组 GPIO,而是排错方法:物理层、链路层、IP 层、应用层按顺序验证。层次一旦理顺,以太网就不再是“插根网线祈祷一下”,而是一套可以观察、测量和复现的系统。

参考资料

打赏

如果这篇学习记录对你有帮助,欢迎扫码支持后续整理。

打赏码

打赏支持:王庆岩

posted on 2026-09-29 09:31  潇洒石头  阅读(34)  评论(0)    收藏  举报

导航