grblhal-RP2040
Explains an RP2040-based CNC controller using W5500 to offload TCP/IP, enabling deterministic real-time control and stable network communication without jitter.
What the Project Does
This is a network-enabled CNC controller firmware built on RP2040.
It allows a CNC machine to be controlled remotely through:
Telnet (command-line control)
WebSocket (real-time UI)
HTTP (web interface)
FTP (file transfer)
MQTT (optional integration)
System Structure (Simplified)
Host PC / Web UI
↓
TCP (Telnet / HTTP / WebSocket)
↓
lwIP
↓
┌───────────────┬──────────────────┐
│ Wi-Fi (CYW43) │ Ethernet (W5500) │
└───────────────┴──────────────────┘
↓
RP2040The key design decision:
The networking layer is modular and replaceable, while the application layer remains unchanged.
Where WIZnet Fits
The system explicitly supports WIZnet W5500 / W5100S as an alternative to Wi-Fi.
Role of W5500 in this architecture
Acts as a hardware TCP/IP engine over SPI
Handles:
packet transmission
retransmission
checksum
socket buffering
Exposes a register-based socket interface to the MCU
Why this matters for CNC
CNC control requires:
precise timing
no jitter
predictable loop execution
W5500 ensures:
networking does not interrupt motion control
CPU cycles remain dedicated to step generation and control logic
Implementation Notes
1) Build-Time Network Selection (Critical Design)
File: driver.h
#if ETHERNET_ENABLE && WIFI_ENABLE
#error "WiFi and Ethernet cannot be enabled at the same time!"
#endifWhat this does:
Prevents dual-stack conflicts
Forces a clean architecture boundary
Why it matters:
Avoids runtime instability
Keeps memory usage predictable
2) WIZnet Driver Integration via CMake
File: CMakeLists.txt
if(ADD_ETHERNET)
target_compile_definitions(grblHAL PUBLIC ETHERNET_ENABLE=1)
target_compile_definitions(grblHAL PUBLIC CYW43_LWIP=0)
include(networking/wiznet/CMakeLists.txt)
target_sources(grblHAL PRIVATE w5x00_ll_driver.c)
endif()Key insight:
Wi-Fi lwIP is explicitly disabled
WIZnet path uses its own driver + lwIP instance
3) Low-Level Driver (W5500 / W5100S Switch)
File: w5x00_ll_driver.c
#if _WIZCHIP_ == W5500
#include "W5500/w5500.h"
#elif _WIZCHIP_ == W5100S
#include "W5100S/w5100s.h"
#endifWhat this enables:
Same codebase supports multiple WIZnet chips
Selection via compile-time macro
4) SPI Bus Control with Dynamic Frequency Switching
static void wizchip_select(void)
{
if(spi_freq != WIZCHIP_SPI_FREQ)
spi_set_speed(WIZCHIP_SPI_FREQ);
DIGITAL_OUT(hw.cs, 0);
}static void wizchip_deselect(void)
{
DIGITAL_OUT(hw.cs, 1);
if(spi_freq != WIZCHIP_SPI_FREQ)
spi_set_speed(spi_freq);
}Why this exists:
SPI bus is shared (e.g., SD card)
W5500 runs at ~33 MHz
Other devices may require slower speeds
Result:
Safe multi-device SPI architecture
5) Hardware Initialization + ioLibrary Pattern
reg_wizchip_spi_cbfunc(spi_get_byte, spi_put_byte);
What this means:
Standard WIZnet ioLibrary integration
Abstracts hardware access cleanly
6) Socket Memory Allocation Strategy
uint8_t memsize[2][8] = {
{8,0,0,0,0,0,0,0},
{8,0,0,0,0,0,0,0}
};Interpretation:
All 8KB assigned to socket 0
Why:
CNC controller uses single dominant connection
Prevents buffer fragmentation
Improves throughput stability
7) Interrupt-Based Packet Reception (Key Optimization)
ctlsocket(socket, CS_SET_INTMASK, (void *)®_val);
ctlwizchip(CW_SET_INTRMASK, (void *)®_val);What happens:
W5500 asserts /INT pin on packet arrival
RP2040 IRQ handler processes it
Why this is critical:
No polling loops
No wasted CPU cycles
Deterministic behavior
8) DMA-Based SPI Transfers
File: spi.c
dma_channel_configure(dma_tx.channel, ...);
dma_channel_configure(dma_rx.channel, ...);
dma_start_channel_mask(...);Behavior:
Large transfers use DMA
CPU is not involved during transfer
Impact:
CNC motion loop remains uninterrupted
High throughput without CPU penalty
9) lwIP Configuration (No RTOS)
File: lwipopts.h
#define NO_SYS 1
#define MEM_SIZE (32*1024)
#define TCP_WND (8 * TCP_MSS)Key design:
No RTOS → polling-based main loop
Large TCP window → better file transfer performance
10) Service Layer Auto-Start
services.telnet = telnetd_init(network.telnet_port);
services.websocket = websocketd_init(network.websocket_port);
services.http = httpd_init(network.http_port);Meaning:
When link is up → all services start automatically
Same stack reused across all protocols
Practical Tips / Pitfalls
Do not enable Wi-Fi and Ethernet together (compile-time enforced)
Use INT pin for efficient packet handling (avoid polling)
Ensure SPI clock compatibility when sharing bus
Allocate socket memory based on actual connection pattern
Use DMA for large SPI transfers to avoid CPU blocking
Keep lwIP tuning aligned with application (e.g., TCP window size)
FAQ
Q: Why use W5500 instead of Wi-Fi in this system?
A: CNC control requires deterministic timing. W5500 removes TCP/IP processing from the MCU, eliminating jitter caused by Wi-Fi stacks and interrupts.
Q: How is W5500 connected to RP2040?
A: Through SPI with optional interrupt pin. SPI can run up to ~33 MHz and supports DMA for high-throughput transfers.
Q: What role does W5500 play in this project?
A: It acts as the network transport engine, handling all Ethernet communication while exposing socket-level access to lwIP and application services.
Q: Can beginners use this project?
A: It is not beginner-level. You need understanding of embedded C, SPI, DMA, and networking stacks like lwIP.
Q: How does this compare to using only lwIP with a software MAC/PHY?
A: Software stacks consume CPU cycles and introduce jitter. W5500 offloads this work, making it better suited for real-time systems like CNC controllers.
Source
Project: grblHAL RP2040 networking implementation
Key files:
networking/wiznet/w5x00_ll_driver.c
networking/wiznet/CMakeLists.txt
driver.h
spi.c
lwipopts.h
Tags
#W5500 #W5100S #RP2040 #grblHAL #CNC #Ethernet #SPI #DMA #lwIP #EmbeddedSystems


