文章目录
-
- 一、前言
-
- [1.1 技术背景](#1.1 技术背景)
- [1.2 应用场景](#1.2 应用场景)
- [1.3 读者收获](#1.3 读者收获)
- [1.4 技术栈](#1.4 技术栈)
- 二、环境准备
-
- [2.1 硬件要求](#2.1 硬件要求)
- [2.2 模块连接](#2.2 模块连接)
- 三、核心实现
-
- [3.1 温度监测与控制](#3.1 温度监测与控制)
- [3.2 pH值监测](#3.2 pH值监测)
- [3.3 自动喂食系统](#3.3 自动喂食系统)
- [3.4 定时任务管理](#3.4 定时任务管理)
- [3.5 主控程序](#3.5 主控程序)
- 四、系统架构
- 五、测试验证
-
- [5.1 传感器校准](#5.1 传感器校准)
- [5.2 功能测试](#5.2 功能测试)
- 六、故障排查
-
- [6.1 pH读数不稳定](#6.1 pH读数不稳定)
- [6.2 温度控制振荡](#6.2 温度控制振荡)
- 七、总结
-
- [7.1 核心知识点](#7.1 核心知识点)
- [7.2 扩展方向](#7.2 扩展方向)
一、前言
1.1 技术背景
水族箱是家庭和办公场所常见的装饰和休闲设施,但传统水族箱需要人工定期维护,包括喂食、换水、调节温度等,管理较为繁琐。智能水族箱控制系统可以自动监测水质、温度,控制过滤、增氧、照明等设备,大大减轻维护工作量。
STM32F103系列微控制器凭借其丰富的外设接口、较高的性价比和低功耗特性,成为智能水族箱控制系统的理想选择。
1.2 应用场景
- 家庭水族箱:自动喂食、定时照明、温度控制
- 办公室水族箱:远程监控、自动维护
- 水族店:批量管理、水质监测
- 实验室:精密环境控制
1.3 读者收获
完成本教程后,你将掌握:
- 多传感器数据采集(温度、pH、浊度)
- 继电器控制(水泵、气泵、加热棒)
- 舵机控制(自动喂食)
- 定时任务管理
- 水位检测与补水
- 异常报警系统
1.4 技术栈
硬件平台:
- 主控芯片:STM32F103C8T6
- 温度传感器:DS18B20
- pH传感器:pH-4502C
- 浊度传感器:TS-300B
- 水位传感器:超声波/浮球
- 舵机:SG90(喂食)
- 继电器模块 × 4
软件工具:
- IDE:Keil MDK-ARM / STM32CubeIDE
- 固件库:STM32Cube HAL库
二、环境准备
2.1 硬件要求
系统框图:
显示模块
喂食系统
控制设备
传感器组
STM32F103主控
GPIO
ADC
TIM2/3
OneWire
USART1
DS18B20
水温
pH-4502C
酸碱度
TS-300B
浊度
超声波
水位
过滤泵
增氧泵
加热棒
LED灯
补水阀
SG90舵机
饲料盒
OLED12864
蜂鸣器
状态LED
硬件清单:
- STM32F103C8T6最小系统板 × 1
- DS18B20防水温度传感器 × 1
- pH-4502C传感器 × 1
- TS-300B浊度传感器 × 1
- 超声波测距模块 × 1
- SG90舵机 × 1
- 5V继电器模块 × 4
- OLED12864显示屏 × 1
- 蜂鸣器 × 1
- 5V电源适配器 × 1
- 杜邦线若干
2.2 模块连接
DS18B20连接:
- DATA → PA0
- VCC → 3.3V
- GND → GND
pH传感器连接:
- 模拟输出 → PA1(ADC1_IN1)
- VCC → 5V
- GND → GND
浊度传感器连接:
- 模拟输出 → PA2(ADC1_IN2)
- VCC → 5V
- GND → GND
继电器连接:
- 过滤泵 → PB12
- 增氧泵 → PB13
- 加热棒 → PB14
- LED灯 → PB15
三、核心实现
3.1 温度监测与控制
📄 创建文件:
Src/temp_control.c
c
/* temp_control.c - 温度监测与控制 */
#include "main.h"
#include "ds18b20.h"
#define TEMP_SET_POINT 26.0f /* 设定温度 */
#define TEMP_HYSTERESIS 1.0f /* 温度回差 */
#define HEATER_PORT GPIOB
#define HEATER_PIN GPIO_PIN_14
static float current_temp = 0.0f;
static uint8_t heater_on = 0;
/**
* @brief 温度控制初始化
*/
void TempControl_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();
GPIO_InitStruct.Pin = HEATER_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_LOW;
HAL_GPIO_Init(HEATER_PORT, &GPIO_InitStruct);
HAL_GPIO_WritePin(HEATER_PORT, HEATER_PIN, GPIO_PIN_RESET);
DS18B20_Init();
}
/**
* @brief 读取温度
*/
float TempControl_Read(void)
{
int16_t temp = DS18B20_ReadTemp();
if (temp != 0x7FFF) {
current_temp = temp / 100.0f;
}
return current_temp;
}
/**
* @brief 温度控制任务
*/
void TempControl_Task(void)
{
/* 读取温度 */
TempControl_Read();
/* 控制加热棒 */
if (current_temp < (TEMP_SET_POINT - TEMP_HYSTERESIS)) {
if (!heater_on) {
heater_on = 1;
HAL_GPIO_WritePin(HEATER_PORT, HEATER_PIN, GPIO_PIN_SET);
DEBUG_PRINT("温度过低,开启加热棒\r\n");
}
} else if (current_temp > (TEMP_SET_POINT + TEMP_HYSTERESIS)) {
if (heater_on) {
heater_on = 0;
HAL_GPIO_WritePin(HEATER_PORT, HEATER_PIN, GPIO_PIN_RESET);
DEBUG_PRINT("温度正常,关闭加热棒\r\n");
}
}
}
/**
* @brief 获取当前温度
*/
float TempControl_GetTemp(void)
{
return current_temp;
}
3.2 pH值监测
📄 创建文件:
Src/ph_sensor.c
c
/* ph_sensor.c - pH传感器 */
#include "main.h"
#define PH_ADC_CHANNEL ADC_CHANNEL_1
#define PH_ADC_PORT GPIOA
#define PH_ADC_PIN GPIO_PIN_1
/* pH校准参数 */
#define PH_VOLTAGE_7 2.5f /* pH=7时的电压 */
#define PH_VOLTAGE_4 3.0f /* pH=4时的电压 */
#define PH_SLOPE ((7.0f - 4.0f) / (PH_VOLTAGE_7 - PH_VOLTAGE_4))
extern ADC_HandleTypeDef hadc1;
/**
* @brief pH传感器初始化
*/
void PH_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
ADC_ChannelConfTypeDef sConfig = {0};
__HAL_RCC_GPIOA_CLK_ENABLE();
__HAL_RCC_ADC1_CLK_ENABLE();
GPIO_InitStruct.Pin = PH_ADC_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_ANALOG;
HAL_GPIO_Init(PH_ADC_PORT, &GPIO_InitStruct);
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_DATAALIGN_RIGHT;
hadc1.Init.NbrOfConversion = 1;
HAL_ADC_Init(&hadc1);
sConfig.Channel = PH_ADC_CHANNEL;
sConfig.Rank = 1;
sConfig.SamplingTime = ADC_SAMPLETIME_71CYCLES_5;
HAL_ADC_ConfigChannel(&hadc1, &sConfig);
}
/**
* @brief 读取pH值
*/
float PH_Read(void)
{
uint32_t adc_value;
float voltage;
float ph;
/* 多次采样取平均 */
uint32_t sum = 0;
uint8_t i;
for (i = 0; i < 10; i++) {
HAL_ADC_Start(&hadc1);
HAL_ADC_PollForConversion(&hadc1, 100);
sum += HAL_ADC_GetValue(&hadc1);
HAL_ADC_Stop(&hadc1);
HAL_Delay(10);
}
adc_value = sum / 10;
/* 转换为电压(3.3V参考,12位ADC) */
voltage = adc_value * 3.3f / 4096.0f;
/* 计算pH值 */
ph = 7.0f + (PH_VOLTAGE_7 - voltage) * PH_SLOPE;
return ph;
}
3.3 自动喂食系统
📄 创建文件:
Src/feeding.c
c
/* feeding.c - 自动喂食系统 */
#include "main.h"
#define SERVO_PIN GPIO_PIN_0
#define SERVO_PORT GPIOB
#define SERVO_TIM TIM3
#define SERVO_CHANNEL TIM_CHANNEL_1
#define SERVO_MIN 500 /* 0度脉宽 */
#define SERVO_MAX 2500 /* 180度脉宽 */
extern TIM_HandleTypeDef htim3;
/**
* @brief 舵机初始化
*/
void Servo_Init(void)
{
GPIO_InitTypeDef GPIO_InitStruct = {0};
TIM_OC_InitTypeDef sConfigOC = {0};
__HAL_RCC_GPIOB_CLK_ENABLE();
__HAL_RCC_TIM3_CLK_ENABLE();
GPIO_InitStruct.Pin = SERVO_PIN;
GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
GPIO_InitStruct.Pull = GPIO_NOPULL;
GPIO_InitStruct.Speed = GPIO_SPEED_FREQ_HIGH;
HAL_GPIO_Init(SERVO_PORT, &GPIO_InitStruct);
htim3.Instance = TIM3;
htim3.Init.Prescaler = 71; /* 72MHz / 72 = 1MHz */
htim3.Init.CounterMode = TIM_COUNTERMODE_UP;
htim3.Init.Period = 19999; /* 20ms周期 */
htim3.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
HAL_TIM_PWM_Init(&htim3);
sConfigOC.OCMode = TIM_OCMODE_PWM1;
sConfigOC.Pulse = SERVO_MIN;
sConfigOC.OCPolarity = TIM_OCPOLARITY_HIGH;
HAL_TIM_PWM_ConfigChannel(&htim3, &sConfigOC, SERVO_CHANNEL);
HAL_TIM_PWM_Start(&htim3, SERVO_CHANNEL);
}
/**
* @brief 设置舵机角度
*/
void Servo_SetAngle(uint8_t angle)
{
if (angle > 180) angle = 180;
uint16_t pulse = SERVO_MIN + (SERVO_MAX - SERVO_MIN) * angle / 180;
__HAL_TIM_SET_COMPARE(&htim3, SERVO_CHANNEL, pulse);
}
/**
* @brief 喂食一次
*/
void Feeding_Do(void)
{
DEBUG_PRINT("开始喂食\r\n");
/* 打开饲料盒 */
Servo_SetAngle(90);
HAL_Delay(1000);
/* 抖动一下,确保饲料落下 */
Servo_SetAngle(70);
HAL_Delay(200);
Servo_SetAngle(90);
HAL_Delay(200);
/* 关闭饲料盒 */
Servo_SetAngle(0);
HAL_Delay(500);
DEBUG_PRINT("喂食完成\r\n");
}
3.4 定时任务管理
📄 创建文件:
Src/schedule.c
c
/* schedule.c - 定时任务管理 */
#include "main.h"
#define SCHEDULE_MAX 10
typedef struct {
uint8_t hour;
uint8_t minute;
uint8_t type; /* 0=喂食, 1=开灯, 2=关灯, 3=换水 */
uint8_t enabled;
} ScheduleItemTypeDef;
static ScheduleItemTypeDef schedule[SCHEDULE_MAX];
static uint8_t schedule_count = 0;
extern RTC_HandleTypeDef hrtc;
/**
* @brief 添加定时任务
*/
void Schedule_Add(uint8_t hour, uint8_t minute, uint8_t type)
{
if (schedule_count >= SCHEDULE_MAX) return;
schedule[schedule_count].hour = hour;
schedule[schedule_count].minute = minute;
schedule[schedule_count].type = type;
schedule[schedule_count].enabled = 1;
schedule_count++;
}
/**
* @brief 检查定时任务
*/
void Schedule_Check(void)
{
RTC_TimeTypeDef sTime;
RTC_DateTypeDef sDate;
uint8_t i;
HAL_RTC_GetTime(&hrtc, &sTime, RTC_FORMAT_BIN);
HAL_RTC_GetDate(&hrtc, &sDate, RTC_FORMAT_BIN);
for (i = 0; i < schedule_count; i++) {
if (!schedule[i].enabled) continue;
if (sTime.Hours == schedule[i].hour &&
sTime.Minutes == schedule[i].minute &&
sTime.Seconds == 0) {
/* 执行定时任务 */
switch (schedule[i].type) {
case 0:
Feeding_Do();
break;
case 1:
Light_Control(1);
break;
case 2:
Light_Control(0);
break;
case 3:
WaterChange_Start();
break;
}
/* 防止重复执行 */
HAL_Delay(1000);
}
}
}
/**
* @brief 初始化默认任务
*/
void Schedule_InitDefault(void)
{
/* 08:00 开灯 */
Schedule_Add(8, 0, 1);
/* 08:30 喂食 */
Schedule_Add(8, 30, 0);
/* 12:00 喂食 */
Schedule_Add(12, 0, 0);
/* 18:00 喂食 */
Schedule_Add(18, 0, 0);
/* 22:00 关灯 */
Schedule_Add(22, 0, 2);
}
3.5 主控程序
📄 创建文件:
Src/main.c
c
/* main.c - 智能水族箱主程序 */
#include "main.h"
/* 函数声明 */
void System_Init(void);
/**
* @brief 系统初始化
*/
void System_Init(void)
{
HAL_Init();
SystemClock_Config();
/* 初始化外设 */
MX_GPIO_Init();
MX_ADC1_Init();
MX_TIM3_Init();
MX_RTC_Init();
MX_USART1_UART_Init();
/* 初始化模块 */
SSD1306_Init();
TempControl_Init();
PH_Init();
Servo_Init();
/* 初始化定时任务 */
Schedule_InitDefault();
DEBUG_PRINT("\r\n===========================\r\n");
DEBUG_PRINT("智能水族箱控制系统\r\n");
DEBUG_PRINT("===========================\r\n");
}
/**
* @brief 主函数
*/
int main(void)
{
uint32_t last_temp_check = 0;
uint32_t last_display = 0;
System_Init();
while (1) {
/* 温度控制(每5秒) */
if (HAL_GetTick() - last_temp_check >= 5000) {
last_temp_check = HAL_GetTick();
TempControl_Task();
}
/* 检查定时任务 */
Schedule_Check();
/* 更新显示(每秒) */
if (HAL_GetTick() - last_display >= 1000) {
last_display = HAL_GetTick();
float temp = TempControl_GetTemp();
float ph = PH_Read();
SSD1306_Clear();
char buf[32];
SSD1306_SetCursor(0, 0);
sprintf(buf, "Temp:%.1fC", temp);
SSD1306_WriteString(buf);
SSD1306_SetCursor(0, 16);
sprintf(buf, "pH:%.1f", ph);
SSD1306_WriteString(buf);
SSD1306_SetCursor(0, 32);
sprintf(buf, "Heater:%s", heater_on ? "ON" : "OFF");
SSD1306_WriteString(buf);
SSD1306_UpdateScreen();
}
HAL_Delay(100);
}
}
四、系统架构
是
否
喂食
开关灯
无
开始
系统初始化
主循环
温度检查?
读取温度
控制加热棒
检查定时任务
任务?
执行喂食
控制灯光
更新显示
五、测试验证
5.1 传感器校准
pH校准:
- 使用pH=7.0标准液,记录电压值
- 使用pH=4.0标准液,记录电压值
- 计算校准系数
温度校准:
- 对比标准温度计,调整偏差
5.2 功能测试
- 温度控制:设定26°C,观察加热棒启停
- 定时喂食:设置测试时间,验证舵机动作
- 灯光控制:定时开关灯
六、故障排查
6.1 pH读数不稳定
排查:
- 检查探头是否清洁
- 确保充分浸泡
- 检查校准参数
6.2 温度控制振荡
优化:
- 增大温度回差
- 增加滤波算法
- 检查加热棒功率
七、总结
7.1 核心知识点
- 多传感器:温度、pH、浊度监测
- PID控制:温度精确控制
- 定时任务:RTC时间管理
- 舵机控制:PWM精确控制
7.2 扩展方向
- 添加WiFi远程监控
- 实现手机APP控制
- 添加摄像头监控
- 集成自动换水系统
💡 提示:pH探头需要定期校准,建议每月校准一次。