MicroPython : MQTT publie BME680 avec ESP32/ESP8266

MicroPython : MQTT publie BME680 avec ESP32/ESP8266

Apprenez à programmer les cartes ESP32 ou ESP8266 avec MicroPython pour publier les lectures du capteur BME680 (température, humidité, pression, qualité du gaz/air) via MQTT sur n’importe quelle plate-forme prenant en charge MQTT ou tout client MQTT. À titre d’exemple, nous publierons les lectures des capteurs sur le tableau de bord Node-RED.

MicroPython MQTT publier les lectures du capteur BME680 ESP32 ESP8266 température humidité pression qualité de l'air gaz

Lecture recommandée: Qu’est-ce que MQTT et comment ça marche

Noter: ce tutoriel est compatible avec les cartes de développement ESP32 et ESP8266.

Aperçu du projet

Le diagramme suivant montre une vue d’ensemble de haut niveau du projet que nous allons construire.

ESP32 ESP8266 Aperçu du projet NodeMCU MQTT Publier BME680 Température Humidité Pression Gaz Lectures
  • L’ESP demande des lectures de température et d’humidité du capteur BME680 ;
  • Les relevés de température sont publiés dans le esp/bme680/température sujet;
  • Les relevés d’humidité sont publiés dans le esp/bme680/humidité sujet;
  • Les relevés de pression sont publiés dans le esp/bme680/pression sujet;
  • Les relevés de pression sont publiés dans le esp/bme680/gaz sujet;
  • Node-RED est abonné à ces sujets ;
  • Node-RED reçoit les lectures des capteurs et les affiche sur des jauges ;
  • Vous pouvez recevoir les lectures sur n’importe quelle autre plate-forme prenant en charge MQTT et gérer les lectures comme vous le souhaitez.

Conditions préalables

Avant de poursuivre ce didacticiel, assurez-vous de remplir les conditions préalables suivantes :

Pour suivre ce tutoriel, vous avez besoin du firmware MicroPython installé dans vos cartes ESP32 ou ESP8266. Vous avez également besoin d’un IDE pour écrire et télécharger le code sur votre carte. Nous vous suggérons d’utiliser Thonny IDE ou uPyCraft IDE :

Courtier MQTT

Courtier Mosquitto MQTT

Pour utiliser MQTT, vous avez besoin d’un courtier. nous utiliserons Courtier en moustiques installé sur un Raspberry Pi. Lisez Comment installer Mosquitto Broker sur Raspberry Pi.

Si vous n’êtes pas familier avec MQTT, assurez-vous de lire notre didacticiel d’introduction : Qu’est-ce que MQTT et comment cela fonctionne

Pièces requises

Pour ce tutoriel, vous avez besoin des pièces suivantes :

Vous pouvez utiliser les liens précédents ou aller directement sur MakerAdvisor.com/tools pour trouver toutes les pièces pour vos projets au meilleur prix !

1641226333 844 MicroPython MQTT publie BME680 avec ESP32ESP8266

Bibliothèque umqtttsimple

Pour utiliser MQTT avec l’ESP32/ESP8266 et MicroPython, nous utiliserons le umqttsimplebibliothèque .py. Suivez les instructions suivantes pour l’IDE que vous utilisez :

  • A. Télécharger la bibliothèque umqttsimple avec uPyCraft IDE
  • B. Télécharger la bibliothèque umqttsimple avec Thonny IDE
try:
    import usocket as socket
except:
    import socket
import ustruct as struct
from ubinascii import hexlify

class MQTTException(Exception):
    pass

