Raspberry Pi Pico: BME280 – получение температуры, влажности и давления (MicroPython)

Узнайте, как использовать модуль датчика BME280 с платой Raspberry Pi Pico на MicroPython для получения показаний температуры, влажности и давления. Это краткое руководство, которое поможет вам быстро начать работу с датчиком BME280 и платой Raspberry Pi Pico.

Raspberry Pi Pico с BME280 - получение температуры, влажности и давления MicroPython

Примечание

Впервые работаете с Raspberry Pi Pico? Начните знакомство с Raspberry Pi Pico с вводного руководства.

Содержание:

Предпочитаете программировать Raspberry Pi Pico в Arduino IDE? Тогда ознакомьтесь с этим руководством: Raspberry Pi Pico – BME280: получение температуры, влажности и давления (Arduino IDE).

Прошивка MicroPython

Для выполнения этого руководства вам необходима прошивка MicroPython, установленная на вашу плату Raspberry Pi Pico. Также вам понадобится IDE для написания и загрузки кода на плату.

Рекомендуемая IDE для работы с MicroPython на Raspberry Pi Pico – это Thonny IDE. Следуйте приведенному ниже руководству, чтобы узнать, как установить Thonny IDE, прошить firmware MicroPython и загрузить код на плату.

Знакомство с модулем датчика BME280

Модуль датчика BME280 измеряет барометрическое давление, температуру и влажность. Поскольку давление изменяется с высотой, вы также можете оценить высоту над уровнем моря. Существует несколько версий этого модуля датчика, но мы будем использовать версию, показанную на рисунке ниже.

Датчик BME280 - температура, влажность и давление

Этот датчик использует протокол связи I2C, поэтому подключение очень простое. Вы можете использовать любые выводы I2C на Raspberry Pi Pico для подключения датчика BME280. Мы будем использовать GPIO 4 (SDA) и GPIO 5 (SCL) – подробнее о GPIO Raspberry Pi Pico.

BME280

Raspberry Pi Pico

Vin

3.3V (OUT)

GND

GND

SCL

GPIO 5

SDA

GPIO 4

Необходимые компоненты

Подключение BME280 к Raspberry Pi Pico - необходимые компоненты

Для этого проекта вам нужно подключить модуль датчика BME280 к выводам I2C Raspberry Pi Pico. Вот список компонентов, необходимых для этого руководства:

  • Модуль датчика BME280

  • Плата Raspberry Pi Pico

  • Макетная плата (breadboard)

  • Соединительные провода (jumper wires)

Подключение BME280 к Raspberry Pi Pico

Подключите BME280 к любой комбинации выводов I2C на Pico – мы будем использовать GPIO 4 (SDA) и GPIO 5 (SCL).

Подключение BME280 к Raspberry Pi Pico - схема соединений

Примечание

Рекомендуем прочитать: Raspberry Pi Pico и Pico W: распиновка и описание GPIO

Библиотека BME280 для MicroPython

Библиотека для чтения данных с датчика BME280 не входит в стандартную библиотеку MicroPython по умолчанию. Существуют различные модули/библиотеки, которые можно использовать для чтения данных с датчика BME280. Мы будем использовать следующую (адаптированную из библиотеки Adafruit BME280).

from machine import I2C
import time

# BME280 default address.
BME280_I2CADDR = 0x76

# Operating Modes
BME280_OSAMPLE_1 = 1
BME280_OSAMPLE_2 = 2
BME280_OSAMPLE_4 = 3
BME280_OSAMPLE_8 = 4
BME280_OSAMPLE_16 = 5

# BME280 Registers

BME280_REGISTER_DIG_T1 = 0x88  # Trimming parameter registers
BME280_REGISTER_DIG_T2 = 0x8A
BME280_REGISTER_DIG_T3 = 0x8C

BME280_REGISTER_DIG_P1 = 0x8E
BME280_REGISTER_DIG_P2 = 0x90
BME280_REGISTER_DIG_P3 = 0x92
BME280_REGISTER_DIG_P4 = 0x94
BME280_REGISTER_DIG_P5 = 0x96
BME280_REGISTER_DIG_P6 = 0x98
BME280_REGISTER_DIG_P7 = 0x9A
BME280_REGISTER_DIG_P8 = 0x9C
BME280_REGISTER_DIG_P9 = 0x9E

BME280_REGISTER_DIG_H1 = 0xA1
BME280_REGISTER_DIG_H2 = 0xE1
BME280_REGISTER_DIG_H3 = 0xE3
BME280_REGISTER_DIG_H4 = 0xE4
BME280_REGISTER_DIG_H5 = 0xE5
BME280_REGISTER_DIG_H6 = 0xE6
BME280_REGISTER_DIG_H7 = 0xE7

BME280_REGISTER_CHIPID = 0xD0
BME280_REGISTER_VERSION = 0xD1
BME280_REGISTER_SOFTRESET = 0xE0

