✌️✌️大家好,这里是5132单片机毕设设计项目分享,今天给大家分享的是基于《基于STM32的智能鱼缸设计》。

目录

一、系统功能

2.1、硬件清单

2.2、功能介绍

2.3、控制模式

二、演示视频和实物

三、系统设计框图

四、软件设计流程图

五、原理图

六、主程序

七、总结

八、资料内容


一、系统功能

2.1、硬件清单

STM32F103C8T6 最小核心控制板 + OLED 显示屏 + ESP8266-01s + 继电器(3 个)+pH 传感器 + 浊度传感器 + 水位传感器 + DS18B20 温度传感器 + 声光报警模块(绿色 LED 灯 + 蜂鸣器)+ 后备电池 + 舵机 + 按键(4 个)

2.2、功能介绍

(1)STM32F103C8T6 最小核心控制板:作为系统的核心控制单元,负责协调和控制整个智能鱼缸水质净化系统的运行。
(2)OLED 显示屏:用于显示时间、控制模式、水位、pH 值、浊度以及温度等参数,让用户能够直观地了解鱼缸的各项数据。
(3)ESP8266-01s:连接云平台,实现手机 APP 对鱼缸设备的远程控制,使用户可以在远程对鱼缸进行操作和监控。
(4)继电器(3 个):分别控制排水泵、补水泵以及加热器,通过继电器的通断来实现对这三个设备的开关控制。
(5)pH 传感器:用于测量鱼缸水质的 pH 值,实时监测水质的酸碱度变化。
(6)浊度传感器:测量水的浊度,反映水质的浑浊程度,以便及时了解水质情况。
(7)水位传感器:检测鱼缸的水位高度,为水位控制提供数据支持。
(8)DS18B20 温度传感器:测量水的温度,确保鱼缸内水温在适宜的范围内。
(9)声光报警模块(绿色 LED 灯 + 蜂鸣器):当检测到的参数(如 pH 值、浊度、温度等)超出设定阈值时,发出声光报警信号,提醒用户注意。
(10)后备电池:在主电源断电时,为系统提供备用电源,保证系统的部分功能能够继续运行。
(11)舵机:模拟净化系统的运行,可能用于控制某些净化装置的动作。
(12)按键(4 个):第一个按键为模式切换按键,用于在远程模式、手动模式和自动模式之间进行切换;后面三个按键在不同的模式下具有不同的功能,如手动模式下控制舵机、补水泵、排水泵、加热器等设备,以及在阈值设置时进行加减操作。

2.3、控制模式

(1)远程模式:上电后可能默认进入该模式(或需通过按键切换),通过 ESP8266-01s 连接云平台,用户可以通过手机 APP 远程控制排水泵、补水泵、声光报警、加热器等设备的开关,同时实时获取温度、水位、浊度、pH 值等数据,并在 OLED 显示屏上显示时间、控制模式及各项参数。
(2)手动模式:通过第一个按键切换到手动模式,此时通过后面三个按键来控制设备。第一个按键控制舵机,第二个按键按一下打开补水泵,再按一下打开排水泵,再按一下全部关闭,第三个按键控制加热器的开关。
(3)自动模式:通过第一个按键切换到自动模式,系统根据各传感器的检测值与设定阈值的对比自动执行相应动作:

  1. 水位控制:当水位小于水位下限阈值时,打开补水泵;当水位大于水位上限阈值时,关闭补水泵,打开排水泵;当水位在上下限阈值之间时,两个水泵都关闭。
  2. pH 值报警:当 pH 值小于 pH 阈值时,声光报警模块发出报警信号。
  3. 浊度报警:当浊度大于浊度阈值时,声光报警模块发出报警信号。
  4. 温度控制:当采集的温度小于温度阈值时,打开加热器;当温度大于等于温度阈值时,关闭加热器。

