智能农业灌溉:STM32+NB-IoT+土壤湿度传感器,自动控制实战

文章目录

一、项目概述

本项目旨在搭建一套基于STM32单片机、NB-IoT模块和土壤湿度传感器的智能农业灌溉系统。系统可实时采集土壤湿度数据,通过NB-IoT将数据上传至云端平台,同时根据预设湿度阈值自动控制水泵的启停,实现无人值守的精准灌溉。整个系统具备低功耗、远距离传输、自动化控制的特点,完全适配农业大田、温室大棚等场景的实际需求。

二、硬件选型与原理

2.1 核心硬件清单

硬件名称 型号/规格 作用说明
STM32单片机 STM32F103C8T6(最小系统板) 核心控制单元,处理数据和指令
土壤湿度传感器 FC-28(模拟量输出) 采集土壤湿度模拟信号
NB-IoT模块 BC28(带天线、串口通信) 数据上传云端、接收远程指令
水泵 5V微型隔膜泵 执行灌溉动作
继电器模块 5V单路继电器(高电平触发) 隔离单片机与水泵,控制通断
电源模块 12V转5V/3.3V稳压模块 为各模块供电
杜邦线、面包板 公对公/公对母 电路连接

2.2 核心原理

  1. 土壤湿度传感器将土壤湿度转化为0-3.3V的模拟电压信号,输入STM32的ADC引脚;
  2. STM32通过ADC采集电压值并转换为湿度百分比,与预设阈值对比:低于阈值则控制继电器吸合,启动水泵灌溉;高于阈值则断开继电器,停止灌溉;
  3. STM32通过串口与NB-IoT模块通信,将湿度数据、水泵状态上传至云端;
  4. 云端可下发远程控制指令,修改湿度阈值或手动控制水泵启停。

三、硬件接线

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 接线注意事项

  1. 所有模块必须共地(GND相连),否则会出现信号紊乱;
  2. BC28模块需单独接天线,且避免金属遮挡,保证NB-IoT信号强度;
  3. 水泵需通过继电器控制,禁止直接接STM32引脚(电流过大易烧毁单片机);
  4. 电源模块输出电流需≥2A,满足水泵、各模块的供电需求。

四、软件开发环境搭建

4.1 软件工具清单

  1. STM32CubeMX(版本6.8.1):用于配置STM32引脚、时钟、外设,生成初始化代码;
  2. Keil MDK-ARM(版本5.38):用于编写、编译代码,下载程序到STM32;
  3. 串口助手(如SSCOM):用于调试串口通信,查看传感器数据和NB-IoT模块反馈;
  4. NB-IoT云端平台(如中国移动OneNET):用于接收数据、下发指令。

4.2 环境搭建步骤

