Demo 8 - Using Air Pressure Sensor to Measure Height on VisionFive 2

According to StarFive, developers have successfully used Air Pressure Sensor on VisionFive 2, and the specific instructions are as follows:

1. Preparation

  • Development board: VisionFive/VisionFive 2
  • Air pressure sensor: BMP180
  • DuPont Line: Many

image

2. Principle of air pressure sensor
The specific air pressure sensor modules used in this demo are as follows:
image
The pressure sensor model is BMP180, which is used to measure the absolute pressure of the surrounding air (atmospheric pressure). Its measurement range is from 300hPa - 1100hPa with an accuracy as low as 0.02hPa. It can also measure temperature and obtain height values through conversion formulas.
image
The BMP180 air pressure sensor communicates through the I2C interface and can be easily connected to VisionFive. The modules used in this demo can be powered by 5V or 3.3V, but cannot be powered simultaneously.

In practical, influenced by the actual measurement environment, the actual pressure and temperature values measured by BMP180 are relatively accurate, but the calculated altitude is not entirely accurate because air flow, sunlight, and other factors may cause changes in atmospheric pressure and temperature, so the calculated altitude may differ by 0-100 meters. But in a relatively stable environment, reliable data can be obtained by measuring the height difference. For example, if a pressure value is measured on the ground, which is recorded as the basic pressure value, and then measure on different floors based on this basic value, the height value obtained is relatively accurate. For future delays, this method will be used to test the height of the floors.
image

3. The use of air pressure sensor
Firstly, refer to the following figure and connect the pressure sensor module to the VisionFive:
image
Connection:
image

Note:
The power supply voltage should be determined based on the actual sensor used. The pressure sensor module used in this demo uses a 5V power supply voltage.

After connecting the BMP180 sensor module to the board, you can use the is2tools to check whether the connection is normal.
Execute following to view the connection status of I2C devices:
i2cdetect -y -r 0
image
From the above figure, it can be seen that the BMP180 sensor module has been successfully connected, and its IIC communication address is 0x77.
To obtain information about BMP180, the board needs to use the IIC interface for communication, which can be assisted by a third-party module bmp180.

git clone https://github.com/m-rtijn/bmp180.git
pip install smbus

After downloading the third-party library bmp180, some modifications need to be made to the bmp180.py file in order to be used on the board, as follows:

"""
This program handles the communication over I2C between a Raspberry Pi and a
BMP180 Temperature/Pressure sensor.
Made by: MrTijn/Tijndagamer
Copyright 2015-2017
Released under the MIT license.
"""

import smbus
import math
from time import sleep