此外,系统支持通过按键进入阈值设置界面,对各参数的阈值进行设置,第一个按键用于切换阈值,第二个按键用于加,第三个按键用于减。


二、演示视频和实物

基于STM32的鱼缸水质净化系统

 


三、系统设计框图


四、软件设计流程图


五、原理图

 


六、主程序

#include "sys.h"                //头文件
#include "delay.h"
#include "usart.h"
#include "led.h"
#include "timer.h"
#include "usart3.h"
#include "gizwits_product.h"
#include "Key.h"
#include "Buzzer.h"
#include "OLED.h"
#include "AD.h"
#include "MyRTC.h"
#include "Servo.h"
#include "ds18b20.h"

uint8_t KeyNum;                               //存储按键值
uint32_t bufe[10];                     //存储传感器采集的数据
uint16_t AD0, AD1, AD2, AD3;             //存储5路ADC值

uint32_t TempYu = 25;
uint32_t ShuiWeiYu_Xia = 10;
uint32_t ShuiWeiYu_Shang = 70;
uint32_t ZhuoYu = 70;
uint32_t PHYu = 5;
short temperature;                               //存放温湿度
u8 state, state2 = 2, state2_1, state2_2, state3;      //按键状态标志
u8 t = 0;                                     //传感器读取时间间隔
u8 flag;                               //远程控制标志
u8 flag2 = 0, flag1, flag3;
uint16_t RTC_Time[] = {0, 0, 0};
uint16_t RTC_Time1[] = {7, 0, 0};    //定时时间---开
uint16_t RTC_Time2[] = {19, 0, 0};   //定时时间---关
u8 T_state, T_state1, qingping = 1, state_dingshi_yu_guan, state_dingshi_yu_kai, state_dingshi_yu_switch, state3_1;
float PH;
extern void TimeSet(void);
extern void TimeRead(void);
extern void DingShiMoShi(void);
extern void YuZhiSet(void);
extern void ChuangGan(void);

void MY_Gizwits_Init(void)              //机智云初始化函数
{
    TIM3_Int_Init(9, 7199); //1MS系统定时
    usart3_init(9600);//WIFI初始化
    memset((uint8_t *)&currentDataPoint, 0, sizeof(dataPoint_t)); //设备状态结构体初始化
    gizwitsInit();//环形缓冲区初始化
    gizwitsSetMode(2);   //设置模式
    userInit();
}
u16 Get_Adc_Average(u8 ADC_CHx, u8 times)
{
    u32 temp_val = 0;
    u8 t;
    for (t = 0; t < times; t++)
    {
        temp_val += AD_GetValue(ADC_CHx);
        //SYSTICK_DelayMs(5);
    }
    return temp_val / times;
}

void shoudong()
{
    TimeRead();
    if (KeyNum == 2)        //按键
    {
        delay_ms(20);
        if (KeyNum == 2)
        {
            state2++;
            if (state2 > 1)
            {
                state2 = 0;
            }
        }
    }
    if (state2 == 0)
    {
        Servo_SetAngle(90);//外设操作
    }
    if (state2 == 1)
    {
        Servo_SetAngle(0);//外设操作
    }
    if (KeyNum == 3)        //按键
    {
        delay_ms(20);
        if (KeyNum == 3)
        {
            state2_1++;
            if (state2_1 > 2)
            {
                state2_1 = 0;
            }
        }
    }
    if (state2_1 == 1)
    {
        BuShuiBen_ON();//外设操作
        PaiShuiBen_OFF();
    }
    if (state2_1 == 0)
    {
        BuShuiBen_OFF();//外设操作
        PaiShuiBen_OFF();
    }
    if (state2_1 == 2)
    {
        BuShuiBen_OFF();//外设操作
        PaiShuiBen_ON();
    }
    if (KeyNum == 4)        //按键
    {
        delay_ms(20);
        if (KeyNum == 4)
        {
            state2_2++;
            if (state2_2 > 1)
            {
                state2_2 = 0;
            }
        }
    }
    if (state2_2 == 1)
    {
        JiaRe_ON();//外设操作
    }
    if (state2_2 == 0)
    {
        JiaRe_OFF();//外设操作
    }


}
void zhidong()
{

    if (bufe[0] < ShuiWeiYu_Xia)
    {
          BuShuiBen_ON();
			    PaiShuiBen_OFF();
    }else if (bufe[0] > ShuiWeiYu_Shang)
		{
          PaiShuiBen_ON();
			    BuShuiBen_OFF();
    }else
    {			    
			   PaiShuiBen_OFF();
         BuShuiBen_OFF();
    }

    if (bufe[3]/10 < TempYu)
    {
        JiaRe_ON();
    }
    else
    {
        JiaRe_OFF();
    }
		
		 if ((bufe[1]/10 < PHYu) || (bufe[2] > ZhuoYu))
    {
        Buzzer_Turn();
    }
    else
    {
        Buzzer_OFF();
    }

}

