Thứ Bảy, 13 tháng 8, 2022

Đọc module cảm biến nhiệt độ, áp suất BMP280 bằng Micropython với Raspberry Pi Pico

Module cảm biến BMP280 giá rẻ có thể dùng để đo nhiệt độ, áp suất khí quyển. Trong phần này chúng ta đi đọc các thông tin này bằng Raspberry Pi Pico với MicroPython.

Bước 1: Đầu tiên chúng ta nối sơ đồ như sau:

Sơ đồ kết nối



Module cảm biến BMP280


Bước 2: Tải thư viện này về và lưu vào bộ nhớ của Pico với tên là bmp280.py
from machine import I2C
import time

# BMP280 default address.
BMP280_I2CADDR = 0x76

# Operating Modes
BMP280_OSAMPLE_1 = 1
BMP280_OSAMPLE_2 = 2
BMP280_OSAMPLE_4 = 3
BMP280_OSAMPLE_8 = 4
BMP280_OSAMPLE_16 = 5

# BMP280 Registers

BMP280_REGISTER_DIG_T1 = 0x88  # Trimming parameter registers
BMP280_REGISTER_DIG_T2 = 0x8A
BMP280_REGISTER_DIG_T3 = 0x8C

BMP280_REGISTER_DIG_P1 = 0x8E
BMP280_REGISTER_DIG_P2 = 0x90
BMP280_REGISTER_DIG_P3 = 0x92
BMP280_REGISTER_DIG_P4 = 0x94
BMP280_REGISTER_DIG_P5 = 0x96
BMP280_REGISTER_DIG_P6 = 0x98
BMP280_REGISTER_DIG_P7 = 0x9A
BMP280_REGISTER_DIG_P8 = 0x9C
BMP280_REGISTER_DIG_P9 = 0x9E

BMP280_REGISTER_DIG_H1 = 0xA1
BMP280_REGISTER_DIG_H2 = 0xE1
BMP280_REGISTER_DIG_H3 = 0xE3
BMP280_REGISTER_DIG_H4 = 0xE4
BMP280_REGISTER_DIG_H5 = 0xE5
BMP280_REGISTER_DIG_H6 = 0xE6
BMP280_REGISTER_DIG_H7 = 0xE7

BMP280_REGISTER_CHIPID = 0xD0
BMP280_REGISTER_VERSION = 0xD1
BMP280_REGISTER_SOFTRESET = 0xE0

BMP280_REGISTER_CONTROL_HUM = 0xF2
BMP280_REGISTER_CONTROL = 0xF4
BMP280_REGISTER_CONFIG = 0xF5
BMP280_REGISTER_PRESSURE_DATA = 0xF7
BMP280_REGISTER_TEMP_DATA = 0xFA
BMP280_REGISTER_HUMIDITY_DATA = 0xFD


class Device:
  """Class for communicating with an I2C device.

  Allows reading and writing 8-bit, 16-bit, and byte array values to
  registers on the device."""

  def __init__(self, address, i2c):
    """Create an instance of the I2C device at the specified address using
    the specified I2C interface object."""
    self._address = address
    self._i2c = i2c

  def writeRaw8(self, value):
    """Write an 8-bit value on the bus (without register)."""
    value = value & 0xFF
    self._i2c.writeto(self._address, value)

  def write8(self, register, value):
    """Write an 8-bit value to the specified register."""
    b=bytearray(1)
    b[0]=value & 0xFF
    self._i2c.writeto_mem(self._address, register, b)

  def write16(self, register, value):
    """Write a 16-bit value to the specified register."""
    value = value & 0xFFFF
    b=bytearray(2)
    b[0]= value & 0xFF
    b[1]= (value>>8) & 0xFF
    self.i2c.writeto_mem(self._address, register, value)

  def readRaw8(self):
    """Read an 8-bit value on the bus (without register)."""
    return int.from_bytes(self._i2c.readfrom(self._address, 1),'little') & 0xFF

  def readU8(self, register):
    """Read an unsigned byte from the specified register."""
    return int.from_bytes(
        self._i2c.readfrom_mem(self._address, register, 1),'little') & 0xFF

  def readS8(self, register):
    """Read a signed byte from the specified register."""
    result = self.readU8(register)
    if result > 127:
      result -= 256
    return result

  def readU16(self, register, little_endian=True):
    """Read an unsigned 16-bit value from the specified register, with the
    specified endianness (default little endian, or least significant byte
    first)."""
    result = int.from_bytes(
        self._i2c.readfrom_mem(self._address, register, 2),'little') & 0xFFFF
    if not little_endian:
      result = ((result << 8) & 0xFF00) + (result >> 8)
    return result

  def readS16(self, register, little_endian=True):
    """Read a signed 16-bit value from the specified register, with the
    specified endianness (default little endian, or least significant byte
    first)."""
    result = self.readU16(register, little_endian)
    if result > 32767:
      result -= 65536
    return result

  def readU16LE(self, register):
    """Read an unsigned 16-bit value from the specified register, in little
    endian byte order."""
    return self.readU16(register, little_endian=True)

  def readU16BE(self, register):
    """Read an unsigned 16-bit value from the specified register, in big
    endian byte order."""
    return self.readU16(register, little_endian=False)

  def readS16LE(self, register):
    """Read a signed 16-bit value from the specified register, in little
    endian byte order."""
    return self.readS16(register, little_endian=True)

  def readS16BE(self, register):
    """Read a signed 16-bit value from the specified register, in big
    endian byte order."""
    return self.readS16(register, little_endian=False)


