Household tap water quality monitoring device based on single-chip microcomputer design

1. Preface

This article introduces a household tap water quality monitoring device based on single-chip microcomputer design. Using STM32F103ZET6 as the main control chip, combined with the water quality sensor and ADC module, the detection and monitoring functions of tap water quality are realized. Through the 0.96-inch OLED display, the collected water quality data is displayed to the user in an intuitive way.

As people pay more attention to health awareness and environmental protection, water quality safety has become an important issue in people’s lives. As one of the most important sources of drinking water in our daily lives, the safety of tap water is directly related to our health.

This design uses the advanced STM32F103ZET6 main control chip, which has powerful processing capabilities and rich peripheral interfaces. Through water quality sensors, analog signals related to water quality can be collected in real time. Then, the analog data is converted into digital signals through the ADC module, and then the corresponding water quality parameters are obtained through algorithm processing. Finally, the results are displayed on a 0.96-inch OLED display, so users can clearly understand the water quality of tap water.

Features of this device: easy to carry, simple operation, good real-time performance and high precision. Users only need to immerse the sensor in tap water to obtain water quality parameters and intuitively understand the water quality status through the display screen, providing a simple and convenient water quality monitoring solution for families.

image-20230814161901679

image-20230814161608644

2. Hardware selection

[1] Main control chip: STM32F103ZET6, which is a high-performance microcontroller based on the ARM Cortex-M3 core. It has rich peripheral interfaces and large storage capacity, and is suitable for data collection and processing of water quality sensors.

【2】Water quality sensor: A sensor for tap water quality monitoring.

【3】Display: Choose a 0.96-inch OLED display to display the collected water quality data on a small-sized device. OLED displays feature high contrast, low power consumption and fast refresh, making them suitable for embedded applications.

3. Common water quality sensors

Here are some common types of water quality sensors that can be used in home tap water quality monitoring devices:

【1】pH sensor: used to measure the acidity and alkalinity of water, that is, pH value. pH sensors are usually based on the glass electrode principle and can provide accurate pH values.

【2】Dissolved oxygen sensor: used to measure the dissolved oxygen content in water. Dissolved oxygen sensors can use membrane sensors or electrode sensors, which provide accurate values of dissolved oxygen concentration based on different measurement principles.

【3】Turbidity sensor: used to measure the concentration of suspended particles in water or the turbidity of water. Turbidity sensors can measure using either the light scattering principle or the light absorption principle.

【4】Conductivity sensor: used to measure the conductivity of water, that is, the conductivity of water. Conductivity sensors can provide total dissolved solids (TDS) values or salinity values in water.

4. Water quality standards for household tap water

The following are common water quality indicators and standard references:

【1】pH value: pH value indicates the acidity and alkalinity of water, which should generally be between 6.5-8.5.

[2] Turbidity: Turbidity represents the concentration of suspended particulate matter in water. The commonly used turbidity unit is NTU (Turbo Colorimetric Turbidity Unit). According to international standards, the turbidity of household tap water should be less than 1 NTU.

【4】Dissolved oxygen: Dissolved oxygen represents the dissolved oxygen content in water, usually in milligrams per liter (mg/L). For domestic tap water, the standard range for dissolved oxygen can be between 5-8 mg/L.

【5】Iron and manganese: Iron and manganese are common metal elements. High concentrations of iron and manganese will bring color and odor to the water. According to standards, the iron content in household tap water should be less than 0.3 mg/L, and the manganese content should be less than 0.1 mg/L.

【6】Fluoride: Fluoride is a beneficial trace element, but high concentrations of fluoride are harmful to the human body. Generally speaking, the fluoride content in household tap water should be less than 1.5 mg/L.

[7] Various harmful substances: Household tap water should comply with relevant national or regional regulations and standards to ensure that it does not contain harmful substances, such as heavy metals, organic pollutants, pesticide residues, etc.

5. Hardware code

5.1 Collection data display

#include "stm32f10x.h"
#include "oled.h"

//Define ADC acquisition channels and pins
#define ADC_CHANNEL 0 // Assume channel 0 of ADC1 is used
#define ADC_PIN GPIO_Pin_0
#define ADC_PORT GPIOA

//Define OLED screen related parameters
#define OLED_WIDTH 128
#define OLED_HEIGHT 64

//global variables
uint16_t adc_value = 0; // Value collected by ADC

// function declaration
void ADC_Configuration(void);
void OLED_Init(void);

int main(void)
{<!-- -->
    //Initialize ADC and OLED
    ADC_Configuration();
    OLED_Init();

    while (1)
    {<!-- -->
        // Start ADC conversion
        ADC_SoftwareStartConvCmd(ADC1, ENABLE);

        // Wait for ADC conversion to complete
        while (ADC_GetFlagStatus(ADC1, ADC_FLAG_EOC) == RESET);

        //Read ADC conversion result
        adc_value = ADC_GetConversionValue(ADC1);

        // Display the value collected by ADC on OLED
        char str[10];
        sprintf(str, "M", adc_value);
        OLED_ShowString(0, 0, str); // Display string at coordinates (0, 0)

        //Delay for a period of time
        for (uint32_t i = 0; i < 100000; i + + );
    }
}