int main(void)
{

    NVIC_PriorityGroupConfig(NVIC_PriorityGroup_2); //设置NVIC中断分组2:2位抢占优先级,2位响应优先级
    uart_init(9600);       //串口初始化为9600
    delay_init();      //延时函数初始化
    LED_Init();            //初始化与LED连接的硬件接口

    Buzzer_Init();             //下面为外设初始化
    OLED_Init();
    Key_Init();
    AD_Init();
    MyRTC_Init();
    Servo_Init();     //舵机初始化,并设置舵机初始角度
    while (DS18B20_Init()) //DS18B20初始化
    {
        printf("ds18b20 success!");
        delay_ms(200);
    }

    MY_Gizwits_Init();   //机智云初始化

    while (1)
    {
        userHandle();    //数据上传
        gizwitsHandle((dataPoint_t *)&currentDataPoint);   //后台处理,必须放在while里

        if (t % 10 == 0)
        {
            AD0 = AD_GetValue(ADC_Channel_0);    //水位传感器     PA0
            if (AD0 > 4000)AD0 = 4000;
            bufe[0] = (u8)(AD0 / 40);

            AD2 = Get_Adc_Average(ADC_Channel_2, 10);
            PH = (float)AD2 * (3.3 / 4096); //读取ADC通道4的值
            PH = -5.7541 * PH + 16.654; //输出电压范围0~3V3      因为STM32的ADC参考电压是3.3V
            //将ADC的原始值(adcx)转换为电压值。这里假设ADC的参考电压是3.3V,并且ADC的位数是12位(即最大值为4096)。
            bufe[1] = PH * 10;

            AD3 = AD_GetValue(ADC_Channel_3);    //浊度传感器     PA3
            if (AD3 > 4000)AD3 = 4000;
            bufe[2] = (u8)(100 - (AD3 / 40));

            temperature = DS18B20_Get_Temp();
            bufe[3] = temperature;
        }
        t++;

        KeyNum = Key_GetNum();
        if (KeyNum == 1)
        {
            qingping = 0;
            state2 = 2,
            delay_ms(20);
            if (KeyNum == 1)
            {
                state++;
                if (state > 3)
                {
                    state = 0;
                }
            }
        }
        if (state == 0)                                //远程模式
        {
            if (qingping == 0)
            {
                OLED_Clear();
                qingping = 1;
            }           
            TimeRead();
            ChuangGan();
            OLED_ShowChinese(1, 7, 49);
            OLED_ShowChinese(1, 8, 50);
        }
        if (state == 2)                           //自动模式
        {
            OLED_ShowChinese(1, 7, 51);
            OLED_ShowChinese(1, 8, 52);
            TimeRead();
            zhidong();
            ChuangGan();
        }
        if (state == 1)                           //手动模式
        {
            OLED_ShowChinese(1, 7, 18);
            OLED_ShowChinese(1, 8, 52);
            ChuangGan();
            shoudong();
        }
        if (state == 3)                           //阈值设置
        {
            if (qingping == 0)
            {
                OLED_Clear();
                qingping = 1;
            }
            YuZhiSet();
        }
    }
}



