LAN9303 + ESP32 RMII Interface: Link Down Despite 50 MHz Clock & Port Connections

aniketsarwade2017
Posts: 1
Joined: Fri May 23, 2025 2:12 pm

LAN9303 + ESP32 RMII Interface: Link Down Despite 50 MHz Clock & Port Connections

Postby aniketsarwade2017 » Fri May 23, 2025 2:19 pm

Hi everyone,

I'm working on bringing up Ethernet communication between an ESP32-WROVER module and a LAN9303 3-port Ethernet switch using RMII.

🔧 Hardware Setup:
ESP32 is running ESP-IDF v5.4.1

LAN9303 is connected in RMII PHY mode (Port 0)

P0_MODE[2:0] = 100 strapping: RMII PHY mode, 12 mA clock out

LAN9303 provides 50 MHz REF_CLK to ESP32 on GPIO0 (verified with oscilloscope)

CRS_DV is connected to GPIO27

LAN9303 Port 1 and Port 2 (P1/P2) are connected to RJ45 jacks

Cables from P1/P2 are plugged into active network devices (router/laptop)

Using integrated magnetics on RJ45 jack

✅ What’s Working:
Clock signal is stable on GPIO0 (50 MHz)

I can see esp_eth_start() completes

Static IP is configured and printed correctly

❌ Problem:
Link never comes up — phy->get_link() always returns false

Ethernet LEDs turn OFF when a cable is plugged in

Using esp_eth_phy_new_lan9303() as a wrapper around esp_eth_phy_new_ip101() with chip ID check bypassed

Eventually, I hit a Guru Meditation Error at ip101_page_select() due to calling IP101-specific methods on a LAN9303 device

💡 What I’ve Tried:
Verified correct REF_CLK input and CRS_DV pin

Tried PHY addresses from 0 to 7

Verified RJ45 magnetics and pinouts

Tried replacing phy->get_link() with direct MII register reads (but no LAN9303-specific driver exists yet)

Bypassed chip ID check by overriding phy->init

❓ What I Need Help With:
Has anyone successfully written or used a custom PHY driver for LAN9303 in ESP-IDF?

Is there a reference for reading link status (bit in MII register 1, Basic Status Register)?

Am I missing any strapping configurations on LAN9303 that disable port functionality or isolate P1/P2?

Can someone from Espressif confirm whether esp_eth_phy_new_ip101() logic is fundamentally incompatible with switches like LAN9303?

📎 Notes:
I’m not using EEPROM or I²C config — just default strap-based config

I can build and integrate a custom driver if I know which functions I must implement minimally for esp_eth_driver_install() to succeed

Thanks in advance for any guidance. I'm happy to share schematics, board layout, or logic traces if needed.




#include "esp_event.h"
#include "esp_log.h"
#include "esp_netif.h"
#include "esp_eth.h"
#include "esp_eth_mac.h"
#include "esp_eth_phy.h"
#include "esp_eth_driver.h"
#include "driver/gpio.h"
#include "sdkconfig.h"
#include "phy_lan9303.h"
#include "lwip/inet.h"
#include <inttypes.h>
#include "esp_ping.h"
#include "ping/ping_sock.h" // Required for esp_ping callbacks
static const char *TAG = "eth_main";

