Skip to content
Buy 10+ on select items — save 10% auto-applied
Free US shipping on orders $35+
Order by 3pm ET — ships same-day from the US
Worldwide shipping via DHL
Skip to main content

Arduino Nano W5500: Ethernet Web Server with JSON API

September 23, 2026 6 views

Arduino Nano W5500: Ethernet Web Server with JSON API | ShillehTek
Project

Build an Arduino Nano + W5500 wired Ethernet web server that shows DHT11 readings, switches a relay, and exposes a JSON API for a second board, by ShillehTek.

45 min Intermediate8 parts

Project Overview

Arduino Nano + W5500 Ethernet module: Build a wired Ethernet web server that shows DHT11 temperature and humidity, reads a button, and switches a relay from any browser on your LAN. You will also add a /json endpoint so a second Arduino Nano (or a Raspberry Pi, or Home Assistant) can pull the same data over the network.

Wi-Fi is convenient until it is not: a garage, a metal cabinet, a basement rack, or anywhere you want a connection that never drops and never needs a password. A W5500 module gives a plain Arduino Nano a real Ethernet port for the price of a coffee. No cloud, no radio, one cable.

  • Time: ~45 minutes
  • Skill level: Intermediate
  • What you will build: A DHCP-or-static Ethernet web server on a Nano with a phone-friendly status page, relay control via URL, a JSON API, and a companion client sketch that mirrors the server's inputs on another board.
Two Arduino Nano boards with W5500 Ethernet modules showing sensor monitoring and relay control over a LAN
Wired, boring, and bulletproof: exactly what you want in a cabinet.

Parts List

From ShillehTek

External

  • An Ethernet cable and a spare port on your router or switch

Note: the W5500 is a 3.3 V part that draws up to about 130 mA while the link is up, which is more than the Nano's own 3V3 pin can supply (it comes from the USB chip's tiny regulator). Power the module from the MB102's 3.3 V rail (or any 3.3 V supply good for 300 mA) and share GND with the Nano. Its SPI inputs are 5 V tolerant, so the Nano's signals connect directly.

Step-by-Step Guide

Step 1 - Wire the W5500

Goal: Connect SPI plus stable 3.3 V power.

What to do: W5500 to Nano: MOSI to D11, MISO to D12, SCLK to D13, SCS (chip select) to D10, RST to D9 (optional), GND to GND. VCC (3.3 V) to the MB102 3.3 V rail, and MB102 GND to Nano GND. Plug the Ethernet cable into your router or switch.

Wiring schematic of an Arduino Nano connected to a W5500 SPI Ethernet module with power and SPI pin mapping
The server side schematic. The W5500 uses SPI and needs a solid 3.3 V supply.

Expected result: The module's link LED lights when the cable is plugged in.

Step 2 - Add the Sensor, Button, and Relay

Goal: Add inputs and an output for the web page to display and control.

What to do: DHT11: DATA to D2, VCC to 5V, GND to GND. Button: D3 to GND (use the internal pull-up). Relay module: IN to D7, VCC to 5V, GND to GND. Install the Adafruit DHT sensor library (plus Unified Sensor). The Ethernet library that supports the W5500 ships with the Arduino IDE.

Expected result: Hardware is ready for the server sketch upload.

Step 3 - Upload the Server Sketch

Goal: Serve a status page, accept relay control URLs, and expose a JSON endpoint.

What to do: Upload the sketch below. Then open the Serial Monitor, note the IP address, and open it in a browser on the same network.

Code:

#include <SPI.h>
#include <Ethernet.h>
#include <DHT.h>

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x01};   // any unique MAC on your LAN
IPAddress ip(192, 168, 1, 177);                     // used only if DHCP fails
EthernetServer server(80);
DHT dht(2, DHT11);
const int RELAY = 7, BTN = 3;
const int RELAY_ON = LOW;                           // most relay modules are active-LOW
bool relayOn = false;

void setup() {
  Serial.begin(9600);
  pinMode(RELAY, OUTPUT); digitalWrite(RELAY, !RELAY_ON);
  pinMode(BTN, INPUT_PULLUP);
  dht.begin();
  Ethernet.init(10);                                // W5500 chip select on D10
  if (Ethernet.begin(mac) == 0) Ethernet.begin(mac, ip);   // try DHCP, fall back to static
  server.begin();
  Serial.print(F("Server at http://")); Serial.println(Ethernet.localIP());
}

void loop() {
  EthernetClient client = server.available();
  if (!client) return;
  String req = client.readStringUntil('\n');        // first line: "GET /on HTTP/1.1"
  while (client.available()) client.read();         // discard the remaining headers
  if (req.indexOf("GET /on ") >= 0)  { relayOn = true;  digitalWrite(RELAY, RELAY_ON);  }
  if (req.indexOf("GET /off ") >= 0) { relayOn = false; digitalWrite(RELAY, !RELAY_ON); }

  float t = dht.readTemperature(), h = dht.readHumidity();
  bool pressed = digitalRead(BTN) == LOW;

  if (req.indexOf("GET /json") >= 0) {              // machine-readable endpoint
    client.println(F("HTTP/1.1 200 OK\r\nContent-Type: application/json\r\nConnection: close\r\n"));
    client.print(F("{\"temp\":"));   client.print(t, 1);
    client.print(F(",\"hum\":"));    client.print(h, 0);
    client.print(F(",\"button\":")); client.print(pressed ? F("true") : F("false"));
    client.print(F(",\"relay\":"));  client.print(relayOn ? F("true") : F("false"));
    client.println(F("}"));
  } else {                                          // human-readable page, refreshes every 5 s
    client.println(F("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n"));
    client.println(F("<!DOCTYPE html><html><head><meta http-equiv='refresh' content='5;url=/'>"
                     "<meta name='viewport' content='width=device-width'><style>body{font-family:sans-serif;padding:20px}"
                     "a{display:inline-block;padding:12px 24px;margin:8px;background:#2a7;color:#fff;text-decoration:none;border-radius:6px}"
                     "</style></head><body><h2>Nano Ethernet Monitor</h2><p>"));
    client.print(F("Temperature: ")); client.print(t, 1); client.print(F(" C<br>Humidity: ")); client.print(h, 0);
    client.print(F(" %<br>Button: ")); client.print(pressed ? F("PRESSED") : F("released"));
    client.print(F("<br>Relay: "));   client.print(relayOn ? F("ON") : F("OFF"));
    client.println(F("</p><a href='/on'>Relay ON</a><a href='/off'>Relay OFF</a></body></html>"));
  }
  delay(1);
  client.stop();
}

Expected result: A simple page with temperature, humidity, the button state, and the relay state, refreshing every five seconds. Tap "Relay ON" and the relay clicks. Open /json to get something like {"temp":23.5,"hum":41,"button":false,"relay":true}.

Step 4 - Understand Why This Works Well on a Nano

Goal: Know when to reach for a cable instead of Wi-Fi.

What to do: The W5500 has the whole TCP/IP stack in silicon, so an 8-bit Nano with 2 KB of RAM can serve pages that would need an ESP32 over Wi-Fi, and it does it with no credentials, no reconnect logic, and no interference. Every line of the sketch that sends HTML is wrapped in F() to keep strings in flash; that discipline helps keep a Nano server stable. The 5-second meta refresh with url=/ means reloading never repeats an ON/OFF command.

Expected result: A server you can screw into a cabinet and forget.

Step 5 - Add a Second Nano as a Client (Board-to-Board Over the LAN)

Goal: Mirror the server's button on another board by fetching /json.

What to do: Wire a second W5500 to a second Nano the same way as the server. Upload the client sketch below, then press the button on the server board.

Wiring schematic of a second Arduino Nano with a W5500 Ethernet module acting as a client that mirrors server states
The client side: same module, fetches the server's JSON every few seconds and copies the state to its own pins.

Code:

#include <SPI.h>
#include <Ethernet.h>

byte mac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x02};
IPAddress serverIp(192, 168, 1, 177);               // the server's address (see its Serial Monitor)
EthernetClient client;

void setup() {
  pinMode(13, OUTPUT);
  Ethernet.init(10);
  Ethernet.begin(mac);
}

void loop() {
  if (client.connect(serverIp, 80)) {
    client.println(F("GET /json HTTP/1.1\r\nHost: nano\r\nConnection: close\r\n"));
    String body;
    unsigned long t0 = millis();
    while (client.connected() && millis() - t0 < 3000) {
      if (client.available()) body += (char)client.read();
    }
    client.stop();
    int i = body.indexOf("\"button\":");                 // find the button field
    if (i >= 0) digitalWrite(13, body.charAt(i + 9) == 't'); // 't' of true -> LED on
  }
  delay(2000);
}

Expected result: Within two seconds the client's LED follows the server's button, which effectively extends a pin across your network with no computer in the loop. Replace D13 with a relay and you have remote switching over structured cabling.

Conclusion

With a W5500 Ethernet module, an Arduino Nano can serve a simple status page, expose a /json API, and let another board read it reliably over a wired LAN. This pattern scales from a workshop sensor to an industrial panel, and the W5500 approach works similarly on other boards when you want both radios and a cable.

Photo and schematic credits: adapted from Viorel Racoviteanu on Hackster.io.

Want the exact parts used in this build? Grab them from ShillehTek.com. If you want help customizing this project or building something for your product, check out our IoT consulting services.

Parts for this build

Everything used in this tutorial. Uncheck what you already have.

All 8 in stock
0 parts selected $0.00