简介:C#上位机(温室监控系统源码)面向学习C#桌面开发、51单片机与农业环境监控的读者,提供一套由PC端监控软件和51普中开发板下位机配合的完整项目参考。压缩包共1065个文件、约50.71MB,内容以dll、xml、txt、cs、png及开发板工程文件为主,既有可运行的界面与通信逻辑,也包含依赖库、配置说明和硬件端源码,便于理解上位机显示、串口通信、传感器采集与控制指令下发之间的关系。当前已有2317人学习或下载。读者可借此梳理C#窗体界面、数据采集传输、阈值控制、历史数据存储等模块的实现思路,并参考其目录组织、事件驱动与软硬件协作方式,对理解上下位机之间的数据流和职责划分也有帮助,适合课程设计、毕业设计及嵌入式入门实践时对照学习。
1. 从一台大棚控制柜说起:这套 C# 上位机源码到底能干什么
凌晨两点,值班室的电脑还在响报警,棚内温度从 18℃ 掉到 9℃,可现场触摸屏只显示“通讯异常”。这套 C# 上位机(温室监控系统源码)解决的正是这种场景:把下位机的温湿度、光照、二氧化碳、土壤墒情拉到一个桌面端,做实时曲线、阈值报警、历史查询和手动控制。它适合正在做 C# 软件毕设、工控上位机二次开发,或者要给大棚、育苗室搭一套本地监控的开发者。代码不是演示级玩具,串口采集、数据落库、界面刷新和报警联动都在一条链路上,拿下来能直接改。
2. 技术栈与目录结构:WinForms、串口、Modbus 和 SQLite 怎么拼起来
这套源码不是网页后台,也不是手机 App,而是装在温室值班室工控机上的 C# 桌面程序。现场常见的画面是:一台工控机、一根 USB 转 485 线、几台采集从站,屏幕上要同时看曲线、看报警、查历史。选型就围绕这个场景走,别拿互联网项目的分层硬套。
2.1 技术选型:为什么是 WinForms + 串口 + 本地库
WinForms 不是最潮的选择,但现场机器老、维护人少,拖控件就能改,出问题也容易定位。上位机最怕的是“看着花哨,现场一断线就抓瞎”,WinForms 的调试成本低,反而适合这类长期运行的采集软件。界面层主要负责表格、曲线、按钮和状态灯,不承担协议解析,也不直接碰数据库。
串口层用System.IO.Ports.SerialPort,协议按 Modbus RTU 走。温室现场常见的采集模块、PLC、温湿度变送器,很多都支持 0x03 读保持寄存器,帧格式简单:从站地址、功能码、起始地址、寄存器数量、CRC。把这一层单独写清楚,后面换设备只改地址表和缩放系数,不用动主界面。
数据落本地库,SQLite 免安装,拷贝目录就能跑。采集数据量不算大,一台设备一秒一条,几天也就几十万行,建好device_id + ts索引后,历史查询够用。配置用 JSON,改设备地址、波特率、报警阈值不用重新编译。这套 C# 软件交付时,我一般把运行时依赖一起带上,现场机器不一定有对应环境。采集端还留了插件口,后面换协议不用把 UI 推倒重来。
2.2 目录结构:UI、采集、协议、数据四层怎么分
拿到源码先看目录,不要急着点运行。下面这张表是我拆这类项目时最先确认的东西:
| 目录 | 职责 | 关键文件 |
|---|---|---|
Forms/ | 界面层,主窗口、历史查询、设置、报警窗口 | MainForm.cs、HistoryForm.cs、SettingsForm.cs |
Services/ | 业务层,采集调度、报警判断、历史查询 | AcquisitionService.cs、AlarmService.cs、HistoryService.cs |
Protocols/ | 协议层,Modbus 帧组装、CRC、半包解析 | ModbusRtuClient.cs、Crc16.cs、FrameParser.cs |
Models/ | 共享模型,设备配置、采样值、报警规则 | DeviceConfig.cs、SensorReading.cs、AlarmRule.cs |
Data/ | 数据层,建库、批量写入、历史读取 | DatabaseInitializer.cs、ReadingRepository.cs |
Config/ | 配置文件,设备表、报警规则 | devices.json、alarm-rules.json |
Plugins/ | 插件目录,放自定义采集器 DLL | *.dll |
依赖方向要单向:Forms -> Services -> Protocols/Data -> Models。别让MainForm直接new SerialPort,也别把 SQL 写在按钮事件里。现场加一台设备、换一个寄存器地址,如果要在三个窗口里来回改,后面一定翻车。界面只订阅采集服务抛出的事件,采集服务负责轮询和重连,协议层只认字节,数据层只认模型。
2.3 配置模型:一台设备怎么描述
设备配置不要写死在代码里。下面这份 JSON 是这类项目里比较常见的结构,字段名可以按你的项目改,但思路差不多:
{ "devices": [ { "id": "gh-01", "name": "1号温室", "port": "COM3", "baudRate": 9600, "dataBits": 8, "parity": "None", "stopBits": "One", "slaveAddress": 1, "pollIntervalMs": 1000, "registers": { "temperature": { "address": 0, "scale": 0.1, "unit": "℃" }, "humidity": { "address": 1, "scale": 0.1, "unit": "%RH" }, "light": { "address": 2, "scale": 1, "unit": "lux" }, "co2": { "address": 3, "scale": 1, "unit": "ppm" }, "soil": { "address": 4, "scale": 0.1, "unit": "%" } } } ] }scale是缩放系数,因为 Modbus 寄存器里通常是整数,温度 251 代表 25.1℃。pollIntervalMs是轮询间隔,温室监控不需要毫秒级,1 秒一次足够,太快反而会让串口和数据库吃不消。slaveAddress必须和从站拨码一致,现场对不上时先查这个,不要先怀疑代码。配置文件要设置成“如果较新则复制”,否则发布后程序读的还是旧配置。
3. 核心链路拆解:从传感器采集到界面刷新的一段代码
一条数据从串口进来,大致走这条链路:定时器触发轮询,协议层组装 0x03 请求,串口收到响应,帧解析器拆出完整帧,解码器按配置换算工程值,采样值进 UI 队列和落库队列,报警服务判断阈值,界面定时批量刷新。按这个顺序读代码,比从MainForm第一行往下翻要快得多。
3.1 采集入口:定时轮询和 Modbus 读保持寄存器
先看请求帧怎么拼。Modbus RTU 读保持寄存器的请求固定 8 字节,地址和寄存器数量都是高字节在前,CRC 低字节在前:
// 组装 Modbus RTU 读保持寄存器请求:从站地址 + 功能码 0x03 + 起始地址 + 寄存器数量 + CRC16 public static byte[] BuildReadHoldingRegisters(byte slave, ushort start, ushort count) { var frame = new byte[8]; frame[0] = slave; frame[1] = 0x03; frame[2] = (byte)(start >> 8); frame[3] = (byte)(start & 0xFF); frame[4] = (byte)(count >> 8); frame[5] = (byte)(count & 0xFF); var crc = Crc16.Compute(frame, 0, 6); frame[6] = (byte)(crc & 0xFF); // CRC 低字节先发 frame[7] = (byte)(crc >> 8); // CRC 高字节后发 return frame; }slave是从站地址,start是起始寄存器地址,count是一次读几个寄存器。比如温度从 0 开始、连续 5 个寄存器,就传0和5。现场如果返回异常码0x83,先查从站地址、寄存器表、波特率,不要一上来改解析代码。CRC 也要单独拎出来:
public static ushort Compute(byte[] data, int offset, int length) { ushort crc = 0xFFFF; for (int i = offset; i < offset + length; i++) { crc ^= data[i]; for (int bit = 0; bit < 8; bit++) { if ((crc & 0x0001) != 0) { crc >>= 1; crc ^= 0xA001; // Modbus 常用反转多项式 } else { crc >>= 1; } } } return crc; }初值是0xFFFF,多项式反转值是0xA001,算完不取反。请求和响应两侧都要用同一份实现,模拟从站里也要抄一份。联调时经常出现“发得出去、回不来”,一半是 CRC 高低位写反,另一半是波特率或校验位对不上。
3.2 接收与解析:DataReceived 里不要碰界面
串口事件跑在后台线程,不是 UI 线程。这里
本文还有配套的精品资源,点击获取