Rust串行通信库z-serial的使用:高效实现跨平台串口数据读写与设备控制

Rust串行通信库z-serial的使用:高效实现跨平台串口数据读写与设备控制

Zenoh Serial

这个仓库包含了基于串行(类似RS232)传输的Zenoh帧格式。

构建

您需要安装rustCargo,请查看Rust官网了解如何安装。

$ cd ~
$ git clone https://github.com/ZettaScaleLabs/z-serial
$ cd z-serial
$ cargo build --release

如何测试

打开两个终端,在第一个终端启动echo服务器:

$ cd ~/z-serial
$ ./target/release/examples/serial-echo <serial device> -s -b <baud rate>
...

在第二个终端启动客户端:

$ cd ~/z-serial
./target/release/examples/serial-echo <serial device>  -i <send interval> -b <baud rate>

完整示例代码

以下是一个使用z-serial库进行串口通信的完整示例:

use std::io::{self, Read, Write};
use std::time::Duration;
use z_serial::{SerialPort, SerialPortBuilder};

fn main() -> io::Result<()> {
    // 配置串口参数
    let port_builder = SerialPortBuilder::new()
        .baud_rate(9600)          // 设置波特率
        .data_bits(8)             // 数据位
        .flow_control_none()      // 无流控制
        .parity_none()            // 无校验位
        .stop_bits(1);            // 停止位

    // 打开串口设备(请根据实际设备路径修改)
    let mut port = port_builder.open("/dev/ttyUSB0")?;
    
    // 设置读写超时时间
    port.set_timeout(Duration::from_millis(1000))?;

    // 要发送的数据
    let data_to_send = b"Hello, Serial World!";
    
    // 写入数据到串口
    port.write_all(data_to_send)?;
    println!("Sent: {:?}", data_to_send);

    // 读取返回数据
    let mut buffer = [0u8; 256];
    let bytes_read = port.read(&mut buffer)?;
    
    // 处理接收到的数据
    if bytes_read > 0 {
        let received_data = &buffer[..bytes_read];
        println!("Received: {:?}", received_data);
        
        // 将接收到的数据转换为字符串(如果是文本数据)
        if let Ok(received_str) = std::str::from_utf8(received_data) {
            println!("Received as string: {}", received_str);
        }
    }

    Ok(())
}

高级使用示例

use z_serial::{SerialPort, SerialPortBuilder, SerialPortInfo};
use std::time::Duration;
use std::io::{self, Read, Write};

// 列出所有可用串口设备
fn list_serial_ports() -> Vec<SerialPortInfo> {
    z_serial::available_ports().unwrap_or_default()
}

// 创建串口通信处理函数
fn create_serial_communication(device: &str, baud_rate: u32) -> io::Result<()> {
    let mut port = SerialPortBuilder::new()
        .baud_rate(baud_rate)
        .data_bits(8)
        .flow_control_none()
        .parity_none()
        .stop_bits(1)
        .open(device)?;

    port.set_timeout(Duration::from_millis(1000))?;

    // 主通信循环
    loop {
        // 读取数据
        let mut buffer = [0u8; 1024];
        match port.read(&mut buffer) {
            Ok(bytes_read) if bytes_read > 0 => {
                let data = &buffer[..bytes_read];
                println!("Received {} bytes: {:?}", bytes_read, data);
                
                // 这里可以添加数据处理逻辑
                process_received_data(data);
                
                // 可选:发送响应
                if should_send_response(data) {
                    let response = prepare_response(data);
                    port.write_all(&response)?;
                }
            }
            Ok(_) => {} // 没有数据收到
            Err(e) if e.kind() == io::ErrorKind::TimedOut => {} // 超时是正常的
            Err(e) => return Err(e), // 其他错误
        }
    }
}

// 数据处理函数
fn process_received_data(data: &[u8]) {
    // 实现您的数据处理逻辑
    println!("Processing data: {:?}", data);
}

// 判断是否需要发送响应
fn should_send_response(data: &[u8]) -> bool {
    // 实现您的响应判断逻辑
    !data.is_empty()
}

// 准备响应数据
fn prepare_response(data: &[u8]) -> Vec<u8> {
    // 实现您的响应准备逻辑
    format!("Echo: {}", String::from_utf8_lossy(data)).into_bytes()
}

安装

在您的项目目录中运行以下Cargo命令:

cargo add z-serial

或者在Cargo.toml中添加以下行:

z-serial = "0.3.1"

完整示例demo

基于上述内容,这里提供一个完整的串口通信示例,包含设备枚举、配置和双向通信:

