Raspberry pie science experiment 4B--04_ Temperature and humidity sensor

Posted by allenskd on Fri, 28 Jan 2022 23:12:05 +0100

Small experiment catalogue

Raspberry pie science experiment
001 turn on the first LED
002 light up the LED lamp group
003_ Switch control LED light
004_ Obtain temperature and humidity

Tip: after writing the article, the directory can be generated automatically. For how to generate it, please refer to the help document on the right

preface

In this experiment, we will contact with the clock signal and receive the high and low levels. Byte conversion

Tip: the following is the main content of this article. The following cases can be used for reference

1, Experimental components

1 Experimental components


2 connected GPIO pin

Pins used: 4, 17, 27, 19

2, Process of communication between DHT11 and MCU or raspberry pie

Because python cannot control the accuracy of us clock, it will be unable to obtain or incomplete to obtain the signal when actually receiving. But the basic principle is shown in the figure below.

3, Code part

The open source project on github is cited here: https://github.com/szazo/DHT11_Python

1. Actual working code of DHT11

# -*- coding: utf-8 -*-
"""
#This is a class of DHT11, which will complete the initialization of the sensor, obtain the value, convert the value to hexadecimal and return it. This article refers to: https://github.com/szazo/DHT11_Python
 
version = '0.0.1'
make day=2022-01-26
"""
__docformat__ = "restructuredtext en"

__all__ = []

__license__ = "MIT license"

import time

import RPi.GPIO as GPIO
import RPi


# Define a display class for display and output
class DHT11Result:
    'DHT11 Return from DHT11.read()Value read in'
    ERR_NO_ERROR = 0
    ERR_MISSING_DATA = 1
    ERR_CRC = 2
    error_code = ERR_NO_ERROR
    temperature = -1
    humidity = -1

    def __init__(self, error_code, temperature, humidity):
        self.error_code = error_code
        self.temperature = temperature
        self.humidity = humidity

    def is_valid(self):
        return self.error_code == DHT11Result.ERR_NO_ERROR


# Class that defines probe initialization and acquisition
class DHT11:
    __pin = 0 #Define the value of Pin

    def __init__(self, pin):
        self.__pin = pin

    def __send_and_sleep(self, output, sleep):
        '''
        Set the transmission level value and sleep time
        :param output: Level value
        :param sleep: Dormancy time
        :return:
        '''
        GPIO.output(self.__pin, output)
        time.sleep(sleep)

    def __collect_input(self):
        '''
         This part completes the collection of input data
        '''
        unchanged_count = 0
        max_unchanged_count = 100

        last=-1
        data=[]
        while True:
            curent=GPIO.input(self.__pin)
            data.append(curent)
            if last != curent:
                unchanged_count=0
                last=curent

            else:
                unchanged_count+=1
                if unchanged_count>max_unchanged_count:
                    break
         
        return data

    def __parse_data_pull_up_lengths(self, data):
        '''
         This part completes the preliminary processing of the data
        ''' 
        STATE_INIT_PULL_DOWN = 1
        STATE_INIT_PULL_UP = 2
        STATE_DATA_FIRST_PULL_DOWN = 3
        STATE_DATA_PULL_UP = 4
        STATE_DATA_PULL_DOWN = 5

        state = STATE_INIT_PULL_DOWN

        lengths = []  # will contain the lengths of data pull up periods
        current_length = 0  # will contain the length of the previous period

        for i in range(len(data)):

            current = data[i]
            current_length += 1
            print(f"id:{i},value:{current},state:{state}")
            if state == STATE_INIT_PULL_DOWN:
                if current == RPi.GPIO.LOW:
                    # ok, we got the initial pull down
                    state = STATE_INIT_PULL_UP
                    continue
                else:
                    continue
            if state == STATE_INIT_PULL_UP:
                if current == RPi.GPIO.HIGH:
                    # ok, we got the initial pull up
                    state = STATE_DATA_FIRST_PULL_DOWN
                    continue
                else:
                    continue
            if state == STATE_DATA_FIRST_PULL_DOWN:
                if current == RPi.GPIO.LOW:
                    # we have the initial pull down, the next will be the data pull up
                    state = STATE_DATA_PULL_UP
                    continue
                else:
                    continue
            if state == STATE_DATA_PULL_UP:
                if current == RPi.GPIO.HIGH:
                    # data pulled up, the length of this pull up will determine whether it is 0 or 1
                    current_length = 0
                    state = STATE_DATA_PULL_DOWN
                    continue
                else:
                    continue
            if state == STATE_DATA_PULL_DOWN:
                if current == RPi.GPIO.LOW:
                    # pulled down, we store the length of the previous pull up period
                    lengths.append(current_length)
                    state = STATE_DATA_PULL_UP
                    continue
                else:
                    continue
         
        return lengths

    def __calculate_bits(self, pull_up_lengths):
        '''
         This part completes the shaping of the data
        ''' 
        # find shortest and longest period
        shortest_pull_up = 1000
        longest_pull_up = 0

        for i in range(0, len(pull_up_lengths)):
            length = pull_up_lengths[i]
           
            if length < shortest_pull_up:
                shortest_pull_up = length
            if length > longest_pull_up:
                longest_pull_up = length

        # use the halfway to determine whether the period it is long or short
        halfway = shortest_pull_up + (longest_pull_up - shortest_pull_up) / 2
       
        bits = []

        for i in range(0, len(pull_up_lengths)):
            bit = False
            if pull_up_lengths[i] > halfway:
                bit = True
            bits.append(bit)
      
        return bits

    def __bits_to_bytes(self, bits):
        '''
         bit turn bytes
        ''' 
        the_bytes = []
        byte = 0

        for i in range(0, len(bits)):
            print(f"left before:{byte}")
            byte = byte << 1
            print(f"left after:{byte}")
            if (bits[i]):
                byte = byte | 1
            else:
                byte = byte | 0
            if ((i + 1) % 8 == 0):
                the_bytes.append(byte)
                byte = 0
        print(f"the_bytes:{the_bytes}")
        return the_bytes

    def __calculate_checksum(self, the_bytes):
        return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255

    def read(self):
       '''
         Read data
        ''' 
        RPi.GPIO.setup(self.__pin, RPi.GPIO.OUT)

        # send initial high
        self.__send_and_sleep(RPi.GPIO.HIGH, 0.05)

        # pull down to low
        self.__send_and_sleep(RPi.GPIO.LOW, 0.02)

        # change to input using pull up
        RPi.GPIO.setup(self.__pin, RPi.GPIO.IN, RPi.GPIO.PUD_UP)

        # collect data into an array
        data = self.__collect_input()

        # parse lengths of all data pull up periods
        pull_up_lengths = self.__parse_data_pull_up_lengths(data)

        # if bit count mismatch, return error (4 byte data + 1 byte checksum)
        if len(pull_up_lengths) != 40:
            return DHT11Result(DHT11Result.ERR_MISSING_DATA, 0, 0)

        # calculate bits from lengths of the pull up periods
        bits = self.__calculate_bits(pull_up_lengths)

        # we have the bits, calculate bytes
        the_bytes = self.__bits_to_bytes(bits)

        # calculate checksum and check
        checksum = self.__calculate_checksum(the_bytes)
        if the_bytes[4] != checksum:
            return DHT11Result(DHT11Result.ERR_CRC, 0, 0)

        # ok, we have valid data

        # The meaning of the return sensor values
        # the_bytes[0]: humidity int
        # the_bytes[1]: humidity decimal
        # the_bytes[2]: temperature int
        # the_bytes[3]: temperature decimal

        temperature = the_bytes[2] + float(the_bytes[3]) / 10
        humidity = the_bytes[0] + float(the_bytes[1]) / 10

        return DHT11Result(DHT11Result.ERR_NO_ERROR, temperature, humidity)

 

2 outer calling code

# -*- coding: utf-8 -*-
"""
#This experiment was completed using DHT11 to read temperature and relative humidity
author = "Derek Tian"
version = '0.0.1'
make day=2022-01-25
"""
__docformat__ = "restructuredtext en"

__all__ = []

__license__ = "MIT license"

import time

import RPi.GPIO as GPIO
import DHT11 as dht11
import time
import datetime

# initialize GPIO
GPIO.setwarnings(True)
GPIO.setmode(GPIO.BCM)
# read data using pin 14
instance = dht11.DHT11(pin=4)

try:
    while True:
        result = instance.read()
        if result.is_valid():
            print("Last valid input: " + str(datetime.datetime.now()))

            print("Temperature: %-3.1f C" % result.temperature)
            print("Humidity: %-3.1f %%" % result.humidity)

        time.sleep(6)

except KeyboardInterrupt:
    print("Cleanup")
    GPIO.cleanup()

4, Code analysis

1) In the main function:

RPI used here GPIO library to directly operate GPIO

import RPi.GPIO as GPIO

