[ODYSSEY-STM32MP157C] Drive UART to read sensor data

In the previous section "[ODYSSEY-STM32MP157C] Driving GPIO to achieve breathing light", we have driven GPIO to realize the breathing light function. In this section, we will operate STM32MP157C's UART2 serial port on Linux to communicate with the sensor and print out the sensor data.

Insert picture description here

Prepare materials

Introduction to PMS5003ST

PMS5003ST is an air quality sensor of Panteng Technology, which can simultaneously monitor the concentration of particulate matter (PM1.0, PM2.5, PM10), formaldehyde concentration and ambient temperature and humidity in the air. Due to the principle of laser scattering, it can be continuously collected with high accuracy and stability.

Insert picture description here

At the same time, the sensor module outputs in the form of a universal digital interface, which simplifies the development of the control terminal.

Insert picture description here

The digital interface of PMS5003ST is defined as follows:

Pin Features Description STM32MP157C
PIN1 VCC Positive power supply (+5V) ④ 5V
PIN2 GND Power negative ⑥ GND
PIN3 SET Setting pin (3.3V)
PIN4 RXD Serial port receiving pin (3.3V) ⑧ PF5/USART2_TX
PIN5 TXD Serial port sending pin (3.3V) ⑩ PD6/USART2_RX
PIN6 RESET Module reset signal (3.3V, low reset)
PIN7 NC -
PIN8 PWM/NC PWM output

In this case, we only need to connect the PIN1, PIN2, PIN4, and PIN5 of the sensor with pins 4, 6, 8, and 10 of the ODYSSEY-STM32MP157C expansion interface.

Insert picture description here

Install usart2 driver

  1. Before compiling, you need to download the kernel header file of the corresponding version.

    sudo apt update
    sudo apt install linux-headers-$(uname -r) -y
    

    Note: This step may need to open the proxy to complete!

  2. Download the seen-linux-dtoverlays repository, compile and install the stm32p1 driver.

    git clone https://github.com/Seeed-Studio/seeed-linux-dtoverlays
    

    Compile and install

    cd seeed-linux-dtoverlays
    make all_stm32mp1 CUSTOM_MOD_FILTER_OUT="jtsn-wm8960"
    sudo make install_stm32mp1 CUSTOM_MOD_FILTER_OUT="jtsn-wm8960"
    

    The corresponding ko file will be installed in the /lib/modules/4.19.9-stm32-r1/extra/seeed/ directory, and the dtbo file will be installed in the /lib/firmware/ directory.

  3. Modify the /boot/uEnv.txt file and add a line at the end of the file to load the usart2 driver.

    uboot_overlay_addr2=/lib/firmware/stm32mp1-seeed-usart2-overlay.dtbo
    
  4. reboot reboot the system, the system may see an increase of /dev/ttySTM2device nodes.

    Execution dmesg | grep ttySTM*View startup information:

    [    0.000000] Kernel command line: console=ttySTM0,115200 root=/dev/mmcblk0p6 ro rootfstype=ext4 rootwait coherent_poot
    [    1.060447] 4000e000.serial: ttySTM2 at MMIO 0x4000e000 (irq = 25, base_baud = 4000000) is a stm32-usart
    [    1.062277] 40010000.serial: ttySTM0 at MMIO 0x40010000 (irq = 27, base_baud = 4000000) is a stm32-usart
    [    1.062366] console [ttySTM0] enabled
    [    1.064147] 5c000000.serial: ttySTM1 at MMIO 0x5c000000 (irq = 70, base_baud = 4000000) is a stm32-usart
    [    1.064420] serial serial0: tty port ttySTM1 registered
    

    Execution cat /proc/tty/driver/stm32-usartView serial port driver:

    serinfo:1.0 driver revision:
    0: uart:stm32-usart mmio:0x40010000 irq:27 tx:1141 rx:63 RTS|CTS|DTR|DSR|CD
    1: uart:stm32-usart mmio:0x5C000000 irq:70 tx:44691 rx:2274 RTS|CTS|DTR|DSR|CD
    2: uart:stm32-usart mmio:0x4000E000 irq:25 tx:0 rx:0 CTS|DSR|CD
    

Read sensor data

In fact, if we connect the serial port on the sensor 2, you can cat /dev/ttySTM2obtain the sensor data. However, these are data in binary format, which we cannot understand, so we need to parse them.

Test the serial port

I choose to use Python to program, first install the serial and pyserial libraries.

pip3 install serial
pip3 install pyserial

After that, we can test in the Python interactive environment:

>>> import serial
>>> port = serial.Serial('/dev/ttySTM2', 9600)
>>> port.isOpen()
True
>>> port.close()

OK, no problem, we can start writing a parsing program!

Analytical data

Create a new show_pms5003st.py file and add the code frame:

import sys
import glob
import serial
import time

dev_name = '/dev/ttySTM2'
baudrate = 9600

