WinForm页面

对应Combox按钮里面要书写对应数

搭建完页面代码后要搭建后端代码,编写完成之后即进行威盟士空气净化器

的数据读取。

后端代码书写

这里使用的是

Modbus协议,所以需要安装对应依赖包

默认窗口配置

 private void Form1_Load(object sender, EventArgs e)

 {

     comboBox1.DataSource = SerialPort.GetPortNames();

     comboBox2.Text = comboBox2.Items[1].ToString();

     comboBox3.Text = comboBox3.Items[0].ToString();

     comboBox4.Text = comboBox4.Items[1].ToString();

     comboBox5.Text = comboBox5.Items[0].ToString();



 }

编写打开端口按钮

private void button1_Click(object sender, EventArgs e)

{

    if (button1.Text == "打开端口")

    {

        serialPort1.PortName = comboBox1.Text;

        serialPort1.BaudRate = Convert.ToInt32(comboBox2.Text);

        serialPort1.DataBits = Convert.ToInt32(comboBox3.Text);

        serialPort1.StopBits = (StopBits)Enum.Parse(typeof(StopBits), comboBox4.Text);

        serialPort1.Parity = (Parity)Enum.Parse(typeof(Parity), comboBox5.Text);

        serialPort1.ReadTimeout = 5000; // 设置为5秒超时

        try

        {

            serialPort1.Open();

            button1.Text = "关闭端口";

            button1.BackColor = Color.Red;

            comboBox1.Enabled = false;

            comboBox2.Enabled = false;

            comboBox3.Enabled = false;

            comboBox4.Enabled = false;

            comboBox5.Enabled = false;

        }

        catch (Exception ex)

        {

            MessageBox.Show(ex.Message);

        }

    }

    else

                    {

        try

        {

            serialPort1.Close();

            button1.Text = "打开端口";

            button1.BackColor = Color.Lime;

            comboBox1.Enabled = true;

            comboBox2.Enabled = true;

            comboBox3.Enabled = true;

            comboBox4.Enabled = true;

            comboBox5.Enabled = true;

        }

        catch (Exception ex)

        {

            MessageBox.Show(ex.Message);

        }

    }

}

编写连接设备按钮

 private void button2_Click(object sender, EventArgs e)

 {

     // 创建 Modbus 工厂

     var factory = new ModbusFactory();



     // 创建 RTU 传输对象

     var transport = factory.CreateRtuTransport(serialPort1);



     var master = factory.CreateMaster(transport);

     // 读取保持寄存器(功能码 0x03)



     ushort startAddress = 0; // 起始地址

     ushort numRegisters = 13; // 读取寄存器数量

     ushort slaveId = 1; // 从站地址

     ushort[] registers = master.ReadHoldingRegisters(1, startAddress, numRegisters);



     //double pm25 = registers[0] / 10.0;

     //double pm10 = registers[1] / 10.0;

     double humidity = registers[2] / 10.0;

     double temperature = registers[3] / 10.0;

     //double co2 = registers[8];           

     double formaldehyde = registers[9] / 100.0;





     Invoke(new Action(() =>

     {

         // 更新 UI

         textBox1.Text = registers[0].ToString();

         textBox2.Text = registers[1].ToString();

         textBox3.Text = humidity.ToString();

         textBox4.Text = temperature.ToString();

         textBox5.Text = registers[8].ToString();

         textBox6.Text = formaldehyde.ToString();

     }));



     string filePath = "TextFile.txt";

     string data = string.Join(", ", registers);



     using (StreamWriter sw = new StreamWriter(filePath, append: true, Encoding.UTF8))

     {

         // 添加分隔线

         sw.WriteLine("======================================");

         sw.WriteLine($"记录时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss}");

         sw.WriteLine($"位置     :{textBox7.Text}");

         sw.WriteLine($"PM2.5    :{registers[0],6} μg/m³");

         sw.WriteLine($"PM10     :{registers[1],6} μg/m³");

         sw.WriteLine($"湿度     :{humidity,6} %RH");

         sw.WriteLine($"温度     :{temperature,6} ℃");

         sw.WriteLine($"二氧化碳 :{registers[9],6} ppm");

         sw.WriteLine($"甲醛     :{formaldehyde,6} ppm");

         sw.WriteLine("======================================");

         sw.WriteLine(); // 空行分隔不同记录

         Console.WriteLine("文本追加写入完成!");

     }

     string filePath2 = "TextFile.csv";

     string data2 = string.Join(", ", registers);



     // 首次写入时添加表头

     bool isFirstWrite = !File.Exists(filePath2);



     using (StreamWriter sw = new StreamWriter(filePath2, append: true, Encoding.UTF8))

     {

         if (isFirstWrite)

         {

             sw.WriteLine("时间,位置,PM2.5(μg/m³),PM10(μg/m³),湿度(%RH),温度(℃),二氧化碳(ppm),甲醛(ppm)");

         }



         // CSV格式一行完成记录

         sw.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss},{textBox7.Text},{registers[0]},{registers[1]},{humidity},{temperature},{registers[9]},{formaldehyde}");

         Console.WriteLine("文本追加写入完成!");

     }



     string filePath3 = "TextFile.json";

     string data3 = string.Join(", ", registers);



     using (StreamWriter sw = new StreamWriter(filePath3, append: true, Encoding.UTF8))

     {

         // 构建JSON对象

         var record = new

         {

             时间 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),

             位置 = textBox7.Text,

             PM25 = $"{registers[0]} μg/m³",

             PM10 = $"{registers[1]} μg/m³",

             湿度 = $"{humidity} %RH",

             温度 = $"{temperature} ℃",

             二氧化碳 = $"{registers[9]} ppm",

             甲醛 = $"{formaldehyde} ppm"

         };



         // 写入JSON格式

         sw.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(record));

         Console.WriteLine("文本追加写入完成!");

     }

 }

