告别复杂配置:用egui轻松实现硬件设备的串口通信

【免费下载链接】egui egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native 【免费下载链接】egui 项目地址: https://gitcode.com/GitHub_Trending/eg/egui

你是否还在为嵌入式系统开发中的界面与串口通信难题而困扰?是否经历过调试硬件时反复切换工具的繁琐?本文将带你探索如何使用egui(一个用Rust编写的跨平台即时模式GUI库)快速构建串口通信界面,实现对硬件设备的数据收发与监控,让嵌入式开发变得简单高效。

串口通信界面设计与实现

egui提供了丰富的UI组件,能够轻松构建直观的串口通信界面。通过结合egui的交互能力和Rust的系统级编程优势,我们可以创建出既美观又实用的硬件调试工具。

以下是一个基本的串口通信界面实现示例,包含端口选择、波特率设置、数据收发区域等核心功能:

use eframe::egui;
use serialport::SerialPort;
use std::time::Duration;

struct SerialMonitor {
    port_name: String,
    baud_rate: u32,
    ports: Vec<String>,
    connected: bool,
    serial_port: Option<Box<dyn SerialPort>>,
    received_data: String,
    send_data: String,
}

impl SerialMonitor {
    fn new(_cc: &eframe::CreationContext<'_>) -> Self {
        Self {
            port_name: String::new(),
            baud_rate: 9600,
            ports: Vec::new(),
            connected: false,
            serial_port: None,
            received_data: String::new(),
            send_data: String::new(),
        }
    }
    
    fn update_ports(&mut self) {
        self.ports.clear();
        if let Ok(ports) = serialport::available_ports() {
            self.ports = ports.iter().map(|p| p.port_name.clone()).collect();
        }
    }
    
    fn connect(&mut self) -> Result<(), Box<dyn std::error::Error>> {
        let port = serialport::new(self.port_name.clone(), self.baud_rate)
            .timeout(Duration::from_millis(10))
            .open()?;
        self.serial_port = Some(port);
        self.connected = true;
        Ok(())
    }
    
    fn disconnect(&mut self) {
        self.serial_port = None;
        self.connected = false;
    }
    
    fn read_data(&mut self) {
        if let Some(port) = &mut self.serial_port {
            let mut buf = [0; 1024];
            if let Ok(n) = port.read(&mut buf) {
                if n > 0 {
                    if let Ok(s) = String::from_utf8(buf[..n].to_vec()) {
                        self.received_data.push_str(&s);
                    }
                }
            }
        }
    }
    
    fn send_data(&mut self) {
        if let Some(port) = &mut self.serial_port {
            let _ = port.write(self.send_data.as_bytes());
            self.send_data.clear();
        }
    }
}

impl eframe::App for SerialMonitor {
    fn setup(&mut self, _ctx: &egui::Context, _frame: &mut eframe::Frame, _storage: Option<&dyn eframe::Storage>) {
        self.update_ports();
    }
    
    fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
        if self.connected {
            self.read_data();
            ctx.request_repaint();
        }
        
        egui::CentralPanel::default().show(ctx, |ui| {
            ui.heading("egui串口监控工具");
            
            ui.horizontal(|ui| {
                ui.label("串口号:");
                egui::ComboBox::from_label("")
                    .selected_text(&self.port_name)
                    .show_ui(ui, |ui| {
                        for port in &self.ports {
                            ui.selectable_value(&mut self.port_name, port.clone(), port);
                        }
                    });
                
                ui.label("波特率:");
                egui::ComboBox::from_label("")
                    .selected_text(&self.baud_rate.to_string())
                    .show_ui(ui, |ui| {
                        for &baud in &[9600, 19200, 38400, 57600, 115200] {
                            ui.selectable_value(&mut self.baud_rate, baud, &baud.to_string());
                        }
                    });
                
                if !self.connected {
                    if ui.button("刷新").clicked() {
                        self.update_ports();
                    }
                    
                    if ui.button("连接").clicked() && !self.port_name.is_empty() {
                        if let Err(e) = self.connect() {
                            eprintln!("连接失败: {}", e);
                        }
                    }
                } else {
                    if ui.button("断开").clicked() {
                        self.disconnect();
                    }
                }
            });
            
            if self.connected {
                ui.separator();
                
                ui.label("接收数据:");
                egui::ScrollArea::vertical().show(ui, |ui| {
                    ui.text_edit_multiline(&mut self.received_data);
                });
                
                ui.separator();
                
                ui.horizontal(|ui| {
                    ui.text_edit_singleline(&mut self.send_data);
                    if ui.button("发送").clicked() {
                        self.send_data();
                    }
                });
            }
        });
    }
}

