MicroPython: BME680 с ESP32 и ESP8266 (температура, влажность, давление, газ)

В этом руководстве вы узнаете, как использовать модуль датчика BME680 с ESP32 и ESP8266 для получения показаний температуры, влажности, давления и газа (качества воздуха) с использованием прошивки MicroPython. Мы создадим простой пример для знакомства с датчиком и веб-сервер для отображения показаний.

MicroPython BME680 с ESP32 и ESP8266 — температура, влажность, давление, газ, качество воздуха

Предварительные требования

Для выполнения этого руководства вам нужна прошивка MicroPython, установленная на вашу плату ESP32 или ESP8266. Вам также понадобится IDE для написания и загрузки кода на плату. Мы рекомендуем использовать Thonny IDE или uPyCraft IDE:

Подробнее о MicroPython: MicroPython Programming with ESP32 and ESP8266 eBook.

Вам также могут быть интересны другие руководства по BME680:

Знакомство с модулем датчика окружающей среды BME680

BME680 — это датчик окружающей среды, который объединяет датчики газа, давления, влажности и температуры. Газовый датчик может обнаруживать широкий спектр газов, таких как летучие органические соединения (VOC). По этой причине BME680 может использоваться для контроля качества воздуха в помещении.

BME680 — датчик газа, влажности, барометрического давления, температуры окружающей среды, качества воздуха (вид спереди)

Измерения BME680

BME680 — это цифровой датчик «4 в 1», который измеряет:

  • Температуру

  • Влажность

  • Барометрическое давление

  • Газ: летучие органические соединения (VOC), такие как этанол и угарный газ

Газовый датчик

BME680 содержит датчик MOX (оксид металла), который обнаруживает VOC в воздухе. Этот датчик даёт качественное представление о сумме VOC/загрязняющих веществ в окружающем воздухе — он не является специфичным для конкретной молекулы газа.

Датчики MOX состоят из поверхности из оксида металла, чувствительного чипа для измерения изменений проводимости и нагревателя. Он обнаруживает VOC путём адсорбции молекул кислорода на чувствительном слое. BME680 реагирует на большинство VOC, загрязняющих воздух в помещении (за исключением CO2).

Когда датчик вступает в контакт с восстанавливающими газами, молекулы кислорода реагируют и увеличивают проводимость поверхности. В качестве необработанного сигнала BME680 выдаёт значения сопротивления. Эти значения изменяются в зависимости от концентрации VOC:

BME680 — датчик газа, сопротивление, принцип работы
  • Более высокая концентрация VOC >> Более низкое сопротивление

  • Более низкая концентрация VOC >> Более высокое сопротивление

Реакции, происходящие на поверхности датчика (а значит, и сопротивление), зависят не только от концентрации VOC, но и от других параметров, таких как температура и влажность.

Важная информация о газовом датчике

Газовый датчик даёт качественное представление о VOC-газах в окружающем воздухе. Таким образом, вы можете отслеживать тенденции, сравнивать результаты и определять, улучшается или ухудшается качество воздуха. Для получения точных измерений необходимо откалибровать датчик по известным источникам и построить калибровочную кривую.

При первом получении датчика рекомендуется запускать его на 48 часов перед началом сбора «реальных» данных. После этого также рекомендуется запускать датчик на 30 минут перед получением показаний газа.

Точность BME680

Вот точность датчиков температуры, влажности и давления BME680:

Датчик

Точность

Температура

+/- 1.0 °C

Влажность

+/- 3%

Давление

+/- 1 гПа

Диапазон работы BME680

В следующей таблице показан диапазон работы датчиков температуры, влажности и давления BME680.

Датчик

Диапазон работы

Температура

от -40 до 85 °C

Влажность

от 0 до 100%

Давление

от 300 до 1100 гПа

Распиновка BME680

Вот распиновка BME680:

Вывод

Описание

VCC

Питание датчика

GND

Общая земля

SCL

Вывод SCL для I2C-соединения / Вывод SCK для SPI-соединения

SDA

Вывод SDA для I2C-соединения / Вывод SDI (MOSI) для SPI-соединения

SDO

Вывод SDO (MISO) для SPI-соединения

CS

Вывод выбора чипа для SPI-соединения

Интерфейс BME680

