Household intelligent watering system based on single chip computer

1. Development environment

keil5, STM32CubeMX, Altium Designer

2. Hardware List

MCU: STM32F051K8Ux

Soil Moisture Sensor: TL-69

Temperature sensor: DS18B20 (digital sensor directly outputs digital signal)

OLED screen: OLED12864,

Water pump: L9110, etc.;

3. Functional design

The sensor collects the temperature and humidity of the air and soil, transmits the data to the single-chip microcomputer, and outputs it on the OLED display after being processed by the single-chip microcomputer.

4. Hardware connection

  • Connect the temperature sensor DS18B20, soil moisture sensor YL-69, water pump L9110 to the GPIO pin of STM32.
  • Connect the OLED screen OLED12862 to the I2C pin of the STM32.

5. Functional analysis

5.1 Overall functions

  • Use STM32’s GPIO library and I2C library to configure and read sensor data.
  • Write code to read data from temperature sensor DS18B20 and humidity sensor YL-69.
  • According to the sensor data, write a code to judge whether the plant needs to be irrigated. If it needs to be irrigated, use the GPIO library to control the switch of the water pump L9110.
  • Use the I2C library of STM32 to drive the oled display.
  • Write code to implement air temperature and soil moisture data to the OLED screen.

5.2 Sensor collection data

1. DS18B20 temperature sensor data acquisition:

– The DS18B20 is a digital temperature sensor that uses a single bus interface for communication. The microcontroller communicates with the DS18B20 through the GPIO port.

– During the communication process, the MCU sends instructions to DS18B20, such as the instruction to read the temperature.

– DS18B20 returns the temperature data to the microcontroller in the form of a sequence through the single bus. The MCU receives and parses this sequence to obtain the original temperature data of the DS18B20 sensor.

– The MCU can get the actual temperature value by reading the original temperature data of DS18B20 and performing corresponding calculations.

2. YL69 humidity sensor data collection:

– YL69 Humidity Sensor is an analog humidity sensor that outputs an analog voltage signal. It usually requires an analog-to-digital converter (ADC) to convert the analog signal into a digital signal for processing by the microcontroller.

– The microcontroller communicates with the YL69 humidity sensor through the GPIO port, and reads the analog voltage signal of the YL69 humidity sensor.

– The microcontroller inputs the analog voltage signal of the YL69 humidity sensor to the internal ADC module for conversion.

– The ADC module converts the analog voltage signal into a digital signal, and passes the converted digital data to the microcontroller.

– The single-chip microcomputer can read the digital data converted by the ADC and perform corresponding processing to obtain the humidity value of the YL69 humidity sensor.

It should be noted that the specific data acquisition methods and communication protocols may vary due to different microcontrollers, sensors and hardware platforms. Therefore, in practical applications, it is necessary to perform corresponding configuration and programming according to the requirements of the hardware and software platforms used, as well as the specifications and interfaces of the sensors. The above is a possible implementation, and specific implementation details may vary.

6. Code writing

1. GPIO pin configuration

You can select the corresponding pin in STM32CubeMX and configure it as push-pull output mode, and then generate the corresponding code.

void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    // Enable the clocks of GPIOA and GPIOB
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);

    // Configure the PA0 pin as a push-pull output
    GPIO_InitStructure.GPIO_Pin = PUMP_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, & GPIO_InitStructure);
}

2. Configure I2C

In STM32CubeMX, I2C peripherals can be selected through the graphical interface and configured accordingly, and then the corresponding code is generated.

Proceed as follows:

1. Open the STM32CubeMX software and create a new project.

2. Select the target STM32F051K8Ux microcontroller model.

3. In the “Pinout & amp; Configuration” tab, find the I2C1 peripheral and configure the related pins.

4. In the “Configuration” tab, find the I2C1 peripheral, and set related parameters, such as clock speed, address, etc.

5. After confirming that the configuration is correct, click the “Project” menu and select “Generate Code” to generate the code.

6. In the generated code, you can find the code similar to the `I2C_Configuration` function you provided.

void I2C_Configuration(void)
{
    I2C_InitTypeDef I2C_InitStructure;

    // Enable the clock of I2C1
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    // Configure the pins of I2C1
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, & GPIO_InitStructure);

    // Configure the parameters of I2C1
    I2C_DeInit(I2C1);
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_ClockSpeed = 100000;
    I2C_Init(I2C1, &I2C_InitStructure);

    // enable I2C1
    I2C_Cmd(I2C1, ENABLE);
}

3. Temperature acquisition function