class MQTTClient:

    def __init__(self, client_id, server, port=0, user=None, password=None, keepalive=0,
                 ssl=False, ssl_params={}):
        if port == 0:
            port = 8883 if ssl else 1883
        self.client_id = client_id
        self.sock = None
        self.server = server
        self.port = port
        self.ssl = ssl
        self.ssl_params = ssl_params
        self.pid = 0
        self.cb = None
        self.user = user
        self.pswd = password
        self.keepalive = keepalive
        self.lw_topic = None
        self.lw_msg = None
        self.lw_qos = 0
        self.lw_retain = False

    def _send_str(self, s):
        self.sock.write(struct.pack("!H", len(s)))
        self.sock.write(s)

    def _recv_len(self):
        n = 0
        sh = 0
        while 1:
            b = self.sock.read(1)[0]
            n |= (b & 0x7f) << sh
            if not b & 0x80:
                return n
            sh += 7

    def set_callback(self, f):
        self.cb = f

    def set_last_will(self, topic, msg, retain=False, qos=0):
        assert 0 <= qos <= 2
        assert topic
        self.lw_topic = topic
        self.lw_msg = msg
        self.lw_qos = qos
        self.lw_retain = retain

    def connect(self, clean_session=True):
        self.sock = socket.socket()
        addr = socket.getaddrinfo(self.server, self.port)[0][-1]
        self.sock.connect(addr)
        if self.ssl:
            import ussl
            self.sock = ussl.wrap_socket(self.sock, **self.ssl_params)
        premsg = bytearray(b"x10")
        msg = bytearray(b"x04MQTTx04x02")

        sz = 10 + 2 + len(self.client_id)
        msg[6] = clean_session << 1
        if self.user is not None:
            sz += 2 + len(self.user) + 2 + len(self.pswd)
            msg[6] |= 0xC0
        if self.keepalive:
            assert self.keepalive < 65536
            msg[7] |= self.keepalive >> 8
            msg[8] |= self.keepalive & 0x00FF
        if self.lw_topic:
            sz += 2 + len(self.lw_topic) + 2 + len(self.lw_msg)
            msg[6] |= 0x4 | (self.lw_qos & 0x1) << 3 | (self.lw_qos & 0x2) << 3
            msg[6] |= self.lw_retain << 5

        i = 1
        while sz > 0x7f:
            premsg[i] = (sz & 0x7f) | 0x80
            sz >>= 7
            i += 1
        premsg[i] = sz

        self.sock.write(premsg, i + 2)
        self.sock.write(msg)
        #print(hex(len(msg)), hexlify(msg, ":"))
        self._send_str(self.client_id)
        if self.lw_topic:
            self._send_str(self.lw_topic)
            self._send_str(self.lw_msg)
        if self.user is not None:
            self._send_str(self.user)
            self._send_str(self.pswd)
        resp = self.sock.read(4)
        assert resp[0] == 0x20 and resp[1] == 0x02
        if resp[3] != 0:
            raise MQTTException(resp[3])
        return resp[2] & 1

    def disconnect(self):
        self.sock.write(b"xe0")
        self.sock.close()

    def ping(self):
        self.sock.write(b"xc0")

    def publish(self, topic, msg, retain=False, qos=0):
        pkt = bytearray(b"x30")
        pkt[0] |= qos << 1 | retain
        sz = 2 + len(topic) + len(msg)
        if qos > 0:
            sz += 2
        assert sz < 2097152
        i = 1
        while sz > 0x7f:
            pkt[i] = (sz & 0x7f) | 0x80
            sz >>= 7
            i += 1
        pkt[i] = sz
        #print(hex(len(pkt)), hexlify(pkt, ":"))
        self.sock.write(pkt, i + 1)
        self._send_str(topic)
        if qos > 0:
            self.pid += 1
            pid = self.pid
            struct.pack_into("!H", pkt, 0, pid)
            self.sock.write(pkt, 2)
        self.sock.write(msg)
        if qos == 1:
            while 1:
                op = self.wait_msg()
                if op == 0x40:
                    sz = self.sock.read(1)
                    assert sz == b"x02"
                    rcv_pid = self.sock.read(2)
                    rcv_pid = rcv_pid[0] << 8 | rcv_pid[1]
                    if pid == rcv_pid:
                        return
        elif qos == 2:
            assert 0

    def subscribe(self, topic, qos=0):
        assert self.cb is not None, "Subscribe callback is not set"
        pkt = bytearray(b"x82")
        self.pid += 1
        struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, self.pid)
        #print(hex(len(pkt)), hexlify(pkt, ":"))
        self.sock.write(pkt)
        self._send_str(topic)
        self.sock.write(qos.to_bytes(1, "little"))
        while 1:
            op = self.wait_msg()
            if op == 0x90:
                resp = self.sock.read(4)
                #print(resp)
                assert resp[1] == pkt[2] and resp[2] == pkt[3]
                if resp[3] == 0x80:
                    raise MQTTException(resp[3])
                return

    # Wait for a single incoming MQTT message and process it.
    # Subscribed messages are delivered to a callback previously
    # set by .set_callback() method. Other (internal) MQTT
    # messages processed internally.
    def wait_msg(self):
        res = self.sock.read(1)
        self.sock.setblocking(True)
        if res is None:
            return None
        if res == b"":
            raise OSError(-1)
        if res == b"xd0":  # PINGRESP
            sz = self.sock.read(1)[0]
            assert sz == 0
            return None
        op = res[0]
        if op & 0xf0 != 0x30:
            return op
        sz = self._recv_len()
        topic_len = self.sock.read(2)
        topic_len = (topic_len[0] << 8) | topic_len[1]
        topic = self.sock.read(topic_len)
        sz -= topic_len + 2
        if op & 6:
            pid = self.sock.read(2)
            pid = pid[0] << 8 | pid[1]
            sz -= 2
        msg = self.sock.read(sz)
        self.cb(topic, msg)
        if op & 6 == 2:
            pkt = bytearray(b"x40x02")
            struct.pack_into("!H", pkt, 2, pid)
            self.sock.write(pkt)
        elif op & 6 == 4:
            assert 0

    # Checks whether a pending message from server is available.
    # If not, returns immediately with None. Otherwise, does
    # the same processing as wait_msg.
    def check_msg(self):
        self.sock.setblocking(False)
        return self.wait_msg()

Afficher le code brut

A. Télécharger la bibliothèque umqttsimple avec uPyCraft IDE

1. Créez un nouveau fichier en appuyant sur le Nouveau fichier bouton.

1641226333 86 MicroPython MQTT publie BME680 avec ESP32ESP8266

2. Copiez le umqttsimple le code de la bibliothèque dedans. Vous pouvez accéder au umqttsimple code de la bibliothèque dans le lien suivant :

3. Enregistrez le fichier en appuyant sur le Sauvegarder bouton.

1641226333 52 MicroPython MQTT publie BME680 avec ESP32ESP8266

