Custom W5500 with ESP32 S3 ,SPI Working but "Link: DOWN" – No Ethernet Connection Detected

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]

:wrench: What I've Tried:

  • Different cables
  • Different router ports
  • Verified PMODE[2:0] = 111 (All Capable + Auto-negotiation)
  • Checked power and reset timing

:red_question_mark: 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.

Posting how the project is supposed to be wired when working is NOT the same as hand drawing the ACTUAL wires, then taking a picture of that and posting.

I guess it is not initialized.
why not use a library? I recommend my EthernerESP32 library


second that!
this simple example works on a ESP32_S3_DevKit_1 using the EthernetESP32 library

/*
 * HelloServer example from the ESP32 WebServer library modified for Ethernet.
 */

#include <EthernetESP32.h>
#include <WebServer.h>
#include <ESPmDNS.h>

 // ESP32_S3_DevKit_1 connections
// ESP32_S3 SCK pin GPIO12  to W5500 SCK
// ESP32_S3 MISO pin GPIO13  to W5500 MISO
// ESP32_S3 MOSI pin GPIO11  to W5500 MOSI
// ESP32_S3 SS  pin GPIO 10   to W5500 SCS



W5500Driver driver;
//ENC28J60Driver driver;
//EMACDriver driver(ETH_PHY_LAN8720);

WebServer server(80);

const int led = 13;

void handleRoot() {
  digitalWrite(led, 1);
  server.send(200, "text/plain", "hello from esp32!");
  digitalWrite(led, 0);
}

void handleNotFound() {
  digitalWrite(led, 1);
  String message = "File Not Found\n\n";
  message += "URI: ";
  message += server.uri();
  message += "\nMethod: ";
  message += (server.method() == HTTP_GET) ? "GET" : "POST";
  message += "\nArguments: ";
  message += server.args();
  message += "\n";
  for (uint8_t i = 0; i < server.args(); i++) {
    message += " " + server.argName(i) + ": " + server.arg(i) + "\n";
  }
  server.send(404, "text/plain", message);
  digitalWrite(led, 0);
}

void setup(void) {
  pinMode(led, OUTPUT);
  digitalWrite(led, 0);
  Serial.begin(115200);

  Serial.begin(115200);
  delay(500);
  while (!Serial);

  Ethernet.init(driver);

  Serial.println("Initialize Ethernet with DHCP:");
  if (Ethernet.begin()) {
    Serial.print("  DHCP assigned IP ");
    Serial.println(Ethernet.localIP());
  } else {
    Serial.println("Failed to configure Ethernet using DHCP");
    while (true) {
      delay(1);
    }
  }

  if (MDNS.begin("esp32")) {
    Serial.println("MDNS responder started");
  }

  server.on("/", handleRoot);

  server.on("/inline", []() {
    server.send(200, "text/plain", "this works as well");
  });

  server.onNotFound(handleNotFound);

  server.begin();
  Serial.println("HTTP server started");
}

void loop(void) {
  server.handleClient();
  delay(2);  //allow the cpu to switch to other tasks
}
this works on a ESP32_S3_DevKit_1 using the EthernetESP32 library

Serial monitor output

Initialize Ethernet with DHCP:
  DHCP assigned IP 192.168.1.70
MDNS responder started
HTTP server started

web client displays

This arduino code is working in ESP32 s3 module + w5500 ethernet shield ,but not working in custom board? can you review the schematic

What is listed in the schematic as R6 & R7(or R29 & R30, it is confusing to have 2 different annotations, but i mean the resistors on RXP & RXN), these resistors have the wrong value. They should be 82R not 49R. I did use some 33R dampening resistors in my circuit, but i don't know if that matters all that much.
Also i Use INTn to reset the W5500 instead of RSTn and for the rest the schematic appears to look the same as what i have. Oh except that Capacitors C36 - C41 should be 1uF not 0.1uF and of course their placement should be as close to the W5500 as possible. (that does look that way though, but the gerbers you posted have all layers in there, which makes it hard to tell)

I suggest you fix these things.

I am designed my schematic by referring this schematic ,here the RXN and RXP resistor values are 49R and cap c36-c41 is 0.1uf

cannot and should not use INTn to reset the W5500.

  • INTn is an output pin, not an input.
  • It is used by the W5500 to signal an interrupt to the host MCU (e.g. packet received, socket closed).
  • Driving INTn low externally can damage the chip or prevent it from functioning correctly.

Where does CHSGND go?

I went by copying the w5500 module and the document i found at the time, and the resulting schematic i made and sent and received with the W5500 manufactured does actually work.

I can't find the document i had at the time and the design guide i find now does show 49R resistors on RN & RXP so i am not sure that makes all that much difference to begin with. A 49R resistor is rather hard to find anyway so i resorted to 51R just like on the module i have. Pretty sure about the 1uF cap size though, but that may have been altered as well. As for the INTn & RSTn. That is what i have and that works ! RSTn is just simply pulled-up HIGH and INTn has a 10k PULLUP and i switch that to reset the W5500. That works. If you think that is an output pin, then mayb5e it is but if i pull it LOW i reset the W5500 with it.
These are the differences i found between your schematic and mine. They are marginal i admit and parts placement is also relevant of course, but if you want to rule-out a hardware issue.. Well anyway this is what i have and i know does work.

1 Like

CHSGND is connected to pin no 8 of RJ45 connector HR911105A

Yes I see that but what else is it connected to?

1 Like

No other connections from CHSGND.I am copy the schematic same as in the reference ckt.

I am also checked with shorting this two GNG ,but didn't work

finally i find the culprits damping resistors(33R) in RXN/RXP and TXN/TXP.
No need of these resistors.

Well there is no need, but my circuit works fine with them. But great that you have resolved your issue

1 Like

which RJ45 socket are you used, in my case the HR911105A

ehhh. i got some from AliExpress eh yep exactly that.

E6.57 for 10 pcs, not bad. RJ45 connectors are generally not that cheap even shitty plastic ones cost some.