From 7848a4ac29ea55fd0f18363362859ee7cf7c727e Mon Sep 17 00:00:00 2001 From: Abhik Roy Date: Tue, 17 Feb 2026 23:49:17 +1100 Subject: [PATCH] feat(examples): Add esp_netif examples demonstrating network interface configurations This commit adds new examples showcasing different esp_netif network interface configurations and use cases. Added examples: - eth_gateway: Ethernet Access Point example that runs a DHCP server on a single Ethernet interface, allowing connected devices to obtain IP addresses automatically - eth_endpoint_wifi_sta: Dual interface example that simultaneously operates both Ethernet STA and Wi-Fi STA interfaces as DHCP clients - eth_gateway_wifi_sta: Hybrid configuration example that operates Ethernet as an Access Point with DHCP server while simultaneously running Wi-Fi as a Station connecting to an external access point, enabling network bridging between wired and wireless networks - eth_endpoint_wifi_ap: Hybrid configuration example that operates Ethernet as a Station with DHCP client while simultaneously running Wi-Fi as an Access Point with DHCP server, enabling network bridging between wired upstream connection and local wireless network - eth_gateway_wifi_ap: Dual AP configuration example that simultaneously operates both Ethernet and Wi-Fi interfaces as Access Points with DHCP servers, enabling the ESP to act as a multi-interface router providing both wired (192.168.5.x) and wireless (192.168.4.x) network access simultaneously. Includes configurable DHCP pools, DNS support, NAPT routing, and client management for both interfaces." --- .gitignore | 4 + examples/esp_netif/EXAMPLES_LIST.md | 30 ++ .../eth_endpoint_wifi_ap/CMakeLists.txt | 8 + .../esp_netif/eth_endpoint_wifi_ap/README.md | 139 +++++ .../eth_endpoint_wifi_ap/main/CMakeLists.txt | 3 + .../main/Kconfig.projbuild | 110 ++++ .../main/eth_endpoint_wifi_ap.c | 367 +++++++++++++ .../main/idf_component.yml | 6 + .../eth_endpoint_wifi_ap/sdkconfig.defaults | 25 + .../eth_endpoint_wifi_sta/CMakeLists.txt | 6 + .../esp_netif/eth_endpoint_wifi_sta/README.md | 112 ++++ .../eth_endpoint_wifi_sta/main/CMakeLists.txt | 3 + .../main/Kconfig.projbuild | 36 ++ .../main/eth_endpoint_wifi_sta.c | 245 +++++++++ .../main/idf_component.yml | 6 + .../eth_endpoint_wifi_sta/sdkconfig.defaults | 11 + examples/esp_netif/eth_gateway/CMakeLists.txt | 6 + examples/esp_netif/eth_gateway/README.md | 92 ++++ .../esp_netif/eth_gateway/main/CMakeLists.txt | 3 + .../eth_gateway/main/Kconfig.projbuild | 69 +++ .../esp_netif/eth_gateway/main/eth_gateway.c | 180 +++++++ .../eth_gateway/main/idf_component.yml | 6 + .../esp_netif/eth_gateway/sdkconfig.defaults | 15 + .../eth_gateway_wifi_ap/CMakeLists.txt | 8 + .../esp_netif/eth_gateway_wifi_ap/README.md | 186 +++++++ .../eth_gateway_wifi_ap/main/CMakeLists.txt | 3 + .../main/Kconfig.projbuild | 175 +++++++ .../main/eth_gateway_wifi_ap.c | 489 ++++++++++++++++++ .../main/idf_component.yml | 6 + .../eth_gateway_wifi_ap/sdkconfig.defaults | 20 + .../eth_gateway_wifi_sta/CMakeLists.txt | 6 + .../esp_netif/eth_gateway_wifi_sta/README.md | 144 ++++++ .../eth_gateway_wifi_sta/main/CMakeLists.txt | 3 + .../main/Kconfig.projbuild | 104 ++++ .../main/eth_gateway_wifi_sta.c | 342 ++++++++++++ .../main/idf_component.yml | 6 + .../eth_gateway_wifi_sta/sdkconfig.defaults | 16 + .../multiple_netifs/main/Kconfig.projbuild | 21 + .../multiple_netifs/main/wifi_connect.c | 22 + 39 files changed, 3033 insertions(+) create mode 100644 examples/esp_netif/EXAMPLES_LIST.md create mode 100644 examples/esp_netif/eth_endpoint_wifi_ap/CMakeLists.txt create mode 100644 examples/esp_netif/eth_endpoint_wifi_ap/README.md create mode 100644 examples/esp_netif/eth_endpoint_wifi_ap/main/CMakeLists.txt create mode 100644 examples/esp_netif/eth_endpoint_wifi_ap/main/Kconfig.projbuild create mode 100644 examples/esp_netif/eth_endpoint_wifi_ap/main/eth_endpoint_wifi_ap.c create mode 100644 examples/esp_netif/eth_endpoint_wifi_ap/main/idf_component.yml create mode 100644 examples/esp_netif/eth_endpoint_wifi_ap/sdkconfig.defaults create mode 100644 examples/esp_netif/eth_endpoint_wifi_sta/CMakeLists.txt create mode 100644 examples/esp_netif/eth_endpoint_wifi_sta/README.md create mode 100644 examples/esp_netif/eth_endpoint_wifi_sta/main/CMakeLists.txt create mode 100644 examples/esp_netif/eth_endpoint_wifi_sta/main/Kconfig.projbuild create mode 100644 examples/esp_netif/eth_endpoint_wifi_sta/main/eth_endpoint_wifi_sta.c create mode 100644 examples/esp_netif/eth_endpoint_wifi_sta/main/idf_component.yml create mode 100644 examples/esp_netif/eth_endpoint_wifi_sta/sdkconfig.defaults create mode 100644 examples/esp_netif/eth_gateway/CMakeLists.txt create mode 100644 examples/esp_netif/eth_gateway/README.md create mode 100644 examples/esp_netif/eth_gateway/main/CMakeLists.txt create mode 100644 examples/esp_netif/eth_gateway/main/Kconfig.projbuild create mode 100644 examples/esp_netif/eth_gateway/main/eth_gateway.c create mode 100644 examples/esp_netif/eth_gateway/main/idf_component.yml create mode 100644 examples/esp_netif/eth_gateway/sdkconfig.defaults create mode 100644 examples/esp_netif/eth_gateway_wifi_ap/CMakeLists.txt create mode 100644 examples/esp_netif/eth_gateway_wifi_ap/README.md create mode 100644 examples/esp_netif/eth_gateway_wifi_ap/main/CMakeLists.txt create mode 100644 examples/esp_netif/eth_gateway_wifi_ap/main/Kconfig.projbuild create mode 100644 examples/esp_netif/eth_gateway_wifi_ap/main/eth_gateway_wifi_ap.c create mode 100644 examples/esp_netif/eth_gateway_wifi_ap/main/idf_component.yml create mode 100644 examples/esp_netif/eth_gateway_wifi_ap/sdkconfig.defaults create mode 100644 examples/esp_netif/eth_gateway_wifi_sta/CMakeLists.txt create mode 100644 examples/esp_netif/eth_gateway_wifi_sta/README.md create mode 100644 examples/esp_netif/eth_gateway_wifi_sta/main/CMakeLists.txt create mode 100644 examples/esp_netif/eth_gateway_wifi_sta/main/Kconfig.projbuild create mode 100644 examples/esp_netif/eth_gateway_wifi_sta/main/eth_gateway_wifi_sta.c create mode 100644 examples/esp_netif/eth_gateway_wifi_sta/main/idf_component.yml create mode 100644 examples/esp_netif/eth_gateway_wifi_sta/sdkconfig.defaults diff --git a/.gitignore b/.gitignore index 4db6959840..f63999c888 100644 --- a/.gitignore +++ b/.gitignore @@ -100,3 +100,7 @@ common_components/modem_sim/modem_sim_esp32/ # repository release tools release_notes.txt + +# clangd cache and index files +.cache/ +*.idx diff --git a/examples/esp_netif/EXAMPLES_LIST.md b/examples/esp_netif/EXAMPLES_LIST.md new file mode 100644 index 0000000000..394268258c --- /dev/null +++ b/examples/esp_netif/EXAMPLES_LIST.md @@ -0,0 +1,30 @@ +# Network Interface Configuration Examples + +This document lists network interface configuration examples in this tree. The examples **2ethDHCP**, **2ethStaticIp**, **EthStaEthAp**, **WifiApWithServer**, and **wifiSta** are maintained in the companion **NAPT** repository (same directory names at the NAPT project root). + +### 1. **eth_gateway** + - **Description**: Single Ethernet interface as **gateway** (DHCP server on the cable; downstream subnet—not a Wi-Fi AP) + - **Location**: `eth_gateway/` + - **Features**: One Ethernet interface, DHCP server, static gateway IP + +### 2. **eth_gateway_wifi_ap** + - **Description**: Ethernet **gateway** + Wi-Fi access point (DHCP on both) + - **Location**: `eth_gateway_wifi_ap/` + - **Features**: Wired gateway role plus Wi-Fi AP for wireless clients + +### 3. **eth_gateway_wifi_sta** + - **Description**: Ethernet **gateway** + Wi-Fi station (upstream Wi-Fi, DHCP server on Ethernet) + - **Location**: `eth_gateway_wifi_sta/` + - **Features**: Ethernet gateway with Wi-Fi STA backhaul + +### 4. **eth_endpoint_wifi_ap** + - **Description**: Ethernet **endpoint** (DHCP client) + Wi-Fi access point + - **Location**: `eth_endpoint_wifi_ap/` + - **Features**: Wired DHCP client toward upstream; local Wi-Fi AP + +### 5. **eth_endpoint_wifi_sta** + - **Description**: Ethernet **endpoint** (DHCP client) + Wi-Fi station (dual DHCP clients) + - **Location**: `eth_endpoint_wifi_sta/` + - **Features**: Both interfaces as DHCP clients + +Additional examples (e.g. **WifiApWifiSta**) may live in the **NAPT** repository alongside the examples named in the introduction. diff --git a/examples/esp_netif/eth_endpoint_wifi_ap/CMakeLists.txt b/examples/esp_netif/eth_endpoint_wifi_ap/CMakeLists.txt new file mode 100644 index 0000000000..d8b3f1eddb --- /dev/null +++ b/examples/esp_netif/eth_endpoint_wifi_ap/CMakeLists.txt @@ -0,0 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(eth_endpoint_wifi_ap) diff --git a/examples/esp_netif/eth_endpoint_wifi_ap/README.md b/examples/esp_netif/eth_endpoint_wifi_ap/README.md new file mode 100644 index 0000000000..b61dfc4f7c --- /dev/null +++ b/examples/esp_netif/eth_endpoint_wifi_ap/README.md @@ -0,0 +1,139 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | + +# Ethernet endpoint + Wi-Fi AP example + +## Overview + +This example initializes **two network interfaces simultaneously**: an **Ethernet interface** as an **endpoint** (DHCP client toward upstream—not Wi-Fi “station”), and a **Wi-Fi interface** operating as an **access point (AP)** with a DHCP server. The Ethernet endpoint obtains an address from the wired network via DHCP, while the Wi-Fi AP provides network access to wireless clients via DHCP (e.g. in the `192.168.5.x` range). This hybrid configuration bridges a wired upstream connection and a local wireless network. + +The example uses the `ethernet_init` component from [esp-eth-drivers](https://github.com/espressif/esp-eth-drivers) to initialize the Ethernet driver based on menuconfig (internal/SPI PHY, etc.) and creates two `esp_netif` instances: one for the Ethernet endpoint (configured with `ESP_NETIF_DEFAULT_ETH` for DHCP client mode) and one for Wi-Fi AP (configured with `esp_netif_create_default_wifi_ap()` with static IP and DHCP server). + +### Key Features + +- **Ethernet endpoint (DHCP client)**: Automatically obtains an IP address from the Ethernet network +- **Wi-Fi AP with DHCP Server**: Automatically assigns IP addresses to devices connected via Wi-Fi +- **Network Bridging**: Bridges traffic between Ethernet and Wi-Fi interfaces +- **NAPT Support**: Optional Network Address Port Translation for routing between interfaces; when the lwIP NAPT (and IPv4 forwarding) features are enabled in menuconfig, NAPT is enabled on the **Wi-Fi AP** interface (for routing traffic through the Ethernet connection) +- **Configurable IP Pool**: Set the range of IP addresses to assign via DHCP (default: `192.168.5.2` - `192.168.5.100`) +- **DHCP Lease Time**: Configurable lease duration for assigned IP addresses +- **DNS Support**: Optional DNS server configuration for DHCP clients; can use the Ethernet endpoint's DNS servers +- **Event-Driven Architecture**: Uses ESP event system to handle network events for both interfaces + +## How to use example + +### Configure the project + +You can configure the project using `idf.py menuconfig` or by using the provided `sdkconfig.defaults` file which contains default configuration values. + +#### Ethernet Configuration + +Configure the Ethernet PHY/SPI settings under "Ethernet" in menuconfig (see the [ethernet_init](https://github.com/espressif/esp-eth-drivers/tree/master/ethernet_init) component documentation). The example depends on the managed component `espressif/ethernet_init` (see `main/idf_component.yml`). + +#### Example Configuration + +Under "Example Configuration" menu, you can configure: + +**Wi-Fi AP Configuration:** +- **WiFi AP SSID**: Wi-Fi network SSID for the Access Point (default: `"myssid-ext"`) +- **WiFi AP Password**: Wi-Fi network password (default: `"mypassword"`) +- **WiFi AP Channel**: Wi-Fi channel for the AP (default: 1) +- **WiFi AP IP Address**: Static IP address for the ESP device (default: `192.168.5.1`) +- **WiFi AP Netmask**: Subnet mask for the network (default: `255.255.255.0`) +- **WiFi AP Gateway**: Default gateway IP (default: `192.168.5.1`) +- **DHCP Lease Time**: Duration of IP address leases in units of `LWIP_DHCPS_LEASE_UNIT` (default: 120, which equals 7200 seconds or 2 hours) +- **DHCP IP Pool Start Address**: First IP address in the DHCP pool (default: `192.168.5.2`) +- **DHCP IP Pool End Address**: Last IP address in the DHCP pool (default: `192.168.5.100`) +- **Enable DNS in DHCP Offers**: Enable DNS server information in DHCP offers (default: enabled) +- **Primary DNS Server**: Primary DNS server IP (default: `192.168.5.1`, can be updated from Ethernet endpoint DNS) +- **Backup DNS Server**: Backup DNS server IP (default: `8.8.8.8`) + +**NAPT Configuration:** +- Enable IP forwarding and NAPT in menuconfig under "Component config" → "LWIP" → "Enable IPv4 forwarding" and "Enable IPv4 NAPT" + +### Build, Flash, and Run + +``` +idf.py -p PORT build flash monitor +``` + +Replace PORT with your serial port. + +**Prerequisites:** +- Connect an Ethernet cable to the ESP's Ethernet port (connected to a network with DHCP server) +- Ensure devices can connect to the Wi-Fi AP with the configured SSID and password + +The example initializes the Ethernet endpoint first, then starts the Wi-Fi AP. It waits for the Ethernet endpoint to obtain an IP address before completing initialization. If DNS is enabled, the Wi-Fi AP's DNS servers will be updated with the Ethernet endpoint's DNS servers automatically. + +## Example Output + +``` +I (1234) eth_endpoint_wifi_ap: Wi-Fi AP initialized. AP IP: 192.168.5.1, netmask: 255.255.255.0 +I (1234) eth_endpoint_wifi_ap: Connect a device to the Wi-Fi AP to get an IP via DHCP +I (1234) eth_endpoint_wifi_ap: Wi-Fi AP Got IP Address +I (1234) eth_endpoint_wifi_ap: ~~~~~~~~~~~ +I (1234) eth_endpoint_wifi_ap: APIP:192.168.5.1 +I (1234) eth_endpoint_wifi_ap: APMASK:255.255.255.0 +I (1234) eth_endpoint_wifi_ap: APGW:192.168.5.1 +I (1244) eth_endpoint_wifi_ap: DHCP_DNS_MAIN:192.168.5.1 +I (1244) eth_endpoint_wifi_ap: DHCP_DNS_BACKUP:8.8.8.8 +I (1254) eth_endpoint_wifi_ap: ~~~~~~~~~~~ +I (1254) eth_endpoint_wifi_ap: Wi-Fi AP started. SSID:myssid-ext password:mypassword channel:1 +I (1254) eth_endpoint_wifi_ap: Wi-Fi Event: base=WIFI_EVENT, id=0 +I (1254) eth_endpoint_wifi_ap: Wi-Fi AP started +I (1264) ethernet_init: Ethernet(IP101[23,18]) Link Up +I (1264) ethernet_init: Ethernet(IP101[23,18]) HW Addr 58:bf:25:e0:41:03 +I (1274) eth_endpoint_wifi_ap: Ethernet Link Up +I (1274) eth_endpoint_wifi_ap: Ethernet HW Addr 58:bf:25:e0:41:03 +I (1284) eth_endpoint_wifi_ap: Ethernet Got IP Address +I (1284) eth_endpoint_wifi_ap: Event: base=IP_EVENT, id=4 +I (1284) eth_endpoint_wifi_ap: ~~~~~~~~~~~ +I (1284) eth_endpoint_wifi_ap: ETHIP:192.168.1.100 +I (1284) eth_endpoint_wifi_ap: ETHMASK:255.255.255.0 +I (1284) eth_endpoint_wifi_ap: ETHGW:192.168.1.1 +I (1294) eth_endpoint_wifi_ap: DHCP_DNS_MAIN:192.168.1.1 +I (1294) eth_endpoint_wifi_ap: DHCP_DNS_BACKUP:8.8.8.8 +I (1304) eth_endpoint_wifi_ap: ~~~~~~~~~~~ +I (1304) eth_endpoint_wifi_ap: Updated Wi-Fi AP DNS with Ethernet endpoint DNS: 192.168.1.1 +I (1304) eth_endpoint_wifi_ap: NAPT enabled on Wi-Fi AP +I (1404) eth_endpoint_wifi_ap: Wi-Fi Event: base=WIFI_EVENT, id=2 +I (1414) eth_endpoint_wifi_ap: station xx:xx:xx:xx:xx:xx join, AID=1 +I (1424) eth_endpoint_wifi_ap: Wi-Fi AP assigned IP to client: 192.168.5.2 +``` + +After initialization: +- The Ethernet endpoint connects to the Ethernet network and receives an IP address via DHCP +- Devices connected to the Wi-Fi AP will receive IPs in the `192.168.5.0/24` range via DHCP +- The ESP can be reached at `192.168.5.1` on the Wi-Fi AP network +- If NAPT is enabled, traffic from Wi-Fi AP clients can be routed through the Ethernet connection +- DNS servers from the Ethernet endpoint are automatically used for Wi-Fi AP DHCP clients (if DNS is enabled) + +## Use Cases + +- **Ethernet-to-Wi-Fi Bridges**: Connect wireless devices to a wired Ethernet network +- **Network Extenders**: Extend Ethernet networks to wireless devices +- **IoT Gateways**: Provide wireless access with Ethernet backhaul +- **Home Automation Hubs**: Connect wireless sensors to Ethernet networks +- **Network Isolation**: Create a separate Wi-Fi network while maintaining internet connectivity via Ethernet + +## Troubleshooting + +- **Ethernet Link Not Up**: Ensure the Ethernet cable is properly connected and the PHY configuration matches your hardware. Verify that the Ethernet network has a DHCP server running. +- **Client Not Getting IP**: Check that the DHCP server started successfully (look for "DHCP server started" in the logs) +- **Wi-Fi AP Not Starting**: Check that the SSID and password are correctly configured in menuconfig +- **No IP Address Received on Ethernet**: Verify that the Ethernet network has a DHCP server running. Check network connectivity +- **IP Pool Exhausted**: If you have more than 99 devices, increase the DHCP IP pool range in menuconfig +- **DNS Issues**: If DNS is enabled but not working, verify the DNS server addresses are correct. The example automatically updates Wi-Fi AP DNS with Ethernet endpoint DNS when available +- **NAPT Not Working**: Ensure IP forwarding and NAPT are enabled in menuconfig (`CONFIG_LWIP_IP_FORWARD=y` and `CONFIG_LWIP_IPV4_NAPT=y`) +- **Hardware Issues**: For hardware and driver issues, refer to ESP-IDF Ethernet and Wi-Fi documentation and the upper level [README](../README.md) + +## Configuration Notes + +- The maximum DHCP pool size is 100 addresses (`DHCPS_MAX_LEASE`) +- DHCP lease time is specified in units of `LWIP_DHCPS_LEASE_UNIT` (default 60 seconds) +- The Wi-Fi AP always uses a static IP address (configured via menuconfig) +- DNS server information is optional and can be disabled in menuconfig +- When DNS is enabled, the Wi-Fi AP's primary DNS is automatically updated with the Ethernet endpoint's DNS server after Ethernet connects +- The example waits for the Ethernet endpoint to obtain an IP address before completing initialization +- NAPT is optional and must be enabled in menuconfig for routing between interfaces +- Both interfaces operate independently: the Ethernet endpoint provides upstream connectivity while the Wi-Fi AP provides local network access diff --git a/examples/esp_netif/eth_endpoint_wifi_ap/main/CMakeLists.txt b/examples/esp_netif/eth_endpoint_wifi_ap/main/CMakeLists.txt new file mode 100644 index 0000000000..c8f4031a7e --- /dev/null +++ b/examples/esp_netif/eth_endpoint_wifi_ap/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "eth_endpoint_wifi_ap.c" + INCLUDE_DIRS "." + REQUIRES ethernet_init esp_netif esp_eth esp_wifi nvs_flash) diff --git a/examples/esp_netif/eth_endpoint_wifi_ap/main/Kconfig.projbuild b/examples/esp_netif/eth_endpoint_wifi_ap/main/Kconfig.projbuild new file mode 100644 index 0000000000..9a78bef8c6 --- /dev/null +++ b/examples/esp_netif/eth_endpoint_wifi_ap/main/Kconfig.projbuild @@ -0,0 +1,110 @@ +menu "Example Configuration" + + # WiFi AP Configuration + config EXAMPLE_WIFI_AP_SSID + string "WiFi AP SSID" + default "myssid-ext" + help + SSID (network name) for the WiFi Access Point. + + config EXAMPLE_WIFI_AP_PASS + string "WiFi AP Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the Access Point. + + config EXAMPLE_WIFI_AP_CHANNEL + int "WiFi AP Channel" + range 1 13 + default 1 + help + WiFi channel for the Access Point (1-13). + + choice EXAMPLE_WIFI_AP_AUTHMODE + prompt "WiFi AP Authentication Mode" + default EXAMPLE_WIFI_AP_AUTHMODE_WPA_WPA2_PSK + help + Authentication mode for the Wi-Fi Access Point. + If the password is empty, Open mode is used regardless of this setting. + + config EXAMPLE_WIFI_AP_AUTHMODE_OPEN + bool "Open" + config EXAMPLE_WIFI_AP_AUTHMODE_WPA2_PSK + bool "WPA2 PSK" + config EXAMPLE_WIFI_AP_AUTHMODE_WPA_WPA2_PSK + bool "WPA/WPA2 PSK" + config EXAMPLE_WIFI_AP_AUTHMODE_WPA3_PSK + bool "WPA3 SAE (PSK)" + config EXAMPLE_WIFI_AP_AUTHMODE_WPA2_WPA3_PSK + bool "WPA2/WPA3 transition" + endchoice + + config EXAMPLE_WIFI_AP_IP_ADDR + string "WiFi AP IP Address" + default "192.168.5.1" + help + IP address for the WiFi Access Point (DHCP server). + This is the static IP address assigned to the WiFi AP interface. + + config EXAMPLE_WIFI_AP_NETMASK + string "WiFi AP Netmask" + default "255.255.255.0" + help + Subnet mask for the WiFi Access Point network. + + config EXAMPLE_WIFI_AP_GW + string "WiFi AP Gateway" + default "192.168.5.1" + help + Default gateway IP address for the WiFi Access Point network. + Typically set to the same IP as the AP itself. + + config EXAMPLE_WIFI_AP_DHCP_LEASE_TIME + int "DHCP Lease Time" + default 120 + help + DHCP lease time in units of LWIP_DHCPS_LEASE_UNIT (default 60 seconds). + Example: Setting this to 120 means a 7200-second (2 hour) lease time. + Setting this to 1 means a 60-second lease time. + + config EXAMPLE_WIFI_AP_DHCP_START_ADDR + string "DHCP IP Pool Start Address" + default "192.168.5.2" + help + Starting IP address of the DHCP address pool. + This is the first IP address that will be assigned to DHCP clients. + + config EXAMPLE_WIFI_AP_DHCP_END_ADDR + string "DHCP IP Pool End Address" + default "192.168.5.100" + help + Ending IP address of the DHCP address pool. + This is the last IP address that will be assigned to DHCP clients. + Maximum pool size is 100 addresses (DHCPS_MAX_LEASE). + + config EXAMPLE_WIFI_AP_DHCP_ENABLE_DNS + bool "Enable DNS in DHCP Offers" + default y + help + Enable DNS server information in DHCP offers. + When enabled, DHCP clients will receive DNS server addresses. + If enabled, DNS can be configured to use Ethernet Station's DNS servers. + + config EXAMPLE_WIFI_AP_DHCP_DNS_MAIN + string "Primary DNS Server" + default "192.168.5.1" + depends on EXAMPLE_WIFI_AP_DHCP_ENABLE_DNS + help + Primary DNS server IP address to be provided to DHCP clients. + Typically set to the AP IP address or a public DNS server. + Can be overridden to use Ethernet Station's DNS if available. + + config EXAMPLE_WIFI_AP_DHCP_DNS_BACKUP + string "Backup DNS Server" + default "8.8.8.8" + depends on EXAMPLE_WIFI_AP_DHCP_ENABLE_DNS + help + Backup DNS server IP address to be provided to DHCP clients. + Typically set to a public DNS server like 8.8.8.8 (Google DNS). + +endmenu diff --git a/examples/esp_netif/eth_endpoint_wifi_ap/main/eth_endpoint_wifi_ap.c b/examples/esp_netif/eth_endpoint_wifi_ap/main/eth_endpoint_wifi_ap.c new file mode 100644 index 0000000000..c24ddae118 --- /dev/null +++ b/examples/esp_netif/eth_endpoint_wifi_ap/main/eth_endpoint_wifi_ap.c @@ -0,0 +1,367 @@ +/* + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + * + * Ethernet endpoint + Wi-Fi AP example + * + * This example initializes an Ethernet interface as an endpoint (DHCP client toward + * upstream) and a Wi-Fi interface as an access point (with DHCP server). The Ethernet + * endpoint obtains an address from the wired network while the Wi-Fi AP provides + * network access to wireless clients. + */ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_netif.h" +#include "esp_eth.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_wifi_types.h" +#include "esp_mac.h" +#include "nvs_flash.h" +#include "dhcpserver/dhcpserver.h" +#include "dhcpserver/dhcpserver_options.h" +#include "ethernet_init.h" +#include "sdkconfig.h" + +static const char *TAG = "eth_endpoint_wifi_ap"; + +/** Event group bit: set when Ethernet endpoint receives IPv4 (IP_EVENT_ETH_GOT_IP). */ +#define EXAMPLE_ETH_ENDPOINT_GOT_IP_BIT (1U << 0) + +static EventGroupHandle_t event_group = NULL; + +/** Event handler for Ethernet events */ +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}; + esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data; + + switch (event_id) { + case ETHERNET_EVENT_CONNECTED: + esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); + ESP_LOGI(TAG, "Ethernet Link Up"); + ESP_LOGI(TAG, "Ethernet HW Addr %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 Link Down"); + break; + case ETHERNET_EVENT_START: + ESP_LOGI(TAG, "Ethernet Started"); + break; + case ETHERNET_EVENT_STOP: + ESP_LOGI(TAG, "Ethernet Stopped"); + break; + default: + break; + } +} + +/** Event handler for IP_EVENT_ETH_GOT_IP */ +static void eth_got_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; + esp_netif_t *netif = event->esp_netif; + esp_netif_dns_info_t dns_info; + + ESP_LOGI(TAG, "Ethernet Got IP Address"); + ESP_LOGI(TAG, "Event: base=%s, id=%ld", event_base, event_id); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip)); + ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask)); + ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw)); + + // Print DHCP DNS information + if (esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info) == ESP_OK) { + ESP_LOGI(TAG, "DHCP_DNS_MAIN:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + } + if (esp_netif_get_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_info) == ESP_OK) { + ESP_LOGI(TAG, "DHCP_DNS_BACKUP:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + } + + ESP_LOGI(TAG, "~~~~~~~~~~~"); + + xEventGroupSetBits(event_group, EXAMPLE_ETH_ENDPOINT_GOT_IP_BIT); +} + +/** Event handler for WiFi AP events */ +static void wifi_ap_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (event_base == WIFI_EVENT) { + ESP_LOGI(TAG, "Wi-Fi Event: base=%s, id=%ld", event_base, event_id); + switch (event_id) { + case WIFI_EVENT_AP_START: + ESP_LOGI(TAG, "Wi-Fi AP started"); + break; + case WIFI_EVENT_AP_STOP: + ESP_LOGI(TAG, "Wi-Fi AP stopped"); + break; + case WIFI_EVENT_AP_STACONNECTED: { + wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data; + ESP_LOGI(TAG, "station "MACSTR" join, AID=%d", + MAC2STR(event->mac), event->aid); + } + break; + case WIFI_EVENT_AP_STADISCONNECTED: { + wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data; + ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d", + MAC2STR(event->mac), event->aid); + } + break; + default: + ESP_LOGW(TAG, "Unhandled Wi-Fi AP event: id=%ld", event_id); + break; + } + } else if (event_base == IP_EVENT && event_id == IP_EVENT_ASSIGNED_IP_TO_CLIENT) { + ip_event_assigned_ip_to_client_t* event = (ip_event_assigned_ip_to_client_t*) event_data; + ESP_LOGI(TAG, "Wi-Fi AP assigned IP to client: " IPSTR, IP2STR(&event->ip)); + } +} + +/** Print WiFi AP IP information */ +static void print_ap_ip_info(esp_netif_t *ap_netif) +{ + esp_netif_ip_info_t ip_info; + esp_netif_dns_info_t dns_info; + + if (esp_netif_get_ip_info(ap_netif, &ip_info) == ESP_OK) { + ESP_LOGI(TAG, "Wi-Fi AP Got IP Address"); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "APIP:" IPSTR, IP2STR(&ip_info.ip)); + ESP_LOGI(TAG, "APMASK:" IPSTR, IP2STR(&ip_info.netmask)); + ESP_LOGI(TAG, "APGW:" IPSTR, IP2STR(&ip_info.gw)); + + // Print DNS information + if (esp_netif_get_dns_info(ap_netif, ESP_NETIF_DNS_MAIN, &dns_info) == ESP_OK) { + ESP_LOGI(TAG, "DHCP_DNS_MAIN:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + } + if (esp_netif_get_dns_info(ap_netif, ESP_NETIF_DNS_BACKUP, &dns_info) == ESP_OK) { + ESP_LOGI(TAG, "DHCP_DNS_BACKUP:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + } + + ESP_LOGI(TAG, "~~~~~~~~~~~"); + } +} + +/** Initialize WiFi AP */ +static esp_netif_t *wifi_init_ap(void) +{ + esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap(); + ESP_ERROR_CHECK(ap_netif != NULL ? ESP_OK : ESP_FAIL); + + // Static IP configuration for WiFi AP + static esp_netif_ip_info_t s_ap_ip_info; + memset(&s_ap_ip_info, 0, sizeof(esp_netif_ip_info_t)); + + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_WIFI_AP_IP_ADDR, &s_ap_ip_info.ip) != ESP_OK) { + ESP_LOGE(TAG, "Invalid IP address: %s", CONFIG_EXAMPLE_WIFI_AP_IP_ADDR); + return NULL; + } + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_WIFI_AP_NETMASK, &s_ap_ip_info.netmask) != ESP_OK) { + ESP_LOGE(TAG, "Invalid netmask: %s", CONFIG_EXAMPLE_WIFI_AP_NETMASK); + return NULL; + } + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_WIFI_AP_GW, &s_ap_ip_info.gw) != ESP_OK) { + ESP_LOGE(TAG, "Invalid gateway: %s", CONFIG_EXAMPLE_WIFI_AP_GW); + return NULL; + } + + ESP_ERROR_CHECK(esp_netif_dhcps_stop(ap_netif)); + ESP_ERROR_CHECK(esp_netif_set_ip_info(ap_netif, &s_ap_ip_info)); + + /* Configure DHCP server options */ + // Set lease time + uint32_t lease_time = CONFIG_EXAMPLE_WIFI_AP_DHCP_LEASE_TIME; + esp_err_t err = esp_netif_dhcps_option(ap_netif, ESP_NETIF_OP_SET, IP_ADDRESS_LEASE_TIME, &lease_time, sizeof(lease_time)); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to set DHCP lease time: %s", esp_err_to_name(err)); + } + + // Configure IP address pool + dhcps_lease_t dhcp_lease; + memset(&dhcp_lease, 0, sizeof(dhcps_lease_t)); + dhcp_lease.enable = true; + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_WIFI_AP_DHCP_START_ADDR, (esp_ip4_addr_t *)&dhcp_lease.start_ip) != ESP_OK) { + ESP_LOGW(TAG, "Invalid DHCP start address: %s, using default pool", CONFIG_EXAMPLE_WIFI_AP_DHCP_START_ADDR); + } else if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_WIFI_AP_DHCP_END_ADDR, (esp_ip4_addr_t *)&dhcp_lease.end_ip) != ESP_OK) { + ESP_LOGW(TAG, "Invalid DHCP end address: %s, using default pool", CONFIG_EXAMPLE_WIFI_AP_DHCP_END_ADDR); + } else { + err = esp_netif_dhcps_option(ap_netif, ESP_NETIF_OP_SET, REQUESTED_IP_ADDRESS, &dhcp_lease, sizeof(dhcps_lease_t)); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to set DHCP IP pool: %s", esp_err_to_name(err)); + } + } + + // Configure DNS servers if enabled +#if CONFIG_EXAMPLE_WIFI_AP_DHCP_ENABLE_DNS + dhcps_offer_t dhcps_dns_value = OFFER_DNS; + err = esp_netif_dhcps_option(ap_netif, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &dhcps_dns_value, sizeof(dhcps_dns_value)); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to enable DNS in DHCP offers: %s", esp_err_to_name(err)); + } else { + // Set DNS server addresses (will be updated from Ethernet Station DNS if available) + esp_netif_dns_info_t dns_info = {}; + dns_info.ip.type = ESP_IPADDR_TYPE_V4; + + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_WIFI_AP_DHCP_DNS_MAIN, &dns_info.ip.u_addr.ip4) == ESP_OK) { + err = esp_netif_set_dns_info(ap_netif, ESP_NETIF_DNS_MAIN, &dns_info); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to set primary DNS: %s", esp_err_to_name(err)); + } + } + + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_WIFI_AP_DHCP_DNS_BACKUP, &dns_info.ip.u_addr.ip4) == ESP_OK) { + err = esp_netif_set_dns_info(ap_netif, ESP_NETIF_DNS_BACKUP, &dns_info); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to set backup DNS: %s", esp_err_to_name(err)); + } + } + } +#endif // CONFIG_EXAMPLE_WIFI_AP_DHCP_ENABLE_DNS + + ESP_ERROR_CHECK(esp_netif_dhcps_start(ap_netif)); + + ESP_LOGI(TAG, "Wi-Fi AP initialized. AP IP: " IPSTR ", netmask: " IPSTR, + IP2STR(&s_ap_ip_info.ip), IP2STR(&s_ap_ip_info.netmask)); + ESP_LOGI(TAG, "Connect a device to the Wi-Fi AP to get an IP via DHCP"); + + // Print AP IP information + print_ap_ip_info(ap_netif); + + return ap_netif; +} + +static wifi_auth_mode_t example_wifi_ap_authmode(void) +{ +#if CONFIG_EXAMPLE_WIFI_AP_AUTHMODE_OPEN + return WIFI_AUTH_OPEN; +#elif CONFIG_EXAMPLE_WIFI_AP_AUTHMODE_WPA2_PSK + return WIFI_AUTH_WPA2_PSK; +#elif CONFIG_EXAMPLE_WIFI_AP_AUTHMODE_WPA_WPA2_PSK + return WIFI_AUTH_WPA_WPA2_PSK; +#elif CONFIG_EXAMPLE_WIFI_AP_AUTHMODE_WPA3_PSK + return WIFI_AUTH_WPA3_PSK; +#elif CONFIG_EXAMPLE_WIFI_AP_AUTHMODE_WPA2_WPA3_PSK + return WIFI_AUTH_WPA2_WPA3_PSK; +#else + return WIFI_AUTH_WPA_WPA2_PSK; +#endif +} + +void app_main(void) +{ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + event_group = xEventGroupCreate(); + + // Initialize Ethernet driver + uint8_t eth_port_cnt = 0; + esp_eth_handle_t *eth_handles = NULL; + ESP_ERROR_CHECK(ethernet_init_all(ð_handles, ð_port_cnt)); + + if (eth_port_cnt == 0) { + ESP_LOGE(TAG, "No Ethernet interface initialized"); + return; + } + if (eth_port_cnt > 1) { + ESP_LOGW(TAG, "%u Ethernet interfaces initialized; this example only uses the first one", eth_port_cnt); + } + + // Register Ethernet 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, ð_got_ip_event_handler, NULL)); + + // Create Ethernet netif (DHCP client mode) + esp_netif_config_t eth_cfg = ESP_NETIF_DEFAULT_ETH(); + esp_netif_t *eth_netif = esp_netif_new(ð_cfg); + ESP_ERROR_CHECK(eth_netif != NULL ? ESP_OK : ESP_FAIL); + + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[0]))); + ESP_ERROR_CHECK(esp_eth_start(eth_handles[0])); + + ESP_LOGI(TAG, "Ethernet Station initialized, waiting for IP address..."); + + // Register WiFi AP event handlers - register only specific events to avoid warnings for unhandled events + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_START, wifi_ap_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_STOP, wifi_ap_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_STACONNECTED, wifi_ap_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_STADISCONNECTED, wifi_ap_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ASSIGNED_IP_TO_CLIENT, wifi_ap_event_handler, NULL)); + + // Initialize WiFi AP netif + esp_netif_t *ap_netif = wifi_init_ap(); + if (ap_netif == NULL) { + ESP_LOGE(TAG, "Failed to initialize WiFi AP"); + return; + } + + // Initialize WiFi driver + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + + // Configure WiFi AP + wifi_config_t ap_wifi_config = { + .ap = { + .ssid = CONFIG_EXAMPLE_WIFI_AP_SSID, + .ssid_len = strlen(CONFIG_EXAMPLE_WIFI_AP_SSID), + .channel = CONFIG_EXAMPLE_WIFI_AP_CHANNEL, + .password = CONFIG_EXAMPLE_WIFI_AP_PASS, + .max_connection = 4, + .authmode = example_wifi_ap_authmode(), + .pmf_cfg = { + .required = false, + }, + }, + }; + if (strlen(CONFIG_EXAMPLE_WIFI_AP_PASS) == 0) { + ap_wifi_config.ap.authmode = WIFI_AUTH_OPEN; + } + + // Set mode and start WiFi + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + + ESP_LOGI(TAG, "Wi-Fi AP started. SSID:%s password:%s channel:%d", + CONFIG_EXAMPLE_WIFI_AP_SSID, CONFIG_EXAMPLE_WIFI_AP_PASS, CONFIG_EXAMPLE_WIFI_AP_CHANNEL); + + // Wait for Ethernet Station to get IP address + xEventGroupWaitBits(event_group, EXAMPLE_ETH_ENDPOINT_GOT_IP_BIT, pdTRUE, pdTRUE, portMAX_DELAY); + + // Update WiFi AP DNS with Ethernet Station DNS if enabled +#if CONFIG_EXAMPLE_WIFI_AP_DHCP_ENABLE_DNS + esp_netif_dns_info_t eth_dns_info; + if (esp_netif_get_dns_info(eth_netif, ESP_NETIF_DNS_MAIN, ð_dns_info) == ESP_OK) { + esp_netif_dns_info_t ap_dns_info = {}; + ap_dns_info.ip.type = ESP_IPADDR_TYPE_V4; + ap_dns_info.ip.u_addr.ip4 = eth_dns_info.ip.u_addr.ip4; + esp_err_t err = esp_netif_set_dns_info(ap_netif, ESP_NETIF_DNS_MAIN, &ap_dns_info); + if (err == ESP_OK) { + ESP_LOGE(TAG, "Updated Wi-Fi AP DNS with Ethernet Station DNS: " IPSTR, IP2STR(ð_dns_info.ip.u_addr.ip4)); + } + } +#endif // CONFIG_EXAMPLE_WIFI_AP_DHCP_ENABLE_DNS + +#if CONFIG_LWIP_IPV4_NAPT + // Setup NAPT (Network Address Port Translation) if enabled + ESP_ERROR_CHECK(esp_netif_napt_enable(ap_netif)); + ESP_LOGI(TAG, "NAPT enabled on Wi-Fi AP"); +#endif +} diff --git a/examples/esp_netif/eth_endpoint_wifi_ap/main/idf_component.yml b/examples/esp_netif/eth_endpoint_wifi_ap/main/idf_component.yml new file mode 100644 index 0000000000..45e2426f1c --- /dev/null +++ b/examples/esp_netif/eth_endpoint_wifi_ap/main/idf_component.yml @@ -0,0 +1,6 @@ +dependencies: + idf: ">=5.0" + espressif/ethernet_init: + version: "~1.2.0" + rules: + - if: "target != linux" diff --git a/examples/esp_netif/eth_endpoint_wifi_ap/sdkconfig.defaults b/examples/esp_netif/eth_endpoint_wifi_ap/sdkconfig.defaults new file mode 100644 index 0000000000..faf8b08a93 --- /dev/null +++ b/examples/esp_netif/eth_endpoint_wifi_ap/sdkconfig.defaults @@ -0,0 +1,25 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +# WiFi AP Configuration +CONFIG_EXAMPLE_WIFI_AP_SSID="myssid-ext" +CONFIG_EXAMPLE_WIFI_AP_PASS="mypassword" +# +# WiFi AP IP Configuration +CONFIG_EXAMPLE_WIFI_AP_IP_ADDR="192.168.5.1" +CONFIG_EXAMPLE_WIFI_AP_NETMASK="255.255.255.0" +CONFIG_EXAMPLE_WIFI_AP_GW="192.168.5.1" +CONFIG_EXAMPLE_WIFI_AP_CHANNEL=1 +CONFIG_EXAMPLE_WIFI_AP_AUTHMODE_WPA_WPA2_PSK=y +# +# WiFi AP DHCP Server Configuration +CONFIG_EXAMPLE_WIFI_AP_DHCP_LEASE_TIME=120 +CONFIG_EXAMPLE_WIFI_AP_DHCP_START_ADDR="192.168.5.2" +CONFIG_EXAMPLE_WIFI_AP_DHCP_END_ADDR="192.168.5.100" +CONFIG_EXAMPLE_WIFI_AP_DHCP_ENABLE_DNS=y +CONFIG_EXAMPLE_WIFI_AP_DHCP_DNS_MAIN="192.168.5.1" +CONFIG_EXAMPLE_WIFI_AP_DHCP_DNS_BACKUP="8.8.8.8" +# +# NAPT Configuration (optional) +CONFIG_LWIP_IP_FORWARD=y +CONFIG_LWIP_IPV4_NAPT=y diff --git a/examples/esp_netif/eth_endpoint_wifi_sta/CMakeLists.txt b/examples/esp_netif/eth_endpoint_wifi_sta/CMakeLists.txt new file mode 100644 index 0000000000..c37d5046ba --- /dev/null +++ b/examples/esp_netif/eth_endpoint_wifi_sta/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(eth_endpoint_wifi_sta) diff --git a/examples/esp_netif/eth_endpoint_wifi_sta/README.md b/examples/esp_netif/eth_endpoint_wifi_sta/README.md new file mode 100644 index 0000000000..2080e993ef --- /dev/null +++ b/examples/esp_netif/eth_endpoint_wifi_sta/README.md @@ -0,0 +1,112 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | + +# Ethernet endpoint + Wi-Fi STA example + +## Overview + +This example initializes **two network interfaces simultaneously**: an **Ethernet interface** as an **endpoint** (DHCP client on the wired port—not Wi-Fi “station”), and a **Wi-Fi station (STA)** interface. Both operate as **DHCP clients** on their respective networks. The ESP connects to a Wi-Fi access point and to an Ethernet network, enabling dual connectivity. + +The example uses the `ethernet_init` component from [esp-eth-drivers](https://github.com/espressif/esp-eth-drivers) to initialize the Ethernet driver based on menuconfig (internal/SPI PHY, etc.) and creates two `esp_netif` instances: one for the Ethernet endpoint (configured with `ESP_NETIF_DEFAULT_ETH`) and one for Wi-Fi STA (configured with `esp_netif_create_default_wifi_sta()`). + +### Key Features + +- **Dual Interface Support**: Simultaneously operates Ethernet and Wi-Fi STA interfaces +- **DHCP Client**: Both interfaces obtain IP addresses automatically via DHCP +- **Independent Connectivity**: Each interface connects to its respective network independently +- **NAPT Support**: Optional Network Address Port Translation; when the lwIP NAPT (and IPv4 forwarding) features are enabled in menuconfig, NAPT is enabled on the **Ethernet endpoint** interface +- **Event-Driven Architecture**: Uses ESP event system to handle network events for both interfaces +- **DNS Support**: Receives and displays DNS server information from DHCP for both interfaces + +## How to use example + +### Configure the project + +You can configure the project using `idf.py menuconfig` or by editing the source code directly. + +#### Ethernet Configuration + +Configure the Ethernet PHY/SPI settings under "Ethernet" in menuconfig (see the [ethernet_init](https://github.com/espressif/esp-eth-drivers/tree/master/ethernet_init) component documentation). The example depends on the managed component `espressif/ethernet_init` (see `main/idf_component.yml`). + +#### Wi-Fi Configuration + +Configure the Wi-Fi credentials by editing the following defines in `main/eth_endpoint_wifi_sta.c`: + +- **EXAMPLE_ESP_WIFI_SSID**: Wi-Fi network SSID (default: `"myssid"`) +- **EXAMPLE_ESP_WIFI_PASS**: Wi-Fi network password (default: `"mypassword"`) + +Alternatively, you can configure these via menuconfig if you add Kconfig options to the example. + +### Build, Flash, and Run + +``` +idf.py -p PORT build flash monitor +``` + +Replace PORT with your serial port. + +**Prerequisites:** +- Connect an Ethernet cable to the ESP's Ethernet port (connected to a network with DHCP server) +- Ensure a Wi-Fi access point is available with the configured SSID and password + +Both interfaces obtain IP addresses via DHCP. The example waits until **both** the Wi-Fi STA and the Ethernet endpoint have received an IPv4 address before continuing (see `xEventGroupWaitBits` in `app_main`). + +## Example Output + +``` +I (2706) eth_endpoint_wifi_sta: Wi-Fi STA initialized. SSID:xxxxx password:xxxxx +I (2706) eth_endpoint_wifi_sta: Wi-Fi Event: base=WIFI_EVENT, id=2 +I (2716) eth_endpoint_wifi_sta: Wi-Fi STA started +I (2856) wifi:new:<1,0>, old:<1,0>, ap:<255,255>, sta:<1,0>, prof:1, snd_ch_cfg:0x0 +I (2856) wifi:state: init -> auth (0xb0) +I (2856) wifi:state: auth -> assoc (0x0) +I (2866) wifi:state: assoc -> run (0x10) +I (3006) wifi:connected with xxxxx, aid = 22, channel 1, BW20, bssid = xx:xx:xx:xx:xx:xx +I (3006) wifi:security: WPA2-PSK, phy: bgn, rssi: -58 +I (3006) wifi:pm start, type: 1 +I (3016) wifi:dp: 1, bi: 102400, li: 3, scale listen interval from 307200 us to 307200 us +I (3026) eth_endpoint_wifi_sta: Wi-Fi Event: base=WIFI_EVENT, id=4 +I (3026) eth_endpoint_wifi_sta: Wi-Fi STA connected +I (3036) wifi:idx:0 (ifx:0, xx:xx:xx:xx:xx:xx), tid:6, ssn:1, winSize:64 +I (3146) wifi:AP's beacon interval = 102400 us, DTIM period = 1 +I (4036) eth_endpoint_wifi_sta: Wi-Fi Got IP Address +I (4036) eth_endpoint_wifi_sta: Event: base=IP_EVENT, id=0 +I (4036) eth_endpoint_wifi_sta: ~~~~~~~~~~~ +I (4036) eth_endpoint_wifi_sta: STAIP:xxx.xxx.xxx.xxx +I (4036) eth_endpoint_wifi_sta: STAMASK:255.255.255.0 +I (4036) eth_endpoint_wifi_sta: STAGW:xxx.xxx.xxx.xxx +I (4046) eth_endpoint_wifi_sta: DHCP_DNS_MAIN:xxx.xxx.xxx.xxx +I (4046) eth_endpoint_wifi_sta: DHCP_DNS_BACKUP:xxx.xxx.xxx.xxx +I (4056) eth_endpoint_wifi_sta: ~~~~~~~~~~~ +I (4056) esp_netif_handlers: sta ip: xxx.xxx.xxx.xxx, mask: 255.255.255.0, gw: xxx.xxx.xxx.xxx +I (4066) main_task: Returned from app_main() +I (4966) eth_endpoint_wifi_sta: Ethernet Link Up +I (4966) eth_endpoint_wifi_sta: Ethernet HW Addr xx:xx:xx:xx:xx:xx +I (4966) eth_endpoint_wifi_sta: Ethernet Got IP Address +I (4966) eth_endpoint_wifi_sta: Event: base=IP_EVENT, id=4 +I (4966) eth_endpoint_wifi_sta: ~~~~~~~~~~~ +I (4966) eth_endpoint_wifi_sta: ETHIP:xxx.xxx.xxx.xxx +I (4966) eth_endpoint_wifi_sta: ETHMASK:255.255.255.0 +I (4966) eth_endpoint_wifi_sta: ETHGW:xxx.xxx.xxx.xxx +I (4976) eth_endpoint_wifi_sta: DHCP_DNS_MAIN:xxx.xxx.xxx.xxx +I (4976) eth_endpoint_wifi_sta: DHCP_DNS_BACKUP:xxx.xxx.xxx.xxx +I (4986) eth_endpoint_wifi_sta: ~~~~~~~~~~~ +I (4986) esp_netif_handlers: eth ip: xxx.xxx.xxx.xxx, mask: 255.255.255.0, gw: xxx.xxx.xxx.xxx +``` + +After both interfaces connect, they will each have their own IP address obtained via DHCP from their respective networks. + +## Troubleshooting + +- **Ethernet Link Not Up**: Ensure the Ethernet cable is properly connected and the PHY configuration matches your hardware. Verify that the Ethernet network has a DHCP server running. +- **Wi-Fi Not Connecting**: Check that the SSID and password are correct in the source code. Ensure the Wi-Fi access point is within range and operational. +- **No IP Address Received**: Verify that DHCP servers are running on both networks. Check network connectivity and DHCP server availability. +- **Waiting for IPs**: The example blocks until both Wi-Fi STA and Ethernet endpoint have IPv4 addresses; link timing can differ per network. +- **Hardware Issues**: For hardware and driver issues, refer to ESP-IDF Ethernet and Wi-Fi documentation and the upper level [README](../README.md) + +## Configuration Notes + +- Both interfaces operate independently and can have different IP addresses, gateways, and DNS servers +- The example waits for both Wi-Fi STA and Ethernet endpoint IPv4 before leaving `app_main()` +- DNS server information is obtained from DHCP for both interfaces and displayed in the logs +- Wi-Fi credentials are currently hardcoded in the source file (`EXAMPLE_ESP_WIFI_SSID` and `EXAMPLE_ESP_WIFI_PASS`) diff --git a/examples/esp_netif/eth_endpoint_wifi_sta/main/CMakeLists.txt b/examples/esp_netif/eth_endpoint_wifi_sta/main/CMakeLists.txt new file mode 100644 index 0000000000..bd24506c94 --- /dev/null +++ b/examples/esp_netif/eth_endpoint_wifi_sta/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "eth_endpoint_wifi_sta.c" + INCLUDE_DIRS "." + REQUIRES ethernet_init esp_netif esp_eth esp_wifi nvs_flash) diff --git a/examples/esp_netif/eth_endpoint_wifi_sta/main/Kconfig.projbuild b/examples/esp_netif/eth_endpoint_wifi_sta/main/Kconfig.projbuild new file mode 100644 index 0000000000..158206e283 --- /dev/null +++ b/examples/esp_netif/eth_endpoint_wifi_sta/main/Kconfig.projbuild @@ -0,0 +1,36 @@ +menu "Example Configuration" + + config EXAMPLE_ESP_WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + + config EXAMPLE_ESP_WIFI_PASS + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + + choice EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD + prompt "WiFi STA scan authentication threshold" + default EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA2_PSK + help + Minimum authentication mode accepted when scanning and connecting as a station. + Lower this to connect to older networks (WEP/WPA). + + config EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_OPEN + bool "Open" + config EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WEP + bool "WEP" + config EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA_PSK + bool "WPA PSK" + config EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA2_PSK + bool "WPA2 PSK" + config EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA_WPA2_PSK + bool "WPA/WPA2 PSK" + config EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA3_PSK + bool "WPA3 PSK" + endchoice + +endmenu diff --git a/examples/esp_netif/eth_endpoint_wifi_sta/main/eth_endpoint_wifi_sta.c b/examples/esp_netif/eth_endpoint_wifi_sta/main/eth_endpoint_wifi_sta.c new file mode 100644 index 0000000000..2ef63ec73f --- /dev/null +++ b/examples/esp_netif/eth_endpoint_wifi_sta/main/eth_endpoint_wifi_sta.c @@ -0,0 +1,245 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* Ethernet endpoint + Wi-Fi STA example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_netif.h" +#include "esp_eth.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_wifi_types.h" +#include "nvs_flash.h" +#include "ethernet_init.h" +#include "sdkconfig.h" + + +#define EXAMPLE_ESP_WIFI_SSID CONFIG_EXAMPLE_ESP_WIFI_SSID +#define EXAMPLE_ESP_WIFI_PASS CONFIG_EXAMPLE_ESP_WIFI_PASS + +/** Event group bit: set when Wi-Fi station receives IPv4 (IP_EVENT_STA_GOT_IP). */ +#define EXAMPLE_WIFI_STA_GOT_IP_BIT (1U << 0) +/** Event group bit: set when Ethernet endpoint receives IPv4 (IP_EVENT_ETH_GOT_IP). */ +#define EXAMPLE_ETH_ENDPOINT_GOT_IP_BIT (1U << 1) + +static const char *TAG = "eth_endpoint_wifi_sta"; + +static EventGroupHandle_t event_group = NULL; + +/** Event handler for Ethernet events */ +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 ethernet driver handle from event data */ + esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data; + + switch (event_id) { + case ETHERNET_EVENT_CONNECTED: + esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); + ESP_LOGI(TAG, "Ethernet Link Up"); + ESP_LOGI(TAG, "Ethernet HW Addr %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 Link Down"); + break; + case ETHERNET_EVENT_START: + ESP_LOGI(TAG, "Ethernet Started"); + break; + case ETHERNET_EVENT_STOP: + ESP_LOGI(TAG, "Ethernet Stopped"); + break; + default: + break; + } +} + + +static void wifi_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (event_base == WIFI_EVENT) { + ESP_LOGI(TAG, "Wi-Fi Event: base=%s, id=%ld", event_base, event_id); + switch (event_id) { + case WIFI_EVENT_STA_START: + ESP_LOGI(TAG, "Wi-Fi STA started"); + esp_wifi_connect(); + break; + case WIFI_EVENT_STA_STOP: + ESP_LOGI(TAG, "Wi-Fi STA stopped"); + break; + case WIFI_EVENT_STA_CONNECTED: + ESP_LOGI(TAG, "Wi-Fi STA connected"); + break; + case WIFI_EVENT_STA_DISCONNECTED: + ESP_LOGI(TAG, "connect to the AP fail... retrying"); + esp_wifi_connect(); + break; + default: + ESP_LOGW(TAG, "Unhandled Wi-Fi event: id=%ld", event_id); + break; + } + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; + const esp_netif_ip_info_t *ip_info = &event->ip_info; + esp_netif_t *netif = event->esp_netif; + esp_netif_dns_info_t dns_info; + + ESP_LOGI(TAG, "Wi-Fi Got IP Address"); + ESP_LOGI(TAG, "Event: base=%s, id=%ld", event_base, event_id); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "STAIP:" IPSTR, IP2STR(&ip_info->ip)); + ESP_LOGI(TAG, "STAMASK:" IPSTR, IP2STR(&ip_info->netmask)); + ESP_LOGI(TAG, "STAGW:" IPSTR, IP2STR(&ip_info->gw)); + + // Print DHCP DNS information + if (esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info) == ESP_OK) { + ESP_LOGI(TAG, "DHCP_DNS_MAIN:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + } + if (esp_netif_get_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_info) == ESP_OK) { + ESP_LOGI(TAG, "DHCP_DNS_BACKUP:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + } + + ESP_LOGI(TAG, "~~~~~~~~~~~"); + + xEventGroupSetBits(event_group, EXAMPLE_WIFI_STA_GOT_IP_BIT); + } +} + + +/** Event handler for IP_EVENT_ETH_GOT_IP */ +static void got_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; + esp_netif_t *netif = event->esp_netif; + esp_netif_dns_info_t dns_info; + + ESP_LOGI(TAG, "Ethernet Got IP Address"); + ESP_LOGI(TAG, "Event: base=%s, id=%ld", event_base, event_id); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip)); + ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask)); + ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw)); + + // Print DHCP DNS information + if (esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info) == ESP_OK) { + ESP_LOGI(TAG, "DHCP_DNS_MAIN:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + } + if (esp_netif_get_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_info) == ESP_OK) { + ESP_LOGI(TAG, "DHCP_DNS_BACKUP:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + } + + ESP_LOGI(TAG, "~~~~~~~~~~~"); + + xEventGroupSetBits(event_group, EXAMPLE_ETH_ENDPOINT_GOT_IP_BIT); +} + +static wifi_auth_mode_t example_wifi_sta_authmode_threshold(void) +{ +#if CONFIG_EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_OPEN + return WIFI_AUTH_OPEN; +#elif CONFIG_EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WEP + return WIFI_AUTH_WEP; +#elif CONFIG_EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA_PSK + return WIFI_AUTH_WPA_PSK; +#elif CONFIG_EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA2_PSK + return WIFI_AUTH_WPA2_PSK; +#elif CONFIG_EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA_WPA2_PSK + return WIFI_AUTH_WPA_WPA2_PSK; +#elif CONFIG_EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA3_PSK + return WIFI_AUTH_WPA3_PSK; +#else + return WIFI_AUTH_WPA2_PSK; +#endif +} + +static void wifi_init_sta(void) +{ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL)); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + wifi_config_t wifi_config = { + .sta = { + .ssid = EXAMPLE_ESP_WIFI_SSID, + .password = EXAMPLE_ESP_WIFI_PASS, + .threshold.authmode = example_wifi_sta_authmode_threshold(), + }, + }; + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + + ESP_LOGI(TAG, "Wi-Fi STA initialized. SSID:%s password:%s", EXAMPLE_ESP_WIFI_SSID, EXAMPLE_ESP_WIFI_PASS); +} + +void app_main(void) +{ + // Initialize Ethernet driver + uint8_t eth_port_cnt = 0; + esp_eth_handle_t *eth_handles = NULL; + + event_group = xEventGroupCreate(); + + ESP_ERROR_CHECK(esp_netif_init()); // Initialize TCP/IP network interface (should be called only once in application) + ESP_ERROR_CHECK(esp_event_loop_create_default()); // Create default event loop that running in background + + ESP_ERROR_CHECK(ethernet_init_all(ð_handles, ð_port_cnt)); + + if (eth_port_cnt == 0) { + ESP_LOGE(TAG, "No Ethernet interface initialized"); + return; + } + if (eth_port_cnt > 1) { + ESP_LOGW(TAG, "%u Ethernet interfaces initialized; this example only uses the first one", eth_port_cnt); + } + + // Register user defined event handers + 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, &got_ip_event_handler, NULL)); + + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); // default esp-netif configuration parameters. + esp_netif_t *eth_netif = esp_netif_new(&cfg); + assert(eth_netif); + + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[0]))); // Attach Ethernet driver to TCP/IP stack + ESP_ERROR_CHECK(esp_eth_start(eth_handles[0])); + + // Initialize wifi + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL)); + esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta(); + assert(sta_netif); + wifi_init_sta(); + xEventGroupWaitBits(event_group, + EXAMPLE_WIFI_STA_GOT_IP_BIT | EXAMPLE_ETH_ENDPOINT_GOT_IP_BIT, + pdTRUE, pdTRUE, portMAX_DELAY); + +#if CONFIG_LWIP_IPV4_NAPT + // Setup NAPT (Network Address Port Translation) if enabled + ESP_ERROR_CHECK(esp_netif_napt_enable(eth_netif)); + ESP_LOGI(TAG, "NAPT enabled on Ethernet"); +#endif +} diff --git a/examples/esp_netif/eth_endpoint_wifi_sta/main/idf_component.yml b/examples/esp_netif/eth_endpoint_wifi_sta/main/idf_component.yml new file mode 100644 index 0000000000..45e2426f1c --- /dev/null +++ b/examples/esp_netif/eth_endpoint_wifi_sta/main/idf_component.yml @@ -0,0 +1,6 @@ +dependencies: + idf: ">=5.0" + espressif/ethernet_init: + version: "~1.2.0" + rules: + - if: "target != linux" diff --git a/examples/esp_netif/eth_endpoint_wifi_sta/sdkconfig.defaults b/examples/esp_netif/eth_endpoint_wifi_sta/sdkconfig.defaults new file mode 100644 index 0000000000..6805563406 --- /dev/null +++ b/examples/esp_netif/eth_endpoint_wifi_sta/sdkconfig.defaults @@ -0,0 +1,11 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +# WiFi Station Configuration +CONFIG_EXAMPLE_ESP_WIFI_SSID="myssid" +CONFIG_EXAMPLE_ESP_WIFI_PASS="mypassword" +CONFIG_EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA2_PSK=y +# +# NAPT Configuration (optional) +CONFIG_LWIP_IP_FORWARD=y +CONFIG_LWIP_IPV4_NAPT=y diff --git a/examples/esp_netif/eth_gateway/CMakeLists.txt b/examples/esp_netif/eth_gateway/CMakeLists.txt new file mode 100644 index 0000000000..71cb7c95ac --- /dev/null +++ b/examples/esp_netif/eth_gateway/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(eth_gateway) diff --git a/examples/esp_netif/eth_gateway/README.md b/examples/esp_netif/eth_gateway/README.md new file mode 100644 index 0000000000..b4ddba5056 --- /dev/null +++ b/examples/esp_netif/eth_gateway/README.md @@ -0,0 +1,92 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | + +# Ethernet gateway example + +## Overview + +This example initializes a **single** Ethernet interface and runs a **DHCP server** on it (**Ethernet gateway** role: downstream subnet on the cable, not a Wi-Fi access point). Devices that connect to the Ethernet port receive an IP address automatically via DHCP (e.g. in the `192.168.5.x` range). The ESP uses a static IP on that interface (default `192.168.5.1`). + +The example uses the `ethernet_init` component from [esp-eth-drivers](https://github.com/espressif/esp-eth-drivers) to initialize the Ethernet driver based on menuconfig (internal/SPI PHY, etc.) and a single `esp_netif` instance configured with `ESP_NETIF_DHCP_SERVER` and a static gateway IP. + +### Key Features + +- **DHCP Server**: Automatically assigns IP addresses to connected devices +- **Configurable IP Pool**: Set the range of IP addresses to assign (default: `192.168.5.2` - `192.168.5.100`) +- **DHCP Lease Time**: Configurable lease duration for assigned IP addresses +- **DNS Support**: Optional DNS server configuration for DHCP clients +- **Static gateway IP**: ESP device uses a static IP address on the Ethernet interface (default: `192.168.5.1`) + +## How to use example + +### Configure the project + +You can configure the project using `idf.py menuconfig` or by using the provided `sdkconfig.defaults` file which contains default configuration values. + +#### Ethernet Configuration + +Configure the Ethernet PHY/SPI settings under "Ethernet" in menuconfig (see the [ethernet_init](https://github.com/espressif/esp-eth-drivers/tree/master/ethernet_init) component documentation). The example depends on the managed component `espressif/ethernet_init` (see `main/idf_component.yml`). + +#### Example Configuration + +Under "Example Configuration" menu, you can configure: + +- **Ethernet gateway IP address**: Static IP address for the ESP device (default: `192.168.5.1`) +- **Ethernet gateway netmask**: Subnet mask for the network (default: `255.255.255.0`) +- **Ethernet gateway default gateway**: Default gateway IP (default: `192.168.5.1`) +- **DHCP Lease Time**: Duration of IP address leases in units of `LWIP_DHCPS_LEASE_UNIT` (default: 120, which equals 7200 seconds or 2 hours) +- **DHCP IP Pool Start Address**: First IP address in the DHCP pool (default: `192.168.5.2`) +- **DHCP IP Pool End Address**: Last IP address in the DHCP pool (default: `192.168.5.100`) +- **Enable DNS in DHCP Offers**: Enable DNS server information in DHCP offers (default: enabled) +- **Primary DNS Server**: Primary DNS server IP (default: `192.168.5.1`) +- **Backup DNS Server**: Backup DNS server IP (default: `8.8.8.8`) + +### Build, Flash, and Run + +``` +idf.py -p PORT build flash monitor +``` + +Replace PORT with your serial port. Connect a PC or another device to the ESP's Ethernet port; the device should obtain an IP via DHCP (e.g. `192.168.5.2`). You can then ping the ESP at `192.168.5.1`. + +## Example Output + +``` +I (3635) ethernet_init: Ethernet(IP101[23,18]) Started +I (3635) eth_gateway: Ethernet Started +I (3635) eth_gateway: Ethernet gateway ready (static IP: 192.168.5.1, netmask: 255.255.255.0). DHCP starts when link is up. +I (3635) esp_netif_lwip: DHCP server started on interface ETH_GW with IP: 192.168.5.1 +I (3645) ethernet_init: Ethernet(IP101[23,18]) Link Up +I (3645) ethernet_init: Ethernet(IP101[23,18]) HW Addr 58:bf:25:e0:41:03 +I (3655) eth_gateway: Ethernet Link Up +I (3655) eth_gateway: Ethernet HW Addr 58:bf:25:e0:41:03 +I (3665) eth_gateway: Connect a device to the Ethernet port to get an IP via DHCP +I (3665) main_task: Returned from app_main() +I (4645) esp_netif_lwip: DHCP server assigned IP to a client, IP is: 192.168.5.2 +I (25635) ethernet_init: Ethernet(IP101[23,18]) Link Down +I (25635) eth_gateway: Ethernet Link Down +I (27635) ethernet_init: Ethernet(IP101[23,18]) Link Up +I (27635) ethernet_init: Ethernet(IP101[23,18]) HW Addr 58:bf:25:e0:41:03 +I (27635) eth_gateway: Ethernet Link Up +I (27635) eth_gateway: Ethernet HW Addr 58:bf:25:e0:41:03 +I (28215) esp_netif_lwip: DHCP server assigned IP to a client, IP is: 192.168.5.2 +I (3628765) esp_netif_lwip: DHCP server assigned IP to a client, IP is: 192.168.5.2 +I (7228765) esp_netif_lwip: DHCP server assigned IP to a client, IP is: 192.168.5.2 +``` + +After connecting a device, it will receive an IP in the `192.168.5.0/24` range and can reach the ESP at `192.168.5.1`. + +## Troubleshooting + +- **Ethernet Link Not Up**: Ensure the Ethernet cable is properly connected and the PHY configuration matches your hardware +- **Client Not Getting IP**: Check that the DHCP server started successfully (look for "DHCP server started" in the logs) +- **IP Pool Exhausted**: If you have more than 99 devices, increase the DHCP IP pool range in menuconfig +- **DNS Issues**: If DNS is enabled but not working, verify the DNS server addresses are correct +- **Hardware Issues**: For hardware and driver issues, refer to ESP-IDF Ethernet documentation and the upper level [README](../README.md) + +## Configuration Notes + +- The maximum DHCP pool size is 100 addresses (`DHCPS_MAX_LEASE`) +- DHCP lease time is specified in units of `LWIP_DHCPS_LEASE_UNIT` (default 60 seconds) +- The ESP device always uses a static IP address (configured via menuconfig) +- DNS server information is optional and can be disabled in menuconfig diff --git a/examples/esp_netif/eth_gateway/main/CMakeLists.txt b/examples/esp_netif/eth_gateway/main/CMakeLists.txt new file mode 100644 index 0000000000..a7900b6fbe --- /dev/null +++ b/examples/esp_netif/eth_gateway/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "eth_gateway.c" + INCLUDE_DIRS "." + REQUIRES ethernet_init esp_netif esp_eth nvs_flash) diff --git a/examples/esp_netif/eth_gateway/main/Kconfig.projbuild b/examples/esp_netif/eth_gateway/main/Kconfig.projbuild new file mode 100644 index 0000000000..4ad7a1aedc --- /dev/null +++ b/examples/esp_netif/eth_gateway/main/Kconfig.projbuild @@ -0,0 +1,69 @@ +menu "Example Configuration" + + config EXAMPLE_ETH_GATEWAY_IP_ADDR + string "Ethernet gateway IP address" + default "192.168.5.1" + help + IP address for the Ethernet gateway (DHCP server). + This is the static IP address assigned to the Ethernet interface. + + config EXAMPLE_ETH_GATEWAY_NETMASK + string "Ethernet gateway netmask" + default "255.255.255.0" + help + Subnet mask for the Ethernet gateway network. + + config EXAMPLE_ETH_GATEWAY_GW + string "Ethernet gateway default gateway" + default "192.168.5.1" + help + Default gateway IP address for the Ethernet gateway network. + Typically set to the same IP as the gateway interface itself. + + config EXAMPLE_ETH_GATEWAY_DHCP_LEASE_TIME + int "DHCP Lease Time" + default 120 + help + DHCP lease time in units of LWIP_DHCPS_LEASE_UNIT (default 60 seconds). + Example: Setting this to 120 means a 7200-second (2 hour) lease time. + Setting this to 1 means a 60-second lease time. + + config EXAMPLE_ETH_GATEWAY_DHCP_START_ADDR + string "DHCP IP Pool Start Address" + default "192.168.5.2" + help + Starting IP address of the DHCP address pool. + This is the first IP address that will be assigned to DHCP clients. + + config EXAMPLE_ETH_GATEWAY_DHCP_END_ADDR + string "DHCP IP Pool End Address" + default "192.168.5.100" + help + Ending IP address of the DHCP address pool. + This is the last IP address that will be assigned to DHCP clients. + Maximum pool size is 100 addresses (DHCPS_MAX_LEASE). + + config EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + bool "Enable DNS in DHCP Offers" + default y + help + Enable DNS server information in DHCP offers. + When enabled, DHCP clients will receive DNS server addresses. + + config EXAMPLE_ETH_GATEWAY_DHCP_DNS_MAIN + string "Primary DNS Server" + default "192.168.5.1" + depends on EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + help + Primary DNS server IP address to be provided to DHCP clients. + Typically set to the gateway IP address or a public DNS server. + + config EXAMPLE_ETH_GATEWAY_DHCP_DNS_BACKUP + string "Backup DNS Server" + default "8.8.8.8" + depends on EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + help + Backup DNS server IP address to be provided to DHCP clients. + Typically set to a public DNS server like 8.8.8.8 (Google DNS). + +endmenu diff --git a/examples/esp_netif/eth_gateway/main/eth_gateway.c b/examples/esp_netif/eth_gateway/main/eth_gateway.c new file mode 100644 index 0000000000..e8f893af3c --- /dev/null +++ b/examples/esp_netif/eth_gateway/main/eth_gateway.c @@ -0,0 +1,180 @@ +/* + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + * + * Ethernet gateway example + * + * This example initializes a single Ethernet interface and runs a DHCP server + * on it (Ethernet gateway role). Devices connecting to the Ethernet port will + * receive an IP address via DHCP (e.g. 192.168.5.x). + */ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "esp_netif.h" +#include "esp_eth.h" +#include "esp_event.h" +#include "esp_log.h" +#include "nvs_flash.h" +#include "dhcpserver/dhcpserver.h" +#include "dhcpserver/dhcpserver_options.h" +#include "ethernet_init.h" + +static const char *TAG = "eth_gateway"; + +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}; + esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data; + esp_netif_t *eth_netif = (esp_netif_t *)arg; + + switch (event_id) { + case ETHERNET_EVENT_CONNECTED: + esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); + ESP_LOGI(TAG, "Ethernet Link Up"); + ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x", + mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); + { + esp_err_t err = esp_netif_dhcps_start(eth_netif); + if (err != ESP_OK) { + ESP_LOGE(TAG, "DHCP server start: %s", esp_err_to_name(err)); + } else { + ESP_LOGI(TAG, "DHCP server started on %s", esp_netif_get_desc(eth_netif)); + } + } + break; + /* Optional: call esp_netif_dhcps_stop(eth_netif) here if link-down should tear down DHCP. */ + case ETHERNET_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "Ethernet Link Down"); + break; + case ETHERNET_EVENT_START: + ESP_LOGI(TAG, "Ethernet Started"); + break; + case ETHERNET_EVENT_STOP: + ESP_LOGI(TAG, "Ethernet Stopped"); + break; + default: + break; + } +} + +void app_main(void) +{ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + uint8_t eth_port_cnt = 0; + esp_eth_handle_t *eth_handles = NULL; + ESP_ERROR_CHECK(ethernet_init_all(ð_handles, ð_port_cnt)); + + if (eth_port_cnt == 0) { + ESP_LOGE(TAG, "No Ethernet interface initialized"); + return; + } + if (eth_port_cnt > 1) { + ESP_LOGW(TAG, "%u Ethernet interfaces initialized; this example only uses the first one", eth_port_cnt); + } + + /* Static IP configuration for Ethernet gateway */ + static esp_netif_ip_info_t s_eth_gw_ip_info; + memset(&s_eth_gw_ip_info, 0, sizeof(esp_netif_ip_info_t)); + + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_GATEWAY_IP_ADDR, &s_eth_gw_ip_info.ip) != ESP_OK) { + ESP_LOGE(TAG, "Invalid IP address: %s", CONFIG_EXAMPLE_ETH_GATEWAY_IP_ADDR); + return; + } + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_GATEWAY_NETMASK, &s_eth_gw_ip_info.netmask) != ESP_OK) { + ESP_LOGE(TAG, "Invalid netmask: %s", CONFIG_EXAMPLE_ETH_GATEWAY_NETMASK); + return; + } + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_GATEWAY_GW, &s_eth_gw_ip_info.gw) != ESP_OK) { + ESP_LOGE(TAG, "Invalid gateway: %s", CONFIG_EXAMPLE_ETH_GATEWAY_GW); + return; + } + + static esp_netif_inherent_config_t inherent_config = { + .flags = ESP_NETIF_DHCP_SERVER, + ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(mac) + .ip_info = &s_eth_gw_ip_info, + .get_ip_event = 0, + .lost_ip_event = 0, + .if_key = "ETH_GW", + .if_desc = "eth_gw", + .route_prio = 50, + .bridge_info = NULL + }; + + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); + cfg.base = &inherent_config; + + esp_netif_t *eth_netif = esp_netif_new(&cfg); + ESP_ERROR_CHECK(eth_netif != NULL ? ESP_OK : ESP_FAIL); + + /* Configure DHCP server options */ + // Set lease time + uint32_t lease_time = CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_LEASE_TIME; + esp_err_t err = esp_netif_dhcps_option(eth_netif, ESP_NETIF_OP_SET, IP_ADDRESS_LEASE_TIME, &lease_time, sizeof(lease_time)); + if (err != ESP_OK) { + ESP_LOGE(TAG, "Failed to set DHCP lease time: %s", esp_err_to_name(err)); + } + + // Configure IP address pool + dhcps_lease_t dhcp_lease; + memset(&dhcp_lease, 0, sizeof(dhcps_lease_t)); + dhcp_lease.enable = true; + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_START_ADDR, (esp_ip4_addr_t *)&dhcp_lease.start_ip) != ESP_OK) { + ESP_LOGW(TAG, "Invalid DHCP start address: %s, using default pool", CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_START_ADDR); + } else if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_END_ADDR, (esp_ip4_addr_t *)&dhcp_lease.end_ip) != ESP_OK) { + ESP_LOGW(TAG, "Invalid DHCP end address: %s, using default pool", CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_END_ADDR); + } else { + err = esp_netif_dhcps_option(eth_netif, ESP_NETIF_OP_SET, REQUESTED_IP_ADDRESS, &dhcp_lease, sizeof(dhcps_lease_t)); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to set DHCP IP pool: %s", esp_err_to_name(err)); + } + } + + // Configure DNS servers if enabled +#if CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + dhcps_offer_t dhcps_dns_value = OFFER_DNS; + err = esp_netif_dhcps_option(eth_netif, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &dhcps_dns_value, sizeof(dhcps_dns_value)); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to enable DNS in DHCP offers: %s", esp_err_to_name(err)); + } else { + // Set DNS server addresses + esp_netif_dns_info_t dns_info = {}; + dns_info.ip.type = ESP_IPADDR_TYPE_V4; + + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_DNS_MAIN, &dns_info.ip.u_addr.ip4) == ESP_OK) { + err = esp_netif_set_dns_info(eth_netif, ESP_NETIF_DNS_MAIN, &dns_info); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to set primary DNS: %s", esp_err_to_name(err)); + } + } + + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_DNS_BACKUP, &dns_info.ip.u_addr.ip4) == ESP_OK) { + err = esp_netif_set_dns_info(eth_netif, ESP_NETIF_DNS_BACKUP, &dns_info); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to set backup DNS: %s", esp_err_to_name(err)); + } + } + } +#endif // CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + + ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_netif)); + + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[0]))); + ESP_ERROR_CHECK(esp_eth_start(eth_handles[0])); + + ESP_LOGI(TAG, "Ethernet gateway ready (static IP: " IPSTR ", netmask: " IPSTR "). DHCP starts when link is up.", + IP2STR(&s_eth_gw_ip_info.ip), IP2STR(&s_eth_gw_ip_info.netmask)); + ESP_LOGI(TAG, "Connect a device to the Ethernet port to get an IP via DHCP"); +} diff --git a/examples/esp_netif/eth_gateway/main/idf_component.yml b/examples/esp_netif/eth_gateway/main/idf_component.yml new file mode 100644 index 0000000000..119518cbd3 --- /dev/null +++ b/examples/esp_netif/eth_gateway/main/idf_component.yml @@ -0,0 +1,6 @@ +dependencies: + idf: "*" + espressif/ethernet_init: + version: "~1.2.0" + rules: + - if: "target != linux" diff --git a/examples/esp_netif/eth_gateway/sdkconfig.defaults b/examples/esp_netif/eth_gateway/sdkconfig.defaults new file mode 100644 index 0000000000..576cea007a --- /dev/null +++ b/examples/esp_netif/eth_gateway/sdkconfig.defaults @@ -0,0 +1,15 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +# Ethernet gateway configuration +CONFIG_EXAMPLE_ETH_GATEWAY_IP_ADDR="192.168.5.1" +CONFIG_EXAMPLE_ETH_GATEWAY_NETMASK="255.255.255.0" +CONFIG_EXAMPLE_ETH_GATEWAY_GW="192.168.5.1" + +# DHCP Server Configuration +CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_LEASE_TIME=120 +CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_START_ADDR="192.168.5.2" +CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_END_ADDR="192.168.5.100" +CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS=y +CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_DNS_MAIN="192.168.5.1" +CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_DNS_BACKUP="8.8.8.8" diff --git a/examples/esp_netif/eth_gateway_wifi_ap/CMakeLists.txt b/examples/esp_netif/eth_gateway_wifi_ap/CMakeLists.txt new file mode 100644 index 0000000000..f4c4608654 --- /dev/null +++ b/examples/esp_netif/eth_gateway_wifi_ap/CMakeLists.txt @@ -0,0 +1,8 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(eth_gateway_wifi_ap) diff --git a/examples/esp_netif/eth_gateway_wifi_ap/README.md b/examples/esp_netif/eth_gateway_wifi_ap/README.md new file mode 100644 index 0000000000..54d2cb3c2f --- /dev/null +++ b/examples/esp_netif/eth_gateway_wifi_ap/README.md @@ -0,0 +1,186 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | + +# Ethernet gateway + Wi-Fi AP example + +## Overview + +This example initializes **two network interfaces simultaneously**: an **Ethernet interface** in a **gateway** role (DHCP server on the wired port—not a Wi-Fi “AP”), and a **Wi-Fi interface** operating as an **access point (AP)** with its own DHCP server. Both interfaces provide network access to connected devices via DHCP simultaneously. The Ethernet gateway serves wired devices (e.g. in the `192.168.5.x` range), while the Wi-Fi AP serves wireless devices (e.g. in the `192.168.4.x` range). This configuration enables the ESP to act as a multi-interface router with both wired and wireless access. + +The example uses the `ethernet_init` component from [esp-eth-drivers](https://github.com/espressif/esp-eth-drivers) to initialize the Ethernet driver based on menuconfig (internal/SPI PHY, etc.) and creates two `esp_netif` instances: one for the Ethernet gateway (configured with `ESP_NETIF_DHCP_SERVER` and a static IP) and one for Wi-Fi AP (configured with `esp_netif_create_default_wifi_ap()`). + +### Key Features + +- **Ethernet gateway with DHCP server**: Automatically assigns IP addresses to devices connected via Ethernet +- **Wi-Fi AP with DHCP Server**: Automatically assigns IP addresses to devices connected via Wi-Fi +- **Dual Network Access**: Provides both wired and wireless network access simultaneously +- **NAPT Support**: Optional Network Address Port Translation for routing between interfaces; when the lwIP NAPT (and IPv4 forwarding) features are enabled in menuconfig, NAPT is enabled on **both** the Ethernet and Wi-Fi interfaces +- **Configurable IP Pools**: Set the range of IP addresses to assign via DHCP for both interfaces (Ethernet: `192.168.5.2` - `192.168.5.100`, Wi-Fi: `192.168.4.2` - `192.168.4.100`) +- **DHCP Lease Time**: Configurable lease duration for assigned IP addresses on both interfaces +- **DNS Support**: Optional DNS server configuration for DHCP clients on both interfaces +- **Event-Driven Architecture**: Uses ESP event system to handle network events for both interfaces +- **Client Management**: Tracks and logs client connections/disconnections for both interfaces + +## How to use example + +### Configure the project + +You can configure the project using `idf.py menuconfig` or by using the provided `sdkconfig.defaults` file which contains default configuration values. + +#### Ethernet Configuration + +Configure the Ethernet PHY/SPI settings under "Ethernet" in menuconfig (see the [ethernet_init](https://github.com/espressif/esp-eth-drivers/tree/master/ethernet_init) component documentation). The example depends on the managed component `espressif/ethernet_init` (see `main/idf_component.yml`). + +#### Example Configuration + +Under "Example Configuration" menu, you can configure: + +**Wi-Fi AP Configuration:** +- **WiFi AP SSID**: Wi-Fi network SSID for the access point (default: `"myssid-ext"`) +- **WiFi AP Password**: Wi-Fi network password (default: `"mypassword"`) +- **WiFi AP Channel**: Wi-Fi channel for the access point (default: `1`, range: 1-13) +- **WiFi AP IP Address**: Static IP address for the Wi-Fi AP interface (default: `192.168.4.1`) +- **WiFi AP Netmask**: Subnet mask for the Wi-Fi network (default: `255.255.255.0`) +- **WiFi AP Gateway**: Default gateway IP (default: `192.168.4.1`) +- **DHCP Lease Time**: Duration of IP address leases in units of `LWIP_DHCPS_LEASE_UNIT` (default: 120, which equals 7200 seconds or 2 hours) +- **DHCP IP Pool Start Address**: First IP address in the DHCP pool (default: `192.168.4.2`) +- **DHCP IP Pool End Address**: Last IP address in the DHCP pool (default: `192.168.4.100`) +- **Enable DNS in DHCP Offers**: Enable DNS server information in DHCP offers (default: enabled) +- **Primary DNS Server**: Primary DNS server IP (default: `192.168.4.1`) +- **Backup DNS Server**: Backup DNS server IP (default: `8.8.8.8`) + +**Ethernet gateway configuration:** +- **Ethernet gateway IP address**: Static IP address for the ESP device (default: `192.168.5.1`) +- **Ethernet gateway netmask**: Subnet mask for the network (default: `255.255.255.0`) +- **Ethernet gateway default gateway**: Default gateway IP (default: `192.168.5.1`) +- **DHCP Lease Time**: Duration of IP address leases in units of `LWIP_DHCPS_LEASE_UNIT` (default: 120, which equals 7200 seconds or 2 hours) +- **DHCP IP Pool Start Address**: First IP address in the DHCP pool (default: `192.168.5.2`) +- **DHCP IP Pool End Address**: Last IP address in the DHCP pool (default: `192.168.5.100`) +- **Enable DNS in DHCP Offers**: Enable DNS server information in DHCP offers (default: enabled) +- **Primary DNS Server**: Primary DNS server IP (default: `192.168.5.1`) +- **Backup DNS Server**: Backup DNS server IP (default: `8.8.8.8`) + +**NAPT Configuration:** +- Enable IP forwarding and NAPT in menuconfig under "Component config" → "LWIP" → "Enable IPv4 forwarding" and "Enable IPv4 NAPT" + +### Build, Flash, and Run + +``` +idf.py -p PORT build flash monitor +``` + +Replace PORT with your serial port. + +**Prerequisites:** +- Connect an Ethernet cable to the ESP's Ethernet port (devices connecting will receive IPs via DHCP) +- Ensure Wi-Fi devices can connect to the configured SSID and password + +The example initializes both the Ethernet gateway and Wi-Fi AP simultaneously. Both interfaces operate independently and provide network access to their respective clients via DHCP. + +## Example Output + +``` +I (589) eth_gateway_wifi_ap: Ethernet gateway netif configured. IP: 192.168.5.1, netmask: 255.255.255.0 +I (589) esp_eth.netif.netif_glue: 24:d7:eb:bb:f2:1b +I (589) esp_eth.netif.netif_glue: ethernet attached to netif +I (3799) ethernet_init: Ethernet(IP101[23,18]) Started +I (3799) esp_netif_lwip: DHCP server started on interface ETH_GW with IP: 192.168.5.1 +I (3809) ethernet_init: Ethernet(IP101[23,18]) Link Up +I (3809) ethernet_init: Ethernet(IP101[23,18]) HW Addr 24:d7:eb:bb:f2:1b +I (3819) eth_gateway_wifi_ap: Ethernet Link Up +I (3819) eth_gateway_wifi_ap: Ethernet HW Addr 24:d7:eb:bb:f2:1b +I (3799) eth_gateway_wifi_ap: Ethernet Started +I (3799) eth_gateway_wifi_ap: Ethernet Got IP Address +I (3799) eth_gateway_wifi_ap: ~~~~~~~~~~~ +I (3829) eth_gateway_wifi_ap: IP:192.168.5.1 +I (3829) eth_gateway_wifi_ap: MASK:255.255.255.0 +I (3839) eth_gateway_wifi_ap: GW:192.168.5.1 +I (3839) eth_gateway_wifi_ap: DHCP_DNS_MAIN:192.168.5.1 +I (3839) eth_gateway_wifi_ap: DHCP_DNS_BACKUP:8.8.8.8 +I (3849) eth_gateway_wifi_ap: ~~~~~~~~~~~ +I (3849) eth_gateway_wifi_ap: Ethernet gateway started. Connect a device to the Ethernet port to get an IP via DHCP +I (3859) eth_gateway_wifi_ap: Wi-Fi AP initialized. +I (3879) wifi:wifi driver task: 3ffc41c8, prio:23, stack:6656, core=0 +I (3889) wifi:wifi firmware version: 0f5722b +I (3889) wifi:wifi certification version: v7.0 +I (3889) wifi:config NVS flash: enabled +I (3899) wifi:config nano formatting: disabled +I (3899) wifi:Init data frame dynamic rx buffer num: 32 +I (3899) wifi:Init static rx mgmt buffer num: 5 +I (3909) wifi:Init management short buffer num: 32 +I (3909) wifi:Init dynamic tx buffer num: 32 +I (3919) wifi:Init static rx buffer size: 1600 +I (3919) wifi:Init static rx buffer num: 10 +I (3929) wifi:Init dynamic rx buffer num: 32 +I (3929) wifi_init: rx ba win: 6 +I (3929) wifi_init: accept mbox: 6 +I (3939) wifi_init: tcpip mbox: 32 +I (3939) wifi_init: udp mbox: 6 +I (3939) wifi_init: tcp mbox: 6 +I (3939) wifi_init: tcp tx win: 5760 +I (3949) wifi_init: tcp rx win: 5760 +I (3949) wifi_init: tcp mss: 1440 +I (3949) wifi_init: WiFi IRAM OP enabled +I (3959) wifi_init: WiFi RX IRAM OP enabled +I (3959) phy_init: phy_version 4863,a3a4459,Oct 28 2025,14:30:06 +I (4039) wifi:mode : softAP (24:d7:eb:bb:f2:19) +I (4049) wifi:Total power save buffer number: 16 +I (4049) wifi:Init max length of beacon: 752/752 +I (4049) wifi:Init max length of beacon: 752/752 +I (4049) eth_gateway_wifi_ap: Wi-Fi Event: base=WIFI_EVENT, id=12 +I (4059) eth_gateway_wifi_ap: Wi-Fi AP started +I (4059) eth_gateway_wifi_ap: Wi-Fi AP started. SSID:myssid-ext password:mypassword channel:1 +I (4059) esp_netif_lwip: DHCP server started on interface WIFI_AP_DEF with IP: 192.168.4.1 +I (4079) eth_gateway_wifi_ap: Wi-Fi AP Got IP Address +I (4079) eth_gateway_wifi_ap: ~~~~~~~~~~~ +I (4079) eth_gateway_wifi_ap: IP:192.168.4.1 +I (4089) eth_gateway_wifi_ap: MASK:255.255.255.0 +I (4089) eth_gateway_wifi_ap: GW:192.168.4.1 +I (4089) eth_gateway_wifi_ap: DHCP_DNS_MAIN:192.168.4.1 +I (4099) eth_gateway_wifi_ap: DHCP_DNS_BACKUP:8.8.8.8 +I (4099) eth_gateway_wifi_ap: ~~~~~~~~~~~ +I (4109) eth_gateway_wifi_ap: Wifi AP started. Connect a device to the Wi-Fi AP to get an IP via DHCP +I (4119) eth_gateway_wifi_ap: NAPT enabled on Ethernet gateway +W (4119) esp_netif_lwip: napt disabled on esp_netif:0x3ffc1a9c +I (4129) eth_gateway_wifi_ap: NAPT enabled on Wi-Fi AP +I (4129) main_task: Returned from app_main() +``` + +After initialization: +- Devices connected to the Ethernet port will receive IPs in the `192.168.5.0/24` range via DHCP +- The ESP can be reached at `192.168.5.1` on the Ethernet network +- Devices connected to the Wi-Fi AP will receive IPs in the `192.168.4.0/24` range via DHCP +- The ESP can be reached at `192.168.4.1` on the Wi-Fi network +- If NAPT is enabled, traffic can be routed between the Ethernet and Wi-Fi interfaces +- Both interfaces operate independently and can accept client connections simultaneously + +## Use Cases + +- **Dual-Band Network Access Points**: Provide both wired and wireless connectivity simultaneously +- **Network Bridges**: Bridge between wired and wireless networks +- **IoT Gateways**: Provide multiple access methods for IoT devices +- **Network Testing and Development**: Create isolated test networks with both wired and wireless access +- **Multi-Interface Routers**: Act as a router providing both Ethernet and Wi-Fi access for home or office networks +- **Network Extenders**: Extend network access through both wired and wireless interfaces + +## Troubleshooting + +- **Ethernet Link Not Up**: Ensure the Ethernet cable is properly connected and the PHY configuration matches your hardware +- **Client Not Getting IP on Ethernet**: Check that the DHCP server started successfully (look for "DHCP server started" in the logs) +- **Client Not Getting IP on Wi-Fi**: Check that the Wi-Fi AP started successfully (look for "Wi-Fi AP started" in the logs) +- **Wi-Fi AP Not Starting**: Verify that the SSID and password are configured correctly in menuconfig. Check that the channel is valid (1-13) +- **IP Pool Exhausted**: If you have more than 99 devices on either interface, increase the DHCP IP pool range in menuconfig +- **DNS Issues**: If DNS is enabled but not working, verify the DNS server addresses are correct for each interface +- **NAPT Not Working**: Ensure IP forwarding and NAPT are enabled in menuconfig (`CONFIG_LWIP_IP_FORWARD=y` and `CONFIG_LWIP_IPV4_NAPT=y`) +- **Hardware Issues**: For hardware and driver issues, refer to ESP-IDF Ethernet and Wi-Fi documentation and the upper level [README](../README.md) + +## Configuration Notes + +- The maximum DHCP pool size is 100 addresses (`DHCPS_MAX_LEASE`) for each interface +- DHCP lease time is specified in units of `LWIP_DHCPS_LEASE_UNIT` (default 60 seconds) +- Both the Ethernet gateway interface and Wi-Fi AP use static IP addresses (configured via menuconfig) +- DNS server information is optional and can be disabled in menuconfig for each interface independently +- The Ethernet gateway uses subnet `192.168.5.0/24` and Wi-Fi AP uses subnet `192.168.4.0/24` by default to avoid conflicts +- NAPT is optional and must be enabled in menuconfig for routing between interfaces +- Both interfaces operate independently: the Ethernet gateway provides wired network access while the Wi-Fi AP provides wireless network access +- Each interface can independently accept client connections and assign IP addresses via DHCP diff --git a/examples/esp_netif/eth_gateway_wifi_ap/main/CMakeLists.txt b/examples/esp_netif/eth_gateway_wifi_ap/main/CMakeLists.txt new file mode 100644 index 0000000000..eb6424bc46 --- /dev/null +++ b/examples/esp_netif/eth_gateway_wifi_ap/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "eth_gateway_wifi_ap.c" + INCLUDE_DIRS "." + REQUIRES ethernet_init esp_netif esp_eth esp_wifi nvs_flash) diff --git a/examples/esp_netif/eth_gateway_wifi_ap/main/Kconfig.projbuild b/examples/esp_netif/eth_gateway_wifi_ap/main/Kconfig.projbuild new file mode 100644 index 0000000000..031448bf35 --- /dev/null +++ b/examples/esp_netif/eth_gateway_wifi_ap/main/Kconfig.projbuild @@ -0,0 +1,175 @@ +menu "Example Configuration" + + # Ethernet gateway configuration + config EXAMPLE_ETH_GATEWAY_IP_ADDR + string "Ethernet gateway IP address" + default "192.168.5.1" + help + IP address for the Ethernet gateway (DHCP server). + This is the static IP address assigned to the Ethernet interface. + + config EXAMPLE_ETH_GATEWAY_NETMASK + string "Ethernet gateway netmask" + default "255.255.255.0" + help + Subnet mask for the Ethernet gateway network. + + config EXAMPLE_ETH_GATEWAY_GW + string "Ethernet gateway default gateway" + default "192.168.5.1" + help + Default gateway IP address for the Ethernet gateway network. + Typically set to the same IP as the gateway interface itself. + + config EXAMPLE_ETH_GATEWAY_DHCP_LEASE_TIME + int "Ethernet gateway DHCP lease time" + default 120 + help + DHCP lease time in units of LWIP_DHCPS_LEASE_UNIT (default 60 seconds). + Example: Setting this to 120 means a 7200-second (2 hour) lease time. + Setting this to 1 means a 60-second lease time. + + config EXAMPLE_ETH_GATEWAY_DHCP_START_ADDR + string "Ethernet gateway DHCP pool start address" + default "192.168.5.2" + help + Starting IP address of the DHCP address pool. + This is the first IP address that will be assigned to DHCP clients. + + config EXAMPLE_ETH_GATEWAY_DHCP_END_ADDR + string "Ethernet gateway DHCP pool end address" + default "192.168.5.100" + help + Ending IP address of the DHCP address pool. + This is the last IP address that will be assigned to DHCP clients. + Maximum pool size is 100 addresses (DHCPS_MAX_LEASE). + + config EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + bool "Enable DNS in Ethernet gateway DHCP offers" + default y + help + Enable DNS server information in DHCP offers. + When enabled, DHCP clients will receive DNS server addresses. + + config EXAMPLE_ETH_GATEWAY_DHCP_DNS_MAIN + string "Ethernet gateway primary DNS server" + default "192.168.5.1" + depends on EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + help + Primary DNS server IP address to be provided to DHCP clients. + Typically set to the gateway IP address or a public DNS server. + + config EXAMPLE_ETH_GATEWAY_DHCP_DNS_BACKUP + string "Ethernet gateway backup DNS server" + default "8.8.8.8" + depends on EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + help + Backup DNS server IP address to be provided to DHCP clients. + Typically set to a public DNS server like 8.8.8.8 (Google DNS). + + # WiFi AP Configuration + config EXAMPLE_WIFI_AP_SSID + string "WiFi AP SSID" + default "myssid-ext" + help + SSID (network name) for the WiFi Access Point. + + config EXAMPLE_WIFI_AP_PASS + string "WiFi AP Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the Access Point. + + config EXAMPLE_WIFI_AP_CHANNEL + int "WiFi AP Channel" + range 1 13 + default 1 + help + WiFi channel for the Access Point (1-13). + + choice EXAMPLE_WIFI_AP_AUTHMODE + prompt "WiFi AP Authentication Mode" + default EXAMPLE_WIFI_AP_AUTHMODE_WPA_WPA2_PSK + help + Authentication mode for the Wi-Fi Access Point. + If the password is empty, Open mode is used regardless of this setting. + + config EXAMPLE_WIFI_AP_AUTHMODE_OPEN + bool "Open" + config EXAMPLE_WIFI_AP_AUTHMODE_WPA2_PSK + bool "WPA2 PSK" + config EXAMPLE_WIFI_AP_AUTHMODE_WPA_WPA2_PSK + bool "WPA/WPA2 PSK" + config EXAMPLE_WIFI_AP_AUTHMODE_WPA3_PSK + bool "WPA3 SAE (PSK)" + config EXAMPLE_WIFI_AP_AUTHMODE_WPA2_WPA3_PSK + bool "WPA2/WPA3 transition" + endchoice + + config EXAMPLE_WIFI_AP_IP_ADDR + string "WiFi AP IP Address" + default "192.168.4.1" + help + IP address for the WiFi Access Point (DHCP server). + This is the static IP address assigned to the WiFi AP interface. + + config EXAMPLE_WIFI_AP_NETMASK + string "WiFi AP Netmask" + default "255.255.255.0" + help + Subnet mask for the WiFi Access Point network. + + config EXAMPLE_WIFI_AP_GW + string "WiFi AP Gateway" + default "192.168.4.1" + help + Default gateway IP address for the WiFi Access Point network. + Typically set to the same IP as the AP itself. + + config EXAMPLE_WIFI_AP_DHCP_LEASE_TIME + int "WiFi AP DHCP Lease Time" + default 120 + help + DHCP lease time in units of LWIP_DHCPS_LEASE_UNIT (default 60 seconds). + Example: Setting this to 120 means a 7200-second (2 hour) lease time. + Setting this to 1 means a 60-second lease time. + + config EXAMPLE_WIFI_AP_DHCP_START_ADDR + string "WiFi AP DHCP IP Pool Start Address" + default "192.168.4.2" + help + Starting IP address of the DHCP address pool. + This is the first IP address that will be assigned to DHCP clients. + + config EXAMPLE_WIFI_AP_DHCP_END_ADDR + string "WiFi AP DHCP IP Pool End Address" + default "192.168.4.100" + help + Ending IP address of the DHCP address pool. + This is the last IP address that will be assigned to DHCP clients. + Maximum pool size is 100 addresses (DHCPS_MAX_LEASE). + + config EXAMPLE_WIFI_AP_DHCP_ENABLE_DNS + bool "Enable DNS in WiFi AP DHCP Offers" + default y + help + Enable DNS server information in DHCP offers. + When enabled, DHCP clients will receive DNS server addresses. + + config EXAMPLE_WIFI_AP_DHCP_DNS_MAIN + string "WiFi AP Primary DNS Server" + default "192.168.4.1" + depends on EXAMPLE_WIFI_AP_DHCP_ENABLE_DNS + help + Primary DNS server IP address to be provided to DHCP clients. + Typically set to the AP IP address or a public DNS server. + + config EXAMPLE_WIFI_AP_DHCP_DNS_BACKUP + string "WiFi AP Backup DNS Server" + default "8.8.8.8" + depends on EXAMPLE_WIFI_AP_DHCP_ENABLE_DNS + help + Backup DNS server IP address to be provided to DHCP clients. + Typically set to a public DNS server like 8.8.8.8 (Google DNS). + +endmenu diff --git a/examples/esp_netif/eth_gateway_wifi_ap/main/eth_gateway_wifi_ap.c b/examples/esp_netif/eth_gateway_wifi_ap/main/eth_gateway_wifi_ap.c new file mode 100644 index 0000000000..fb0dedb00e --- /dev/null +++ b/examples/esp_netif/eth_gateway_wifi_ap/main/eth_gateway_wifi_ap.c @@ -0,0 +1,489 @@ +/* + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + * + * Ethernet gateway + Wi-Fi AP example + * + * This example initializes the Ethernet interface as a gateway (DHCP server on the wired port) + * and the Wi-Fi interface as an access point (DHCP server). Both interfaces provide network + * access to connected devices simultaneously. + */ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_netif.h" +#include "esp_eth.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_wifi_types.h" +#include "esp_mac.h" +#include "nvs_flash.h" +#include "dhcpserver/dhcpserver.h" +#include "dhcpserver/dhcpserver_options.h" +#include "ethernet_init.h" +#include "sdkconfig.h" + +static const char *TAG = "eth_gateway_wifi_ap"; + +/* Common/Utility functions */ + +/** Parse IP address configuration from strings into ip_info structure */ +static esp_err_t parse_ip_config_from_strings(esp_netif_ip_info_t *ip_info, + const char *ip_addr, const char *netmask, const char *gw) +{ + memset(ip_info, 0, sizeof(esp_netif_ip_info_t)); + + if (esp_netif_str_to_ip4(ip_addr, &ip_info->ip) != ESP_OK) { + ESP_LOGE(TAG, "Invalid IP address: %s", ip_addr); + return ESP_ERR_INVALID_ARG; + } + if (esp_netif_str_to_ip4(netmask, &ip_info->netmask) != ESP_OK) { + ESP_LOGE(TAG, "Invalid netmask: %s", netmask); + return ESP_ERR_INVALID_ARG; + } + if (esp_netif_str_to_ip4(gw, &ip_info->gw) != ESP_OK) { + ESP_LOGE(TAG, "Invalid gateway: %s", gw); + return ESP_ERR_INVALID_ARG; + } + + return ESP_OK; +} + +/** Configure DHCP server options (lease time and IP pool) */ +static esp_err_t configure_dhcp_server_options(esp_netif_t *netif, uint32_t lease_time, + const char *start_addr, const char *end_addr) +{ + esp_err_t err; + + // Set lease time + err = esp_netif_dhcps_option(netif, ESP_NETIF_OP_SET, IP_ADDRESS_LEASE_TIME, &lease_time, sizeof(lease_time)); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to set DHCP lease time: %s", esp_err_to_name(err)); + } + + // Configure IP address pool + dhcps_lease_t dhcp_lease; + memset(&dhcp_lease, 0, sizeof(dhcps_lease_t)); + dhcp_lease.enable = true; + if (esp_netif_str_to_ip4(start_addr, (esp_ip4_addr_t *)&dhcp_lease.start_ip) != ESP_OK) { + ESP_LOGW(TAG, "Invalid DHCP start address: %s, using default pool", start_addr); + } else if (esp_netif_str_to_ip4(end_addr, (esp_ip4_addr_t *)&dhcp_lease.end_ip) != ESP_OK) { + ESP_LOGW(TAG, "Invalid DHCP end address: %s, using default pool", end_addr); + } else { + err = esp_netif_dhcps_option(netif, ESP_NETIF_OP_SET, REQUESTED_IP_ADDRESS, &dhcp_lease, sizeof(dhcps_lease_t)); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to set DHCP IP pool: %s", esp_err_to_name(err)); + } + } + + return ESP_OK; +} + +/** Configure DHCP DNS servers for a network interface */ +static esp_err_t configure_dhcp_dns(esp_netif_t *netif, const char *dns_main, const char *dns_backup) +{ + esp_err_t err; + + // Enable DNS in DHCP offers + dhcps_offer_t dhcps_dns_value = OFFER_DNS; + err = esp_netif_dhcps_option(netif, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &dhcps_dns_value, sizeof(dhcps_dns_value)); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to enable DNS in DHCP offers: %s", esp_err_to_name(err)); + return err; + } + + // Set DNS server addresses + esp_netif_dns_info_t dns_info = {}; + dns_info.ip.type = ESP_IPADDR_TYPE_V4; + + if (dns_main && esp_netif_str_to_ip4(dns_main, &dns_info.ip.u_addr.ip4) == ESP_OK) { + err = esp_netif_set_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to set primary DNS: %s", esp_err_to_name(err)); + } + } + + if (dns_backup && esp_netif_str_to_ip4(dns_backup, &dns_info.ip.u_addr.ip4) == ESP_OK) { + err = esp_netif_set_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_info); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to set backup DNS: %s", esp_err_to_name(err)); + } + } + + return ESP_OK; +} + +/** Print IP information for a network interface */ +static void print_ip_info(esp_netif_t *netif, const char *interface_name) +{ + esp_netif_ip_info_t ip_info; + esp_netif_dns_info_t dns_info; + + if (esp_netif_get_ip_info(netif, &ip_info) == ESP_OK) { + ESP_LOGI(TAG, "%s Got IP Address", interface_name); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "IP:" IPSTR, IP2STR(&ip_info.ip)); + ESP_LOGI(TAG, "MASK:" IPSTR, IP2STR(&ip_info.netmask)); + ESP_LOGI(TAG, "GW:" IPSTR, IP2STR(&ip_info.gw)); + + // Print DNS information + if (esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info) == ESP_OK) { + ESP_LOGI(TAG, "DHCP_DNS_MAIN:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + } + if (esp_netif_get_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_info) == ESP_OK) { + ESP_LOGI(TAG, "DHCP_DNS_BACKUP:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + } + + ESP_LOGI(TAG, "~~~~~~~~~~~"); + } +} + +/* Ethernet-related functions */ + +/** Event handler for Ethernet events */ +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}; + esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data; + esp_netif_t *eth_netif = (esp_netif_t *)arg; + + switch (event_id) { + case ETHERNET_EVENT_CONNECTED: + esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); + ESP_LOGI(TAG, "Ethernet Link Up"); + ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x", + mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); + { + esp_err_t err = esp_netif_dhcps_start(eth_netif); + if (err != ESP_OK) { + ESP_LOGW(TAG, "DHCP server start: %s", esp_err_to_name(err)); + } else { + ESP_LOGI(TAG, "DHCP server started on %s", esp_netif_get_desc(eth_netif)); + } + } + break; + /* Optional: call esp_netif_dhcps_stop(eth_netif) here if link-down should tear down DHCP. */ + case ETHERNET_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "Ethernet Link Down"); + break; + case ETHERNET_EVENT_START: + ESP_LOGI(TAG, "Ethernet Started"); + break; + case ETHERNET_EVENT_STOP: + ESP_LOGI(TAG, "Ethernet Stopped"); + break; + default: + break; + } +} + + +/** + * Configure Ethernet gateway network interface (netif) layer + * + * This function sets up the L3 (network layer) configuration for the Ethernet gateway: + * - Parses IP configuration (IP address, netmask, gateway) + * - Creates the Ethernet gateway netif with inherent configuration + * - Configures DHCP server options (lease time, IP pool) + * - Configures DNS servers (if enabled) + * - Does not start the DHCP server (that happens on Ethernet link up). + * + * @return Pointer to the configured Ethernet gateway netif, or NULL on error + */ +static esp_netif_t *eth_gw_setup_netif(void) +{ + // Static IP configuration for Ethernet gateway + static esp_netif_ip_info_t s_eth_gw_ip_info; + esp_err_t err = parse_ip_config_from_strings(&s_eth_gw_ip_info, + CONFIG_EXAMPLE_ETH_GATEWAY_IP_ADDR, + CONFIG_EXAMPLE_ETH_GATEWAY_NETMASK, + CONFIG_EXAMPLE_ETH_GATEWAY_GW); + if (err != ESP_OK) { + return NULL; + } + + static esp_netif_inherent_config_t inherent_config = { + .flags = ESP_NETIF_DHCP_SERVER, + ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(mac) + .ip_info = &s_eth_gw_ip_info, + .get_ip_event = 0, + .lost_ip_event = 0, + .if_key = "ETH_GW", + .if_desc = "eth_gw", + .route_prio = 50, + .bridge_info = NULL + }; + + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); + cfg.base = &inherent_config; + + esp_netif_t *eth_netif = esp_netif_new(&cfg); + ESP_ERROR_CHECK(eth_netif != NULL ? ESP_OK : ESP_FAIL); + + /* Configure DHCP server options */ + configure_dhcp_server_options(eth_netif, + CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_LEASE_TIME, + CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_START_ADDR, + CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_END_ADDR); + + // Configure DNS servers if enabled +#if CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + configure_dhcp_dns(eth_netif, CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_DNS_MAIN, CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_DNS_BACKUP); +#endif // CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + + ESP_LOGI(TAG, "Ethernet gateway netif configured. IP: " IPSTR ", netmask: " IPSTR ". DHCP starts when link is up.", + IP2STR(&s_eth_gw_ip_info.ip), IP2STR(&s_eth_gw_ip_info.netmask)); + + return eth_netif; +} + +/** + * Initialize and start Ethernet gateway + * + * This is the main entry point for Ethernet gateway initialization. It handles: + * - Registering Ethernet event handlers (L2 events) + * - Attaching the netif to the Ethernet driver handle + * - Starting the Ethernet driver + * + * @param eth_handle Ethernet driver handle (must be initialized via ethernet_init_all()) + * @return Pointer to the Ethernet gateway netif, or NULL on error + */ +static esp_netif_t *eth_gw_start(esp_eth_handle_t eth_handle) +{ + // Setup Ethernet gateway network interface (IP, DHCP DNS options; DHCP server starts on link up) + esp_netif_t *eth_netif = eth_gw_setup_netif(); + if (eth_netif == NULL) { + ESP_LOGE(TAG, "Failed to setup Ethernet gateway netif"); + return NULL; + } + + ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_netif)); + + // Attach netif to Ethernet driver and start Ethernet + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle))); + ESP_ERROR_CHECK(esp_eth_start(eth_handle)); + + // Print Ethernet gateway IP information + print_ip_info(eth_netif, "Ethernet"); + ESP_LOGI(TAG, "Ethernet gateway started. Connect a device to the Ethernet port to get an IP via DHCP"); + + return eth_netif; +} + +/* WiFi-related functions */ + +/** Event handler for WiFi AP events */ +static void wifi_ap_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (event_base == WIFI_EVENT) { + ESP_LOGI(TAG, "Wi-Fi Event: base=%s, id=%ld", event_base, event_id); + switch (event_id) { + case WIFI_EVENT_AP_START: + ESP_LOGI(TAG, "Wi-Fi AP started"); + break; + case WIFI_EVENT_AP_STOP: + ESP_LOGI(TAG, "Wi-Fi AP stopped"); + break; + case WIFI_EVENT_AP_STACONNECTED: { + wifi_event_ap_staconnected_t* event = (wifi_event_ap_staconnected_t*) event_data; + ESP_LOGI(TAG, "station "MACSTR" join, AID=%d", + MAC2STR(event->mac), event->aid); + } + break; + case WIFI_EVENT_AP_STADISCONNECTED: { + wifi_event_ap_stadisconnected_t* event = (wifi_event_ap_stadisconnected_t*) event_data; + ESP_LOGI(TAG, "station "MACSTR" leave, AID=%d", + MAC2STR(event->mac), event->aid); + } + break; + default: + ESP_LOGW(TAG, "Unhandled Wi-Fi AP event: id=%ld", event_id); + break; + } + } +} + +/** + * Configure WiFi AP network interface (netif) layer + * + * This function sets up the L3 (network layer) configuration for the WiFi AP: + * - Creates the WiFi AP netif + * - Configures static IP address, netmask, and gateway + * - Configures DHCP server options (lease time, IP pool) + * - Configures DNS servers (if enabled) + * - Starts the DHCP server + * + * @return Pointer to the configured WiFi AP netif, or NULL on error + */ +static esp_netif_t *wifi_ap_setup_netif(void) +{ + esp_netif_t *ap_netif = esp_netif_create_default_wifi_ap(); + ESP_ERROR_CHECK(ap_netif != NULL ? ESP_OK : ESP_FAIL); + + // Static IP configuration for WiFi AP + static esp_netif_ip_info_t s_ap_ip_info; + esp_err_t err = parse_ip_config_from_strings(&s_ap_ip_info, + CONFIG_EXAMPLE_WIFI_AP_IP_ADDR, + CONFIG_EXAMPLE_WIFI_AP_NETMASK, + CONFIG_EXAMPLE_WIFI_AP_GW); + if (err != ESP_OK) { + return NULL; + } + + ESP_ERROR_CHECK(esp_netif_dhcps_stop(ap_netif)); + ESP_ERROR_CHECK(esp_netif_set_ip_info(ap_netif, &s_ap_ip_info)); + + /* Configure DHCP server options */ + configure_dhcp_server_options(ap_netif, + CONFIG_EXAMPLE_WIFI_AP_DHCP_LEASE_TIME, + CONFIG_EXAMPLE_WIFI_AP_DHCP_START_ADDR, + CONFIG_EXAMPLE_WIFI_AP_DHCP_END_ADDR); + + // Configure DNS servers if enabled +#if CONFIG_EXAMPLE_WIFI_AP_DHCP_ENABLE_DNS + configure_dhcp_dns(ap_netif, CONFIG_EXAMPLE_WIFI_AP_DHCP_DNS_MAIN, CONFIG_EXAMPLE_WIFI_AP_DHCP_DNS_BACKUP); +#endif // CONFIG_EXAMPLE_WIFI_AP_DHCP_ENABLE_DNS + + ESP_ERROR_CHECK(esp_netif_dhcps_start(ap_netif)); + + ESP_LOGI(TAG, "Wi-Fi AP initialized."); + + return ap_netif; +} + +static wifi_auth_mode_t example_wifi_ap_authmode(void) +{ +#if CONFIG_EXAMPLE_WIFI_AP_AUTHMODE_OPEN + return WIFI_AUTH_OPEN; +#elif CONFIG_EXAMPLE_WIFI_AP_AUTHMODE_WPA2_PSK + return WIFI_AUTH_WPA2_PSK; +#elif CONFIG_EXAMPLE_WIFI_AP_AUTHMODE_WPA_WPA2_PSK + return WIFI_AUTH_WPA_WPA2_PSK; +#elif CONFIG_EXAMPLE_WIFI_AP_AUTHMODE_WPA3_PSK + return WIFI_AUTH_WPA3_PSK; +#elif CONFIG_EXAMPLE_WIFI_AP_AUTHMODE_WPA2_WPA3_PSK + return WIFI_AUTH_WPA2_WPA3_PSK; +#else + return WIFI_AUTH_WPA_WPA2_PSK; +#endif +} + +/** + * Initialize and start WiFi AP + * + * This is the main entry point for WiFi AP initialization. It handles: + * - Registering WiFi AP event handlers (L2 events) + * - Setting up the network interface via wifi_ap_setup_netif() (L3 configuration) + * - Initializing the WiFi driver + * - Configuring WiFi AP settings (SSID, password, channel, authentication) + * - Starting the WiFi AP + * + * @return Pointer to the WiFi AP netif, or NULL on error + */ +static esp_netif_t *wifi_ap_start(void) +{ + // Register WiFi AP event handlers - register only specific events to avoid warnings for unhandled events + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_START, wifi_ap_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_STOP, wifi_ap_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_STACONNECTED, wifi_ap_event_handler, NULL)); + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, WIFI_EVENT_AP_STADISCONNECTED, wifi_ap_event_handler, NULL)); + + // Setup WiFi AP network interface (IP, DHCP, DNS configuration) + esp_netif_t *wifi_ap_netif = wifi_ap_setup_netif(); + if (wifi_ap_netif == NULL) { + ESP_LOGE(TAG, "Failed to setup WiFi AP netif"); + return NULL; + } + + // Initialize WiFi driver + wifi_init_config_t wifi_cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&wifi_cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + + // Configure WiFi AP settings (SSID, password, channel, authentication) + wifi_config_t ap_wifi_config = { + .ap = { + .ssid = CONFIG_EXAMPLE_WIFI_AP_SSID, + .ssid_len = strlen(CONFIG_EXAMPLE_WIFI_AP_SSID), + .channel = CONFIG_EXAMPLE_WIFI_AP_CHANNEL, + .password = CONFIG_EXAMPLE_WIFI_AP_PASS, + .max_connection = 4, + .authmode = example_wifi_ap_authmode(), + .pmf_cfg = { + .required = false, + }, + }, + }; + if (strlen(CONFIG_EXAMPLE_WIFI_AP_PASS) == 0) { + ap_wifi_config.ap.authmode = WIFI_AUTH_OPEN; + } + + // Set mode and start WiFi AP + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_AP)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_AP, &ap_wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + + ESP_LOGI(TAG, "Wi-Fi AP started. SSID:%s password:%s channel:%d", + CONFIG_EXAMPLE_WIFI_AP_SSID, CONFIG_EXAMPLE_WIFI_AP_PASS, CONFIG_EXAMPLE_WIFI_AP_CHANNEL); + + // Print WiFi AP IP information + print_ip_info(wifi_ap_netif, "Wi-Fi AP"); + ESP_LOGI(TAG, "Wifi AP started. Connect a device to the Wi-Fi AP to get an IP via DHCP"); + + return wifi_ap_netif; +} + +/* Main function */ + +void app_main(void) +{ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + // Initialize Ethernet driver + uint8_t eth_port_cnt = 0; + esp_eth_handle_t *eth_handles = NULL; + ESP_ERROR_CHECK(ethernet_init_all(ð_handles, ð_port_cnt)); + + if (eth_port_cnt == 0) { + ESP_LOGE(TAG, "No Ethernet interface initialized"); + return; + } + if (eth_port_cnt > 1) { + ESP_LOGW(TAG, "%u Ethernet interfaces initialized; this example only uses the first one", eth_port_cnt); + } + + // Initialize Ethernet gateway + esp_netif_t *eth_netif = eth_gw_start(eth_handles[0]); + if (eth_netif == NULL) { + ESP_LOGE(TAG, "Failed to initialize Ethernet gateway"); + return; + } + + // Initialize WiFi AP + esp_netif_t *wifi_ap_netif = wifi_ap_start(); + if (wifi_ap_netif == NULL) { + ESP_LOGE(TAG, "Failed to initialize WiFi AP"); + return; + } + +#if CONFIG_LWIP_IPV4_NAPT + // Setup NAPT (Network Address Port Translation) if enabled + ESP_ERROR_CHECK(esp_netif_napt_enable(eth_netif)); + ESP_LOGI(TAG, "NAPT enabled on Ethernet gateway"); + ESP_ERROR_CHECK(esp_netif_napt_enable(wifi_ap_netif)); + ESP_LOGI(TAG, "NAPT enabled on Wi-Fi AP"); +#endif +} diff --git a/examples/esp_netif/eth_gateway_wifi_ap/main/idf_component.yml b/examples/esp_netif/eth_gateway_wifi_ap/main/idf_component.yml new file mode 100644 index 0000000000..119518cbd3 --- /dev/null +++ b/examples/esp_netif/eth_gateway_wifi_ap/main/idf_component.yml @@ -0,0 +1,6 @@ +dependencies: + idf: "*" + espressif/ethernet_init: + version: "~1.2.0" + rules: + - if: "target != linux" diff --git a/examples/esp_netif/eth_gateway_wifi_ap/sdkconfig.defaults b/examples/esp_netif/eth_gateway_wifi_ap/sdkconfig.defaults new file mode 100644 index 0000000000..aece93d07e --- /dev/null +++ b/examples/esp_netif/eth_gateway_wifi_ap/sdkconfig.defaults @@ -0,0 +1,20 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +# Ethernet gateway configuration +CONFIG_EXAMPLE_ETH_GATEWAY_IP_ADDR="192.168.5.1" +CONFIG_EXAMPLE_ETH_GATEWAY_NETMASK="255.255.255.0" +CONFIG_EXAMPLE_ETH_GATEWAY_GW="192.168.5.1" +# +# WiFi AP Configuration +CONFIG_EXAMPLE_WIFI_AP_SSID="myssid-ext" +CONFIG_EXAMPLE_WIFI_AP_PASS="mypassword" +CONFIG_EXAMPLE_WIFI_AP_CHANNEL=1 +CONFIG_EXAMPLE_WIFI_AP_AUTHMODE_WPA_WPA2_PSK=y +CONFIG_EXAMPLE_WIFI_AP_IP_ADDR="192.168.4.1" +CONFIG_EXAMPLE_WIFI_AP_NETMASK="255.255.255.0" +CONFIG_EXAMPLE_WIFI_AP_GW="192.168.4.1" +# +# NAPT Configuration (optional) +CONFIG_LWIP_IP_FORWARD=y +CONFIG_LWIP_IPV4_NAPT=y diff --git a/examples/esp_netif/eth_gateway_wifi_sta/CMakeLists.txt b/examples/esp_netif/eth_gateway_wifi_sta/CMakeLists.txt new file mode 100644 index 0000000000..8cd08da082 --- /dev/null +++ b/examples/esp_netif/eth_gateway_wifi_sta/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(eth_gateway_wifi_sta) diff --git a/examples/esp_netif/eth_gateway_wifi_sta/README.md b/examples/esp_netif/eth_gateway_wifi_sta/README.md new file mode 100644 index 0000000000..e5ccd2b9ab --- /dev/null +++ b/examples/esp_netif/eth_gateway_wifi_sta/README.md @@ -0,0 +1,144 @@ +| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-S2 | ESP32-S3 | +| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | + +# Ethernet gateway + Wi-Fi STA example + +## Overview + +This example initializes **two network interfaces simultaneously**: an **Ethernet interface** in a **gateway** role (DHCP server on the wired port—not a Wi-Fi “AP”), and a **Wi-Fi interface** operating as a **station (STA)**. The Ethernet gateway provides network access to connected devices via DHCP (e.g. in the `192.168.5.x` range), while the Wi-Fi STA connects to an external access point for upstream connectivity. This hybrid configuration enables the ESP to bridge between a wired local network and a wireless upstream connection. + +The example uses the `ethernet_init` component from [esp-eth-drivers](https://github.com/espressif/esp-eth-drivers) to initialize the Ethernet driver based on menuconfig (internal/SPI PHY, etc.) and creates two `esp_netif` instances: one for the Ethernet gateway (configured with `ESP_NETIF_DHCP_SERVER` and a static IP) and one for Wi-Fi STA (configured with `esp_netif_create_default_wifi_sta()`). + +### Key Features + +- **Ethernet gateway with DHCP server**: Automatically assigns IP addresses to devices connected via Ethernet +- **Wi-Fi STA**: Connects to an external Wi-Fi access point for internet connectivity +- **Network Bridging**: Bridges traffic between Ethernet and Wi-Fi interfaces +- **NAPT Support**: Optional Network Address Port Translation for routing between interfaces; when the lwIP NAPT (and IPv4 forwarding) features are enabled in menuconfig, NAPT is enabled on the **Ethernet gateway** interface (for routing traffic through the Wi-Fi STA connection) +- **Configurable IP Pool**: Set the range of IP addresses to assign via DHCP (default: `192.168.5.2` - `192.168.5.100`) +- **DHCP Lease Time**: Configurable lease duration for assigned IP addresses +- **DNS Support**: Optional DNS server configuration for DHCP clients, can use Wi-Fi STA's DNS servers +- **Event-Driven Architecture**: Uses ESP event system to handle network events for both interfaces + +## How to use example + +### Configure the project + +You can configure the project using `idf.py menuconfig` or by using the provided `sdkconfig.defaults` file which contains default configuration values. + +#### Ethernet Configuration + +Configure the Ethernet PHY/SPI settings under "Ethernet" in menuconfig (see the [ethernet_init](https://github.com/espressif/esp-eth-drivers/tree/master/ethernet_init) component documentation). The example depends on the managed component `espressif/ethernet_init` (see `main/idf_component.yml`). + +#### Example Configuration + +Under "Example Configuration" menu, you can configure: + +**Wi-Fi Configuration:** +- **WiFi SSID**: Wi-Fi network SSID to connect to (default: `"myssid"`) +- **WiFi Password**: Wi-Fi network password (default: `"mypassword"`) + +**Ethernet gateway configuration:** +- **Ethernet gateway IP address**: Static IP address for the ESP device (default: `192.168.5.1`) +- **Ethernet gateway netmask**: Subnet mask for the network (default: `255.255.255.0`) +- **Ethernet gateway default gateway**: Default gateway IP (default: `192.168.5.1`) +- **DHCP Lease Time**: Duration of IP address leases in units of `LWIP_DHCPS_LEASE_UNIT` (default: 120, which equals 7200 seconds or 2 hours) +- **DHCP IP Pool Start Address**: First IP address in the DHCP pool (default: `192.168.5.2`) +- **DHCP IP Pool End Address**: Last IP address in the DHCP pool (default: `192.168.5.100`) +- **Enable DNS in DHCP Offers**: Enable DNS server information in DHCP offers (default: enabled) +- **Primary DNS Server**: Primary DNS server IP (default: `192.168.5.1`, can be updated from Wi-Fi STA DNS) +- **Backup DNS Server**: Backup DNS server IP (default: `8.8.8.8`) + +**NAPT Configuration:** +- Enable IP forwarding and NAPT in menuconfig under "Component config" → "LWIP" → "Enable IPv4 forwarding" and "Enable IPv4 NAPT" + +### Build, Flash, and Run + +``` +idf.py -p PORT build flash monitor +``` + +Replace PORT with your serial port. + +**Prerequisites:** +- Connect an Ethernet cable to the ESP's Ethernet port (devices connecting will receive IPs via DHCP) +- Ensure a Wi-Fi access point is available with the configured SSID and password + +The example initializes the Ethernet gateway first, then connects the Wi-Fi STA. It waits for the Wi-Fi STA to obtain an IP address before completing initialization. If DNS is enabled, the Ethernet gateway's DNS servers will be updated with the Wi-Fi STA's DNS servers automatically. + +## Example Output + +``` +I (1234) eth_gateway_wifi_sta: Ethernet gateway ready (static IP: 192.168.5.1, netmask: 255.255.255.0). DHCP starts when link is up. +I (1234) eth_gateway_wifi_sta: Connect a device to the Ethernet port to get an IP via DHCP +I (1234) esp_netif_lwip: DHCP server started on interface ETH_GW with IP: 192.168.5.1 +I (1244) ethernet_init: Ethernet(IP101[23,18]) Link Up +I (1244) ethernet_init: Ethernet(IP101[23,18]) HW Addr 58:bf:25:e0:41:03 +I (1254) eth_gateway_wifi_sta: Ethernet Link Up +I (1254) eth_gateway_wifi_sta: Ethernet HW Addr 58:bf:25:e0:41:03 +I (1254) eth_gateway_wifi_sta: Ethernet Got IP Address +I (1254) eth_gateway_wifi_sta: Event: base=IP_EVENT, id=4 +I (1254) eth_gateway_wifi_sta: ~~~~~~~~~~~ +I (1254) eth_gateway_wifi_sta: ETHIP:192.168.5.1 +I (1254) eth_gateway_wifi_sta: ETHMASK:255.255.255.0 +I (1254) eth_gateway_wifi_sta: ETHGW:192.168.5.1 +I (1264) eth_gateway_wifi_sta: DHCP_DNS_MAIN:192.168.5.1 +I (1264) eth_gateway_wifi_sta: DHCP_DNS_BACKUP:8.8.8.8 +I (1274) eth_gateway_wifi_sta: ~~~~~~~~~~~ +I (1274) eth_gateway_wifi_sta: Wi-Fi STA initialized. SSID:myssid password:mypassword +I (1274) eth_gateway_wifi_sta: Wi-Fi Event: base=WIFI_EVENT, id=2 +I (1284) eth_gateway_wifi_sta: Wi-Fi STA started +I (1424) wifi:connected with myssid, aid = 1, channel 6, BW20, bssid = xx:xx:xx:xx:xx:xx +I (1424) wifi:security: WPA2-PSK, phy: bgn, rssi: -45 +I (1434) eth_gateway_wifi_sta: Wi-Fi Event: base=WIFI_EVENT, id=4 +I (1434) eth_gateway_wifi_sta: Wi-Fi STA connected +I (2534) eth_gateway_wifi_sta: Wi-Fi Got IP Address +I (2534) eth_gateway_wifi_sta: Event: base=IP_EVENT, id=0 +I (2534) eth_gateway_wifi_sta: ~~~~~~~~~~~ +I (2534) eth_gateway_wifi_sta: STAIP:192.168.1.100 +I (2534) eth_gateway_wifi_sta: STAMASK:255.255.255.0 +I (2534) eth_gateway_wifi_sta: STAGW:192.168.1.1 +I (2544) eth_gateway_wifi_sta: DHCP_DNS_MAIN:192.168.1.1 +I (2544) eth_gateway_wifi_sta: DHCP_DNS_BACKUP:8.8.8.8 +I (2554) eth_gateway_wifi_sta: ~~~~~~~~~~~ +I (2554) eth_gateway_wifi_sta: Updated Ethernet gateway DNS with Wi-Fi STA DNS: 192.168.1.1 +I (2554) eth_gateway_wifi_sta: NAPT enabled on Ethernet gateway +I (2644) esp_netif_lwip: DHCP server assigned IP to a client, IP is: 192.168.5.2 +``` + +After initialization: +- Devices connected to the Ethernet port will receive IPs in the `192.168.5.0/24` range via DHCP +- The ESP can be reached at `192.168.5.1` on the Ethernet network +- The Wi-Fi STA connects to the external access point and obtains an IP address +- If NAPT is enabled, traffic from Ethernet devices can be routed through the Wi-Fi connection +- DNS servers from the Wi-Fi STA are automatically used for Ethernet gateway DHCP clients (if DNS is enabled) + +## Use Cases + +- **Wi-Fi-to-Ethernet Bridges**: Connect wired devices to a Wi-Fi network +- **Network Extenders**: Extend Wi-Fi networks to wired devices +- **IoT Gateways**: Provide wired access with wireless backhaul +- **Home Automation Hubs**: Connect wired sensors to Wi-Fi networks +- **Network Isolation**: Create a separate Ethernet network while maintaining internet connectivity via Wi-Fi + +## Troubleshooting + +- **Ethernet Link Not Up**: Ensure the Ethernet cable is properly connected and the PHY configuration matches your hardware +- **Client Not Getting IP**: Check that the DHCP server started successfully (look for "DHCP server started" in the logs) +- **Wi-Fi Not Connecting**: Check that the SSID and password are correct in menuconfig. Ensure the Wi-Fi access point is within range and operational +- **No IP Address Received on Wi-Fi**: Verify that the Wi-Fi network has a DHCP server running. Check network connectivity +- **IP Pool Exhausted**: If you have more than 99 devices, increase the DHCP IP pool range in menuconfig +- **DNS Issues**: If DNS is enabled but not working, verify the DNS server addresses are correct. The example automatically updates Ethernet gateway DNS with Wi-Fi STA DNS when available +- **NAPT Not Working**: Ensure IP forwarding and NAPT are enabled in menuconfig (`CONFIG_LWIP_IP_FORWARD=y` and `CONFIG_LWIP_IPV4_NAPT=y`) +- **Hardware Issues**: For hardware and driver issues, refer to ESP-IDF Ethernet and Wi-Fi documentation and the upper level [README](../README.md) + +## Configuration Notes + +- The maximum DHCP pool size is 100 addresses (`DHCPS_MAX_LEASE`) +- DHCP lease time is specified in units of `LWIP_DHCPS_LEASE_UNIT` (default 60 seconds) +- The Ethernet gateway always uses a static IP address (configured via menuconfig) +- DNS server information is optional and can be disabled in menuconfig +- When DNS is enabled, the Ethernet gateway's primary DNS is automatically updated with the Wi-Fi STA's DNS server after Wi-Fi connects +- The example waits for Wi-Fi STA to obtain an IP address before completing initialization +- NAPT is optional and must be enabled in menuconfig for routing between interfaces +- Both interfaces operate independently: the Ethernet gateway provides local network access while the Wi-Fi STA provides upstream connectivity diff --git a/examples/esp_netif/eth_gateway_wifi_sta/main/CMakeLists.txt b/examples/esp_netif/eth_gateway_wifi_sta/main/CMakeLists.txt new file mode 100644 index 0000000000..0d34ae597b --- /dev/null +++ b/examples/esp_netif/eth_gateway_wifi_sta/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "eth_gateway_wifi_sta.c" + INCLUDE_DIRS "." + REQUIRES ethernet_init esp_netif esp_eth esp_wifi nvs_flash) diff --git a/examples/esp_netif/eth_gateway_wifi_sta/main/Kconfig.projbuild b/examples/esp_netif/eth_gateway_wifi_sta/main/Kconfig.projbuild new file mode 100644 index 0000000000..38f3a14f49 --- /dev/null +++ b/examples/esp_netif/eth_gateway_wifi_sta/main/Kconfig.projbuild @@ -0,0 +1,104 @@ +menu "Example Configuration" + + config EXAMPLE_ESP_WIFI_SSID + string "WiFi SSID" + default "myssid" + help + SSID (network name) for the example to connect to. + + config EXAMPLE_ESP_WIFI_PASS + string "WiFi Password" + default "mypassword" + help + WiFi password (WPA or WPA2) for the example to use. + + choice EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD + prompt "WiFi STA scan authentication threshold" + default EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA2_PSK + help + Minimum authentication mode accepted when scanning and connecting as a station. + Lower this to connect to older networks (WEP/WPA). + + config EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_OPEN + bool "Open" + config EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WEP + bool "WEP" + config EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA_PSK + bool "WPA PSK" + config EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA2_PSK + bool "WPA2 PSK" + config EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA_WPA2_PSK + bool "WPA/WPA2 PSK" + config EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA3_PSK + bool "WPA3 PSK" + endchoice + + config EXAMPLE_ETH_GATEWAY_IP_ADDR + string "Ethernet gateway IP address" + default "192.168.5.1" + help + IP address for the Ethernet gateway (DHCP server). + This is the static IP address assigned to the Ethernet interface. + + config EXAMPLE_ETH_GATEWAY_NETMASK + string "Ethernet gateway netmask" + default "255.255.255.0" + help + Subnet mask for the Ethernet gateway network. + + config EXAMPLE_ETH_GATEWAY_GW + string "Ethernet gateway default gateway" + default "192.168.5.1" + help + Default gateway IP address for the Ethernet gateway network. + Typically set to the same IP as the gateway interface itself. + + config EXAMPLE_ETH_GATEWAY_DHCP_LEASE_TIME + int "DHCP Lease Time" + default 120 + help + DHCP lease time in units of LWIP_DHCPS_LEASE_UNIT (default 60 seconds). + Example: Setting this to 120 means a 7200-second (2 hour) lease time. + Setting this to 1 means a 60-second lease time. + + config EXAMPLE_ETH_GATEWAY_DHCP_START_ADDR + string "DHCP IP Pool Start Address" + default "192.168.5.2" + help + Starting IP address of the DHCP address pool. + This is the first IP address that will be assigned to DHCP clients. + + config EXAMPLE_ETH_GATEWAY_DHCP_END_ADDR + string "DHCP IP Pool End Address" + default "192.168.5.100" + help + Ending IP address of the DHCP address pool. + This is the last IP address that will be assigned to DHCP clients. + Maximum pool size is 100 addresses (DHCPS_MAX_LEASE). + + config EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + bool "Enable DNS in DHCP Offers" + default y + help + Enable DNS server information in DHCP offers. + When enabled, DHCP clients will receive DNS server addresses. + If enabled, DNS can be configured to use WiFi STA's DNS servers. + + config EXAMPLE_ETH_GATEWAY_DHCP_DNS_MAIN + string "Primary DNS Server" + default "192.168.5.1" + depends on EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + help + Primary DNS server IP address to be provided to DHCP clients. + Typically set to the gateway IP address or a public DNS server. + Can be overridden to use WiFi STA's DNS if available. + + config EXAMPLE_ETH_GATEWAY_DHCP_DNS_BACKUP + string "Backup DNS Server" + default "8.8.8.8" + depends on EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + help + Backup DNS server IP address to be provided to DHCP clients. + Typically set to a public DNS server like 8.8.8.8 (Google DNS). + +endmenu diff --git a/examples/esp_netif/eth_gateway_wifi_sta/main/eth_gateway_wifi_sta.c b/examples/esp_netif/eth_gateway_wifi_sta/main/eth_gateway_wifi_sta.c new file mode 100644 index 0000000000..88a215207b --- /dev/null +++ b/examples/esp_netif/eth_gateway_wifi_sta/main/eth_gateway_wifi_sta.c @@ -0,0 +1,342 @@ +/* + * SPDX-FileCopyrightText: 2025-2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + * + * Ethernet gateway + Wi-Fi STA example + * + * This example initializes an Ethernet interface as a gateway (DHCP server on the wired port) + * and a Wi-Fi interface as a station. The Ethernet gateway provides network access to + * connected devices while the Wi-Fi STA connects to an external access point. + */ +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include "freertos/event_groups.h" +#include "esp_netif.h" +#include "esp_eth.h" +#include "esp_event.h" +#include "esp_log.h" +#include "esp_wifi.h" +#include "esp_wifi_types.h" +#include "nvs_flash.h" +#include "dhcpserver/dhcpserver.h" +#include "dhcpserver/dhcpserver_options.h" +#include "ethernet_init.h" +#include "sdkconfig.h" + +static const char *TAG = "eth_gateway_wifi_sta"; + +/** Event group bit: set when Wi-Fi station receives IPv4 (IP_EVENT_STA_GOT_IP). */ +#define EXAMPLE_WIFI_STA_GOT_IP_BIT (1U << 0) + +static EventGroupHandle_t event_group = NULL; + +/** Event handler for Ethernet events */ +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}; + esp_eth_handle_t eth_handle = *(esp_eth_handle_t *)event_data; + esp_netif_t *eth_netif = (esp_netif_t *)arg; + + switch (event_id) { + case ETHERNET_EVENT_CONNECTED: + esp_eth_ioctl(eth_handle, ETH_CMD_G_MAC_ADDR, mac_addr); + ESP_LOGI(TAG, "Ethernet Link Up"); + ESP_LOGI(TAG, "Ethernet HW Addr %02x:%02x:%02x:%02x:%02x:%02x", + mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); + { + esp_err_t err = esp_netif_dhcps_start(eth_netif); + if (err != ESP_OK) { + ESP_LOGW(TAG, "DHCP server start: %s", esp_err_to_name(err)); + } else { + ESP_LOGI(TAG, "DHCP server started on %s", esp_netif_get_desc(eth_netif)); + } + } + break; + /* Optional: call esp_netif_dhcps_stop(eth_netif) here if link-down should tear down DHCP. */ + case ETHERNET_EVENT_DISCONNECTED: + ESP_LOGI(TAG, "Ethernet Link Down"); + break; + case ETHERNET_EVENT_START: + ESP_LOGI(TAG, "Ethernet Started"); + break; + case ETHERNET_EVENT_STOP: + ESP_LOGI(TAG, "Ethernet Stopped"); + break; + default: + break; + } +} + +/** Event handler for WiFi events */ +static void wifi_event_handler(void *arg, esp_event_base_t event_base, + int32_t event_id, void *event_data) +{ + if (event_base == WIFI_EVENT) { + ESP_LOGI(TAG, "Wi-Fi Event: base=%s, id=%ld", event_base, event_id); + switch (event_id) { + case WIFI_EVENT_STA_START: + ESP_LOGI(TAG, "Wi-Fi STA started"); + esp_wifi_connect(); + break; + case WIFI_EVENT_STA_STOP: + ESP_LOGI(TAG, "Wi-Fi STA stopped"); + break; + case WIFI_EVENT_STA_CONNECTED: + ESP_LOGI(TAG, "Wi-Fi STA connected"); + break; + case WIFI_EVENT_STA_DISCONNECTED: + ESP_LOGI(TAG, "Wi-Fi STA disconnected, retrying..."); + esp_wifi_connect(); + break; + default: + ESP_LOGW(TAG, "Unhandled Wi-Fi event: id=%ld", event_id); + break; + } + } else if (event_base == IP_EVENT && event_id == IP_EVENT_STA_GOT_IP) { + ip_event_got_ip_t* event = (ip_event_got_ip_t*) event_data; + const esp_netif_ip_info_t *ip_info = &event->ip_info; + esp_netif_t *netif = event->esp_netif; + esp_netif_dns_info_t dns_info; + + ESP_LOGI(TAG, "Wi-Fi Got IP Address"); + ESP_LOGI(TAG, "Event: base=%s, id=%ld", event_base, event_id); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "STAIP:" IPSTR, IP2STR(&ip_info->ip)); + ESP_LOGI(TAG, "STAMASK:" IPSTR, IP2STR(&ip_info->netmask)); + ESP_LOGI(TAG, "STAGW:" IPSTR, IP2STR(&ip_info->gw)); + + // Print DHCP DNS information + if (esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info) == ESP_OK) { + ESP_LOGI(TAG, "DHCP_DNS_MAIN:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + } + if (esp_netif_get_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_info) == ESP_OK) { + ESP_LOGI(TAG, "DHCP_DNS_BACKUP:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + } + + ESP_LOGI(TAG, "~~~~~~~~~~~"); + + xEventGroupSetBits(event_group, EXAMPLE_WIFI_STA_GOT_IP_BIT); + } +} + +/** Event handler for IP_EVENT_ETH_GOT_IP */ +static void got_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; + esp_netif_t *netif = event->esp_netif; + esp_netif_dns_info_t dns_info; + + ESP_LOGI(TAG, "Ethernet Got IP Address"); + ESP_LOGI(TAG, "Event: base=%s, id=%ld", event_base, event_id); + ESP_LOGI(TAG, "~~~~~~~~~~~"); + ESP_LOGI(TAG, "ETHIP:" IPSTR, IP2STR(&ip_info->ip)); + ESP_LOGI(TAG, "ETHMASK:" IPSTR, IP2STR(&ip_info->netmask)); + ESP_LOGI(TAG, "ETHGW:" IPSTR, IP2STR(&ip_info->gw)); + + // Print DNS information + if (esp_netif_get_dns_info(netif, ESP_NETIF_DNS_MAIN, &dns_info) == ESP_OK) { + ESP_LOGI(TAG, "DHCP_DNS_MAIN:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + } + if (esp_netif_get_dns_info(netif, ESP_NETIF_DNS_BACKUP, &dns_info) == ESP_OK) { + ESP_LOGI(TAG, "DHCP_DNS_BACKUP:" IPSTR, IP2STR(&dns_info.ip.u_addr.ip4)); + } + + ESP_LOGI(TAG, "~~~~~~~~~~~"); +} + +static wifi_auth_mode_t example_wifi_sta_authmode_threshold(void) +{ +#if CONFIG_EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_OPEN + return WIFI_AUTH_OPEN; +#elif CONFIG_EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WEP + return WIFI_AUTH_WEP; +#elif CONFIG_EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA_PSK + return WIFI_AUTH_WPA_PSK; +#elif CONFIG_EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA2_PSK + return WIFI_AUTH_WPA2_PSK; +#elif CONFIG_EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA_WPA2_PSK + return WIFI_AUTH_WPA_WPA2_PSK; +#elif CONFIG_EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA3_PSK + return WIFI_AUTH_WPA3_PSK; +#else + return WIFI_AUTH_WPA2_PSK; +#endif +} + +/** Initialize WiFi STA */ +static void wifi_init_sta(void) +{ + ESP_ERROR_CHECK(esp_event_handler_register(WIFI_EVENT, ESP_EVENT_ANY_ID, wifi_event_handler, NULL)); + wifi_init_config_t cfg = WIFI_INIT_CONFIG_DEFAULT(); + ESP_ERROR_CHECK(esp_wifi_init(&cfg)); + ESP_ERROR_CHECK(esp_wifi_set_storage(WIFI_STORAGE_RAM)); + wifi_config_t wifi_config = { + .sta = { + .ssid = CONFIG_EXAMPLE_ESP_WIFI_SSID, + .password = CONFIG_EXAMPLE_ESP_WIFI_PASS, + .threshold.authmode = example_wifi_sta_authmode_threshold(), + }, + }; + ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA)); + ESP_ERROR_CHECK(esp_wifi_set_config(WIFI_IF_STA, &wifi_config)); + ESP_ERROR_CHECK(esp_wifi_start()); + + ESP_LOGI(TAG, "Wi-Fi STA initialized. SSID:%s password:%s", CONFIG_EXAMPLE_ESP_WIFI_SSID, CONFIG_EXAMPLE_ESP_WIFI_PASS); +} + +void app_main(void) +{ + esp_err_t ret = nvs_flash_init(); + if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND) { + ESP_ERROR_CHECK(nvs_flash_erase()); + ret = nvs_flash_init(); + } + ESP_ERROR_CHECK(ret); + + ESP_ERROR_CHECK(esp_netif_init()); + ESP_ERROR_CHECK(esp_event_loop_create_default()); + + event_group = xEventGroupCreate(); + + // Initialize Ethernet driver + uint8_t eth_port_cnt = 0; + esp_eth_handle_t *eth_handles = NULL; + ESP_ERROR_CHECK(ethernet_init_all(ð_handles, ð_port_cnt)); + + if (eth_port_cnt == 0) { + ESP_LOGE(TAG, "No Ethernet interface initialized"); + return; + } + if (eth_port_cnt > 1) { + ESP_LOGW(TAG, "%u Ethernet interfaces initialized; this example only uses the first one", eth_port_cnt); + } + + // Static IP configuration for Ethernet gateway + static esp_netif_ip_info_t s_eth_gw_ip_info; + memset(&s_eth_gw_ip_info, 0, sizeof(esp_netif_ip_info_t)); + + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_GATEWAY_IP_ADDR, &s_eth_gw_ip_info.ip) != ESP_OK) { + ESP_LOGE(TAG, "Invalid IP address: %s", CONFIG_EXAMPLE_ETH_GATEWAY_IP_ADDR); + return; + } + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_GATEWAY_NETMASK, &s_eth_gw_ip_info.netmask) != ESP_OK) { + ESP_LOGE(TAG, "Invalid netmask: %s", CONFIG_EXAMPLE_ETH_GATEWAY_NETMASK); + return; + } + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_GATEWAY_GW, &s_eth_gw_ip_info.gw) != ESP_OK) { + ESP_LOGE(TAG, "Invalid gateway: %s", CONFIG_EXAMPLE_ETH_GATEWAY_GW); + return; + } + + static esp_netif_inherent_config_t inherent_config = { + .flags = ESP_NETIF_DHCP_SERVER, + ESP_COMPILER_DESIGNATED_INIT_AGGREGATE_TYPE_EMPTY(mac) + .ip_info = &s_eth_gw_ip_info, + .get_ip_event = 0, + .lost_ip_event = 0, + .if_key = "ETH_GW", + .if_desc = "eth_gw", + .route_prio = 50, + .bridge_info = NULL + }; + + esp_netif_config_t cfg = ESP_NETIF_DEFAULT_ETH(); + cfg.base = &inherent_config; + + esp_netif_t *eth_netif = esp_netif_new(&cfg); + ESP_ERROR_CHECK(eth_netif != NULL ? ESP_OK : ESP_FAIL); + + /* Configure DHCP server options */ + // Set lease time + uint32_t lease_time = CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_LEASE_TIME; + esp_err_t err = esp_netif_dhcps_option(eth_netif, ESP_NETIF_OP_SET, IP_ADDRESS_LEASE_TIME, &lease_time, sizeof(lease_time)); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to set DHCP lease time: %s", esp_err_to_name(err)); + } + + // Configure IP address pool + dhcps_lease_t dhcp_lease; + memset(&dhcp_lease, 0, sizeof(dhcps_lease_t)); + dhcp_lease.enable = true; + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_START_ADDR, (esp_ip4_addr_t *)&dhcp_lease.start_ip) != ESP_OK) { + ESP_LOGW(TAG, "Invalid DHCP start address: %s, using default pool", CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_START_ADDR); + } else if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_END_ADDR, (esp_ip4_addr_t *)&dhcp_lease.end_ip) != ESP_OK) { + ESP_LOGW(TAG, "Invalid DHCP end address: %s, using default pool", CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_END_ADDR); + } else { + err = esp_netif_dhcps_option(eth_netif, ESP_NETIF_OP_SET, REQUESTED_IP_ADDRESS, &dhcp_lease, sizeof(dhcps_lease_t)); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to set DHCP IP pool: %s", esp_err_to_name(err)); + } + } + + // Configure DNS servers if enabled +#if CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + dhcps_offer_t dhcps_dns_value = OFFER_DNS; + err = esp_netif_dhcps_option(eth_netif, ESP_NETIF_OP_SET, ESP_NETIF_DOMAIN_NAME_SERVER, &dhcps_dns_value, sizeof(dhcps_dns_value)); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to enable DNS in DHCP offers: %s", esp_err_to_name(err)); + } else { + // Set DNS server addresses (will be updated from WiFi STA DNS if available) + esp_netif_dns_info_t dns_info = {}; + dns_info.ip.type = ESP_IPADDR_TYPE_V4; + + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_DNS_MAIN, &dns_info.ip.u_addr.ip4) == ESP_OK) { + err = esp_netif_set_dns_info(eth_netif, ESP_NETIF_DNS_MAIN, &dns_info); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to set primary DNS: %s", esp_err_to_name(err)); + } + } + + if (esp_netif_str_to_ip4(CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_DNS_BACKUP, &dns_info.ip.u_addr.ip4) == ESP_OK) { + err = esp_netif_set_dns_info(eth_netif, ESP_NETIF_DNS_BACKUP, &dns_info); + if (err != ESP_OK) { + ESP_LOGW(TAG, "Failed to set backup DNS: %s", esp_err_to_name(err)); + } + } + } +#endif // CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + + ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, ð_event_handler, eth_netif)); + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_ETH_GOT_IP, &got_ip_event_handler, NULL)); + + ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handles[0]))); + ESP_ERROR_CHECK(esp_eth_start(eth_handles[0])); + + ESP_LOGI(TAG, "Ethernet gateway ready (static IP: " IPSTR ", netmask: " IPSTR "). DHCP starts when link is up.", + IP2STR(&s_eth_gw_ip_info.ip), IP2STR(&s_eth_gw_ip_info.netmask)); + ESP_LOGI(TAG, "Connect a device to the Ethernet port to get an IP via DHCP"); + + // Initialize WiFi STA + ESP_ERROR_CHECK(esp_event_handler_register(IP_EVENT, IP_EVENT_STA_GOT_IP, wifi_event_handler, NULL)); + esp_netif_t *sta_netif = esp_netif_create_default_wifi_sta(); + ESP_ERROR_CHECK(sta_netif != NULL ? ESP_OK : ESP_FAIL); + wifi_init_sta(); + + // Wait for WiFi STA to get IP address + xEventGroupWaitBits(event_group, EXAMPLE_WIFI_STA_GOT_IP_BIT, pdTRUE, pdTRUE, portMAX_DELAY); + + // Update Ethernet gateway DNS with Wi-Fi STA DNS if enabled +#if CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + esp_netif_dns_info_t sta_dns_info; + if (esp_netif_get_dns_info(sta_netif, ESP_NETIF_DNS_MAIN, &sta_dns_info) == ESP_OK) { + esp_netif_dns_info_t eth_dns_info = {}; + eth_dns_info.ip.type = ESP_IPADDR_TYPE_V4; + eth_dns_info.ip.u_addr.ip4 = sta_dns_info.ip.u_addr.ip4; + err = esp_netif_set_dns_info(eth_netif, ESP_NETIF_DNS_MAIN, ð_dns_info); + if (err == ESP_OK) { + ESP_LOGI(TAG, "Updated Ethernet gateway DNS with Wi-Fi STA DNS: " IPSTR, IP2STR(&sta_dns_info.ip.u_addr.ip4)); + } + } +#endif // CONFIG_EXAMPLE_ETH_GATEWAY_DHCP_ENABLE_DNS + +#if CONFIG_LWIP_IPV4_NAPT + // Setup NAPT (Network Address Port Translation) if enabled + ESP_ERROR_CHECK(esp_netif_napt_enable(eth_netif)); + ESP_LOGI(TAG, "NAPT enabled on Ethernet gateway"); +#endif +} diff --git a/examples/esp_netif/eth_gateway_wifi_sta/main/idf_component.yml b/examples/esp_netif/eth_gateway_wifi_sta/main/idf_component.yml new file mode 100644 index 0000000000..119518cbd3 --- /dev/null +++ b/examples/esp_netif/eth_gateway_wifi_sta/main/idf_component.yml @@ -0,0 +1,6 @@ +dependencies: + idf: "*" + espressif/ethernet_init: + version: "~1.2.0" + rules: + - if: "target != linux" diff --git a/examples/esp_netif/eth_gateway_wifi_sta/sdkconfig.defaults b/examples/esp_netif/eth_gateway_wifi_sta/sdkconfig.defaults new file mode 100644 index 0000000000..fb6e2cb762 --- /dev/null +++ b/examples/esp_netif/eth_gateway_wifi_sta/sdkconfig.defaults @@ -0,0 +1,16 @@ +# This file was generated using idf.py save-defconfig. It can be edited manually. +# Espressif IoT Development Framework (ESP-IDF) Project Minimal Configuration +# +# WiFi Station Configuration +CONFIG_EXAMPLE_ESP_WIFI_SSID="myssid" +CONFIG_EXAMPLE_ESP_WIFI_PASS="mypassword" +CONFIG_EXAMPLE_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA2_PSK=y +# +# Ethernet gateway configuration +CONFIG_EXAMPLE_ETH_GATEWAY_IP_ADDR="192.168.5.1" +CONFIG_EXAMPLE_ETH_GATEWAY_NETMASK="255.255.255.0" +CONFIG_EXAMPLE_ETH_GATEWAY_GW="192.168.5.1" +# +# NAPT Configuration (optional) +CONFIG_LWIP_IP_FORWARD=y +CONFIG_LWIP_IPV4_NAPT=y diff --git a/examples/esp_netif/multiple_netifs/main/Kconfig.projbuild b/examples/esp_netif/multiple_netifs/main/Kconfig.projbuild index 3929fa20ea..edf50e39e5 100644 --- a/examples/esp_netif/multiple_netifs/main/Kconfig.projbuild +++ b/examples/esp_netif/multiple_netifs/main/Kconfig.projbuild @@ -12,6 +12,27 @@ menu "Example Configuration" help WiFi password (WPA or WPA2) for the example to use. + choice ESP_WIFI_STA_AUTHMODE_THRESHOLD + prompt "WiFi STA scan authentication threshold" + default ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA2_PSK + help + Minimum authentication mode accepted when scanning and connecting as a station. + Lower this to connect to older networks (WEP/WPA). + + config ESP_WIFI_STA_AUTHMODE_THRESHOLD_OPEN + bool "Open" + config ESP_WIFI_STA_AUTHMODE_THRESHOLD_WEP + bool "WEP" + config ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA_PSK + bool "WPA PSK" + config ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA2_PSK + bool "WPA2 PSK" + config ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA_WPA2_PSK + bool "WPA/WPA2 PSK" + config ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA3_PSK + bool "WPA3 PSK" + endchoice + config ESP_MAXIMUM_RETRY int "Maximum retry" default 0 diff --git a/examples/esp_netif/multiple_netifs/main/wifi_connect.c b/examples/esp_netif/multiple_netifs/main/wifi_connect.c index 5723051301..f6fbab7d18 100644 --- a/examples/esp_netif/multiple_netifs/main/wifi_connect.c +++ b/examples/esp_netif/multiple_netifs/main/wifi_connect.c @@ -16,9 +16,11 @@ #include "freertos/event_groups.h" #include "esp_system.h" #include "esp_wifi.h" +#include "esp_wifi_types.h" #include "esp_event.h" #include "esp_log.h" #include "iface_info.h" +#include "sdkconfig.h" #define WIFI_CONNECTED_BIT BIT0 #define WIFI_FAIL_BIT BIT1 @@ -26,6 +28,25 @@ static const char *TAG = "wifi_connect"; static int s_retry_num = 0; + +static wifi_auth_mode_t example_wifi_sta_authmode_threshold(void) +{ +#if CONFIG_ESP_WIFI_STA_AUTHMODE_THRESHOLD_OPEN + return WIFI_AUTH_OPEN; +#elif CONFIG_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WEP + return WIFI_AUTH_WEP; +#elif CONFIG_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA_PSK + return WIFI_AUTH_WPA_PSK; +#elif CONFIG_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA2_PSK + return WIFI_AUTH_WPA2_PSK; +#elif CONFIG_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA_WPA2_PSK + return WIFI_AUTH_WPA_WPA2_PSK; +#elif CONFIG_ESP_WIFI_STA_AUTHMODE_THRESHOLD_WPA3_PSK + return WIFI_AUTH_WPA3_PSK; +#else + return WIFI_AUTH_WPA2_PSK; +#endif +} static EventGroupHandle_t s_wifi_event_group; static void event_handler(void *args, esp_event_base_t event_base, @@ -98,6 +119,7 @@ iface_info_t *example_wifi_init(int prio) .sta = { .ssid = CONFIG_ESP_WIFI_SSID, .password = CONFIG_ESP_WIFI_PASSWORD, + .threshold.authmode = example_wifi_sta_authmode_threshold(), }, }; ESP_ERROR_CHECK(esp_wifi_set_mode(WIFI_MODE_STA));