ESP-IDF Component

Net-Switcher - WiFi/Ethernet Failover

2025
C / ESP-IDF

WiFi/Ethernet network switcher for ESP-IDF with preference-based routing, automatic failover, 3-tier DNS resolution, pre/post switch callbacks, and DHCP management. 18-function API for dual-interface IoT devices that need zero-downtime connectivity.

Technical Specs

Interfaces
WiFi STA + Ethernet (W5500, etc.)
Failover
Automatic preference-based switching
DNS Strategy
3-tier (interface → gateway → 8.8.8.8)
Callbacks
Pre-switch + post-switch hooks
API Surface
18 functions, 1 enum, 1 callback type
DHCP
Auto-stop on inactive interface
Dependencies
esp_netif, lwip, esp_event
Target Chips
ESP32, S2, S3, C3, C5, C6, H2
ESP-IDF Version
≥ 5.0.0
Configuration
Runtime only (no Kconfig)
Version
1.0.0
License
MIT
WiFiEthernetESP-IDFW5500FailoverDNSlwIPesp_netifDual-InterfaceIoTMIT License

Preference-Based Failover Logic

The core of Net-Switcher is a preference-based routing decision in net_switcher_update(). You set a preferred interface (Ethernet or WiFi), and the switcher always routes through it when available - falling back to the other interface only when the preferred one drops. When the preferred link comes back, it switches back automatically. The user wires this into their WiFi/IP event handlers by calling net_switcher_setWiFiConnected() or net_switcher_setEthernetConnected() then net_switcher_update() - keeping the component event-agnostic so it works with any network stack setup.

The switching decision is deterministic: preferred + available = use it; preferred + unavailable = failover; neither available = do nothing. net_switcher_switchTo() provides a manual override that bypasses the preference logic entirely for cases where the application needs direct control.

3-Tier DNS Resolution

Network switches break DNS if the new interface has different name servers. The internal push_dns_to_lwip() function handles this with a 3-tier fallback: first, it reads ESP_NETIF_DNS_MAIN and ESP_NETIF_DNS_BACKUP from the target interface and pushes them to lwIP via dns_setserver(). If neither is valid, it falls back to the gateway IP (most routers forward DNS) with Google's 8.8.4.4 as backup. As a last resort, it uses 8.8.8.8 / 8.8.4.4. After every DNS change, dns_clear_cache() flushes stale entries so the first request on the new interface resolves correctly.

Switch Lifecycle & DHCP

The actual switch (do_switch()) follows a strict sequence: fire the pre-switch callback (so the app can disconnect MQTT, WebSocket, or other services), stop DHCP on the inactive interface via esp_netif_dhcpc_stop(), set the new default netif, push DNS servers, update internal state, then fire the post-switch callback (so the app can reconnect). If the target is already the active interface, it just refreshes the default netif and DNS without triggering callbacks - preventing unnecessary reconnection churn.

Minimal & Event-Agnostic Design

The component deliberately does not subscribe to ESP-IDF events internally. Instead, it exposes setWiFiConnected() / setEthernetConnected() and expects the application to call them from its own event handlers. This keeps the component decoupled from any specific WiFi or Ethernet driver setup. All state is runtime-configured through the 18-function API - there's no Kconfig, no compile-time settings. Dependencies are limited to three core ESP-IDF components: esp_netif, lwip, and esp_event.