Set the BCM mode to operate GPIO, set the pin of DHT11 connection to 4, and initialize the working class of DHT11

GPIO.setwarnings(True)
GPIO.setmode(GPIO.BCM)
# read data using pin 14
instance = dht11.DHT11(pin=4)

Use a loop to get the value:

 while True:
        result = instance.read()
        if result.is_valid():
            print("Last valid input: " + str(datetime.datetime.now()))

            print("Temperature: %-3.1f C" % result.temperature)
            print("Humidity: %-3.1f %%" % result.humidity)

2) For dth11 Analysis of PY

(1) The function of transmitting signal is defined, and the sleep time is also defined here (this is the key, because it is the digital way of using software to simulate PWM). Because it needs to be called many times, it is abstracted as an independent function

 def __send_and_sleep(self, output, sleep):
        GPIO.output(self.__pin, output)
        time.sleep(sleep)

(2)__ collect_ The input() function receives the collected value

 def __collect_input(self):
        '''
         This part completes the collection of input data
        '''
        unchanged_count = 0
        max_unchanged_count = 100

        last=-1
        data=[]
        while True:
            curent=GPIO.input(self.__pin)
            
            data.append(curent)
            if last != curent:
                unchanged_count=0
                last=curent

            else:
                unchanged_count+=1
                if unchanged_count>max_unchanged_count:
                    break
         
        return data

Receive the signal through the cycle, collect the digital value, and set the maximum number of reading failures to 100. The collected digital signal is 0 or 1
You can print and display it by adding print
(3)__ parse_ data_ pull_ up_ The length function completes the sorting of the types of original data collection
The key of the program is to define five states

        STATE_INIT_PULL_DOWN = 1 #Pre low level before transmission
        STATE_INIT_PULL_UP = 2  #The pre high level before transmission, which will tell the host to prepare for reception
        STATE_DATA_FIRST_PULL_DOWN = 3  # Send low level flag data ready to send
        STATE_DATA_PULL_UP = 4  #send data
        STATE_DATA_PULL_DOWN = 5  #End of data transmission

Through the cyclic processing of the received data packet, pick out the effective data frame for storage. The following is the processing output for each bit value:

When the data state changes to "STATE_DATA_PULL_UP" and the new bit is high, it will be initialized

 if state == STATE_DATA_PULL_UP:
                if current == RPi.GPIO.HIGH:
                    # data pulled up, the length of this pull up will determine whether it is 0 or 1
                    current_length = 0
                    state = STATE_DATA_PULL_DOWN
                    continue
                else:
                    continue

When the data status is "STATE_DATA_PULL_DOWN" and the new bit is a low-level value, write the value of count into the list

            if state == STATE_DATA_PULL_DOWN:
                if current == RPi.GPIO.LOW:
                    # pulled down, we store the length of the previous pull up period
                    lengths.append(current_length)
                    state = STATE_DATA_PULL_UP
                    continue
                else:
                    continue

Final returned value:
(4)__ calculate_bits function completes the work of changing the value from count value to binary value. Here, the author adopts the way of intermediate value to judge.
Set the initial upper and lower boundaries, 01000 Since the maximum received byte effective bit cannot exceed 1000, it is set to 1000 here (because it is wasteful)

        shortest_pull_up = 1000
        longest_pull_up = 0

By finding the upper and lower boundaries, the intermediate value is calculated

 for i in range(0, len(pull_up_lengths)):
            length = pull_up_lengths[i]
            print(f"length:{length}|shortest_pull_up:{shortest_pull_up}|longest_pull_up:{longest_pull_up}")
            if length < shortest_pull_up:
                shortest_pull_up = length
            if length > longest_pull_up:
                longest_pull_up = length

        # use the halfway to determine whether the period it is long or short
        halfway = shortest_pull_up + (longest_pull_up - shortest_pull_up) / 2

Generate binary list according to intermediate value judgment

 for i in range(0, len(pull_up_lengths)):
            bit = False
            if pull_up_lengths[i] > halfway:
                bit = True
            bits.append(bit)

(5)__ bits_to_bytes obtains the collected value by bit calculation:
Here, the author completes the conversion from binary to decimal by shifting the bit bit to the left. When the bit bit is continuously shifted to the left to the 8th bit, the value will be a hexadecimal value.

 for i in range(0, len(bits)):
            print(f"left before:{byte}")
            byte = byte << 1
            print(f"left after:{byte}")
            if (bits[i]):
                byte = byte | 1
            else:
                byte = byte | 0
            if ((i + 1) % 8 == 0):
                the_bytes.append(byte)

