Rust嵌入式网络库embassy-net的使用,异步TCP/IP协议栈实现轻量级物联网设备通信

Rust嵌入式网络库embassy-net的使用,异步TCP/IP协议栈实现轻量级物联网设备通信

embassy-net介绍

embassy-net是一个无标准库(std)、无内存分配(no-alloc)的异步网络栈,专为嵌入式系统设计。

它基于smoltcp构建,提供了更高层次、更规范的API。它将smoltcp提供的组件粘合在一起,处理低级细节,使用为嵌入式系统设计的默认值和内存管理,旨在提供更"开箱即用"的体验。

特性

  • IPv4, IPv6支持
  • 以太网和裸IP介质
  • TCP, UDP, DNS, DHCPv4
  • TCP sockets实现了embedded-io异步特性
  • 组播支持

硬件支持

  • esp-wifi:用于ESP32芯片的WiFi支持
  • cyw43:用于Raspberry Pi Pico W中的CYW43xx芯片的WiFi支持
  • embassy-usb:支持USB以太网(CDC NCM)
  • embassy-stm32:支持STM32芯片的内置以太网MAC
  • embassy-net-wiznet:支持Wiznet SPI以太网MAC+PHY芯片(W5100S, W5500)
  • embassy-net-esp-hosted:使用ESP32芯片作为非ESP32 MCU的WiFi适配器

完整示例代码

以下是一个使用embassy-net实现TCP客户端连接的完整示例:

use embassy_net::{Stack, Config, DhcpConfig, StaticConfig};
use embassy_net::tcp::TcpSocket;
use embassy_net::driver::Driver;
use embassy_time::{Duration, Timer};
use core::str::FromStr;

#[embassy_executor::task]
async fn net_task(stack: &'static Stack<Driver>) {
    stack.run().await
}

#[embassy_executor::task]
async fn tcp_client_task(stack: &'static Stack<Driver>) {
    let mut rx_buffer = [0; 4096];
    let mut tx_buffer = [0; 4096];
    
    loop {
        // Wait for DHCP to assign IP address
        while stack.config_v4().is_none() {
            Timer::after(Duration::from_secs(1)).await;
        }

        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
        
        // Connect to server
        let remote_endpoint = (Ipv4Address::from_str("192.168.1.100").unwrap(), 8000);
        socket.connect(remote_endpoint).await.unwrap();
        
        // Send data
        socket.write_all(b"Hello from embassy-net!").await.unwrap();
        
        // Read response
        let mut buf = [0; 1024];
        let n = socket.read(&mut buf).await.unwrap();
        info!("Received {} bytes: {:?}", n, &buf[..n]);
        
        socket.close().await;
        
        Timer::after(Duration::from_secs(10)).await;
    }
}

#[embassy_executor::main]
async fn main(spawner: embassy_executor::Spawner) {
    // Initialize network driver (this depends on your hardware)
    let driver = init_network_driver();
    
    // Create network stack
    let config = Config::Dhcp(DhcpConfig {
        hostname: Some("embassy-net-example".into()),
        ..Default::default()
    });
    
    let stack = &*Box::leak(Box::new(Stack::new(
        driver,
        config,
        embassy_net::stack::StackResources::new(),
        seed,
    )));
    
    // Spawn network task
    spawner.spawn(net_task(stack)).unwrap();
    
    // Spawn TCP client task
    spawner.spawn(tcp_client_task(stack)).unwrap();
}

添加对新硬件的支持

要为新硬件添加embassy-net支持(如新的以太网或WiFi芯片),你需要实现embassy-net-driver特性。

互操作性

这个crate可以在任何执行器上运行。它使用embassy-time进行时间记录和超时处理。


1 回复

embassy-net: Rust嵌入式网络库的异步TCP/IP协议栈实现

介绍

embassy-net是Rust生态中专门为嵌入式设备设计的异步网络协议栈,属于embassy嵌入式运行时的一部分。它为资源受限的物联网设备提供了轻量级的TCP/IP网络功能实现,具有以下特点:

  • 专为嵌入式系统优化,内存占用小
  • 纯Rust实现,无unsafe代码
  • 基于async/await的异步API
  • 支持以太网和WiFi等常见接口
  • 包含ARP、IPv4、ICMP、TCP和UDP等协议实现

基本使用方法

添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
embassy-net = { version = "0.1", features = ["tcp", "udp"] }
embassy-executor = { version = "0.1" }
embassy-time = { version = "0.1" }

初始化网络栈

use embassy_net::{Stack, Config};
use embassy_net::driver::Driver;
use embassy_net::wire::{Ipv4Address, Ipv4Cidr};

// 假设有一个实现了Driver trait的网卡驱动实例
let driver = MyNetworkDriver::new();

// 配置网络参数
let config = Config::ipv4_static(embassy_net::StaticConfigV4 {
    address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 100), 24),
    dns_servers: Vec::new(),
    gateway: Some(Ipv4Address::new(192, 168, 1, 1)),
});