class BMP280:
  def __init__(self, mode=BMP280_OSAMPLE_1, address=BMP280_I2CADDR, i2c=None,
               **kwargs):
    # Check that mode is valid.
    if mode not in [BMP280_OSAMPLE_1, BMP280_OSAMPLE_2, BMP280_OSAMPLE_4,
                    BMP280_OSAMPLE_8, BMP280_OSAMPLE_16]:
        raise ValueError(
            'Unexpected mode value {0}. Set mode to one of '
            'BMP280_ULTRALOWPOWER, BMP280_STANDARD, BMP280_HIGHRES, or '
            'BMP280_ULTRAHIGHRES'.format(mode))
    self._mode = mode
    # Create I2C device.
    if i2c is None:
      raise ValueError('An I2C object is required.')
    self._device = Device(address, i2c)
    # Load calibration values.
    self._load_calibration()
    self._device.write8(BMP280_REGISTER_CONTROL, 0x3F)
    self.t_fine = 0

  def _load_calibration(self):

    self.dig_T1 = self._device.readU16LE(BMP280_REGISTER_DIG_T1)
    self.dig_T2 = self._device.readS16LE(BMP280_REGISTER_DIG_T2)
    self.dig_T3 = self._device.readS16LE(BMP280_REGISTER_DIG_T3)

    self.dig_P1 = self._device.readU16LE(BMP280_REGISTER_DIG_P1)
    self.dig_P2 = self._device.readS16LE(BMP280_REGISTER_DIG_P2)
    self.dig_P3 = self._device.readS16LE(BMP280_REGISTER_DIG_P3)
    self.dig_P4 = self._device.readS16LE(BMP280_REGISTER_DIG_P4)
    self.dig_P5 = self._device.readS16LE(BMP280_REGISTER_DIG_P5)
    self.dig_P6 = self._device.readS16LE(BMP280_REGISTER_DIG_P6)
    self.dig_P7 = self._device.readS16LE(BMP280_REGISTER_DIG_P7)
    self.dig_P8 = self._device.readS16LE(BMP280_REGISTER_DIG_P8)
    self.dig_P9 = self._device.readS16LE(BMP280_REGISTER_DIG_P9)

    self.dig_H1 = self._device.readU8(BMP280_REGISTER_DIG_H1)
    self.dig_H2 = self._device.readS16LE(BMP280_REGISTER_DIG_H2)
    self.dig_H3 = self._device.readU8(BMP280_REGISTER_DIG_H3)
    self.dig_H6 = self._device.readS8(BMP280_REGISTER_DIG_H7)

    h4 = self._device.readS8(BMP280_REGISTER_DIG_H4)
    h4 = (h4 << 24) >> 20
    self.dig_H4 = h4 | (self._device.readU8(BMP280_REGISTER_DIG_H5) & 0x0F)

    h5 = self._device.readS8(BMP280_REGISTER_DIG_H6)
    h5 = (h5 << 24) >> 20
    self.dig_H5 = h5 | (
        self._device.readU8(BMP280_REGISTER_DIG_H5) >> 4 & 0x0F)

  def read_raw_temp(self):
    """Reads the raw (uncompensated) temperature from the sensor."""
    meas = self._mode
    self._device.write8(BMP280_REGISTER_CONTROL_HUM, meas)
    meas = self._mode << 5 | self._mode << 2 | 1
    self._device.write8(BMP280_REGISTER_CONTROL, meas)
    sleep_time = 1250 + 2300 * (1 << self._mode)

    sleep_time = sleep_time + 2300 * (1 << self._mode) + 575
    sleep_time = sleep_time + 2300 * (1 << self._mode) + 575
    time.sleep_us(sleep_time)  # Wait the required time
    msb = self._device.readU8(BMP280_REGISTER_TEMP_DATA)
    lsb = self._device.readU8(BMP280_REGISTER_TEMP_DATA + 1)
    xlsb = self._device.readU8(BMP280_REGISTER_TEMP_DATA + 2)
    raw = ((msb << 16) | (lsb << 8) | xlsb) >> 4
    return raw

  def read_raw_pressure(self):
    """Reads the raw (uncompensated) pressure level from the sensor."""
    """Assumes that the temperature has already been read """
    """i.e. that enough delay has been provided"""
    msb = self._device.readU8(BMP280_REGISTER_PRESSURE_DATA)
    lsb = self._device.readU8(BMP280_REGISTER_PRESSURE_DATA + 1)
    xlsb = self._device.readU8(BMP280_REGISTER_PRESSURE_DATA + 2)
    raw = ((msb << 16) | (lsb << 8) | xlsb) >> 4
    return raw

  def read_raw_humidity(self):
    """Assumes that the temperature has already been read """
    """i.e. that enough delay has been provided"""
    msb = self._device.readU8(BMP280_REGISTER_HUMIDITY_DATA)
    lsb = self._device.readU8(BMP280_REGISTER_HUMIDITY_DATA + 1)
    raw = (msb << 8) | lsb
    return raw

  def read_temperature(self):
    """Get the compensated temperature in 0.01 of a degree celsius."""
    adc = self.read_raw_temp()
    var1 = ((adc >> 3) - (self.dig_T1 << 1)) * (self.dig_T2 >> 11)
    var2 = ((
        (((adc >> 4) - self.dig_T1) * ((adc >> 4) - self.dig_T1)) >> 12) *
        self.dig_T3) >> 14
    self.t_fine = var1 + var2
    return (self.t_fine * 5 + 128) >> 8

  def read_pressure(self):
    """Gets the compensated pressure in Pascals."""
    adc = self.read_raw_pressure()
    var1 = self.t_fine - 128000
    var2 = var1 * var1 * self.dig_P6
    var2 = var2 + ((var1 * self.dig_P5) << 17)
    var2 = var2 + (self.dig_P4 << 35)
    var1 = (((var1 * var1 * self.dig_P3) >> 8) +
            ((var1 * self.dig_P2) >> 12))
    var1 = (((1 << 47) + var1) * self.dig_P1) >> 33
    if var1 == 0:
      return 0
    p = 1048576 - adc
    p = (((p << 31) - var2) * 3125) // var1
    var1 = (self.dig_P9 * (p >> 13) * (p >> 13)) >> 25
    var2 = (self.dig_P8 * p) >> 19
    return ((p + var1 + var2) >> 8) + (self.dig_P7 << 4)
    '''
    self._calc_t_fine()
    if self._p == 0:
        var1 = self._t_fine - 128000
        var2 = var1 * var1 * self._P6
        var2 = var2 + ((var1 * self._P5) << 17)
        var2 = var2 + (self._P4 << 35)
        var1 = ((var1 * var1 * self._P3) >> 8) + ((var1 * self._P2) << 12)
        var1 = (((1 << 47) + var1) * self._P1) >> 33

        if var1 == 0:
            return 0

        p = 1048576 - self._p_raw
        p = int((((p << 31) - var2) * 3125) / var1)
        var1 = (self._P9 * (p >> 13) * (p >> 13)) >> 25
        var2 = (self._P8 * p) >> 19

        p = ((p + var1 + var2) >> 8) + (self._P7 << 4)
        self._p = p / 256.0
    return self._p
    '''

  def read_humidity(self):
    adc = self.read_raw_humidity()
    # print 'Raw humidity = {0:d}'.format (adc)
    h = self.t_fine - 76800
    h = (((((adc << 14) - (self.dig_H4 << 20) - (self.dig_H5 * h)) +
         16384) >> 15) * (((((((h * self.dig_H6) >> 10) * (((h *
                          self.dig_H3) >> 11) + 32768)) >> 10) + 2097152) *
                          self.dig_H2 + 8192) >> 14))
    h = h - (((((h >> 15) * (h >> 15)) >> 7) * self.dig_H1) >> 4)
    h = 0 if h < 0 else h
    h = 419430400 if h > 419430400 else h
    return h >> 12

  @property
  def temperature(self):
    "Return the temperature in degrees."
    t = self.read_temperature()/100
    return t

  @property
  def pressure(self):
    "Return the pressure in Pa."
    p = self.read_pressure() / 256
    p = p + 1300 #Just for fine exactly!
    return p

  @property
  def humidity(self):
    "Return the humidity in percent."
    h = self.read_humidity()
    hi = h // 1024
    hd = h * 100 // 1024 - hi * 100
    return "{}.{:02d}%".format(hi, hd)



