基于C#和RS485串口通信的温湿度上位机实现方案

基于C#和RS485串口通信的温湿度上位机实现方案,整合了Modbus RTU协议通信、实时数据显示、数据存储及报警功能,代码可直接运行于.NET Framework 4.8环境:


一、系统架构设计

RS485串口
上位机
温湿度传感器
Modbus RTU协议
数据解析
实时显示
数据存储
异常报警


二、核心代码实现

1. 串口通信模块(SerialPortManager.cs)
csharp 复制代码
using System.IO.Ports;
using Modbus.Device;

public class SerialPortManager : IDisposable
{
    private SerialPort _port;
    private IModbusMaster _master;
    private const int BaudRate = 9600;
    private const Parity Parity = Parity.None;
    private const StopBits StopBits = StopBits.One;
    private const int DataBits = 8;

    public event Action<byte[]> DataReceived;
    
    public void Connect(string portName, byte slaveId = 1)
    {
        _port = new SerialPort(portName, BaudRate, Parity, DataBits, StopBits)
        {
            ReadTimeout = 1000,
            WriteTimeout = 1000
        };
        
        _master = ModbusSerialMaster.CreateRtu(_port);
        _port.Open();
        
        // 启动数据采集定时器
        var timer = new System.Timers.Timer(1000);
        timer.Elapsed += async (s, e) => await ReadDataAsync();
        timer.Start();
    }

    private async Task ReadDataAsync()
    {
        try
        {
            ushort[] data = await _master.ReadHoldingRegistersAsync(slaveId, 0, 4);
            var buffer = new byte[8];
            Array.Copy(BitConverter.GetBytes(data[0]), 0, buffer, 0, 2);
            Array.Copy(BitConverter.GetBytes(data[1]), 0, buffer, 2, 2);
            Array.Copy(BitConverter.GetBytes(data[2]), 0, buffer, 4, 2);
            Array.Copy(BitConverter.GetBytes(data[3]), 0, buffer, 6, 2);
            
            DataReceived?.Invoke(buffer);
        }
        catch(ModbusException ex)
        {
            HandleCommunicationError(ex);
        }
    }

    public void Disconnect() => _port?.Close();
    public void Dispose() => _port?.Dispose();
}
2. 数据处理模块(DataProcessor.cs)
csharp 复制代码
public class DataProcessor
{
    public (float Temperature, float Humidity)? ParseData(byte[] data)
    {
        if (data.Length < 8) return null;
        
        // 假设数据格式:温度高字节+温度低字节+湿度高字节+湿度低字节
        short tempRaw = (short)(data[0] << 8 | data[1]);
        short humiRaw = (short)(data[2] << 8 | data[3]);
        
        return (tempRaw / 10.0f, humiRaw / 10.0f);
    }
}
3. 实时数据显示(MainForm.cs)
csharp 复制代码
using System.Windows.Forms.DataVisualization.Charting;

public partial class MainForm : Form
{
    private SerialPortManager _serialManager;
    private DataProcessor _dataProcessor;
    private Chart _tempChart, _humiChart;
    private Queue<float> _tempBuffer = new Queue<float>(100);
    private Queue<float> _humiBuffer = new Queue<float>(100);

    public MainForm()
    {
        InitializeComponent();
        InitializeComponents();
        _serialManager = new SerialPortManager();
        _dataProcessor = new DataProcessor();
        
        _serialManager.DataReceived += OnDataReceived;
        btnConnect.Click += (s,e) => _serialManager.Connect("COM3");
    }

    private void InitializeComponents()
    {
        // 温度曲线初始化
        _tempChart = new Chart { Dock = DockStyle.Fill };
        var tempSeries = new Series("Temperature") { ChartType = SeriesChartType.Spline };
        _tempChart.Series.Add(tempSeries);
        
        // 湿度曲线初始化
        _humiChart = new Chart { Dock = DockStyle.Fill };
        var humiSeries = new Series("Humidity") { ChartType = SeriesChartType.Spline };
        _humiChart.Series.Add(humiSeries);
        
        // 控件布局
        var splitContainer = new SplitContainer();
        splitContainer.Panel1.Controls.Add(_tempChart);
        splitContainer.Panel2.Controls.Add(_humiChart);
        this.Controls.Add(splitContainer);
    }

    private void OnDataReceived(byte[] data)
    {
        if (InvokeRequired)
        {
            Invoke(new Action(() => OnDataReceived(data)));
            return;
        }
        
        var result = _dataProcessor.ParseData(data);
        if (result.HasValue)
        {
            UpdateUI(result.Value.Temperature, result.Value.Humidity);
            CheckAlarmConditions(result.Value);
        }
    }

