基于STM32的水质监测系统:从传感器采样到GSM短信告警的完整实现
2026/9/11 12:46:15 网站建设 项目流程

简介:一份基于STM32单片机的水质监测系统设计资料包,面向电子、嵌入式与自动化专业学生的毕业设计或课程设计,解决水质监测项目从硬件电路到软件程序实现的完整流程。资源包大小为981.8MB,包含系统原理图、PCB图、完整源代码、讲解视频以及设计论文,覆盖电路设计、程序调试和论文撰写的核心环节。内容深入介绍了STM32单片机模块、浑浊度检测传感器、PH传感器、温度检测、GSM无线传输、LCD1602显示和声光告警等模块的工作原理与设计方法,说明了如何利用片内A/D转换器将传感器模拟信号转为数字信号,并由单片机处理后进行显示与异常告警。该设计能够实时监测水温、PH值和浑浊度,指标超标时通过GSM短信通知,兼具实用性与扩展性。目前已有210人学习查看,适合需要快速入手水质监测系统设计的读者参考。

1. 水质监测系统拆解:从传感器采样到GSM告警的完整硬件链路

鱼塘增氧机、水产养殖基地或小型水站的现场监控,最怕的不是数据不准,而是异常数据没能及时送到负责人手里。基于STM32单片机的水质监测系统把浑浊度、PH值、水温三路采样做到一块板上,配合LCD1602本地显示和GSM短信告警,属于典型的“感知—处理—上报”闭环。这套设计的思路比具体型号更有参考价值:模拟前端怎么接才能让ADC读数稳得住,告警阈值放在哪里比较合适,GSM模块的电源噪声如何不污染模拟采样。无论是做课程设计还是改造成产品原型,把这几条链路理清楚,后续迭代会省很多时间。

2. 浑浊度/PH/温度三路信号链路与STM32 ADC采样参数设计

2.1 传感器输出特性与选型逻辑

水质监测的核心指标有三种物理量,对应三类传感器接口。浑浊度传感器利用红外光电对管,光源穿过水体后,接收管收到的光强随悬浮颗粒浓度变化,输出为模拟电压。常见的SP201型传感器在纯净水中输出电压较低,浑浊度升高时电压随之上升,典型输出范围在0.4V到4.5V之间。这个电压范围恰好落进STM32 ADC的输入区间,不需要额外做电平转换。

PH传感器是另一个思路。PH复合电极输出的是毫伏级电压信号,且电极内阻极高,直接接入ADC会因源阻抗过高导致采样值严重跳动。常规做法是用运算放大器搭一个高输入阻抗的前置跟随器,把电极信号缓冲后再进行电压平移和放大。考虑到PH值与电压呈线性关系,标准缓冲液标定时每pH单位对应约59.16mV(25℃),所以运放级的放大倍数和偏置电压决定了ADC读数的分辨率。温度检测则选择DS18B20数字传感器,走单总线协议,不占用ADC通道,避免了模拟信号长线传输带来的干扰问题。

三路信号中,ADC只处理浑浊度和PH两路模拟量,温度走数字接口。这种混合采集方式在中小型监测设备里很常见,数字传感器把温漂问题在源头解决,模拟通道则集中精力处理两个最容易受干扰的物理量。

2.2 STM32内部ADC的配置要点

STM32F103系列内部集成了12位逐次逼近型ADC,最大转换速率为1MHz,但实际工程中很少有人把采样速度跑到极限。原因是传感器输出的是缓变信号,采样速率过快反而会把高频噪声引入数据中。设计时合理做法是将ADC时钟配置在12MHz以内(APB2时钟分频得到),采样时间选择较长的档位,让采样电容充分充电。

以下是通过STM32CubeMX生成的ADC1初始化代码,用作单通道采集配置:

ADC_HandleTypeDef hadc1; void MX_ADC1_Init(void) { hadc1.Instance = ADC1; hadc1.Init.ScanConvMode = DISABLE; // 单通道模式,不使用扫描 hadc1.Init.ContinuousConvMode = DISABLE; // 单次转换,软件触发 hadc1.Init.DiscontinuousConvMode = DISABLE; hadc1.Init.ExternalTrigConv = ADC_SOFTWARE_START; // 软件启动转换 hadc1.Init.DataAlign = ADC_RIGHTALIGN; // 右对齐,方便读取 hadc1.Init.NbrOfConversion = 1; // 转换通道数 HAL_ADC_Init(&hadc1); }

配置中把ContinuousConvMode设为DISABLE,目的是每次转换都由软件主动发起。这样的好处是数据读取节奏可控,在轮询采样时可以配合逐次滤波算法。ExternalTrigConv选择软件触发,避免外部定时器触发引入的不确定性。右对齐模式让ADC的12位数据直接存放在寄存器低12位,读取后无需移位处理。

那滤波怎么做呢?连续采集16次取平均是最简单有效的方法。ADC转换结果本身带有量化噪声,而传感器输出叠加的随机噪声在多次采样后会相互抵消:

uint32_t read_adc_filtered(ADC_HandleTypeDef *hadc) { uint32_t sum = 0; uint8_t i; for (i = 0; i < 16; i++) { HAL_ADC_Start(hadc); HAL_ADC_PollForConversion(hadc, 100); // 等待转换完成,超时100ms sum += HAL_ADC_GetValue(hadc); } return sum / 16; // 返回平均值,等效于低通滤波 }

取16次平均不是拍脑袋定的数值。如果取太少,滤波效果不明显;取太多,比如64次以上,每次采样的周期变长,系统响应速度下降。16次在大多数水质监测场景中都能兼顾响应速度和噪声抑制,计算量也很小,不需要动用DSP库。

2.3 浑浊度与PH的软件标定方法

ADC读到的数字量不是最终显示值,需要一层线性映射函数。浑浊度传感器的输出电压与NTU(浊度单位)之间存在近似线性关系,常见做法是在传感器数据手册给出的曲线上取两个端点做线性插值。这里给出一个简洁的映射公式作为工程参考:

float turbidity_calc(uint16_t adc_val) { // 假设0NTU对应0.4V,1000NTU对应4.5V,12位ADC参考电压3.3V float voltage = (float)adc_val * 3.3f / 4095.0f; float ntu = (voltage - 0.4f) * 1000.0f / (4.5f - 0.4f); if (ntu < 0.0f) ntu = 0.0f; return ntu; }

PH值的标定则稍有不同。PH电极的电压输出受温度影响,严格的系统会在标定公式中加入温度补偿系数。多数课程设计项目采用两点标定法:用PH=4.0和PH=6.86的缓冲液分别记录ADC原始值,得到斜率和截距,再代入测量电压解出当前PH值。这样做的好处是能抵消运放偏置电压和ADC参考电压的误差。

本章涉及的所有传感器信号处理逻辑,在配套的源代码工程里都有对应实现。原理图和PCB图则展示了模拟前端电路的具体连接方式。

3. LCD1602显示与阈值判定:从ADC原始值到本地人机交互

3.1 四线模式接线与驱动时序

LCD1602显示模块在系统里承担本地数据显示功能,实时展示当前的水温、浑浊度、PH值以及告警状态。1602支持8线和4线两种并行接口模式,8线模式需要占用PB0-PB7共8根IO,4线模式只占用D4-D7四根数据线加RS、EN两根控制线,共6个GPIO。对于F103C8T6这种48引脚封装的芯片,引脚资源紧凑,4线模式是更合理的选择。

本设计的接线分配如下表所示:

LCD1602引脚STM32 GPIO功能说明
VSS/VDDGND/5V供电,背光可接PWM控制
V0电位器中点对比度调节
RSPB11寄存器选择:0写命令,1写数据
ENPB12使能信号,下降沿锁存数据
D4-D7PB8-PB10/PB13数据线,4位数据时分复用

3.2 初始化与数据写入的时序实现

4线模式下写一个字节需要分两次操作:先送高4位,再送低4位,每次都要拉高EN引脚产生下降沿锁存数据。初始化序列也比8线模式复杂,需要按照HD44780控制器的时序要求逐条执行。典型的初始化流程如下:

void lcd_write_nibble(uint8_t nibble) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_8, (nibble & 0x01) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_9, (nibble & 0x02) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_10, (nibble & 0x04) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_13, (nibble & 0x08) ? GPIO_PIN_SET : GPIO_PIN_RESET); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_SET); // EN拉高 HAL_Delay(1); HAL_GPIO_WritePin(GPIOB, GPIO_PIN_12, GPIO_PIN_RESET); // EN拉低,下降沿锁存 } void lcd_write_cmd(uint8_t cmd) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_RESET); // RS=0 写命令 lcd_write_nibble(cmd >> 4); // 高四位 lcd_write_nibble(cmd & 0x0F); // 低四位 HAL_Delay(2); } void lcd_write_data(uint8_t data) { HAL_GPIO_WritePin(GPIOB, GPIO_PIN_11, GPIO_PIN_SET); // RS=1 写数据 lcd_write_nibble(data >> 4); lcd_write_nibble(data & 0x0F); HAL_Delay(2); }

这里的时序逻辑需要说明一下:RS引脚的电平决定了当前通信类型,RS为低时写入的是命令字节,RS为高时写入的是显示数据。EN引脚的下降沿是锁存时刻,所以每次写数据前必须确认EN处于低电平,然后拉高再拉低,形成完整的脉冲。HAL_Delay(1)的时间用于满足HD44780的最小时序要求,如果主频提高,这个延时要适当延长。

3.3 告警阈值的设定与分段判定逻辑

水质监测的意义在于及时发现异常。本系统对三个指标分别设置了阈值,超过阈值时触发声光告警,同时组装GSM短信内容。阈值判定的代码结构如下:

typedef struct { float ph_min; float ph_max; float turbidity_max; float temp_min; float temp_max; } water_threshold_t; water_threshold_t threshold = {6.5f, 8.5f, 50.0f, 5.0f, 35.0f}; void water_quality_judge(water_data_t *data) { uint8_t alarm_flag = 0; if (data->ph < threshold.ph_min ||>void sim800c_init(void) { uint8_t buffer[64]; // 等待模块上电稳定 HAL_Delay(3000); // AT指令测试通信 send_at_command("AT\r\n", buffer, 1000); // 关闭回显,减少串口数据量 send_at_command("ATE0\r\n", buffer, 1000); // 设置为文本模式 send_at_command("AT+CMGF=1\r\n", buffer, 1000); // 设置字符集为GSM编码,避免中文乱码 send_at_command("AT+CSCS=\"GSM\"\r\n", buffer, 1000); }

短信发送的核心动作是先发送目标手机号指令,等模块返回>提示符后,再发送短信内容并以十六进制的0x1A(Ctrl+Z)结尾。实现代码如下:

void sim800c_send_sms(uint8_t *phone, uint8_t *message) { uint8_t cmd[64]; uint8_t tx_buffer[128]; uint16_t len; // 组装AT+CMGS指令 sprintf((char *)cmd, "AT+CMGS=\"%s\"\r\n", phone); send_at_command(cmd, tx_buffer, 500); // 等待模块返回'>'字符 HAL_UART_Receive(&huart2, tx_buffer, 1, 2000); // 发送短信内容 len = strlen((char *)message); HAL_UART_Transmit(&huart2, message, len, 1000); // 发送结束符0x1A HAL_UART_Transmit(&huart2, (uint8_t *)"\x1A", 1, 1000); // 等待发送完成 HAL_Delay(3000); }

AT+CMGF=1设置的是文本模式,AT+CSCS设置字符集。注意短信内容的编码问题:如果发送纯ASCII字符,文本模式足够;如果要发送中文,则必须使用UCS2编码,把中文字符转换成十六进制Unicode码,文本模式直接发中文会出现乱码。常见做法是在程序中内置一个编码转换函数,或直接把告警内容设计为英文缩写加数字的形式。

4.3 告警场景下的发送时序与状态机

GSM短信发送是一个慢操作,从模块唤醒到短信送达运营商服务器,耗时可能超过10秒。如果把这个操作放在传感器采样循环里同步执行,整个系统的实时性会被拖垮。所以代码里设置了告警标志位,主循环检测到标志后进入短信发送状态机,发送过程中不阻塞传感器采样。

以下表格展示了告警链路中常见的问题和对应排查手段:

现象可能原因排查与处理
AT指令无响应模块未开机或TX/RX接反检查PWRKEY是否拉低1秒,核对USART2引脚
短信发送失败供电跌落导致模块重启示波器观测VCC波形,加大储能电容至1000μF
中文短信乱码字符集设置错误改用UCS2编码,或发送ASCII内容
本地显示正常但无短信未注册网络观察NET_LED指示,SIM卡是否有PIN码锁定

4.4 低功耗处理

系统在正常工作状态下,GSM模块长时间待机是不必要的,既增加功耗也占用网络资源。常见做法是在无告警时让模块进入休眠模式,唤醒后再恢复通信。除用户自身操作外,还可以在电源设计上为GSM模块增加独立的MOS管开关控制,平时完全断电,告警时才上电发送短信。这个方案虽然增加了硬件成本,但把休眠期的待机电流从毫安级降到微安级,对电池供电的场景更有实际意义。

5. 电源树与PCB布局校验:低噪声地线与告警电路的供电设计

5.1 电源分配与模拟地数字地处理

整个系统的电源树从12V适配器输入开始,一路通过降压模块得到5V给LCD和传感器供电,另一路经过AMS1117-3.3稳压得到3.3V给STM32和GSM模块。AMS1117虽然廉价可靠,但它的压差要求较高,输入电压不能低于4.5V。如果系统改用电池供电,低功耗的降压方案应该换成RT8059这类同步降压芯片。

PCB布局上,水质监测系统对噪声比较敏感,ADC参考电压的微小波动会直接反映到显示数据上。设计时要把模拟地(AGND)和数字地(DGND)分开铺设,在电源入口处用0欧电阻或磁珠做单点连接。浑浊度传感器的模拟信号走线要远离GSM模块的射频电路,最好用地线包裹起来做屏蔽。采样电路的参考电压从AMS1117的输出端单独引线,让VREF+引脚与主电源轨之间做一个RC低通滤波,典型值为10Ω串联电阻加1μF对地电容。

此外需注意PH传感器是高阻信号源,其信号线如果走线过长,在PCB上容易拾取工频干扰。设计时把运放电路放在PH接口附近,尽量缩短模拟信号的走线长度。运放电源引脚上并联0.1μF和10μF去耦电容也很重要,0.1μF负责滤除高频干扰,10μF负责稳定低频供电。

5.2 用示波器验证GSM发射时的电源跌落

检查GSM供电是否达标,只是大概率判断。更可靠的方法是用示波器实测:让系统进入告警状态,触发短信发送,同时观察3.3V电源轨的波形。GSM模块在发射瞬间会从电源吸取大电流,如果波形跌落幅度超过200mV,就说明电源内阻偏大,需要增加输入电容或者加宽电源走线。

5.3 独立看门狗保障连续运行

监测设备常在无人值守的场合运行,程序跑飞后如果不能自动恢复,告警功能就会静默失效。实用的方案是开启STM32的独立看门狗(IWDG),在主循环的固定位置喂狗,同时在初始化阶段读取复位标志,判断上次复位是上电复位还是看门狗复位:

void check_reset_reason(void) { if (__HAL_RCC_GET_FLAG(RCC_FLAG_IWDGRST) != RESET) { // 看门狗复位,记录到Flash,便于现场排查 write_log_to_flash(LOG_IWDG_RESET); __HAL_RCC_CLEAR_RESET_FLAGS(); } }

这个细节容易被忽略,但在布点调试时非常管用。产品部署后如果频繁复位,结合日志能快速区分是程序逻辑问题还是外部干扰问题。另外提示一点:check_reset_reason需要在系统初始化早期调用,一旦调用外设库函数,复位标志可能被清除,就查不到原因了。

系统全部调试完毕后,可以通过一个简单场景验证整体功能:把浑浊度传感器放入清水中,记录ADC值和显示浑浊度,然后向水中滴入少量牛奶并搅拌,观察浑浊度数值是否上升,超过阈值后蜂鸣器是否鸣叫,GSM模块是否能收到报警短信。这个验证过程能覆盖信号采集、数据处理、显示、告警、无线传输的完整链路。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询