4 Lightweight Network Protocol Stacks in Embedded Development: Which Ones Do You Know?

Those involved in embedded development know that the network protocol stack is the “skeleton” for device networking. However, the “small body” of embedded devices (with a few KB of memory and tens of MHz of clock speed) cannot handle the protocol stacks used on PCs, which is why lightweight embedded network protocol stacks have become a necessity.1. uIP: The Extremely Simplified “Microcontroller-Specific Stack”

uIP has been popular in the embedded community for over 20 years, and the core reason is one word: “savings”. However, its “savings” are not about blindly cutting code, but rather about breaking down the “skeleton” of the TCP/IP protocol to the extreme. Understanding its design logic reveals how “resource-constrained devices can connect to the network”.In a conventional TCP/IP protocol stack, each connection requires a lot of state information (such as send window size, retransmission timer, unacknowledged data buffer), which can occupy several hundred KB for just one connection. But uIP compresses the “connection state” into a structure called uip_conn, with each TCP connection only occupying 36 bytes (in early versions).It eliminates two “non-essential” components:① No independent send buffer: A typical protocol stack first stores the data to be sent in a buffer and then sends it slowly; uIP directly “sends data while accumulating it”. When the application provides a segment of data, it encapsulates it into a TCP packet and sends it out, releasing memory after sending, keeping at most a “backup of the most recently sent packet” (for retransmission).② Uses “polling” instead of “interrupt-driven”: Many protocol stacks rely on interrupts to handle data transmission and reception, which requires memory to store the interrupt context; uIP requires the application to actively call the uip_input() function to process data, eliminating the need for interrupt buffers, making it particularly friendly for 8-bit microcontrollers (with simple interrupt mechanisms).However, the trade-off is evident: TCP’s “congestion control” is essentially a simplified version, lacking the logic for dynamically adjusting the send window, defaulting to a fixed window size (usually 1-2 packets), making it prone to packet loss during network congestion. Additionally, the maximum number of concurrent connections is around 10 (beyond that, memory cannot handle it), limiting it to “one-to-one simple communication” (e.g., sensors sending data to a server).If using uIP, the most common pitfall is “data packet merging/splitting”. Since it lacks a large buffer, received data is directly handed to the application. If a packet received at once is larger than the application expects (e.g., the server sends 100 bytes while the application is only prepared to receive 50 bytes), the remaining 50 bytes will be overwritten. The solution is to implement a “circular buffer” to store received data first, allowing the application to read it gradually.Suitable scenarios: 8-bit/16-bit MCUs (e.g., STC89C52, MSP430), memory ≤ 32KB, only transmitting small data (e.g., temperature and humidity, switch signals), without complex protocols (HTTP, MQTT).

If the device is connected to low-rate networks like LoRa or ZigBee, uIP is sufficient, and its shortcomings will not be apparent.2. LwIPLwIP is currently the most widely used lightweight protocol stack, found in official STM32 examples, the underlying layer of ESP8266, and router firmware. It is more feature-rich than uIP while being more resource-efficient than traditional protocol stacks. The core secret is its “modular design that can be trimmed”, allowing users to add or remove modules like building blocks based on their needs.

4 Lightweight Network Protocol Stacks in Embedded Development: Which Ones Do You Know?

LwIP breaks the entire protocol stack into three layers, each of which can be independently enabled or disabled:① Lower layer (link layer): Supports various interfaces such as Ethernet, PPP (dial-up), SLIP (serial networking), and even allows custom drivers for niche hardware (e.g., custom wireless modules).② Core layer (IP/TCP/UDP): Each protocol is an independent module; disabling TCP by turning off the LWIP_TCP macro can save over 10KB of memory; turning off DHCP can directly reduce the code size by half;③ Application layer: Comes with basic modules like an HTTP server and SNMP client, but these modules are “optional”. For example, if a sensor does not require web control, the HTTP module can be excluded from compilation.More importantly, it has two types of “programming interfaces” to adapt to different devices:① RAW API: Directly operates on the lower-level functions of the protocol stack, saving resources (each connection occupies 200-500 bytes), but requires manual handling of the TCP state machine (e.g., when to retransmit, when to close the connection), suitable for microcontrollers with memory ≤ 64KB (e.g., STM32F103).② Socket API: Almost identical to the socket() function on PCs (connect(), send(), recv() have similar usage), without needing to manage the lower-level state machine, but occupying an additional 1-2KB of memory, suitable for slightly larger 32-bit MCUs (e.g., STM32F407) or embedded Linux.Suitable scenarios: Almost all 32-bit MCUs and IoT devices, from small STM32F1-based temperature and humidity gateways to large embedded Linux-based routers. As long as it is not extremely low-end (8-bit MCUs) or extremely high-end (requiring industrial-grade security), choosing LwIP is generally a safe bet.3. uC/TCP-IPuC/TCP-IP is the “companion stack” for the uC/OS real-time operating system, which has been used in industrial scenarios (machine tools, robots) for decades, relying on “real-time performance + reliability”. uC/TCP-IP inherits these two characteristics. The difference from LwIP is essentially “real-time system priority” vs. “general adaptability priority”.Industrial devices are most afraid of “network delays causing control latency”—for example, if a machine tool is processing and the sensor data is delayed by 100ms, it could result in defective parts. The design of uC/TCP-IP is to “prevent network operations from blocking the system”.