void DS18B20_ReadTemperature(float *temperature)
{
    uint8_t buffer[2];

    // Send read temperature command
    I2C_GenerateSTART(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
        ;
    I2C_Send7bitAddress(I2C1, DS18B20_ADDRESS, I2C_Direction_Transmitter);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
        ;
    I2C_SendData(I2C1, 0x00);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
        ;
    I2C_GenerateSTOP(I2C1, ENABLE);

    // read temperature data
    I2C_GenerateSTART(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
        ;
    I2C_Send7bitAddress(I2C1, DS18B20_ADDRESS, I2C_Direction_Receiver);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
        ;
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))
        ;
    buffer[0] = I2C_ReceiveData(I2C1);
    I2C_AcknowledgeConfig(I2C1, DISABLE);
    I2C_GenerateSTOP(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))
        ;
    buffer[1] = I2C_ReceiveData(I2C1);

    // Calculate the temperature value
    *temperature = (float)((buffer[1] << 8) | buffer[0]) / 16.0;
}

The DS18B20_ReadTemperature function reads temperature data from a DS18B20 temperature sensor connected to the I2C bus. Here’s a step-by-step explanation of how it works:

1. Declare an array buffer to store 2 bytes of temperature data.

2. Send the command to read the temperature by generating a start condition on the I2C bus and selecting the sensor as the transmitter.

3. Wait for the master transmitter mode to be selected, then send the address of the temperature register (0x00) to the sensor.

4. Wait for the byte transfer to complete, then generate a STOP condition to end the transfer.

5. Send a command to read temperature data by generating a START condition on the I2C bus and selecting the sensor as the receiver.

6. Wait for master receiver mode to be selected, then wait for byte reception to complete.

7. Store the received byte in buffer[0].

8. Disable the acknowledge bit, indicating that no more bytes will be received.

9. Generate a STOP condition to end the transfer.

10. Wait for byte reception to complete and store it in buffer[1].

11. Calculate the temperature value by combining the two bytes in buffer and dividing by 16.0.

12. Store the temperature value in the memory location pointed to by the temperature pointer.

Overall, this function reads temperature data from a DS18B20 sensor and calculates the temperature in degrees Celsius. The temperature value is then stored in the memory location pointed to by the temperature pointer for further use.

4. Humidity acquisition function

void YL69_ReadHumidity(float *humidity)
{
    uint8_t buffer[2];

    // Send read humidity command
    I2C_GenerateSTART(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
        ;
    I2C_Send7bitAddress(I2C1, YL69_ADDRESS, I2C_Direction_Transmitter);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
        ;
    I2C_SendData(I2C1, 0x00);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
        ;
    I2C_GenerateSTOP(I2C1, ENABLE);

    // read humidity data
    I2C_GenerateSTART(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
        ;
    I2C_Send7bitAddress(I2C1, YL69_ADDRESS, I2C_Direction_Receiver);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
        ;
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))
        ;
    buffer[0] = I2C_ReceiveData(I2C1);
    I2C_AcknowledgeConfig(I2C1, DISABLE);
    I2C_GenerateSTOP(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))
        ;
    buffer[1] = I2C_ReceiveData(I2C1);

    // Calculate the humidity value
    *humidity = (float)((buffer[1] << 8) | buffer[0]) / 1024.0 * 100.0;
}

Read the humidity value of the YL69 humidity sensor and store the result in the memory location pointed by the humidity pointer.

1. Define a buffer array to store the data read from the sensor.

2. Send the read humidity command:

– Generate a start condition to start the I2C bus.

– Wait for a master mode selection event.

– Send the I2C address of the sensor and the direction of transmission (transmitter).

– Wait for master transmitter mode select event.

– Send command to read humidity data (0x00).

– Wait for master byte transfer complete event.

– A STOP condition is generated, ending the transfer.

3. Read humidity data:

– Generate a start condition to start the I2C bus.

– Wait for a master mode selection event.

– Sending sensor’s I2C address and transmission direction (receiver).

– Wait for master receiver mode select event.

– Wait for master byte receive complete event.

– Store the received bytes in buffer[0].

– Disable the acknowledge bit, indicating that no more bytes will be received.

– A STOP condition is generated, ending the transfer.

– Wait for master byte receive complete event.

– Store the received bytes in buffer[1].

4. Calculate the humidity value:

– Shift buffer[1] to the left by 8 bits and perform a bitwise OR operation with buffer[0] to obtain 16-bit humidity data.

– Convert the humidity data to floating point, divide by 1024.0 and multiply by 100.0 to get the humidity percentage value.

– Store the humidity percentage value in the memory location pointed to by the humidity pointer.

Overall, this code communicates with YL69 humidity sensor via I2C bus, sends read humidity command and reads humidity data. It then converts the read humidity data into a percentage value and stores the result in the memory location pointed to by the `humidity` pointer.

7. Complete code

#include "stm32f10x.h"
#include "stm32f10x_gpio.h"
#include "stm32f10x_i2c.h"
#include "stdio.h"

#define DS18B20_ADDRESS 0x48
#define YL69_ADDRESS 0x5C
#define PUMP_PIN GPIO_Pin_0

void GPIO_Configuration(void)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    // Enable the clocks of GPIOA and GPIOB
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA | RCC_APB2Periph_GPIOB, ENABLE);

    // Configure the PA0 pin as a push-pull output
    GPIO_InitStructure.GPIO_Pin = PUMP_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_Out_PP;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOA, & GPIO_InitStructure);
}