// Event handler for Ethernet state
static void eth_event_handler(void *arg, esp_event_base_t event_base,
int32_t event_id, void *event_data) {
switch (event_id) {
case ETHERNET_EVENT_CONNECTED:
ESP_LOGI(TAG, "✅ Ethernet Link Up");
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;
}
}
static void on_ping_success(esp_ping_handle_t hdl, void *args) {
uint8_t ttl;
uint16_t seqno;
uint32_t elapsed_time, recv_len;
ip_addr_t target_addr;

esp_ping_get_profile(hdl, ESP_PING_PROF_SEQNO, &seqno, sizeof(seqno));
esp_ping_get_profile(hdl, ESP_PING_PROF_TTL, &ttl, sizeof(ttl));
esp_ping_get_profile(hdl, ESP_PING_PROF_IPADDR, &target_addr, sizeof(target_addr));
esp_ping_get_profile(hdl, ESP_PING_PROF_SIZE, &recv_len, sizeof(recv_len));
esp_ping_get_profile(hdl, ESP_PING_PROF_TIMEGAP, &elapsed_time, sizeof(elapsed_time));
ESP_LOGI(TAG, "🎯 Ping success: seq=%d, ttl=%d, time=%d ms, from=" IPSTR,
(int)seqno, (int)ttl, (int)elapsed_time, IP2STR(&target_addr.u_addr.ip4));

}

static void on_ping_end(esp_ping_handle_t hdl, void *args) {
ESP_LOGI(TAG, "✅ Ping session complete.");
esp_ping_delete_session(hdl);
}

