基于STM32的水质监测系统:从传感器采样到GSM短信告警的完整实现
简介一份基于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.16mV25℃所以运放级的放大倍数和偏置电压决定了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.4V1000NTU对应4.5V12位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电极的电压输出受温度影响严格的系统会在标定公式中加入温度补偿系数。多数课程设计项目采用两点标定法用PH4.0和PH6.86的缓冲液分别记录ADC原始值得到斜率和截距再代入测量电压解出当前PH值。这样做的好处是能抵消运放偏置电压和ADC参考电压的误差。本章涉及的所有传感器信号处理逻辑在配套的源代码工程里都有对应实现。原理图和PCB图则展示了模拟前端电路的具体连接方式。3. LCD1602显示与阈值判定从ADC原始值到本地人机交互3.1 四线模式接线与驱动时序LCD1602显示模块在系统里承担本地数据显示功能实时展示当前的水温、浑浊度、PH值以及告警状态。1602支持8线和4线两种并行接口模式8线模式需要占用PB0-PB7共8根IO4线模式只占用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); // RS0 写命令 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); // RS1 写数据 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(ATCMGF1\r\n, buffer, 1000); // 设置字符集为GSM编码避免中文乱码 send_at_command(ATCSCS\GSM\\r\n, buffer, 1000); }短信发送的核心动作是先发送目标手机号指令等模块返回提示符后再发送短信内容并以十六进制的0x1ACtrlZ结尾。实现代码如下void sim800c_send_sms(uint8_t *phone, uint8_t *message) { uint8_t cmd[64]; uint8_t tx_buffer[128]; uint16_t len; // 组装ATCMGS指令 sprintf((char *)cmd, ATCMGS\%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); }ATCMGF1设置的是文本模式ATCSCS设置字符集。注意短信内容的编码问题如果发送纯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模块是否能收到报警短信。这个验证过程能覆盖信号采集、数据处理、显示、告警、无线传输的完整链路。本文还有配套的精品资源点击获取