void TimeSet()           //设置时间
{
    if (KeyNum == 2)    //PB10
    {
        delay_ms(20);
        if (KeyNum == 2)
        {
            T_state++;
            if (T_state > 2)
            {
                T_state = 0;
            }
        }
    }
    if (T_state == 0)    //时间显示模式
    {
        MyRTC_ReadTime();

        OLED_ShowNum(1, 5, MyRTC_Time[3], 2);    //时
        OLED_ShowString(1, 7, ":");
        OLED_ShowNum(1, 8, MyRTC_Time[4], 2);    //分
        OLED_ShowString(1, 10, ":");
        OLED_ShowNum(1, 11, MyRTC_Time[5], 2);   //秒

        RTC_Time[0] = MyRTC_Time[3];
        RTC_Time[1] = MyRTC_Time[4];
        RTC_Time[2] = MyRTC_Time[5];
    }
    if (T_state == 1)    //修改时间
    {
        if (KeyNum == 5)
        {
            delay_ms(20);
            if (KeyNum == 5)
            {
                T_state1++;
                if (T_state1 > 2)
                {
                    T_state1 = 0;
                }
            }
        }
        if (T_state1 == 0)  //修改时
        {
            if (KeyNum == 4)RTC_Time[0]++;
            if (KeyNum == 3)RTC_Time[0]--;
            if (RTC_Time[0] > 23 & RTC_Time[0] < 100)RTC_Time[0] = 0;
            if (RTC_Time[0] > 100)RTC_Time[0] = 23;
            OLED_ShowNum(1, 5, RTC_Time[0], 2);  //时
        }
        if (T_state1 == 1)  //修改分
        {
            if (KeyNum == 4)RTC_Time[1]++;
            if (KeyNum == 3)RTC_Time[1]--;
            if (RTC_Time[1] > 59 & RTC_Time[1] < 100)RTC_Time[1] = 0;
            if (RTC_Time[1] > 100)RTC_Time[1] = 59;
            OLED_ShowNum(1, 8, RTC_Time[1], 2);  //时
        }
        if (T_state1 == 2)  //修改秒
        {
            if (KeyNum == 4)RTC_Time[2]++;
            if (KeyNum == 3)RTC_Time[2]--;
            if (RTC_Time[2] > 59)RTC_Time[2] = 0;
            if (RTC_Time[2] > 59 & RTC_Time[2] < 100)RTC_Time[2] = 0;
            if (RTC_Time[2] > 100)RTC_Time[2] = 59;
            OLED_ShowNum(1, 11, RTC_Time[2], 2);  //时
        }
    }
    if (T_state == 2)
    {
        MyRTC_Time[3] = RTC_Time[0];
        MyRTC_Time[4] = RTC_Time[1];
        MyRTC_Time[5] = RTC_Time[2];
        MyRTC_SetTime();
        T_state = 0;
    }
}

void TimeRead()
{
    MyRTC_ReadTime();

    OLED_ShowNum(1, 5, MyRTC_Time[3], 2);    //时
    OLED_ShowString(1, 7, ":");
    OLED_ShowNum(1, 8, MyRTC_Time[4], 2);    //分
    OLED_ShowString(1, 10, ":");
    OLED_ShowNum(1, 11, MyRTC_Time[5], 2);   //秒
}