4. Appelez ce nouveau fichier « umqttsimple.py » et appuyez sur d’accord.

enregistrer le nouvel IDE upycraft umqttsimple.py

5. Clique le Télécharger et exécuter bouton.

1641226333 752 MicroPython MQTT publie BME680 avec ESP32ESP8266

6. Le fichier doit être enregistré sur le appareil dossier avec le nom « umqttsimple.py» comme le montre la figure ci-dessous.

enregistrer umqttsimple.py upycraft IDE

Maintenant, vous pouvez utiliser les fonctionnalités de la bibliothèque dans votre code en important la bibliothèque.

B. Télécharger la bibliothèque umqttsimple avec Thonny IDE

1. Copiez le code de la bibliothèque dans un nouveau fichier. le Le code de la bibliothèque umqttsimple peut être trouvé ici.

2. Aller à Déposer > Enregistrer sous…

Thonny IDE ESP32 ESP8266 MicroPython Enregistrer la bibliothèque de fichiers sur l'appareil enregistrer sous

3. Sélectionnez enregistrer dans « Périphérique MicroPython » :

Thonny IDE ESP32 ESP8266 MicroPython Enregistrer la bibliothèque de fichiers sur la sélection de l'appareil

4. Nommez votre fichier comme umqttsimple.py et appuyez sur le bouton OK :

bibliothèque umqttsimple nouveau fichier MicroPython Thonny IDE

Et c’est tout. La bibliothèque a été téléchargée sur votre tableau. Pour vous assurer qu’il a été téléchargé avec succès, accédez à Fichier > Enregistrer sous… et sélectionnez le périphérique MicroPython. Votre fichier doit y figurer :

Fichier MicroPython de la bibliothèque umqttsimple créé Thonny IDE

Après avoir téléchargé la bibliothèque sur votre tableau, vous pouvez utiliser les fonctionnalités de la bibliothèque dans votre code en important la bibliothèque.

Bibliothèque MicroPython BME680

La bibliothèque à lire depuis le capteur BME680 ne fait pas partie par défaut de la bibliothèque MicroPython standard. Vous devez donc télécharger la bibliothèque suivante sur votre carte ESP32/ESP8266 (enregistrez-la sous le nom bme680.py).

# Spaces, comments and some functions have been removed from the original file to save memory
# Original source: https://github.com/adafruit/Adafruit_CircuitPython_BME680/blob/master/adafruit_bme680.py
import time
import math
from micropython import const
from ubinascii import hexlify as hex
try:
  import struct
except ImportError:
  import ustruct as struct
_BME680_CHIPID = const(0x61)
_BME680_REG_CHIPID = const(0xD0)
_BME680_BME680_COEFF_ADDR1 = const(0x89)
_BME680_BME680_COEFF_ADDR2 = const(0xE1)
_BME680_BME680_RES_HEAT_0 = const(0x5A)
_BME680_BME680_GAS_WAIT_0 = const(0x64)
_BME680_REG_SOFTRESET = const(0xE0)
_BME680_REG_CTRL_GAS = const(0x71)
_BME680_REG_CTRL_HUM = const(0x72)
_BME280_REG_STATUS = const(0xF3)
_BME680_REG_CTRL_MEAS = const(0x74)
_BME680_REG_CONFIG = const(0x75)
_BME680_REG_PAGE_SELECT = const(0x73)
_BME680_REG_MEAS_STATUS = const(0x1D)
_BME680_REG_PDATA = const(0x1F)
_BME680_REG_TDATA = const(0x22)
_BME680_REG_HDATA = const(0x25)
_BME680_SAMPLERATES = (0, 1, 2, 4, 8, 16)
_BME680_FILTERSIZES = (0, 1, 3, 7, 15, 31, 63, 127)
_BME680_RUNGAS = const(0x10)
_LOOKUP_TABLE_1 = (2147483647.0, 2147483647.0, 2147483647.0, 2147483647.0, 2147483647.0,
  2126008810.0, 2147483647.0, 2130303777.0, 2147483647.0, 2147483647.0,
  2143188679.0, 2136746228.0, 2147483647.0, 2126008810.0, 2147483647.0,
  2147483647.0)
_LOOKUP_TABLE_2 = (4096000000.0, 2048000000.0, 1024000000.0, 512000000.0, 255744255.0, 127110228.0,
  64000000.0, 32258064.0, 16016016.0, 8000000.0, 4000000.0, 2000000.0, 1000000.0,
  500000.0, 250000.0, 125000.0)
def _read24(arr):
  ret = 0.0
  for b in arr:
    ret *= 256.0
    ret += float(b & 0xFF)
  return ret