// 创建网络栈
let stack = Stack::new(driver, config);

TCP服务器示例

use embassy_net::tcp::TcpSocket;
use embassy_net::IpEndpoint;

#[embassy_executor::task]
async fn tcp_server_task(stack: &'static Stack<MyNetworkDriver>) {
    let mut rx_buffer = [0; 4096];
    let mut tx_buffer = [0; 4096];
    
    loop {
        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
        socket.listen(8080).unwrap();
        
        let (mut socket, remote_endpoint) = socket.accept().await.unwrap();
        log::info!("Connection from {}", remote_endpoint);
        
        loop {
            let n = socket.read(&mut rx_buffer).await.unwrap();
            if n == 0 {
                break;
            }
            
            log::info!("Received {} bytes: {:?}", n, &rx_buffer[..n]);
            socket.write_all(&rx_buffer[..n].await.unwrap();
        }
    }
}

TCP客户端示例

#[embassy_executor::task]
async fn tcp_client_task(stack: &'static Stack<MyNetworkDriver>) {
    let mut rx_buffer = [0; 4096];
    let mut tx_buffer = [0; 4096];
    let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
    
    let remote_endpoint = IpEndpoint::new(Ipv4Address::new(192, 168, 1, 1), 8080);
    socket.connect(remote_endpoint).await.unwrap();
    
    let message = b"Hello from embassy-net!";
    socket.write_all(message).await.unwrap();
    
    let n = socket.read(&mut rx_buffer).await.unwrap();
    log::info!("Received echo: {:?}", &rx_buffer[..n]);
}

UDP示例

use embassy_net::udp::UdpSocket;

#[embassy_executor::task]
async fn udp_task(stack: &'static Stack极简嵌入式网络应用完整示例:

```rust
use embassy_executor::Spawner;
use embassy_net::{Stack, Config};
use embassy_net::driver::Driver;
use embassy_net::wire::{Ipv4Address, Ipv4Cidr};
use embassy_net::tcp::TcpSocket;
use embassy_net::IpEndpoint;
use log::info;

// 定义网络驱动类型
struct MyNetworkDriver;

impl Driver for MyNetworkDriver {
    // 实现必要的驱动方法
    // ...
}

#[embassy_executor::main]
async fn main(spawner: Spawner) {
    // 初始化日志
    env_logger::init();
    
    // 创建网络驱动实例
    let driver = MyNetworkDriver;
    
    // 配置静态IP
    let config = Config::ipv4_static(embassy_net::StaticConfigV4 {
        address: Ipv4Cidr::new(Ipv4Address::new(192, 168, 1, 100), 24),
        dns_servers: Vec::new(),
        gateway: Some(Ipv4Address::new(192, 168, 1, 1)),
    });
    
    // 创建网络栈
    let stack = &*Box::leak(Box::new(Stack::new(driver, config)));
    
    // 启动网络任务
    spawner.spawn(network_task(stack)).unwrap();
    
    // 启动TCP服务器
    spawner.spawn(tcp_server_task(stack)).unwrap();
    
    // 启动TCP客户端(演示用)
    spawner.spawn(tcp_client_task(stack)).unwrap();
}

#[embassy_executor::task]
async fn network_task(stack: &'static Stack<MyNetworkDriver>) {
    stack.run().await
}

#[embassy_executor::task]
async fn tcp_server_task(stack: &'static Stack<MyNetworkDriver>) {
    let mut rx_buffer = [0; 1024];
    let mut tx_buffer = [0; 1024];
    
    loop {
        let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
        socket.listen(8080).unwrap();
        
        let (mut socket, remote_endpoint) = socket.accept().await.unwrap();
        info!("Connection from {}", remote_endpoint);
        
        loop {
            let n = socket.read(&mut rx_buffer).await.unwrap();
            if n == 0 { break; }
            
            info!("Received {} bytes", n);
            socket.write_all(&rx_buffer[..n]).await.unwrap();
        }
    }
}

#[embassy_executor::task]
async fn tcp_client_task(stack: &'static Stack<MyNetworkDriver>) {
    let mut rx_buffer = [0; 1024];
    let mut tx_buffer = [0; 1024];
    
    let mut socket = TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
    let remote = IpEndpoint::new(Ipv4Address::new(192, 168, 1, 100), 8080);
    
    socket.connect(remote).await.unwrap();
    socket.write_all(b"Ping").await.unwrap();
    
    let n = socket.read(&mut rx_buffer).await.unwrap();
    info!("Received echo: {:?}", &rx_buffer[..n]);
}

注意事项

  1. embassy-net需要与embassy-executor运行时配合使用
  2. 需要提供实现了Driver trait的底层网络驱动
  3. 缓冲区大小需要根据设备资源情况合理配置
  4. 目前主要支持IPv4,IPv6支持还在开发中

embassy-net为嵌入式设备提供了简单易用的网络功能,特别适合需要轻量级TCP/IP协议栈的物联网应用场景。

回到顶部