1. 项目概述:MFC C++农业数据采集系统
农业数据采集系统是现代农业信息化建设的基础设施,它通过传感器、人工录入等多种方式收集田间环境参数、作物生长状况、农机作业数据等关键信息。使用MFC(Microsoft Foundation Classes)框架结合C++开发这类系统,能够充分发挥Windows平台下GUI开发的高效性,同时保证数据处理性能。
我在2018年参与过某省农业物联网平台建设时,就采用MFC开发了数据采集终端软件。相比Web方案,MFC的突出优势在于:
- 对老旧硬件设备的兼容性更好(很多农业现场还在使用Windows XP工控机)
- 可以深度调用本地API实现高精度定时采集
- 离线环境下仍能稳定运行
- 界面响应速度更快
典型的农业采集系统通常包含以下模块:
- 传感器数据接口(串口/USB)
- 人工录入表单
- 数据校验规则
- 本地存储管理
- 数据上传模块
- 系统配置界面
2. 开发环境搭建
2.1 Visual Studio配置
推荐使用VS2019或更高版本,安装时务必勾选:
- "使用C++的桌面开发"工作负载
- 单个组件中的"MFC for latest v142 build tools"
- Windows 10 SDK(版本根据目标系统选择)
注意:如果遇到"error: microsoft visual c++ 14.0 or greater is required"错误,需要安装对应的VC++ redistributable运行库。农业现场电脑往往没有网络连接,建议将运行库打包进安装程序。
2.2 MFC工程创建步骤
- 新建项目 → Visual C++ → MFC应用程序
- 应用程序类型选择"基于对话框"
- 在高级功能中勾选:
- ActiveX控件(用于图表显示)
- 公共控件清单
- 生成的工程中包含:
- 主对话框类(CXXXDlg)
- 应用程序类(CXXXApp)
- stdafx.h预编译头文件
3. 核心功能实现
3.1 串口通信模块
农业设备常用RS485/RS232接口,在MFC中实现串口通信有两种主流方案:
cpp复制// 方案一:使用MSComm控件(快速实现)
void CAgriDataDlg::OnCommMscomm()
{
VARIANT variant_inp;
COleSafeArray safearray_inp;
CString strtemp;
if(m_Comm.get_CommEvent() == 2) // 接收事件
{
variant_inp = m_Comm.get_Input();
safearray_inp = variant_inp;
safearray_inp.GetElement(0, strtemp.GetBufferSetLength(1000));
strtemp.ReleaseBuffer();
// 解析传感器数据帧
ParseSensorData(strtemp);
}
}
// 方案二:使用WinAPI(更灵活)
HANDLE hCom = CreateFile("COM3", GENERIC_READ|GENERIC_WRITE,
0, NULL, OPEN_EXISTING, 0, NULL);
DCB dcb;
GetCommState(hCom, &dcb);
dcb.BaudRate = 9600;
dcb.ByteSize = 8;
dcb.Parity = NOPARITY;
dcb.StopBits = ONESTOPBIT;
SetCommState(hCom, &dcb);
实测对比:
- MSComm控件开发速度快,但灵活性差
- WinAPI方式需要处理更多细节,但可以精确控制超时、缓存等参数
- 农业现场建议增加485转USB芯片的自动识别功能
3.2 数据存储设计
考虑到农业现场可能断网,需要可靠的本地存储方案:
cpp复制// 使用SQLite本地数据库
CString strSQL;
strSQL.Format("INSERT INTO sensor_data (time, temp, humidity) VALUES ('%s', %.1f, %.1f)",
CTime::GetCurrentTime().Format("%Y-%m-%d %H:%M:%S"),
fTemperature, fHumidity);
try {
CDatabase db;
db.OpenEx("DSN=agri_data;UID=;PWD=");
db.ExecuteSQL(strSQL);
db.Close();
} catch(CDBException* e) {
CString strErr;
strErr.Format("数据库错误:%s", e->m_strError);
AfxMessageBox(strErr);
e->Delete();
// 失败时写入文本日志
CStdioFile file;
if(file.Open("backup.log", CFile::modeWrite|CFile::modeCreate|CFile::modeNoTruncate)) {
file.SeekToEnd();
file.WriteString(strSQL + "\n");
file.Close();
}
}
关键设计要点:
- 采用数据库+文本日志双保险
- 时间戳统一使用UTC时间避免时区问题
- 数值字段保留适当精度(农业气象数据通常需要1位小数)
- 为每个传感器建立单独的数据表
3.3 用户界面设计
农业系统的UI需要特别考虑:
- 大字体显示(田间使用可能光线强烈)
- 减少键盘输入(多用下拉选择)
- 重要参数高亮显示
使用MFC Grid Control显示数据表示例:
cpp复制// 初始化表格
m_Grid.SetRowCount(50);
m_Grid.SetColumnCount(5);
m_Grid.SetFixedRowCount(1);
m_Grid.SetFixedColumnCount(1);
// 设置列标题
m_Grid.SetItemText(0,1, "时间");
m_Grid.SetItemText(0,2, "温度(℃)");
m_Grid.SetItemText(0,3, "湿度(%)");
m_Grid.SetItemText(0,4, "光照(Lux)");
// 添加Combo选择框
CCellCombo* pCombo = new CCellCombo;
pCombo->SetStyle(CBS_DROPDOWNLIST);
pCombo->AddString("正常");
pCombo->AddString("异常");
m_Grid.SetCellType(1,4, RUNTIME_CLASS(CCellCombo));
4. 典型问题解决方案
4.1 串口数据丢失问题
农业现场常见问题及解决方法:
-
数据不完整
- 增加接收超时判断(建议300ms)
- 设置合适的缓存大小(通常2K足够)
- 添加帧头帧尾校验
-
多设备冲突
- 实现Modbus协议轮询机制
- 为每个设备设置独立地址
- 增加重试机制(建议最多3次)
-
电磁干扰
- 在代码中增加CRC校验
- 使用屏蔽双绞线
- 避免与变频器等大功率设备共用电源
4.2 数据上传断点续传
网络不稳定的农村地区需要特殊处理:
cpp复制class CUploadThread : public CWinThread
{
public:
CStringArray m_arrFiles;
protected:
virtual BOOL InitInstance()
{
while(!m_arrFiles.IsEmpty()) {
CString strFile = m_arrFiles[0];
if(UploadSingleFile(strFile)) {
m_arrFiles.RemoveAt(0);
WriteProgress(); // 记录上传进度
} else {
Sleep(5000); // 等待网络恢复
}
}
return FALSE;
}
BOOL UploadSingleFile(LPCTSTR lpszFile)
{
CInternetSession session("AgriUpload/1.0");
CHttpConnection* pConn = NULL;
CHttpFile* pFile = NULL;
try {
pConn = session.GetHttpConnection("www.agriserver.com");
pFile = pConn->OpenRequest(CHttpConnection::HTTP_VERB_POST,
"/api/upload");
// 设置多部分表单数据
CString strBoundary = "----WebKitFormBoundaryE19zNvXGzXaLvS5C";
CString strHeaders;
strHeaders.Format("Content-Type: multipart/form-data; boundary=%s\r\n",
strBoundary);
CFile dataFile(lpszFile, CFile::modeRead);
DWORD dwLen = (DWORD)dataFile.GetLength();
BYTE* pBuf = new BYTE[dwLen];
dataFile.Read(pBuf, dwLen);
// 构造POST数据
CString strPostData;
strPostData.Format("--%s\r\n"
"Content-Disposition: form-data; name=\"file\"; filename=\"%s\"\r\n"
"Content-Type: application/octet-stream\r\n\r\n",
strBoundary, CPath::FindFileName(lpszFile));
pFile->SendRequest(strHeaders, (LPVOID)(LPCTSTR)strPostData, strPostData.GetLength());
pFile->Write(pBuf, dwLen);
CString strEnd = "\r\n--" + strBoundary + "--\r\n";
pFile->Write((LPVOID)(LPCTSTR)strEnd, strEnd.GetLength());
delete[] pBuf;
dataFile.Close();
DWORD dwRet;
pFile->QueryInfoStatusCode(dwRet);
return dwRet == 200;
} catch(CInternetException* e) {
e->Delete();
return FALSE;
}
}
};
5. 部署与维护建议
5.1 安装包制作
使用InstallShield或VS自带的安装项目:
- 包含必要的运行库(VC++ redistributable)
- 自动安装USB转串口驱动
- 创建开始菜单快捷方式
- 添加开机自启动选项
5.2 田间设备配置清单
| 设备类型 | 推荐型号 | 接口方式 | 备注 |
|---|---|---|---|
| 气象站 | 精讯畅通JXBS-3001 | RS485 Modbus | 需防雷击 |
| 土壤传感器 | 托普云农TZS-3X | USB | 定期校准 |
| 摄像头 | 海康威视DS-2CD3 | RTSP | 防水外壳 |
| 工控机 | 研华ARK-1120 | - | 宽温型号 |
5.3 系统优化技巧
-
内存管理:
- 使用CMapPtrToPtr缓存常用数据
- 及时释放GDI对象
- 大文件采用内存映射方式读取
-
性能提升:
- 将频繁访问的数据库字段建立索引
- 使用工作线程处理耗时操作
- 减少界面刷新频率(农业系统不需要实时刷新)
-
稳定性增强:
- 添加看门狗机制
- 实现自动异常恢复
- 定期清理日志文件
这套系统在某水稻种植基地连续运行3年,累计采集数据超过200万条,最关键的改进是在数据校验环节增加了作物生长阶段的合理性判断。例如在抽穗期突然出现零下温度数据时,会自动触发传感器校准流程而不是简单记录异常值。
