Arduino Ethernet-compatible library for the WCH CH392 SPI Ethernet controller (CH392F / CH392T).
Use it as a drop-in replacement for the official Arduino Ethernet library (W5500-style API).
| Chip | SPI sockets | Max SPI (datasheet) | Default chunk size |
|---|---|---|---|
| CH392F | 4 | 10 MHz (use 1 MHz on ESP32) | 512 bytes |
| CH392T | 8 | 24 MHz | 1536 bytes |
Select chip in src/CH392_Config.h or with -DCH392_CHIP=CH392F.
| CH392 | MCU |
|---|---|
| SCS | GPIO CS (e.g. 5) |
| SCK | SPI SCK |
| SDI | SPI MOSI |
| SDO | SPI MISO |
| INT | GPIO (e.g. 13), pull-up |
| RST | GPIO (e.g. 14), optional |
| VCC / GND | 3.3 V |
Connect the Ethernet magnetics and RJ45 per your module schematic.
#include <CH392.h> // ESP32: or #include <CH392Ethernet.h> for Ethernet.* names
#define PIN_CS 5
#define PIN_INT 13
#define PIN_RST 14
byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
void setup() {
Serial.begin(115200);
CH392.init(PIN_CS, PIN_INT, PIN_RST);
if (CH392.begin(mac) == 0) {
Serial.println(F("DHCP failed"));
while (1) {}
}
Serial.println(CH392.localIP());
}
void loop() {
CH392.maintain(); // DHCP lease renewal when using DHCP
}IPAddress ip(192, 168, 1, 100);
IPAddress dns(192, 168, 1, 1);
IPAddress gw(192, 168, 1, 1);
IPAddress mask(255, 255, 255, 0);
CH392.begin(mac, ip, dns, gw, mask);| W5500 / Arduino Ethernet | CH392 |
|---|---|
#include <Ethernet.h> |
#include <CH392.h> or <CH392Ethernet.h> |
Ethernet.init(cs) |
CH392.init(cs, intPin, rstPin) |
Ethernet.begin(mac) |
CH392.begin(mac) |
EthernetClient |
CH392Client (or EthernetClient with shim) |
EthernetServer |
CH392Server |
EthernetUDP |
CH392UDP |
Ethernet.maintain() |
CH392.maintain() |
On AVR/STM32, CH392.h already defines Ethernet as an alias. On ESP32, use CH392Ethernet.h for Ethernet macros.
init(cs),init(cs, int, rst)begin(mac),begin(mac, timeout),begin(mac, ip, …)localIP(),subnetMask(),gatewayIP(),dnsServerIP()MACAddress()/macAddress()linkStatus()→LinkON,LinkOFF,Unknownmaintain()→DHCP_CHECK_NONE(0),DHCP_CHECK_RENEW_FAIL(1),DHCP_CHECK_RENEW_OK(2),DHCP_CHECK_REBIND_FAIL(3),DHCP_CHECK_REBIND_OK(4) — same values as Arduino EthernetDhcp.hhostByName(host, ip)
connect(ip, port),connect(ip, port, localPort),connect(host, port)connected(),stop(),available(),read(),peek()write(),print(),println(),flush(),availableForWrite()remoteIP(),remotePort()setTimeout(ms)/setConnectionTimeout(ms)— default 1000 ms (Arduino Ethernet parity)
begin()available()— returns a client only when RX data is bufferedaccept()— hands out each established client once, even with no data yetwrite(),print(),println()
begin(port),beginPacket(),write(),endPacket()parsePacket(),available(),read(),remoteIP(),remotePort()— datagram boundaries preserved
| Folder | Sketch |
|---|---|
Basics/LinkStatus |
Cable link up/down |
Basics/HardwareTest |
SPI + chip ID |
TCP/WebServer |
Minimal HTTP server |
TCP/EchoServer |
TCP echo |
TCP/TCPClient |
Outbound TCP client |
UDP/UDPSendReceive |
UDP echo |
Network/DHCP |
DHCP lease |
Network/DHCP_Maintain |
maintain() renew/rebind |
Network/DNSLookup |
hostByName() |
Advanced/MAC_RAW |
Raw Ethernet frames |
Advanced/TCP_Server_Raw |
WCH low-level TCP (debug) |
Application demos: extras/Relay_Test, extras/MQTT_Test (requires PubSubClient).
| Sketch | Purpose |
|---|---|
TCP_ConnectStress |
Thousands of connect/send/stop cycles |
HTTP_RequestStress |
Repeated HTTP GET against a WebServer |
UDP_EchoStress |
Continuous UDP send/receive |
MQTT_LongRun |
Hours/days MQTT publish soak |
Edit peer/broker IPs in each sketch. Leave running unattended and watch Serial counters.
- TX is gated on
SINT_STAT_SENBUF_FREE(not HW idle-queue — stuck at 0 on many CH392F parts). - Socket IRQs are consumed once (clear-on-read); do not double-read.
- Software RX uses backpressure: HW FIFO is not drained when the SW ring is full.
- DNS allocates a temporary UDP socket (never steals socket 0).
- Link-down in
maintain()tears down TCP sockets and restores the listen port on link-up. - Disconnecting sockets with unread data are reclaimed after 30 s if the app never
stop()s. - Keep
CH392_DEBUGandCH392_TX_DEBUGat0for field builds. - CH392F: treat as one concurrent server client unless you add multi-listen yourself.
- Always call
CH392.maintain()inloop()for DHCP + link recovery.
SPI too fast or noisy wiring. Default 1 MHz on ESP32. Verify MISO/MOSI and short leads.
Multi-kilobyte HTTP must use paced sends (sendChunk() with SENBUF wait). Do not blast the TX buffer faster than the chip can drain.
Ensure client.stop() is called after each HTTP response and call CH392.maintain() in loop().
Set CH392_DEBUG to 0 in CH392_Config.h.
Confirm library ≥ 1.0.3. rc=-2 after broker “New connection” then immediate client close was caused by status-based onDisconnect() killing new TCP sessions; disconnect is IRQ-only now. Also set ethClient.setConnectionTimeout(15000) for PubSubClient.
Every example is compiled for ESP32 (esp32:esp32:esp32, core 3.3.0) on push and pull request via GitHub Actions.
Install arduino-cli, then:
Windows (PowerShell):
cd C:\Users\ABDO\Documents\Arduino\libraries\CH392
.\scripts\compile-examples.ps1Linux / macOS:
cd path/to/CH392
chmod +x scripts/compile-examples.sh
./scripts/compile-examples.shOverride board or core version:
FQBN=esp32:esp32:esp32s3 ESP32_VERSION=3.3.0 ./scripts/compile-examples.shgit initin the library folder (if not already a repo).- Push to GitHub.
- Replace
YOUR_GITHUB_USERin the README badge with your username. - Actions run automatically — no secrets required.
MIT — see LICENSE. Based on WCH CH392 reference code.