1. 项目概述与核心功能
这个基于STM32的水位检测自动控制系统,是我花了三个月时间打磨出来的养鱼神器。作为一个嵌入式开发老鸟兼资深鱼友,我实在受不了每天手动监测鱼缸水温和换水的繁琐操作。整套系统实现了从硬件到软件的全栈控制,主要包含五大核心功能:
- 水位实时监测:采用HCSR04超声波模块,测量范围20-400cm,精度±0.5cm(经温度补偿后)
- 水温精准检测:DS18B20数字温度传感器,测量范围0-50℃,分辨率0.0625℃
- 智能水泵控制:通过5V继电器驱动最大10A电流的水泵,具备防频繁启停逻辑
- 云端数据同步:ESP8266 WiFi模块对接阿里云IoT平台,数据上传频率可配置
- 手机远程监控:自研Android APP支持实时数据展示和历史曲线查询
关键设计原则:在保证可靠性的前提下尽量降低成本,主控选用STM32F103C8T6(市场价格约12元),整套BOM成本控制在100元以内。
2. 硬件设计与选型解析
2.1 主控模块选型
选择STM32F103C8T6(Cortex-M3内核)主要基于三点考量:
- 充足的GPIO资源(37个IO)满足多传感器接入
- 内置12位ADC便于未来扩展其他模拟传感器
- 72MHz主频能流畅运行FreeRTOS实时系统
实际使用中通过CubeMX配置了以下外设:
- TIM2用于超声波模块的高精度时间捕获
- USART1连接ESP8266进行AT指令通信
- GPIOB组驱动继电器和水泵控制电路
2.2 传感器模块对比
水位检测方案对比表:
| 方案类型 | 测量精度 | 抗干扰性 | 安装复杂度 | 成本 |
|---|---|---|---|---|
| 超声波 | ±0.5cm | 中等 | 简单 | 15元 |
| 压力式 | ±1cm | 强 | 复杂 | 50元 |
| 浮球式 | ±2cm | 弱 | 中等 | 8元 |
最终选用HCSR04超声波模块,因其在精度和成本间取得最佳平衡。实际安装时需注意:
- 传感器需垂直水面安装
- 测量面与水面距离保持5cm以上
- 避开气泡聚集区域
2.3 电源电路设计
系统采用两级电源架构:
- 主电源:12V/2A适配器输入
- 降压电路:
- LM2596降至5V(给ESP8266供电)
- AMS1117-3.3V给STM32供电
血泪教训:最初尝试用7805线性稳压,结果水泵启动时电压跌落导致MCU复位。改用开关电源后问题解决。
3. 关键代码实现详解
3.1 超声波驱动优化
原始代码中的距离计算存在两个问题:
- 未考虑温度对声速的影响
- 定时器时钟配置不精确
改进后的算法实现:
c复制// 带温度补偿的距离计算
float Get_CalibratedDistance(float temp) {
uint32_t echo_time = TIM2->CNT;
float sound_speed = 331.4f + 0.6f * temp; // 声速温度补偿公式
return (echo_time * sound_speed) / 20000.0f;
}
// 定时器配置关键点
void TIM2_Init(void) {
htim2.Instance = TIM2;
htim2.Init.Prescaler = 72-1; // 1MHz计数频率(72MHz/72)
htim2.Init.CounterMode = TIM_COUNTERMODE_UP;
htim2.Init.Period = 0xFFFFFFFF;
HAL_TIM_Base_Start(&htim2);
}
3.2 温度传感器驱动
DS18B20的稳定读取依赖精确的时序控制,以下是经过验证的驱动方案:
c复制// 单总线写时序
void DS18B20_WriteBit(uint8_t bit) {
set_as_output();
HAL_GPIO_WritePin(DS18B20_GPIO_Port, DS18B20_Pin, 0);
delay_us(5); // 保持5us低电平
if(bit) HAL_GPIO_WritePin(DS18B20_GPIO_Port, DS18B20_Pin, 1);
delay_us(60); // 保持60us周期
set_as_input();
}
// 温度读取流程
float Read_Temperature(void) {
DS18B20_Reset();
DS18B20_WriteByte(0xCC); // 跳过ROM
DS18B20_WriteByte(0x44); // 启动转换
HAL_Delay(750); // 等待转换完成
DS18B20_Reset();
DS18B20_WriteByte(0xCC);
DS18B20_WriteByte(0xBE); // 读取暂存器
uint8_t tempL = DS18B20_ReadByte();
uint8_t tempH = DS18B20_ReadByte();
return ((tempH << 8) | tempL) * 0.0625f;
}
3.3 水泵智能控制逻辑
水泵控制算法经过三次迭代优化:
- 基础版本:简单阈值控制
c复制if(level < LOW_LEVEL) PUMP_ON();
else PUMP_OFF();
- 加入状态锁存:
c复制static uint8_t last_state = 0;
if(level < (LOW_LEVEL-2)) last_state = 1;
else if(level > (LOW_LEVEL+2)) last_state = 0;
- 最终版本:带温度保护和软件去抖
c复制#define HYSTERESIS 3 // 水位回差3cm
#define MIN_INTERVAL 30 // 最小间隔30秒
void Pump_Control(float level, float temp) {
static uint32_t last_time = 0;
static uint8_t pump_state = 0;
if(HAL_GetTick() - last_time < MIN_INTERVAL*1000) return;
if(!pump_state && level<(LOW_LEVEL-HYSTERESIS) && temp<35.0f) {
PUMP_ON();
pump_state = 1;
last_time = HAL_GetTick();
}
else if(pump_state && (level>(LOW_LEVEL+HYSTERESIS) || temp>=35.0f)) {
PUMP_OFF();
pump_state = 0;
last_time = HAL_GetTick();
}
}
4. 云端通信实现
4.1 阿里云IoT配置
设备接入阿里云需要完成以下关键步骤:
- 创建产品:选择"自定义品类"-"数据格式"为透传
- 添加设备:获取三元组(ProductKey、DeviceName、DeviceSecret)
- 生成连接参数:
- ClientID: ${deviceName}|securemode=3,signmethod=hmacsha1|
- Username: ${deviceName}&$
- Password: 用DeviceSecret计算得到的签名
重要提示:阿里云MQTT的keepalive需设置为60-120秒,过短会导致频繁重连
4.2 ESP8266 AT指令流程
稳定的WiFi连接需要以下AT指令序列:
c复制void ESP8266_Connect(void) {
Send_AT_Command("AT+RST", "ready", 2000); // 模块复位
Send_AT_Command("AT+CWMODE=1", "OK", 500); // 设置为Station模式
Send_AT_Command("AT+CWJAP=\"SSID\",\"PASSWORD\"", "OK", 10000);
Send_AT_Command("AT+CIPSNTPCFG=1,8", "OK", 500); // 配置SNTP
Send_AT_Command("AT+MQTTUSERCFG=0,1,\"ClientID\",\"Username\",\"Password\"", "OK", 1000);
Send_AT_Command("AT+MQTTCONN=0,\"iot-xxx.mqtt.aliyuncs.com\",1883,1)", "OK", 5000);
}
4.3 数据上报协议设计
采用精简的JSON格式上报数据:
json复制{
"id": 123,
"version":"1.0",
"params":{
"water_level": 25.3,
"water_temp": 28.1,
"pump_status": 1,
"ts": 1630000000
}
}
数据上传频率配置策略:
- 常规状态:每5分钟上报一次
- 水位/温度突变:超过阈值立即上报
- 水泵状态变化:即时上报
5. 常见问题与解决方案
5.1 超声波测量异常排查
现象:测量值固定为最大值或剧烈波动
- 检查TRIG和ECHO引脚是否接反
- 确认供电电压稳定(5V±0.5V)
- 测量环境避免强气流和泡沫干扰
解决方案:
c复制// 增加数据校验逻辑
float Get_ValidDistance(void) {
float dist[5];
for(int i=0; i<5; i++) dist[i] = Get_WaterLevel();
qsort(dist, 5, sizeof(float), compare_float);
return dist[2]; // 取中值
}
5.2 DS18B20读取失败处理
典型错误:
- 读取值为85℃或-127℃
- 总线无响应
处理流程:
- 检查上拉电阻(4.7KΩ最佳)
- 确认GPIO配置为开漏输出
- 增加重试机制:
c复制#define MAX_RETRY 3
float Safe_ReadTemp(void) {
for(int i=0; i<MAX_RETRY; i++) {
float temp = Read_Temperature();
if(temp > -10 && temp < 50) return temp;
HAL_Delay(100);
}
return NAN; // 返回无效值
}
5.3 网络连接不稳定优化
改善措施:
- 增加WiFi信号强度检测:
c复制int Get_WIFI_Strength(void) {
Send_AT_Command("AT+CWJAP?", "OK", 1000);
// 解析返回数据中的RSSI值
return Parse_RSSI(recv_buffer);
}
- 实现断线自动重连:
c复制void Network_Task(void *arg) {
while(1) {
if(!ESP8266_IsConnected()) {
ESP8266_Disconnect();
ESP8266_Connect();
}
vTaskDelay(10000); // 每10秒检查一次
}
}
6. 手机APP开发要点
6.1 Android端关键功能
-
数据展示界面:
- 实时水位柱状图
- 温度变化曲线
- 水泵状态指示灯
-
控制功能:
- 手动模式开关
- 水位阈值设置
- 温度报警设置
-
历史数据查询:
- 按小时/日/周统计
- 异常事件记录
6.2 通信协议设计
APP与阿里云交互采用MQTT+HTTPS双通道:
- 实时数据:MQTT推送
- 历史数据:HTTPS API查询
- 控制指令:MQTT QoS1级别
消息主题规划:
- 上行:
/sys/${productKey}/${deviceName}/thing/event/property/post - 下行:
/sys/${productKey}/${deviceName}/thing/service/property/set
6.3 界面优化技巧
-
水位显示采用渐变颜色:
- 正常范围:蓝色
- 接近阈值:黄色
- 危险水位:红色
-
温度曲线使用SurfaceView实现平滑绘制
-
状态变更添加振动反馈
7. 系统部署与维护
7.1 安装注意事项
-
硬件安装:
- 超声波传感器高于最高水位5cm以上
- 温度传感器远离加热棒
- 水泵进水管加装过滤器
-
初次调试步骤:
- 先测试各模块单独工作
- 再逐步集成系统
- 最后进行长时间稳定性测试
7.2 日常维护建议
-
每月检查:
- 传感器探头清洁
- 水管连接紧固
- 电源线绝缘检查
-
软件维护:
- 定期备份配置参数
- 关注阿里云证书到期时间
- 更新APP时保持协议兼容
7.3 扩展改进方向
-
硬件扩展:
- 增加PH值检测
- 添加溶解氧传感器
- 集成自动喂食器
-
软件升级:
- 增加AI预测水质变化
- 实现多设备组网
- 开发iOS版本APP
这套系统经过半年实际运行测试,在1.5米鱼缸场景下表现稳定。最关键的收获是:硬件设计要预留20%的余量,软件必须考虑各种异常情况。现在我的龙鱼再也不用担心换水问题,而我也终于可以从日常维护中解脱出来,有更多时间欣赏鱼儿游动的美妙姿态了。