    private void UpdateUI(float temp, float humi)
    {
        lblTemp.Text = $"{temp:F1} °C";
        lblHumi.Text = $"{humi:F1} %RH";
        
        if (_tempBuffer.Count >= 100) _tempBuffer.Dequeue();
        if (_humiBuffer.Count >= 100) _humiBuffer.Dequeue();
        
        _tempBuffer.Enqueue(temp);
        _humiBuffer.Enqueue(humi);
        
        // 更新曲线
        UpdateChart(_tempChart.Series[0], _tempBuffer);
        UpdateChart(_humiChart.Series[1], _humiBuffer);
    }

    private void UpdateChart(Series series, Queue<float> buffer)
    {
        series.Points.Clear();
        for (int i = 0; i < buffer.Count; i++)
        {
            series.Points.AddXY(i, buffer.ElementAt(i));
        }
    }
}

三、关键功能实现

1. 异常报警模块(AlarmManager.cs)
csharp 复制代码
public class AlarmManager
{
    private const float TempUpper = 30.0f;
    private const float TempLower = 10.0f;
    private const float HumiUpper = 70.0f;
    private const float HumiLower = 30.0f;

    public void CheckAlarm(float temp, float humi)
    {
        bool tempAlarm = temp > TempUpper || temp < TempLower;
        bool humiAlarm = humi > HumiUpper || humi < HumiLower;

        if (tempAlarm || humiAlarm)
        {
            PlayAlertSound();
            FlashAlarmLight();
            LogAlarm(temp, humi);
        }
    }

    private void PlayAlertSound()
    {
        using (var player = new SoundPlayer("alert.wav"))
        {
            player.Play();
        }
    }

    private void FlashAlarmLight()
    {
        panelAlarm.BackColor = panelAlarm.BackColor == Color.Red 
            ? Color.Transparent 
            : Color.Red;
    }

    private void LogAlarm(float temp, float humi)
    {
        string log = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss} - 温度异常: {temp:F1}°C, 湿度异常: {humi:F1}%RH";
        File.AppendAllText("alarm.log", log);
    }
}
2. 数据持久化(DataStorage.cs)
csharp 复制代码
using System.Data.SQLite;

public class DataStorage
{
    private const string DbPath = "data.sqlite";
    private SQLiteConnection _connection;

    public DataStorage()
    {
        _connection = new SQLiteConnection($"Data Source={DbPath};Version=3;");
        _connection.Open();
        CreateTable();
    }

    private void CreateTable()
    {
        string sql = @"CREATE TABLE IF NOT EXISTS measurements (
                        id INTEGER PRIMARY KEY AUTOINCREMENT,
                        timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
                        temperature REAL,
                        humidity REAL)";
        using (var cmd = new SQLiteCommand(sql, _connection))
        {
            cmd.ExecuteNonQuery();
        }
    }

    public void SaveData(float temp, float humi)
    {
        string sql = "INSERT INTO measurements (temperature, humidity) VALUES (@temp, @humi)";
        using (var cmd = new SQLiteCommand(sql, _connection))
        {
            cmd.Parameters.AddWithValue("@temp", temp);
            cmd.Parameters.AddWithValue("@humi", humi);
            cmd.ExecuteNonQuery();
        }
    }
}

四、界面设计(MainForm.Designer.cs)

csharp 复制代码
partial class MainForm
{
    private System.ComponentModel.IContainer components = null;
    private Label lblTemp;
    private Label lblHumi;
    private Panel panelAlarm;
    private AlarmManager _alarmManager;
    private DataStorage _dataStorage;

    protected override void Dispose(bool disposing)
    {
        if (disposing && (components != null))
        {
            components.Dispose();
        }
        base.Dispose(disposing);
    }

    private void InitializeComponent()
    {
        this.lblTemp = new System.Windows.Forms.Label();
        this.lblHumi = new System.Windows.Forms.Label();
        this.panelAlarm = new System.Windows.Forms.Panel();
        this.SuspendLayout();
        
        // 温度显示
        this.lblTemp.AutoSize = true;
        this.lblTemp.Font = new System.Drawing.Font("微软雅黑", 24F);
        this.lblTemp.Location = new System.Drawing.Point(20, 20);
        this.lblTemp.Name = "lblTemp";
        this.lblTemp.Size = new System.Drawing.Size(100, 37);
        this.lblTemp.TabIndex = 0;
        this.lblTemp.Text = "00.0 °C";
        
        // 湿度显示
        this.lblHumi.AutoSize = true;
        this.lblHumi.Font = new System.Drawing.Font("微软雅黑", 24F);
        this.lblHumi.Location = new System.Drawing.Point(20, 80);
        this.lblHumi.Name = "lblHumi";
        this.lblHumi.Size = new System.Drawing.Size(100, 37);
        this.lblHumi.TabIndex = 1;
        this.lblHumi.Text = "00.0 %RH";
        
        // 报警面板
        this.panelAlarm.BackColor = System.Drawing.Color.Transparent;
        this.panelAlarm.Location = new System.Drawing.Point(20, 140);
        this.panelAlarm.Name = "panelAlarm";
        this.panelAlarm.Size = new System.Drawing.Size(20, 20);
        this.panelAlarm.TabIndex = 2;
        
        // 布局设置
        this.Controls.Add(this.panelAlarm);
        this.Controls.Add(this.lblHumi);
        this.Controls.Add(this.lblTemp);
        this.Name = "MainForm";
        this.Size = new System.Drawing.Size(800, 600);
        this.ResumeLayout(false);
        this.PerformLayout();
    }
}