fn main() -> Result<(), eframe::Error> {
    let options = eframe::NativeOptions {
        viewport: egui::ViewportBuilder::default().with_inner_size([600.0, 400.0]),
        ..Default::default()
    };
    
    eframe::run_native(
        "egui串口监控工具",
        options,
        Box::new(|cc| Ok(Box::new(SerialMonitor::new(cc)))),
    )
}

多窗口串口通信应用

egui不仅支持单个窗口应用,还能轻松创建多窗口串口通信界面,这对于同时监控多个串口设备非常有用。egui的examples/serial_windows目录提供了一个多窗口示例,展示了如何顺序打开多个窗口,并在关闭一个窗口后延迟打开下一个窗口。

多窗口串口通信示例

该示例的主要实现逻辑如下:

fn main() -> eframe::Result {
    env_logger::init();

    let options = eframe::NativeOptions {
        run_and_return: true,
        viewport: egui::ViewportBuilder::default().with_inner_size([320.0, 240.0]),
        ..Default::default()
    };

    log::info!("Starting first window…");
    eframe::run_native(
        "First Window",
        options.clone(),
        Box::new(|_cc| Ok(Box::new(MyApp { has_next: true }))),
    )?;

    std::thread::sleep(std::time::Duration::from_secs(2));

    log::info!("Starting second window…");
    eframe::run_native(
        "Second Window",
        options.clone(),
        Box::new(|_cc| Ok(Box::new(MyApp { has_next: true }))),
    )?;

    std::thread::sleep(std::time::Duration::from_secs(2));

    log::info!("Starting third window…");
    eframe::run_native(
        "Third Window",
        options,
        Box::new(|_cc| Ok(Box::new(MyApp { has_next: false }))),
    )
}

在实际的串口通信应用中,我们可以利用这种多窗口能力,为每个串口设备创建独立的监控窗口,实现多设备并行监控。

完整示例代码与项目结构

egui串口通信示例项目的完整结构如下:

examples/serial_windows/
├── Cargo.toml          # 项目依赖配置
├── README.md           # 示例说明文档
├── screenshot.png      # 示例截图
└── src/
    └── main.rs         # 主程序代码

你可以通过以下命令克隆项目并运行串口通信示例:

git clone https://gitcode.com/GitHub_Trending/eg/egui.git
cd egui/examples/serial_windows
cargo run

结语

egui为嵌入式系统和硬件设备开发提供了强大而灵活的界面构建能力。通过结合Rust的系统级编程优势和egui的跨平台UI渲染能力,我们能够快速开发出专业的串口通信工具,极大提高硬件调试和嵌入式开发的效率。

无论是简单的设备监控还是复杂的嵌入式系统调试,egui都能为你提供直观、高效的用户界面解决方案。希望本文介绍的egui串口通信应用能够帮助你更好地连接和控制硬件设备。

如果你想深入了解egui的更多功能,可以参考以下资源:

通过这些资源,你可以进一步探索egui在嵌入式系统、物联网设备和工业控制等领域的应用潜力。

【免费下载链接】egui egui: an easy-to-use immediate mode GUI in Rust that runs on both web and native 【免费下载链接】egui 项目地址: https://gitcode.com/GitHub_Trending/eg/egui

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