class Adafruit_BME680:
  def __init__(self, *, refresh_rate=10):
    self._write(_BME680_REG_SOFTRESET, [0xB6])
    time.sleep(0.005)
    chip_id = self._read_byte(_BME680_REG_CHIPID)
    if chip_id != _BME680_CHIPID:
      raise RuntimeError('Failed 0x%x' % chip_id)
    self._read_calibration()
    self._write(_BME680_BME680_RES_HEAT_0, [0x73])
    self._write(_BME680_BME680_GAS_WAIT_0, [0x65])
    self.sea_level_pressure = 1013.25
    self._pressure_oversample = 0b011
    self._temp_oversample = 0b100
    self._humidity_oversample = 0b010
    self._filter = 0b010
    self._adc_pres = None
    self._adc_temp = None
    self._adc_hum = None
    self._adc_gas = None
    self._gas_range = None
    self._t_fine = None
    self._last_reading = 0
    self._min_refresh_time = 1000 / refresh_rate
  @property
  def pressure_oversample(self):
    return _BME680_SAMPLERATES[self._pressure_oversample]
  @pressure_oversample.setter
  def pressure_oversample(self, sample_rate):
    if sample_rate in _BME680_SAMPLERATES:
      self._pressure_oversample = _BME680_SAMPLERATES.index(sample_rate)
    else:
      raise RuntimeError("Invalid")
  @property
  def humidity_oversample(self):
    return _BME680_SAMPLERATES[self._humidity_oversample]
  @humidity_oversample.setter
  def humidity_oversample(self, sample_rate):
    if sample_rate in _BME680_SAMPLERATES:
      self._humidity_oversample = _BME680_SAMPLERATES.index(sample_rate)
    else:
      raise RuntimeError("Invalid")
  @property
  def temperature_oversample(self):
      return _BME680_SAMPLERATES[self._temp_oversample]
  @temperature_oversample.setter
  def temperature_oversample(self, sample_rate):
    if sample_rate in _BME680_SAMPLERATES:
      self._temp_oversample = _BME680_SAMPLERATES.index(sample_rate)
    else:
      raise RuntimeError("Invalid")
  @property
  def filter_size(self):
    return _BME680_FILTERSIZES[self._filter]
  @filter_size.setter
  def filter_size(self, size):
    if size in _BME680_FILTERSIZES:
      self._filter = _BME680_FILTERSIZES[size]
    else:
      raise RuntimeError("Invalid")
  @property
  def temperature(self):
    self._perform_reading()
    calc_temp = (((self._t_fine * 5) + 128) / 256)
    return calc_temp / 100
  @property
  def pressure(self):
    self._perform_reading()
    var1 = (self._t_fine / 2) - 64000
    var2 = ((var1 / 4) * (var1 / 4)) / 2048
    var2 = (var2 * self._pressure_calibration[5]) / 4
    var2 = var2 + (var1 * self._pressure_calibration[4] * 2)
    var2 = (var2 / 4) + (self._pressure_calibration[3] * 65536)
    var1 = (((((var1 / 4) * (var1 / 4)) / 8192) *
      (self._pressure_calibration[2] * 32) / 8) +
      ((self._pressure_calibration[1] * var1) / 2))
    var1 = var1 / 262144
    var1 = ((32768 + var1) * self._pressure_calibration[0]) / 32768
    calc_pres = 1048576 - self._adc_pres
    calc_pres = (calc_pres - (var2 / 4096)) * 3125
    calc_pres = (calc_pres / var1) * 2
    var1 = (self._pressure_calibration[8] * (((calc_pres / 8) * (calc_pres / 8)) / 8192)) / 4096
    var2 = ((calc_pres / 4) * self._pressure_calibration[7]) / 8192
    var3 = (((calc_pres / 256) ** 3) * self._pressure_calibration[9]) / 131072
    calc_pres += ((var1 + var2 + var3 + (self._pressure_calibration[6] * 128)) / 16)
    return calc_pres/100
  @property
  def humidity(self):
    self._perform_reading()
    temp_scaled = ((self._t_fine * 5) + 128) / 256
    var1 = ((self._adc_hum - (self._humidity_calibration[0] * 16)) -
      ((temp_scaled * self._humidity_calibration[2]) / 200))
    var2 = (self._humidity_calibration[1] *
      (((temp_scaled * self._humidity_calibration[3]) / 100) +
       (((temp_scaled * ((temp_scaled * self._humidity_calibration[4]) / 100)) /
         64) / 100) + 16384)) / 1024
    var3 = var1 * var2
    var4 = self._humidity_calibration[5] * 128
    var4 = (var4 + ((temp_scaled * self._humidity_calibration[6]) / 100)) / 16
    var5 = ((var3 / 16384) * (var3 / 16384)) / 1024
    var6 = (var4 * var5) / 2
    calc_hum = (((var3 + var6) / 1024) * 1000) / 4096
    calc_hum /= 1000
    if calc_hum > 100:
      calc_hum = 100
    if calc_hum < 0:
      calc_hum = 0
    return calc_hum
  @property
  def altitude(self):
    pressure = self.pressure
    return 44330 * (1.0 - math.pow(pressure / self.sea_level_pressure, 0.1903))
  @property
  def gas(self):
    self._perform_reading()
    var1 = ((1340 + (5 * self._sw_err)) * (_LOOKUP_TABLE_1[self._gas_range])) / 65536
    var2 = ((self._adc_gas * 32768) - 16777216) + var1
    var3 = (_LOOKUP_TABLE_2[self._gas_range] * var1) / 512
    calc_gas_res = (var3 + (var2 / 2)) / var2
    return int(calc_gas_res)
  def _perform_reading(self):
    if (time.ticks_diff(self._last_reading, time.ticks_ms()) * time.ticks_diff(0, 1)
        < self._min_refresh_time):
      return
    self._write(_BME680_REG_CONFIG, [self._filter << 2])
    self._write(_BME680_REG_CTRL_MEAS,
      [(self._temp_oversample << 5)|(self._pressure_oversample << 2)])
    self._write(_BME680_REG_CTRL_HUM, [self._humidity_oversample])
    self._write(_BME680_REG_CTRL_GAS, [_BME680_RUNGAS])
    ctrl = self._read_byte(_BME680_REG_CTRL_MEAS)
    ctrl = (ctrl & 0xFC) | 0x01
    self._write(_BME680_REG_CTRL_MEAS, [ctrl])
    new_data = False
    while not new_data:
      data = self._read(_BME680_REG_MEAS_STATUS, 15)
      new_data = data[0] & 0x80 != 0
      time.sleep(0.005)
    self._last_reading = time.ticks_ms()
    self._adc_pres = _read24(data[2:5]) / 16
    self._adc_temp = _read24(data[5:8]) / 16
    self._adc_hum = struct.unpack('>H', bytes(data[8:10]))[0]
    self._adc_gas = int(struct.unpack('>H', bytes(data[13:15]))[0] / 64)
    self._gas_range = data[14] & 0x0F
    var1 = (self._adc_temp / 8) - (self._temp_calibration[0] * 2)
    var2 = (var1 * self._temp_calibration[1]) / 2048
    var3 = ((var1 / 2) * (var1 / 2)) / 4096
    var3 = (var3 * self._temp_calibration[2] * 16) / 16384
    self._t_fine = int(var2 + var3)
  def _read_calibration(self):
    coeff = self._read(_BME680_BME680_COEFF_ADDR1, 25)
    coeff += self._read(_BME680_BME680_COEFF_ADDR2, 16)
    coeff = list(struct.unpack('<hbBHhbBhhbbHhhBBBHbbbBbHhbb', bytes(coeff[1:39])))
    coeff = [float(i) for i in coeff]
    self._temp_calibration = [coeff[x] for x in [23, 0, 1]]
    self._pressure_calibration = [coeff[x] for x in [3, 4, 5, 7, 8, 10, 9, 12, 13, 14]]
    self._humidity_calibration = [coeff[x] for x in [17, 16, 18, 19, 20, 21, 22]]
    self._gas_calibration = [coeff[x] for x in [25, 24, 26]]
    self._humidity_calibration[1] *= 16
    self._humidity_calibration[1] += self._humidity_calibration[0] % 16
    self._humidity_calibration[0] /= 16
    self._heat_range = (self._read_byte(0x02) & 0x30) / 16
    self._heat_val = self._read_byte(0x00)
    self._sw_err = (self._read_byte(0x04) & 0xF0) / 16
  def _read_byte(self, register):
    return self._read(register, 1)[0]
  def _read(self, register, length):
    raise NotImplementedError()
  def _write(self, register, values):
    raise NotImplementedError()
