文章目录
-
- 一、项目概述
- 二、硬件选型与原理
-
- [2.1 核心硬件清单](#2.1 核心硬件清单)
- [2.2 核心原理](#2.2 核心原理)
- 三、硬件接线
-
- [3.1 接线总览(STM32F103C8T6)](#3.1 接线总览(STM32F103C8T6))
- [3.2 接线注意事项](#3.2 接线注意事项)
- 四、软件开发环境搭建
-
- [4.1 软件工具清单](#4.1 软件工具清单)
- [4.2 环境搭建步骤](#4.2 环境搭建步骤)
-
- 步骤1:安装STM32CubeMX
- [步骤2:安装Keil MDK-ARM](#步骤2:安装Keil MDK-ARM)
- 步骤3:配置串口助手
- 五、STM32CubeMX配置
-
- [5.1 新建工程](#5.1 新建工程)
- [5.2 外设配置](#5.2 外设配置)
- 六、代码编写(详细版)
-
- [6.1 核心代码结构](#6.1 核心代码结构)
- [6.2 代码关键部分解释](#6.2 代码关键部分解释)
- [6.3 BC28模块AT指令配置(关键)](#6.3 BC28模块AT指令配置(关键))
- 七、系统工作流程图
- 八、云端平台配置(OneNET为例)
-
- [8.1 创建设备](#8.1 创建设备)
- [8.2 数据展示配置](#8.2 数据展示配置)
- [8.3 远程控制配置](#8.3 远程控制配置)
- 九、系统调试与测试
-
- [9.1 硬件调试](#9.1 硬件调试)
- [9.2 软件调试](#9.2 软件调试)
- [9.3 现场测试](#9.3 现场测试)
- 十、常见问题与解决方法
-
- [10.1 ADC采集数据异常](#10.1 ADC采集数据异常)
- [10.2 NB-IoT无法上传数据](#10.2 NB-IoT无法上传数据)
- [10.3 水泵无法启动](#10.3 水泵无法启动)
- 十一、系统优化与扩展
-
- [11.1 低功耗优化](#11.1 低功耗优化)
- [11.2 功能扩展](#11.2 功能扩展)
- 总结
一、项目概述
本项目旨在搭建一套基于STM32单片机、NB-IoT模块和土壤湿度传感器的智能农业灌溉系统。系统可实时采集土壤湿度数据,通过NB-IoT将数据上传至云端平台,同时根据预设湿度阈值自动控制水泵的启停,实现无人值守的精准灌溉。整个系统具备低功耗、远距离传输、自动化控制的特点,完全适配农业大田、温室大棚等场景的实际需求。
二、硬件选型与原理
2.1 核心硬件清单
| 硬件名称 | 型号/规格 | 作用说明 |
|---|---|---|
| STM32单片机 | STM32F103C8T6(最小系统板) | 核心控制单元,处理数据和指令 |
| 土壤湿度传感器 | FC-28(模拟量输出) | 采集土壤湿度模拟信号 |
| NB-IoT模块 | BC28(带天线、串口通信) | 数据上传云端、接收远程指令 |
| 水泵 | 5V微型隔膜泵 | 执行灌溉动作 |
| 继电器模块 | 5V单路继电器(高电平触发) | 隔离单片机与水泵,控制通断 |
| 电源模块 | 12V转5V/3.3V稳压模块 | 为各模块供电 |
| 杜邦线、面包板 | 公对公/公对母 | 电路连接 |
2.2 核心原理
- 土壤湿度传感器将土壤湿度转化为0-3.3V的模拟电压信号,输入STM32的ADC引脚;
- STM32通过ADC采集电压值并转换为湿度百分比,与预设阈值对比:低于阈值则控制继电器吸合,启动水泵灌溉;高于阈值则断开继电器,停止灌溉;
- STM32通过串口与NB-IoT模块通信,将湿度数据、水泵状态上传至云端;
- 云端可下发远程控制指令,修改湿度阈值或手动控制水泵启停。
三、硬件接线
3.1 接线总览(STM32F103C8T6)
| STM32引脚 | 外接设备 | 备注 |
|---|---|---|
| PA0 | 土壤湿度传感器AO | ADC采集引脚 |
| PA1 | 继电器模块IN | 控制水泵(高电平吸合) |
| PA9 | BC28模块TX | USART1_RX(串口接收) |
| PA10 | BC28模块RX | USART1_TX(串口发送) |
| 3.3V | 传感器VCC、BC28 VCC | 供电(BC28需稳定3.3V) |
| GND | 所有模块GND | 共地,保证信号稳定 |
| 5V | 继电器模块VCC | 继电器供电 |
3.2 接线注意事项
- 所有模块必须共地(GND相连),否则会出现信号紊乱;
- BC28模块需单独接天线,且避免金属遮挡,保证NB-IoT信号强度;
- 水泵需通过继电器控制,禁止直接接STM32引脚(电流过大易烧毁单片机);
- 电源模块输出电流需≥2A,满足水泵、各模块的供电需求。
四、软件开发环境搭建
4.1 软件工具清单
- STM32CubeMX(版本6.8.1):用于配置STM32引脚、时钟、外设,生成初始化代码;
- Keil MDK-ARM(版本5.38):用于编写、编译代码,下载程序到STM32;
- 串口助手(如SSCOM):用于调试串口通信,查看传感器数据和NB-IoT模块反馈;
- NB-IoT云端平台(如中国移动OneNET):用于接收数据、下发指令。
4.2 环境搭建步骤
步骤1:安装STM32CubeMX
- 下载STM32CubeMX安装包(官网:https://www.st.com/en/development-tools/stm32cubemx.html);
- 安装过程中选择默认路径,完成后安装对应的STM32F1系列固件库(STM32Cube FW_F1 V1.8.5);
- 验证安装:打开STM32CubeMX,选择STM32F103C8T6芯片,能正常进入配置界面即完成。
步骤2:安装Keil MDK-ARM
- 下载MDK5.38安装包,安装时选择支持STM32F1系列的编译器;
- 安装STM32F1的器件库(Device Pack),版本≥2.3.0;
- 破解Keil(仅用于学习),确保能编译超过2KB的代码;
- 验证:新建工程,选择STM32F103C8T6,能正常编译空工程即完成。
步骤3:配置串口助手
- 下载SSCOM串口助手,安装后打开;
- 连接STM32的USB转串口模块到电脑,在设备管理器中查看串口号(如COM3);
- 串口助手配置:波特率9600、数据位8、停止位1、校验位无、流控无,点击"打开串口"。
五、STM32CubeMX配置
5.1 新建工程
- 打开STM32CubeMX,点击"New Project";
- 在搜索框输入"STM32F103C8T6",选择对应芯片,点击"Start Project";
- 配置RCC(时钟):选择"HSE"(外部高速时钟),晶振8MHz,时钟树配置为72MHz(STM32F103最大主频)。
5.2 外设配置
步骤1:配置ADC(土壤湿度采集)
- 点击"ADC1",选择"IN0"(对应PA0引脚);
- 配置ADC参数:分辨率12位、转换模式单次转换、扫描模式关闭;
- 开启ADC中断(可选,也可轮询采集)。
步骤2:配置GPIO(水泵控制)
- 点击"PA1",设置为"GPIO_Output"(输出模式);
- 配置默认电平:低电平(水泵默认关闭);
- 输出模式:推挽输出、速度50MHz。
步骤3:配置USART1(NB-IoT通信)
- 点击"USART1",设置为"Asynchronous"(异步模式);
- 配置参数:波特率9600、数据位8、停止位1、校验位无;
- 开启USART1中断(用于接收NB-IoT模块的数据)。
步骤4:生成代码
- 点击"Project Manager",设置工程名称、保存路径,选择"MDK-ARM v5";
- 点击"Code Generator",勾选"Generate peripheral initialization as a pair of '.c/.h' files per peripheral";
- 点击"Generate Code",生成初始化代码后用Keil打开。
六、代码编写(详细版)
6.1 核心代码结构
c
/* 头文件包含 */
#include "main.h"
#include "adc.h"
#include "gpio.h"
#include "usart.h"
#include <stdio.h>
#include <string.h>
/* 宏定义 */
#define HUMIDITY_LOW_THRESHOLD 30 // 湿度下限阈值(百分比),低于此值启动水泵
#define HUMIDITY_HIGH_THRESHOLD 70 // 湿度上限阈值(百分比),高于此值停止水泵
#define ADC_MAX_VALUE 4095 // 12位ADC最大值
#define VREF 3.3f // 参考电压3.3V
/* 全局变量 */
uint16_t adc_raw_value = 0; // ADC原始值
float humidity_voltage = 0.0f; // 湿度传感器输出电压
uint8_t humidity_percent = 0; // 土壤湿度百分比(0-100)
uint8_t pump_status = 0; // 水泵状态:0-关闭,1-开启
/* 函数声明 */
void SystemClock_Config(void);
static void MX_GPIO_Init(void);
static void MX_ADC1_Init(void);
static void MX_USART1_UART_Init(void);
void ADC_Get_Humidity(void); // ADC采集湿度
void Pump_Control(void); // 水泵控制逻辑
void NB_IoT_Send_Data(void); // NB-IoT上传数据
void USART1_Send_String(uint8_t *str, uint16_t len); // 串口发送字符串
uint8_t USART1_Receive_Data(void); // 串口接收数据
/* 主函数 */
int main(void)
{
/* 初始化HAL库 */
HAL_Init();
/* 配置系统时钟为72MHz */
SystemClock_Config();
/* 初始化外设:GPIO、ADC、USART */
MX_GPIO_Init();
MX_ADC1_Init();
MX_USART1_UART_Init();
/* 初始化水泵为关闭状态 */
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
/* 主循环 */
while (1)
{
/* 1. 采集土壤湿度 */
ADC_Get_Humidity();
/* 2. 根据湿度控制水泵 */
Pump_Control();
/* 3. 通过NB-IoT上传数据(每5秒上传一次) */
NB_IoT_Send_Data();
/* 4. 延时500ms,降低循环频率,减少功耗 */
HAL_Delay(500);
}
}
/* ADC采集土壤湿度函数 */
void ADC_Get_Humidity(void)
{
/* 启动ADC转换 */
HAL_ADC_Start(&hadc1);
/* 等待转换完成 */
if (HAL_ADC_PollForConversion(&hadc1, 100) == HAL_OK)
{
/* 读取ADC原始值 */
adc_raw_value = HAL_ADC_GetValue(&hadc1);
/* 转换为电压值:电压 = (ADC值 / ADC最大值) * 参考电压 */
humidity_voltage = (float)adc_raw_value / ADC_MAX_VALUE * VREF;
/* 转换为湿度百分比(FC-28传感器:0V=干燥,3.3V=饱和) */
humidity_percent = (uint8_t)(humidity_voltage / VREF * 100);
/* 串口打印调试信息 */
char debug_buf[100];
sprintf(debug_buf, "ADC原始值:%d,电压:%.2fV,湿度:%d%%\r\n", adc_raw_value, humidity_voltage, humidity_percent);
USART1_Send_String((uint8_t *)debug_buf, strlen(debug_buf));
}
/* 停止ADC转换 */
HAL_ADC_Stop(&hadc1);
}
/* 水泵控制逻辑函数 */
void Pump_Control(void)
{
/* 湿度低于下限,启动水泵 */
if (humidity_percent < HUMIDITY_LOW_THRESHOLD)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_SET);
pump_status = 1;
USART1_Send_String((uint8_t *)"湿度低于阈值,启动水泵\r\n", strlen("湿度低于阈值,启动水泵\r\n"));
}
/* 湿度高于上限,停止水泵 */
else if (humidity_percent > HUMIDITY_HIGH_THRESHOLD)
{
HAL_GPIO_WritePin(GPIOA, GPIO_PIN_1, GPIO_PIN_RESET);
pump_status = 0;
USART1_Send_String((uint8_t *)"湿度高于阈值,停止水泵\r\n", strlen("湿度高于阈值,停止水泵\r\n"));
}
}
/* NB-IoT上传数据函数(以OneNET平台为例) */
void NB_IoT_Send_Data(void)
{
/* 构建OneNET平台的JSON数据格式 */
char nb_data_buf[200];
sprintf(nb_data_buf, "{\"humidity\":%d,\"pump_status\":%d}\r\n", humidity_percent, pump_status);
/* 发送AT指令,配置BC28模块上传数据 */
// 1. 检查NB-IoT模块是否在线
USART1_Send_String((uint8_t *)"AT\r\n", strlen("AT\r\n"));
HAL_Delay(1000);
// 2. 发送数据到OneNET平台(需提前配置BC28的APN、设备ID等)
USART1_Send_String((uint8_t *)"AT+QMTPUB=0,0,0,\"/sys/xxx/xxx/thing/event/property/post\"\r\n", strlen("AT+QMTPUB=0,0,0,\"/sys/xxx/xxx/thing/event/property/post\"\r\n"));
HAL_Delay(500);
USART1_Send_String((uint8_t *)nb_data_buf, strlen(nb_data_buf));
/* 串口打印上传成功提示 */
USART1_Send_String((uint8_t *)"NB-IoT数据上传完成\r\n", strlen("NB-IoT数据上传完成\r\n"));
}
/* USART1发送字符串函数 */
void USART1_Send_String(uint8_t *str, uint16_t len)
{
HAL_UART_Transmit(&huart1, str, len, 1000);
}
/* USART1接收数据函数(中断方式,此处简化为轮询) */
uint8_t USART1_Receive_Data(void)
{
uint8_t data = 0;
HAL_UART_Receive(&huart1, &data, 1, 100);
return data;
}
/* 系统时钟配置函数(STM32CubeMX自动生成,略作修改) */
void SystemClock_Config(void)
{
RCC_OscInitTypeDef RCC_OscInitStruct = {0};
RCC_ClkInitTypeDef RCC_ClkInitStruct = {0};
/* 配置HSE振荡器 */
RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
RCC_OscInitStruct.HSEState = RCC_HSE_ON;
RCC_OscInitStruct.HSEPredivValue = RCC_HSE_PREDIV_DIV1;
RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSE;
RCC_OscInitStruct.PLL.PLLMUL = RCC_PLL_MUL9;
if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
{
Error_Handler();
}
/* 配置系统时钟、AHB、APB总线时钟 */
RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
|RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;
RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
{
Error_Handler();
}
}
/* 错误处理函数 */
void Error_Handler(void)
{
__disable_irq();
while (1)
{
/* 错误时LED闪烁(若接有LED),此处简化为死循环 */
}
}
#ifdef USE_FULL_ASSERT
void assert_failed(uint8_t *file, uint32_t line)
{
/* 断言失败处理,可打印文件名和行号 */
}
#endif /* USE_FULL_ASSERT */
6.2 代码关键部分解释
- ADC采集逻辑 :STM32的12位ADC将0-3.3V的模拟电压转换为0-4095的数字值,通过公式
湿度百分比 = (ADC值/4095)*100将电压值转换为直观的湿度百分比; - 水泵控制:通过对比实时湿度与预设阈值,控制PA1引脚的高低电平,进而控制继电器的吸合/断开,实现水泵的自动启停;
- NB-IoT通信:通过USART1向BC28模块发送AT指令,将湿度、水泵状态封装为JSON格式上传至OneNET云端;
- 串口调试:所有关键数据(ADC值、电压、湿度、水泵状态)均通过串口打印,方便调试时排查问题。
6.3 BC28模块AT指令配置(关键)
在上传数据前,需先配置BC28模块接入NB-IoT网络并连接云端,核心AT指令如下:
bash
# 1. 查看模块版本
AT+QGMR
# 2. 配置NB-IoT频段(以中国移动为例,使用850/900MHz)
AT+QBAND=0,8
# 3. 配置APN(中国移动:cmnb)
AT+QCGDEFCONT="IP","cmnb"
# 4. 注册网络
AT+CEREG?
# 返回+CEREG: 0,1 表示注册成功
# 5. 连接OneNET MQTT服务器
AT+QMTCONN=0,"mqtt.heclouds.com",1883,1
# 6. 订阅云端指令主题(接收远程控制指令)
AT+QMTSUB=0,1,"/sys/xxx/xxx/thing/service/property/set",1
七、系统工作流程图
是
否
是
否
是
否
系统上电初始化
STM32配置ADC/USART/GPIO
土壤湿度传感器采集模拟信号
STM32 ADC转换为数字值
计算土壤湿度百分比
湿度 < 下限阈值?
PA1输出高电平,继电器吸合,水泵启动
湿度 > 上限阈值?
PA1输出低电平,继电器断开,水泵停止
水泵保持当前状态
记录水泵当前状态
STM32通过USART1向BC28发送数据
BC28通过NB-IoT上传至云端
云端是否下发控制指令?
STM32接收指令,修改阈值/控制水泵
八、云端平台配置(OneNET为例)
8.1 创建设备
- 登录OneNET官网(https://open.iot.10086.cn/),进入"开发者中心";
- 新建产品,选择"NB-IoT"协议,填写产品名称、描述;
- 在产品下创建设备,记录设备ID、产品ID、鉴权信息(用于BC28模块连接)。
8.2 数据展示配置
- 进入设备的"数据流模板",添加两个数据流:
humidity(湿度,整型)、pump_status(水泵状态,整型); - 配置"应用管理",添加可视化面板,展示湿度曲线、水泵状态开关;
- 配置"触发器":当湿度低于阈值时,发送短信/APP推送告警。
8.3 远程控制配置
- 在"设备控制"中添加"写属性"指令,支持修改
HUMIDITY_LOW_THRESHOLD和HUMIDITY_HIGH_THRESHOLD; - 配置手动控制水泵的指令,下发
pump_control=1(启动)或pump_control=0(停止)。
九、系统调试与测试
9.1 硬件调试
- 断开水泵,仅连接传感器、STM32、NB-IoT模块,上电后查看串口助手是否有ADC数据输出;
- 用手捏住土壤湿度传感器的探头(模拟高湿度),查看湿度百分比是否上升,水泵状态是否为关闭;
- 将传感器探头放入干燥土壤(模拟低湿度),查看湿度百分比是否下降,水泵状态是否为开启;
- 检查NB-IoT模块的信号指示灯:常亮表示已注册网络,闪烁表示数据传输中。
9.2 软件调试
- 修改代码中的湿度阈值(如将下限改为20,上限改为60),重新编译下载,验证水泵控制逻辑是否生效;
- 在OneNET平台查看数据是否实时上传,曲线是否与串口数据一致;
- 从云端下发远程控制指令,修改阈值,查看STM32是否接收并执行。
9.3 现场测试
- 将系统安装至温室大棚/大田,固定传感器探头(埋入土壤10cm深处);
- 连续运行24小时,记录湿度数据和水泵启停次数,验证系统稳定性;
- 测试NB-IoT信号弱的区域,查看数据上传是否中断(BC28支持断点续传)。
十、常见问题与解决方法
10.1 ADC采集数据异常
- 现象:ADC值固定为0或4095,湿度百分比无变化;
- 原因:传感器接线错误、电源电压不稳、ADC引脚配置错误;
- 解决:检查传感器VCC/GND是否接对,用万用表测量传感器AO引脚电压,重新配置STM32CubeMX的ADC参数。
10.2 NB-IoT无法上传数据
- 现象:串口打印"AT"无响应,或注册网络失败;
- 原因:BC28模块供电不足、天线未接、APN配置错误;
- 解决:更换3.3V/2A电源,重新接天线,核对APN指令(中国移动为cmnb,联通为unilink)。
10.3 水泵无法启动
- 现象:串口显示"启动水泵",但水泵无动作;
- 原因:继电器接线错误、继电器触发电平错误、水泵电源未接;
- 解决:检查继电器IN引脚接PA1,VCC接5V,水泵接继电器的常开触点。
十一、系统优化与扩展
11.1 低功耗优化
- STM32配置睡眠模式,仅在采集数据时唤醒,其余时间休眠;
- BC28模块配置为"按需唤醒",减少待机功耗;
- 采用太阳能电池板+锂电池供电,适配无市电的农田场景。
11.2 功能扩展
- 添加温度传感器(DS18B20),采集土壤温度,实现温湿度双参数监控;
- 添加雨量传感器,雨天自动停止灌溉;
- 接入多个传感器,实现多区域灌溉控制;
- 开发手机APP,实时查看数据、远程控制。
总结
- 本智能农业灌溉系统以STM32为核心,结合土壤湿度传感器、NB-IoT模块,实现了土壤湿度采集、水泵自动控制、数据云端上传的全流程功能,代码可直接复制使用,零基础小白可按步骤完成硬件接线和软件调试;
- 系统的核心逻辑是通过ADC将传感器模拟信号转换为数字值,对比预设阈值控制水泵,同时通过NB-IoT实现数据的远程传输和控制;
- 调试过程中需重点关注硬件接线(共地、供电)、NB-IoT模块的网络注册、云端平台的参数配置,这三个环节是系统正常运行的关键。