use std::io::{self, Read, Write};
use std::time::Duration;
use z_serial::{SerialPort, SerialPortBuilder, SerialPortInfo};

fn main() -> io::Result<()> {
    // 列出所有可用串口设备
    println!("Available serial ports:");
    let ports = z_serial::available_ports().unwrap_or_default();
    for port in &ports {
        println!("- {}", port.port_name);
    }

    if ports.is_empty() {
        eprintln!("No serial ports found!");
        return Ok(());
    }

    // 使用第一个找到的串口设备
    let device = &ports[0].port_name;
    println!("Using device: {}", device);

    // 配置串口参数
    let mut port = SerialPortBuilder::new()
        .baud_rate(115200)        // 设置波特率
        .data_bits(8)             // 数据位
        .flow_control_none()      // 无流控制
        .parity_none()            // 无校验位
        .stop_bits(1)             // 停止位
        .open(device)?;           // 打开串口设备

    // 设置读写超时时间
    port.set_timeout(Duration::from_millis(1000))?;

    println!("Serial port opened successfully. Press Ctrl+C to exit.");

    // 主通信循环
    let mut buffer = [0u8; 1024];
    loop {
        // 尝试读取数据
        match port.read(&mut buffer) {
            Ok(bytes_read) if bytes_read > 0 => {
                let received_data = &buffer[..bytes_read];
                println!("Received {} bytes: {:?}", bytes_read, received_data);
                
                // 尝试转换为字符串显示
                if let Ok(text) = std::str::from_utf8(received_data) {
                    println!("As text: {}", text);
                }
                
                // 自动回复接收到的数据
                port.write_all(received_data)?;
                println!("Echoed back {} bytes", bytes_read);
            }
            Ok(_) => {
                // 没有数据收到,发送测试消息
                let test_message = b"PING";
                port.write_all(test_message)?;
                println!("Sent: {:?}", test_message);
                
                // 短暂等待
                std::thread::sleep(Duration::from_millis(500));
            }
            Err(e) if e.kind() == io::ErrorKind::TimedOut => {
                // 超时是正常的,继续循环
                continue;
            }
            Err(e) => {
                eprintln!("Read error: {}", e);
                break;
            }
        }
    }

    Ok(())
}

这个完整示例展示了如何:

  1. 枚举系统中可用的串口设备
  2. 配置串口通信参数(波特率、数据位、停止位等)
  3. 打开串口设备并进行双向数据通信
  4. 处理超时和错误情况
  5. 实现简单的数据回显功能

使用时请根据实际硬件设备修改串口设备路径和通信参数。


1 回复

Rust串行通信库z-serial的使用指南

概述

z-serial是一个跨平台的Rust串行通信库,提供了简单高效的API来实现串口数据读写和设备控制。该库支持Windows、Linux和macOS系统,能够帮助开发者快速构建与串口设备通信的应用程序。

主要特性

  • 跨平台支持(Windows/Linux/macOS)
  • 异步和同步读写操作
  • 灵活的串口配置选项
  • 错误处理和超时机制
  • 零成本抽象和高性能

安装方法

在Cargo.toml中添加依赖:

[dependencies]
z-serial = "0.3"

基本使用方法

1. 打开串口

use z_serial::{SerialPort, SerialPortBuilder};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut port = SerialPortBuilder::new("/dev/ttyUSB0")
        .baud_rate(9600)
        .data_bits(8)
        .stop_bits(1)
        .parity_none()
        .open()?;
    
    Ok(())
}

2. 同步读写数据

use z_serial::{SerialPort, SerialPortBuilder};
use std::io::{Read, Write};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut port = SerialPortBuilder::new("COM3")
        .baud_rate(115200)
        .open()?;

    // 写入数据
    let data = b"Hello, Serial!";
    port.write_all(data)?;

    // 读取数据
    let mut buffer = [0; 128];
    let bytes_read = port.read(&mut buffer)?;
    println!("Received: {:?}", &buffer[..bytes_read]);

    Ok(())
}

3. 异步操作(使用async/await)

use z_serial::{AsyncSerialPort, SerialPortBuilder};
use tokio::io::{AsyncReadExt, AsyncWriteExt};

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut port = SerialPortBuilder::new("/dev/ttyS0")
        .baud_rate(9600)
        .open_async()?;

    // 异步写入
    port.write_all(b"AT+COMMAND\r\n").await?;

    // 异步读取
    let mut response = vec![0; 1024];
    let n = port.read(&mut response).await?;
    println!("Response: {}", String::from_utf8_lossy(&response[..n]));

    Ok(())
}

