嵌入式以太网编程
概述
嵌入式以太网编程涉及TCP/IP协议栈的实现和应用,是物联网和工业控制系统的核心技术。
网络协议栈
TCP/IP模型
| Text Only |
|---|
| ┌─────────────────────────────┐
│ 应用层 (Application) │ HTTP, MQTT, CoAP
├─────────────────────────────┤
│ 传输层 (Transport) │ TCP, UDP
├─────────────────────────────┤
│ 网络层 (Network) │ IP, ICMP, ARP
├─────────────────────────────┤
│ 数据链路层 (Data Link) │ Ethernet, MAC
├─────────────────────────────┤
│ 物理层 (Physical) │ PHY
└─────────────────────────────┘
|
LwIP协议栈
LwIP(Lightweight IP)是专为嵌入式系统设计的轻量级TCP/IP协议栈。
初始化LwIP
| C |
|---|
| #include "lwip/init.h"
#include "lwip/netif.h"
#include "netif/ethernetif.h"
static struct netif netif;
void lwip_init(void)
{
ip4_addr_t ipaddr, netmask, gw;
IP4_ADDR(&ipaddr, 192, 168, 1, 100);
IP4_ADDR(&netmask, 255, 255, 255, 0);
IP4_ADDR(&gw, 192, 168, 1, 1);
lwip_init();
netif_add(&netif, &ipaddr, &netmask, &gw, NULL,
ethernetif_init, ethernet_input);
netif_set_up(&netif);
netif_set_default(&netif);
}
|
TCP客户端
| C |
|---|
| #include "lwip/sockets.h"
int tcp_client_connect(const char *host, int port)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
inet_pton(AF_INET, host, &addr.sin_addr);
if (connect(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
close(sock);
return -1;
}
return sock;
}
void tcp_client_example(void)
{
int sock = tcp_client_connect("192.168.1.1", 80);
// 发送HTTP请求
const char *request = "GET / HTTP/1.1\r\nHost: 192.168.1.1\r\n\r\n";
send(sock, request, strlen(request), 0);
// 接收响应
char buf[1024];
int len = recv(sock, buf, sizeof(buf), 0);
close(sock);
}
|
TCP服务器
| C |
|---|
| void tcp_server_example(void)
{
int server_sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(8080);
addr.sin_addr.s_addr = INADDR_ANY;
bind(server_sock, (struct sockaddr *)&addr, sizeof(addr));
listen(server_sock, 5);
while (1) {
struct sockaddr_in client_addr;
socklen_t addr_len = sizeof(client_addr);
int client_sock = accept(server_sock,
(struct sockaddr *)&client_addr,
&addr_len);
// 处理客户端
char buf[1024];
int len = recv(client_sock, buf, sizeof(buf), 0);
if (len > 0) {
send(client_sock, buf, len, 0);
}
close(client_sock);
}
}
|
UDP通信
| C |
|---|
| void udp_example(void)
{
int sock = socket(AF_INET, SOCK_DGRAM, 0);
struct sockaddr_in local_addr;
local_addr.sin_family = AF_INET;
local_addr.sin_port = htons(12345);
local_addr.sin_addr.s_addr = INADDR_ANY;
bind(sock, (struct sockaddr *)&local_addr, sizeof(local_addr));
// 发送UDP数据
struct sockaddr_in remote_addr;
remote_addr.sin_family = AF_INET;
remote_addr.sin_port = htons(12346);
inet_pton(AF_INET, "192.168.1.2", &remote_addr.sin_addr);
const char *data = "Hello UDP";
sendto(sock, data, strlen(data), 0,
(struct sockaddr *)&remote_addr, sizeof(remote_addr));
// 接收UDP数据
char buf[1024];
struct sockaddr_in from_addr;
socklen_t from_len = sizeof(from_addr);
int len = recvfrom(sock, buf, sizeof(buf), 0,
(struct sockaddr *)&from_addr, &from_len);
}
|
MQTT客户端
使用Paho MQTT
| C |
|---|
| #include "MQTTClient.h"
#define ADDRESS "tcp://192.168.1.1:1883"
#define CLIENTID "EmbeddedClient"
#define TOPIC "sensor/data"
#define QOS 1
#define TIMEOUT 10000L
void mqtt_publish(void)
{
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
MQTTClient_message pubmsg = MQTTClient_message_initializer;
MQTTClient_create(&client, ADDRESS, CLIENTID,
MQTTCLIENT_PERSISTENCE_NONE, NULL);
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
if (MQTTClient_connect(client, &conn_opts) == MQTTCLIENT_SUCCESS) {
pubmsg.payload = "Hello MQTT";
pubmsg.payloadlen = strlen("Hello MQTT");
pubmsg.qos = QOS;
pubmsg.retained = 0;
MQTTClient_publishMessage(client, TOPIC, &pubmsg, NULL);
MQTTClient_disconnect(client, TIMEOUT);
}
MQTTClient_destroy(&client);
}
|
订阅消息
| C |
|---|
| void messageArrived(void *context, char *topicName,
int topicLen, MQTTClient_message *message)
{
printf("Message arrived: %.*s\n", message->payloadlen,
(char *)message->payload);
MQTTClient_freeMessage(&message);
MQTTClient_free(topicName);
}
void mqtt_subscribe(void)
{
MQTTClient client;
MQTTClient_connectOptions conn_opts = MQTTClient_connectOptions_initializer;
MQTTClient_create(&client, ADDRESS, CLIENTID,
MQTTCLIENT_PERSISTENCE_NONE, NULL);
MQTTClient_setCallbacks(client, NULL, NULL, messageArrived, NULL);
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
if (MQTTClient_connect(client, &conn_opts) == MQTTCLIENT_SUCCESS) {
MQTTClient_subscribe(client, TOPIC, QOS);
// 保持连接,等待消息
while (1) {
// 可以在这里做其他工作
sleep(1);
}
}
}
|
HTTP客户端
简单HTTP GET
| C |
|---|
| #include "lwip/sockets.h"
void http_get(const char *host, const char *path)
{
int sock = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(80);
inet_pton(AF_INET, host, &addr.sin_addr);
connect(sock, (struct sockaddr *)&addr, sizeof(addr));
// 发送请求
char request[256];
snprintf(request, sizeof(request),
"GET %s HTTP/1.1\r\nHost: %s\r\nConnection: close\r\n\r\n",
path, host);
send(sock, request, strlen(request), 0);
// 接收响应
char response[4096];
int total = 0, len;
while ((len = recv(sock, response + total,
sizeof(response) - total - 1, 0)) > 0) {
total += len;
}
response[total] = '\0';
close(sock);
}
|
网络配置
DHCP客户端
| C |
|---|
| #include "lwip/dhcp.h"
void start_dhcp(void)
{
dhcp_start(&netif);
// 等待获取IP
while (!dhcp_supplied_address(&netif)) {
// 处理LwIP定时任务
sys_check_timeouts();
sleep(1);
}
printf("IP: %s\n", ipaddr_ntoa(&netif.ip_addr));
}
|
静态IP配置
| C |
|---|
| void set_static_ip(void)
{
ip4_addr_t ipaddr, netmask, gw;
IP4_ADDR(&ipaddr, 192, 168, 1, 100);
IP4_ADDR(&netmask, 255, 255, 255, 0);
IP4_ADDR(&gw, 192, 168, 1, 1);
netif_set_addr(&netif, &ipaddr, &netmask, &gw);
}
|
STM32以太网
HAL库初始化
| C |
|---|
| #include "stm32f4xx_hal.h"
ETH_HandleTypeDef heth;
void ETH_Init(void)
{
heth.Instance = ETH;
heth.Init.AutoNegotiation = ETH_AUTONEGOTIATION_ENABLE;
heth.Init.Speed = ETH_SPEED_100M;
heth.Init.DuplexMode = ETH_MODE_FULLDUPLEX;
heth.Init.PhyAddress = 0;
heth.Init.RxMode = ETH_RXINTERRUPT_MODE;
heth.Init.ChecksumMode = ETH_CHECKSUM_BY_HARDWARE;
heth.Init.MediaInterface = ETH_MEDIA_INTERFACE_RMII;
HAL_ETH_Init(&heth);
}
|
以太网中断处理
| C |
|---|
| void ETH_IRQHandler(void)
{
HAL_ETH_IRQHandler(&heth);
}
void HAL_ETH_RxCpltCallback(ETH_HandleTypeDef *heth)
{
// 接收完成,通知LwIP处理
ethernetif_input(&netif);
}
|
ESP32以太网
| C |
|---|
| #include "esp_eth.h"
#include "esp_event.h"
void eth_init(void)
{
// 初始化MAC
eth_mac_config_t mac_config = ETH_MAC_DEFAULT_CONFIG();
esp_eth_mac_t *mac = esp_eth_mac_new_dm9051(&mac_config);
// 初始化PHY
eth_phy_config_t phy_config = ETH_PHY_DEFAULT_CONFIG();
esp_eth_phy_t *phy = esp_eth_phy_new_dm9051(&phy_config);
// 初始化以太网
esp_eth_config_t config = ETH_DEFAULT_CONFIG(mac, phy);
esp_eth_handle_t eth_handle;
esp_eth_driver_install(&config, ð_handle);
// 启动以太网
esp_eth_start(eth_handle);
}
|
网络调试
ping工具
| C |
|---|
| #include "lwip/icmp.h"
#include "lwip/raw.h"
void ping_send(const char *target)
{
// 构造ICMP Echo请求
// 发送并等待响应
}
|
netstat
| Bash |
|---|
| # 查看网络连接
cat /proc/net/tcp
cat /proc/net/udp
cat /proc/net/raw
|
参考资料