STM32嵌入式开发:基于STM32F103的智能水族箱控制

文章目录

    • 一、前言
      • [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校准:

  1. 使用pH=7.0标准液,记录电压值
  2. 使用pH=4.0标准液,记录电压值
  3. 计算校准系数

温度校准:

  • 对比标准温度计,调整偏差

5.2 功能测试

  • 温度控制:设定26°C,观察加热棒启停
  • 定时喂食:设置测试时间,验证舵机动作
  • 灯光控制:定时开关灯

六、故障排查

6.1 pH读数不稳定

排查:

  1. 检查探头是否清洁
  2. 确保充分浸泡
  3. 检查校准参数

6.2 温度控制振荡

优化:

  1. 增大温度回差
  2. 增加滤波算法
  3. 检查加热棒功率

七、总结

7.1 核心知识点

  1. 多传感器:温度、pH、浊度监测
  2. PID控制:温度精确控制
  3. 定时任务:RTC时间管理
  4. 舵机控制:PWM精确控制

7.2 扩展方向

  • 添加WiFi远程监控
  • 实现手机APP控制
  • 添加摄像头监控
  • 集成自动换水系统

💡 提示:pH探头需要定期校准,建议每月校准一次。

相关推荐
凌盛羽2 小时前
使用python绘图分析电池充电曲线
开发语言·python·stm32·单片机·fpga开发·51单片机
yongui478342 小时前
红外额温枪/体温枪单片机控制源码(STM32方案)
stm32·单片机·mongodb
DC升降压/LED驱动IC2 小时前
源芯片选型指南之 AP5193 DC-DC 宽电压 LED 降压恒流驱动器
stm32·单片机·嵌入式硬件·物联网·51单片机·proteus
蓝凌y2 小时前
51单片机之花样灯
c语言·单片机·嵌入式硬件·51单片机
天天爱吃肉82183 小时前
【 电机热网络温度预测模型学习笔记】
笔记·功能测试·嵌入式硬件·学习·机器学习·信息可视化·汽车
爱喝纯牛奶的柠檬3 小时前
STM32驱动HC-SR04超声波测距模块
stm32·单片机·嵌入式硬件
飞凌嵌入式3 小时前
飞凌嵌入式RK3506J核心板通过OpenHarmony 5.1兼容性认证
嵌入式硬件·开源·鸿蒙
李永奉3 小时前
杰理芯片SDK-更改芯片产品蓝牙名功能
单片机·嵌入式硬件·mcu·物联网·语音识别
坤坤藤椒牛肉面3 小时前
ARM中断设置--定时器中断
单片机·嵌入式硬件