4. 配置串口参数

use z_serial::{SerialPortBuilder, DataBits, StopBits, Parity, FlowControl};

let port = SerialPortBuilder::new("COM1")
    .baud_rate(115200)
    .data_bits(DataBits::Eight)
    .stop_bits(StopBits::One)
    .parity(Parity::None)
    .flow_control(FlowControl::None)
    .timeout(std::time::Duration::from_millis(1000))
    .open()?;

5. 设备控制示例

use z_serial::{SerialPort, SerialPortBuilder};
use std::thread;
use std::time::Duration;

fn control_device() -> Result<(), Box<dyn std::error::Error>> {
    let mut port = SerialPortBuilder::new("/dev/ttyACM0")
        .baud_rate(9600)
        .open()?;

    // 发送控制命令
    let commands = [
        b"SET_LED_ON\r\n",
        b"SET_MOTOR_SPEED:50\r\n",
        b"GET_TEMPERATURE\r\n"
    ];

    for cmd in commands.iter() {
        port.write_all(cmd)?;
        thread::sleep(Duration::from_millis(100));
        
        let mut response = [0; 64];
        match port.read(&mut response) {
            Ok(n) => println!("Response: {}", String::from_utf8_lossy(&response[..n])),
            Err(e) => eprintln!("Read error: {}", e),
        }
    }

    Ok(())
}

错误处理

use z_serial::{SerialPort, SerialPortBuilder, SerialError};

fn handle_serial_errors() -> Result<(), SerialError> {
    let port = SerialPortBuilder::new("COM5")
        .baud_rate(9600)
        .open()
        .map_err(|e| {
            eprintln!("Failed to open serial port: {}", e);
            e
        })?;
    
    // 其他操作...
    Ok(())
}

注意事项

  1. 在Linux/macOS上可能需要适当的权限才能访问串口设备
  2. 确保使用的波特率、数据位等参数与目标设备匹配
  3. 考虑使用超时设置来避免阻塞操作
  4. 在生产环境中建议实现完整的错误处理和重试机制

这个库为Rust开发者提供了简单而强大的串口通信能力,适用于各种嵌入式系统、工业控制和物联网设备的开发场景。

完整示例代码

use z_serial::{SerialPort, SerialPortBuilder, DataBits, StopBits, Parity, FlowControl};
use std::io::{Read, Write};
use std::thread;
use std::time::Duration;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 配置并打开串口
    let mut port = SerialPortBuilder::new("/dev/ttyUSB0")  // 根据实际设备修改端口
        .baud_rate(9600)                                  // 设置波特率
        .data_bits(DataBits::Eight)                       // 设置数据位
        .stop_bits(StopBits::One)                         // 设置停止位
        .parity(Parity::None)                             // 设置校验位
        .flow_control(FlowControl::None)                  // 设置流控制
        .timeout(Duration::from_millis(1000))             // 设置超时时间
        .open()?;                                         // 打开串口

    println!("串口打开成功!");

    // 同步写入数据
    let command = b"AT+VER\r\n";                          // 示例命令
    port.write_all(command)?;
    println!("发送命令: {}", String::from_utf8_lossy(command));

    // 等待设备响应
    thread::sleep(Duration::from_millis(100));

    // 同步读取数据
    let mut buffer = [0; 128];                            // 接收缓冲区
    match port.read(&mut buffer) {
        Ok(bytes_read) => {
            if bytes_read > 0 {
                println!("接收数据: {}", String::from_utf8_lossy(&buffer[..bytes_read]));
            } else {
                println!("未接收到数据");
            }
        }
        Err(e) => eprintln!("读取错误: {}", e),
    }

    // 发送多个控制命令
    let commands = [
        b"LED_ON\r\n",
        b"GET_STATUS\r\n",
        b"LED_OFF\r\n"
    ];

    for cmd in commands.iter() {
        port.write_all(cmd)?;
        println!("发送命令: {}", String::from_utf8_lossy(cmd));
        
        thread::sleep(Duration::from_millis(50));
        
        let mut response = [0; 64];
        match port.read(&mut response) {
            Ok(n) => {
                if n > 0 {
                    println!("设备响应: {}", String::from_utf8_lossy(&response[..n]));
                }
            }
            Err(e) => eprintln!("读取错误: {}", e),
        }
        
        thread::sleep(Duration::from_millis(100));
    }

    println!("通信完成!");
    Ok(())
}
回到顶部