class BME680_I2C(Adafruit_BME680):
  def __init__(self, i2c, address=0x77, debug=False, *, refresh_rate=10):
    self._i2c = i2c
    self._address = address
    self._debug = debug
    super().__init__(refresh_rate=refresh_rate)
  def _read(self, register, length):
    result = bytearray(length)
    self._i2c.readfrom_mem_into(self._address, register & 0xff, result)
    if self._debug:
      print("t${:x} read ".format(register), " ".join(["{:02x}".format(i) for i in result]))
    return result
  def _write(self, register, values):
    if self._debug:
      print("t${:x} write".format(register), " ".join(["{:02x}".format(i) for i in values]))
    for value in values:
      self._i2c.writeto_mem(self._address, register, bytearray([value & 0xFF]))
      register += 1

Afficher le code brut

Schéma : ESP32 avec BME680

Câbler le Capteur BME680 à la carte de développement ESP32 comme indiqué dans le schéma suivant.

Schéma de câblage ESP32 BME680 I2C

Apprenez à utiliser les GPIO ESP32 avec notre guide : Référence de brochage ESP32 : Quelles broches GPIO devriez-vous utiliser ?

Schéma : ESP8266 NodeMCU avec BME680

Si vous utilisez un ESP8266 NodeMCU, suivez plutôt le schéma suivant.

ESP8266 NodeMCU BME680 Schéma de câblage du capteur environnemental I2C

Apprenez à utiliser les GPIO ESP8266 avec notre guide : Référence de brochage ESP8266 : Quelles broches GPIO devriez-vous utiliser ?

Code

Après avoir téléchargé les bibliothèques sur l’ESP32 ou l’ESP8266, copiez le code suivant dans le main.py déposer. Il publie la température, l’humidité et la pression sur le esp/bme680/température, esp/bme680/humidité, esp/bme680/pression, et esp/bme680/gaz sujets toutes les 5 secondes.

# Complete project details at https://Raspberryme.com/micropython-mqtt-publish-bme680-esp32-esp8266/

import time
from umqttsimple import MQTTClient
import ubinascii
import machine
import micropython
import network
import esp
from bme680 import *
from machine import Pin, I2C

esp.osdebug(None)
import gc
gc.collect()

ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'REPLACE_WITH_YOUR_PASSWORD'
mqtt_server="XXX.XXX.XXX.XXX"
#EXAMPLE IP ADDRESS
#mqtt_server="192.168.1.106"