BME280_REGISTER_CONTROL_HUM = 0xF2
BME280_REGISTER_CONTROL = 0xF4
BME280_REGISTER_CONFIG = 0xF5
BME280_REGISTER_PRESSURE_DATA = 0xF7
BME280_REGISTER_TEMP_DATA = 0xFA
BME280_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 BME280:
  def __init__(self, mode=BME280_OSAMPLE_1, address=BME280_I2CADDR, i2c=None,
               **kwargs):
    # Check that mode is valid.
    if mode not in [BME280_OSAMPLE_1, BME280_OSAMPLE_2, BME280_OSAMPLE_4,\
                    BME280_OSAMPLE_8, BME280_OSAMPLE_16]:
        raise ValueError(
            'Unexpected mode value {0}. Set mode to one of '
            'BME280_ULTRALOWPOWER, BME280_STANDARD, BME280_HIGHRES, or '
            'BME280_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(BME280_REGISTER_CONTROL, 0x3F)
    self.t_fine = 0

  def _load_calibration(self):

    self.dig_T1 = self._device.readU16LE(BME280_REGISTER_DIG_T1)
    self.dig_T2 = self._device.readS16LE(BME280_REGISTER_DIG_T2)
    self.dig_T3 = self._device.readS16LE(BME280_REGISTER_DIG_T3)

    self.dig_P1 = self._device.readU16LE(BME280_REGISTER_DIG_P1)
    self.dig_P2 = self._device.readS16LE(BME280_REGISTER_DIG_P2)
    self.dig_P3 = self._device.readS16LE(BME280_REGISTER_DIG_P3)
    self.dig_P4 = self._device.readS16LE(BME280_REGISTER_DIG_P4)
    self.dig_P5 = self._device.readS16LE(BME280_REGISTER_DIG_P5)
    self.dig_P6 = self._device.readS16LE(BME280_REGISTER_DIG_P6)
    self.dig_P7 = self._device.readS16LE(BME280_REGISTER_DIG_P7)
    self.dig_P8 = self._device.readS16LE(BME280_REGISTER_DIG_P8)
    self.dig_P9 = self._device.readS16LE(BME280_REGISTER_DIG_P9)

    self.dig_H1 = self._device.readU8(BME280_REGISTER_DIG_H1)
    self.dig_H2 = self._device.readS16LE(BME280_REGISTER_DIG_H2)
    self.dig_H3 = self._device.readU8(BME280_REGISTER_DIG_H3)
    self.dig_H6 = self._device.readS8(BME280_REGISTER_DIG_H7)

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

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

  def read_raw_temp(self):
    """Reads the raw (uncompensated) temperature from the sensor."""
    meas = self._mode
    self._device.write8(BME280_REGISTER_CONTROL_HUM, meas)
    meas = self._mode << 5 | self._mode << 2 | 1
    self._device.write8(BME280_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(BME280_REGISTER_TEMP_DATA)
    lsb = self._device.readU8(BME280_REGISTER_TEMP_DATA + 1)
    xlsb = self._device.readU8(BME280_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(BME280_REGISTER_PRESSURE_DATA)
    lsb = self._device.readU8(BME280_REGISTER_PRESSURE_DATA + 1)
    xlsb = self._device.readU8(BME280_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(BME280_REGISTER_HUMIDITY_DATA)
    lsb = self._device.readU8(BME280_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)

  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()
    ti = t // 100
    td = t - ti * 100
    return "{}.{:02d}C".format(ti, td)

  @property
  def pressure(self):
    "Return the temperature in hPa."
    p = self.read_pressure() // 256
    pi = p // 100
    pd = p - pi * 100
    return "{}.{:02d}hPa".format(pi, pd)

  @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)

Посмотреть исходный код

Загрузите приведенную выше библиотеку на вашу плату Raspberry Pi Pico (сохраните файл с именем BME280.py). Следуйте инструкциям ниже, чтобы узнать, как загрузить библиотеку с помощью Thonny IDE.

Если вы используете Thonny IDE, выполните следующие шаги:

1. Скопируйте код библиотеки в новый файл. Код библиотеки BME280 можно найти здесь.

Библиотека BME280 MicroPython в Thonny IDE

2. Перейдите в File > Save as…

Thonny IDE MicroPython - сохранение файла библиотеки на устройство

3. Выберите сохранение на «Raspberry Pi Pico»:

Сохранение файлов на Raspberry Pi Pico в Thonny IDE

4. Назовите файл BME280.py и нажмите кнопку OK:

Сохранение библиотеки BME280 на Raspberry Pi Pico в Thonny IDE

Вот и все. Библиотека загружена на вашу плату. Чтобы убедиться, что загрузка прошла успешно, перейдите в File > Save as… и выберите устройство MicroPython. Ваш файл должен быть в списке:

Библиотека BME280 сохранена на Raspberry Pi Pico в Thonny IDE

После загрузки библиотеки на плату вы можете использовать функции библиотеки в своем коде, импортировав ее.

BME280: давление, температура и влажность – код MicroPython

После загрузки библиотеки на Raspberry Pi Pico создайте новый файл с именем main.py и вставьте следующий код. Он просто выводит температуру, влажность и давление в консоль каждые 5 секунд.

# Complete project details at https://RandomNerdTutorials.com/raspberry-pi-pico-bme280-micropython/

from machine import Pin, I2C
from time import sleep
import BME280

# Initialize I2C communication
i2c = I2C(id=0, scl=Pin(5), sda=Pin(4), freq=10000)

while True:
    try:
        # Initialize BME280 sensor
        bme = BME280.BME280(i2c=i2c)

        # Read sensor data
        tempC = bme.temperature
        hum = bme.humidity
        pres = bme.pressure

        # Convert temperature to fahrenheit
        tempF = (bme.read_temperature()/100) * (9/5) + 32
        tempF = str(round(tempF, 2)) + 'F'

        # Print sensor readings
        print('Temperature: ', tempC)
        print('Temperature: ', tempF)
        print('Humidity: ', hum)
        print('Pressure: ', pres)

    except Exception as e:
        # Handle any exceptions during sensor reading
        print('An error occurred:', e)

    sleep(5)

Посмотреть исходный код

Как работает код

Сначала необходимо импортировать нужные библиотеки, включая модуль BME280, который вы загрузили ранее.

from machine import Pin, I2C
from time import sleep
import BME280

Задайте идентификатор I2C, выводы и частоту. Мы используем GPIO 5 и 4, но любые другие выводы I2C тоже должны работать.

i2c = I2C(id=0, scl=Pin(5), sda=Pin(4), freq=10000)

Примечание

GPIO 5 и 4 относятся к I2C id=0 – проверьте другие комбинации и их идентификаторы в руководстве по распиновке.

В цикле while создайте объект BME280 с именем bme с определенными ранее выводами I2C:

bme = BME280.BME280(i2c=i2c)

По умолчанию библиотека предполагает, что ваш датчик BME280 имеет стандартный I2C-адрес 0x76 (если вы откроете код библиотеки, вы увидите адрес, определенный в строке 5: BME280_I2CADDR = 0x76).

Если ваш датчик не работает, возможно, он имеет другой I2C-адрес. Чтобы проверить I2C-адрес, ознакомьтесь со следующим руководством.

Если после запуска I2C-сканера, который мы предоставили ранее, ваш датчик имеет другой I2C-адрес, измените эту строку:

bme = BME280.BME280(i2c=i2c)

На:

bme = BME280.BME280(i2c=i2c, addr=YOUR_I2C_ADDRESS)

Например, если ваш I2C-адрес 0x78, это будет выглядеть так:

bme = BME280.BME280(i2c=i2c, addr=0x78)

Чтение температуры, влажности и давления так же просто, как использование методов temperature, humidity и pressure объекта bme.

temp = bme.temperature
hum = bme.humidity
pres = bme.pressure

Если вы хотите отображать температуру в градусах Фаренгейта, просто раскомментируйте (удалите #) следующие две строки:

# temp = (bme.read_temperature()/100) * (9/5) + 32
# temp = str(round(temp, 2)) + 'F'

Наконец, выведите показания в оболочку:

print('Temperature: ', temp)
print('Humidity: ', hum)
print('Pressure: ', pres)

В конце мы добавляем задержку в 5 секунд:

sleep(5)

Мы используем try и except для перехвата любых непредвиденных ошибок, которые могут возникнуть при попытке чтения данных с датчика.

except Exception as e:
    print('An error occurred:', e)

Демонстрация

Сохраните код на плату Raspberry Pi Pico с помощью Thonny IDE или любой другой MicroPython IDE по вашему выбору.

Raspberry Pi Pico с BME280

Скопируйте предоставленный код MicroPython в новый файл в Thonny IDE.

Raspberry Pi Pico Thonny IDE - чтение температуры, влажности и давления с BME280

После копирования кода в файл нажмите на значок Save. Затем выберите Raspberry Pi Pico.

Сохранение файла на Raspberry Pi Pico в Thonny IDE

Сохраните файл с именем: main.py.

Сохранение файла main.py на Raspberry Pi Pico в Thonny IDE

Примечание

Когда вы называете файл main.py, Raspberry Pi Pico будет автоматически запускать этот файл при загрузке. Если вы дадите ему другое имя, он все равно будет сохранен в файловой системе платы, но не будет запускаться автоматически при загрузке.

Нажмите маленькую зеленую кнопку «Run Current Script» или клавишу F5.

Запуск скрипта в Thonny IDE

Новые показания температуры и влажности будут выводиться в оболочку приблизительно каждые пять секунд.

Raspberry Pi Pico - вывод температуры, влажности и давления с BME280

Устранение неполадок

Ваш BME280 не работает? Возможные причины: неправильное подключение, неверный I2C-адрес, сломанный датчик или поддельный датчик BME280. Ознакомьтесь с руководством по устранению неполадок BME280, чтобы найти причину и решение вашей проблемы.

Заключение

В этом руководстве вы узнали, как подключить датчик температуры, влажности и давления BME280 к Raspberry Pi Pico и как написать программу на MicroPython для получения и отображения показаний в консоли MicroPython. Это один из самых простых примеров для начала работы с датчиком BME280.

Надеемся, что это руководство было для вас полезным. У нас есть руководства для других популярных датчиков окружающей среды: