For network communication on resource-constrained microcontrollers, lwIP (lightweight IP) is an ideal choice. lwIP is a lightweight TCP/IP protocol stack designed specifically for embedded systems.
Basics of lwIP
What is a Network Protocol Stack?
Imagine the process of sending a package:
- • Application Layer: The item you want to send (data)
- • Transport Layer: Choosing a courier service (TCP/UDP)
- • Network Layer: Filling in the address (IP address)
- • Link Layer: Actual transportation (Ethernet)
lwIP is like this “courier system” that helps you package, address, and transport data in a standard format.
Architecture of lwIP
The lwIP protocol stack mainly consists of the following modules:
┌─────────────────────────────────────┐
│ Application Layer (HTTP/MQTT/Custom Protocol) │
├─────────────────────────────────────┤
│ Transport Layer (TCP/UDP) │
├─────────────────────────────────────┤
│ Network Layer (IP/ICMP) │
├─────────────────────────────────────┤
│ Link Layer (ARP/Ethernet Driver) │
├─────────────────────────────────────┤
│ Hardware Abstraction Layer (PHY/MAC Driver) │
└─────────────────────────────────────┘
Description of Core Components:
- 1. Memory Management: lwIP manages memory using both memory pools (memp) and heap memory.
- 2. Network Interface (netif): Each network interface corresponds to a physical network connection.
- 3. Protocol Implementations: TCP, UDP, IP, ICMP, ARP, etc.
- 4. Application Interfaces: Three programming interfaces: Raw API, Netconn API, Socket API.
Three Programming Interfaces of lwIP
1. Raw API
- • Lowest level, highest performance
- • Requires understanding of protocol details
- • Suitable for scenarios with extremely high performance requirements
2. Netconn API
- • Medium level of abstraction
- • Thread-safe, suitable for multi-threaded environments
- • Commonly used with STM32 HAL library
3. Socket API
- • Closest to standard BSD Socket
- • Easy to port PC-side code
- • Requires operating system support
Porting lwIP on STM32
Hardware Requirements
To implement network communication on STM32, the following hardware is required:
- 1. STM32 Chip: Models that support Ethernet peripherals (e.g., STM32F407, STM32H743, etc.)
- 2. PHY Chip: Physical layer chip (e.g., LAN8742A, LAN8720A, etc.)
- 3. Network Transformer: RJ45 interface module (usually integrated on the development board)
- 4. External Clock: 25MHz crystal oscillator (required by PHY chip)
Software Architecture
The typical architecture for using lwIP on STM32 is as follows:
┌─────────────────────────────────────┐
│ User Application Code │
├─────────────────────────────────────┤
│ lwIP Protocol Stack (lwip.c/h) │
├─────────────────────────────────────┤
│ STM32 HAL Ethernet Driver │
│ (stm32f4xx_hal_eth.c) │
├─────────────────────────────────────┤
│ PHY Driver (lan8742.c) │
├─────────────────────────────────────┤
│ Hardware Layer (MAC + PHY) │
└─────────────────────────────────────┘
Key Steps for Porting
Configure lwIP Parameters
Configure key parameters in <span>lwipopts.h</span>:
- •
<span>LWIP_TCP</span>: Enable TCP (usually required) - •
<span>LWIP_UDP</span>: Enable UDP - •
<span>TCP_MSS</span>: Maximum segment size for TCP (default 536 bytes) - •
<span>TCP_WND</span>: TCP window size - •
<span>MEM_SIZE</span>: Memory pool size (adjust according to RAM) - •
<span>PBUF_POOL_SIZE</span>: Size of PBUF pool (affects the number of concurrent connections)
Implement Network Interface Initialization
The following functions need to be implemented:
- •
<span>low_level_init()</span>: Initialize MAC and PHY - •
<span>low_level_output()</span>: Send data packets - •
<span>ethernetif_input()</span>: Handle received data packets
Configure Clocks and Interrupts
- • Configure system clock (usually requires above 168MHz)
- • Configure Ethernet MAC clock
- • Enable Ethernet receive interrupts
Start lwIP Task
Create lwIP tasks in FreeRTOS and periodically call <span>sys_check_timeouts()</span><span> to handle timeouts.</span>
Basic Network Communication
TCP Server Example
The basic process for implementing a TCP server is as follows:
- 1. Create Connection: Use
<span>netconn_new()</span><span> to create a TCP connection</span> - 2. Bind Port: Use
<span>netconn_bind()</span><span> to bind the local port</span> - 3. Listen for Connections: Use
<span>netconn_listen()</span><span> to start listening</span> - 4. Accept Connection: Use
<span>netconn_accept()</span><span> to accept client connections</span> - 5. Send and Receive Data: Use
<span>netconn_recv()</span><span> and </span><code><span>netconn_write()</span><span> to transmit data</span> - 6. Close Connection: Use
<span>netconn_close()</span><span> to close the connection</span>
Key Points:
- • TCP is connection-oriented and requires establishing a connection first
- • The server needs to continuously listen, usually placed in a separate task
- • It is recommended to create a separate task for each client connection
UDP Communication Example
UDP communication is simpler:
- 1. Create Connection: Create a UDP type netconn
- 2. Bind Port: Bind the local port
- 3. Send Data: Use
<span>netconn_sendto()</span><span> to send to a specified address</span> - 4. Receive Data: Use
<span>netconn_recvfrom()</span><span> to receive data</span>
Key Points:
- • UDP is connectionless and does not require establishing a connection
- • Suitable for scenarios with high real-time requirements and allowed packet loss
- • The application layer needs to handle packet loss and out-of-order delivery
DHCP Automatic IP Acquisition
The steps to automatically acquire an IP address using DHCP are:
- 1. Enable
<span>LWIP_DHCP</span>in<span>lwipopts.h</span> - 2. Call
<span>dhcp_start()</span><span> after initializing the network interface</span> - 3. Periodically call
<span>dhcp_fine_tmr()</span>and<span>dhcp_coarse_tmr()</span> - 4. Check the status of
<span>netif_is_up()</span>and<span>netif_is_link_up()</span>
Advantages: No manual IP configuration is required, and the device can automatically connect to the network.
Troubleshooting
Network Cannot Connect, Ping Fails
Possible Causes and Solutions:
- 1. PHY Chip Not Initialized
- • Check the reset and configuration of the PHY chip
- • Confirm if the PHY address is correct (usually 0 or 1)
- • Use an oscilloscope to check if the 25MHz clock is normal
- • Ensure a unique MAC address is set during initialization
- • MAC address format: 6 bytes, e.g.,
<span>{0x02, 0x00, 0x00, 0x00, 0x00, 0x01}</span>
- • Check if
<span>netif_add()</span>was successful - • Confirm that
<span>netif_set_up()</span>and<span>netif_set_link_up()</span>have been called - • Check the status bits of
<span>netif->flags</span>
- • Static IP: Check if IP, subnet mask, and gateway are correct
- • DHCP: Confirm if the DHCP server is reachable, check the return value of
<span>dhcp_supplied_address()</span>
Debugging Tips:
- • Use
<span>netif_list</span>to traverse all network interfaces and check their status - • Print
<span>netif->ip_addr</span>to view the current IP address - • Use Wireshark to capture and analyze network traffic
TCP Connection Frequently Drops
Possible Causes and Solutions:
- 1. Insufficient Memory
- • Increase
<span>MEM_SIZE</span>and<span>PBUF_POOL_SIZE</span> - • Check for memory leaks (unreleased netbuf)
- • Use
<span>mem_malloc()</span>and<span>mem_free()</span><span> to track memory usage</span>
- • Adjust
<span>TCP_MSL</span>(maximum segment lifetime) - • Increase
<span>TCP_KEEPALIVE</span>related parameters - • Check the application layer heartbeat mechanism
- • Increase
<span>TCP_WND</span>(receive window) - • Call
<span>netconn_recv()</span>promptly to read data - • Use
<span>netconn_set_recvtimeout()</span>to set receive timeout
- • Check network quality (packet loss rate, latency)
- • Adjust
<span>TCP_MSS</span>to reduce packet size - • Implement application layer retransmission mechanism
Debugging Tips:
- • Enable
<span>LWIP_STATS</span>to track TCP connection status - • Use
<span>tcp_debug_print_pcbs()</span>to print all TCP connection information - • Monitor the length of
<span>tcp_active_pcbs</span>linked list
Slow Data Transfer Speed
Possible Causes and Solutions:
- 1. TCP Window Too Small
- • Increase
<span>TCP_WND</span>(recommended to be 4-8 times TCP_MSS) - • Enable
<span>TCP_WND_UPDATE_THRESHOLD</span>to optimize window updates
- • Increase
<span>TCP_SND_BUF</span>(send buffer size) - • Check the write size limit of
<span>netconn_write()</span>
- • Increase the priority of the lwIP task
- • Ensure the Ethernet interrupt priority is high enough
- • Avoid performing time-consuming operations in interrupts
- • Check if full-duplex mode is enabled
- • Confirm PHY working rate (10M/100M)
- • Check if auto-negotiation was successful
Performance Optimization Suggestions:
- • Use Raw API instead of Netconn API (20-30% performance improvement)
- • Enable
<span>LWIP_TCP_FAST_OPEN</span>(if supported) - • Batch send data to reduce system call frequency
DHCP IP Acquisition Failure
Possible Causes and Solutions:
- 1. DHCP Server Unreachable
- • Confirm that the device and router are on the same subnet (initial state)
- • Check cable connections and network interface status
- • Test network connectivity using a static IP
- • Increase
<span>DHCP_TIMEOUT</span>and<span>DHCP_T1</span>,<span>DHCP_T2</span> - • Check the calling frequency of
<span>dhcp_fine_tmr()</span>
- • DHCP requires additional memory space
- • Ensure
<span>MEM_SIZE</span>is large enough
- • Ensure DHCP is enabled on only one network interface
- • Check the number of interfaces in
<span>netif_list</span>
Debugging Tips:
- • Enable
<span>LWIP_DEBUG</span>and<span>DHCP_DEBUG</span> - • Print the DHCP state machine status (
<span>dhcp->state</span>) - • Use Wireshark to capture and view the DHCP interaction process
Memory Leak Causing System Crash
Possible Causes and Solutions:
- 1. netbuf Not Released
- • Must call
<span>netbuf_delete()</span>after each<span>netconn_recv()</span> - • Use the return value of
<span>netconn_recv()</span>to check for data
- • Must call
<span>netconn_delete()</span>after the connection is closed - • Close the connection after the server has processed the client
- • Check for unreleased PBUF in custom code
- • Use
<span>pbuf_free()</span>to release PBUF
- • Monitor
<span>MEMP_STATS</span>statistics - • Increase the corresponding memory pool size
Preventive Measures:
- • Regularly check using memory statistics features
- • Implement memory usage monitoring and alerts
- • Focus on resource release during code reviews
Race Conditions in Multi-threaded Environments
Possible Causes and Solutions:
- 1. lwIP Core Functions Not Thread-Safe
- • Use
<span>sys_mutex</span>to protect critical sections - • Alternatively, use
<span>tcpip_callback()</span>to submit operations to the lwIP thread
- • Avoid operating the network interface simultaneously in interrupts and tasks
- • Use message queues to pass network events
- • Use mutexes to protect shared netconn structures
- • Avoid performing time-consuming operations in callback functions
Solutions:
- • All lwIP API calls should be made within the lwIP task
- • Use
<span>tcpip_callback()</span><span> to safely call lwIP functions from other threads</span> - • Notify the application layer of network events through message queues
Debugging Tips and Tools
Using Wireshark for Packet Analysis
Steps:
- 1. Install Wireshark on your PC
- 2. Connect the STM32 device to the same network as the PC
- 3. Select the network interface in Wireshark to start capturing packets
- 4. Filter specific IPs or protocols for analysis
Common Filter Conditions:
- •
<span>ip.addr == 192.168.1.100</span>: Filter specific IP - •
<span>tcp.port == 80</span>: Filter TCP port 80 - •
<span>dhcp</span>: Show only DHCP packets
Enable lwIP Debug Information
Enable debugging in <span>lwipopts.h</span>:
#define LWIP_DEBUG 1
#define TCP_DEBUG LWIP_DBG_ON
#define NETIF_DEBUG LWIP_DBG_ON
#define DHCP_DEBUG LWIP_DBG_ON
Note: Debug information will significantly increase code size and runtime; it should be disabled in release versions.
Using Ping to Test Connectivity
Basic Usage:
- •
<span>ping 192.168.1.100</span>: Test if the device is reachable - •
<span>ping -t 192.168.1.100</span>: Continuous ping (Windows) - •
<span>ping -c 10 192.168.1.100</span>: Send 10 packets (Linux)
Analysis Metrics:
- • Response time: Should normally be less than 10ms (LAN)
- • Packet loss rate: Should be 0%
- • TTL value: Check the number of routing hops
Memory Usage Monitoring
After enabling statistics features, you can print memory usage:
// Print memory statistics
printf("MEM: used=%d, max=%d, err=%d\n",
lwip_stats.mem.used,
lwip_stats.mem.max,
lwip_stats.mem.err);
// Print PBUF statistics
printf("PBUF: used=%d, max=%d\n",
lwip_stats.pbuf.used,
lwip_stats.pbuf.max);