基于STM32的蔬菜大棚温湿度智能控制系统设计

2023-09-27 08:14:44 浏览数 (1)

一、前言

随着人们对健康和可持续生活方式的关注不断增加,蔬菜大棚成为了现代农业中的重要组成部分。蔬菜大棚提供了一个受控的环境,使得农民能够在任何季节种植蔬菜,并根据需要进行调节。为了实现最佳的蔬菜生长和产量,对温度和湿度等环境条件的精确控制至关重要。

传统的蔬菜大棚管理通常依赖于人工监测和调节。这种方法存在一些问题,例如人工监测容易出现误差和延迟,而且对于大规模的蔬菜大棚来说,人工调节工作量巨大。所以开发一种基于智能控制系统的蔬菜大棚温湿度管理方案变得非常重要。

基于STM32微控制器的蔬菜大棚温湿度智能控制系统用于解决传统管理方法的问题,并提供一种自动化的解决方案。该系统利用STM32微控制器的强大计算和控制能力,结合温湿度传感器和执行器,实现对蔬菜大棚环境的精确监测和控制。

通过该系统,农民可以实时监测蔬菜大棚内的温度和湿度,并根据预设的目标范围自动调节。系统可以自动控制温室内的加热器、通风设备和加湿器等设备,以维持最适宜的生长环境条件。项目的目标是提高蔬菜大棚的生产效率和质量,降低能源消耗,并减少人力投入。通过智能控制系统的应用,农民能够实现更加可持续和高效的农业生产,为社会提供更多健康的蔬菜产品。

二、系统设计流程

2.1 硬件选型

硬件选型是设计蔬菜大棚温湿度智能控制系统的重要环节。

【1】主控芯片:STM32F103ZET6 主控芯片使用STM32F103ZET6,它是一款高性能的ARM Cortex-M3内核微控制器,具有丰富的外设资源和强大的处理能力。该芯片可满足本项目对控制和数据处理的要求。

【2】温湿度传感器:DHT11 空气温湿度采集选用DHT11传感器,它采用数字信号输出,具有简单、低成本和较好的精度,适合大棚环境的温湿度监测。

【3】土壤湿度传感器:土壤湿度传感器 土壤湿度采集选用土壤湿度传感器,通过模拟-数字转换器(ADC)接口采集土壤湿度数据。该传感器能够准确测量土壤湿度,为农作物提供合适的灌溉水量。

【4】通风风机:5V小风扇 继电器 为了实现通风控制,选择5V小风扇作为通风装置,并通过继电器控制其开关状态。根据温度数据和设定阈值,通过STM32的GPIO口控制继电器的高低电平实现通风风扇的启停控制。

【5】照明灯:LED白色灯模块 为了提供适当的照明条件,选择LED白色灯模块作为照明装置。该模块使用STM32的GPIO口控制其开关状态,实现灯光的开启和关闭。

【6】灌溉系统:抽水电机 继电器 灌溉系统采用抽水电机作为水源,并通过继电器控制其开启和关闭。通过单片机控制继电器的高低电平来控制抽水电机的工作状态,实现灌溉系统的自动化操作。

【7】显示模块:LCD显示屏 为了方便用户观察当前的温湿度等数据,选用LCD显示屏进行数据的显示。通过STM32的数字接口与LCD显示屏进行通信,将采集到的数据实时显示在屏幕上。

2.2 软件设计思路

本项目的代码设计思路可以分为以下几个关键部分:

【1】初始化设置:首先,需要进行主控芯片的初始化设置,包括引脚配置、时钟设置等。同时,还需要对LCD显示屏进行初始化配置,以便后续显示数据。

【2】传感器数据采集:使用合适的库函数或代码,读取DHT11传感器和土壤湿度传感器的数据。通过适当的接口与主控芯片进行通信,获取温度、湿度和土壤湿度的数值。

【3】数据处理与判断:根据采集到的温湿度和土壤湿度数值,进行相应的数据处理和判断。判断当前温度是否超出设定范围,以及土壤湿度是否低于设定阈值等。