(6)__ calculate_checksum writes the generated 4-bit value into an array, which is not discussed here.

Read process of red()
In function initialization, the first step is to set the working direction of PIN pin: (output, input). Here, it is set as output, which is used to activate DHT11 module

 RPi.GPIO.setup(self.__pin, RPi.GPIO.OUT)

"Call"__ send_ and_ "Sleep" function to control the pin
Output a high level and sleep for 0.05 seconds to activate DHT11 module

  self.__send_and_sleep(RPi.GPIO.HIGH, 0.05)

Output a low level and sleep for 0.02 seconds, ready to receive the output value of DHT11 module

 self.__send_and_sleep(RPi.GPIO.LOW, 0.02)

After sleeping for 0.02 seconds, turn the PIN pin into the input mode and set the high level as the signal acquisition bit

RPi.GPIO.setup(self.__pin, RPi.GPIO.IN, RPi.GPIO.PUD_UP)

Receive PIN input

data = self.__collect_input() 

Call the function to perform state transition on the received data frame

 pull_up_lengths = self.__parse_data_pull_up_lengths(data)

Generate 4 8-byte data and 1 8-byte validation bit data,

 if len(pull_up_lengths) != 40:
            return DHT11Result(DHT11Result.ERR_MISSING_DATA, 0, 0)
 In fact, the validity bit data is the first four byte Sum of data.

The last 63 is the validation bit

# calculate bits from lengths of the pull up periods
bits = self.__calculate_bits(pull_up_lengths)

    # we have the bits, calculate bytes
    the_bytes = self.__bits_to_bytes(bits)

    # calculate checksum and check
    checksum = self.__calculate_checksum(the_bytes)
    if the_bytes[4] != checksum:
        return DHT11Result(DHT11Result.ERR_CRC, 0, 0)

    # ok, we have valid data

    # The meaning of the return sensor values
    # the_bytes[0]: humidity int
    # the_bytes[1]: humidity decimal
    # the_bytes[2]: temperature int
    # the_bytes[3]: temperature decimal

    temperature = the_bytes[2] + float(the_bytes[3]) / 10
    humidity = the_bytes[0] + float(the_bytes[1]) / 10

    return DHT11Result(DHT11Result.ERR_NO_ERROR, temperature, humidity)

  Modify the code and add key handling and in the outermost calling function LED Light display (on when pressed, on when there is data)
# -*- coding: utf-8 -*-
"""
#This experiment was completed using DHT11 to read temperature and relative humidity
author = "Derek Tian"
version = '0.0.1'
make day=2022-01-25
"""
__docformat__ = "restructuredtext en"

__all__ = []

__license__ = "MIT license"

import time
from signal import pause

import RPi.GPIO as GPIO
import DHT11 as dht11
import time
import datetime
from  gpiozero import LED,Button

# initialize GPIO
GPIO.setwarnings(True)
GPIO.setmode(GPIO.BCM)
# read data using pin 14
instance = dht11.DHT11(pin=4)

button=Button(19)
red=LED(17)
yellow=LED(27)
bl=LED(5)
bl.off()
def getValue():
    red.on()
    result = instance.read()
    if result.is_valid():
        print("Last valid input: " + str(datetime.datetime.now()))
        print("Temperature: %-3.1f C" % result.temperature)
        print("Humidity: %-3.1f %%" % result.humidity)
        yellow.on()
    else:
        yellow.off()
        print("not get value !!!!")

def fGetValue():
    red.off()
    yellow.off()


if __name__=='__main__':
 try:
    button.when_pressed = getValue
    button.when_released = fGetValue

    pause()
    # while True:
    #     result = instance.read()
    #     if result.is_valid():
    #         print("Last valid input: " + str(datetime.datetime.now()))
    #
    #         print("Temperature: %-3.1f C" % result.temperature)
    #         print("Humidity: %-3.1f %%" % result.humidity)
    #
    #     time.sleep(6)

 except KeyboardInterrupt:
     print("Cleanup")
     GPIO.cleanup()

Final effect:

After pressing the key, the red light is on, and the value is obtained successfully (the yellow light is on):


After pressing the key, the red light is on, the value acquisition fails, and the yellow light is not on:

Summary:

For DHT11, it is very important to obtain the clock information of the signal. If the clock cannot be obtained correctly, it is impossible to obtain the value or activate the sensor. python is not very good at this kind of code that needs us to control. It is better to use c or golang.

Topics: Single-Chip Microcomputer IoT Raspberry Pi