void DingShiMoShi()
{
    TimeRead();

//...............................定时模式..................................../
    if ((MyRTC_Time[3] == RTC_Time1[0]) && (MyRTC_Time[4] == RTC_Time1[1]) && (MyRTC_Time[5] == RTC_Time1[2]))
    {
        //外设操作
    }

    if ((MyRTC_Time[3] == RTC_Time2[0]) && (MyRTC_Time[4] == RTC_Time2[1]) && (MyRTC_Time[5] == RTC_Time2[2]))
    {
        //外设操作
    }

//...............................修改定时时间..................................../

    OLED_ShowChinese(3, 1, 31);
    OLED_ShowString(3, 3, ":");
    OLED_ShowNum(3, 5, RTC_Time1[0], 2);
    OLED_ShowString(3, 7, ":");
    OLED_ShowNum(3, 8, RTC_Time1[1], 2);
    OLED_ShowString(3, 10, ":");
    OLED_ShowNum(3, 11, RTC_Time1[2], 2);

    OLED_ShowChinese(4, 1, 32);
    OLED_ShowString(4, 3, ":");
    OLED_ShowNum(4, 5, RTC_Time2[0], 2);
    OLED_ShowString(4, 7, ":");
    OLED_ShowNum(4, 8, RTC_Time2[1], 2);
    OLED_ShowString(4, 10, ":");
    OLED_ShowNum(4, 11, RTC_Time2[2], 2);


    if (KeyNum == 5)
    {
        delay_ms(20);
        if (KeyNum == 5)
        {
            state_dingshi_yu_switch++;
            if (state_dingshi_yu_switch > 2)
            {
                state_dingshi_yu_switch = 0;
            }
        }
    }

    if (state_dingshi_yu_switch == 1)    //设置阈值开的时间
    {
        if (KeyNum == 2)
        {
            delay_ms(20);
            if (KeyNum == 2)
            {
                state_dingshi_yu_kai++;
                if (state_dingshi_yu_kai > 2)
                {
                    state_dingshi_yu_kai = 0;
                }
            }
        }
        if (state_dingshi_yu_kai == 0)    //时
        {
            if (KeyNum == 3) RTC_Time2[0]++;
            if (KeyNum == 4) RTC_Time2[0]--;
        }
        if (state_dingshi_yu_kai == 1)//分
        {
            if (KeyNum == 3) RTC_Time2[1]++;
            if (KeyNum == 4) RTC_Time2[1]--;
        }
        if (state_dingshi_yu_kai == 2)//秒
        {
            if (KeyNum == 3) RTC_Time2[2]++;
            if (KeyNum == 4) RTC_Time2[2]--;
        }
    }
    else
    {
        if (KeyNum == 2)
        {
            delay_ms(20);
            if (KeyNum == 2)
            {
                state_dingshi_yu_guan++;
                if (state_dingshi_yu_guan > 2)
                {
                    state_dingshi_yu_guan = 0;
                }
            }
        }
        if (state_dingshi_yu_guan == 0)    //时
        {
            if (KeyNum == 3) RTC_Time1[0]++;
            if (KeyNum == 4) RTC_Time1[0]--;
        }
        if (state_dingshi_yu_guan == 1)//分
        {
            if (KeyNum == 3) RTC_Time1[1]++;
            if (KeyNum == 4) RTC_Time1[1]--;
        }
        if (state_dingshi_yu_guan == 2)//秒
        {
            if (KeyNum == 3) RTC_Time1[2]++;
            if (KeyNum == 4) RTC_Time1[2]--;
        }
    }
    if (state3_1 == 0)    //时
    {
        if (KeyNum == 5) RTC_Time1[0]++;
        if (KeyNum == 6) RTC_Time1[0]--;
    }
    if (state3_1 == 1)//分
    {
        if (KeyNum == 5) RTC_Time1[1]++;
        if (KeyNum == 6) RTC_Time1[1]--;
    }
    if (state3_1 == 2)//秒
    {
        if (KeyNum == 5) RTC_Time1[2]++;
        if (KeyNum == 6) RTC_Time1[2]--;
    }
}