//ADC configuration function
void ADC_Configuration(void)
{<!-- -->
    ADC_InitTypeDef ADC_InitStructure;
    GPIO_InitTypeDef GPIO_InitStructure;
    
    // Enable the clocks of ADC1 and GPIOA
    RCC_APB2PeriphClockCmd(RCC_APB2Periph_ADC1 | RCC_APB2Periph_GPIOA, ENABLE);
    
    // Configure the ADC pin as an analog input
    GPIO_InitStructure.GPIO_Pin = ADC_PIN;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AIN;
    GPIO_Init(ADC_PORT, &GPIO_InitStructure);
    
    //ADC configuration
    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, & amp;ADC_InitStructure);

    //Configure ADC channel
    ADC_RegularChannelConfig(ADC1, ADC_CHANNEL, 1, ADC_SampleTime_13Cycles5);

    // Enable ADC
    ADC_Cmd(ADC1, ENABLE);

    // Enable ADC calibration
    ADC_ResetCalibration(ADC1);
    while (ADC_GetResetCalibrationStatus(ADC1))
        ;
    ADC_StartCalibration(ADC1);
    while (ADC_GetCalibrationStatus(ADC1))
        ;

    //Set ADC conversion sequence
    ADC_RegularChannelConfig(ADC1, ADC_CHANNEL, 1, ADC_SampleTime_13Cycles5);
}

// OLED screen initialization function
void OLED_Init(void)
{<!-- -->
    //Initialize OLED screen
    OLED_Init();
    OLED_Clear();
}

5.2 oled.h

#ifndef __OLED_H__
#define __OLED_H__

#include "stm32f10x.h"

#define OLED_WIDTH 128
#define OLED_HEIGHT 64

void OLED_Init(void);
void OLED_Clear(void);
void OLED_WriteByte(uint8_t data, uint8_t cmd);
void OLED_SetPos(uint8_t x, uint8_t y);
void OLED_ShowChar(uint8_t x, uint8_t y, char ch);
void OLED_ShowString(uint8_t x, uint8_t y, const char* str);

#endif /* __OLED_H__ */

5.3 oled.c

#include "OLED.h"

const uint8_t OLED_GRAM[128][8] = {<!-- -->0}; // OLED display cache

void OLED_Init(void)
{<!-- -->
    // ... Initialize OLED screen related operations ...
}

void OLED_Clear(void)
{<!-- -->
    // Clear the OLED display cache (all black)
    memset((void*)OLED_GRAM, 0x00, sizeof(OLED_GRAM));

    //Update OLED screen display
    OLED_SetPos(0, 0);
    for (uint8_t i = 0; i < 8; i + + )
    {<!-- -->
        OLED_WriteByte(0xb0 + i, 0x00); //Set page address (0-7)
        OLED_WriteByte(0x00, 0x00); //Set the lower 4 bits of the column address (0-3)
        OLED_WriteByte(0x10, 0x00); //Set the high 4 bits of the column address (4-7)

        for (uint8_t j = 0; j < 128; j + + )
        {<!-- -->
            OLED_WriteByte(0x00, 0x40); // Write data, all black
        }
    }
}

void OLED_WriteByte(uint8_t data, uint8_t cmd)
{<!-- -->
    // ... specific operations for writing data to the OLED screen ...
}

void OLED_SetPos(uint8_t x, uint8_t y)
{<!-- -->
    //... Specific operations for setting the OLED screen display position...
}

void OLED_ShowChar(uint8_t x, uint8_t y, char ch)
{<!-- -->
    uint8_t c = ch - ' '; // Get the font in the font library

    if (x >= OLED_WIDTH || y >= OLED_HEIGHT) return;

    for (uint8_t i = 0; i < 6; i + + )
    {<!-- -->
        uint8_t line = cFont6x8[c][i];

        for (uint8_t j = 0; j < 8; j + + )
        {<!-- -->
            if (line & 0x01)
            {<!-- -->
                OLED_GRAM[y + j][x + i] = 1;
            }
            else
            {<!-- -->
                OLED_GRAM[y + j][x + i] = 0;
            }

            line >>= 1;
        }
    }

    //Update OLED screen display
    OLED_SetPos(x, y / 8);
    for (uint8_t i = 0; i < 8; i + + )
    {<!-- -->
        OLED_WriteByte(0xb0 + (y / 8) + i, 0x00); // Set page address
        OLED_WriteByte((x & amp; 0x0f), 0x00); // Set the lower 4 bits of the column address
        OLED_WriteByte((x >> 4) | 0x10, 0x00); // Set the high 4 bits of the column address

        for (uint8_t j = 0; j < 128; j + + )
        {<!-- -->
            OLED_WriteByte(OLED_GRAM[y + i][j], 0x40); //Write data
        }
    }
}

void OLED_ShowString(uint8_t x, uint8_t y, const char* str)
{<!-- -->
    while (*str != '\0')
    {<!-- -->
        OLED_ShowChar(x, y, *str + + );
        x + = 6;

        if (x >= OLED_WIDTH)
        {<!-- -->
            x = 0;
            y + = 8;
        }
    }
}