void app_main(void) {
ESP_LOGI(TAG, "🚀 Starting Ethernet with Static IP");

// Init TCP/IP and event loop
ESP_ERROR_CHECK(esp_netif_init());
ESP_ERROR_CHECK(esp_event_loop_create_default());

// Create default Ethernet netif
esp_netif_config_t netif_cfg = ESP_NETIF_DEFAULT_ETH();
esp_netif_t *eth_netif = esp_netif_new(&netif_cfg);

// Register Ethernet event handler
ESP_ERROR_CHECK(esp_event_handler_register(ETH_EVENT, ESP_EVENT_ANY_ID, &eth_event_handler, NULL));

// Use static IP
ESP_ERROR_CHECK(esp_netif_dhcpc_stop(eth_netif));

esp_netif_ip_info_t ip_info;
ip_info.ip.addr = ipaddr_addr("192.168.20.249");
ip_info.gw.addr = ipaddr_addr("192.168.20.1");
ip_info.netmask.addr = ipaddr_addr("255.255.255.0");
ESP_ERROR_CHECK(esp_netif_set_ip_info(eth_netif, &ip_info));

// Configure MAC
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
eth_esp32_emac_config_t esp32_emac_config = {
.smi_mdc_gpio_num = 23,
.smi_mdio_gpio_num = 18,
.interface = EMAC_DATA_INTERFACE_RMII,
.clock_config = {
.rmii = {
.clock_mode = EMAC_CLK_EXT_IN, // Using 50 MHz from LAN9303
.clock_gpio = 0 // GPIO0 = REF_CLK input
}
}
};
esp_eth_mac_t *mac = esp_eth_mac_new_esp32(&esp32_emac_config, &mac_config);

// Configure PHY
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
phy_config.phy_addr = 1; // Default PHY address
phy_config.reset_gpio_num = -1; // No reset GPIO used
esp_eth_phy_t *phy = esp_eth_phy_new_lan9303(&phy_config);

// Create Ethernet driver config
esp_eth_config_t config = {
.mac = mac,
.phy = phy,
.check_link_period_ms = 2000,
};

// Install Ethernet driver
esp_eth_handle_t eth_handle = NULL;
ESP_ERROR_CHECK(esp_eth_driver_install(&config, &eth_handle));
ESP_ERROR_CHECK(esp_netif_attach(eth_netif, esp_eth_new_netif_glue(eth_handle)));

// Start Ethernet
ESP_ERROR_CHECK(esp_eth_start(eth_handle));

vTaskDelay(pdMS_TO_TICKS(1000)); // Allow time for PHY to settle
for (int i = 0; i < 10; ++i) {
bool link_up = phy->get_link(phy);
ESP_LOGI(TAG, "Link status [%d/10]: %s", i + 1, link_up ? "UP" : "DOWN");
vTaskDelay(pdMS_TO_TICKS(1000));
}
// ✅ Check link status directly via PHY driver
if (phy && phy->get_link) {
bool link_up = phy->get_link(phy);
if (link_up) {
ESP_LOGI(TAG, "✅ Link is UP (via PHY->get_link)");
} else {
ESP_LOGW(TAG, "❌ Link is DOWN (via PHY->get_link)");
}
}

// Print current IP info
esp_netif_ip_info_t current_ip;
ESP_ERROR_CHECK(esp_netif_get_ip_info(eth_netif, &current_ip));
ESP_LOGI(TAG, "🔗 Static IP: " IPSTR, IP2STR(&current_ip.ip));
ESP_LOGI(TAG, "🛡️ Gateway: " IPSTR, IP2STR(&current_ip.gw));
ESP_LOGI(TAG, "📐 Netmask: " IPSTR, IP2STR(&current_ip.netmask));
}
I (230) app_init: App version: 1
I (233) app_init: Compile time: May 23 2025 14:40:25
I (238) app_init: ELF file SHA256: 509ff8635...
I (242) app_init: ESP-IDF: v5.4.1-dirty
I (247) efuse_init: Min chip rev: v0.0
I (251) efuse_init: Max chip rev: v3.99
I (255) efuse_init: Chip rev: v3.0
I (259) heap_init: Initializing. RAM available for dynamic allocation:
I (265) heap_init: At 3FFAE6E0 len 00001920 (6 KiB): DRAM
I (270) heap_init: At 3FFB3728 len 0002C8D8 (178 KiB): DRAM
I (275) heap_init: At 3FFE0440 len 00003AE0 (14 KiB): D/IRAM
I (280) heap_init: At 3FFE4350 len 0001BCB0 (111 KiB): D/IRAM
I (286) heap_init: At 4008DA48 len 000125B8 (73 KiB): IRAM
I (293) spi_flash: detected chip: generic
I (295) spi_flash: flash io: dio
W (298) spi_flash: Detected size(16384k) larger than the size in the binary image header(2048k). Using the size in the binary image header.
I (311) main_task: Started on CPU0
I (321) main_task: Calling app_main()
I (321) eth_main: 🚀 Starting Ethernet with Static IP
E (341) ip101: ip101_init(189): wrong chip ID
W (341) lan9303: Bypassing chip ID mismatch for LAN9303
I (341) esp_eth.netif.netif_glue: 48:55:19:bc:53:13
I (341) esp_eth.netif.netif_glue: ethernet attached to netif
I (341) eth_main: ⚙️ Ethernet Started
I (1351) eth_main: Link status [1/10]: DOWN
I (2351) eth_main: Link status [2/10]: DOWN
I (3351) eth_main: Link status [3/10]: DOWN
I (4351) eth_main: Link status [4/10]: DOWN
I (5351) eth_main: Link status [5/10]: DOWN
I (6351) eth_main: Link status [6/10]: DOWN
I (7351) eth_main: Link status [7/10]: DOWN
I (8351) eth_main: Link status [8/10]: DOWN
I (9351) eth_main: Link status [9/10]: DOWN
I (10351) eth_main: Link status [10/10]: DOWN
W (11351) eth_main: ❌ Link is DOWN (via PHY->get_link)
I (11351) eth_main: 🔗 Static IP: 192.168.20.249
I (11351) eth_main: 🛡️ Gateway: 192.168.20.1
I (11351) eth_main: 📐 Netmask: 255.255.255.0
I (11351) main_task: Returned from app_main()

ondrej
Espressif staff
Espressif staff
Posts: 227
Joined: Fri May 07, 2021 10:35 am

Re: LAN9303 + ESP32 RMII Interface: Link Down Despite 50 MHz Clock & Port Connections

Postby ondrej » Fri Jun 13, 2025 8:15 am

If you don't need to manage the switch, try the approach demonstrated at https://github.com/espressif/esp-eth-dr ... ric_switch

Who is online

Users browsing this forum: PerplexityBot, Qwantbot, YisouSpider and 6 guests