void YuZhiSet()
{
    zhidong();
    OLED_ShowChinese(1, 3, 72);   //显示“阈值设置”
    OLED_ShowChinese(1, 4, 73);
    OLED_ShowChinese(1, 5, 74);
    OLED_ShowChinese(1, 6, 75);
	


    OLED_ShowString(2, 1, "S_W_X:");
    OLED_ShowNum(2, 7, ShuiWeiYu_Xia, 2);
    OLED_ShowString(3, 1, "S_W_S:");
    OLED_ShowNum(3, 7, ShuiWeiYu_Shang, 2);	
    OLED_ShowString(4, 10, "PH:");
    OLED_ShowNum(4, 13, PHYu, 2);
    OLED_ShowString(2, 10, "Temp:");
    OLED_ShowNum(2, 15, TempYu, 2);
	  OLED_ShowString(3, 10, "Z_D:");
    OLED_ShowNum(3, 14, ZhuoYu, 2);


    if (KeyNum == 2)                          //自动模式下PB0按键控制阈值切换
    {
        delay_ms(20);
        if (KeyNum == 2)
        {
            state3++;
            if (state3 > 4)
            {
                state3 = 0;
            }
        }
    }

    if (state3 == 0)
    {
        if (KeyNum == 3)ShuiWeiYu_Xia++;
        if (KeyNum == 4)ShuiWeiYu_Xia--;
    }
		    if (state3 == 1)
    {
        if (KeyNum == 3)ShuiWeiYu_Shang++;
        if (KeyNum == 4)ShuiWeiYu_Shang--;
    }
    if (state3 == 4)
    {
        if (KeyNum == 3)PHYu++;
        if (KeyNum == 4)PHYu--;
    }
    if (state3 == 2)
    {
        if (KeyNum == 3)TempYu ++;
        if (KeyNum == 4)TempYu --;
    }
		 if (state3 == 3)
    {
        if (KeyNum == 3)ZhuoYu ++;
        if (KeyNum == 4)ZhuoYu --;
    }
}


void ChuangGan()
{
    OLED_ShowChinese(2, 1, 39);
    OLED_ShowChinese(2, 2, 40);
    OLED_ShowString(2, 5, ":");
    OLED_ShowNum(2, 6, bufe[0], 2);
    OLED_ShowString(2, 8, "%");
    OLED_ShowString(4, 8, "%");

    OLED_ShowString(2, 11, "PH:");
    OLED_ShowNum(2, 14, bufe[1] / 10, 1);
    OLED_ShowString(2, 15, ".");
    OLED_ShowNum(2, 16, bufe[1] % 10, 1);

    OLED_ShowChinese(3, 1, 26);
    OLED_ShowChinese(3, 2, 28);
    OLED_ShowString(3, 5, ":");
    if (temperature < 0)
    {
        OLED_ShowString(3, 6, "-");     //显示负号
        temperature = -temperature;                 //转为正数
    }
    else OLED_ShowString(3, 6, "+");        //去掉负号
    OLED_ShowNum(3, 7, temperature / 10, 2);
    OLED_ShowString(3, 9, ".");
    OLED_ShowNum(3, 10, temperature % 10, 1);

    OLED_ShowChinese(4, 1, 78);
    OLED_ShowChinese(4, 2, 79);
    OLED_ShowString(4, 5, ":");
    OLED_ShowNum(4, 6, bufe[2], 2);
}

七、总结

基于STM32的智能鱼缸设计 本设计实现了鱼缸水质智能监测与控制系统,采用STM32F103C8T6作为主控芯片,配备多种传感器(pH、浊度、水位、温度)和WiFi模块(ESP8266)。系统具有三种工作模式:远程模式(通过手机APP控制)、手动模式(按键操作)和自动模式(根据预设阈值自动调节)。主要功能包括:实时监测水质参数、自动控制水泵/加热器、异常报警等。通过OLED显示屏直观显示各项参数,并支持阈值设置。该系统有效解决了传统鱼缸管理不便的问题,实现了智能化、远程化的鱼缸管理。

八、资料内容

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