【4】控制执行器:根据数据处理和判断的结果,控制相应的执行器,如通风风扇、照明灯和灌溉系统。通过设置相应的引脚电平或触发继电器,实现执行器的开启或关闭。

【5】LCD显示:将采集到的温湿度和土壤湿度数值通过LCD显示屏进行显示,以便用户实时监测。

【6】用户交互:通过按键输入或其他方式,实现用户与系统的交互。设置土壤湿度阈值、调节温度范围等。

【7】循环运行:将上述步骤组织成一个循环运行的程序,确保系统能够持续采集数据、处理判断和控制执行器的操作。

三、代码实现

3.1 DHT11温湿度读取

读取DHT11传感器环境温湿度并通过串口打印出来。

代码语言:javascript复制
#include "stm32f10x.h"
#include "stdio.h"

// 定义DHT11数据引脚
#define DHT11_PIN   GPIO_Pin_0
#define DHT11_PORT  GPIOA

// DHT11初始化函数
void DHT11_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    // 使能GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);

    // 配置DHT11引脚为推挽输出
    GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
}

// 延时函数,单位为微秒
void Delay_us(uint32_t nCount)
{
    uint32_t i;
    for(i=0; i<nCount; i  );
}

// 软件延时函数,单位为毫秒
void Delay_ms(uint32_t nCount)
{
    uint32_t i;
    for(i=0; i<nCount*1000; i  );
}

// 从DHT11读取一位数据
uint8_t DHT11_ReadBit(void)
{
    uint8_t retries = 0;
    
    while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == Bit_RESET)
    {
        if (retries   > 100) return 0;
        Delay_us(1);
    }
    
    Delay_us(40);   // 延时40us
    
    if (GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == Bit_SET)
        retries = 100;  // 超时标识
    else
        retries = 0;

    while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == Bit_SET)
    {
        if (retries   > 100) return 0;
        Delay_us(1);
    }

    return 1;
}

// 从DHT11读取一个字节数据
uint8_t DHT11_ReadByte(void)
{
    uint8_t i, temp = 0;
    
    for(i=0; i<8; i  )
    {
        temp <<= 1;
        temp |= DHT11_ReadBit();
    }
    
    return temp;
}

// 读取DHT11的温湿度值
uint8_t DHT11_ReadData(uint8_t* temperature, uint8_t* humidity)
{
    uint8_t data[5], checksum;

    // 主机将总线拉低至少18ms
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin = DHT11_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(DHT11_PORT, &GPIO_InitStructure);

    GPIO_ResetBits(DHT11_PORT, DHT11_PIN);
    Delay_ms(20);
    GPIO_SetBits(DHT11_PORT, DHT11_PIN);
    Delay_us(30);

    // 设置为输入模式
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IPU;
    GPIO_Init(DHT11_PORT, &GPIO_InitStructure);
    
    // 等待 DHT11 响应
    if (GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == Bit_RESET)
    {
        while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == Bit_RESET);
        while(GPIO_ReadInputDataBit(DHT11_PORT, DHT11_PIN) == Bit_SET);
        
        // 读取5字节数据
        for(uint8_t i=0; i<5; i  )
            data[i] = DHT11_ReadByte();
        
        // 读取校验和
        checksum = DHT11_ReadByte();

        // 校验数据
        if((data[0]   data[1]   data[2]   data[3]) != checksum)
            return 0;
        
        *humidity = data[0];
        *temperature = data[2];

        return 1;
    }
    else
    {
        return 0;
    }
}

// 初始化USART1
void USART1_Init(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 使能USART1和GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

    // 配置USART1的引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;  // TX
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;  // RX
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 配置USART1
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);

    // 使能USART1
    USART_Cmd(USART1, ENABLE);
}