CMD_READ = bytearray([0x42, 0x4d, 0xe2, 0x00, 0x00, 0x01, 0x71])
CMD_PASS = bytearray([0x42, 0x4d, 0xe1, 0x00, 0x00, 0x01, 0x70])
CMD_ACTI = bytearray([0x42, 0x4d, 0xe1, 0x00, 0x01, 0x01, 0x71])
CMD_STAN = bytearray([0x42, 0x4d, 0xe4, 0x00, 0x00, 0x01, 0x73])
CMD_NORM = bytearray([0x42, 0x4d, 0xe4, 0x00, 0x01, 0x01, 0x74])

def loop(serial):
    pass


def main():
    print("Run ODYSSEY-uart demo")
    s = serial.Serial(dev_name, baudrate)

    if not s.isOpen():
        s.open()

    try:
        s.write(CMD_PASS)
    except Exception as err:
        print(err)
    finally:
        time.sleep(1)

    loop(s)
    s.close()


if __name__ == '__main__':
    main()

PMS5003ST enters the active mode by default, that is, the sensor will actively send data out periodically, so the first thing I do after opening the serial port is to set it to passive mode, and the program will actively query the data. The format of the returned data is as follows:

Insert picture description here

Since a valid data consists of two bytes, I added a function to handle it:

def pms_value(hByte, lByte):
    return (hByte << 8 | lByte)

Then the focus of parsing is in the loop function, because every valid frame starts with 0x42+ 0x4d, so just recognize it and get the length of the frame for parsing. For the sake of convenience (lazy), I did not add the status record, only the data length of 36 bytes was parsed, and then verified, the valid data was extracted and printed out.

The loop function code is as follows:

def loop(serial):
    while True:
        serial.write(CMD_READ)

        start1 = serial.read(1)

        if (start1[0] == 0x42):
            start2 = serial.read(1)
            if (start2[0] == 0x4d):
                print("Is a frame")
            else:
                continue
        else:
            continue

        len1 = serial.read(1)
        len2 = serial.read(1)
        size = pms_value(len1[0], len2[0])

        if (size == 36):
            print("Is a response")
            resp = serial.read(size)

            for i in resp:
                print("{:x}".format(i), end=' ')
            print("")

            checksum = pms_value(resp[size-2], resp[size-1])

            dsum = start1[0] + start2[0] + len1[0] + len2[0]

            for i in range(0, size - 2):
                dsum = dsum + resp[i]

            dsum = dsum & 0xffff

            if (checksum != dsum):
                print("Checksum invalid. {} != {}".format(checksum, dsum))
                continue

            PM1_0_CF1  = pms_value(resp[0], resp[1])
            PM2_5_CF1  = pms_value(resp[2], resp[3])
            PM10_0_CF1 = pms_value(resp[4], resp[5])
            PM1_0_atm  = pms_value(resp[6], resp[7])
            PM2_5_atm  = pms_value(resp[8], resp[9])
            PM10_0_atm = pms_value(resp[10], resp[11])
            air_0_3um  = pms_value(resp[12], resp[13])
            air_0_5um  = pms_value(resp[14], resp[15])
            air_1_0um  = pms_value(resp[16], resp[17])
            air_2_5um  = pms_value(resp[18], resp[19])
            air_5_0um  = pms_value(resp[20], resp[21])
            air_10_0um = pms_value(resp[22], resp[23])
            hcho       = pms_value(resp[24], resp[25])
            temp       = pms_value(resp[26], resp[27])/10
            humi       = pms_value(resp[28], resp[29])/10
            version    = resp[32]
            errorCode  = resp[33]

            print("\nResponse => len: {} bytes, version: {:0>2x}, Error: {:0>2x}".format(size+4, version, errorCode))
            print("+-----------------------------------------------------+")
            print("|  CF=1  | PM1.0 = {:<4d} | PM2.5 = {:<4d} | PM10  = {:<4d} |".format(PM1_0_CF1, PM2_5_CF1, PM10_0_CF1))
            print("|  atm.  | PM1.0 = {:<4d} | PM2.5 = {:<4d} | PM10  = {:<4d} |".format(PM1_0_atm, PM2_5_atm, PM10_0_atm))
            print("|        | 0.3um = {:<4d} | 0.5um = {:<4d} | 1.0um = {:<4d} |".format(air_0_3um, air_0_5um, air_1_0um))
            print("|        | 2.5um = {:<4d} | 5.0um = {:<4d} | 10um  = {:<4d} |".format(air_2_5um, air_5_0um, air_10_0um))
            print("| extra  | hcho  = {:<4d} | temp  = {:<.1f} | humi  = {:<.1f} |".format(hcho, temp, humi))
            print("+-----------------------------------------------------+\n")

        time.sleep(3)

running result

Implementation python3 show_pms5003st.py, the operation is as follows:

Insert picture description here

Great, we have got the sensor data, in the next section we will learn how to upload this data to the cloud.


Insert picture description here

Guess you like

Origin blog.csdn.net/luckydarcy/article/details/108900941