ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

基于STM32的WiFi智能鱼缸监控系统:从传感器采集到远程投喂的完整实现

基于STM32的WiFi智能鱼缸监控系统:从传感器采集到远程投喂的完整实现 1. 鱼缸这件小事为什么值得用一块 STM32 认真对待养鱼这件事外行看是“买个缸、倒点水、撒把饲料”内行才知道它本质上是一套持续运行的微型环境控制系统。水温波动超过两三度热带鱼就开始应激水位因为蒸发悄悄下降加热棒露出水面干烧轻则跳闸重则炸管出差三天没人投喂回来一缸鱼翻肚皮。这些场景我身边养鱼的朋友几乎都遇到过而市面上的成品智能鱼缸要么贵得离谱要么功能阉割得只剩一个定时插座。所以“基于 STM32 的 WiFi 智能鱼缸监控系统”这个毕业设计题目看起来是个老生常谈的课设实际上它踩中了三个非常实在的需求点温度闭环、水位安全、远程投喂。它不追求花哨而是把一套真实可用的环境监控逻辑跑通这对电子、自动化、物联网方向的学生来说是一个能同时练到传感器采集、执行器驱动、无线通信、上位机交互的综合性项目。这篇文章我打算按“真做一遍”的思路来写不讲空泛的选题意义而是把选型逻辑、电路细节、代码结构、WiFi 联网的坑、投喂机构的机械设计这些真正卡人的地方拆开讲。适合正在做毕设的本科生、想复刻一个家用小系统的DIY爱好者也适合刚接触 STM32 想找一个完整项目练手的朋友。读完你应该能自己画出一版原理图、搭出一套能跑的原型并且知道哪些地方最容易翻车。提示本文涉及的 WiFi 通信仅指设备接入家庭局域网并与手机/服务器进行数据交互所有联网配置均在合法合规的私有网络环境下完成不涉及任何网络入侵或密码破解相关内容。2. 系统整体架构先把数据流想清楚再动手画板子2.1 从“感知—决策—执行—交互”四层拆解很多人拿到题目第一反应是打开 Keil 新建工程结果写了两周发现传感器读数和执行器逻辑搅在一起改一个地方崩三个地方。正确的做法是先画数据流图脑子里画就行不用真画把系统分成四层感知层DS18B20 水温传感器、HC-SR04 超声波水位传感器或者更简单的浮球开关、可选的光照传感器。决策层STM32 主控负责采集数据、判断阈值、驱动执行器、打包数据上传。执行层加热棒通过继电器控制、水泵换水/循环、舵机驱动的投喂机构。交互层ESP8266/ESP32 WiFi 模块 手机 APP 或网页端实现远程查看和手动控制。这个分层不是学术摆设它直接决定了你的代码结构。感知层的数据采集应该放在定时器中断或独立的任务里决策层的阈值判断放在主循环执行层的动作要带状态锁防止继电器频繁抖动交互层的数据收发用串口 DMA 环形缓冲区。我见过太多毕设代码把所有逻辑塞进while(1)结果 WiFi 一断线整个系统就卡死鱼缸直接失控。2.2 主控选型的现实考量F103 够不够用STM32F103C8T6 是这个题目的绝对主力原因很实际价格十块钱出头、资料铺天盖地、Keil 和 CubeMX 支持完善、GPIO 和定时器资源足够。有人会问要不要上 F407 或者 H7我的建议是没必要。这个系统里最重的计算任务是超声波测距的时间捕获和 WiFi 协议的串口收发F103 的 72MHz 主频和 3 个通用定时器完全扛得住。真正需要关注的是引脚分配。F103C8T6 只有 37 个可用 IO你要提前列一张表外设接口类型占用引脚备注DS18B20单总线PB12需要 4.7K 上拉HC-SR04GPIO 定时器捕获TRIGPB0, ECHOPA0(TIM2_CH1)捕获上升沿和下降沿继电器×2GPIO 推挽PB5, PB6低电平触发注意舵机PWMPA8(TIM1_CH1)50Hz, 0.5~2.5msESP8266USART2PA2(TX), PA3(RX)波特率 115200OLEDI2CPB8, PB90.96 寸 SSD1306按键×3GPIO 输入上拉PB13~PB15手动控制这张表建议你在画原理图之前就定死不然后面改引脚会连带改代码、改 PCB非常痛苦。2.3 供电设计别让继电器把 MCU 拉复位这是新手最容易踩的坑。继电器线圈吸合瞬间电流能到 70~100mA如果和 STM32 共用一路 3.3V LDO电压瞬间跌落会导致 MCU 复位表现就是“鱼缸一加热屏幕就重启”。正确做法是电源分域12V 输入加热棒和水泵用→ 降压到 5V继电器、舵机、ESP8266→ 再降压到 3.3VSTM32、传感器。继电器线圈两端并联续流二极管1N4148 或 1N4007吸收反向电动势。舵机和 MCU 的 5V 之间加一个 470uF 电解电容做储能缓冲。我实测过不加续流二极管的情况下继电器断开瞬间示波器能看到 40V 左右的尖峰虽然不一定每次都炸但长期运行芯片寿命会明显缩短。3. 温度与水位采集两个传感器的脾气你得摸透3.1 DS18B20 的时序容错与多点组网DS18B20 是单总线器件优点是只需要一根数据线缺点是时序极其敏感。它的通信靠微秒级的拉高拉低来区分 0 和 1如果中途被中断打断读出来的就是 85℃ 这个经典错误值。我在实际调试中总结了几个关键点第一关中断。在 DS18B20 的读写时序函数里进入前__disable_irq()出来后__enable_irq()尤其是你同时跑了定时器中断和串口中断的时候不关中断几乎必错。第二延时精度。用HAL_Delay()做微秒延时是不行的它的最小分辨率是 1ms。要么用__NOP()循环做粗略延时要么用 DWT 周期计数器做精确延时。我一般用后者// DWT 初始化后用这个做微秒延时 void delay_us(uint32_t us) { uint32_t start DWT-CYCCNT; uint32_t ticks us * (SystemCoreClock / 1000000); while ((DWT-CYCCNT - start) ticks); }第三上拉电阻。数据线必须接 4.7K 上拉到 3.3V太小功耗大太大上升沿变缓读不到数据。如果你要接多个 DS18B20比如同时测缸内和缸外温度每个器件的 64 位 ROM 地址不同需要先搜索 ROM 再逐个读取代码量会翻倍毕设里一般一个就够了。3.2 超声波测水位为什么我不推荐 HC-SR04 直接测HC-SR04 测距原理是发 40kHz 脉冲、等回波、算时间差。放在鱼缸里测水位有个致命问题水面会波动而且水蒸气会在探头上凝结。我试过把 HC-SR04 架在缸口上方往下打读数在 ±3cm 范围内跳根本没法做阈值判断。更靠谱的方案有两个方案一浮球开关 分段检测。在缸壁不同高度装 2~3 个浮球开关分别对应“低水位报警”“正常”“高水位停止加水”。成本几块钱可靠性极高缺点是只能测离散的几个点不能显示连续水位。方案二压力传感器测水压。在缸底放一个 MS5837 这类水深传感器通过水压反推水位高度精度能到毫米级。缺点是价格贵几十块而且要防水封装。对于毕设我建议用方案一 超声波做辅助显示。浮球开关负责安全联锁低水位强制关加热棒超声波负责在 OLED 上显示一个大概的水位百分比两者互补。这样既保证了安全性又有数据可视化的亮点。3.3 传感器数据的滤波处理原始数据不能直接用。DS18B20 偶尔会蹦出一个 85 或者 0超声波会有野值。我一般用中值滤波 滑动平均的组合#define FILTER_LEN 5 float temp_buf[FILTER_LEN]; float filter_temp(float new_val) { // 移位 for (int i 0; i FILTER_LEN - 1; i) temp_buf[i] temp_buf[i 1]; temp_buf[FILTER_LEN - 1] new_val; // 冒泡排序取中值 float tmp[FILTER_LEN]; memcpy(tmp, temp_buf, sizeof(tmp)); for (int i 0; i FILTER_LEN - 1; i) for (int j 0; j FILTER_LEN - 1 - i; j) if (tmp[j] tmp[j 1]) { float t tmp[j]; tmp[j] tmp[j 1]; tmp[j 1] t; } return tmp[FILTER_LEN / 2]; }中值滤波能干掉突发的野值滑动平均让曲线更平滑。实测下来温度读数波动从 ±0.5℃ 降到 ±0.1℃水位显示也不再乱跳。4. 投喂机构机械结构比代码更容易翻车4.1 舵机方案 vs 步进电机方案投喂机构的核心是“定量、定时、不卡料”。我见过三种做法舵机旋转挡板舵机转 45 度打开一个出料口饲料靠重力落下。结构简单但出料量靠开口大小和时间控制不太精确。步进电机驱动螺旋杆像绞肉机一样把饲料推出来精度高但需要额外的驱动模块ULN2003 或 A4988代码也复杂。舵机 转盘分格转盘上挖若干小格每格装一次的量舵机转一格投一份。这个方案精度最好机械加工要求也最高。毕设推荐舵机旋转挡板理由是代码简单、成本低、调试直观。舵机用 SG90 就够了扭矩 1.8kg·cm带动一个小挡板绰绰有余。4.2 舵机 PWM 的坑为什么你的舵机在抖SG90 的控制信号是 50Hz PWM高电平 0.5ms 对应 0 度2.5ms 对应 180 度。用 STM32 的 TIM1 输出 PWM配置如下// 72MHz / 72 1MHz, 周期 20000 20ms 50Hz htim1.Init.Prescaler 72 - 1; htim1.Init.Period 20000 - 1; // 0度: CCR 500, 180度: CCR 2500 __HAL_TIM_SET_COMPARE(htim1, TIM_CHANNEL_1, 500);常见问题是舵机抖动或者不动原因通常是电源不够。舵机堵转电流能到 700mA如果直接从 STM32 的 3.3V 取电必抖。必须用独立的 5V 供电并且和 MCU 共地。PWM 频率不对。有人用 1kHz 去驱动舵机内部的控制芯片根本识别不了。信号线太长。超过 30cm 建议加一个 100Ω 电阻做阻抗匹配。4.3 防卡料与缺料检测饲料受潮会结块这是实际使用中最常见的问题。我在出料口加了一个红外对管检测是否有饲料落下。如果舵机动作后 2 秒内没有检测到落料就再转一次连续三次失败则上报“缺料/卡料”告警到手机端。这个逻辑不复杂但能极大提升系统的可用性。另外饲料仓建议做成可拆卸的密封盒里面放一小包干燥剂。这个细节跟代码无关但决定了你这个系统是“能用一次”还是“能用一个月”。5. WiFi 联网与远程交互ESP8266 的稳定通信实践5.1 AT 指令模式 vs 透传模式ESP8266 和 STM32 配合有两种主流方式AT 指令模式STM32 通过串口发 AT 指令控制 ESP8266灵活但代码量大要处理各种返回值和超时。透传模式ESP8266 烧录固件后自动连接服务器STM32 只管往串口发数据模块自动转发。代码简单但灵活性差。毕设推荐AT 指令模式因为你能在代码里体现“协议解析”这个工作量答辩时更好讲。核心流程是ATCWMODE1设置为 Station 模式。ATCWJAPSSID,PASSWORD连接家庭路由器。ATCIPSTARTTCP,服务器IP,端口建立 TCP 连接。ATCIPSEND长度然后发送数据。每一步都要等OK返回超时重试。我一般给每个指令 3 秒超时重试 3 次失败就重启模块。5.2 串口数据收发的环形缓冲区设计STM32 和 ESP8266 之间是高速串口通信115200如果用查询方式接收主循环会被拖慢。正确做法是串口中断 环形缓冲区#define RX_BUF_SIZE 256 uint8_t rx_buf[RX_BUF_SIZE]; volatile uint16_t rx_head 0, rx_tail 0; void USART2_IRQHandler(void) { if (__HAL_UART_GET_FLAG(huart2, UART_FLAG_RXNE)) { uint8_t data (uint8_t)(huart2.Instance-DR 0xFF); uint16_t next (rx_head 1) % RX_BUF_SIZE; if (next ! rx_tail) { rx_buf[rx_head] data; rx_head next; } } } // 主循环里解析 int available(void) { return (rx_head - rx_tail RX_BUF_SIZE) % RX_BUF_SIZE; } int read_byte(void) { if (rx_head rx_tail) return -1; uint8_t d rx_buf[rx_tail]; rx_tail (rx_tail 1) % RX_BUF_SIZE; return d; }这个结构能保证即使主循环在忙别的串口数据也不会丢。解析 AT 返回值时用状态机逐字节匹配OK、ERROR、IPD这些关键字。5.3 断线重连与心跳机制WiFi 断线是常态不是异常。路由器重启、信号波动、模块发热都会导致 TCP 断开。系统必须能自动恢复心跳包每 30 秒往服务器发一个{type:heartbeat}服务器 60 秒没收到就认为设备离线。断线检测发送 AT 指令时如果连续 3 次超时判定为断线执行ATCIPCLOSE后重新ATCIPSTART。看门狗STM32 的独立看门狗IWDG设 4 秒超时主循环里定期喂狗。如果程序跑飞自动复位重连。我实测过加了这套机制后系统连续运行一周没有出现需要手动重启的情况。5.4 数据协议设计JSON 还是自定义格式和服务器通信的数据格式我建议用精简 JSON{t:26.5,w:80,feed:0,alarm:0}字段含义温度、水位百分比、投喂状态、告警标志。JSON 的好处是可读性强、服务器端解析方便Python 一行json.loads搞定缺点是比二进制多占几个字节。对于 30 秒发一次的数据量来说完全无所谓。服务器端可以用 Python Flask 写一个简单的 HTTP 接口手机端用微信小程序或者网页访问。如果不想搭服务器也可以用现成的物联网云平台但毕设建议自己写答辩时能体现完整的链路。6. 调试过程中那些让人抓狂的真实问题6.1 程序下载后不运行复位才跑这个问题的经典原因是BOOT0 引脚悬空或者被拉高。STM32F103 的 BOOT0 决定启动模式如果它在上电时是高电平芯片会进入系统存储器启动模式不执行你的代码。解决方法是 BOOT0 接 10K 下拉电阻到 GND或者直接接地。另一个原因是复位电路电容太大。有些开发板用 10uF 的复位电容上电时复位引脚需要很长时间才能升到高电平导致芯片启动异常。标准值应该是 100nF。6.2 串口打印乱码九成是波特率不匹配。检查三个地方代码里huart.Init.BaudRate、串口助手设置、系统时钟配置。如果用的是外部晶振还要确认HSE_VALUE宏定义和实际晶振频率一致8MHz 还是 12MHz。我遇到过一次板子上焊的是 12MHz 晶振代码里写的是 8MHz结果所有串口数据都是乱码查了一下午。6.3 继电器吸合导致 OLED 花屏这是 I2C 总线被干扰的典型表现。继电器动作时产生的电磁干扰耦合到了 I2C 的 SCL/SDA 线上。解决方法I2C 线尽量短远离继电器和加热棒的走线。SCL/SDA 各加一个 100Ω 电阻串联再各加 4.7K 上拉。继电器模块和 MCU 之间用光耦隔离PC817彻底切断电气连接。6.4 WiFi 模块发热严重ESP8266 在持续发送数据时电流能到 170mA如果用的是 AMS1117 这种线性稳压器压差大的话发热非常明显。建议5V 转 3.3V 用 MP1584 这类开关电源模块效率高、发热小。模块背面贴一块小散热片。如果只是间歇性发送数据可以在发送间隙让模块进入ATSLEEP模式。7. 从能跑到好用几个提升完成度的细节7.1 OLED 界面的信息层级0.96 寸 OLED 只有 128×64 像素能显示 4 行 16 字。我建议这样布局第一行当前温度 目标温度范围。第二行水位百分比 状态正常/低/高。第三行WiFi 连接状态 服务器连接状态。第四行下次投喂倒计时。按键用来切换手动/自动模式、手动触发投喂、设置温度阈值。界面不用花哨信息一目了然就行。7.2 掉电保存与参数恢复温度阈值、投喂时间这些参数如果存在 RAM 里断电就丢了。用 STM32 内部的 Flash 模拟 EEPROM或者外挂一个 AT24C02。我一般用内部 Flash 的最后一页写一个简单的键值对存储typedef struct { float temp_min; float temp_max; uint8_t feed_hour; uint8_t feed_min; uint32_t magic; // 0x5A5A5A5A 用于判断是否已初始化 } Config_t;上电时先读如果 magic 不对就用默认值。注意 Flash 写入前要先擦除整页写入次数有限约 1 万次不要频繁写。7.3 外壳与防水处理电子部分做完只是成功了一半鱼缸环境是高湿度的。我的做法是主控板和电源板装在一个防水接线盒里出线孔用防水接头。温度传感器用不锈钢探头封装的 DS18B20直接扔水里。超声波模块朝下的一面涂一层三防漆防止水汽凝结。舵机投喂机构放在缸盖上方避免接触水面。这些细节看起来跟“技术”无关但决定了你的系统能不能在真实环境里活过一周。7.4 答辩时怎么讲这个项目最后说点实际的。答辩老师最关心的不是你用了多少模块而是你为什么这么选、遇到了什么问题、怎么解决的。所以你的 PPT 里应该有一张系统框图体现四层架构。一张传感器数据曲线展示滤波前后的对比。一段断线重连的日志证明系统有容错能力。一个投喂机构的实物照片或视频。代码量不是重点逻辑清晰、有实测数据、能回答“如果 WiFi 断了怎么办”“如果传感器坏了怎么办”这类问题才是拿高分的关键。我个人在做这类环境监控系统时最大的体会是硬件的可靠性永远排在功能之前。一个只能测温度但从不死机的系统比一个功能齐全但三天两头重启的系统有价值得多。所以先把电源、复位、看门狗这些基础打牢再去堆功能这个顺序不能反。
返回列表