五、工业级优化方案

1. 通信可靠性增强
csharp 复制代码
// 自动重连机制
private async Task ReconnectAsync()
{
    int retryCount = 0;
    while (retryCount < 3)
    {
        try
        {
            _port.Close();
            _port.Open();
            return;
        }
        catch (IOException)
        {
            await Task.Delay(2000);
            retryCount++;
        }
    }
    throw new TimeoutException("无法连接设备");
}

// CRC校验增强
private ushort CalculateCrc(byte[] data)
{
    ushort crc = 0xFFFF;
    for (int i = 0; i < data.Length; i++)
    {
        crc ^= (ushort)data[i] << 8;
        for (int j = 0; j < 8; j++)
        {
            if ((crc & 0x8000) != 0)
                crc = (ushort)((crc << 1) ^ 0xA001);
            else
                crc <<= 1;
        }
    }
    return crc;
}
2. 性能优化
  • 双缓冲绘图:防止曲线闪烁

    csharp 复制代码
    this.SetStyle(ControlStyles.AllPaintingInWmPaint | 
                 ControlStyles.UserPaint | 
                 ControlStyles.DoubleBuffer, true);
  • 数据压缩存储:使用差分编码减少存储空间

    csharp 复制代码
    public void SaveData(float temp, float humi)
    {
        if (_lastTemp == 0) _lastTemp = temp;
        if (_lastHumi == 0) _lastHumi = humi;
    
        var deltaTemp = temp - _lastTemp;
        var deltaHumi = humi - _lastHumi;
    
        string sql = "INSERT INTO measurements (temperature, humidity) " +
                     $"VALUES ({_lastTemp + deltaTemp}, {_lastHumi + deltaHumi})";
        // ...
    }

参考代码 基于c#的温湿度上位机源码,利用485串口通信 www.youwenfan.com/contentcsr/112096.html

六、调试与测试

1. 模拟数据测试
matlab 复制代码
// 生成模拟数据
private void GenerateTestData()
{
    var rand = new Random();
    for(int i=0; i<1000; i++)
    {
        var temp = 20 + rand.NextDouble()*10;
        var humi = 40 + rand.NextDouble()*20;
        UpdateUI(temp, humi);
    }
}
2. 实际设备调试
matlab 复制代码
// 扫描可用串口
private void ScanPorts()
{
    string[] ports = SerialPort.GetPortNames();
    cboPortName.Items.Clear();
    cboPortName.Items.AddRange(ports);
    if (ports.Length > 0) cboPortName.SelectedIndex = 0;
}

七、部署方案

1. 安装包制作
matlab 复制代码
<!-- Inno Setup脚本 -->
[Files]
Source: "YourApp.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "data.sqlite"; DestDir: "{app}"; Flags: ignoreversion
Source: "alert.wav"; DestDir: "{app}"; Flags: ignoreversion

[Icons]
Name: "{group}\温湿度监控"; Filename: "{app}\YourApp.exe"
2. 系统服务化
matlab 复制代码
public class BackgroundService : ServiceBase
{
    protected override void OnStart(string[] args)
    {
        Task.Run(() => 
        {
            var manager = new SerialPortManager();
            manager.Connect("COM3");
        });
    }
}

八、扩展功能建议

  1. Modbus TCP支持 :通过ModbusIpMaster实现网络通信

  2. 多设备管理:添加设备地址配置界面

  3. 数据导出:支持Excel/CSV格式导出历史数据

  4. Web监控:集成SignalR实现浏览器实时查看

相关推荐
ZHOUPUYU4 小时前
PHP 8.3网关优化:我用JIT将QPS提升300%的真实踩坑录
开发语言·php
寻寻觅觅☆8 小时前
东华OJ-基础题-106-大整数相加(C++)
开发语言·c++·算法
l1t8 小时前
在wsl的python 3.14.3容器中使用databend包
开发语言·数据库·python·databend
赶路人儿9 小时前
Jsoniter(java版本)使用介绍
java·开发语言
ceclar1239 小时前
C++使用format
开发语言·c++·算法
码说AI10 小时前
python快速绘制走势图对比曲线
开发语言·python
Gofarlic_OMS10 小时前
科学计算领域MATLAB许可证管理工具对比推荐
运维·开发语言·算法·matlab·自动化
星空下的月光影子10 小时前
易语言开发从入门到精通:补充篇·网络爬虫与自动化采集分析系统深度实战·HTTP/HTTPS请求·HTML/JSON解析·反爬策略·电商价格监控·新闻资讯采集
开发语言
老约家的可汗10 小时前
初识C++
开发语言·c++
wait_luky10 小时前
python作业3
开发语言·python