步骤1:安装STM32CubeMX
  1. 下载STM32CubeMX安装包(官网:https://www.st.com/en/development-tools/stm32cubemx.html);
  2. 安装过程中选择默认路径,完成后安装对应的STM32F1系列固件库(STM32Cube FW_F1 V1.8.5);
  3. 验证安装:打开STM32CubeMX,选择STM32F103C8T6芯片,能正常进入配置界面即完成。
步骤2:安装Keil MDK-ARM
  1. 下载MDK5.38安装包,安装时选择支持STM32F1系列的编译器;
  2. 安装STM32F1的器件库(Device Pack),版本≥2.3.0;
  3. 破解Keil(仅用于学习),确保能编译超过2KB的代码;
  4. 验证:新建工程,选择STM32F103C8T6,能正常编译空工程即完成。
步骤3:配置串口助手
  1. 下载SSCOM串口助手,安装后打开;
  2. 连接STM32的USB转串口模块到电脑,在设备管理器中查看串口号(如COM3);
  3. 串口助手配置:波特率9600、数据位8、停止位1、校验位无、流控无,点击"打开串口"。

五、STM32CubeMX配置

5.1 新建工程

  1. 打开STM32CubeMX,点击"New Project";
  2. 在搜索框输入"STM32F103C8T6",选择对应芯片,点击"Start Project";
  3. 配置RCC(时钟):选择"HSE"(外部高速时钟),晶振8MHz,时钟树配置为72MHz(STM32F103最大主频)。

5.2 外设配置

步骤1:配置ADC(土壤湿度采集)
  1. 点击"ADC1",选择"IN0"(对应PA0引脚);
  2. 配置ADC参数:分辨率12位、转换模式单次转换、扫描模式关闭;
  3. 开启ADC中断(可选,也可轮询采集)。
步骤2:配置GPIO(水泵控制)
  1. 点击"PA1",设置为"GPIO_Output"(输出模式);
  2. 配置默认电平:低电平(水泵默认关闭);
  3. 输出模式:推挽输出、速度50MHz。
步骤3:配置USART1(NB-IoT通信)
  1. 点击"USART1",设置为"Asynchronous"(异步模式);
  2. 配置参数:波特率9600、数据位8、停止位1、校验位无;
  3. 开启USART1中断(用于接收NB-IoT模块的数据)。
步骤4:生成代码
  1. 点击"Project Manager",设置工程名称、保存路径,选择"MDK-ARM v5";
  2. 点击"Code Generator",勾选"Generate peripheral initialization as a pair of '.c/.h' files per peripheral";
  3. 点击"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 代码关键部分解释

  1. ADC采集逻辑 :STM32的12位ADC将0-3.3V的模拟电压转换为0-4095的数字值,通过公式湿度百分比 = (ADC值/4095)*100将电压值转换为直观的湿度百分比;
  2. 水泵控制:通过对比实时湿度与预设阈值,控制PA1引脚的高低电平,进而控制继电器的吸合/断开,实现水泵的自动启停;
  3. NB-IoT通信:通过USART1向BC28模块发送AT指令,将湿度、水泵状态封装为JSON格式上传至OneNET云端;
  4. 串口调试:所有关键数据(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 创建设备

  1. 登录OneNET官网(https://open.iot.10086.cn/),进入"开发者中心";
  2. 新建产品,选择"NB-IoT"协议,填写产品名称、描述;
  3. 在产品下创建设备,记录设备ID、产品ID、鉴权信息(用于BC28模块连接)。

8.2 数据展示配置

  1. 进入设备的"数据流模板",添加两个数据流:humidity(湿度,整型)、pump_status(水泵状态,整型);
  2. 配置"应用管理",添加可视化面板,展示湿度曲线、水泵状态开关;
  3. 配置"触发器":当湿度低于阈值时,发送短信/APP推送告警。

8.3 远程控制配置

  1. 在"设备控制"中添加"写属性"指令,支持修改HUMIDITY_LOW_THRESHOLDHUMIDITY_HIGH_THRESHOLD
  2. 配置手动控制水泵的指令,下发pump_control=1(启动)或pump_control=0(停止)。

九、系统调试与测试

9.1 硬件调试

  1. 断开水泵,仅连接传感器、STM32、NB-IoT模块,上电后查看串口助手是否有ADC数据输出;
  2. 用手捏住土壤湿度传感器的探头(模拟高湿度),查看湿度百分比是否上升,水泵状态是否为关闭;
  3. 将传感器探头放入干燥土壤(模拟低湿度),查看湿度百分比是否下降,水泵状态是否为开启;
  4. 检查NB-IoT模块的信号指示灯:常亮表示已注册网络,闪烁表示数据传输中。

9.2 软件调试

  1. 修改代码中的湿度阈值(如将下限改为20,上限改为60),重新编译下载,验证水泵控制逻辑是否生效;
  2. 在OneNET平台查看数据是否实时上传,曲线是否与串口数据一致;
  3. 从云端下发远程控制指令,修改阈值,查看STM32是否接收并执行。

9.3 现场测试

  1. 将系统安装至温室大棚/大田,固定传感器探头(埋入土壤10cm深处);
  2. 连续运行24小时,记录湿度数据和水泵启停次数,验证系统稳定性;
  3. 测试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 低功耗优化

  1. STM32配置睡眠模式,仅在采集数据时唤醒,其余时间休眠;
  2. BC28模块配置为"按需唤醒",减少待机功耗;
  3. 采用太阳能电池板+锂电池供电,适配无市电的农田场景。

11.2 功能扩展

  1. 添加温度传感器(DS18B20),采集土壤温度,实现温湿度双参数监控;
  2. 添加雨量传感器,雨天自动停止灌溉;
  3. 接入多个传感器,实现多区域灌溉控制;
  4. 开发手机APP,实时查看数据、远程控制。

总结

  1. 本智能农业灌溉系统以STM32为核心,结合土壤湿度传感器、NB-IoT模块,实现了土壤湿度采集、水泵自动控制、数据云端上传的全流程功能,代码可直接复制使用,零基础小白可按步骤完成硬件接线和软件调试;
  2. 系统的核心逻辑是通过ADC将传感器模拟信号转换为数字值,对比预设阈值控制水泵,同时通过NB-IoT实现数据的远程传输和控制;
  3. 调试过程中需重点关注硬件接线(共地、供电)、NB-IoT模块的网络注册、云端平台的参数配置,这三个环节是系统正常运行的关键。
相关推荐
光子物联单片机2 小时前
STM32传感器模块编程实践(十八)DIY电子游戏机模型
stm32·单片机·嵌入式硬件
古译汉书2 小时前
【IoT死磕系列】Day 3:学习HTTP!实战:STM32手写GET请求获取天气实战(附源码+八股文)
数据结构·stm32·物联网·网络协议·学习·算法·http
速易达网络3 小时前
物联网仿真教学平台
物联网
風清掦3 小时前
【江科大STM32学习笔记-06】TIM 定时器 - 6.2 定时器的输出比较功能
笔记·stm32·单片机·嵌入式硬件·学习
uran11 小时前
从电磁兼容到代码优化:STM32 GPIO速度与EMI的隐秘关联
stm32·gpio·emc·嵌入式优化
想放学的刺客11 小时前
整理了120道单片机嵌入式面试题与答案,覆盖了硬件电路和C语言等核心领域。
c语言·c++·stm32·单片机·嵌入式硬件·mcu·51单片机
Decksweeper11 小时前
【大一做毕设?十二天四人从零开始手搓智能小车!(基于rdk x5、stm32与yolo v5,小车可实现巡线,避障,识别与夹取特定物块)】
stm32·嵌入式硬件·yolo·课程设计
回不去的bug11 小时前
【STM32】玩转IIC之驱动MPU6050及姿态解算
stm32·单片机·嵌入式硬件·mpu6050
seefire12 小时前
昆仑通态触摸屏物联网远程运维McgsIot
物联网