Bước 3: Copy đoạn code này về lưu vào Pico và chạy.

from machine import Pin, I2C
from time import sleep
#Nhập thư viện bmp280.py
import bmp280

# ESP32 - Pin assignment
#i2c = I2C(scl=Pin(22), sda=Pin(21), freq=10000)

# ESP8266 - Pin assignment option1: sda=12(D6), scl=14(D5), option2: sda=12(D6), scl=13(D7)
#i2c = I2C(scl=Pin(14), sda=Pin(12), freq=10_000)
#Raspberry Pico
i2c = I2C(0, scl = Pin(1), sda = Pin(0), freq = 100_000)

#Khai báo địa chỉ thiết bị i2c
addressI2C_Device = 0x76
readSensorOk=True
#Thử kết nối xem có được hay không, nếu không thì báo lỗi
try:
  camBienBMP = bmp280.BMP280(i2c=i2c, address=addressI2C_Device)
  #hum = bmp.humidity #for module bme280
except OSError as e:
  print('Không tìm được cảm biến i2c, xem lại địa chỉ thiết bị, hoặc chân kết nối SDA, SCL')
  readSensorOk=False
  devices = i2c.scan()
  if len(devices)>0:
      for x in devices : print(hex(x), end=' ')

def altitude_HYP(Pa , temperatureC):
    # Hypsometric Equation (Max Altitude < 11 Km above sea level)
    temperatureK = temperatureC+273.15
    local_pressure = Pa
    sea_level_pressure = 101325 # hPa      
    pressure_ratio = sea_level_pressure/local_pressure # sea level pressure = 101325 Pa
    h = (((pressure_ratio**(1/5.257)) - 1) * temperatureK ) / 0.0065
    return h


# altitude from international barometric formula, given in BMP 180 datasheet
def altitude_IBF(pressure_Pa):
    local_pressure = pressure_Pa/100    # Unit : hPa
    sea_level_pressure = 1013.25 # Unit : hPa
    pressure_ratio = local_pressure / sea_level_pressure
    altitude = 44330*(1-(pressure_ratio**(1/5.255)))
    return altitude


count=0
while readSensorOk==True:
    nhietDo = camBienBMP.temperature
    apSuatKhiQuyen = camBienBMP.pressure
    print("#"+str(count)+"s")
    count +=2
    print("Nhiệt độ:", nhietDo,chr(186)+"C")
    print("Áp suất khí quyển:", apSuatKhiQuyen, "Pa (Pascal) =",apSuatKhiQuyen/101325,"atm")
    print("Độ cao so với mực nước biển:",altitude_HYP(apSuatKhiQuyen,nhietDo),"meter (Hypsometric Formula)")
    print("Độ cao so với mực nước biển:",altitude_IBF(apSuatKhiQuyen),"meter (International Barometric Formula)\n")
    sleep(2)




Không có nhận xét nào:

Đăng nhận xét