void I2C_Configuration(void)
{
    I2C_InitTypeDef I2C_InitStructure;

    // Enable the clock of I2C1
    RCC_APB1PeriphClockCmd(RCC_APB1Periph_I2C1, ENABLE);

    // Configure the pins of I2C1
    GPIO_InitTypeDef GPIO_InitStructure;
    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_6 | GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF_OD;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_50MHz;
    GPIO_Init(GPIOB, & GPIO_InitStructure);

    // Configure the parameters of I2C1
    I2C_DeInit(I2C1);
    I2C_InitStructure.I2C_Mode = I2C_Mode_I2C;
    I2C_InitStructure.I2C_DutyCycle = I2C_DutyCycle_2;
    I2C_InitStructure.I2C_OwnAddress1 = 0x00;
    I2C_InitStructure.I2C_Ack = I2C_Ack_Enable;
    I2C_InitStructure.I2C_AcknowledgedAddress = I2C_AcknowledgedAddress_7bit;
    I2C_InitStructure.I2C_ClockSpeed = 100000;
    I2C_Init(I2C1, &I2C_InitStructure);

    // enable I2C1
    I2C_Cmd(I2C1, ENABLE);
}

void DS18B20_ReadTemperature(float *temperature)
{
    uint8_t buffer[2];

    // Send read temperature command
    I2C_GenerateSTART(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
        ;
    I2C_Send7bitAddress(I2C1, DS18B20_ADDRESS, I2C_Direction_Transmitter);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
        ;
    I2C_SendData(I2C1, 0x00);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
        ;
    I2C_GenerateSTOP(I2C1, ENABLE);

    // read temperature data
    I2C_GenerateSTART(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
        ;
    I2C_Send7bitAddress(I2C1, DS18B20_ADDRESS, I2C_Direction_Receiver);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
        ;
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))
        ;
    buffer[0] = I2C_ReceiveData(I2C1);
    I2C_AcknowledgeConfig(I2C1, DISABLE);
    I2C_GenerateSTOP(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))
        ;
    buffer[1] = I2C_ReceiveData(I2C1);

    // Calculate the temperature value
    *temperature = (float)((buffer[1] << 8) | buffer[0]) / 16.0;
}

void YL69_ReadHumidity(float *humidity)
{
    uint8_t buffer[2];

    // Send read humidity command
    I2C_GenerateSTART(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
        ;
    I2C_Send7bitAddress(I2C1, YL69_ADDRESS, I2C_Direction_Transmitter);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_TRANSMITTER_MODE_SELECTED))
        ;
    I2C_SendData(I2C1, 0x00);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_TRANSMITTED))
        ;
    I2C_GenerateSTOP(I2C1, ENABLE);

    // read humidity data
    I2C_GenerateSTART(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_MODE_SELECT))
        ;
    I2C_Send7bitAddress(I2C1, YL69_ADDRESS, I2C_Direction_Receiver);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_RECEIVER_MODE_SELECTED))
        ;
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))
        ;
    buffer[0] = I2C_ReceiveData(I2C1);
    I2C_AcknowledgeConfig(I2C1, DISABLE);
    I2C_GenerateSTOP(I2C1, ENABLE);
    while (!I2C_CheckEvent(I2C1, I2C_EVENT_MASTER_BYTE_RECEIVED))
        ;
    buffer[1] = I2C_ReceiveData(I2C1);

    // Calculate the humidity value
    *humidity = (float)((buffer[1] << 8) | buffer[0]) / 1024.0 * 100.0;
}

void ControlPump(float temperature, float humidity)
{
    if (temperature > 25.0 & amp; & amp; humidity < 50.0) {
        // turn on the pump
        GPIO_SetBits(GPIOA, PUMP_PIN);
    } else {
        // turn off the pump
        GPIO_ResetBits(GPIOA, PUMP_PIN);
    }
}

void OLED_WriteString(uint8_t row, uint8_t col, char *str)
{
    // write string on OLED screen
    //...
}

void OLED_Clear(void)
{
    // Clear the OLED screen
    //...
}

void DisplayData(float temperature, float humidity)
{
    char str[16];

    // Clear the OLED screen
    OLED_Clear();

    // display temperature data
    sprintf(str, "Temp: %.2f C", temperature);
    OLED_WriteString(0, 0, str);

    // display humidity data
    sprintf(str, "Humidity: %.2f%%", humidity);
    OLED_WriteString(1, 0, str);
}

int main(void)
{
    float temperature, humidity;

    // Initialize GPIO and I2C
    GPIO_Configuration();
    I2C_Configuration();

    while (1) {
        // read temperature data
        DS18B20_ReadTemperature( &temperature);

        // read humidity data
        YL69_ReadHumidity( & amp; humidity);

        // control the water pump
        ControlPump(temperature, humidity);

        // display data on OLED screen
        DisplayData(temperature, humidity);

        // delay for a while
        //...
    }
}

The knowledge points of the article match the official knowledge files, and you can further learn relevant knowledge