Hi everyone,
I'm testing a W5500 Ethernet module with my ESP32 board using SPI. I'm able to successfully communicate with the chip — I can read the version register (0x04) and the PHY configuration register (0xBA). So SPI and reset seem to be working fine.
However, the W5500 reports Link: DOWN, even though the Ethernet cable is connected to a live switch/router port. Here's the serial output:
W5500 Version: 0x4
PHYCFGR: 0xBA
PHY Active: Yes
PHY Mode Configured by: Hardware pins (PMODE[2:0])
Operation Mode: All Capable, Auto-negotiation Enabled
Duplex: Half Duplex
Speed: 100 Mbps
Link: DOWN
#include <SPI.h>
//#define W5500_CS 10 //uno
#define W5500_CS 10 // Chip Select pin
#define W5500_MISO 13 // MISO (Master In Slave Out)
#define W5500_MOSI 11 // MOSI (Master Out Slave In)
#define W5500_SCK 12 // Serial Clock
#define led 21
#define W5500_RST 3 // GPIO 3 connected to W5500 RESET pin
void resetW5500() {
pinMode(W5500_RST, OUTPUT);
digitalWrite(W5500_RST, LOW); // Start reset
delayMicroseconds(1000); // Hold low for at least 500 µs (we use 1000 for safety)
digitalWrite(W5500_RST, HIGH); // Release reset
delay(100); // Wait 100 ms for W5500 to stabilize
}
void setup() {
Serial.begin(115200);
while (!Serial);
resetW5500();
Serial.println("resetted");
// Initialize SPI with custom pins
SPI.begin(W5500_SCK, W5500_MISO, W5500_MOSI, W5500_CS);
//SPI.begin();
pinMode(W5500_CS, OUTPUT);
pinMode(led, OUTPUT);
pinMode(20, INPUT);
digitalWrite(W5500_CS, HIGH); // Deselect W5500 module
Serial.println("Testing SPI communication with W5500...");
}
void loop() {
digitalWrite(led, 1);
int btn = digitalRead(20);
Serial.print("Button: ");
Serial.println(btn);
// Check link status
uint8_t linkStatus = checkLinkStatus();
if (linkStatus == 1) {
Serial.println("Link is up and connected!");
} else {
Serial.println("No link detected.");
}
// Check PHY status
uint8_t phyStatus = readPHYStatus();
Serial.print("PHY Status: 0x");
Serial.println(phyStatus, HEX);
if (phyStatus & 0x01) {
Serial.println("PHY Link is up!");
} else {
Serial.println("PHY No link detected.");
}
// Read W5500 version
uint8_t version = readW5500Version();
Serial.print("W5500 Version: 0x");
Serial.println(version, HEX);
printPHYStatus();
if(btn == 0){
// softPHYReset();
digitalWrite(led, 0);
}
delay(100); // Wait before checking again
}
uint8_t checkLinkStatus() {
uint8_t highByte = 0x00; // Address high byte for the status register (0x0001)
uint8_t lowByte = 0x01; // Address low byte for the status register (0x0001)
digitalWrite(W5500_CS, LOW); // Select W5500
// Send the address (high byte, low byte) and control byte for a read operation
SPI.transfer(highByte); // Send high byte of the address
SPI.transfer(lowByte); // Send low byte of the address
SPI.transfer(0x00); // Control byte for read operation
uint8_t status = SPI.transfer(0x00); // Receive the data
digitalWrite(W5500_CS, HIGH); // Deselect W5500
// Check if link is up (bit 0 of status register)
return (status & 0x01); // Returns 1 if link is up, 0 if no link
}
uint8_t readPHYStatus() {
uint8_t highByte = 0x00; // Address high byte for PHY status register (0x0035)
uint8_t lowByte = 0x35; // Address low byte for PHY status register (0x0035)
digitalWrite(W5500_CS, LOW); // Select W5500
// Send the address (high byte, low byte) and control byte for a read operation
SPI.transfer(highByte); // Send high byte of the address
SPI.transfer(lowByte); // Send low byte of the address
SPI.transfer(0x00); // Control byte for read operation
uint8_t status = SPI.transfer(0x00); // Receive the data
digitalWrite(W5500_CS, HIGH); // Deselect W5500
return status;
}
uint8_t readW5500Version() {
uint8_t highByte = 0x00; // Address high byte for VERSIONR register (0x0039)
uint8_t lowByte = 0x39; // Address low byte for VERSIONR register (0x0039)
digitalWrite(W5500_CS, LOW); // Select W5500
// Send the address (high byte, low byte) and control byte for a read operation
SPI.transfer(highByte); // Send high byte of the address
SPI.transfer(lowByte); // Send low byte of the address
SPI.transfer(0x00); // Control byte for read operation
uint8_t result = SPI.transfer(0x00); // Receive the data
digitalWrite(W5500_CS, HIGH); // Deselect W5500
return result;
}
uint8_t readPHYCFGR() {
uint8_t highByte = 0x00; // Address high byte for PHYCFGR (0x002E)
uint8_t lowByte = 0x2E; // Address low byte for PHYCFGR (0x002E)
digitalWrite(W5500_CS, LOW); // Select W5500
SPI.transfer(highByte); // Send high byte of the address
SPI.transfer(lowByte); // Send low byte of the address
SPI.transfer(0x00); // Control byte for read operation
uint8_t status = SPI.transfer(0x00); // Receive the data
digitalWrite(W5500_CS, HIGH); // Deselect W5500
return status;
}
void printPHYStatus() {
uint8_t phycfgr = readPHYCFGR();
// Print the entire PHYCFGR register value
Serial.print("PHYCFGR: 0x");
Serial.println(phycfgr, HEX);
// Bit 7: PHY Reset Status
Serial.print("PHY Active: ");
Serial.println((phycfgr & 0x80) ? "Yes" : "No");
// Bit 6: PHY Operation Mode Configuration Source
Serial.print("PHY Mode Configured by: ");
Serial.println((phycfgr & 0x40) ? "OPMDC bits" : "Hardware pins (PMODE[2:0])");
// Bits 5-3: Operation Mode (OPMDC)
Serial.print("Operation Mode: ");
uint8_t opmdc = (phycfgr >> 3) & 0x07; // Extract bits 5, 4, and 3
switch (opmdc) {
case 0b000: Serial.println("10BT Half-Duplex, Auto-negotiation Disabled"); break;
case 0b001: Serial.println("10BT Full-Duplex, Auto-negotiation Disabled"); break;
case 0b010: Serial.println("100BT Half-Duplex, Auto-negotiation Disabled"); break;
case 0b011: Serial.println("100BT Full-Duplex, Auto-negotiation Disabled"); break;
case 0b100: Serial.println("100BT Half-Duplex, Auto-negotiation Enabled"); break;
case 0b101: Serial.println("100BT Full-Duplex, Auto-negotiation Enabled"); break;
case 0b110: Serial.println("Power Down Mode"); break;
case 0b111: Serial.println("All Capable, Auto-negotiation Enabled"); break;
default: Serial.println("Unknown Mode"); break;
}
// Bit 2: Duplex Status (DPX)
Serial.print("Duplex: ");
Serial.println((phycfgr & 0x04) ? "Full Duplex" : "Half Duplex");
// Bit 1: Speed Status (SPD)
Serial.print("Speed: ");
Serial.println((phycfgr & 0x02) ? "100 Mbps" : "10 Mbps");
// Bit 0: Link Status (LNK)
Serial.print("Link: ");
Serial.println((phycfgr & 0x01) ? "UP" : "DOWN");
}
void softPHYReset() {
uint8_t highByte = 0x00; // Address high byte for PHYCFGR register (0x002E)
uint8_t lowByte = 0x2E; // Address low byte for PHYCFGR register (0x002E)
digitalWrite(W5500_CS, LOW); // Select W5500
// Send the address (high byte, low byte) and control byte for a read operation
SPI.transfer(highByte); // Send high byte of the address
SPI.transfer(lowByte); // Send low byte of the address
SPI.transfer(0x00); // Control byte for read operation
uint8_t phyConfig = SPI.transfer(0x00); // Receive the PHYCFGR register value
digitalWrite(W5500_CS, HIGH); // Deselect W5500
// Perform the reset operation
digitalWrite(W5500_CS, LOW); // Select W5500
SPI.transfer(highByte); // Send high byte of the address
SPI.transfer(lowByte); // Send low byte of the address
SPI.transfer(0x04); // Control byte for write operation
SPI.transfer(phyConfig & 0x7F); // Clear the 7th bit (RST = 0) to reset
digitalWrite(W5500_CS, HIGH); // Deselect W5500
delay(10); // Wait for reset to take effect
// Set the 7th bit back to 1 to resume normal operation
digitalWrite(W5500_CS, LOW); // Select W5500
SPI.transfer(highByte); // Send high byte of the address
SPI.transfer(lowByte); // Send low byte of the address
SPI.transfer(0x04); // Control byte for write operation
SPI.transfer(phyConfig | 0x80); // Set the 7th bit (RST = 1) to resume normal operation
digitalWrite(W5500_CS, HIGH); // Deselect W5500
Serial.println("RESET complete");
delay(1000);
}
![Screenshot 2025-06-11 201043|690x458]
What I've Tried:
- Different cables
- Different router ports
- Verified PMODE[2:0] =
111(All Capable + Auto-negotiation) - Checked power and reset timing
Question:
Why does the W5500 PHY report Link: DOWN even though it's connected to a working Ethernet network? Is there anything I’m missing in the physical setup or configuration?
This arduino code is working in ESP32 s3 module + w5500 ethernet shield ,but not working in custom board?
Any help or suggestions are appreciated!
Thanks in advance.