class bmp180:
    # Global variables
    address = None
    bus = smbus.SMBus(0) # 星光派开发板40Pin上的I2C使用i2c-0
    mode = 1 # TODO: Add a way to change the mode

    # BMP180 registers
    CONTROL_REG = 0xF4
    DATA_REG = 0xF6

    # Calibration data registers
    CAL_AC1_REG = 0xAA
    CAL_AC2_REG = 0xAC
    CAL_AC3_REG = 0xAE
    CAL_AC4_REG = 0xB0
    CAL_AC5_REG = 0xB2
    CAL_AC6_REG = 0xB4
    CAL_B1_REG = 0xB6
    CAL_B2_REG = 0xB8
    CAL_MB_REG = 0xBA
    CAL_MC_REG = 0xBC
    CAL_MD_REG = 0xBE

    # Calibration data variables
    calAC1 = 0
    calAC2 = 0
    calAC3 = 0
    calAC4 = 0
    calAC5 = 0
    calAC6 = 0
    calB1 = 0
    calB2 = 0
    calMB = 0
    calMC = 0
    calMD = 0


    def __init__(self, address):
        self.address = address

        # Get the calibration data from the BMP180
        self.read_calibration_data()

    # I2C methods

    def read_signed_16_bit(self, register):
        """Reads a signed 16-bit value.

        register -- the register to read from.
        Returns the read value.
        """
        msb = self.bus.read_byte_data(self.address, register)
        lsb = self.bus.read_byte_data(self.address, register + 1)

        if msb > 127:
            msb -= 256

        return (msb << 8) + lsb

    def read_unsigned_16_bit(self, register):
        """Reads an unsigned 16-bit value.

        Reads the given register and the following, and combines them as an
        unsigned 16-bit value.
        register -- the register to read from.
        Returns the read value.
        """
        msb = self.bus.read_byte_data(self.address, register)
        lsb = self.bus.read_byte_data(self.address, register + 1)

        return (msb << 8) + lsb

    # BMP180 interaction methods

    def read_calibration_data(self):
        """Reads and stores the raw calibration data."""
        self.calAC1 = self.read_signed_16_bit(self.CAL_AC1_REG)
        self.calAC2 = self.read_signed_16_bit(self.CAL_AC2_REG)
        self.calAC3 = self.read_signed_16_bit(self.CAL_AC3_REG)
        self.calAC4 = self.read_unsigned_16_bit(self.CAL_AC4_REG)
        self.calAC5 = self.read_unsigned_16_bit(self.CAL_AC5_REG)
        self.calAC6 = self.read_unsigned_16_bit(self.CAL_AC6_REG)
        self.calB1 = self.read_signed_16_bit(self.CAL_B1_REG)
        self.calB2 = self.read_signed_16_bit(self.CAL_B2_REG)
        self.calMB = self.read_signed_16_bit(self.CAL_MB_REG)
        self.calMC = self.read_signed_16_bit(self.CAL_MC_REG)
        self.calMD = self.read_signed_16_bit(self.CAL_MD_REG)

    def get_raw_temp(self):
        """Reads and returns the raw temperature data."""
        # Write 0x2E to CONTROL_REG to start the measurement
        self.bus.write_byte_data(self.address, self.CONTROL_REG, 0x2E)

        # Wait 4,5 ms
        sleep(0.0045)

        # Read the raw data from the DATA_REG, 0xF6
        raw_data = self.read_unsigned_16_bit(self.DATA_REG)

        # Return the raw data
        return raw_data

    def get_raw_pressure(self):
        """Reads and returns the raw pressure data."""
        # Write appropriate data to sensor to start the measurement
        self.bus.write_byte_data(self.address, self.CONTROL_REG, 0x34 + (self.mode << 6))

        # Sleep for 8 ms.
        # TODO: Way to use the correct wait time for the current mode
        sleep(0.008)

        MSB = self.bus.read_byte_data(self.address, self.DATA_REG)
        LSB = self.bus.read_byte_data(self.address, self.DATA_REG + 1)
        XLSB = self.bus.read_byte_data(self.address, self.DATA_REG + 2)

        raw_data = ((MSB << 16) + (LSB << 8) + XLSB) >> (8 - self.mode)

        return raw_data

    def get_temp(self):
        """Reads the raw temperature and calculates the actual temperature.

        The calculations used to get the actual temperature are from the BMP-180
        datasheet.
        Returns the actual temperature in degrees Celcius.
        """
        UT = self.get_raw_temp()

        X1 = 0
        X2 = 0
        B5 = 0
        actual_temp = 0.0

        X1 = ((UT - self.calAC6) * self.calAC5) / math.pow(2, 15)
        X2 = (self.calMC * math.pow(2, 11)) / (X1 + self.calMD)
        B5 = X1 + X2
        actual_temp = ((B5 + 8) / math.pow(2, 4)) / 10

        return actual_temp

    def get_pressure(self):
        """Reads and calculates the actual pressure.

        Returns the actual pressure in Pascal.
        """
        UP = self.get_raw_pressure()
        UT = self.get_raw_temp()
        B3 = 0
        B4 = 0
        B5 = 0
        B6 = 0
        B7 = 0
        X1 = 0
        X2 = 0
        X3 = 0
        pressure = 0

        # These calculations are from the BMP180 datasheet, page 15

        # Not sure if these calculations should be here, maybe they could be
        # removed?
        X1 = ((UT - self.calAC6) * self.calAC5) / math.pow(2, 15)
        X2 = (self.calMC * math.pow(2, 11)) / (X1 + self.calMD)
        B5 = X1 + X2

        # Todo: change math.pow cals to constants
        B6 = B5 - 4000
        X1 = (self.calB2 * (B6 * B6 / math.pow(2, 12))) / math.pow(2, 11)
        X2 = self.calAC2 * B6 / math.pow(2, 11)
        X3 = X1 + X2
        B3 = (((self.calAC1 * 4 + int(X3)) << self.mode) + 2) / 4
        X1 = self.calAC3 * B6 / math.pow(2, 13)
        X2 = (self.calB1 * (B6 * B6 / math.pow(2, 12))) / math.pow(2, 16)
        X3 = ((X1 + X2) + 2) / math.pow(2, 2)
        B4 = self.calAC4 * (X3 + 32768) / math.pow(2,15)
        B7 = (UP - B3) * (50000 >> self.mode)

        if B7 < 0x80000000:
            pressure = (B7 * 2) / B4
        else:
            pressure = (B7 / B4) * 2

        X1 = (pressure / math.pow(2, 8)) * (pressure / math.pow(2, 8))
        X1 = (X1 * 3038) / math.pow(2, 16)
        X2 = (-7357 * pressure) / math.pow(2, 16)
        pressure = pressure + (X1 + X2 + 3791) / math.pow(2, 4)

        return pressure

    def get_altitude(self, sea_level_pressure = 101325):
        """Calulates the altitude.

        This method calculates the altitude using the pressure.
        This method is not reliable when the sensor is inside.
        sea_level_pressure -- the pressure at the sea level closest to you in
        Pascal.
        Returns the altitude in meters.

        !!! This method probably does not work correctly. I've tried to test
        it but at the moment I have no way of verifying the data. !!!
        """
        altitude = 0.0
        pressure = float(self.get_pressure())

        # altitude = 44330.0 * (1.0 - math.pow(pressure / sea_level_pressure, 0.00019029495))
        altitude = 44330.0 * (1.0 - pow(pressure / sea_level_pressure, (1.0/5.255))) # 矫正计算结果
        altitude = round(altitude, 2)

        return altitude