// 发送字符到USART1
void USART1_SendChar(char ch)
{
    while(USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
    USART_SendData(USART1, (uint8_t)ch);
}

// 发送字符串到USART1
void USART1_SendString(const char* str)
{
    while(*str)
    {
        USART1_SendChar(*str  );
    }
}

int main(void)
{
    uint8_t temperature, humidity;

    // 初始化DHT11和USART1
    DHT11_Init();
    USART1_Init();

    while(1)
    {
        if (DHT11_ReadData(&temperature, &humidity))
        {
            // 发送温湿度数据到串口
            char buffer[50];
            sprintf(buffer, "Temperature: %d°C, Humidity: %d%%rn", temperature, humidity);
            USART1_SendString(buffer);
        }

        Delay_ms(2000);  // 2秒钟读取一次数据
    }
}

将代码下载到STM32F103ZET6开发板上,接上DHT11。当成功运行时,环境温湿度数据会通过USART1串口打印出来。

3.2 读取土壤湿度值

通过ADC1的通道1采集土壤传感器的湿度值,打印到串口.

代码语言:javascript复制
#include "stm32f10x.h"
#include "stdio.h"

// 函数声明
void ADC_Configuration(void);
void UART_Configuration(void);
void USART1_SendChar(char ch);

int main(void)
{
    // 初始化ADC和串口
    ADC_Configuration();
    UART_Configuration();

    while (1)
    {
        // 启动ADC转换
        ADC_SoftwareStartConvCmd(ADC1, ENABLE);

        // 等待转换完成
        while (!ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC));

        // 读取ADC值
        uint16_t adcValue = ADC_GetConversionValue(ADC1);

        // 将ADC值转换为湿度百分比
        float humidity = (float)adcValue / 4095 * 100;

        // 将湿度值打印到串口
        char buffer[20];
        sprintf(buffer, "Humidity: %.2f%%rn", humidity);
        for (int i = 0; buffer[i] != ''; i  )
        {
            USART1_SendChar(buffer[i]);
        }

        // 延时一段时间
        for (int i = 0; i < 1000000; i  );
    }
}

// ADC配置
void ADC_Configuration(void)
{
    ADC_InitTypeDef ADC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;

    // 使能ADC1和GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);

    // 配置GPIOA.1为模拟输入
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_1;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // ADC配置
    ADC_DeInit(ADC1);
    ADC_InitStructure.ADC_Mode = ADC_Mode_Independent;
    ADC_InitStructure.ADC_ScanConvMode = DISABLE;
    ADC_InitStructure.ADC_ContinuousConvMode = DISABLE;
    ADC_InitStructure.ADC_ExternalTrigConv = ADC_ExternalTrigConv_None;
    ADC_InitStructure.ADC_DataAlign = ADC_DataAlign_Right;
    ADC_InitStructure.ADC_NbrOfChannel = 1;
    ADC_Init(ADC1, &ADC_InitStructure);

    // 配置ADC1的通道1为采样通道
    ADC_RegularChannelConfig(ADC1, ADC_Channel_1, 1, ADC_SampleTime_55Cycles5);

    // 使能ADC1
    ADC_Cmd(ADC1, ENABLE);

    // ADC校准
    ADC_ResetCalibration(ADC1);
    while (ADC_GetResetCalibrationStatus(ADC1));
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1));
}

// 串口配置
void UART_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    USART_InitTypeDef USART_InitStructure;

    // 使能USART1和GPIOA时钟
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1 | RCC_APB2Periph_GPIOA, ENABLE);

    // 配置USART1引脚
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN_FLOATING;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // USART配置
    USART_InitStructure.USART_BaudRate = 115200;
    USART_InitStructure.USART_WordLength = USART_WordLength_8b;
    USART_InitStructure.USART_StopBits = USART_StopBits_1;
    USART_InitStructure.USART_Parity = USART_Parity_No;
    USART_InitStructure.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
    USART_InitStructure.USART_Mode = USART_Mode_Tx;
    USART_Init(USART1, &USART_InitStructure);

    // 使能USART1
    USART_Cmd(USART1, ENABLE);
}

// 发送字符到USART1
void USART1_SendChar(char ch)
{
    USART_SendData(USART1, (uint8_t)ch);
    while (USART_GetFlagStatus(USART1, USART_FLAG_TXE) == RESET);
}

以上代码使用STM32的标准库函数进行配置和操作。在ADC_Configuration函数中进行ADC的初始化配置,包括GPIO引脚配置、ADC时钟使能、通道配置等。 在UART_Configuration函数中进行串口USART1的初始化配置,包括GPIO引脚配置、波特率设置等。