client_id = ubinascii.hexlify(machine.unique_id())

topic_pub_temp = b'esp/bme680/temperature'
topic_pub_hum = b'esp/bme680/humidity'
topic_pub_pres = b'esp/bme680/pressure'
topic_pub_gas = b'esp/bme680/gas'

last_message = 0
message_interval = 5

station = network.WLAN(network.STA_IF)

station.active(True)
station.connect(ssid, password)

while station.isconnected() == False:
  pass

print('Connection successful')

# ESP32 - Pin assignment
#i2c = I2C(scl=Pin(22), sda=Pin(21))
# ESP8266 - Pin assignment
i2c = I2C(scl=Pin(5), sda=Pin(4))

bme = BME680_I2C(i2c=i2c)

def connect_mqtt():
  global client_id, mqtt_server
  client = MQTTClient(client_id, mqtt_server)
  #client = MQTTClient(client_id, mqtt_server, user=your_username, password=your_password)
  client.connect()
  print('Connected to %s MQTT broker' % (mqtt_server))
  return client

def restart_and_reconnect():
  print('Failed to connect to MQTT broker. Reconnecting...')
  time.sleep(10)
  machine.reset()

def read_bme_sensor():
  try:
    temp = (b'{:.2f}'.format(bme.temperature))
    #temp = (b'{0:.2f}'.format((bme.temperature) * (9/5) + 32))
    hum = (b'{:.2f}'.format(bme.humidity))
    pres = (b'{:.2f}'.format(bme.pressure))
    gas = (b'{:.2f}'.format(bme.gas/1000))
    
    return temp, hum, pres, gas
    #else:
    #  return('Invalid sensor readings.')
  except OSError as e:
    return('Failed to read sensor.')

try:
  client = connect_mqtt()
except OSError as e:
  restart_and_reconnect()

while True:
  try:
    if (time.time() - last_message) > message_interval:
      temp, hum, pres, gas = read_bme_sensor()
      print(temp)
      print(hum)
      print(pres)
      print(gas)
      client.publish(topic_pub_temp, temp)
      client.publish(topic_pub_hum, hum)      
      client.publish(topic_pub_pres, pres)
      client.publish(topic_pub_gas, gas)
      
      last_message = time.time()
  except OSError as e:
    restart_and_reconnect()

Afficher le code brut

Comment fonctionne le code

Importez les bibliothèques suivantes :

import time
from umqttsimple import MQTTClient
import ubinascii
import machine
import micropython
import network
import esp
from bme680 import *
from machine import Pin, I2C

Dans les variables suivantes, vous devez entrer vos informations d’identification réseau et l’adresse IP de votre courtier.

ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'REPLACE_WITH_YOUR_PASSWORD'
mqtt_server="REPLACE_WITH_YOUR_MQTT_BROKER_IP"

Par exemple, l’adresse IP de notre courtier est : 192.168.1.106.

mqtt_server="192.168.1.106"

Noter: lisez ce tutoriel pour voir comment obtenir l’adresse IP de votre courtier.

Pour créer un client MQTT, nous devons obtenir l’ID unique ESP. C’est ce que nous faisons dans la ligne suivante (elle est enregistrée sur le identité du client variable).

client_id = ubinascii.hexlify(machine.unique_id())

Ensuite, créez les sujets dans lesquels vous souhaitez que votre ESP publie. Dans notre exemple, il publiera la température sur le esp/bme680/température sujet, humidité sur le esp/bme680/humidité sujet, pression sur le esp/bme680/pression sujet, et gaz sur le esp/bme680/gaz sujet .

topic_pub_temp = b'esp/bme680/temperature'
topic_pub_hum = b'esp/bme680/humidity'
topic_pub_pres = b'esp/bme680/pressure'
topic_pub_gas = b'esp/bme680/gas'

Ensuite, créez les variables suivantes :

last_message = 0
message_interval = 5

le Dernier message La variable sera conservée la dernière fois qu’un message a été envoyé. le intervalle_message est le temps entre chaque message envoyé. Ici, nous le réglons sur 5 secondes (cela signifie qu’un nouveau message sera envoyé toutes les 5 secondes). Vous pouvez le changer si vous voulez.

Après cela, connectez l’ESP à votre réseau local.

station = network.WLAN(network.STA_IF)

station.active(True)
station.connect(ssid, password)

while station.isconnected() == False:
  pass

print('Connection successful')

Créé un i2c exemple sur les broches ESP32 I2C pour communiquer avec le capteur BME680 :

i2c = I2C(scl=Pin(22), sda=Pin(21))

Si vous utilisez un ESP8266, utilisez plutôt la ligne suivante (pour utiliser les broches I2C par défaut de l’ESP8266).

i2c = I2C(scl=Pin(5), sda=Pin(4))

Créez une instance BME680 sur les broches ESP I2C :

bme = BME680_I2C(i2c=i2c)

Connectez-vous au courtier MQTT

le connect_mqtt() La fonction crée un client MQTT et se connecte à votre courtier.

def connect_mqtt():
  global client_id, mqtt_server
  client = MQTTClient(client_id, mqtt_server)
  #client = MQTTClient(client_id, mqtt_server, user=your_username, password=your_password)
  client.connect()
  print('Connected to %s MQTT broker' % (mqtt_server))
  return client

