Chapter 76: Ethernet Interface on ESP32 Variants
Chapter Objectives
Upon completing this chapter, you will be able to:
- Understand the fundamental concepts of Ethernet, including MAC and PHY layers.
- Identify the advantages of using wired Ethernet connectivity for embedded systems.
- Differentiate between the internal Ethernet MAC capabilities of various ESP32 series microcontrollers.
- Configure and initialize the ESP32 (classic) Ethernet interface using the ESP-IDF v5.x framework.
- Handle Ethernet and IP events to monitor network status.
- Implement a basic Ethernet application to obtain an IP address via DHCP.
- Troubleshoot common issues encountered during Ethernet development.
Introduction
While Wi-Fi offers unparalleled flexibility and mobility for IoT devices, there are many scenarios where wired connectivity is not just preferable, but essential. Industrial automation, critical infrastructure monitoring, smart building systems, and high-bandwidth data logging often demand the reliability, security, and consistent performance that only a wired Ethernet connection can provide. Ethernet eliminates concerns about wireless interference, signal strength fluctuations, and complex security configurations, offering a robust and predictable communication channel.
The ESP32 series of microcontrollers, renowned for their versatility, also offers capabilities for wired Ethernet integration. This chapter will delve into setting up and utilizing the Ethernet interface on various ESP32 variants, focusing on the ESP32’s built-in Ethernet Media Access Controller (EMAC) and discussing solutions for variants that lack this dedicated hardware. We will explore the underlying theory, practical implementation steps using ESP-IDF v5.x, and best practices for robust wired networking applications.
Theory
1. What is Ethernet?
Ethernet is a family of computer networking technologies commonly used in local area networks (LANs) and metropolitan area networks (MANs). It was standardized by the IEEE 802.3 group. At its core, Ethernet defines the physical and data link layers of the network, specifying how data is transmitted over a wired medium (typically twisted-pair copper cables) and how devices access that medium. It’s known for its reliability, speed, and widespread adoption.
%%{init: {"theme": "base", "themeVariables": {"primaryTextColor": "#333333", "lineColor": "#777777"}}}%% graph TD; subgraph "Local Area Network (LAN)" ESP32_Device["<b>ESP32 Device</b><br>(Wired Ethernet)"] Router["<b>Router / Switch</b><br>(Network Hub)"] PC["<b>Computer</b><br>(LAN Client)"] Server["<b>Local Server</b><br>(e.g., NAS, Web Server)"] Internet["<b>Internet</b><br>(via Router's WAN Port)"] ESP32_Device ---|"Ethernet Cable"| Router; PC ---|"Ethernet Cable"| Router; Server ---|"Ethernet Cable"| Router; Router ---|"WAN Connection"| Internet; end style ESP32_Device fill:#DBEAFE,stroke:#2563EB,stroke-width:2px,color:#1E40AF style Router fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E style PC fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF style Server fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF style Internet fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46
2. Why Use Ethernet on ESP32?
While Wi-Fi is convenient, Ethernet offers distinct advantages for specific applications:
Feature | Description | Benefit for ESP32 Applications |
---|---|---|
Reliability | Wired connections are less susceptible to interference, signal degradation, and congestion. | Ideal for critical applications (industrial control, monitoring) where stable communication is paramount. |
Security | Requires physical access to the network cable, offering inherent security over remote-access wireless. | Enhanced protection for sensitive data and critical infrastructure deployments. |
Bandwidth & Latency | Often provides higher sustained data rates and lower, more predictable latency than Wi-Fi. | Suitable for real-time control, video streaming, or high-volume data logging. |
Power over Ethernet (PoE) | Allows a single cable to provide both data and power to the device. | Simplifies deployment, reduces cabling complexity, and allows placement in areas without dedicated power outlets. |
Industrial Environments | Widely adopted in industrial settings (e.g., EtherCAT, PROFINET) for its robustness and deterministic behavior. | Enables ESP32 integration into existing industrial networks and automation systems. |
3. Ethernet MAC and PHY
An Ethernet interface fundamentally consists of two main components:
- Media Access Control (MAC) Layer: This layer operates at the Data Link Layer (Layer 2) of the OSI model. Its primary responsibilities include:
- Framing: Encapsulating data into Ethernet frames for transmission and decapsulating incoming frames.
- Addressing: Managing unique 48-bit MAC addresses for devices on the network.
- Error Detection: Using Cyclic Redundancy Checks (CRCs) to detect transmission errors.
- Media Access: Controlling how devices share the physical medium (e.g., Carrier Sense Multiple Access with Collision Detection – CSMA/CD, though less relevant in modern switched networks).
- In the context of ESP32, the EMAC (Ethernet Media Access Controller) is a dedicated hardware peripheral inside certain ESP32 chips that implements the MAC layer.
- Physical Layer Transceiver (PHY) Layer: This layer operates at the Physical Layer (Layer 1) of the OSI model. Its responsibilities include:
- Signal Conversion: Converting digital data from the MAC into analog electrical signals suitable for transmission over the Ethernet cable, and vice-versa.
- Line Coding: Encoding and decoding data for transmission.
- Link Management: Detecting link status (e.g., cable connected, link speed, duplex mode).
- The PHY is typically an external chip (e.g., Microchip LAN8720, Realtek RTL8201, Davicom DM9051) that connects to the ESP32’s EMAC.
4. RMII vs. MII Interface
The communication between the MAC and PHY layers occurs via a standardized interface. The two most common are:
- MII (Media Independent Interface): This is the original interface, requiring 16 pins for data and control signals (4-bit transmit data, 4-bit receive data, and various control/clock signals).
- RMII (Reduced Media Independent Interface): A more compact version of MII, requiring only 9 pins. It achieves this reduction by using a shared clock for transmit and receive paths and a 2-bit data path (instead of 4-bit). RMII is commonly used with microcontrollers like the ESP32 due to its lower pin count, making board design simpler.
Feature | MII (Media Independent Interface) | RMII (Reduced Media Independent Interface) |
---|---|---|
Pin Count | 16 pins (for data and control) | 9 pins (for data and control) |
Data Path Width | 4-bit (transmit and receive) | 2-bit (transmit and receive) |
Clocking | Separate transmit and receive clocks (25 MHz for 100 Mbps) | Single shared clock (50 MHz reference clock) |
Complexity | Higher pin count, potentially more complex PCB routing. | Lower pin count, simpler PCB routing. |
Common Use | Older or higher pin-count devices. | Microcontrollers like ESP32, where pin economy is crucial. Preferred for ESP32. |
For ESP32, RMII is the preferred interface for connecting to external PHYs.
5. ESP32’s Internal MAC
The original ESP32 (classic) series (e.g., ESP32-WROOM-32, ESP32-DevKitC) features a dedicated Ethernet Media Access Controller (EMAC) peripheral. This internal MAC supports both MII and RMII modes, though RMII is typically used due to pin constraints. It requires an external PHY chip to complete the Ethernet interface.
Important Note for Other Variants:
- ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6, ESP32-H2: These newer ESP32 series variants do not have an internal EMAC peripheral. To achieve wired Ethernet connectivity with these chips, an external Ethernet controller chip that communicates via SPI or SDIO is required (e.g., W5500, ENC28J60). The ESP-IDF provides specific drivers for these external SPI/SDIO Ethernet modules. This chapter will primarily focus on the ESP32 (classic) with its internal EMAC, but will briefly touch upon the approach for other variants.
6. ESP-IDF Ethernet Driver Architecture
The ESP-IDF provides a robust and flexible Ethernet driver component (esp_eth
) that abstracts the complexities of the MAC and PHY layers.
%%{init: {"theme": "base", "themeVariables": {"lineColor": "#2563EB", "textColor": "#1F2937"}}}%% graph TD; App["<b>Application Layer</b><br>(e.g., HTTP Client, MQTT, Custom Protocols)"] --> LwIP; LwIP["<b>LwIP TCP/IP Stack</b><br>(IP, TCP, UDP, DHCP, DNS)"] --> NetIF; NetIF["<b>esp_netif Abstraction Layer</b><br>(Network Interface Management, Event Propagation)"] --> ESP_ETH; subgraph "esp_eth Component" direction LR ESP_ETH_Core["<b>esp_eth Core</b><br>(Common Ethernet API)"] MAC_Driver["<b>MAC Driver</b><br>(e.g., esp_eth_mac_new_esp32(),<br>esp_eth_mac_new_w5500())"] PHY_Driver["<b>PHY Driver</b><br>(e.g., esp_eth_phy_new_lan8720(),<br>esp_eth_phy_new_ip101())"] ESP_ETH_Core --> MAC_Driver; ESP_ETH_Core --> PHY_Driver; end ESP_ETH --> Hardware; subgraph "Hardware Layer" direction LR EMAC_Internal["<b>ESP32 Internal EMAC</b><br>(for ESP32 classic)"] Ext_PHY["<b>External PHY Chip</b><br>(e.g., LAN8720)"] Ext_Controller["<b>External Ethernet Controller</b><br>(e.g., W5500 via SPI - for S2/S3/C3 etc.)"] EMAC_Internal -.-> Ext_PHY; end MAC_Driver --> EMAC_Internal; MAC_Driver -.-> Ext_Controller; PHY_Driver --> Ext_PHY; classDef appLayer fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF; classDef netStack fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF; classDef netifLayer fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF; classDef espEthComp fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E; classDef hardwareLayer fill:#FEE2E2,stroke:#DC2626,stroke-width:1px,color:#991B1B; class App appLayer; class LwIP netStack; class NetIF netifLayer; class ESP_ETH_Core espEthComp; class MAC_Driver espEthComp; class PHY_Driver espEthComp; class Hardware hardwareLayer; class EMAC_Internal hardwareLayer; class Ext_PHY hardwareLayer; class Ext_Controller hardwareLayer;
Key components and their roles:
- LwIP (Lightweight IP): This is the open-source TCP/IP stack integrated into ESP-IDF. It handles network protocols like IP, TCP, UDP, DHCP, DNS, etc.
esp_netif
: This component provides a network interface abstraction layer. It acts as a bridge between the low-level network drivers (likeesp_eth
) and the LwIP stack. It manages network interface creation, IP address assignment, and event propagation.esp_eth
: This is the core Ethernet driver component. It provides:- MAC Drivers: Implementations for different MAC hardware (e.g.,
esp_eth_mac_new_esp32()
for the internal EMAC,esp_eth_mac_new_w5500()
for the W5500 SPI Ethernet controller). - PHY Drivers: Implementations for various external PHY chips (e.g.,
esp_eth_phy_new_lan8720()
,esp_eth_phy_new_ip101()
). - Common Ethernet API: A unified API for initializing, starting, stopping, and managing Ethernet devices, regardless of the underlying MAC/PHY combination.
- MAC Drivers: Implementations for different MAC hardware (e.g.,
7. Initialization Flow
Setting up the Ethernet interface in ESP-IDF v5.x typically follows these steps:
- Initialize TCP/IP Stack: Call
esp_netif_init()
to initialize the TCP/IP stack. This should be done once per application. - Create Default Event Loop: Call
esp_event_loop_create_default()
to create the default event loop. This loop is crucial for handling system events, including network events. - Create Ethernet NetIF: Use
esp_netif_new()
withesp_netif_eth_create_default_config()
to create a default Ethernet network interface. This sets up theesp_netif
instance for Ethernet. - Configure MAC: Create a new MAC object using the appropriate function for your hardware. For ESP32 (classic), this is
esp_eth_mac_new_esp32()
, providing configuration for GPIO pins, clock, and other MAC-specific settings. - Configure PHY: Create a new PHY object using the function corresponding to your external PHY chip (e.g.,
esp_eth_phy_new_lan8720()
). - Install Ethernet Driver: Call
esp_eth_driver_install()
to combine the MAC and PHY objects into a complete Ethernet driver instance. - Attach NetIF to Driver: Use
esp_netif_attach()
to link theesp_netif
instance with the installed Ethernet driver. This bridges the LwIP stack to the hardware driver. - Register Event Handlers: Register callback functions to handle various Ethernet and IP events. These events inform your application about changes in network status (e.g., link up/down, IP address obtained).
- Start Ethernet: Call
esp_eth_start()
to bring up the Ethernet interface and begin the negotiation process with the network.
%%{init: {"theme": "base", "themeVariables": {"lineColor": "#2563EB", "textColor": "#1F2937"}}}%% graph TD A[<b>Start: Initialize Ethernet</b>] --> B["esp_netif_init()<br>Initialize TCP/IP Stack"]; B --> C["esp_event_loop_create_default()<br>Create Default Event Loop"]; C --> D["esp_netif_new() with<br>ESP_NETIF_DEFAULT_ETH()<br>Create Ethernet NetIF Object"]; D --> E["MAC Configuration<br>eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();<br>esp_eth_mac_new_esp32(&mac_config) (for classic ESP32)<br>OR esp_eth_mac_new_w5500() (for SPI ETH)"]; E --> F["PHY Configuration<br>eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();<br>esp_eth_phy_new_lan8720(&phy_config) (or other PHY)"]; F --> G["esp_eth_driver_install(ð_config, ð_handle)<br>Install Ethernet Driver (MAC + PHY)"]; G --> H["esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle))<br>Attach NetIF to Driver"]; H --> I["Register Event Handlers<br>ETH_EVENT & IP_EVENT"]; I --> J["esp_eth_start(eth_handle)<br>Start Ethernet Interface"]; J --> K[<b>Ethernet Initialized & Started</b><br>Waiting for Link Up & IP Address]; classDef startEnd fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6; classDef process fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF; classDef decision fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E; class A,K startEnd; class B,C,D,G,H,I,J process; class E,F decision;
8. Event Handling
The ESP-IDF event loop system is central to handling asynchronous network events. Your application should register handlers for the following key events:
Event Constant | Trigger Condition | Typical Use / Action |
---|---|---|
ETH_EVENT_START |
Ethernet driver successfully started via esp_eth_start() . |
Log status, initialize application-specific network tasks. |
ETH_EVENT_STOP |
Ethernet driver stopped via esp_eth_stop() . |
Log status, clean up network resources. |
ETH_EVENT_CONNECTED |
Ethernet PHY detects a valid link (cable connected, link partner active). | Log link status, update UI, attempt to get MAC address, enable network operations. |
ETH_EVENT_DISCONNECTED |
Ethernet PHY detects link down (cable unplugged, link partner inactive). | Log link status, update UI, disable network operations, attempt reconnection if applicable. |
IP_EVENT_ETH_GOT_IP |
Ethernet interface successfully obtains an IP address (via DHCP or static configuration). | Log IP details, start main application network communication (e.g., HTTP client, MQTT, server). This is a crucial event. |
9. IP Addressing (DHCP, Static)
- DHCP (Dynamic Host Configuration Protocol): This is the most common method for assigning IP addresses. The ESP32 acts as a DHCP client, requesting an IP address, subnet mask, gateway, and DNS server from a DHCP server on the network. This is the default behavior for
esp_netif_eth_create_default_config()
. - Static IP: For fixed deployments or networks without a DHCP server, you can configure a static IP address, subnet mask, and gateway directly in your application. This is done by modifying the
esp_netif_config_t
structure before creating theesp_netif
instance.
%%{init: {"theme": "base", "themeVariables": {"lineColor": "#2563EB", "textColor": "#1F2937"}}}%% graph TD Start[("Start IP Configuration")] --> Method{Choose IP Addressing Method}; Method --"DHCP (Default)"--> DHCP_Process; subgraph DHCP_Process ["DHCP Client Process"] direction LR D1["ESP32 Sends<br>DHCP Discover"] --> D2["DHCP Server Responds<br>DHCP Offer"]; D2 --> D3["ESP32 Sends<br>DHCP Request"]; D3 --> D4["DHCP Server Responds<br>DHCP ACK + IP Details"]; D4 --> IP_Obtained_DHCP[("IP Address Obtained via DHCP")]; end Method --"Static IP"--> Static_Process; subgraph Static_Process ["Static IP Configuration"] direction LR S1["Define Static IP, Netmask, Gateway<br>in esp_netif_ip_info_t"] --> S2["Stop DHCP Client (if running)<br>esp_netif_dhcpc_stop()"]; S2 --> S3["Set Static IP Info<br>esp_netif_set_ip_info()"]; S3 --> IP_Set_Static[("Static IP Address Set")]; end IP_Obtained_DHCP --> End_IP["Network Ready with IP"]; IP_Set_Static --> End_IP; classDef startEnd fill:#EDE9FE,stroke:#5B21B6,stroke-width:2px,color:#5B21B6; classDef decision fill:#FEF3C7,stroke:#D97706,stroke-width:1px,color:#92400E; classDef process fill:#DBEAFE,stroke:#2563EB,stroke-width:1px,color:#1E40AF; classDef success fill:#D1FAE5,stroke:#059669,stroke-width:2px,color:#065F46; class Start success; class End_IP success; class IP_Obtained_DHCP success; class IP_Set_Static success; class Method decision; class D1,D2,D3,D4,S1,S2,S3 process;
Practical Examples
This example demonstrates how to initialize the Ethernet interface on an ESP32 (classic) board (e.g., ESP32-DevKitC with an external LAN8720 PHY module) and obtain an IP address via DHCP.
1. Project Setup
Create a new ESP-IDF project using VS Code. You can use the “ESP-IDF: New Project” command. Name it ethernet_example
.
2. main/ethernet_example_main.c
#include <stdio.h>
#include <string.h>
#include "sdkconfig.h"
#include "esp_event.h"
#include "esp_netif.h"
#include "esp_eth.h"
#include "esp_log.h"
#include "driver/gpio.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
// Include specific PHY drivers based on your hardware
#if CONFIG_ETH_PHY_LAN8720
#include "eth_phy_lan8720.h"
#elif CONFIG_ETH_PHY_IP101
#include "eth_phy_ip101.h"
#elif CONFIG_ETH_PHY_RTL8201
#include "eth_phy_rtl8201.h"
#endif
static const char *TAG = "ETHERNET_EXAMPLE";
/**
* @brief Ethernet event handler
* This function is called when an Ethernet event occurs.
*
* @param arg User data (not used in this example)
* @param event_base The event base (e.g., ETH_EVENT)
* @param event_id The specific event ID
* @param event_data Data associated with the event
*/
static void eth_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
uint8_t mac_addr[6] = {0};
/* we can get the event base details from event_data if needed */
switch (event_id) {
case ETHERNET_EVENT_START:
ESP_LOGI(TAG, "Ethernet Started");
break;
case ETHERNET_EVENT_STOP:
ESP_LOGI(TAG, "Ethernet Stopped");
break;
case ETHERNET_EVENT_CONNECTED:
ESP_LOGI(TAG, "Ethernet Linked Up");
esp_eth_ioctl(esp_eth_get_handle_from_netif_impl((esp_netif_t *)event_data), ETH_CMD_G_MAC_ADDR, mac_addr);
ESP_LOGI(TAG, "Ethernet MAC Address: %02x:%02x:%02x:%02x:%02x:%02x",
mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]);
break;
case ETHERNET_EVENT_DISCONNECTED:
ESP_LOGI(TAG, "Ethernet Linked Down");
break;
default:
break;
}
}
/**
* @brief IP event handler
* This function is called when an IP event occurs.
*
* @param arg User data (not used in this example)
* @param event_base The event base (e.g., IP_EVENT)
* @param event_id The specific event ID
* @param event_data Data associated with the event
*/
static void ip_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data)
{
ip_event_got_ip_t *event = (ip_event_got_ip_t *)event_data;
const esp_netif_ip_info_t *ip_info = &event->ip_info;
switch (event_id) {
case IP_EVENT_ETH_GOT_IP:
ESP_LOGI(TAG, "Ethernet Got IP Address");
ESP_LOGI(TAG, "~~~~~~~~~~~");
ESP_LOGI(TAG, "ETH IP:" IPSTR, IP2STR(&ip_info->ip));
ESP_LOGI(TAG, "ETH Netmask:" IPSTR, IP2STR(&ip_info->netmask));
ESP_LOGI(TAG, "ETH Gateway:" IPSTR, IP2STR(&ip_info->gw));
ESP_LOGI(TAG, "~~~~~~~~~~~");
break;
default:
break;
}
}
void app_main(void)
{
// 1. Initialize TCP/IP network interface (esp_netif)
ESP_ERROR_CHECK(esp_netif_init());
// 2. Create default event loop
ESP_ERROR_CHECK(esp_event_loop_create_default());
// 3. Create default Ethernet network interface (esp_netif)
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
esp_netif_t *eth_netif = esp_netif_new(&netif_cfg);
// 4. Configure Ethernet MAC and PHY
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
// Set the GPIO for the Ethernet PHY's power pin (if applicable)
// For LAN8720, this is often GPIO16 or GPIO17, check your board's schematic
// For example, if using ESP32-Ethernet-Kit, it's GPIO17
phy_config.phy_addr = CONFIG_ETH_PHY_ADDR; // PHY address, typically 0 or 1
phy_config.reset_gpio_num = CONFIG_ETH_PHY_RST_GPIO; // GPIO for PHY reset, e.g., 5 or 17
// Initialize the internal ESP32 EMAC
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&mac_config);
// Initialize the external PHY driver
esp_eth_phy_t *phy = NULL;
#if CONFIG_ETH_PHY_LAN8720
phy = esp_eth_phy_new_lan8720(&phy_config);
#elif CONFIG_ETH_PHY_IP101
phy = esp_eth_phy_new_ip101(&phy_config);
#elif CONFIG_ETH_PHY_RTL8201
phy = esp_eth_phy_new_rtl8201(&phy_config);
#else
ESP_LOGE(TAG, "No Ethernet PHY selected in menuconfig!");
return;
#endif
// 5. Install Ethernet driver
esp_eth_handle_t eth_handle = NULL;
esp_eth_config_t eth_config = {
.mac = mac,
.phy = phy,
.smi_mdc_gpio_num = CONFIG_ETH_MDC_GPIO, // MDC GPIO, e.g., 23
.smi_mdio_gpio_num = CONFIG_ETH_MDIO_GPIO, // MDIO GPIO, e.g., 18
};
ESP_ERROR_CHECK(esp_eth_driver_install(ð_config, ð_handle));
// 6. Attach Ethernet driver to TCP/IP stack
ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)));
// 7. Register event handlers
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, NULL));
ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &ip_event_handler, NULL));
// 8. Start Ethernet driver
ESP_ERROR_CHECK(esp_eth_start(eth_handle));
// Application can continue running other tasks
ESP_LOGI(TAG, "Ethernet initialization complete. Waiting for IP...");
}
3. main/CMakeLists.txt
The default CMakeLists.txt
should be sufficient. Ensure idf_component_register
is present.
# In the main/CMakeLists.txt file
idf_component_register(SRCS "ethernet_example_main.c"
INCLUDE_DIRS ".")
4. main/Kconfig.projbuild
This file allows you to configure project-specific settings via idf.py menuconfig.
Create main/Kconfig.projbuild and add the following:
menu "Ethernet Configuration"
config ETH_PHY_LAN8720
bool "Use LAN8720 PHY"
default y
help
Select this if you are using a LAN8720 Ethernet PHY.
config ETH_PHY_IP101
bool "Use IP101 PHY"
default n
help
Select this if you are using an IP101 Ethernet PHY.
config ETH_PHY_RTL8201
bool "Use RTL8201 PHY"
default n
help
Select this if you are using an RTL8201 Ethernet PHY.
config ETH_PHY_ADDR
int "Ethernet PHY Address"
default 0
range 0 31
help
The address of the Ethernet PHY chip. Typically 0 or 1.
config ETH_PHY_RST_GPIO
int "Ethernet PHY Reset GPIO"
default 5
help
GPIO pin connected to the PHY's reset pin. Common values are 5 or 17.
Check your board's schematic.
config ETH_MDC_GPIO
int "Ethernet MDC GPIO"
default 23
help
GPIO pin for MDC (Management Data Clock). Common value is 23.
config ETH_MDIO_GPIO
int "Ethernet MDIO GPIO"
default 18
help
GPIO pin for MDIO (Management Data Input/Output). Common value is 18.
endmenu
5. Build Instructions
- Open VS Code: Open your
ethernet_example
project folder in VS Code. - Configure with
menuconfig
:- Press
Ctrl+Shift+P
(orCmd+Shift+P
on macOS) to open the command palette. - Type and select “ESP-IDF: SDK Configuration Editor (menuconfig)”.
- Navigate to “Ethernet Configuration”.
- Select your PHY type (e.g.,
Use LAN8720 PHY
). - Set the
Ethernet PHY Address
(usually 0 or 1). - Crucially, set the correct GPIO pins for
Ethernet PHY Reset GPIO
,Ethernet MDC GPIO
, andEthernet MDIO GPIO
according to your specific ESP32 board and Ethernet module’s schematic. Common values are provided as defaults, but verify them. - Save the configuration and exit
menuconfig
.
- Press
- Build the Project:
- Open the VS Code Terminal (
Terminal > New Terminal
). - Run
idf.py build
. This will compile the project.
- Open the VS Code Terminal (
6. Run/Flash/Observe Steps
- Connect Hardware:
- Connect your ESP32 development board (e.g., ESP32-DevKitC with a LAN8720 Ethernet module) to your computer via USB.
- Connect an Ethernet cable from your module’s RJ45 port to a network switch or router that has a DHCP server.
- Flash the Firmware:
- In the VS Code Terminal, run
idf.py flash
. This will erase the chip and flash your compiled firmware.
- In the VS Code Terminal, run
- Monitor Serial Output:
- After flashing, run
idf.py monitor
. This will open the serial monitor and display the logs from your ESP32.
- After flashing, run
Expected Output:
You should see log messages similar to this:
I (XXX) ETHERNET_EXAMPLE: Ethernet initialization complete. Waiting for IP...
I (XXX) ETHERNET_EXAMPLE: Ethernet Started
I (XXX) ETHERNET_EXAMPLE: Ethernet Linked Up
I (XXX) ETHERNET_EXAMPLE: Ethernet MAC Address: XX:XX:XX:XX:XX:XX
I (XXX) ETHERNET_EXAMPLE: Ethernet Got IP Address
I (XXX) ETHERNET_EXAMPLE: ~~~~~~~~~~~
I (XXX) ETHERNET_EXAMPLE: ETH IP:192.168.1.100
I (XXX) ETHERNET_EXAMPLE: ETH Netmask:255.255.255.0
I (XXX) ETHERNET_EXAMPLE: ETH Gateway:192.168.1.1
I (XXX) ETHERNET_EXAMPLE: ~~~~~~~~~~~
The XXX
represents timestamps. The IP address, netmask, and gateway will vary based on your network configuration.
Variant Notes
The applicability of the internal Ethernet MAC varies significantly across the ESP32 series:
- ESP32 (Classic) (e.g., ESP32-WROOM-32, ESP32-DevKitC):
- Applicability: Yes, this is the primary target for direct Ethernet connectivity using an external PHY. It features a dedicated internal EMAC peripheral that supports both MII and RMII interfaces.
- Implementation: The example provided in this chapter directly uses the
esp_eth_mac_new_esp32()
function to leverage this internal hardware. You connect an external PHY chip (like LAN8720, IP101, RTL8201) to the ESP32’s EMAC pins via RMII.
- ESP32-S2 (e.g., ESP32-S2-WROVER):
- Applicability: No, it does not have an internal EMAC peripheral.
- Implementation: To achieve wired Ethernet, you must use an external Ethernet controller chip that communicates over SPI or SDIO. Popular choices include the W5500 or ENC28J60. The ESP-IDF provides specific MAC drivers for these external chips, such as
esp_eth_mac_new_w5500()
. The setup would involve configuring the SPI/SDIO bus and then using the appropriate external MAC driver. The rest of theesp_netif
and LwIP stack interaction remains similar.
- ESP32-S3 (e.g., ESP32-S3-WROOM-1):
- Applicability: No, similar to ESP32-S2, it lacks an internal EMAC.
- Implementation: Requires an external Ethernet controller chip (e.g., W5500, ENC28J60) connected via SPI or SDIO, using drivers like
esp_eth_mac_new_w5500()
.
- ESP32-C3 (e.g., ESP32-C3-MINI-1):
- Applicability: No, it does not have an internal EMAC.
- Implementation: Requires an external Ethernet controller chip (e.g., W5500, ENC28J60) connected via SPI, using drivers like
esp_eth_mac_new_w5500()
.
- ESP32-C6 (e.g., ESP32-C6-MINI-1):
- Applicability: No, it does not have an internal EMAC.
- Implementation: Requires an external Ethernet controller chip (e.g., W5500, ENC28J60) connected via SPI, using drivers like
esp_eth_mac_new_w5500()
.
- ESP32-H2:
- Applicability: No, it does not have an internal EMAC. Primarily designed for Thread and Zigbee.
- Implementation: If Ethernet connectivity is needed, it would also require an external Ethernet controller chip via SPI.
Summary of Variant Differences:
ESP32 Variant | Internal EMAC | Direct PHY Connection (RMII/MII) | External SPI/SDIO Ethernet Controller Required |
---|---|---|---|
ESP32 (Classic) | Yes | Yes | No (for direct Ethernet) |
ESP32-S2 | No | No | Yes (e.g., W5500 , ENC28J60 ) |
ESP32-S3 | No | No | Yes (e.g., W5500 , ENC28J60 ) |
ESP32-C3 | No | No | Yes (e.g., W5500 , ENC28J60 ) |
ESP32-C6 | No | No | Yes (e.g., W5500 , ENC28J60 ) |
ESP32-H2 | No | No | Yes (e.g., W5500 , ENC28J60 ) |
For variants without an internal EMAC, the esp_eth_mac_new_esp32()
function is not applicable. Instead, you would initialize an SPI bus and then create the MAC object using the specific driver for your external chip, like this:
// Example for ESP32-S2/S3/C3/C6/H2 with W5500
#include "driver/spi_master.h"
#include "eth_mac_w5500.h" // Include for W5500 MAC driver
// ... inside app_main ...
// Configuration for SPI bus
spi_bus_config_t buscfg = {
.miso_io_num = CONFIG_W5500_SPI_MISO_GPIO,
.mosi_io_num = CONFIG_W5500_SPI_MOSI_GPIO,
.sclk_io_num = CONFIG_W5500_SPI_SCLK_GPIO,
.quadwp_io_num = -1,
.quadhd_io_num = -1,
.max_transfer_sz = 16384,
};
ESP_ERROR_CHECK(spi_bus_initialize(SPI2_HOST, &buscfg, SPI_DMA_CH_AUTO));
// Configuration for W5500 Ethernet MAC
eth_w5500_config_t w5500_config = ETH_W5500_DEFAULT_CONFIG(SPI2_HOST, CONFIG_W5500_SPI_CS_GPIO, CONFIG_W5500_RST_GPIO);
mac = esp_eth_mac_new_w5500(&w5500_config);
// The rest of the initialization (PHY, driver install, attach, event handlers, start)
// would be similar, but the PHY would typically be integrated into the W5500 itself,
// so you might use a "dummy" PHY or the W5500's internal PHY capabilities.
// For W5500, you often don't need a separate PHY driver instance.
// esp_eth_phy_t *phy = esp_eth_phy_new_w5500(&phy_config); // Or similar if a separate W5500 PHY driver exists.
// In many cases, the W5500 MAC driver handles the PHY internally.
Common Mistakes & Troubleshooting Tips
- Incorrect GPIO Pin Assignments:
- Mistake: Using the wrong GPIO pins for MDC, MDIO, or the PHY reset. This is the most frequent cause of Ethernet initialization failure.
- Troubleshooting: Double-check your board’s schematic and the datasheet of your Ethernet PHY module. Ensure the pins configured in
menuconfig
(or directly in code) precisely match the hardware connections. Remember that different ESP32 development boards might use different default pins for Ethernet. - Tip: If you’re using a common dev kit like the ESP32-Ethernet-Kit, refer to its official documentation for pin assignments.
- Missing or Incorrect PHY Reset Circuitry:
- Mistake: The external PHY chip often requires a proper reset sequence (a low pulse on its reset pin during power-up or initialization). If the
phy_config.reset_gpio_num
is incorrect or the external circuitry isn’t providing a proper reset, the PHY won’t initialize. - Troubleshooting: Verify the
CONFIG_ETH_PHY_RST_GPIO
setting. Some modules might have an auto-reset circuit, but many require the ESP32 to control the reset pin. Ensure the reset pin is correctly connected and thereset_gpio_num
is set to the GPIO connected to the PHY’s reset.
- Mistake: The external PHY chip often requires a proper reset sequence (a low pulse on its reset pin during power-up or initialization). If the
- RMII Clock Issues:
- Mistake: The RMII interface requires a 50 MHz clock signal. This clock can be provided by the ESP32 itself (from a dedicated GPIO, often GPIO0 or GPIO17 depending on the board) or by the external PHY module. If the clock source is unstable, missing, or improperly configured, the Ethernet communication will fail.
- Troubleshooting: Check your board’s schematic to see if the 50 MHz clock is generated by the PHY or by the ESP32. If the ESP32 is generating it, ensure the correct GPIO is configured as the
ETH_CLK_OUT
pin (this is typically handled by theesp_eth_mac_new_esp32
function based on internal configuration, but it’s good to be aware). Verify the clock signal with an oscilloscope if possible.
- Network Configuration Problems (DHCP/Static IP):
- Mistake: The ESP32 fails to obtain an IP address, or the network is not correctly configured.
- Troubleshooting:
- DHCP: Ensure your network has a DHCP server running and that the Ethernet cable is connected to a port that provides network access. Test with another device (e.g., laptop) using the same cable and port.
- Static IP: If using a static IP, ensure the IP address, netmask, and gateway are correct for your network segment and that the chosen IP is not already in use.
- Check for firewall rules on your router that might be blocking new devices.
- Power Supply Issues:
- Mistake: Insufficient or noisy power supply to the ESP32 or the Ethernet PHY module. Ethernet PHYs can draw significant current, especially during link negotiation.
- Troubleshooting: Ensure your power supply can provide enough current for both the ESP32 and the Ethernet module. Use good quality power cables and consider adding decoupling capacitors near the PHY if experiencing instability.
Exercises
Exercise 1: Static IP Configuration
Modify the provided ethernet_example
to use a static IP address instead of DHCP.
Steps:
- Remove DHCP configuration: In
app_main
, modify theesp_netif_config_t
foreth_netif
to use a static IP. - Define Static IP: Define constants for your desired static IP address, subnet mask, and gateway.
- Apply Static IP: Use
esp_netif_dhcpc_stop()
to disable the DHCP client and thenesp_netif_set_ip_info()
to set the static IP information. - Test: Flash the updated code and verify that the ESP32 obtains the configured static IP address.
Exercise 2: Simple HTTP Client over Ethernet
Extend the Ethernet example to perform a basic HTTP GET request to a public website (e.g., http://example.com
) once an IP address is obtained.
Steps:
- Integrate HTTP Client: Include the
esp_http_client.h
header. - Create HTTP GET Function: Write a function (e.g.,
http_get_task
) that usesesp_http_client_init()
,esp_http_client_set_url()
,esp_http_client_perform()
, andesp_http_client_cleanup()
to fetch content. - Trigger on IP Event: In the
ip_event_handler
, afterIP_EVENT_ETH_GOT_IP
is received, create a new FreeRTOS task to run yourhttp_get_task
. - Parse Response (Optional): Print the HTTP response status code and a portion of the received content to the serial monitor.
- Test: Flash the code and observe the HTTP request and response in the serial monitor.
Exercise 3: Ethernet Link Status LEDs
Add functionality to control two LEDs: one indicating Ethernet link status (connected/disconnected) and another indicating data activity (flashing when data is transmitted or received).
Steps:
- Define LED GPIOs: In
menuconfig
or directly insdkconfig.h
, define two GPIOs for your LEDs (e.g.,CONFIG_LED_LINK_GPIO
,CONFIG_LED_ACTIVITY_GPIO
). - Initialize GPIOs: In
app_main
, configure these GPIOs as outputs. - Link Status LED:
- In
eth_event_handler
, turn on the link status LED whenETHERNET_EVENT_CONNECTED
occurs and turn it off whenETHERNET_EVENT_DISCONNECTED
occurs.
- In
- Activity LED:
- This is more advanced and requires custom event handling or polling. You can register for
ETH_EVENT_TX_DONE
andETH_EVENT_RX_DONE
(if available and enabled inmenuconfig
underComponent config > Ethernet > Ethernet driver > Enable Ethernet event loop
). - Alternatively, for a simpler approach, you could periodically poll the
esp_eth_ioctl
withETH_CMD_G_LINK_STATUS
andETH_CMD_G_DUPLEX_MODE
to infer activity, or implement a simplevTaskDelay
loop in a separate task that toggles the LED. - A more robust solution for activity would involve custom MAC driver modifications or using a PHY that exposes activity pins. For this exercise, a simple periodic blink or a blink on successful HTTP communication (from Exercise 2) would suffice.
- This is more advanced and requires custom event handling or polling. You can register for
- Test: Flash the code and observe the LEDs reacting to the Ethernet link status and network activity.
Summary
- Ethernet provides a reliable, secure, and high-performance wired networking solution for ESP32 applications, especially in industrial and critical environments.
- An Ethernet interface consists of a MAC (Media Access Control) layer (often internal to the ESP32 classic) and a PHY (Physical Layer Transceiver) layer (an external chip).
- The ESP32 (classic) features an internal EMAC peripheral that interfaces with external PHYs via RMII or MII.
- ESP32-S2, ESP32-S3, ESP32-C3, ESP32-C6, and ESP32-H2 variants do NOT have an internal EMAC and require external Ethernet controller chips (e.g., W5500, ENC28J60) connected via SPI/SDIO.
- The ESP-IDF
esp_eth
component provides a unified driver framework for Ethernet, integrating with the LwIP TCP/IP stack viaesp_netif
. - Ethernet initialization involves setting up
esp_netif
, creating MAC and PHY drivers, installing the Ethernet driver, attaching the netif, and registering event handlers for network status updates. - Key events to monitor include
ETH_EVENT_CONNECTED
,ETH_EVENT_DISCONNECTED
, andIP_EVENT_ETH_GOT_IP
. - IP addresses can be obtained dynamically via DHCP (default) or configured statically.
- Common troubleshooting steps involve verifying GPIO pin assignments, PHY reset, RMII clock, and network configuration.
Further Reading
- Espressif ESP-IDF Programming Guide – Ethernet: https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_eth.html
- Espressif ESP-IDF Examples – Ethernet: https://github.com/espressif/esp-idf/tree/master/examples/ethernet
- Microchip LAN8720A Datasheet: (Search for the official datasheet, e.g., on Microchip’s website)
- Wiznet W5500 Datasheet: (Search for the official datasheet, e.g., on Wiznet’s website)