Embedded Ethernet & TCP/IP
Ethernet brings embedded devices onto IP networks for industrial control, IoT gateways and cameras. Learn the MAC/PHY split and RMII/MII, frame structure and overheads, the TCP/IP stack layers, lightweight stacks (lwIP), DMA descriptor rings, UDP vs TCP trade-offs, sockets and raw APIs, industrial Ethernet protocols, security, and how to compute real throughput.
Many Cortex-M4/M7 MCUs include an Ethernet MAC; an external PHY chip converts its digital signals to the analog signals on the twisted pair. Above that, a TCP/IP stack (lwIP, FreeRTOS+TCP, NetX, Zephyr) implements ARP, IP, ICMP, UDP, TCP, DHCP and DNS in a few tens of KB.
With Ethernet the device can serve a web page, publish MQTT telemetry, speak Modbus TCP or be updated remotely. With that power comes responsibility: network input is untrusted, and stacks must be configured for memory and security.
An analogy
The TCP/IP stack is a postal system: the PHY is the road, the MAC is the local post office stamping frames with MAC addresses, IP routes letters between cities by IP address, and TCP is registered mail — numbered, acknowledged and resent if lost. UDP is a postcard: cheap and quick, no guarantee.
Layers of the network stack
Each layer adds its header and relies on the one below:
- Physical (PHY): 10/100/1000BASE-T signalling, auto-negotiation, link status
- Data link (MAC): Ethernet frames, MAC addresses, CRC
- Network (IP): addressing and routing; ARP maps IP → MAC; ICMP ping
- Transport: UDP (datagrams) or TCP (reliable byte stream)
- Application: HTTP, MQTT, Modbus TCP, CoAP, SNTP, TFTP
Hardware: MAC, PHY and DMA
The MAC connects to the PHY via RMII (7 pins, 50 MHz reference clock) and a management bus (MDIO/MDC) to configure the PHY. The MAC uses DMA descriptor rings in RAM: the driver hands buffers to hardware, which fills them with received frames and raises an interrupt. On Cortex-M7, descriptors and buffers must be in non-cacheable RAM or cache-maintained — a very common bug.
UDP vs TCP in embedded
UDP is simple, low-latency and good for periodic sensor data, discovery and streaming where a late sample is useless. TCP guarantees ordered delivery with retransmission and flow control but needs buffers per connection (window × connections) and has less predictable timing. MQTT and HTTP run on TCP; real-time industrial protocols often use UDP or raw Ethernet.
Industrial Ethernet, TSN and security
Real-time fieldbuses (EtherCAT, PROFINET IRT, EtherNet/IP) use special hardware or scheduling for µs-level determinism; Time-Sensitive Networking (TSN) adds scheduled traffic and precise time sync (IEEE 802.1AS/PTP) to standard Ethernet. Every networked device must use TLS (mbedTLS/wolfSSL) for sensitive data, disable unused services, validate all input lengths, and support secure firmware updates — embedded stacks have had critical parsing vulnerabilities.
Formulas
- Wire bytes per full frame:
Preamble+SFD 8 + header 14 + payload 1500 + FCS 4 + IFG 12 = 1538 B - TCP efficiency (MSS 1460):
η = 1460 / 1538 = 94.9 % - UDP efficiency (1472 B payload):
η = 1472 / 1538 = 95.7 % - Frame time:
t = 1538 × 8 / bit rate (100 Mb/s → 123 µs) - TCP window limit:
Throughput ≤ window size / round-trip time
Worked example: Goodput at 100 Mb/s
Given: TCP with MSS 1460
Find: Maximum payload rate.
- η = 1460 / 1538 = 94.9%
- 100 × 0.949
Answer: ≈ 94.9 Mb/s (≈ 11.9 MB/s) if the MCU can keep up
Worked example: Window-limited TCP
Given: lwIP TCP_WND = 4 × 1460 B; RTT = 2 ms
Find: Throughput limit.
- 5840 B / 2 ms = 2.92 MB/s
Answer: ≈ 23 Mb/s — enlarge window for more
Worked example: Frame time
Given: Full-size frame at 100 Mb/s
Find: Serialisation time.
- 1538 × 8 bits / 100 Mb/s
Answer: 123 µs
UDP echo server with the lwIP raw API
#include "lwip/udp.h"
static void udp_echo_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p,
const ip_addr_t *addr, u16_t port)
{
if (p != NULL) {
udp_sendto(pcb, p, addr, port); /* send the same buffer back */
pbuf_free(p); /* ALWAYS free received pbufs */
}
}
void udp_echo_init(void)
{
struct udp_pcb *pcb = udp_new();
if (pcb && udp_bind(pcb, IP_ADDR_ANY, 7) == ERR_OK) {
udp_recv(pcb, udp_echo_recv, NULL); /* callback per datagram */
}
}The raw (callback) API has the lowest RAM and CPU cost and runs in the stack's context — callbacks must be short and non-blocking. With an RTOS, the netconn or BSD-socket APIs are easier but cost more memory.
Common mistakes
- Wrong: Not freeing pbufs in lwIP callbacks. — Right: Every received pbuf must be freed exactly once — leaks exhaust the pool.
- Wrong: DMA buffers in cached RAM on Cortex-M7. — Right: Place in non-cacheable MPU region or clean/invalidate caches.
- Wrong: Calling blocking APIs from raw-API callbacks. — Right: Callbacks run in the stack context; defer long work.
- Wrong: Plain-text protocols exposed to the internet. — Right: TLS, authentication, input validation, minimal services.
Applications
- Industrial control: Modbus TCP, EtherNet/IP, PROFINET devices.
- IoT gateways: Aggregate fieldbus data and publish via MQTT/TLS.
- Building automation: BACnet/IP controllers, PoE sensors.
- Test equipment: LXI/SCPI instruments controlled over LAN.
Key takeaways
- MAC (on-chip) + PHY (external) + TCP/IP stack (lwIP).
- Full frames ≈ 95% efficient.
- UDP for low latency, TCP for reliability.
- Mind DMA/cache, buffer pools and security.
Going further
- TSN: Scheduled traffic (802.1Qbv) and gPTP for deterministic Ethernet.
- Single-pair Ethernet: 10BASE-T1L up to 1 km for field devices; 100BASE-T1 in cars.
- Zero-copy stacks: Pass DMA buffers up the stack without memcpy.