在主函数中,进入一个无限循环。在循环中,启动ADC转换,并等待转换完成。 通过ADC_GetConversionValue函数读取ADC转换结果,将其转换为湿度百分比。 使用sprintf函数将湿度值格式化为字符串,并使用USART1_SendChar函数将字符串逐个字符发送到USART1串口。 通过延时函数进行一段时间的延时,以控制打印速率。

3.3 大棚补光灯控制

以下是使用STM32F103ZET6读取BH1750光照传感器输出的光照强度,并根据阈值控制LED补光灯灯开关实现代码:

代码语言:javascript复制
#include "stm32f10x.h"
#include "i2c.h"
#include "delay.h"

#define BH1750_ADDRESS 0x23

void BH1750_Init()
{
    // 初始化I2C总线
    I2C_Init();
}

void BH1750_Start()
{
    // 启动BH1750测量
    uint8_t cmd = 0x01; // 单次高分辨率模式
    I2C_Start();
    I2C_SendByte(BH1750_ADDRESS);
    I2C_WaitAck();
    I2C_SendByte(cmd);
    I2C_WaitAck();
    I2C_Stop();
}

uint16_t BH1750_Read()
{
    // 读取BH1750测量结果
    uint16_t lux;
    I2C_Start();
    I2C_SendByte(BH1750_ADDRESS   1); // 发送读命令
    I2C_WaitAck();
    lux = I2C_ReceiveByte() << 8; // 读取高字节
    I2C_Ack();
    lux |= I2C_ReceiveByte(); // 读取低字节
    I2C_NAck();
    I2C_Stop();
    return lux;
}

void LED_Control(uint8_t state)
{
    // 控制LED照明灯开关
    if (state)
        GPIO_SetBits(GPIOA, GPIO_Pin_8); // 打开LED
    else
        GPIO_ResetBits(GPIOA, GPIO_Pin_8); // 关闭LED
}

int main(void)
{
    // 初始化GPIO口
    GPIO_InitTypeDef GPIO_InitStructure;
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_8;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, &GPIO_InitStructure);

    // 初始化BH1750传感器
    BH1750_Init();

    while (1)
    {
        // 启动测量
        BH1750_Start();

        // 延时等待测量完成
        DelayMs(200);

        // 读取光照强度
        uint16_t lux = BH1750_Read();

        // 判断阈值并控制LED
        if (lux > 1000)
            LED_Control(1); // 光照强度高于阈值,打开LED
        else
            LED_Control(0); // 光照强度低于阈值,关闭LED
    }
}

代码中初始化I2C总线和BH1750传感器,通过BH1750_Init()函数实现。在主循环中,启动测量延时等待测量完成。使用BH1750_Read()函数读取测量结果,即光照强度。根据阈值判断光照强度是否高于设定值,通过LED_Control()函数控制LED的开关状态。

四、总结

本项目基于STM32微控制器实现了一个蔬菜大棚温湿度智能控制系统。系统的主控芯片采用了STM32F103ZET6,用于控制和协调各个硬件模块的工作。系统包括空气温湿度采集模块(DHT11)、土壤湿度采集模块(ADC接口)、通风风机(5V小风扇 继电器控制)、照明灯(LED白色灯模块)、灌溉系统(抽水电机 继电器控制)以及LCD显示屏。

系统的功能包括温湿度的实时监测、土壤湿度的检测、通风风扇的自动控制、灌溉系统的自动控制和数据的显示。通过按键设置土壤湿度阈值,实现自动浇水功能,当土壤湿度低于阈值时,系统自动开启灌溉系统进行浇水。同时,根据设定的温度阈值,系统自动控制通风风扇进行降温。

蔬菜大棚温湿度智能控制系统利用STM32微控制器和各种传感器实现了对环境参数的监测和控制,提高了蔬菜大棚的自动化程度和生产效率。同时,通过自动控制灌溉和通风系统,能够更好地满足蔬菜生长的需求,提高农作物的产量和质量。

0 人点赞