if __name__ == "__main__":
    bmp = bmp180(0x77)
    print(bmp.get_temp())
    print(bmp.get_pressure())
    print(bmp.get_altitude())

Then, refer to the example and write the following program to read the required data:

# -*- coding: utf-8 -*-
# file: ~/projects/pressure/bmp180/read_data_from_bm180.py
from bmp180 import bmp180
import time

bmp = bmp180(0x77)

while True:
    temp = bmp.get_temp()
    pressure = bmp.get_pressure() / 1000
    # altitude = bmp.get_altitude() # 当参数为空时,用于测量基准值
    altitude = bmp.get_altitude(102225.9) # 以基准值为基础,测量高度值
    print("当前温度: %0.1f ℃" % temp)
    print("当前气压: %0.4f kPa" % pressure)
    print("可能高度: %0.2f 米\n" % altitude)
    time.sleep(2)

Then run read_data_from_bm180.py can output the actual measured data.

pyhon3 read_data_from_bm180.py

The actual results are as follows:
image
In actual use, it is necessary to use bmp.get_altitude() to measure the reference value, and then use bmp.get_altitude(baseline value) to measure the Elevation value based on this reference value.
In actual measurements, due to environmental influences, the result values may drift to a certain extent. It is possible to consider continuously reading multiple pieces of data and then using certain algorithms to obtain the final reasonable test results.

4. Summary
In this demo, we learned the basic use of air pressure sensors.
In fact, pressure sensors are widely used in indoor and outdoor navigation, weather forecasting, indoor environment monitoring, fan power control and many other occasions.

2 Likes