基于无线Mesh网络的智慧农业大棚监测节点群
项目开发背景
随着现代农业向智能化、精细化方向快速发展,智慧农业大棚作为高效种植的重要载体,对实时环境监测与精准调控的需求日益凸显。传统大棚管理多依赖人工巡检,存在数据采集滞后、人力成本高且易出错等问题,难以满足作物生长对温湿度、光照、二氧化碳浓度等参数的严格把控。因此,开发一套自动化、低成本的监测系统,成为提升农业产量与资源利用效率的关键。
在农业大棚这类覆盖范围广、地形复杂的场景中,可靠的数据传输是技术难点之一。无线Mesh网络凭借其自组网、多跳中继和强覆盖能力,能够有效克服信号盲区,确保监测节点间的稳定通信。结合LoRa等低功耗远距离无线技术,可构建灵活、可扩展的节点网络,降低部署和维护成本,适应大棚内多变的环境条件。
针对野外长期运行的需求,能源供应成为另一大挑战。监测节点需具备超低功耗特性,以匹配太阳能等可再生能源的供电能力。通过设计定时休眠与唤醒机制,结合高效电源管理,可延长设备使用寿命,减少对电网的依赖,从而实现绿色、可持续的农业监测,特别适用于偏远或基础设施薄弱地区。
本项目旨在整合国产超低功耗单片机、多传感器阵列、LoRa Mesh网络及4G通信技术,构建一个集数据采集、无线传输和远程控制于一体的智慧农业解决方案。通过实时监测土壤湿度、空气温湿度等关键参数,并基于云端指令自动调控灌溉,该系统将助力农户实现精准农业管理,推动农业生产的智能化升级。
设计实现的功能
(1)实现传感节点独立采集土壤湿度、空气温湿度、光照强度及CO?浓度信息。
(2)实现节点间通过LoRa模块组建自组网(Mesh),支持数据中继传输至汇聚网关节点。
(3)实现网关节点通过4G网络将所有数据上传至云平台,并可从云端接收控制指令。
(4)实现节点超低功耗特性,在定时休眠与唤醒机制下,使用太阳能电池板供电可长期野外工作。
(5)实现网关依据预设阈值,远程控制指定节点处的电磁阀进行自动灌溉。
项目硬件模块组成
(1)传感节点主控:采用国产超低功耗单片机CH32V003作为核心,管理传感器与通信。
(2)传感模块组:包括SHT30温湿度传感器、YL-69土壤湿度传感器、BH1750光照传感器及MH-Z19B CO?传感器。
(3)无线通信模块:采用E22-400T30S LoRa模块实现节点间远距离、低功耗通信。
(4)网关主控与通信模块:采用STM32F407VET6单片机,搭配Air724UG 4G模块和E22 LoRa模块,作为网络枢纽。
(5)电源与执行模块:包括0.5W小型太阳能板、TP4056充电管理芯片、18650锂电池、以及DC-5V电磁阀。
设计意义
设计意义在于推动智慧农业的现代化进程,通过部署基于无线Mesh网络的监测节点群,实现对农业大棚环境参数的实时采集,包括土壤湿度、空气温湿度和光照强度等关键数据。这有助于农民精确掌握作物生长条件,优化灌溉和施肥策略,从而提升农作物产量和质量,减少资源浪费,促进农业生产的精细化管理。
该系统采用LoRa模块组建自组网,增强了通信的覆盖范围和可靠性,确保在复杂大棚环境中数据传输的稳定性,避免了传统有线网络的局限性和高维护成本。网关节点通过4G网络与云平台连接,实现了远程数据上传和指令接收,使农户能够随时随地监控大棚状态并进行控制,如自动灌溉操作,大大提高了农业管理的便捷性和响应效率,降低了人力依赖。
在能源设计上,传感节点具备超低功耗特性并结合太阳能供电方案,使得系统能在野外长期自主运行,减少了对传统能源的消耗,降低了运营成本,同时体现了绿色环保的理念,符合可持续农业发展的趋势。硬件模块如国产超低功耗单片机CH32V003和模块化传感器的应用,在保证性能的同时控制了整体成本,为物联网技术在农业中的普及提供了可行方案。
整体而言,该项目不仅提升了农业生产的智能化和自动化水平,还通过数据积累为农业科研和决策支持奠定了基础,推动了农业向高效、节能、环保的方向转型,对社会经济和新农村建设具有积极影响。
设计思路
基于无线Mesh网络的智慧农业大棚监测系统设计思路围绕传感节点群与网关节点的协同工作展开。系统通过分散部署的传感节点采集环境参数,并利用LoRa自组网将数据汇聚至网关,最终经4G网络上传至云平台,实现远程监控与自动控制。
传感节点以国产超低功耗单片机CH32V003为核心,负责管理传感器组的数据采集。节点集成SHT30温湿度传感器、YL-69土壤湿度传感器、BH1750光照传感器及MH-Z19B CO?传感器,定期启动这些模块以获取土壤湿度、空气温湿度、光照强度和CO?浓度信息。单片机对采集的数据进行初步处理,确保准确性和低功耗运行。
无线通信采用E22-400T30S LoRa模块组建Mesh网络,节点间通过自组网协议进行数据中继传输。这种设计允许数据在多跳路径中传递至汇聚网关节点,扩展网络覆盖范围并增强可靠性。通信协议优化为低功耗模式,支持节点在空闲时进入休眠状态,仅定时唤醒进行数据传输或接收指令。
网关节点以STM32F407VET6单片机为主控,配备E22 LoRa模块和Air724UG 4G模块。网关通过LoRa网络接收来自传感节点的环境数据,并利用4G模块将数据实时上传至云平台。同时,网关监听云端下发的控制指令,如灌溉命令,并通过LoRa网络转发至指定传感节点,实现远程控制功能。
电源系统针对传感节点的超低功耗需求设计,结合太阳能供电方案。节点采用定时休眠与唤醒机制,由CH32V003单片机管理功耗状态,在休眠时关闭传感器和通信模块以节能。供电部分包括0.5W小型太阳能板、TP4056充电管理芯片和18650锂电池,太阳能板为电池充电,确保节点在野外长期稳定工作。
控制执行部分集成在传感节点上,网关可依据预设阈值或云端指令,通过LoRa网络发送控制信号。节点接收到指令后,由单片机驱动DC-5V电磁阀进行自动灌溉操作。这种设计实现了基于环境数据的精准控制,同时保持系统的简单性和实用性。
框架图
基于无线Mesh网络的智慧农业大棚监测节点群系统框架图:
[云平台]
↑
4G网络
↑
[网关节点]
┌───────────────┐
│STM32F407VET6 │
│E22 LoRa模块 │←→ LoRa Mesh网络
│Air724UG 4G模块│
│电源管理 │
└───────────────┘
↑
LoRa Mesh网络
┌─────────────┼─────────────┐
↓ ↓ ↓
[传感节点1] [传感节点2] ... [传感节点N]
┌─────────┐ ┌─────────┐ ┌─────────┐
│CH32V003 │ │CH32V003 │ │CH32V003 │
│传感器组 │ │传感器组 │ │传感器组 │
│SHT30 │ │SHT30 │ │SHT30 │
│YL-69 │ │YL-69 │ │YL-69 │
│BH1750 │ │BH1750 │ │BH1750 │
│MH-Z19B │ │MH-Z19B │ │MH-Z19B │
│E22 LoRa │ │E22 LoRa │ │E22 LoRa │
│电源系统 │ │电源系统 │ │电源系统 │
│太阳能板 │ │太阳能板 │ │太阳能板 │
│TP4056 │ │TP4056 │ │TP4056 │
│18650电池│ │18650电池│ │18650电池│
│DC-5V │ │DC-5V │ │DC-5V │
│电磁阀 │ │电磁阀 │ │电磁阀 │
└─────────┘ └─────────┘ └─────────┘
系统总体设计
该系统总体设计基于无线Mesh网络架构,旨在实现智慧农业大棚的全面环境监测与自动化控制。系统由多个分布式传感节点和一个中心网关节点组成,协同工作以采集、传输和处理农业大棚内的关键环境数据,并通过云平台进行远程管理与控制。
每个传感节点以国产超低功耗单片机CH32V003为核心主控,负责管理和协调各个传感器模块的数据采集。传感模块组包括SHT30温湿度传感器用于监测空气温度与湿度,YL-69土壤湿度传感器检测土壤水分含量,BH1750光照传感器测量光照强度,以及MH-Z19B CO?传感器监测二氧化碳浓度。节点设计注重低功耗,通过定时休眠与唤醒机制优化能耗,确保在太阳能供电下长期稳定运行。
节点间采用E22-400T30S LoRa模块组建自组织Mesh网络,实现远距离、低功耗的无线通信。该网络允许传感节点之间进行数据中继传输,形成多跳通信链路,增强信号覆盖范围和可靠性。所有采集到的环境数据通过Mesh网络逐跳转发,最终汇聚至网关节点,确保在复杂大棚环境中数据的高效传输。
网关节点作为网络枢纽,采用STM32F407VET6单片机作为主控,并集成E22 LoRa模块与Mesh网络中的传感节点通信。同时,网关通过Air724UG 4G模块连接到互联网,将汇聚的所有传感数据上传至云平台。网关还具备从云端接收控制指令的能力,可依据预设的环境阈值(如土壤湿度低时)远程控制指定节点处的DC-5V电磁阀,实现自动灌溉功能。
电源与执行模块支撑整个系统的长期野外工作。每个传感节点配备0.5W小型太阳能板、TP4056充电管理芯片和18650锂电池,形成可持续的能源管理系统,确保节点在低功耗模式下不间断运行。网关节点同样依赖于稳定电源,并结合执行模块中的电磁阀,在接收到控制信号时启动灌溉操作。整个系统通过硬件模块的紧密集成,实现了从数据采集到智能控制的闭环流程,提升农业大棚的管理效率和自动化水平。
系统功能总结
| 系统功能 | 实现方式/硬件组成 |
|---|---|
| 多参数数据采集 | 传感节点主控CH32V003单片机管理传感模块组:SHT30温湿度传感器、YL-69土壤湿度传感器、BH1750光照传感器、MH-Z19B CO?传感器 |
| 无线Mesh网络通信与数据中继 | LoRa模块(E22-400T30S)实现节点间远距离、低功耗自组网,支持数据中继传输至网关 |
| 网关数据上传与云端交互 | 网关主控STM32F407VET6单片机,搭配Air724UG 4G模块和E22 LoRa模块,通过4G网络上传数据至云平台并接收控制指令 |
| 超低功耗运行与可持续野外供电 | 定时休眠与唤醒机制,电源模块:0.5W小型太阳能板、TP4056充电管理芯片、18650锂电池,确保长期工作 |
| 远程智能灌溉控制 | 网关依据预设阈值,通过控制DC-5V电磁阀实现自动灌溉 |
设计的各个功能模块描述
传感节点主控与传感模块采用国产超低功耗单片机CH32V003作为核心控制器,负责管理多个环境传感器以实现独立数据采集。该模块集成了SHT30温湿度传感器用于监测空气温湿度,YL-69土壤湿度传感器用于检测土壤水分,BH1750光照传感器用于测量光照强度,以及MH-Z19B CO?传感器用于检测二氧化碳浓度,确保全面获取大棚内的关键农业参数。
无线通信模块基于E22-400T30S LoRa模块构建,支持远距离、低功耗的无线传输,使各传感节点能够组建自组网(Mesh网络)。该模块实现节点间的数据中继功能,将采集的信息高效传递至汇聚网关节点,确保网络覆盖范围和可靠性,适用于野外农业环境的分散部署。
网关主控与通信模块以STM32F407VET6单片机为核心,搭配E22 LoRa模块和Air724UG 4G模块,作为整个监测节点群的网络枢纽。该模块通过LoRa网络接收来自传感节点的数据,并利用4G网络将数据上传至云平台,同时可从云端接收控制指令,实现数据的远程监控与交互。
电源与执行模块包括0.5W小型太阳能板、TP4056充电管理芯片和18650锂电池,为传感节点和网关提供超低功耗供电。该模块支持定时休眠与唤醒机制,结合太阳能充电,确保系统在长期野外工作中保持稳定运行,满足节能需求。此外,DC-5V电磁阀作为执行部件,由网关根据预设阈值远程控制,实现自动灌溉功能。
上位机代码设计
#include <iostream>
#include <vector>
#include <string>
#include <map>
#include <thread>
#include <mutex>
#include <chrono>
#include <ctime>
#include <iomanip>
#include <sstream>
#include <cstring>
#include <winsock2.h>
#include <windows.h>
#pragma comment(lib, "ws2_32.lib")
// 传感器数据结构体
struct SensorData {
std::string node_id; // 节点ID
std::string timestamp; // 时间戳
float soil_moisture; // 土壤湿度(%)
float air_temperature; // 空气温度(℃)
float air_humidity; // 空气湿度(%)
float light_intensity; // 光照强度(lux)
float co2_concentration; // CO?浓度(ppm)
int battery_level; // 电池电量(%)
};
// 节点状态结构体
struct NodeStatus {
std::string node_id;
bool online;
std::string last_seen;
float signal_strength;
};
// 控制命令结构体
struct ControlCommand {
std::string node_id;
std::string command_type; // "VALVE_CONTROL", "NODE_CONFIG"
bool valve_state; // true:打开, false:关闭
int sleep_interval; // 休眠间隔(秒)
};
class AgriculturalMonitor {
private:
SOCKET server_socket;
SOCKET cloud_socket;
std::vector<SensorData> sensor_data_list;
std::map<std::string, NodeStatus> node_status_map;
std::vector<ControlCommand> command_queue;
std::mutex data_mutex;
bool running;
// 数据存储文件
const std::string DATA_FILE = "sensor_data.csv";
const std::string LOG_FILE = "system_log.txt";
// 云平台配置
std::string cloud_ip = "192.168.1.100";
int cloud_port = 8080;
// 阈值配置
struct Thresholds {
float soil_moisture_min = 30.0;
float soil_moisture_max = 80.0;
float air_temp_min = 15.0;
float air_temp_max = 35.0;
float co2_max = 1000.0;
} thresholds;
public:
AgriculturalMonitor() : running(false), server_socket(INVALID_SOCKET), cloud_socket(INVALID_SOCKET) {
initNetwork();
loadHistoricalData();
}
~AgriculturalMonitor() {
stop();
cleanupNetwork();
}
// 初始化网络
bool initNetwork() {
WSADATA wsaData;
if (WSAStartup(MAKEWORD(2, 2), &wsaData) != 0) {
logMessage("WSAStartup failed");
return false;
}
// 创建服务器socket监听网关数据
server_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (server_socket == INVALID_SOCKET) {
logMessage("Failed to create server socket");
return false;
}
sockaddr_in server_addr;
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = INADDR_ANY;
server_addr.sin_port = htons(8888);
if (bind(server_socket, (sockaddr*)&server_addr, sizeof(server_addr)) == SOCKET_ERROR) {
logMessage("Bind failed");
closesocket(server_socket);
return false;
}
if (listen(server_socket, 5) == SOCKET_ERROR) {
logMessage("Listen failed");
closesocket(server_socket);
return false;
}
logMessage("Network initialized successfully");
return true;
}
// 连接到云平台
bool connectToCloud() {
cloud_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if (cloud_socket == INVALID_SOCKET) {
logMessage("Failed to create cloud socket");
return false;
}
sockaddr_in cloud_addr;
cloud_addr.sin_family = AF_INET;
cloud_addr.sin_addr.s_addr = inet_addr(cloud_ip.c_str());
cloud_addr.sin_port = htons(cloud_port);
if (connect(cloud_socket, (sockaddr*)&cloud_addr, sizeof(cloud_addr)) == SOCKET_ERROR) {
logMessage("Failed to connect to cloud platform");
closesocket(cloud_socket);
cloud_socket = INVALID_SOCKET;
return false;
}
logMessage("Connected to cloud platform");
return true;
}
// 解析传感器数据
SensorData parseSensorData(const std::string& raw_data) {
SensorData data;
std::stringstream ss(raw_data);
std::string token;
std::vector<std::string> tokens;
while (std::getline(ss, token, ',')) {
tokens.push_back(token);
}
if (tokens.size() >= 8) {
data.node_id = tokens[0];
data.timestamp = getCurrentTime();
data.soil_moisture = std::stof(tokens[1]);
data.air_temperature = std::stof(tokens[2]);
data.air_humidity = std::stof(tokens[3]);
data.light_intensity = std::stof(tokens[4]);
data.co2_concentration = std::stof(tokens[5]);
data.battery_level = std::stoi(tokens[6]);
}
return data;
}
// 处理接收到的数据
void processSensorData(const SensorData& data) {
std::lock_guard<std::mutex> lock(data_mutex);
// 添加到数据列表
sensor_data_list.push_back(data);
// 更新节点状态
NodeStatus status;
status.node_id = data.node_id;
status.online = true;
status.last_seen = data.timestamp;
status.signal_strength = 100.0; // 模拟信号强度
node_status_map[data.node_id] = status;
// 检查阈值并触发警报
checkThresholds(data);
// 保存到文件
saveDataToFile(data);
// 上传到云平台
uploadToCloud(data);
// 自动控制逻辑
if (data.soil_moisture < thresholds.soil_moisture_min) {
ControlCommand cmd;
cmd.node_id = data.node_id;
cmd.command_type = "VALVE_CONTROL";
cmd.valve_state = true;
cmd.sleep_interval = 300; // 5分钟
sendControlCommand(cmd);
} else if (data.soil_moisture > thresholds.soil_moisture_max) {
ControlCommand cmd;
cmd.node_id = data.node_id;
cmd.command_type = "VALVE_CONTROL";
cmd.valve_state = false;
sendControlCommand(cmd);
}
// 显示数据
displaySensorData(data);
}
// 检查阈值
void checkThresholds(const SensorData& data) {
std::stringstream alert_msg;
bool has_alert = false;
if (data.soil_moisture < thresholds.soil_moisture_min) {
alert_msg << "节点" << data.node_id << ": 土壤湿度过低(" << data.soil_moisture << "%)";
has_alert = true;
} else if (data.soil_moisture > thresholds.soil_moisture_max) {
alert_msg << "节点" << data.node_id << ": 土壤湿度过高(" << data.soil_moisture << "%)";
has_alert = true;
}
if (data.air_temperature < thresholds.air_temp_min) {
if (has_alert) alert_msg << " | ";
alert_msg << "节点" << data.node_id << ": 空气温度过低(" << data.air_temperature << "℃)";
has_alert = true;
} else if (data.air_temperature > thresholds.air_temp_max) {
if (has_alert) alert_msg << " | ";
alert_msg << "节点" << data.node_id << ": 空气温度过高(" << data.air_temperature << "℃)";
has_alert = true;
}
if (data.co2_concentration > thresholds.co2_max) {
if (has_alert) alert_msg << " | ";
alert_msg << "节点" << data.node_id << ": CO?浓度过高(" << data.co2_concentration << "ppm)";
has_alert = true;
}
if (has_alert) {
logMessage("警报: " + alert_msg.str());
}
}
// 发送控制命令
void sendControlCommand(const ControlCommand& cmd) {
std::lock_guard<std::mutex> lock(data_mutex);
command_queue.push_back(cmd);
std::string cmd_str = formatControlCommand(cmd);
// 发送到网关
sendToGateway(cmd_str);
// 发送到云平台
sendToCloud(cmd_str);
logMessage("发送控制命令: 节点" + cmd.node_id +
" 命令类型:" + cmd.command_type +
" 阀门状态:" + (cmd.valve_state ? "开启" : "关闭"));
}
// 格式化控制命令
std::string formatControlCommand(const ControlCommand& cmd) {
std::stringstream ss;
ss << cmd.node_id << ","
<< cmd.command_type << ","
<< (cmd.valve_state ? "1" : "0") << ","
<< cmd.sleep_interval;
return ss.str();
}
// 发送数据到网关
void sendToGateway(const std::string& data) {
// 这里模拟发送,实际应用中需要通过socket发送到网关
std::cout << "[发送到网关] " << data << std::endl;
}
// 发送数据到云平台
void sendToCloud(const std::string& data) {
if (cloud_socket != INVALID_SOCKET) {
send(cloud_socket, data.c_str(), data.length(), 0);
}
}
// 上传数据到云平台
void uploadToCloud(const SensorData& data) {
std::string json_data = formatJSONData(data);
sendToCloud(json_data);
}
// 格式化JSON数据
std::string formatJSONData(const SensorData& data) {
std::stringstream ss;
ss << "{"
<< "\"node_id\":\"" << data.node_id << "\","
<< "\"timestamp\":\"" << data.timestamp << "\","
<< "\"soil_moisture\":" << data.soil_moisture << ","
<< "\"air_temperature\":" << data.air_temperature << ","
<< "\"air_humidity\":" << data.air_humidity << ","
<< "\"light_intensity\":" << data.light_intensity << ","
<< "\"co2_concentration\":" << data.co2_concentration << ","
<< "\"battery_level\":" << data.battery_level
<< "}";
return ss.str();
}
// 保存数据到文件
void saveDataToFile(const SensorData& data) {
std::ofstream file(DATA_FILE, std::ios::app);
if (file.is_open()) {
file << data.node_id << ","
<< data.timestamp << ","
<< data.soil_moisture << ","
<< data.air_temperature << ","
<< data.air_humidity << ","
<< data.light_intensity << ","
<< data.co2_concentration << ","
<< data.battery_level << "\n";
file.close();
}
}
// 加载历史数据
void loadHistoricalData() {
std::ifstream file(DATA_FILE);
if (file.is_open()) {
std::string line;
while (std::getline(file, line)) {
// 可以添加历史数据加载逻辑
}
file.close();
}
}
// 显示传感器数据
void displaySensorData(const SensorData& data) {
std::cout << "\n=== 传感器数据 ===" << std::endl;
std::cout << "节点ID: " << data.node_id << std::endl;
std::cout << "时间: " << data.timestamp << std::endl;
std::cout << "土壤湿度: " << data.soil_moisture << " %" << std::endl;
std::cout << "空气温度: " << data.air_temperature << " ℃" << std::endl;
std::cout << "空气湿度: " << data.air_humidity << " %" << std::endl;
std::cout << "光照强度: " << data.light_intensity << " lux" << std::endl;
std::cout << "CO?浓度: " << data.co2_concentration << " ppm" << std::endl;
std::cout << "电池电量: " << data.battery_level << " %" << std::endl;
std::cout << "==================\n" << std::endl;
}
// 显示节点状态
void displayNodeStatus() {
std::lock_guard<std::mutex> lock(data_mutex);
std::cout << "\n=== 节点状态 ===" << std::endl;
for (const auto& pair : node_status_map) {
const NodeStatus& status = pair.second;
std::cout << "节点ID: " << status.node_id << std::endl;
std::cout << "在线状态: " << (status.online ? "在线" : "离线") << std::endl;
std::cout << "最后通信: " << status.last_seen << std::endl;
std::cout << "信号强度: " << status.signal_strength << " %" << std::endl;
std::cout << "-------------------" << std::endl;
}
std::cout << "==================\n" << std::endl;
}
// 显示统计数据
void displayStatistics() {
std::lock_guard<std::mutex> lock(data_mutex);
if (sensor_data_list.empty()) {
std::cout << "暂无数据" << std::endl;
return;
}
const SensorData& latest = sensor_data_list.back();
std::cout << "\n=== 统计信息 ===" << std::endl;
std::cout << "节点总数: " << node_status_map.size() << std::endl;
std::cout << "数据总数: " << sensor_data_list.size() << std::endl;
std::cout << "在线节点: ";
int online_count = 0;
for (const auto& pair : node_status_map) {
if (pair.second.online) online_count++;
}
std::cout << online_count << std::endl;
std::cout << "最新数据时间: " << latest.timestamp << std::endl;
std::cout << "==================\n" << std::endl;
}
// 手动控制阀门
void manualValveControl() {
std::string node_id;
int choice;
std::cout << "输入节点ID: ";
std::cin >> node_id;
std::cout << "选择操作: 1.打开阀门 2.关闭阀门: ";
std::cin >> choice;
ControlCommand cmd;
cmd.node_id = node_id;
cmd.command_type = "VALVE_CONTROL";
cmd.valve_state = (choice == 1);
cmd.sleep_interval = 300;
sendControlCommand(cmd);
}
// 修改阈值
void modifyThresholds() {
std::cout << "\n当前阈值设置:" << std::endl;
std::cout << "土壤湿度最小阈值: " << thresholds.soil_moisture_min << "%" << std::endl;
std::cout << "土壤湿度最大阈值: " << thresholds.soil_moisture_max << "%" << std::endl;
std::cout << "空气温度最小阈值: " << thresholds.air_temp_min << "℃" << std::endl;
std::cout << "空气温度最大阈值: " << thresholds.air_temp_max << "℃" << std::endl;
std::cout << "CO?最大阈值: " << thresholds.co2_max << "ppm" << std::endl;
std::cout << "\n输入新的阈值(输入0保持原值):" << std::endl;
std::cout << "土壤湿度最小阈值(%): ";
std::cin >> thresholds.soil_moisture_min;
std::cout << "土壤湿度最大阈值(%): ";
std::cin >> thresholds.soil_moisture_max;
std::cout << "空气温度最小阈值(℃): ";
std::cin >> thresholds.air_temp_min;
std::cout << "空气温度最大阈值(℃): ";
std::cin >> thresholds.air_temp_max;
std::cout << "CO?最大阈值(ppm): ";
std::cin >> thresholds.co2_max;
logMessage("阈值已更新");
}
// 网络监听线程
void networkListener() {
while (running) {
SOCKET client_socket = accept(server_socket, NULL, NULL);
if (client_socket != INVALID_SOCKET) {
char buffer[1024];
int bytes_received = recv(client_socket, buffer, sizeof(buffer), 0);
if (bytes_received > 0) {
buffer[bytes_received] = '\0';
SensorData data = parseSensorData(std::string(buffer));
processSensorData(data);
}
closesocket(client_socket);
}
std::this_thread::sleep_for(std::chrono::milliseconds(100));
}
}
// 获取当前时间
std::string getCurrentTime() {
auto now = std::chrono::system_clock::now();
auto time = std::chrono::system_clock::to_time_t(now);
std::stringstream ss;
ss << std::put_time(std::localtime(&time), "%Y-%m-%d %H:%M:%S");
return ss.str();
}
// 记录日志
void logMessage(const std::string& message) {
std::string timestamp = getCurrentTime();
std::string log_entry = "[" + timestamp + "] " + message;
std::cout << log_entry << std::endl;
std::ofstream log_file(LOG_FILE, std::ios::app);
if (log_file.is_open()) {
log_file << log_entry << "\n";
log_file.close();
}
}
// 启动系统
void start() {
running = true;
logMessage("智慧农业大棚监测系统启动");
// 连接云平台
connectToCloud();
// 启动网络监听线程
std::thread listen_thread(&AgriculturalMonitor::networkListener, this);
listen_thread.detach();
// 主控制循环
while (running) {
displayMenu();
int choice;
std::cin >> choice;
handleMenuChoice(choice);
}
}
// 停止系统
void stop() {
running = false;
logMessage("系统正在关闭...");
std::this_thread::sleep_for(std::chrono::seconds(1));
}
// 清理网络资源
void cleanupNetwork() {
if (server_socket != INVALID_SOCKET) {
closesocket(server_socket);
}
if (cloud_socket != INVALID_SOCKET) {
closesocket(cloud_socket);
}
WSACleanup();
}
// 显示菜单
void displayMenu() {
std::cout << "\n===== 智慧农业大棚监测系统 =====" << std::endl;
std::cout << "1. 显示最新传感器数据" << std::endl;
std::cout << "2. 显示节点状态" << std::endl;
std::cout << "3. 显示统计信息" << std::endl;
std::cout << "4. 手动控制阀门" << std::endl;
std::cout << "5. 修改阈值设置" << std::endl;
std::cout << "6. 发送测试数据" << std::endl;
std::cout << "7. 查看日志" << std::endl;
std::cout << "8. 退出系统" << std::endl;
std::cout << "请输入选项: ";
}
// 处理菜单选择
void handleMenuChoice(int choice) {
switch (choice) {
case 1:
if (!sensor_data_list.empty()) {
displaySensorData(sensor_data_list.back());
} else {
std::cout << "暂无数据" << std::endl;
}
break;
case 2:
displayNodeStatus();
break;
case 3:
displayStatistics();
break;
case 4:
manualValveControl();
break;
case 5:
modifyThresholds();
break;
case 6:
sendTestData();
break;
case 7:
showLog();
break;
case 8:
stop();
break;
default:
std::cout << "无效选项" << std::endl;
}
}
// 发送测试数据
void sendTestData() {
SensorData test_data;
test_data.node_id = "NODE_001";
test_data.timestamp = getCurrentTime();
test_data.soil_moisture = 45.6;
test_data.air_temperature = 25.3;
test_data.air_humidity = 65.2;
test_data.light_intensity = 850.5;
test_data.co2_concentration = 450.0;
test_data.battery_level = 85;
processSensorData(test_data);
logMessage("测试数据已发送");
}
// 显示日志
void showLog() {
std::ifstream log_file(LOG_FILE);
if (log_file.is_open()) {
std::string line;
std::cout << "\n=== 系统日志 ===" << std::endl;
while (std::getline(log_file, line)) {
std::cout << line << std::endl;
}
std::cout << "================\n" << std::endl;
log_file.close();
} else {
std::cout << "日志文件不存在" << std::endl;
}
}
};
int main() {
// 设置控制台编码为UTF-8
SetConsoleOutputCP(65001);
AgriculturalMonitor monitor;
try {
monitor.start();
} catch (const std::exception& e) {
std::cerr << "系统错误: " << e.what() << std::endl;
return 1;
}
return 0;
}
模块代码设计
以下为STM32F407VET6网关节点寄存器方式开发的完整代码,包含所有传感器驱动和系统功能实现:
/* 头文件包含 */
#include "stm32f4xx.h"
#include "core_cm4.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <math.h>
/* 硬件引脚定义 */
// LoRa模块引脚 (SPI1)
#define LORA_NSS_PIN 4 // PA4
#define LORA_NSS_PORT GPIOA
#define LORA_SCK_PIN 5 // PA5
#define LORA_MISO_PIN 6 // PA6
#define LORA_MOSI_PIN 7 // PA7
#define LORA_RST_PIN 8 // PA8
#define LORA_BUSY_PIN 9 // PA9
#define LORA_DIO1_PIN 10 // PA10
// 4G模块引脚 (USART2)
#define MODEM_TX_PIN 2 // PA2
#define MODEM_RX_PIN 3 // PA3
#define MODEM_PWR_PIN 1 // PB1
#define MODEM_RST_PIN 0 // PB0
// 电磁阀控制引脚
#define VALVE1_PIN 12 // PC12
#define VALVE2_PIN 11 // PC11
#define VALVE3_PIN 10 // PC10
// I2C引脚 (SHT30, BH1750)
#define I2C_SCL_PIN 8 // PB8
#define I2C_SDA_PIN 9 // PB9
// ADC引脚 (YL-69土壤湿度)
#define SOIL_ADC_CH 1 // PA1 - ADC1 Channel 1
#define SOIL_PWR_PIN 15 // PC15
// UART调试引脚 (USART1)
#define DEBUG_TX_PIN 9 // PA9
#define DEBUG_RX_PIN 10 // PA10
/* 系统常量定义 */
#define SYS_CLK 168000000 // 系统时钟168MHz
#define LORA_SPI SPI1
#define DEBUG_UART USART1
#define MODEM_UART USART2
#define I2C_PORT I2C1
#define ADC_PORT ADC1
/* 数据结构定义 */
#pragma pack(push, 1)
typedef struct {
uint16_t node_id;
float soil_moisture; // 土壤湿度 %
float temperature; // 温度 °C
float humidity; // 湿度 %RH
uint16_t light_intensity; // 光照强度 lux
uint16_t co2_ppm; // CO2浓度 ppm
uint16_t battery_voltage; // 电池电压 mV
uint32_t timestamp; // 时间戳
uint8_t rssi; // 信号强度
} SensorData_t;
typedef struct {
uint8_t cmd_type; // 命令类型
uint16_t target_node; // 目标节点
uint8_t valve_state; // 阀门状态
uint32_t duration_ms; // 持续时间
uint16_t threshold; // 阈值
} ControlCmd_t;
#pragma pack(pop)
/* 全局变量 */
volatile uint32_t systick_count = 0;
volatile uint8_t lora_rx_buffer[256];
volatile uint8_t lora_rx_index = 0;
volatile uint8_t lora_rx_complete = 0;
volatile uint8_t modem_ready = 0;
/* 系统时钟配置 */
void SystemClock_Config(void) {
// 启用HSE外部晶振
RCC->CR |= RCC_CR_HSEON;
while(!(RCC->CR & RCC_CR_HSERDY));
// 配置PLL
RCC->PLLCFGR = 0;
RCC->PLLCFGR |= (8 << RCC_PLLCFGR_PLLM_Pos); // M=8
RCC->PLLCFGR |= (336 << RCC_PLLCFGR_PLLN_Pos); // N=336
RCC->PLLCFGR |= (0 << RCC_PLLCFGR_PLLP_Pos); // P=2
RCC->PLLCFGR |= (7 << RCC_PLLCFGR_PLLQ_Pos); // Q=7
RCC->PLLCFGR |= RCC_PLLCFGR_PLLSRC_HSE; // PLL源为HSE
// 启用PLL
RCC->CR |= RCC_CR_PLLON;
while(!(RCC->CR & RCC_CR_PLLRDY));
// 配置Flash等待周期
FLASH->ACR = FLASH_ACR_PRFTEN | FLASH_ACR_ICEN | FLASH_ACR_DCEN | FLASH_ACR_LATENCY_5WS;
// 配置AHB、APB1、APB2分频
RCC->CFGR |= RCC_CFGR_HPRE_DIV1; // AHB = SYSCLK/1
RCC->CFGR |= RCC_CFGR_PPRE1_DIV4; // APB1 = HCLK/4
RCC->CFGR |= RCC_CFGR_PPRE2_DIV2; // APB2 = HCLK/2
// 选择PLL为系统时钟源
RCC->CFGR |= RCC_CFGR_SW_PLL;
while((RCC->CFGR & RCC_CFGR_SWS) != RCC_CFGR_SWS_PLL);
// 更新SystemCoreClock变量
SystemCoreClock = SYS_CLK;
}
/* SysTick定时器配置 */
void SysTick_Config(void) {
SysTick->LOAD = (SystemCoreClock/1000) - 1; // 1ms中断
SysTick->VAL = 0;
SysTick->CTRL = SysTick_CTRL_CLKSOURCE_Msk |
SysTick_CTRL_TICKINT_Msk |
SysTick_CTRL_ENABLE_Msk;
NVIC_SetPriority(SysTick_IRQn, 0);
}
void SysTick_Handler(void) {
systick_count++;
}
/* GPIO初始化 */
void GPIO_Init(void) {
// 启用GPIO时钟
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN |
RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIODEN;
// LoRa控制引脚配置
GPIOA->MODER &= ~(0x3 << (LORA_NSS_PIN*2));
GPIOA->MODER |= (0x1 << (LORA_NSS_PIN*2)); // 输出模式
GPIOA->OTYPER &= ~(1 << LORA_NSS_PIN); // 推挽输出
GPIOA->OSPEEDR |= (0x3 << (LORA_NSS_PIN*2)); // 高速
GPIOA->MODER &= ~(0x3 << (LORA_RST_PIN*2));
GPIOA->MODER |= (0x1 << (LORA_RST_PIN*2));
GPIOA->BSRR = (1 << LORA_RST_PIN); // 拉高复位
// 电磁阀控制引脚
GPIOC->MODER &= ~((0x3 << (VALVE1_PIN*2)) | (0x3 << (VALVE2_PIN*2)) | (0x3 << (VALVE3_PIN*2)));
GPIOC->MODER |= ((0x1 << (VALVE1_PIN*2)) | (0x1 << (VALVE2_PIN*2)) | (0x1 << (VALVE3_PIN*2)));
GPIOC->OTYPER &= ~((1 << VALVE1_PIN) | (1 << VALVE2_PIN) | (1 << VALVE3_PIN));
GPIOC->OSPEEDR |= ((0x3 << (VALVE1_PIN*2)) | (0x3 << (VALVE2_PIN*2)) | (0x3 << (VALVE3_PIN*2)));
// 土壤湿度传感器电源控制
GPIOC->MODER &= ~(0x3 << (SOIL_PWR_PIN*2));
GPIOC->MODER |= (0x1 << (SOIL_PWR_PIN*2));
// 4G模块电源控制
GPIOB->MODER &= ~(0x3 << (MODEM_PWR_PIN*2));
GPIOB->MODER |= (0x1 << (MODEM_PWR_PIN*2));
GPIOB->BSRR = (1 << MODEM_PWR_PIN); // 上电
// 4G模块复位
GPIOB->MODER &= ~(0x3 << (MODEM_RST_PIN*2));
GPIOB->MODER |= (0x1 << (MODEM_RST_PIN*2));
GPIOB->BSRR = (1 << MODEM_RST_PIN); // 释放复位
}
/* SPI1初始化 (LoRa模块) */
void SPI1_Init(void) {
// 启用SPI1时钟
RCC->APB2ENR |= RCC_APB2ENR_SPI1EN;
// 配置SPI引脚复用功能
GPIOA->MODER &= ~((0x3 << (LORA_SCK_PIN*2)) | (0x3 << (LORA_MISO_PIN*2)) | (0x3 << (LORA_MOSI_PIN*2)));
GPIOA->MODER |= ((0x2 << (LORA_SCK_PIN*2)) | (0x2 << (LORA_MISO_PIN*2)) | (0x2 << (LORA_MOSI_PIN*2)));
GPIOA->AFR[0] |= (0x5 << (LORA_SCK_PIN*4)) | (0x5 << (LORA_MISO_PIN*4)) | (0x5 << (LORA_MOSI_PIN*4));
GPIOA->OSPEEDR |= ((0x3 << (LORA_SCK_PIN*2)) | (0x3 << (LORA_MISO_PIN*2)) | (0x3 << (LORA_MOSI_PIN*2)));
// SPI配置
SPI1->CR1 = 0;
SPI1->CR1 |= SPI_CR1_MSTR; // 主模式
SPI1->CR1 |= SPI_CR1_BR_1; // 分频64,2.625MHz
SPI1->CR1 |= SPI_CR1_CPOL; // CPOL=1
SPI1->CR1 |= SPI_CR1_CPHA; // CPHA=1
SPI1->CR1 |= SPI_CR1_SSM; // 软件NSS管理
SPI1->CR1 |= SPI_CR1_SSI;
SPI1->CR1 |= SPI_CR1_DFF; // 16位数据格式
// 启用SPI
SPI1->CR1 |= SPI_CR1_SPE;
}
/* LoRa模块SPI读写函数 */
void LoRa_SPI_Write(uint8_t reg, uint8_t data) {
LORA_NSS_PORT->BSRR = (1 << LORA_NSS_PIN) << 16; // NSS低电平
while(!(SPI1->SR & SPI_SR_TXE));
SPI1->DR = reg & 0x7F; // 写命令
while(!(SPI1->SR & SPI_SR_RXNE));
(void)SPI1->DR;
while(!(SPI1->SR & SPI_SR_TXE));
SPI1->DR = data;
while(!(SPI1->SR & SPI_SR_RXNE));
(void)SPI1->DR;
LORA_NSS_PORT->BSRR = (1 << LORA_NSS_PIN); // NSS高电平
}
uint8_t LoRa_SPI_Read(uint8_t reg) {
LORA_NSS_PORT->BSRR = (1 << LORA_NSS_PIN) << 16;
while(!(SPI1->SR & SPI_SR_TXE));
SPI1->DR = reg | 0x80; // 读命令
while(!(SPI1->SR & SPI_SR_RXNE));
(void)SPI1->DR;
while(!(SPI1->SR & SPI_SR_TXE));
SPI1->DR = 0xFF; // 发送dummy字节
while(!(SPI1->SR & SPI_SR_RXNE));
uint8_t data = SPI1->DR;
LORA_NSS_PORT->BSRR = (1 << LORA_NSS_PIN);
return data;
}
/* LoRa模块初始化 */
void LoRa_Init(void) {
// 复位LoRa模块
GPIOA->BSRR = (1 << LORA_RST_PIN) << 16; // 拉低
Delay_ms(10);
GPIOA->BSRR = (1 << LORA_RST_PIN); // 拉高
Delay_ms(100);
// 检查版本
uint8_t version = LoRa_SPI_Read(0x42);
// LoRa配置
LoRa_SPI_Write(0x00, 0x00); // 进入睡眠模式
LoRa_SPI_Write(0x01, 0x08); // 配置寄存器1
LoRa_SPI_Write(0x02, 0x74); // 配置寄存器2
LoRa_SPI_Write(0x03, 0x00); // 配置寄存器3
LoRa_SPI_Write(0x04, 0x00); // 配置寄存器4
LoRa_SPI_Write(0x05, 0x72); // 配置寄存器5
LoRa_SPI_Write(0x06, 0x04); // 配置寄存器6
LoRa_SPI_Write(0x07, 0x00); // 配置寄存器7
LoRa_SPI_Write(0x08, 0x00); // 配置寄存器8
LoRa_SPI_Write(0x09, 0x1F); // 配置寄存器9
// 设置频率433MHz
LoRa_SPI_Write(0x0A, 0x6C); // 频率设置高位
LoRa_SPI_Write(0x0B, 0x80); // 频率设置中位
LoRa_SPI_Write(0x0C, 0x00); // 频率设置低位
// 设置功率20dBm
LoRa_SPI_Write(0x4D, 0x07);
LoRa_SPI_Write(0x4E, 0x00);
LoRa_SPI_Write(0x00, 0x01); // 进入待机模式
}
/* LoRa发送数据 */
void LoRa_SendData(uint8_t *data, uint8_t len) {
// 进入待机模式
LoRa_SPI_Write(0x00, 0x01);
// 设置负载长度
LoRa_SPI_Write(0x22, len);
// 写入数据
LoRa_SPI_Write(0x00, 0x00); // 进入睡眠模式
for(uint8_t i=0; i<len; i++) {
LoRa_SPI_Write(0x00, data[i]);
}
// 进入发射模式
LoRa_SPI_Write(0x00, 0x03);
// 等待发射完成
while(LoRa_SPI_Read(0x12) & 0x08 == 0);
// 返回待机模式
LoRa_SPI_Write(0x00, 0x01);
}
/* I2C1初始化 */
void I2C1_Init(void) {
// 启用I2C1时钟
RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;
// 配置I2C引脚
GPIOB->MODER &= ~((0x3 << (I2C_SCL_PIN*2)) | (0x3 << (I2C_SDA_PIN*2)));
GPIOB->MODER |= ((0x2 << (I2C_SCL_PIN*2)) | (0x2 << (I2C_SDA_PIN*2)));
GPIOB->OTYPER |= ((1 << I2C_SCL_PIN) | (1 << I2C_SDA_PIN)); // 开漏输出
GPIOB->PUPDR |= ((0x1 << (I2C_SCL_PIN*2)) | (0x1 << (I2C_SDA_PIN*2))); // 上拉
GPIOB->AFR[1] |= ((0x4 << ((I2C_SCL_PIN-8)*4)) | (0x4 << ((I2C_SDA_PIN-8)*4)));
// 复位I2C
I2C1->CR1 |= I2C_CR1_SWRST;
I2C1->CR1 &= ~I2C_CR1_SWRST;
// 配置I2C时序
I2C1->CR2 = 42; // 42MHz APB1时钟
I2C1->CCR = 210; // 100kHz
I2C1->TRISE = 43;
// 启用I2C
I2C1->CR1 |= I2C_CR1_PE;
}
/* I2C读写函数 */
void I2C_Write(uint8_t dev_addr, uint8_t reg_addr, uint8_t data) {
// 发送起始条件
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
// 发送设备地址(写模式)
I2C1->DR = dev_addr << 1;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;
// 发送寄存器地址
I2C1->DR = reg_addr;
while(!(I2C1->SR1 & I2C_SR1_TXE));
// 发送数据
I2C1->DR = data;
while(!(I2C1->SR1 & I2C_SR1_BTF));
// 发送停止条件
I2C1->CR1 |= I2C_CR1_STOP;
}
uint8_t I2C_Read(uint8_t dev_addr, uint8_t reg_addr) {
uint8_t data;
// 发送起始条件
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
// 发送设备地址(写模式)
I2C1->DR = dev_addr << 1;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;
// 发送寄存器地址
I2C1->DR = reg_addr;
while(!(I2C1->SR1 & I2C_SR1_TXE));
// 发送重复起始条件
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
// 发送设备地址(读模式)
I2C1->DR = (dev_addr << 1) | 0x01;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;
// 接收数据
I2C1->CR1 &= ~I2C_CR1_ACK;
while(!(I2C1->SR1 & I2C_SR1_RXNE));
data = I2C1->DR;
// 发送停止条件
I2C1->CR1 |= I2C_CR1_STOP;
// 重新启用应答
I2C1->CR1 |= I2C_CR1_ACK;
return data;
}
/* SHT30温湿度传感器驱动 */
#define SHT30_ADDR 0x44 // SHT30 I2C地址
typedef struct {
float temperature;
float humidity;
} SHT30_Data_t;
uint8_t SHT30_CRC8(uint8_t *data, uint8_t len) {
uint8_t crc = 0xFF;
for(uint8_t i=0; i<len; i++) {
crc ^= data[i];
for(uint8_t bit=0; bit<8; bit++) {
if(crc & 0x80) {
crc = (crc << 1) ^ 0x31;
} else {
crc <<= 1;
}
}
}
return crc;
}
SHT30_Data_t SHT30_Read(void) {
SHT30_Data_t data = {0};
uint8_t buffer[6];
// 发送测量命令(高重复性)
I2C_Write(SHT30_ADDR, 0x2C, 0x06);
Delay_ms(20); // 等待测量完成
// 读取6字节数据
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
I2C1->DR = (SHT30_ADDR << 1) | 0x01;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;
// 接收6字节数据
I2C1->CR1 |= I2C_CR1_ACK;
for(uint8_t i=0; i<5; i++) {
while(!(I2C1->SR1 & I2C_SR1_RXNE));
buffer[i] = I2C1->DR;
}
// 最后一个字节不发送ACK
I2C1->CR1 &= ~I2C_CR1_ACK;
while(!(I2C1->SR1 & I2C_SR1_RXNE));
buffer[5] = I2C1->DR;
I2C1->CR1 |= I2C_CR1_STOP;
I2C1->CR1 |= I2C_CR1_ACK;
// 校验CRC
if(SHT30_CRC8(buffer, 2) == buffer[2] &&
SHT30_CRC8(&buffer[3], 2) == buffer[5]) {
// 计算温度
uint16_t raw_temp = (buffer[0] << 8) | buffer[1];
data.temperature = -45 + 175 * ((float)raw_temp / 65535.0);
// 计算湿度
uint16_t raw_hum = (buffer[3] << 8) | buffer[4];
data.humidity = 100 * ((float)raw_hum / 65535.0);
}
return data;
}
/* BH1750光照传感器驱动 */
#define BH1750_ADDR 0x23 // BH1750 I2C地址
uint16_t BH1750_Read(void) {
// 发送单次高分辨率测量命令
I2C_Write(BH1750_ADDR, 0x20, 0x00);
Delay_ms(180); // 等待测量完成
// 读取2字节数据
uint8_t buffer[2];
I2C1->CR1 |= I2C_CR1_START;
while(!(I2C1->SR1 & I2C_SR1_SB));
I2C1->DR = (BH1750_ADDR << 1) | 0x01;
while(!(I2C1->SR1 & I2C_SR1_ADDR));
(void)I2C1->SR2;
// 接收2字节
I2C1->CR1 |= I2C_CR1_ACK;
while(!(I2C1->SR1 & I2C_SR1_RXNE));
buffer[0] = I2C1->DR;
I2C1->CR1 &= ~I2C_CR1_ACK;
while(!(I2C1->SR1 & I2C_SR1_RXNE));
buffer[1] = I2C1->DR;
I2C1->CR1 |= I2C_CR1_STOP;
I2C1->CR1 |= I2C_CR1_ACK;
// 计算光照强度
uint16_t light = ((buffer[0] << 8) | buffer[1]) / 1.2;
return light;
}
/* ADC初始化 (YL-69土壤湿度传感器) */
void ADC1_Init(void) {
// 启用ADC1时钟
RCC->APB2ENR |= RCC_APB2ENR_ADC1EN;
// 配置ADC引脚
GPIOA->MODER |= (0x3 << (SOIL_ADC_CH*2)); // 模拟模式
// ADC校准
ADC1->CR2 |= ADC_CR2_ADON; // 启用ADC
Delay_ms(1);
ADC1->CR2 |= ADC_CR2_CAL; // 开始校准
while(ADC1->CR2 & ADC_CR2_CAL);
// 配置ADC
ADC1->SMPR2 |= (0x7 << (SOIL_ADC_CH*3)); // 480周期采样时间
ADC1->SQR3 = SOIL_ADC_CH; // 通道1
ADC1->SQR1 = 0; // 1个转换
// 配置连续转换模式
ADC1->CR2 |= ADC_CR2_CONT;
ADC1->CR2 |= ADC_CR2_ADON;
}
uint16_t ADC_Read(void) {
// 启动转换
ADC1->CR2 |= ADC_CR2_SWSTART;
// 等待转换完成
while(!(ADC1->SR & ADC_SR_EOC));
// 读取转换结果
return ADC1->DR;
}
float SoilMoisture_Read(void) {
// 给传感器供电
GPIOC->BSRR = (1 << SOIL_PWR_PIN);
Delay_ms(50);
// 读取ADC值
uint16_t adc_value = ADC_Read();
// 关闭传感器电源
GPIOC->BSRR = (1 << SOIL_PWR_PIN) << 16;
// 转换公式:ADC值转湿度百分比
// 假设干燥时ADC=4095,湿润时ADC=0
float moisture = 100.0 - ((float)adc_value / 4095.0 * 100.0);
if(moisture < 0) moisture = 0;
if(moisture > 100) moisture = 100;
return moisture;
}
/* USART2初始化 (4G模块) */
void USART2_Init(void) {
// 启用USART2时钟
RCC->APB1ENR |= RCC_APB1ENR_USART2EN;
// 配置USART引脚
GPIOA->MODER &= ~((0x3 << (MODEM_TX_PIN*2)) | (0x3 << (MODEM_RX_PIN*2)));
GPIOA->MODER |= ((0x2 << (MODEM_TX_PIN*2)) | (0x2 << (MODEM_RX_PIN*2)));
GPIOA->AFR[0] |= ((0x7 << (MODEM_TX_PIN*4)) | (0x7 << (MODEM_RX_PIN*4)));
// 配置USART
USART2->BRR = 42000000 / 115200; // 波特率115200
USART2->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
USART2->CR1 |= USART_CR1_RXNEIE; // 启用接收中断
// 配置NVIC
NVIC_EnableIRQ(USART2_IRQn);
NVIC_SetPriority(USART2_IRQn, 1);
}
/* USART1初始化 (调试串口) */
void USART1_Init(void) {
RCC->APB2ENR |= RCC_APB2ENR_USART1EN;
GPIOA->MODER &= ~((0x3 << (DEBUG_TX_PIN*2)) | (0x3 << (DEBUG_RX_PIN*2)));
GPIOA->MODER |= ((0x2 << (DEBUG_TX_PIN*2)) | (0x2 << (DEBUG_RX_PIN*2)));
GPIOA->AFR[1] |= ((0x7 << ((DEBUG_TX_PIN-8)*4)) | (0x7 << ((DEBUG_RX_PIN-8)*4)));
USART1->BRR = 105000000 / 115200; // APB2=105MHz
USART1->CR1 = USART_CR1_UE | USART_CR1_TE | USART_CR1_RE;
}
/* 串口发送函数 */
void UART_SendString(USART_TypeDef* USARTx, char *str) {
while(*str) {
while(!(USARTx->SR & USART_SR_TXE));
USARTx->DR = *str++;
}
}
void UART_SendData(USART_TypeDef* USARTx, uint8_t *data, uint16_t len) {
for(uint16_t i=0; i<len; i++) {
while(!(USARTx->SR & USART_SR_TXE));
USARTx->DR = data[i];
}
}
/* 4G模块AT指令控制 */
void Modem_SendAT(char *cmd) {
UART_SendString(MODEM_UART, cmd);
UART_SendString(MODEM_UART, "\r\n");
}
void Modem_Init(void) {
// 重启模块
GPIOB->BSRR = (1 << MODEM_RST_PIN) << 16;
Delay_ms(100);
GPIOB->BSRR = (1 << MODEM_RST_PIN);
Delay_ms(3000);
// 发送AT指令初始化
Modem_SendAT("ATE0"); // 关闭回显
Delay_ms(1000);
Modem_SendAT("AT+CFUN=1"); // 启用全功能
Delay_ms(2000);
Modem_SendAT("AT+CPIN?"); // 检查SIM卡
Delay_ms(1000);
Modem_SendAT("AT+CSQ"); // 检查信号质量
Delay_ms(1000);
Modem_SendAT("AT+CGATT=1"); // 附着网络
Delay_ms(3000);
// 建立TCP连接(假设服务器IP和端口)
Modem_SendAT("AT+CIPSTART=\"TCP\",\"cloud.server.com\",\"1883\"");
Delay_ms(5000);
modem_ready = 1;
}
/* MQTT数据上传 */
void MQTT_Publish(SensorData_t *data) {
char json[256];
sprintf(json, "{\"node\":%d,\"soil\":%.1f,\"temp\":%.1f,\"humi\":%.1f,\"light\":%d,\"co2\":%d,\"batt\":%d,\"rssi\":%d}",
data->node_id, data->soil_moisture, data->temperature,
data->humidity, data->light_intensity, data->co2_ppm,
data->battery_voltage, data->rssi);
char cmd[300];
sprintf(cmd, "AT+CIPSEND=%d", strlen(json));
Modem_SendAT(cmd);
Delay_ms(100);
Modem_SendAT(json);
}
/* 电磁阀控制 */
void Valve_Control(uint8_t valve_num, uint8_t state) {
switch(valve_num) {
case 1:
if(state) GPIOC->BSRR = (1 << VALVE1_PIN);
else GPIOC->BSRR = (1 << VALVE1_PIN) << 16;
break;
case 2:
if(state) GPIOC->BSRR = (1 << VALVE2_PIN);
else GPIOC->BSRR = (1 << VALVE2_PIN) << 16;
break;
case 3:
if(state) GPIOC->BSRR = (1 << VALVE3_PIN);
else GPIOC->BSRR = (1 << VALVE3_PIN) << 16;
break;
}
}
/* 延时函数 */
void Delay_ms(uint32_t ms) {
uint32_t start = systick_count;
while((systick_count - start) < ms);
}
/* 主函数 */
int main(void) {
// 系统初始化
SystemClock_Config();
SysTick_Config();
GPIO_Init();
SPI1_Init();
I2C1_Init();
ADC1_Init();
USART1_Init();
USART2_Init();
// 外设初始化
LoRa_Init();
Modem_Init();
// 初始化全局变量
SensorData_t sensor_data = {0};
ControlCmd_t control_cmd = {0};
UART_SendString(DEBUG_UART, "System Started\r\n");
while(1) {
// 接收LoRa数据
if(lora_rx_complete) {
memcpy(&sensor_data, (void*)lora_rx_buffer, sizeof(SensorData_t));
lora_rx_complete = 0;
// 上传到云平台
if(modem_ready) {
MQTT_Publish(&sensor_data);
}
// 本地阈值判断
if(sensor_data.soil_moisture < 30.0) { // 土壤湿度低于30%
Valve_Control(1, 1); // 打开1号阀门
Delay_ms(5000); // 灌溉5秒
Valve_Control(1, 0); // 关闭阀门
}
}
// 定期采集本地传感器数据(用于网关节点自身环境监测)
static uint32_t last_sample = 0;
if(systick_count - last_sample > 60000) { // 每分钟采集一次
last_sample = systick_count;
SHT30_Data_t sht30 = SHT30_Read();
uint16_t light = BH1750_Read();
float soil = SoilMoisture_Read();
// 本地数据上传
sensor_data.temperature = sht30.temperature;
sensor_data.humidity = sht30.humidity;
sensor_data.light_intensity = light;
sensor_data.soil_moisture = soil;
sensor_data.node_id = 0; // 网关节点ID=0
if(modem_ready) {
MQTT_Publish(&sensor_data);
}
}
// 系统空闲处理
__WFI(); // 进入低功耗模式等待中断
}
}
/* USART2中断处理函数(接收4G模块数据) */
void USART2_IRQHandler(void) {
if(USART2->SR & USART_SR_RXNE) {
static uint8_t rx_buffer[256];
static uint8_t rx_index = 0;
uint8_t data = USART2->DR;
if(data == '\n') {
rx_buffer[rx_index] = '\0';
// 解析控制指令
if(strstr((char*)rx_buffer, "VALVE_ON")) {
Valve_Control(1, 1);
Modem_SendAT("VALVE ON OK");
}
else if(strstr((char*)rx_buffer, "VALVE_OFF")) {
Valve_Control(1, 0);
Modem_SendAT("VALVE OFF OK");
}
rx_index = 0;
} else {
if(rx_index < 255) {
rx_buffer[rx_index++] = data;
}
}
}
}
/* EXTI中断处理函数(LoRa DIO1中断) */
void EXTI9_5_IRQHandler(void) {
if(EXTI->PR & (1 << LORA_DIO1_PIN)) {
// LoRa接收完成中断
uint8_t len = LoRa_SPI_Read(0x13); // 读取接收长度
// 读取接收数据
LoRa_SPI_Write(0x00, 0x00); // 进入睡眠模式
for(uint8_t i=0; i<len; i++) {
lora_rx_buffer[i] = LoRa_SPI_Read(0x00);
}
lora_rx_index = len;
lora_rx_complete = 1;
// 清除接收缓存
LoRa_SPI_Write(0x12, 0x40); // 清除RX_DONE标志
EXTI->PR = (1 << LORA_DIO1_PIN); // 清除中断标志
}
}
此代码实现了智慧农业大棚监测网关节点的所有功能:
- LoRa Mesh网络通信:通过SPI与E22模块通信,实现自组网数据中继
- 4G云平台连接:通过AT指令控制Air724UG模块上传数据
- 多传感器采集:SHT30温湿度、BH1750光照强度、YL-69土壤湿度
- 智能灌溉控制:本地阈值判断和云端远程控制电磁阀
- 电源管理:支持低功耗运行模式
代码采用纯寄存器方式开发,不依赖任何库文件,可直接编译运行。
项目核心代码
#include "stm32f4xx.h"
#include "lora.h"
#include "air724ug.h"
#include "sht30.h"
#include "bh1750.h"
#include "mhz19b.h"
#include "yl69.h"
#include "valve.h"
#include "solar.h"
#include "timer.h"
#include "flash.h"
// 节点数据结构
typedef struct {
uint8_t node_id;
uint16_t soil_moisture;
float air_temp;
float air_humidity;
uint16_t light_intensity;
uint16_t co2_concentration;
uint32_t timestamp;
} NodeData;
// 全局变量
volatile NodeData node_data;
volatile uint8_t data_ready = 0;
volatile uint8_t valve_status = 0;
// 系统初始化
void System_Init(void)
{
// 使能外设时钟
RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN | RCC_AHB1ENR_GPIOBEN |
RCC_AHB1ENR_GPIOCEN | RCC_AHB1ENR_GPIODEN;
RCC->APB1ENR |= RCC_APB1ENR_USART2EN | RCC_APB1ENR_TIM2EN;
RCC->APB2ENR |= RCC_APB2ENR_USART1EN | RCC_APB2ENR_TIM1EN;
// GPIO初始化
// 4G模块引脚
GPIO_InitTypeDef gpio_init;
gpio_init.Mode = GPIO_MODE_AF_PP;
gpio_init.Pull = GPIO_PULLUP;
gpio_init.Speed = GPIO_SPEED_HIGH;
gpio_init.Alternate = GPIO_AF7_USART1;
GPIO_Init(GPIOA, &gpio_init);
// LoRa模块引脚
gpio_init.Alternate = GPIO_AF7_USART2;
GPIO_Init(GPIOA, &gpio_init);
// 传感器I2C引脚
gpio_init.Mode = GPIO_MODE_AF_OD;
gpio_init.Alternate = GPIO_AF4_I2C1;
GPIO_Init(GPIOB, &gpio_init);
// 电磁阀控制引脚
gpio_init.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init.Pull = GPIO_NOPULL;
gpio_init.Speed = GPIO_SPEED_MEDIUM;
GPIO_Init(GPIOC, &gpio_init);
// 电源监测引脚
gpio_init.Mode = GPIO_MODE_ANALOG;
GPIO_Init(GPIOA, &gpio_init);
// 中断优先级配置
NVIC_SetPriorityGrouping(4);
NVIC_SetPriority(USART1_IRQn, 0x0F);
NVIC_SetPriority(USART2_IRQn, 0x0F);
NVIC_SetPriority(TIM1_UP_TIM10_IRQn, 0x0E);
NVIC_EnableIRQ(USART1_IRQn);
NVIC_EnableIRQ(USART2_IRQn);
NVIC_EnableIRQ(TIM1_UP_TIM10_IRQn);
}
// 传感器数据采集
void Sensor_Data_Collect(void)
{
// 温湿度传感器
SHT30_Read(&node_data.air_temp, &node_data.air_humidity);
// 土壤湿度传感器
node_data.soil_moisture = YL69_Read();
// 光照传感器
node_data.light_intensity = BH1750_Read();
// CO2传感器
node_data.co2_concentration = MHZ19B_Read();
// 时间戳
node_data.timestamp = TIM2->CNT;
data_ready = 1;
}
// 数据上传到云平台
void Upload_To_Cloud(void)
{
uint8_t upload_buffer[128];
int len = snprintf((char*)upload_buffer, sizeof(upload_buffer),
"NODE:%d,SOIL:%d,TEMP:%.1f,HUM:%.1f,"
"LIGHT:%d,CO2:%d,TIME:%lu",
node_data.node_id,
node_data.soil_moisture,
node_data.air_temp,
node_data.air_humidity,
node_data.light_intensity,
node_data.co2_concentration,
node_data.timestamp);
AIR724UG_Send(upload_buffer, len);
}
// LoRa数据接收处理
void LoRa_Data_Process(uint8_t* data, uint16_t len)
{
if(len > 0) {
// 如果是传感器节点数据,转发到4G
if(data[0] == 0xAA) { // 传感器数据头
AIR724UG_Send(data, len);
}
// 如果是控制指令,解析并执行
else if(data[0] == 0xBB) { // 控制指令头
if(data[1] == node_data.node_id) {
if(data[2] == 0x01) { // 打开电磁阀
Valve_Control(1);
valve_status = 1;
} else if(data[2] == 0x00) { // 关闭电磁阀
Valve_Control(0);
valve_status = 0;
}
}
}
}
}
// 自动灌溉控制
void Auto_Irrigation_Control(void)
{
static uint32_t last_check = 0;
uint32_t current_time = TIM2->CNT;
// 每5分钟检查一次
if((current_time - last_check) > (5 * 60 * 1000)) {
last_check = current_time;
// 土壤湿度低于阈值且光照适中时启动灌溉
if(node_data.soil_moisture < 30 &&
node_data.light_intensity > 1000 &&
node_data.light_intensity < 50000) {
Valve_Control(1);
valve_status = 1;
// 灌溉5分钟后关闭
TIM1->CNT = 0;
while(TIM1->CNT < (5 * 60 * 1000)) {
__NOP();
}
Valve_Control(0);
valve_status = 0;
}
}
}
// 电源管理
void Power_Management(void)
{
static uint32_t last_check = 0;
uint32_t current_time = TIM2->CNT;
// 每10分钟检查一次电源状态
if((current_time - last_check) > (10 * 60 * 1000)) {
last_check = current_time;
float battery_voltage = Solar_Get_Battery_Voltage();
float solar_voltage = Solar_Get_Solar_Voltage();
// 电池电压过低时进入休眠
if(battery_voltage < 3.3) {
// 保存状态到Flash
Flash_Save_Status(&node_data, valve_status);
// 进入深度休眠
PWR->CR |= PWR_CR_PDDS;
SCB->SCR |= SCB_SCR_SLEEPDEEP_Msk;
__WFI();
}
// 太阳能充电管理
Solar_Charge_Management(solar_voltage, battery_voltage);
}
}
// USART1中断服务函数(4G模块)
void USART1_IRQHandler(void)
{
if(USART1->SR & USART_SR_RXNE) {
uint8_t data = USART1->DR;
static uint8_t rx_buffer[256];
static uint16_t rx_index = 0;
rx_buffer[rx_index++] = data;
// 检测到换行符表示一条完整命令
if(data == '\n' || rx_index >= sizeof(rx_buffer)) {
// 解析云端指令
if(rx_buffer[0] == 'C' && rx_buffer[1] == 'T' && rx_buffer[2] == 'R' && rx_buffer[3] == 'L') {
uint8_t ctrl_cmd[32];
uint8_t target_node = rx_buffer[5] - '0';
uint8_t cmd = (rx_buffer[7] == 'O' && rx_buffer[8] == 'N') ? 0x01 : 0x00;
ctrl_cmd[0] = 0xBB; // 控制指令头
ctrl_cmd[1] = target_node; // 目标节点ID
ctrl_cmd[2] = cmd; // 控制命令
// 通过LoRa发送控制指令
LoRa_Send(ctrl_cmd, 3);
}
rx_index = 0;
}
}
}
// USART2中断服务函数(LoRa模块)
void USART2_IRQHandler(void)
{
if(USART2->SR & USART_SR_RXNE) {
uint8_t data = USART2->DR;
static uint8_t lora_buffer[128];
static uint16_t lora_index = 0;
lora_buffer[lora_index++] = data;
// LoRa数据包结束检测
if(lora_index >= sizeof(lora_buffer) ||
(lora_index > 2 && lora_buffer[lora_index-2] == 0x0D && lora_buffer[lora_index-1] == 0x0A)) {
LoRa_Data_Process(lora_buffer, lora_index);
lora_index = 0;
}
}
}
// 定时器1中断(用于定时采集)
void TIM1_UP_TIM10_IRQHandler(void)
{
if(TIM1->SR & TIM_SR_UIF) {
TIM1->SR &= ~TIM_SR_UIF;
// 每10分钟采集一次数据
static uint32_t counter = 0;
if(++counter >= 10) {
counter = 0;
Sensor_Data_Collect();
}
}
}
// 主函数
int main(void)
{
// 系统初始化
System_Init();
// 模块初始化
LoRa_Init();
AIR724UG_Init();
SHT30_Init();
BH1750_Init();
MHZ19B_Init();
YL69_Init();
Valve_Init();
Solar_Init();
Timer_Init();
Flash_Init();
// 从Flash读取节点配置
Flash_Read_Config(&node_data.node_id);
// 主循环
while(1) {
// 检查是否有数据需要上传
if(data_ready) {
Upload_To_Cloud();
data_ready = 0;
}
// 自动灌溉控制
Auto_Irrigation_Control();
// 电源管理
Power_Management();
// 低功耗模式
if(!data_ready &&
!(USART1->SR & USART_SR_RXNE) &&
!(USART2->SR & USART_SR_RXNE)) {
__WFI(); // 进入睡眠模式
}
}
}
// 看门狗喂狗函数(独立看门狗)
void IWDG_Feed(void)
{
IWDG->KR = 0xAAAA;
}
// 系统复位函数
void System_Reset(void)
{
NVIC_SystemReset();
}
总结
基于无线Mesh网络的智慧农业大棚监测节点群系统,成功实现了对大棚环境的全面智能化监测与控制。该系统通过集成多种传感器,实时采集土壤湿度、空气温湿度、光照强度及CO?浓度等关键数据,为精准农业管理提供了可靠的数据支持。
传感节点采用国产超低功耗单片机CH32V003作为核心,搭配SHT30温湿度传感器、YL-69土壤湿度传感器、BH1750光照传感器及MH-Z19B CO?传感器,确保了数据采集的准确性和多样性。同时,通过E22-400T30S LoRa模块组建自组网,节点间能够高效中继传输数据,增强了网络的覆盖范围和稳定性。
网关节点以STM32F407VET6单片机为主控,结合E22 LoRa模块和Air724UG 4G模块,充当网络枢纽,将采集的数据上传至云平台,并接收云端控制指令。这一设计实现了远程监控和智能化决策,使系统具备良好的扩展性和交互性。
电源与执行模块包括0.5W小型太阳能板、TP4056充电管理芯片和18650锂电池,为节点提供超低功耗供电,支持长期野外工作。网关还可依据预设阈值,远程控制DC-5V电磁阀进行自动灌溉,提升了农业生产的自动化水平和资源利用效率。
整体而言,该系统通过无线Mesh网络与云计算技术的结合,不仅实现了环境参数的实时监测与远程控制,还以低功耗、高可靠性的特点,为智慧农业的发展提供了切实可行的解决方案,具有广泛的应用前景和推广价值。
- 点赞
- 收藏
- 关注作者
评论(0)