Si votre courtier MQTT requiert un nom d’utilisateur et un mot de passe, vous devez utiliser la ligne suivante pour transmettre le nom d’utilisateur et le mot de passe de votre courtier comme arguments.

client = MQTTClient(client_id, mqtt_server, user=your_username, password=your_password)

Redémarrer et se reconnecter

le restart_and_reconnect() La fonction réinitialise la carte ESP32/ESP8266. Cette fonction sera appelée si nous ne sommes pas en mesure de publier les lectures via MQTT au cas où le courtier se déconnecterait.

def restart_and_reconnect():
  print('Failed to connect to MQTT broker. Reconnecting...')
  time.sleep(10)
  machine.reset()

Lire le capteur BME680

Nous avons créé une fonction appelée read_bme_sensor() qui renvoie les lectures de température, d’humidité et de pression actuelles du capteur BME680 et gère toutes les exceptions, au cas où nous ne serions pas en mesure d’obtenir les lectures du capteur.

def read_bme_sensor():
  try:
    temp = (b'{:.2f}'.format(bme.temperature))
    #temp = (b'{0:.2f}'.format((bme.temperature) * (9/5) + 32))
    hum = (b'{:.2f}'.format(bme.humidity))
    pres = (b'{:.2f}'.format(bme.pressure))
    gas = (b'{:.2f}'.format(bme.gas/1000))
    
    return temp, hum, pres, gas
    #else:
    #  return('Invalid sensor readings.')
  except OSError as e:
    return('Failed to read sensor.')

Publication de messages MQTT

Dans la boucle while, nous publions de nouvelles lectures BME680 toutes les 5 secondes.

Tout d’abord, nous vérifions s’il est temps d’obtenir de nouvelles lectures :

if (time.time() - last_message) > message_interval:

Si c’est le cas, demandez de nouvelles lectures au capteur BME680 en appelant le read_bme_sensor() une fonction. La température est enregistrée sur le température variable, l’humidité est enregistrée sur le Hum variable et la pression sur le presse variable.

temp, hum, pres, gas = read_bme_sensor()

Enfin, publiez les lectures en utilisant le publier() méthode sur le client objet. le publier() La méthode accepte comme arguments le sujet et le message, comme suit :

client.publish(topic_pub_temp, temp)
client.publish(topic_pub_hum, hum)
client.publish(topic_pub_pres, pres)
client.publish(topic_pub_gas, gas)

Enfin, mettez à jour l’heure à laquelle le dernier message a été envoyé :

last_message = time.time()

Si l’ESP32 ou l’ESP8266 se déconnecte du courtier et que nous ne sommes pas en mesure de publier les lectures, appelez le restart_and_reconnect() fonction pour réinitialiser la carte ESP et essayer de vous reconnecter au courtier.

except OSError as e:
  restart_and_reconnect()

Après avoir téléchargé le code, vous devriez obtenir de nouvelles lectures de capteur sur la coque toutes les 5 secondes.

Maintenant, passez à la section suivante pour préparer Node-RED à recevoir les lectures que l’ESP publie.

Préparation du tableau de bord Node-RED

L’ESP32 ou l’ESP8266 publie des relevés de température toutes les 10 secondes sur le esp/bme680/température, esp/bme680/humidité, esp/bme680/pression et esp/bme680/gaz les sujets. Désormais, vous pouvez utiliser n’importe quel tableau de bord prenant en charge MQTT ou tout autre appareil prenant en charge MQTT pour vous abonner à ces sujets et recevoir les lectures.

À titre d’exemple, nous allons créer un flux simple à l’aide de Node-RED pour vous abonner à ces sujets et afficher les lectures sur les jauges.

Si vous n’avez pas installé Node-RED, suivez les tutoriels suivants :

Après avoir exécuté Node-RED sur votre Raspberry Pi, accédez à votre adresse IP Raspberry Pi suivie de :1880.

http://raspberry-pi-ip-address:1880

L’interface Node-RED devrait s’ouvrir. Faites glisser quatre nœuds MQTT, deux nœuds de jauge et deux champs de texte vers le flux.

Faites glisser les nœuds ESP32 ESP8266 Node-RED BME680

Cliquez sur le nœud MQTT et modifiez ses propriétés.

Modifier mqtt dans le nœud ESP32 ESP8266 Node-RED BME680

Le champ Serveur fait référence au courtier MQTT. Dans notre cas, le courtier MQTT est le Raspberry Pi, il est donc défini sur localhost:1883. Si vous utilisez un courtier Cloud MQTT, vous devez modifier ce champ.

Insérez le sujet auquel vous souhaitez vous abonner et la QoS. Ce nœud MQTT précédent est abonné au esp/bme680/température sujet.

Cliquez sur l’autre MQTT dans les nœuds et modifiez ses propriétés avec le même serveur, mais pour les autres rubriques : esp/bme680/humidité, esp/bme680/pression, et esp/bme680/gaz.

Cliquez sur les nœuds de la jauge et modifiez ses propriétés pour chaque lecture. Le nœud suivant est défini pour les lectures de température. Modifiez l’autre nœud du graphique pour les lectures d’humidité.

