从硬件选型到代码实现,手把手教你搭建一套工业级大棚监控系统
去年帮老家一个亲戚做了个大棚监控系统,彻底解决了他每天凌晨3点起来看温度的问题。以前他种草莓,一到降温天就整夜睡不着,生怕冻坏了苗子。现在手机和电脑上随时能看大棚里的温湿度、光照、土壤水分,超过阈值自动报警,还能远程控制卷帘机和浇水设备。
很多人觉得农业物联网很高大上,其实原理一点都不复杂。今天我就把这套系统完整拆解出来,从硬件选型、通信协议到C#上位机代码,全部讲透。看完你也能自己动手做一套,成本不到500块钱。
一、系统整体架构:三层结构,简单可靠
农业大棚监控系统不需要太复杂的架构,越简单越稳定。我设计的这套系统采用经典的三层架构,已经在5个大棚稳定运行了一年多,没有出过任何问题。
- 感知层
- 传输层
- 应用层:负责数据的显示、存储、分析和报警,核心是C#上位机
二、硬件选型:性价比最高的组合
做农业项目,成本控制非常重要。我选的这些元器件都是经过市场验证的,便宜又好用,新手也能轻松买到。
总成本:102元,加上外壳和线材,总共不到150元。如果需要监控多个大棚,只需要增加感知层设备即可,上位机可以同时管理几十个节点。
三、蓝牙数据采集方案:适合单个大棚
蓝牙方案成本最低,部署最简单,适合距离不超过10米的单个大棚。上位机电脑放在大棚旁边的管理房里,通过蓝牙和主控板通信。
3.1 HC-05模块配置
首先需要把HC-05模块配置成从机模式,波特率设置为9600。
- 按住HC-05模块上的按键,然后上电,进入AT模式
- 打开串口调试助手,发送以下AT指令:
AT+ROLE=0 // 设置为从机模式AT+NAME=Greenhouse // 设置蓝牙名称AT+PSWD=1234 // 设置配对密码AT+UART=9600,0,0 // 设置波特率9600,无校验,1位停止位
3.2 Arduino端代码
Arduino负责采集传感器数据,然后通过蓝牙发送给上位机。我设计了一个简单的通信协议,数据格式为:温度,湿度,光照,土壤湿度,CO2\n
#include<DHT.h>#include<Wire.h>#include<BH1750.h>#define DHTPIN 2#define DHTTYPE DHT22DHT dht(DHTPIN, DHTTYPE);BH1750 lightMeter;voidsetup(){ Serial.begin(9600); dht.begin(); Wire.begin(); lightMeter.begin();}voidloop(){ // 读取温湿度 float temp = dht.readTemperature(); float humi = dht.readHumidity(); // 读取光照强度 float light = lightMeter.readLightLevel(); // 读取土壤湿度 int soil = analogRead(A0); // 转换为百分比(0-100) int soilPercent = map(soil, 1023, 0, 0, 100); // 读取CO2浓度(简化版) int co2 = analogRead(A1); // 发送数据 Serial.print(temp); Serial.print(","); Serial.print(humi); Serial.print(","); Serial.print(light); Serial.print(","); Serial.print(soilPercent); Serial.print(","); Serial.println(co2); delay(1000); // 每秒采集一次}
3.3 C#上位机蓝牙通信代码
C#中可以直接使用SerialPort类来操作蓝牙串口,因为蓝牙配对成功后,系统会自动创建一个虚拟串口。
using System.IO.Ports;public class BluetoothClient{ private SerialPort _serialPort; public event Action<SensorData> DataReceived; publicvoidConnect(string portName) { _serialPort = new SerialPort(portName, 9600); _serialPort.DataReceived += SerialPort_DataReceived; _serialPort.Open(); } privatevoidSerialPort_DataReceived(object sender, SerialDataReceivedEventArgs e) { string data = _serialPort.ReadLine(); string[] parts = data.Split(','); if (parts.Length == 5) { var sensorData = new SensorData { Temperature = float.Parse(parts[0]), Humidity = float.Parse(parts[1]), Light = float.Parse(parts[2]), SoilMoisture = int.Parse(parts[3]), CO2 = int.Parse(parts[4]), Timestamp = DateTime.Now }; DataReceived?.Invoke(sensorData); } } publicvoidDisconnect() { if (_serialPort != null && _serialPort.IsOpen) { _serialPort.Close(); _serialPort.Dispose(); } }}public class SensorData{ public float Temperature { get; set; } public float Humidity { get; set; } public float Light { get; set; } public int SoilMoisture { get; set; } public int CO2 { get; set; } public DateTime Timestamp { get; set; }}
四、WiFi数据采集方案:适合多个大棚
WiFi方案传输距离更远,支持远程访问,适合需要监控多个大棚或者不在本地的情况。上位机可以放在任何有网络的地方,甚至可以部署在云服务器上。
4.1 ESP8266模块配置
ESP8266是一款非常强大的WiFi模块,可以直接作为主控板使用,也可以作为Arduino的从模块。我这里直接用ESP8266作为主控板,这样可以省去Arduino,进一步降低成本。
#include<ESP8266WiFi.h>#include<DHT.h>#define DHTPIN 2#define DHTTYPE DHT22DHT dht(DHTPIN, DHTTYPE);const char* ssid = "你的WiFi名称";const char* password = "你的WiFi密码";const char* serverIP = "192.168.1.100"; // 上位机IP地址const int serverPort = 8888;WiFiClient client;voidsetup(){ Serial.begin(115200); dht.begin(); // 连接WiFi WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("WiFi connected"); // 连接上位机服务器 if (client.connect(serverIP, serverPort)) { Serial.println("Connected to server"); }}voidloop(){ if (!client.connected()) { // 重新连接 if (client.connect(serverIP, serverPort)) { Serial.println("Reconnected to server"); } else { delay(1000); return; } } // 读取传感器数据 float temp = dht.readTemperature(); float humi = dht.readHumidity(); int soil = analogRead(A0); int soilPercent = map(soil, 1023, 0, 0, 100); // 发送数据 String data = String(temp) + "," + String(humi) + "," + String(soilPercent) + "\n"; client.print(data); delay(1000);}
4.2 C#上位机TCP服务器代码
WiFi方案中,上位机需要作为TCP服务器,监听ESP8266的连接请求。
using System.Net;using System.Net.Sockets;using System.Text;public class TcpServer{ private TcpListener _listener; private List<TcpClient> _clients = new List<TcpClient>(); public event Action<SensorData> DataReceived; publicvoidStart(int port) { _listener = new TcpListener(IPAddress.Any, port); _listener.Start(); // 开始接受客户端连接 Task.Run(AcceptClientsAsync); } privateasync Task AcceptClientsAsync() { while (true) { var client = await _listener.AcceptTcpClientAsync(); _clients.Add(client); // 开始接收该客户端的数据 Task.Run(() => ReceiveDataAsync(client)); } } privateasync Task ReceiveDataAsync(TcpClient client) { using (client) using (var stream = client.GetStream()) using (var reader = new StreamReader(stream, Encoding.UTF8)) { string line; while ((line = await reader.ReadLineAsync()) != null) { string[] parts = line.Split(','); if (parts.Length == 3) { var sensorData = new SensorData { Temperature = float.Parse(parts[0]), Humidity = float.Parse(parts[1]), SoilMoisture = int.Parse(parts[2]), Timestamp = DateTime.Now }; DataReceived?.Invoke(sensorData); } } } _clients.Remove(client); } publicvoidStop() { _listener.Stop(); foreach (var client in _clients) { client.Close(); } _clients.Clear(); }}
五、C#上位机界面设计与功能实现
上位机是整个系统的核心,我用WinForm做了一个简洁实用的界面,包含实时数据显示、历史曲线、报警设置和设备控制四个主要功能模块。
5.1 实时数据显示
用Label和ProgressBar显示当前的温湿度、光照、土壤湿度等数据,超过阈值时自动变红。
5.2 历史曲线
用Chart控件绘制24小时的历史数据曲线,可以直观地看到环境参数的变化趋势。
// 初始化曲线privatevoidInitChart(){ chart1.Series.Clear(); var tempSeries = new Series("温度"); tempSeries.ChartType = SeriesChartType.Line; tempSeries.Color = Color.Red; chart1.Series.Add(tempSeries); var humiSeries = new Series("湿度"); humiSeries.ChartType = SeriesChartType.Line; humiSeries.Color = Color.Blue; chart1.Series.Add(humiSeries); chart1.ChartAreas[0].AxisX.LabelStyle.Format = "HH:mm"; chart1.ChartAreas[0].AxisX.IntervalType = DateTimeIntervalType.Hours; chart1.ChartAreas[0].AxisX.Interval = 1;}// 添加数据点privatevoidAddDataPoint(SensorData data){ chart1.Series["温度"].Points.AddXY(data.Timestamp, data.Temperature); chart1.Series["湿度"].Points.AddXY(data.Timestamp, data.Humidity); // 只保留最近24小时的数据 if (chart1.Series["温度"].Points.Count > 1440) { chart1.Series["温度"].Points.RemoveAt(0); chart1.Series["湿度"].Points.RemoveAt(0); }}
5.3 报警功能
当环境参数超过设定的阈值时,上位机自动弹出报警窗口,播放报警声音,并记录报警日志。
private void CheckAlarm(SensorData data){ if (data.Temperature > maxTemp.Value || data.Temperature < minTemp.Value) { MessageBox.Show($"温度异常!当前温度:{data.Temperature:F1}℃", "报警", MessageBoxButtons.OK, MessageBoxIcon.Warning); // 播放报警声音 System.Media.SystemSounds.Exclamation.Play(); // 记录报警日志 LogAlarm("温度异常", data.Temperature); } // 其他参数的报警检查类似}
5.4 设备控制
通过继电器模块,可以远程控制水泵、卷帘机、通风扇等设备。上位机发送控制指令给Arduino/ESP8266,然后由它来控制继电器的通断。
六、数据存储与报表生成
所有采集到的数据都会自动保存到SQLite数据库中,支持导出Excel报表,方便进行数据分析和生产记录。
using System.Data.SQLite;public class DataStorage{ private string _connectionString = "Data Source=greenhouse.db;Version=3;"; publicDataStorage() { // 创建数据库和表 using (var conn = new SQLiteConnection(_connectionString)) { conn.Open(); string sql = @"CREATE TABLE IF NOT EXISTS SensorData ( Id INTEGER PRIMARY KEY AUTOINCREMENT, Temperature REAL, Humidity REAL, Light REAL, SoilMoisture INTEGER, CO2 INTEGER, Timestamp DATETIME)"; using (var cmd = new SQLiteCommand(sql, conn)) { cmd.ExecuteNonQuery(); } } } publicvoidSaveData(SensorData data) { using (var conn = new SQLiteConnection(_connectionString)) { conn.Open(); string sql = @"INSERT INTO SensorData (Temperature, Humidity, Light, SoilMoisture, CO2, Timestamp) VALUES (@Temperature, @Humidity, @Light, @SoilMoisture, @CO2, @Timestamp)"; using (var cmd = new SQLiteCommand(sql, conn)) { cmd.Parameters.AddWithValue("@Temperature", data.Temperature); cmd.Parameters.AddWithValue("@Humidity", data.Humidity); cmd.Parameters.AddWithValue("@Light", data.Light); cmd.Parameters.AddWithValue("@SoilMoisture", data.SoilMoisture); cmd.Parameters.AddWithValue("@CO2", data.CO2); cmd.Parameters.AddWithValue("@Timestamp", data.Timestamp); cmd.ExecuteNonQuery(); } } } // 查询历史数据和导出Excel的方法类似}
七、实战经验与优化建议
这套系统我已经用了一年多,踩过不少坑,也做了很多优化,分享几个最重要的经验:
电源一定要稳定:农业现场电压波动大,一定要用质量好的电源,最好加个稳压模块。我之前因为电源问题,烧了两个ESP8266模块。
做好防水防潮:大棚里湿度大,所有的电子元件都要做防水处理。我用的是防水接线盒,传感器探头用热缩管包好。
数据丢包处理:无线通信不可避免会有丢包,上位机要做超时重传机制,不要因为一次丢包就认为设备离线。
低功耗设计:如果用电池供电,一定要优化代码,让模块在不采集数据的时候进入休眠模式。ESP8266的深度休眠模式电流只有几微安。
备份机制:数据库要定期备份,最好同时在本地和云端各存一份,防止数据丢失。
最后说几句
农业物联网其实没有大家想象的那么难,核心就是"采集-传输-处理"这三个环节。用C#做上位机最大的优势就是开发速度快,界面友好,而且可以很方便地和各种硬件通信。
这套系统不仅可以用在农业大棚,还可以用在养殖场、仓库、实验室等需要环境监控的场景。只要稍微修改一下传感器和控制逻辑,就能适应不同的需求。
如果你也想自己动手做一套,或者有什么问题,欢迎在评论区交流。大家一起学习进步。