Ανάγνωση θερμοκρασίας, υγρασίας και πίεσης με Raspberry Pi Pico

Μάθετε πώς να χρησιμοποιείτε τη μονάδα αισθητήρα BMP280 με την πλακέτα Raspberry Pi Pico με MicroPython για να λαμβάνετε μετρήσεις θερμοκρασίας, (υγρασίας BME280) και πίεσης. Αυτό είναι ένα σύντομο κείμενο για να ξεκινήσετε γρήγορα τη διασύνδεση του αισθητήρα BME280 με την πλακέτα Raspberry Pi Pico. ΣΗΜΕΙΩΣΗ: Ορισμένοι προμηθευτές πωλούν μονάδες αισθητήρων BMP280 ως BME280. Είναι πολύ δύσκολο να πούμε τη διαφορά μεταξύ τους κατά την αγορά, επειδή είναι πολύ παρόμοιες. Οι αισθητήρες BME280 είναι συνήθως ακριβότεροι από τους αισθητήρες BMP280. Το μειονέκτημα της ύπαρξης ενός BMP280 είναι ότι δεν μετρά την υγρασία, μετρά μόνο τη θερμοκρασία και την πίεση.

Πώς να καταλάβετε εάν έχετε αισθητήρα BMP280; Στην παρακάτω εικόνα, μπορείτε να δείτε ότι το τσιπ αισθητήρα BME280 έχει τετράγωνο σχήμα, ενώ το BMP280 μοιάζει περισσότερο με ορθογώνιο σχήμα.

Παρουσιάζουμε τη μονάδα αισθητήρα BME280

Η μονάδες αισθητήρα BMP280/BME280 διαβάζουν τη βαρομετρική πίεση, τη θερμοκρασία και την υγρασία. Επειδή η πίεση αλλάζει με το υψόμετρο, μπορείτε επίσης να εκτιμήσετε το υψόμετρο. Υπάρχουν διάφορες εκδόσεις αυτής της μονάδας αισθητήρα. Ο αισθητήρας επικοινωνεί χρησιμοποιώντας πρωτόκολλο επικοινωνίας I2C, οπότε η καλωδίωση είναι πολύ απλή. Μπορείτε να χρησιμοποιήσετε οποιεσδήποτε ακίδες Raspberry Pi Pico I2C για να συνδέσετε τον αισθητήρα BME280. Θα χρησιμοποιήσουμε τις GPIO 4 (SDA) και GPIO 5 (SCL)—μάθετε περισσότερα για τα GPIOs του Raspberry Pi Pico.

ΒΜΕ280Βατόμουρο Pi Pico
Vcc3.3V (out)
GNDGND
SCLGPIO 5
SDAGPIO 4

Σύνδεση

Συνδέστε το BME280 σε οποιονδήποτε συνδυασμό ακίδων Pico I2C – θα χρησιμοποιήσουμε GPIO 4 (SDA) και GPIO 5 (SCL).

Βιβλιοθήκη 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 εδώ.

Κώδικας MicroPytho ανάγνωσης πίεσης, θερμοκρασίας και υγρασίας

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

Πώς λειτουργεί ο κώδικας

Πρώτον, πρέπει να εισαγάγετε τις απαραίτητες βιβλιοθήκες, συμπεριλαμβανομένου του ΒΜΕ280 λειτουργική μονάδα που έχετε εισαγάγει προηγουμένως.

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 – ελέγξτε εδώ άλλους συνδυασμούς και τα αναγνωριστικά τους.

Στο ενώ βρόχο, δημιουργήστε ένα ΒΜΕ280 αντικείμενο που ονομάζεται 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)

Η θερμοκρασία ανάγνωσης, η υγρασία και η πίεση είναι τόσο απλή όσο η χρήση του θερμοκρασία, υγρασία και πίεση Μέθοδοι σχετικά με την 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)

Χρησιμοποιούμε δοκιμάζω και εκτός για να εντοπίσετε τυχόν απροσδόκητα σφάλματα που ενδέχεται να προκύψουν κατά την προσπάθεια ανάγνωσης του αισθητήρα.

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