Wiznet makers

viktor

Published July 23, 2026 ©

173 UCC

20 WCC

49 VAR

0 Contests

0 Followers

0 Following

Original Link

How to Build a Tiny DNS Sinkhole with W5500 on XIAO ESP32-S3?

This project replaces a Raspberry Pi 3B running Pi-hole with a compact DNS sinkhole built from a Seeed Studio XIAO ESP32-S3 and a WIZnet WIZ850io

COMPONENTS Hardware components

WIZnet - WIZ850io

x 1


PROJECT DESCRIPTION

Summary

This project replaces a Raspberry Pi 3B running Pi-hole with a compact DNS sinkhole built from a Seeed Studio XIAO ESP32-S3 and a WIZnet WIZ850io module containing the W5500 Ethernet controller. The ESP32-S3 processes DNS requests and searches the blocklist, while the W5500 provides the wired network connection needed to keep the DNS service independent of Wi-Fi reliability.

What the Project Does

The project runs a network-wide DNS filtering service on a small ESP32-S3 board. Devices on the network are configured to use the sinkhole’s IPv4 address as their DNS server. Every conventional DNS query is then sent to the XIAO ESP32-S3 before the client connects to the requested Internet service.

The firmware extracts the requested domain from the DNS packet and searches a locally stored blocklist. When the domain is blocked, the server returns 0.0.0.0. Because the returned address is not a usable destination, the client cannot retrieve advertisements, tracking scripts, malware content, or other resources associated with that domain. Allowed domains are resolved through an upstream DNS server and returned to the client.

The project uses blocklists derived from StevenBlack’s hosts repository. Different list combinations can target advertising, malware, tracking, fake-news domains, gambling, adult content, or other categories. Custom entries can also be added or removed through the firmware’s web interface.

Its main software components are:

  • An asynchronous UDP DNS server listening on port 53.
  • A blocklist stored in PSRAM and searched with a binary-search implementation.
  • A small upstream DNS cache.
  • Primary and secondary upstream DNS resolution.
  • A web interface for configuration, status, logging, and blocklist management.
  • Wi-Fi provisioning and optional wired Ethernet operation.
  • OTA firmware and web-content updates.

The XIAO ESP32-S3 is selected partly because its 8 MB of PSRAM can hold a substantially sized blocklist. The upstream firmware documentation states that blocklist lookups on an ESP32-S3 with 8 MB PSRAM take less than 50 microseconds under its tested conditions. That value is a project-reported result rather than an independently verified benchmark.

The resulting device is smaller and operationally simpler than a Linux-based DNS appliance. It does not require a Linux distribution, package manager, filesystem maintenance, or a continuously running single-board computer. The repository also includes a FreeCAD enclosure design and images of the compact assembled device.

Where WIZnet Fits

The WIZnet component is a WIZ850io module based on the W5500. It connects to the XIAO ESP32-S3 over SPI and provides a physical 100BASE-T Ethernet interface with an integrated RJ45 connector and magnetics.

The project assigns the following XIAO pins to the Ethernet module:

XIAO ESP32-S3WIZ850io function
D10 / GPIO9MOSI
D9 / GPIO8MISO
D8 / GPIO7SPI clock
D2 / GPIO3Chip select
D1 / GPIO2Interrupt
D0 / GPIO1Hardware reset
3.3 VModule power
GNDGround

These assignments are documented in the README and repeated in utils.cpp, confirming that the repository was customized for this specific XIAO-to-WIZ850io wiring.

The W5500’s practical role is to keep the DNS infrastructure on wired Ethernet. A DNS server is a dependency for every client configured to use it, so loss of its network link affects browsing, application connections, telemetry, and software updates across the network. The author chose Ethernet specifically to avoid relying on Wi-Fi being continuously stable.

There is an important architectural detail: this firmware does not appear to use WIZnet ioLibrary’s hardware socket API directly. It initializes the W5500 through the ESP32 Arduino ETH interface and uses AsyncUDP, getaddrinfo(), and ESP32 LwIP functions for DNS traffic and upstream resolution. The W5500 therefore acts primarily as the wired Ethernet controller, while the ESP32-S3 software stack handles the UDP sockets, DNS parsing, caching, and filtering.

This means the project benefits from the W5500’s compact Ethernet integration and reliable physical link, but it is not a direct demonstration of W5500 TCP/IP socket offload. A separate ioLibrary-based implementation would require a different transport layer and direct use of W5500 hardware sockets.

The WIZ850io also supports the physical design goal. It combines the W5500, RJ45 connector, transformer, and supporting circuitry in a ready-to-wire module, avoiding the PCB work required to build a custom Ethernet interface around the bare W5500.

Implementation Notes

W5500 SPI Pin Configuration

File: ESP32_AdBlocker/utils.cpp

// SPI pins for Ethernet
int ethCS   = 3;
int ethInt  = 2;
int ethRst  = 1;
int ethSclk = 7;
int ethMiso = 8;
int ethMosi = 9;

These values map the XIAO ESP32-S3 GPIOs to the WIZ850io chip-select, interrupt, reset, clock, MISO, and MOSI signals. They matter because the XIAO form factor exposes a limited number of pins, and the firmware must pass the exact wiring definition to the ESP32 Ethernet driver.

W5500 Ethernet Initialization

File: ESP32_AdBlocker/utils.cpp

if (!ETH.begin(ETH_PHY_W5500,
               ETH_PHY_ADDR_AUTO,
               ethCS, ethInt, ethRst,
               SPI2_HOST,
               ethSclk, ethMiso, ethMosi,
               ETH_PHY_SPI_FREQ_MHZ)) {
    LOG_WRN("Ethernet W5500 init failed");
    return false;
}

This code selects the W5500 driver, supplies the configured SPI pins, and attaches the controller to the ESP32-S3’s SPI2 host. The firmware subsequently waits for the physical link and an IP address, then starts services such as mDNS and connection monitoring.

DNS Filtering Decision

File: ESP32_AdBlocker/appSpecific.cpp

bool blocked = !strcmp(domainName, blockedDomain)
    ? true
    : (bool)binarySearch(domainName, false);

blocked ? ++blockCnt : ++allowCnt;

return blocked
    ? IPAddress(0, 0, 0, 0)
    : resolveDomain(domainName);

This is the core sinkhole decision. The firmware reuses the previous blocked result when possible, otherwise searches the sorted blocklist. Blocked domains resolve to 0.0.0.0; allowed domains are sent to the upstream resolver. The W5500 transports the Ethernet traffic, but this filtering logic runs on the ESP32-S3.

How It Compares with the Alternatives

Pi-hole: Pi-hole offers a much broader administration ecosystem, richer statistics, mature list management, integrations, and the flexibility of a Linux host. This ESP32-S3 implementation uses less hardware and avoids operating-system maintenance, but it has tighter memory limits and fewer diagnostic and policy-management features.

AdGuard Home: AdGuard Home provides extensive client controls, encrypted DNS options, query logs, rewrite rules, and a polished management interface. The XIAO project is smaller and more appliance-like, but it does not provide the same depth of DNS policy or encrypted upstream support in the reviewed code.

Router-based filtering: Router filtering avoids adding another physical device, but its capability depends entirely on the router firmware. Some routers only allow a few blocked domains, while others can run full DNS filtering packages. The XIAO device provides a dedicated service without replacing the router firmware.

Wi-Fi-only ESP32 sinkhole: The same firmware can operate over Wi-Fi, making the WIZ850io optional at the software level. Wired Ethernet is preferable when DNS availability is considered infrastructure-critical, because the service does not depend on radio conditions, association state, or access-point congestion.

Linux SBC: A Raspberry Pi or similar board has more CPU, RAM, storage, and software flexibility. The XIAO ESP32-S3 and WIZ850io combination is more compact and has fewer software layers, but it is less appropriate for very large networks, extensive logs, multiple filtering engines, or complex per-client policies.

Practical Tips / Pitfalls

  • Assign the sinkhole a static IPv4 address. Clients and routers must be able to reach the same DNS address after every reboot.
  • Power the WIZ850io from 3.3 V, not 5 V, and keep the SPI wiring short. Poor power integrity or long jumper wires can cause intermittent Ethernet initialization.
  • Confirm the ethCS, ethInt, ethRst, ethSclk, ethMiso, and ethMosi values against the physical wiring before flashing.
  • Configure both the router’s advertised DNS address and its fallback behavior carefully. A public secondary DNS server may allow clients to bypass filtering.
  • The reviewed firmware is IPv4-oriented. IPv6 DNS, DNS-over-HTTPS, and browser “secure DNS” can bypass the sinkhole unless separately controlled.
  • Select a consolidated blocklist that fits available PSRAM. Very large lists increase loading time and may exceed the device’s memory budget.
  • Treat this as a convenience and privacy filter, not a complete security boundary. DNS blocking cannot inspect traffic addressed directly by IP and does not replace endpoint protection, firewalling, or network segmentation.

FAQ

Q: Why use the W5500 for this DNS sinkhole?

The DNS server must remain reachable whenever network clients need name resolution. The W5500 gives the XIAO ESP32-S3 a wired Ethernet connection, removing dependency on Wi-Fi association and radio conditions. In this project, connection stability and compact packaging are more important than raw throughput.

Q: How does the W5500 connect to the XIAO ESP32-S3?

The WIZ850io connects through SPI using MOSI, MISO, clock, and chip-select signals, plus interrupt and reset lines. The documented build uses GPIO9, GPIO8, GPIO7, GPIO3, GPIO2, and GPIO1 respectively, with 3.3 V power and a common ground.

Q: What does the W5500 do inside this project?

It provides the physical wired Ethernet interface between the DNS sinkhole and the local network. The ESP32-S3 still parses DNS packets, searches the blocklist, forwards allowed queries, caches answers, and serves the administration interface. The W5500 is not performing the domain-filtering decision.

Q: Can beginners build this project?

A beginner with basic Arduino experience can follow it, but the project requires careful SPI wiring, ESP32-S3 board configuration, PSRAM support, static-IP planning, and router DNS changes. Understanding basic DNS behavior is important because an incorrect configuration can temporarily prevent clients from resolving domain names.

Q: How does W5500 Ethernet compare with running the sinkhole over Wi-Fi?

Wi-Fi requires no external Ethernet module and may be adequate for experimentation or travel use. W5500 Ethernet adds wiring and hardware but provides a fixed physical connection that is less sensitive to wireless interference, reconnection delays, or access-point changes. For a DNS service used by an entire network, that operational stability is the main justification.

Documents
Comments Write