BME680 поддерживает интерфейсы I2C и SPI.

BME680 — датчик газа, влажности, барометрического давления, температуры (вид сзади)

BME680 I2C

Этот датчик обменивается данными по протоколу I2C, поэтому подключение очень простое. Вы можете использовать пины I2C по умолчанию для ESP32 или ESP8266, как показано в следующей таблице:

BME680

ESP32

ESP8266

Vin

3.3V

3.3V

GND

GND

GND

SCL

GPIO 22

GPIO 5 (D1)

SDA

GPIO 21

GPIO 4 (D2)

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

ESP32 с датчиком газа BME680 — схема подключения

Для этого проекта вам нужно подключить модуль датчика BME680 к пинам I2C ESP32 или ESP8266. Вот список необходимых компонентов:

Схема подключения — ESP32

Следуйте приведённой ниже схеме, если вы используете плату ESP32:

ESP32 BME680 — схема подключения I2C

Рекомендуемое чтение: Справочное руководство по распиновке ESP32

Схема подключения — ESP8266

Следуйте приведённой ниже схеме, если вы используете плату ESP8266:

ESP8266 NodeMCU BME680 — схема подключения I2C

Рекомендуемое чтение: Справочное руководство по распиновке ESP8266

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

Библиотека для чтения данных с датчика BME680 не входит в стандартную библиотеку MicroPython по умолчанию. Поэтому вам нужно загрузить следующую библиотеку на вашу плату ESP32/ESP8266 (сохраните её с именем 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

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

Следуйте следующему набору инструкций для IDE, которую вы используете:

    1. Загрузка библиотеки BME680 с помощью uPyCraft IDE

    1. Загрузка библиотеки BME680 с помощью Thonny IDE

A. Загрузка библиотеки BME680 с помощью uPyCraft IDE

В этом разделе показано, как загрузить библиотеку с помощью uPyCraft IDE. Если вы используете Thonny IDE, читайте следующий раздел.

1. Создайте новый файл, нажав кнопку New File (1).

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

3. После копирования кода сохраните файл, нажав кнопку Save (2).

Установка библиотеки BME680 MicroPython ESP32 ESP8266 uPyCraft IDE — шаг 1

4. Назовите этот новый файл «bme680.py» и нажмите ok.

bme680.py — новый файл MicroPython

5. Нажмите кнопку Download and Run.

Установка библиотеки BME680 MicroPython ESP32 ESP8266 uPyCraft IDE — шаг 3

Файл должен быть сохранён в папке устройства с именем «bme680.py», как выделено на следующем рисунке.

uPyCraft IDE — сохранение файла библиотеки на устройство ESP32 ESP8266

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

B. Загрузка библиотеки BME680 с помощью Thonny IDE

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

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

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

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

3. Выберите сохранение на «MicroPython device»:

Thonny IDE ESP32 ESP8266 MicroPython — выбор устройства

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

Thonny IDE ESP32 ESP8266 MicroPython — имя файла

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

Thonny IDE ESP32 ESP8266 MicroPython — файл библиотеки сохранён на устройство

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

Код — температура, влажность, давление и газ (качество воздуха) BME680

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

# Complete project details at https://RandomNerdTutorials.com/micropython-bme680-esp32-esp8266/

from machine import Pin, I2C
from time import sleep
from bme680 import *

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

while True:
  try:
    temp = str(round(bme.temperature, 2)) + ' C'
    #temp = (bme.temperature) * (9/5) + 32
    #temp = str(round(temp, 2)) + 'F'

    hum = str(round(bme.humidity, 2)) + ' %'

    pres = str(round(bme.pressure, 2)) + ' hPa'

    gas = str(round(bme.gas/1000, 2)) + ' KOhms'

    print('Temperature:', temp)
    print('Humidity:', hum)
    print('Pressure:', pres)
    print('Gas:', gas)
    print('-------')
  except OSError as e:
    print('Failed to read sensor.')

  sleep(5)

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

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

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

from machine import Pin, I2C
from time import sleep
from bme680 import *

Установите пины I2C. В данном случае мы используем пины I2C по умолчанию. Если вы используете ESP32, установите пины следующим образом:

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

Если вы используете ESP8266, закомментируйте предыдущую строку и раскомментируйте следующую, чтобы получилось:

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

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

bme = BME680_I2C(i2c=i2c)

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

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

hum = str(round(bme.humidity, 2)) + ' %'

pres = str(round(bme.pressure, 2)) + ' hPa'

gas = str(round(bme.gas/1000, 2)) + ' KOhms'

Наконец, выведите показания в консоль:

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

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

sleep(5)

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

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

BME680 — чтение температуры, влажности, давления и газа (качество воздуха)

Отображение показаний BME680 на веб-сервере

Теперь, когда вы знаете, как получить температуру, влажность, давление и газ с датчика BME680, мы отобразим показания датчика на веб-сервере, к которому можно получить доступ в вашей локальной сети.

ESP32 ESP8266 MicroPython BME680 — тест веб-сервера на мобильном устройстве

Для этого примера вам нужны три файла:

  • bme680.py: это файл, содержащий все методы для работы с датчиком BME680. Это файл, который вы загрузили ранее.

  • boot.py: запускается при старте устройства и устанавливает несколько параметров конфигурации, таких как учётные данные сети, импорт библиотек, настройка пинов и т.д.

  • main.py: это основной скрипт, в котором мы будем обрабатывать веб-сервер. Он выполняется сразу после boot.py.

Примечание: Хорошей практикой является использование файлов boot.py и main.py. Однако при желании вы можете включить весь код в файл main.py.

boot.py

Создайте новый файл в вашей IDE с именем boot.py и скопируйте следующий код.

# Complete project details at https://RandomNerdTutorials.com/micropython-bme680-esp32-esp8266/

try:
  import usocket as socket
except:
  import socket

from time import sleep

from machine import Pin, I2C
import network

import esp
esp.osdebug(None)

import gc
gc.collect()

from bme680 import *

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

ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'REPLACE_WITH_YOUR_PASSWORD'

station = network.WLAN(network.STA_IF)

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

while station.isconnected() == False:
  pass

print('Connection successful')
print(station.ifconfig())

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

Этот файл импортирует необходимые библиотеки, определяет пины I2C для подключения к датчику и подключается к вашей сети.

В коде мы используем пины I2C ESP32:

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

Если вы используете ESP8266, закомментируйте предыдущую строку и раскомментируйте следующую:

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

Затем вставьте учётные данные вашей сети в следующие переменные:

ssid = 'REPLACE_WITH_YOUR_SSID'
password = 'REPLACE_WITH_YOUR_PASSWORD'

main.py

В файле main.py мы создадим веб-сервер и будем обрабатывать запросы. Скопируйте следующий код в файл main.py.

# Complete project details at https://RandomNerdTutorials.com/micropython-bme680-esp32-esp8266/

def web_page():
  bme = BME680_I2C(i2c=i2c)

  html = """<html><head><title>ESP with BME680</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,"><style>body { text-align: center; font-family: "Trebuchet MS", Arial;}
  table { border-collapse: collapse; margin-left:auto; margin-right:auto; }
  th { padding: 12px; background-color: #0043af; color: white; }
  tr { border: 1px solid #ddd; padding: 12px; }
  tr:hover { background-color: #bcbcbc; }
  td { border: none; padding: 12px; }
  .sensor { color:white; font-weight: bold; background-color: #bcbcbc; padding: 1px;
  </style></head><body><h1>ESP with BME680</h1>
  <table><tr><th>MEASUREMENT</th><th>VALUE</th></tr>
  <tr><td>Temp. Celsius</td><td><span class="sensor">""" + str(round(bme.temperature, 2)) + """ C</span></td></tr>
  <tr><td>Temp. Fahrenheit</td><td><span class="sensor">""" + str(round((bme.temperature) * (9/5) + 32, 2))  + """ F</span></td></tr>
  <tr><td>Pressure</td><td><span class="sensor">""" + str(round(bme.pressure, 2)) + """ hPa</span></td></tr>
  <tr><td>Humidity</td><td><span class="sensor">""" + str(round(bme.humidity, 2)) + """ %</span></td></tr>
  <tr><td>Gas</td><td><span class="sensor">""" + str(round(bme.gas/1000, 2)) + """ KOhms</span></td></tr></body></html>"""
  return html

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind(('', 80))
s.listen(5)

while True:
  try:
    if gc.mem_free() < 102000:
      gc.collect()
    conn, addr = s.accept()
    conn.settimeout(3.0)
    print('Got a connection from %s' % str(addr))
    request = conn.recv(1024)
    conn.settimeout(None)
    request = str(request)
    print('Content = %s' % request)
    response = web_page()
    conn.send('HTTP/1.1 200 OK\n')
    conn.send('Content-Type: text/html\n')
    conn.send('Connection: close\n\n')
    conn.sendall(response)
    conn.close()
  except OSError as e:
    conn.close()
    print('Connection closed')

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

Этот код создаёт сервер на сокетах, который отправляет HTML-страницу с последними показаниями датчика при получении запроса на IP-адрес ESP32 или ESP8266.

По сути, у нас есть функция web_page(), которая возвращает HTML для построения веб-страницы с последними показаниями датчика. Этот HTML-текст создаёт таблицу для отображения показаний:

def web_page():
  bme = BME680_I2C(i2c=i2c)

  html = """<html><head><title>ESP with BME680</title>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="icon" href="data:,"><style>body { text-align: center; font-family: "Trebuchet MS", Arial;}
  table { border-collapse: collapse; margin-left:auto; margin-right:auto; }
  th { padding: 12px; background-color: #0043af; color: white; }
  tr { border: 1px solid #ddd; padding: 12px; }
  tr:hover { background-color: #bcbcbc; }
  td { border: none; padding: 12px; }
  .sensor { color:white; font-weight: bold; background-color: #bcbcbc; padding: 1px;
  </style></head><body><h1>ESP with BME680</h1>
  <table><tr><th>MEASUREMENT</th><th>VALUE</th></tr>
  <tr><td>Temp. Celsius</td><td><span class="sensor">""" + str(round(bme.temperature, 2)) + """ C</span></td></tr>
  <tr><td>Temp. Fahrenheit</td><td><span class="sensor">""" + str(round((bme.temperature) * (9/5) + 32, 2))  + """ F</span></td></tr>
  <tr><td>Pressure</td><td><span class="sensor">""" + str(round(bme.pressure, 2)) + """ hPa</span></td></tr>
  <tr><td>Humidity</td><td><span class="sensor">""" + str(round(bme.humidity, 2)) + """ %</span></td></tr>
  <tr><td>Gas</td><td><span class="sensor">""" + str(round(bme.gas/1000, 2)) + """ KOhms</span></td></tr></body></html>"""
  return html

Затем мы создаём сервер на сокетах, который отправляет HTML при получении запроса. HTML-текст сохраняется в переменной response:

response = web_page()

И отправляется клиенту:

conn.sendall(response)

Мы подробно объясняли, как работают такие веб-серверы, в предыдущих руководствах. Если вы хотите узнать, как это работает, вы можете прочитать следующие статьи:

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

Загрузите все предыдущие файлы на плату ESP32 или ESP8266 в следующем порядке:

  1. bme680.py

  2. boot.py

  3. main.py

Если вы не знаете, как загружать код, вы можете прочитать наши руководства по началу работы с uPyCraft IDE или Thonny IDE:

После загрузки кода IP-адрес вашего ESP32 или ESP8266 должен отобразиться в Serial Monitor.

ESP32 ESP8266 MicroPython — IP-адрес

Откройте веб-браузер в вашей локальной сети и введите IP-адрес вашего ESP (в нашем примере IP — http://192.168.1.114). Вы должны получить страницу с последними показаниями датчика, как показано на следующем рисунке.

ESP32 ESP8266 MicroPython BME680 — демонстрация веб-сервера

Автообновление веб-страницы

В скрипте веб-сервера, представленном в этом проекте, вам нужно обновлять веб-страницу, чтобы увидеть последние показания. Если вы добавите следующий мета-тег внутрь HTML-тегов <head></head>, ваша веб-страница будет автоматически обновляться каждые 10 секунд:

<meta http-equiv="refresh" content="10">

Заключение

Мы надеемся, что это руководство было для вас полезным. У нас есть другие проекты и руководства по MicroPython, которые могут вам понравиться:

Если вы хотите узнать больше о программировании ESP32 и ESP8266 с MicroPython, получите доступ к нашей электронной книге: MicroPython Programming with ESP32 and ESP8266.

Спасибо за чтение.