生成有关实时读取的事件函数:

private void timer1_Tick(object sender, EventArgs e)

private void checkBox1_CheckedChanged(object sender, EventArgs e)、private void button4_Click(object sender, EventArgs e)

进行代码编写:

private void timer1_Tick(object sender, EventArgs e)

{

    // 创建 Modbus 工厂

    var factory = new ModbusFactory();



    // 创建 RTU 传输对象

    var transport = factory.CreateRtuTransport(serialPort1);



    var master = factory.CreateMaster(transport);

    // 读取保持寄存器(功能码 0x03)



    ushort startAddress = 0; // 起始地址

    ushort numRegisters = 13; // 读取寄存器数量

    ushort slaveId = 1; // 从站地址

    ushort[] registers = master.ReadHoldingRegisters(1, startAddress, numRegisters);



    //double pm25 = registers[0] / 10.0;

    //double pm10 = registers[1] / 10.0;

    double humidity = registers[2] / 10.0;

    double temperature = registers[3] / 10.0;

    //double co2 = registers[8];           

    double formaldehyde = registers[9] / 100.0;





    Invoke(new Action(() =>

    {

        // 更新 UI

        textBox1.Text = registers[0].ToString();

        textBox2.Text = registers[1].ToString();

        textBox3.Text = humidity.ToString();

        textBox4.Text = temperature.ToString();

        textBox5.Text = registers[8].ToString();

        textBox6.Text = formaldehyde.ToString();

    }));



    string filePath = "TextFile.txt";

    string data = string.Join(", ", registers);



    using (StreamWriter sw = new StreamWriter(filePath, append: true, Encoding.UTF8))

    {

        // 添加分隔线

        sw.WriteLine("======================================");

        sw.WriteLine($"记录时间:{DateTime.Now:yyyy-MM-dd HH:mm:ss}");

        sw.WriteLine($"位置     :{textBox7.Text}");

        sw.WriteLine($"PM2.5    :{registers[0],6} μg/m³");

        sw.WriteLine($"PM10     :{registers[1],6} μg/m³");

        sw.WriteLine($"湿度     :{humidity,6} %RH");

        sw.WriteLine($"温度     :{temperature,6} ℃");

        sw.WriteLine($"二氧化碳 :{registers[9],6} ppm");

        sw.WriteLine($"甲醛     :{formaldehyde,6} ppm");

        sw.WriteLine("======================================");

        sw.WriteLine(); // 空行分隔不同记录

        Console.WriteLine("文本追加写入完成!");

    }

    string filePath2 = "TextFile2.csv";

    string data2 = string.Join(", ", registers);



    // 首次写入时添加表头

    bool isFirstWrite = !File.Exists(filePath2);



    using (StreamWriter sw = new StreamWriter(filePath, append: true, Encoding.UTF8))

    {

        if (isFirstWrite)

        {

            sw.WriteLine("时间,位置,PM2.5(μg/m³),PM10(μg/m³),湿度(%RH),温度(℃),二氧化碳(ppm),甲醛(ppm)");

        }



        // CSV格式一行完成记录

        sw.WriteLine($"{DateTime.Now:yyyy-MM-dd HH:mm:ss},{textBox7.Text},{registers[0]},{registers[1]},{humidity},{temperature},{registers[9]},{formaldehyde}");

        Console.WriteLine("文本追加写入完成!");

    }



    string filePath3 = "TextFile.json";

    string data3 = string.Join(", ", registers);



    using (StreamWriter sw = new StreamWriter(filePath3, append: true, Encoding.UTF8))

    {

        // 构建JSON对象

        var record = new

        {

            时间 = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),

            位置 = textBox7.Text,

            PM25 = $"{registers[0]} μg/m³",

            PM10 = $"{registers[1]} μg/m³",

            湿度 = $"{humidity} %RH",

            温度 = $"{temperature} ℃",

            二氧化碳 = $"{registers[9]} ppm",

            甲醛 = $"{formaldehyde} ppm"

        };



        // 写入JSON格式

        sw.WriteLine(Newtonsoft.Json.JsonConvert.SerializeObject(record));

        Console.WriteLine("文本追加写入完成!");

    }

}