4 Lightweight Network Protocol Stacks in Embedded Development: Which Ones Do You Know?

It treats each network function as a “uC/OS task”; for example, receiving TCP data is one task, obtaining an IP via DHCP is another task, and these tasks have their own priorities (which can be manually set). For instance, setting the priority of the “sensor data reception” task to the highest ensures that even during network congestion, it can seize CPU resources to process data without being delayed by other network operations (like DNS resolution).Additionally, its “error handling” is particularly detailed: network instability in industrial environments (due to electromagnetic interference) often leads to data loss and disconnections. uC/TCP-IP has a dedicated “connection monitoring task” that checks the connection status every few hundred milliseconds. If it detects that a connection has not responded for 3 seconds, it will proactively release resources (close the connection, clear the buffer), preventing “dead connections” from occupying memory.If you have not used uC/OS, jumping straight into uC/TCP-IP can be confusing, as many of its features rely on uC/OS mechanisms: for example, to send data, you must first create a uC/OS message queue, place the data in the queue, and the TCP sending task will automatically take data from the queue to send; if you are used to using LwIP’s send() to send data directly, you might find it “unnecessary”, but this is precisely the key to its “non-blocking” nature.Another detail: its “memory management” is tightly coupled with uC/OS—you can only use the memory pool created by uC/OS (using OSMemCreate()), and cannot use standard C’s malloc(). The advantage is fast memory allocation (real-time performance is crucial for industrial devices), but the downside is that you must calculate the memory pool size in advance. For example, if you expect to have 5 TCP connections simultaneously, each requiring 1KB of memory, you must create a 5KB memory pool in advance; otherwise, it will be insufficient, and if too much is allocated, it will be wasted.Suitable scenarios: Industrial devices using uC/OS, such as PLCs (programmable logic controllers) in factories, control modules for robots, and in-vehicle terminals in cars (these devices typically run uC/OS).

If the device uses FreeRTOS, unless industrial certification is particularly needed, it is better to use LwIP directly (FreeRTOS has a dedicated LwIP port that is more convenient).4. mbed TLS + Network StackNowadays, IoT devices must emphasize “security”; without data encryption, information sent to the server may be intercepted (e.g., passwords for smart locks). The advantage of mbed TLS + network stack is not just that it “can encrypt”, but that it “integrates encryption and networking”, eliminating the need to piece together a protocol stack and encryption library, saving 90% of compatibility issues.It supports “lightweight encryption algorithms”; IoT devices often have weak computing power (e.g., ARM Cortex-M0+, with a clock speed of only 48MHz), and running heavy encryption algorithms like RSA can cause lag. mbed TLS includes a lightweight version of ECC (Elliptic Curve Cryptography), which, at the same level of security, has keys that are 10 times smaller than RSA (e.g., 256-bit ECC ≈ 2048-bit RSA security), and encryption speed is 3 times faster, making it particularly suitable for low-end IoT MCUs.Many users find it “insufficient memory”; in fact, this is often due to enabling too many “unnecessary security features” by default. Here are several parameters that must be disabled:① Disable full verification of X.509 certificates: By default, it verifies all fields of the certificate (validity period, issuer, signature chain). For IoT devices, it is sufficient to verify “whether the certificate has been tampered with”; disabling other verifications can save over 10KB of memory;② Use “pre-shared keys” instead of certificates: Certificates must reside in Flash (occupying at least 2KB), while small devices can directly use a “pre-shared key” (PSK)—the device and server agree on a key in advance, using it for encryption during communication, saving Flash and verification time;③ Disable rarely used encryption algorithms: mbed TLS by default supports a plethora of algorithms like AES, DES, 3DES, but in practice, using just AES is sufficient. Disabling other algorithms can save 5-8KB of ROM.Suitable scenarios: IoT devices requiring encryption, such as smart home devices (cameras, locks), wearable devices (watches, fitness bands), and industrial IoT (sensors transmitting sensitive data).

4 Lightweight Network Protocol Stacks in Embedded Development: Which Ones Do You Know?

Finally, regardless of which one you choose, first test it on the “minimal system”. For example, run LwIP on STM32F103 (32KB RAM) with only TCP + DHCP enabled to see how much memory is used; use ESP8266 to run mbed TLS and try sending an encrypted packet to see if it lags. Only by running it in practice can you determine if it is suitable.

Leave a Comment