基于STM32的WiFi智能鱼缸监控系统:从传感器采集到远程投喂的完整实现
2026/9/23 18:20:08 网站建设 项目流程

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 + 定时器捕获TRIG=PB0, ECHO=PA0(TIM2_CH1)捕获上升沿和下降沿
继电器×2GPIO 推挽PB5, PB6低电平触发注意
舵机PWMPA8(TIM1_CH1)50Hz, 0.5~2.5ms
ESP8266USART2PA2(TX), PA3(RX)波特率 115200
OLEDI2CPB8, PB90.96 寸 SSD1306
按键×3GPIO 输入上拉PB13~PB15手动控制

这张表建议你在画原理图之前就定死,不然后面改引脚会连带改代码、改 PCB,非常痛苦。

2.3 供电设计:别让继电器把 MCU 拉复位

这是新手最容易踩的坑。继电器线圈吸合瞬间电流能到 70~100mA,如果和 STM32 共用一路 3.3V LDO,电压瞬间跌落会导致 MCU 复位,表现就是“鱼缸一加热,屏幕就重启”。正确做法是电源分域

  • 12V 输入(加热棒和水泵用)→ 降压到 5V(继电器、舵机、ESP8266)→ 再降压到 3.3V(STM32、传感器)。
  • 继电器线圈两端并联续流二极管(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 指令模式,因为你能在代码里体现“协议解析”这个工作量,答辩时更好讲。核心流程是:

  1. AT+CWMODE=1设置为 Station 模式。
  2. AT+CWJAP="SSID","PASSWORD"连接家庭路由器。
  3. AT+CIPSTART="TCP","服务器IP",端口建立 TCP 连接。
  4. AT+CIPSEND=长度然后发送数据。

每一步都要等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 返回值时,用状态机逐字节匹配OKERROR+IPD这些关键字。

5.3 断线重连与心跳机制

WiFi 断线是常态,不是异常。路由器重启、信号波动、模块发热都会导致 TCP 断开。系统必须能自动恢复:

  • 心跳包:每 30 秒往服务器发一个{"type":"heartbeat"},服务器 60 秒没收到就认为设备离线。
  • 断线检测:发送 AT 指令时如果连续 3 次超时,判定为断线,执行AT+CIPCLOSE后重新AT+CIPSTART
  • 看门狗: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 这类开关电源模块,效率高、发热小。
  • 模块背面贴一块小散热片。
  • 如果只是间歇性发送数据,可以在发送间隙让模块进入AT+SLEEP模式。

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 断了怎么办”“如果传感器坏了怎么办”这类问题,才是拿高分的关键。

我个人在做这类环境监控系统时最大的体会是:硬件的可靠性永远排在功能之前。一个只能测温度但从不死机的系统,比一个功能齐全但三天两头重启的系统有价值得多。所以先把电源、复位、看门狗这些基础打牢,再去堆功能,这个顺序不能反。

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

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

立即咨询