Modifier le nœud de jauge ESP32 ESP8266 Node-RED BME680

Câblez vos nœuds comme indiqué ci-dessous :

Nœud connecté ESP32 ESP8266 Nœud-RED BME680

Enfin, déployez votre flux (appuyez sur le bouton en haut à droite).

Déployer le bouton Node-RED

Alternativement, vous pouvez aller à Menu > Importer et copiez ce qui suit dans votre Presse-papiers pour créer votre flux Node-RED.

[{"id":"3b7f947c.9759ec","type":"mqtt in","z":"254c9c97.f85b34","name":"","topic":"esp/bme680/temperature","qos":"1","datatype":"auto","broker":"8db3fac0.99dd48","x":470,"y":2640,"wires":[["b87b21c3.96672"]]},{"id":"b87b21c3.96672","type":"ui_gauge","z":"254c9c97.f85b34","name":"","group":"37de8fe8.46846","order":2,"width":0,"height":0,"gtype":"gage","title":"Temperature","label":"ºC","format":"{{value}}","min":0,"max":"40","colors":["#00b500","#f7df09","#ca3838"],"seg1":"","seg2":"","x":690,"y":2640,"wires":[]},{"id":"f92248f4.545778","type":"mqtt in","z":"254c9c97.f85b34","name":"","topic":"esp/bme680/humidity","qos":"1","datatype":"auto","broker":"8db3fac0.99dd48","x":460,"y":2700,"wires":[["4114a401.5ac69c"]]},{"id":"4114a401.5ac69c","type":"ui_gauge","z":"254c9c97.f85b34","name":"","group":"37de8fe8.46846","order":2,"width":0,"height":0,"gtype":"gage","title":"Humidity","label":"%","format":"{{value}}","min":"30","max":"100","colors":["#53a4e6","#1d78a9","#4e38c9"],"seg1":"","seg2":"","x":680,"y":2700,"wires":[]},{"id":"ad51f895.2c2848","type":"mqtt in","z":"254c9c97.f85b34","name":"","topic":"esp/bme680/pressure","qos":"1","datatype":"auto","broker":"8db3fac0.99dd48","x":460,"y":2760,"wires":[["3a95123b.66405e"]]},{"id":"c074e688.198b78","type":"mqtt in","z":"254c9c97.f85b34","name":"","topic":"esp/bme680/gas","qos":"1","datatype":"auto","broker":"8db3fac0.99dd48","x":440,"y":2820,"wires":[["d3539c06.00a17"]]},{"id":"3a95123b.66405e","type":"ui_text","z":"254c9c97.f85b34","group":"37de8fe8.46846","order":2,"width":0,"height":0,"name":"","label":"Pressure","format":"{{msg.payload}} hPa","layout":"row-spread","x":680,"y":2760,"wires":[]},{"id":"d3539c06.00a17","type":"ui_text","z":"254c9c97.f85b34","group":"37de8fe8.46846","order":3,"width":0,"height":0,"name":"","label":"Gas","format":"{{msg.payload}} KOhm","layout":"row-spread","x":670,"y":2820,"wires":[]},{"id":"8db3fac0.99dd48","type":"mqtt-broker","z":"","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"compatmode":false,"keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","closeTopic":"","closeQos":"0","closePayload":"","willTopic":"","willQos":"0","willPayload":""},{"id":"37de8fe8.46846","type":"ui_group","z":"","name":"BME680","tab":"53b8c8f9.cfbe48","order":1,"disp":true,"width":"6","collapse":false},{"id":"53b8c8f9.cfbe48","type":"ui_tab","z":"","name":"Home","icon":"dashboard","order":5,"disabled":false,"hidden":false}]

Afficher le code brut

Manifestation

Accédez à votre adresse IP Raspberry Pi suivie de :1880/ui.

http://raspberry-pi-ip-address:1880/ui

Vous devriez avoir accès aux relevés actuels de température, d’humidité et de pression du BME680 sur le tableau de bord. Vous pouvez utiliser d’autres nœuds de type tableau de bord pour afficher les lectures de différentes manières.

ESP32 ESP8266 Node-RED BME680 Température Humidité Pression Gaz Qualité de l'air

C’est ça! Vos cartes ESP32 ou ESP8266 publient les lectures de température, d’humidité et de pression BME680 sur Node-RED via MQTT à l’aide de MicroPython.

Emballer

MQTT est un excellent protocole de communication pour échanger de petites quantités de données entre les appareils IoT. Dans ce didacticiel, vous avez appris à publier les lectures d’un capteur BME680 avec l’ESP32 et l’ESP8266 à l’aide de MicroPython sur différents sujets MQTT. Ensuite, vous pouvez utiliser n’importe quel appareil ou plate-forme domotique pour vous abonner à ces sujets et recevoir les lectures.

Au lieu d’un Capteur BME680, vous pouvez utiliser n’importe quel autre capteur comme le capteur DHT11 ou DHT22 ou le capteur de température DS18B20.

Nous avons d’autres projets/tutoriels liés au capteur BME680 qui pourraient également vous plaire :

Apprenez-en plus sur MicroPython avec notre livre électronique : Programmation MicroPython à l’aide d’ESP32/ESP8266.