private void checkBox1_CheckedChanged(object sender, EventArgs e)

{

    if (checkBox1.Checked)

    {

        if (!serialPort1.IsOpen)

        {

            MessageBox.Show("请先连接,再实时显示数据", "错误", MessageBoxButtons.OK, MessageBoxIcon.Error);

            checkBox1.Checked = false;

            return;

        }



        timer1.Interval = 1000;

        timer1.Start();



    }

    else

    {

        timer1.Stop();



    }

}



private void button4_Click(object sender, EventArgs e)

{

    timer1.Interval = 1000;

    timer1.Start();

}

编写完成之后即可在读取威盟士空气净化器对应设备数据

代码功能分析

环境监测设备串口通信程序综述

该代码是一个基于 C# WinForms 和 NModbus 库开发的串口通信应用,主要用于与环境监测设备进行数据交互,实现了环境参数的读取、显示和多格式记录功能。

核心功能模块

1. 串口配置与管理模块

  • 程序启动时自动检测并加载可用串口列表
  • 支持配置串口关键参数:波特率、数据位、停止位和校验位
  • 提供 "打开 / 关闭端口" 按钮,实现串口连接状态切换
  • 连接状态变化时,自动更新 UI 控件状态(启用 / 禁用参数配置项)
  • 连接状态通过按钮文本和颜色直观展示(打开时红色,关闭时绿色)

2. 数据读取与解析模块

  • 基于 NModbus 库实现 Modbus RTU 协议通信
  • 支持手动读取和定时自动读取两种模式
  • 读取保持寄存器(功能码 0x03)中的环境监测数据
  • 解析并转换寄存器数据为实际物理量:
    • 湿度和温度除以 10.0 获取实际值
    • 甲醛浓度除以 100.0 获取实际值
  • 线程安全地更新 UI 显示各环境参数(PM2.5、PM10、湿度、温度等)

3. 数据记录模块

  • 支持三种格式的数据记录:
    • 文本文件(TXT):格式化显示,包含分隔线和清晰标识
    • 逗号分隔值文件(CSV):适合表格软件导入和数据分析
    • JSON 格式文件:便于程序解析和处理的结构化数据
  • 记录内容包含时间戳、监测位置和各项环境参数
  • 首次写入 CSV 文件时自动添加表头信息

4. 实时监控模块

  • 通过复选框控制是否启用实时数据监控
  • 使用定时器实现固定间隔(1 秒)自动读取和记录数据
  • 启动实时监控前验证串口是否已连接,确保操作有效性

技术实现特点

Modbus 协议实现:使用 NModbus 库简化 Modbus RTU 通信的开发,无需手动处理协议细节和 CRC 校验

多格式数据持久化:同时支持 TXT、CSV 和 JSON 三种格式的数据记录,满足不同场景需求

线程安全处理:在数据更新 UI 时使用 Invoke 方法,确保跨线程操作安全​​​​​​​

用户体验优化:通过颜色变化直观展示连接状态,操作前验证状态避免错误​​​​​​​

错误处理:包含基本的异常捕获机制,出错时显示错误信息

潜在问题与改进点

代码复用性:数据读取和记录的代码在 button2_Click 和 timer1_Tick 中重复,可提取为独立方法​​​​​​​

资源管理:Modbus 相关对象(factory、transport、master)没有显式释放,可能导致资源泄漏​​​​​​​

错误处理:异常处理不够细致,可增加对 Modbus 通信错误的专门处理​​​​​​​

配置灵活性:数据记录路径和时间间隔当前是硬编码,可改为用户可配置​​​​​​​

日志完善性:可增加详细的通信日志,便于调试和问题排查

该程序整体实现了环境监测设备的数据采集与记录功能,适合作为环境监测系统的基础应用,通过进一步优化可提升其可靠性和扩展性。​​​​​​​​​​​​​​

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