Компоненты · 12 мин чтения

BME280 и Arduino: температура, влажность, давление и высота (отличия от BMP280)

Как подключить BME280 или BMP280 к Arduino: I2C-адрес 0x76/0x77, библиотека Adafruit, высота по давлению на уровне моря, предупреждение о 3,3 V, метеостанция на ESP32 и код для Pico.

BME280 — цифровой датчик окружающей среды от Bosch, который измеряет температуру, относительную влажность и атмосферное давление одним крошечным чипом. Для работы с Arduino нужны всего четыре провода (VCC, GND, SDA, SCL) и библиотека Adafruit BME280; несколько строк кода выводят в монитор порта показания в °C, % и гПа (hPa). Поскольку датчик измеряет давление, он может вычислить и высоту твоего места над уровнем моря. Его «брат» BMP280 выглядит почти так же, но не измеряет влажность: он даёт только температуру и давление.

В этом руководстве ты узнаешь, чем отличаются два датчика, почему I2C-адрес бывает то 0x76, то 0x77, как подключить их к Arduino Uno, ESP32 и Raspberry Pi Pico, как работает формула высоты и как выбрать правильное давление на уровне моря. Затем ты соберёшь мини-метеостанцию на ESP32, которая открывается с телефона, запустишь на Pico драйвер на MicroPython без библиотек и увидишь, как один скетч выявляет очень распространённую проблему «BMP280, проданный как BME280».

Модуль датчика температуры, влажности и давления BME280
Модуль BME280: крошечный чип под металлической крышкой измеряет температуру, влажность и давление и подключается по I2C или SPI.

Что такое BME280 и как он работает?

Внутри BME280 три чувствительных элемента. Датчик давления на основе пьезорезистивного эффекта измеряет, насколько прогибается тонкая кремниевая мембрана под давлением воздуха. Датчик влажности — это полимерный слой, ёмкость которого меняется по мере того, как он впитывает влагу из воздуха. Датчик температуры сообщает температуру окружающей среды и также используется для коррекции двух других измерений. Каждый чип индивидуально калибруется на заводе, а коэффициенты записываются в его внутреннюю память; при запуске библиотека считывает их и переводит сырые числа в реальные единицы по формулам из даташита. Маленькое отверстие в металлической крышке пропускает воздух к датчику, поэтому никогда не закрывай его скотчем или клеем.

ХарактеристикаBME280BMP280
Что измеряетТемпература, влажность, давлениеТемпература, давление
Температура-40…+85 °C, ±1 °C (±0,5 °C при 25 °C)-40…+85 °C, ±1 °C (±0,5 °C при 25 °C)
Относительная влажность0–100%, ±3%Нет
Диапазон давления300–1100 гПа300–1100 гПа
Точность давленияАбсолютная ±1 гПа, относительная ±0,12 гПа (≈ ±1 м)Абсолютная ±1 гПа, относительная ±0,12 гПа (≈ ±1 м)
Напряжение питания чипа1,71–3,6 V1,71–3,6 V
Ток (1 измерение в секунду)3,6 µA (все три измерения)2,7 µA
Ток в режиме сна0,1 µA0,1 µA
ИнтерфейсI2C (до 3,4 MHz) и SPI (до 10 MHz)I2C и SPI
Chip ID (регистр 0xD0)0x600x58
Размер чипа2,5 × 2,5 мм (квадратный)2,0 × 2,5 мм (прямоугольный)

Проще говоря, BMP280 — это чуть более дешёвый BME280 без датчика влажности; давление и температуру он измеряет так же хорошо. Если нужны только высота или атмосферное давление, BMP280 хватит. Для проектов, где важна влажность, например метеостанции, теплицы или контроля микроклимата в комнате, выбирай BME280. На ступень выше в семействе стоит BME680/BME688: он добавляет газовый датчик, который обнаруживает в воздухе летучие органические соединения (VOC), но стоит в несколько раз дороже.

BME280 или DHT11/DHT22?

ХарактеристикаDHT11DHT22BME280
Диапазон и точность по температуре0–50 °C, ±2 °C-40…+80 °C, ±0,5 °C-40…+85 °C, ±1 °C
Точность по влажности±5%±2–5%±3%
Давление и высотаНетНетДа
Самое быстрое чтение1 раз в секунду1 раз в 2 секундыДесятки раз в секунду
ПодключениеОдин провод данныхОдин провод данныхI2C или SPI
Ток (во время измерения)≈ 1 mA≈ 1,5 mAМенее 1 mA

Датчики DHT дешёвые и простые, но медленные, иногда выдают ошибки чтения и не измеряют давление. BME280 же делит одну шину I2C с другими устройствами, например с дисплеем, во сне почти не потребляет ток и идеально подходит для проектов, которые месяцами работают от батарей. Подробности о DHT11 смотри в статье Датчик температуры и влажности DHT11, а чтобы увидеть все варианты рядом, прочитай сравнение датчиков температуры.

Виды модулей и предупреждение о 3,3 V

Отдельный чип настолько мал, что его всегда используют на плате-модуле, и распространены два вида таких модулей. 6-пиновые модули (обычно фиолетовые, продаются как GY-BME280-3.3 или GY-BMP280-3.3) имеют пины VCC, GND, SCL, SDA, CSB и SDO; у большинства из них нет стабилизатора, и цифра 3.3 в названии говорит именно об этом: питать их можно только от 3,3 V. 4-пиновые модули (VIN, GND, SCL, SDA) обычно несут маленький стабилизатор на 3,3 V, а часто и транзисторы для согласования уровней на линиях I2C, поэтому их можно подключать к пину 5V Arduino Uno. Если не знаешь точно, какой вид у тебя, поищи на плате крошечную трёхногую микросхему стабилизатора; если её нет, используй 3,3 V.

Подключение 3,3-вольтового модуля без стабилизатора к пину 5V может повредить датчик: абсолютный максимум для чипа — 4,25 V. Повреждённый датчик не всегда выходит из строя полностью; иногда он показывает температуру, сдвинутую на несколько градусов, или скачущие значения давления, и из-за этого неисправность сложнее заметить.

Подключение BME280: Arduino Uno, ESP32 и Pico

Пин BME280Arduino UnoESP32Raspberry Pi Pico
VCC / VIN3.3V (6 пинов) или 5V (4 пина, со стабилизатором)3V33V3(OUT), пин 36
GNDGNDGNDGND, пин 38
SCLA5GPIO 22GP5, пин 7
SDAA4GPIO 21GP4, пин 6
CSB (6 пинов)3.3V (выбор I2C)3V33V3
SDO (6 пинов)Свободен или GND: 0x76, 3.3V: 0x77Свободен или GND: 0x76, 3V3: 0x77Свободен или GND: 0x76, 3V3: 0x77

На адресе спотыкается больше всего людей. Если пин SDO чипа на GND, адрес 0x76; если на 3,3 V — 0x77. В большинстве дешёвых модулей SDO подтянут к GND резистором, поэтому их адрес 0x76, а на фирменных платах Adafruit — 0x77, и библиотека по умолчанию пробует 0x77. Поэтому адрес стоит явно указать в коде. Если при включении пин CSB на GND, чип переходит в режим SPI и не отвечает по I2C; если используешь I2C, подключи CSB к 3,3 V или убедись, что модуль уже подтягивает его вверх. I2C-пины своей платы найдёшь в утилите распиновки, а логику шины I2C — в статье Что такое I2C?.

Если на одной шине нужны два датчика, подключи пин SDO одного из них к 3,3 V, чтобы сменить его адрес на 0x77; на 4-пиновых модулях это обычно делается паяной перемычкой на обратной стороне платы. Для более чем двух датчиков лучше подходит SPI: SCL идёт на SCK (13 на Uno), SDA на MOSI (11), SDO на MISO (12), а пин CSB каждого датчика — на свой цифровой пин; в коде достаточно объявить объект как Adafruit_BME280 bme(10);. Поскольку 5-вольтовая Uno управляет линиями SPI напрямую, между ней и 3,3-вольтовым модулем обязательно нужен преобразователь уровней.

Arduino Uno работает от 5 V, а библиотека Wire включает на A4 и A5 встроенные подтягивающие резисторы к 5 V. 3,3-вольтовый 6-пиновый модуль обычно всё равно работает, потому что подтягивающие резисторы на модуле держат шину около 3,3 V, но для постоянного проекта надёжнее всего 4-пиновый модуль со стабилизатором или преобразователь логических уровней. ESP32 и Pico и так работают от 3,3 V, поэтому у них такой проблемы нет.

Установка библиотеки Adafruit BME280

  1. В Arduino IDE открой Инструменты › Управлять библиотеками (Tools › Manage Libraries) и найди Adafruit BME280.
  2. После нажатия «Установить» (Install) в окне с вопросом о зависимостях выбери Установить все (Install all), чтобы заодно установились Adafruit Unified Sensor и Adafruit BusIO.
  3. Если у тебя датчик BMP280, точно так же установи библиотеку Adafruit BMP280: библиотека BME280 не распознаёт BMP280.
  4. При желании открой пример «Файл › Примеры › Adafruit BME280 Library › bme280test» (File › Examples), но не забудь указать адрес (0x76) в строке begin().

Первый скетч: чтение температуры, влажности и давления с BME280

C++ (Arduino IDE)
1#include <Wire.h>
2#include <Adafruit_Sensor.h>
3#include <Adafruit_BME280.h>
4
5#define DENIZ_SEVIYESI_HPA 1013.25   // bugünkü deniz seviyesi basıncı / today's sea-level pressure
6
7Adafruit_BME280 bme;
8
9void setup() {
10  Serial.begin(9600);
11  // Önce 0x76, olmazsa 0x77 dene / try 0x76 first, then 0x77
12  if (!bme.begin(0x76) && !bme.begin(0x77)) {
13    Serial.println(F("BME280 bulunamadi! Kablolari ve adresi kontrol et."));
14    Serial.print(F("Chip ID: 0x"));
15    Serial.println(bme.sensorID(), HEX);       // 0x58 ise bu bir BMP280 / 0x58 means BMP280
16    while (true) delay(10);
17  }
18  Serial.println(F("BME280 hazir"));
19}
20
21void loop() {
22  float sicaklik = bme.readTemperature();          // °C
23  float nem      = bme.readHumidity();             // % bağıl nem / % relative humidity
24  float basinc   = bme.readPressure() / 100.0F;    // Pa -> hPa
25  float irtifa   = bme.readAltitude(DENIZ_SEVIYESI_HPA);   // metre / meters
26
27  Serial.print(F("Sicaklik: ")); Serial.print(sicaklik, 1); Serial.println(F(" C"));
28  Serial.print(F("Nem:      ")); Serial.print(nem, 1);      Serial.println(F(" %"));
29  Serial.print(F("Basinc:   ")); Serial.print(basinc, 1);   Serial.println(F(" hPa"));
30  Serial.print(F("Irtifa:   ")); Serial.print(irtifa, 0);   Serial.println(F(" m"));
31  Serial.println();
32  delay(2000);
33}
ФункцияЧто делает
bme.begin(0x76)Запускает датчик; возвращает true, если по адресу отвечает устройство с chip ID 0x60
readTemperature()Температура, °C
readHumidity()Относительная влажность, %
readPressure()Давление, Па (раздели на 100, чтобы получить гПа)
readAltitude(P0)Высота относительно давления на уровне моря P0 (гПа), в метрах
seaLevelForAltitude(высота, P)Давление на уровне моря по известной высоте, гПа
sensorID()Считанный chip ID: 0x60 — BME280, 0x58 — BMP280
setTemperatureCompensation(x)Прибавляет x °C к каждому показанию температуры; влажность и давление корректируются соответственно

Загрузи скетч и открой монитор порта на скорости 9600 бод: в комнатных условиях ты увидишь примерно 23,4 °C, влажность 45% и 1009,8 гПа. Давление меняется медленно, на протяжении часов; подыши на датчик — и влажность поднимется за считаные секунды, коснись его пальцем — и температура пойдёт вверх. По умолчанию библиотека измеряет непрерывно в нормальном режиме (normal) с максимальной передискретизацией (x16); для первого теста это идеально, но, как ты увидишь ниже, для долгих метеоизмерений другая настройка даёт более точные результаты.

Режимы измерения: forced, normal и правильные настройки

У BME280 три режима. В режиме sleep (сон) он не измеряет и потребляет 0,1 µA. В режиме forced (принудительный) он делает одно измерение и снова засыпает; для каждого нового значения ты вызываешь takeForcedMeasurement(). В режиме normal (нормальный) он непрерывно измеряет без команд, с заданным временем ожидания между измерениями. Передискретизация (oversampling) измеряет одно и то же значение несколько раз и усредняет его, чтобы уменьшить шум, а IIR-фильтр сглаживает резкие скачки давления (хлопнувшая дверь, порыв ветра). В даташите Bosch рекомендует такие настройки в зависимости от задачи:

ЗадачаРежимПередискретизация (давление / температура / влажность)IIR-фильтрСредний ток
Мониторинг погодыForced, 1 измерение в минутуx1 / x1 / x1Выкл.≈ 0,16 µA
Измерение влажностиForced, 1 измерение в секундуПропуск / x1 / x1Выкл.≈ 2,9 µA
Навигация в помещении (этажи, лифты)Normal, ожидание 0,5 мсx16 / x2 / x116≈ 633 µA
Когда датчик измеряет непрерывно, чип и плата чуть-чуть нагреваются; это называется самонагревом. Если измеряешь температуру в комнате или на улице, используй режим forced и ограничься несколькими измерениями в минуту. Если показание всё равно стабильно выше, чем у надёжного термометра, на одну и ту же величину, можно добавить поправку, например bme.setTemperatureCompensation(-1.0);.

Давление на уровне моря и расчёт высоты

С высотой атмосферное давление падает, поэтому по измеренному давлению можно рассчитать высоту. Библиотека использует такую барометрическую формулу: h = 44330 × (1 − (P / P0)^0,1903). Здесь P — измеренное давление, а P0 — текущее давление на уровне моря (оба в гПа). В стандартной атмосфере принимается P0 = 1013,25 гПа, а у уровня моря разница в 1 гПа соответствует примерно 8,3 метра.

Подвох в том, что P0 — величина непостоянная: в зависимости от погоды она гуляет примерно от 990 до 1035 гПа. Если сегодня реальное значение 1023 гПа, а в коде осталось 1013.25, датчик покажет высоту примерно на 80 метров ниже настоящей. Для точных результатов P0 нужно держать актуальным: бери значение «давление, приведённое к уровню моря» из метеосервисов или значение Q из сводки METAR ближайшего аэропорта (например, Q1018 означает 1018 гПа). Можно пойти и обратным путём: если высота твоего места известна, рассчитай P0 с помощью seaLevelForAltitude(высота, давление). Например, если в Анкаре (около 938 м) датчик показывает 905 гПа, давление, приведённое к уровню моря, составит около 1012,6 гПа — это и есть число, которое ты видишь в приложениях погоды.

Для абсолютной высоты абсолютная точность ±1 гПа означает погрешность около ±8 метров. Зато в относительных измерениях датчик гораздо лучше: ±0,12 гПа ≈ ±1 м. Этаж в доме — это около 3 метров, или 0,36 гПа, поэтому BME280 легко определит, на каком ты этаже, или заметит движение лифта. Скетч ниже при запуске берёт за опорное значение среднее из 20 измерений, а затем выводит высоту относительно этой точки и давление на уровне моря, рассчитанное по известной высоте:

C++ (Arduino IDE)
1#include <Wire.h>
2#include <Adafruit_Sensor.h>
3#include <Adafruit_BME280.h>
4
5#define BILINEN_IRTIFA 938.0   // bulunduğun yerin rakımı (m), örnek Ankara / your altitude (m), e.g. Ankara
6
7Adafruit_BME280 bme;
8float referansHpa;             // açılıştaki basınç / pressure at start-up
9
10void setup() {
11  Serial.begin(9600);
12  if (!bme.begin(0x76)) {
13    Serial.println(F("BME280 bulunamadi!"));
14    while (true) delay(10);
15  }
16  // İç mekan navigasyonu ayarı: düşük gürültü / indoor navigation preset: low noise
17  bme.setSampling(Adafruit_BME280::MODE_NORMAL,
18                  Adafruit_BME280::SAMPLING_X2,    // sıcaklık / temperature
19                  Adafruit_BME280::SAMPLING_X16,   // basınç / pressure
20                  Adafruit_BME280::SAMPLING_X1,    // nem / humidity
21                  Adafruit_BME280::FILTER_X16,     // IIR filtre / IIR filter
22                  Adafruit_BME280::STANDBY_MS_0_5);
23  delay(1000);                                      // filtre otursun / let the filter settle
24
25  float toplam = 0;
26  for (int i = 0; i < 20; i++) {                   // 20 ölçümün ortalaması / average of 20 readings
27    toplam += bme.readPressure() / 100.0F;
28    delay(50);
29  }
30  referansHpa = toplam / 20;
31}
32
33void loop() {
34  float basinc = bme.readPressure() / 100.0F;                               // istasyon basıncı / station pressure
35  float denizSeviyesi = bme.seaLevelForAltitude(BILINEN_IRTIFA, basinc);    // hPa
36  float bagilYukseklik = bme.readAltitude(referansHpa);                     // başlangıca göre / relative to start
37
38  Serial.print(F("Istasyon basinci: "));   Serial.print(basinc, 2);         Serial.println(F(" hPa"));
39  Serial.print(F("Deniz seviyesi (QNH): ")); Serial.print(denizSeviyesi, 1); Serial.println(F(" hPa"));
40  Serial.print(F("Baslangica gore: "));    Serial.print(bagilYukseklik, 2); Serial.println(F(" m"));
41  Serial.println();
42  delay(1000);
43}

Если взять плату с пола и поднять над головой, значение «Baslangica gore» (относительно старта) вырастет примерно на 2 метра. Поскольку атмосферное давление за день меняется на несколько гПа, через несколько часов это относительное показание уплывает, поэтому при долгой работе опорную точку нужно время от времени обновлять. Чтобы ещё больше успокоить показания, можно добавить скользящее среднее из статьи Фильтрация данных датчиков.

Мини-метеостанция на ESP32: веб-страница, которая открывается с телефона

Благодаря Wi-Fi у ESP32 показания BME280 можно смотреть на веб-странице с любого телефона или компьютера в той же сети. Скетч ниже использует библиотеку WebServer, которая входит в ядро ESP32, поэтому дополнительные библиотеки не нужны. Главная страница загружается один раз, а затем небольшой фрагмент JavaScript в ней каждые 5 секунд запрашивает JSON с адреса /veri и обновляет значения без перезагрузки страницы. Датчик работает в режиме forced, то есть измеряет только тогда, когда кто-то смотрит на страницу, а давление показывается приведённым к уровню моря, чтобы его можно было сравнить с прогнозом погоды. Если ESP32 для тебя в новинку, начни со статьи Что такое ESP32?.

  1. Подключи BME280, как в таблице: VCC к 3V3, SDA к GPIO 21, SCL к GPIO 22.
  2. Впиши данные своей сети в поля WIFI_ADI (имя сети) и WIFI_SIFRE (пароль) в коде, а высоту своего места — в RAKIM_M.
  3. Выбери плату ESP32 Dev Module и загрузи скетч.
  4. Открой монитор порта на скорости 115200 бод; после подключения к сети там появится IP-адрес.
  5. Введи этот адрес (например, 192.168.1.42) в браузере телефона, подключённого к той же сети Wi-Fi; появятся карточки температуры, влажности и давления.
C++ (Arduino IDE)
1#include <WiFi.h>
2#include <WebServer.h>
3#include <Wire.h>
4#include <Adafruit_Sensor.h>
5#include <Adafruit_BME280.h>
6
7const char* WIFI_ADI   = "WiFiAdin";      // kendi ağ adın / your network name
8const char* WIFI_SIFRE = "WiFiSifren";    // kendi şifren / your password
9const float RAKIM_M    = 938.0;           // bulunduğun yerin rakımı / your local altitude
10
11Adafruit_BME280 bme;
12WebServer server(80);
13
14// Telefonda açılan sayfa, 5 sn'de bir /veri'yi okur / phone page, reads /veri every 5 s
15const char SAYFA[] PROGMEM = R"rawliteral(
16<!DOCTYPE html><html><head><meta charset="utf-8">
17<meta name="viewport" content="width=device-width,initial-scale=1">
18<title>Mini Hava İstasyonu</title>
19<style>
20body{font-family:sans-serif;text-align:center;background:#0f172a;color:#e2e8f0;margin:0;padding:16px}
21.k{display:inline-block;min-width:140px;margin:8px;padding:16px;border-radius:14px;background:#1e293b}
22.d{font-size:2.2em;font-weight:bold;margin:6px 0}
23</style></head><body>
24<h2>Mini Hava İstasyonu</h2>
25<div class="k">Sıcaklık<div class="d" id="t">--</div>°C</div>
26<div class="k">Nem<div class="d" id="h">--</div>%</div>
27<div class="k">Basınç<div class="d" id="p">--</div>hPa</div>
28<script>
29async function guncelle() {
30  const v = await (await fetch('/veri')).json();
31  document.getElementById('t').textContent = v.t.toFixed(1);
32  document.getElementById('h').textContent = v.h.toFixed(0);
33  document.getElementById('p').textContent = v.p.toFixed(1);
34}
35guncelle();
36setInterval(guncelle, 5000);
37</script>
38</body></html>
39)rawliteral";
40
41void anaSayfa() {
42  server.send(200, "text/html", SAYFA);
43}
44
45void veriGonder() {
46  bme.takeForcedMeasurement();                     // tek ölçüm al, sonra uyu / one measurement, then sleep
47  float p = bme.readPressure() / 100.0F;
48  String json = "{\"t\":" + String(bme.readTemperature(), 2) +
49                ",\"h\":" + String(bme.readHumidity(), 1) +
50                ",\"p\":" + String(bme.seaLevelForAltitude(RAKIM_M, p), 1) + "}";
51  server.send(200, "application/json", json);
52}
53
54void setup() {
55  Serial.begin(115200);
56  Wire.begin(21, 22);                              // SDA, SCL
57  if (!bme.begin(0x76)) {
58    Serial.println("BME280 bulunamadi!");
59    while (true) delay(10);
60  }
61  // Hava durumu izleme ayarı: kendi kendini ısıtmayı azaltır / weather monitoring preset: less self-heating
62  bme.setSampling(Adafruit_BME280::MODE_FORCED,
63                  Adafruit_BME280::SAMPLING_X1,    // sıcaklık / temperature
64                  Adafruit_BME280::SAMPLING_X1,    // basınç / pressure
65                  Adafruit_BME280::SAMPLING_X1,    // nem / humidity
66                  Adafruit_BME280::FILTER_OFF);
67
68  WiFi.mode(WIFI_STA);
69  WiFi.begin(WIFI_ADI, WIFI_SIFRE);
70  while (WiFi.status() != WL_CONNECTED) {
71    delay(500);
72    Serial.print(".");
73  }
74  Serial.print("\nTarayicida ac / open in browser: http://");
75  Serial.println(WiFi.localIP());
76
77  server.on("/", anaSayfa);
78  server.on("/veri", veriGonder);
79  server.begin();
80}
81
82void loop() {
83  server.handleClient();
84}
Во время работы ESP32 нагревается на несколько градусов, и если поставить датчик прямо рядом с платой, это тепло исказит показания. Вынеси BME280 от платы на кабеле длиной 10–15 см, размести его возле вентиляционных отверстий корпуса и не держи на солнце. Для более продвинутой панели с графиками и управлением кнопками переходи к статье Веб-сервер ESP32 и WebSocket.

BME280 с Raspberry Pi Pico (MicroPython)

В MicroPython нет встроенного модуля для BME280. Вместо того чтобы скачивать файл драйвера, код ниже несёт небольшой драйвер внутри себя: он считывает калибровочные коэффициенты, заставляет датчик сделать одно измерение в режиме forced и рассчитывает результат по формулам из даташита. Поскольку код проверяет chip ID, он работает и с BMP280 и автоматически пропускает строку влажности. Подключи датчик к GP4 (SDA), GP5 (SCL) и 3V3(OUT), а в Thonny сохрани код на Pico под именем main.py. Если Pico для тебя в новинку, статьи Что такое Raspberry Pi Pico? и Основы MicroPython облегчат дело.

MicroPython
1from machine import Pin, I2C
2import struct, time
3
4i2c = I2C(0, sda=Pin(4), scl=Pin(5), freq=400000)
5ADR = 0x76                                   # olmazsa 0x77 dene / try 0x77 if it fails
6P0 = 1013.25                                 # deniz seviyesi basıncı (hPa) / sea-level pressure (hPa)
7
8def s8(x):                                   # işaretli 8 bit / signed 8-bit
9    return x - 256 if x > 127 else x
10
11chip = i2c.readfrom_mem(ADR, 0xD0, 1)[0]
12BME = chip == 0x60                           # 0x60 BME280, 0x58 BMP280
13print("Chip ID:", hex(chip), "BME280" if BME else "BMP280 (nem yok / no humidity)")
14
15# Fabrika kalibrasyon katsayıları / factory calibration coefficients
16T1, T2, T3, P1, P2, P3, P4, P5, P6, P7, P8, P9 = struct.unpack("<HhhHhhhhhhhh", i2c.readfrom_mem(ADR, 0x88, 24))
17if BME:
18    H1 = i2c.readfrom_mem(ADR, 0xA1, 1)[0]
19    e = i2c.readfrom_mem(ADR, 0xE1, 7)
20    H2 = struct.unpack("<h", e[0:2])[0]
21    H3 = e[2]
22    H4 = (s8(e[3]) << 4) | (e[4] & 0x0F)
23    H5 = (s8(e[5]) << 4) | (e[4] >> 4)
24    H6 = s8(e[6])
25    i2c.writeto_mem(ADR, 0xF2, b"\x01")      # nem x1 (ctrl_meas'tan önce) / humidity x1 (before ctrl_meas)
26
27def oku():
28    i2c.writeto_mem(ADR, 0xF4, b"\x25")      # sıcaklık x1, basınç x1, forced mod / temp x1, press x1, forced mode
29    time.sleep_ms(15)                        # ölçümün bitmesini bekle / wait for the measurement
30    d = i2c.readfrom_mem(ADR, 0xF7, 8 if BME else 6)
31    adc_p = (d[0] << 12) | (d[1] << 4) | (d[2] >> 4)
32    adc_t = (d[3] << 12) | (d[4] << 4) | (d[5] >> 4)
33
34    # Sıcaklık (datasheet formülü) / temperature (datasheet formula)
35    v1 = (adc_t / 16384 - T1 / 1024) * T2
36    v2 = (adc_t / 131072 - T1 / 8192) ** 2 * T3
37    t_fine = v1 + v2
38    t = t_fine / 5120
39
40    # Basınç, Pa / pressure, Pa
41    v1 = t_fine / 2 - 64000
42    v2 = v1 * v1 * P6 / 32768 + v1 * P5 * 2
43    v2 = v2 / 4 + P4 * 65536
44    v1 = (P3 * v1 * v1 / 524288 + P2 * v1) / 524288
45    v1 = (1 + v1 / 32768) * P1
46    p = 1048576 - adc_p
47    p = (p - v2 / 4096) * 6250 / v1
48    p = p + (P9 * p * p / 2147483648 + p * P8 / 32768 + P7) / 16
49
50    h = None
51    if BME:                                  # nem, % / humidity, %
52        adc_h = (d[6] << 8) | d[7]
53        x = t_fine - 76800
54        x = (adc_h - (H4 * 64 + H5 / 16384 * x)) * (H2 / 65536 * (1 + H6 / 67108864 * x * (1 + H3 / 67108864 * x)))
55        x = x * (1 - H1 * x / 524288)
56        h = min(max(x, 0), 100)
57    return t, p / 100, h                     # °C, hPa, %
58
59while True:
60    t, p, h = oku()
61    irtifa = 44330 * (1 - (p / P0) ** 0.1903)    # metre / meters
62    print("Sicaklik: %.1f C  Basinc: %.1f hPa  Irtifa: %.0f m" % (t, p, irtifa))
63    if h is not None:
64        print("Nem: %.1f %%" % h)
65    time.sleep(2)

В коде стоит обратить внимание на несколько деталей. Настройка влажности (регистр 0xF2) вступает в силу только после записи в 0xF4, поэтому сначала отправляется настройка влажности, а потом команда измерения. Значение 0x25, записанное в 0xF4, означает «температура x1, давление x1, режим forced»; с такими настройками измерение длится максимум около 10 мс, так что ожидания в 15 мс достаточно. При проверке на примерных калибровочных значениях из даташита Bosch на BMP280 код выдаёт 25,08 °C и 1006,5 гПа, то есть его расчёты в точности совпадают с формулами Bosch.

BMP280 под видом BME280: какой датчик у тебя на самом деле?

Поскольку оба чипа подходят к одной и той же разводке платы, некоторые продавцы выдают модули с BMP280 за BME280; иногда на плате написано просто «BME/BMP280». Симптом всегда один: библиотека Adafruit BME280 выводит «Could not find a valid BME280 sensor, check wiring, address, sensor ID!» (не найден подходящий датчик BME280), а сразу под этим SensorID was: 0x58. Другие библиотеки ошибки не показывают, но всегда сообщают влажность 0 или nan. С проводами всё в порядке: у тебя просто чип BMP280, который не измеряет влажность.

BME280
BME280
BMP280
BMP280
  • Посмотри на форму чипа: если металлическая крышка квадратная (2,5 × 2,5 мм), это BME280; если явно прямоугольная (2,0 × 2,5 мм) — BMP280. Приблизь чип камерой телефона, и разница будет хорошо видна.
  • Считай chip ID: самый надёжный способ — скетч ниже; он читает идентификатор в регистре 0xD0 чипа и сообщает, какой это датчик.
  • Обрати внимание на цену: настоящий BME280 заметно дороже BMP280, так что к модулям «BME280», которые продаются намного ниже рыночной цены, стоит относиться с подозрением.
C++ (Arduino IDE)
1#include <Wire.h>
2
3void setup() {
4  Serial.begin(9600);
5  Wire.begin();
6  bool bulundu = false;
7
8  for (uint8_t adres = 0x76; adres <= 0x77; adres++) {
9    Wire.beginTransmission(adres);
10    if (Wire.endTransmission() != 0) continue;   // bu adreste cihaz yok / no device at this address
11
12    Wire.beginTransmission(adres);
13    Wire.write(0xD0);                             // chip ID register'ı / chip ID register
14    Wire.endTransmission();
15    Wire.requestFrom(adres, (uint8_t)1);
16    uint8_t id = Wire.read();
17    bulundu = true;
18
19    Serial.print(F("Adres 0x"));
20    Serial.print(adres, HEX);
21    Serial.print(F("  Chip ID 0x"));
22    Serial.print(id, HEX);
23    if (id == 0x60)                    Serial.println(F("  -> BME280 (sicaklik + nem + basinc)"));
24    else if (id >= 0x56 && id <= 0x58) Serial.println(F("  -> BMP280 (nem olcmez)"));
25    else if (id == 0x61)               Serial.println(F("  -> BME680 / BME688"));
26    else if (id == 0x55)               Serial.println(F("  -> BMP180"));
27    else                               Serial.println(F("  -> bilinmeyen yonga / unknown chip"));
28  }
29
30  if (!bulundu) Serial.println(F("0x76 ve 0x77'de sensor yok: kablolari kontrol et"));
31}
32
33void loop() {
34}

Если результат 0x58, не расстраивайся: температуру и давление BMP280 измеряет так же хорошо, как BME280. Установи библиотеку Adafruit BMP280 и используй скетч ниже; если нужна влажность, добавь в проект отдельный DHT22 или верни товар. Изредка попадаются и экземпляры BMP280 с ID 0x56 или 0x57; для них строку begin нужно записать как bmp.begin(0x76, 0x56).

C++ (Arduino IDE)
1#include <Wire.h>
2#include <Adafruit_BMP280.h>
3
4Adafruit_BMP280 bmp;
5
6void setup() {
7  Serial.begin(9600);
8  if (!bmp.begin(0x76) && !bmp.begin(0x77)) {
9    Serial.println(F("BMP280 bulunamadi! Kablolari ve adresi kontrol et."));
10    while (true) delay(10);
11  }
12  // Datasheet'in önerdiği varsayılan ayarlar / default settings from the datasheet
13  bmp.setSampling(Adafruit_BMP280::MODE_NORMAL,
14                  Adafruit_BMP280::SAMPLING_X2,     // sıcaklık / temperature
15                  Adafruit_BMP280::SAMPLING_X16,    // basınç / pressure
16                  Adafruit_BMP280::FILTER_X16,      // IIR filtre / IIR filter
17                  Adafruit_BMP280::STANDBY_MS_500); // bekleme süresi / standby time
18}
19
20void loop() {
21  Serial.print(F("Sicaklik: ")); Serial.print(bmp.readTemperature(), 1);       Serial.println(F(" C"));
22  Serial.print(F("Basinc:   ")); Serial.print(bmp.readPressure() / 100.0F, 1); Serial.println(F(" hPa"));
23  Serial.print(F("Irtifa:   ")); Serial.print(bmp.readAltitude(1013.25), 0);   Serial.println(F(" m"));
24  Serial.println();
25  delay(2000);
26}

Идеи проектов с BME280

  • Настольная метеостанция: выводи температуру, влажность и давление на OLED-дисплей или I2C LCD, подключённый к той же шине I2C.
  • Прогноз погоды по барометру: записывай давление каждый час; падение более чем на 2 гПа за 3 часа обычно предвещает ухудшение погоды, а устойчивый рост — ясное небо.
  • Точка росы и предупреждение о плесени: рассчитай точку росы по формуле Магнуса (a = 17,62, b = 243,12 °C) и подавай сигнал, когда температура стены приближается к ней.
  • Умная теплица: включай вентилятор или насос туманообразования в зависимости от влажности и температуры; подробности в статье Проект умной теплицы на Arduino.
  • Регистратор данных: записывай показания на модуль SD-карты или, с ESP32, в Google Таблицы и строй недельный график.
  • Датчик для умного дома: отправляй значения с ESP32 в Home Assistant по MQTT.
  • Уличная станция на батареях: буди ESP32 из глубокого сна каждые 10 минут; во сне BME280 потребляет всего 0,1 µA.
  • Счётчик этажей: с помощью скетча относительной высоты показывай, на каком этаже ты оказываешься в лифте или на лестнице.

Частые ошибки и как их исправить

СимптомВероятная причинаРешение
«Could not find a valid BME280 sensor», SensorID 0x0 или 0xFFНеверный адрес или ошибка в подключенииУкажи begin(0x76), проверь, не перепутаны ли SDA и SCL и подключён ли GND, запусти I2C-сканер
Та же ошибка, SensorID 0x58На самом деле это чип BMP280Используй библиотеку Adafruit BMP280, а для влажности добавь отдельный датчик
fatal error: Adafruit_Sensor.h: No such file or directory (файл не найден)Не установлена зависимая библиотекаУстанови библиотеку Adafruit Unified Sensor
Значения nanВ режиме forced не запрошено новое измерение, или вызов begin() завершился ошибкойВызывай takeForcedMeasurement() перед каждым чтением и проверяй результат begin()
Температура завышена на 1–3 °CСамонагрев из-за непрерывных измерений, тепло от ESP32 или стабилизатора рядомПерейди в режим forced, отодвинь датчик от платы, при необходимости используй setTemperatureCompensation()
Давление около 900 гПаЭто давление на уровне станции; оно падает с ростом высотыЧтобы сравнивать с прогнозами, приведи его к уровню моря с помощью seaLevelForAltitude()
Высота ошибается на десятки метровДавление на уровне моря оставлено равным 1013.25Введи текущее P0 (QNH) или откалибруй по известной высоте
Случайные зависания на длинных проводахШина I2C длинная и шумнаяДержи кабель короче 50 см, снизь скорость I2C или перейди на SPI
Не помещай датчик в закрытую герметичную коробку: для измерения влажности и давления воздух должен доходить до чипа. На улице самые точные результаты даёт жалюзийный корпус, открытый снизу и защищающий от дождя и прямого солнца (как метеобудки на метеостанциях). Если датчик намокнет от конденсата, он может какое-то время показывать влажность 100%; когда он высохнет, всё вернётся в норму.

BME280 выполняет на одной маленькой плате все основные измерения метеостанции: сначала проверь адрес и chip ID, затем выбери режим измерения под свою задачу, а для расчёта высоты держи давление на уровне моря актуальным. В приложении BlueGrays есть проекты, которые можно объединить с этим руководством: измерение температуры и влажности на DHT11 и DHT22, погодные дисплеи на ESP32, регистратор температуры и веб-сервер на ESP32 — каждый с пошаговыми схемами подключения и кодом.

Частые вопросы

Чем BME280 отличается от BMP280?

BME280 измеряет температуру, относительную влажность и давление; BMP280 измеряет только температуру и давление, датчика влажности у него нет. Точность давления и температуры у них одинаковая. Различаются и чипы: BME280 квадратный (2,5 × 2,5 мм), BMP280 прямоугольный (2,0 × 2,5 мм), а их chip ID — 0x60 и 0x58.

I2C-адрес BME280 — 0x76 или 0x77?

0x76, когда пин SDO подключён к GND, и 0x77, когда он подключён к 3,3 V. Большинство дешёвых модулей используют 0x76, а платы Adafruit — 0x77. Библиотека Adafruit по умолчанию пробует 0x77, поэтому в коде может понадобиться написать bme.begin(0x76); если сомневаешься, запусти скетч I2C-сканера.

Работает ли BME280 от 5 V?

Чип как таковой рассчитан максимум на 3,6 V. У большинства 4-пиновых модулей с пометкой VIN есть стабилизатор, поэтому их можно подключать к 5V. 6-пиновые 3,3-вольтовые модули без стабилизатора питай только от 3,3 V: 5 V может повредить датчик.

Как исправить ошибку «Could not find a valid BME280 sensor»?

Сначала посмотри на значение SensorID, которое выводится под ошибкой. Если это 0x58, датчик у тебя BMP280, и нужна библиотека Adafruit BMP280. Если 0x0 или 0xFF, проблема в адресе или подключении: попробуй begin(0x76), убедись, что SDA и SCL не перепутаны и что пин CSB на 3,3 V.

Насколько точно BME280 измеряет высоту?

Изменения относительной высоты он измеряет с точностью около ±1 м, и этого хватает, чтобы заметить переход на другой этаж. Абсолютная высота зависит от введённого давления на уровне моря: ошибка в 1 гПа сдвигает результат примерно на 8 м. Если ввести текущее значение QNH или откалибровать по известной высоте, можно добиться точности в несколько метров.

Почему BME280 завышает температуру?

Самые частые причины — самонагрев из-за непрерывных измерений и тепло от соседних деталей, например ESP32 или стабилизатора. Перейди в режим forced, делай несколько измерений в минуту и вынеси датчик от платы на кабеле. Если остаётся постоянное смещение, сравни показания с надёжным термометром и исправь его через setTemperatureCompensation().

Все проекты
Температура и влажность с DHT11
#55Средний

Температура и влажность с DHT11

Насколько тепло в твоей комнате и влажный ли воздух или сухой? Датчик DHT11 измеряет температуру и влажность, а Arduino выводит значения на двухстрочный LCD-экран и обновляет их каждые пару секунд. Ес

4.7(47)
73
Температура и влажность с DHT22 на ESP32 (с LCD-дисплеем)
#117Средний

Температура и влажность с DHT22 на ESP32 (с LCD-дисплеем)

Этот проект измеряет температуру и влажность воздуха датчиком DHT22 и в реальном времени показывает данные на I2C LCD 16x2. Данные читаются с датчика по однопроводному протоколу через библиотеку Adafruit DHT. Проект служит основой для IoT-метеостанций, умного дома или систем мониторинга среды в теплицах. Связь с DHT22 идёт через пин GPIO 4 у ESP32.

4.8(38)
56
Часы, календарь и метеостанция на матрице (MAX7219, DS1307)
#85Средний
Premium

Часы, календарь и метеостанция на матрице (MAX7219, DS1307)

Табло с бегущей строкой для стола или стены, которое по очереди показывает время, дату и погоду в комнате. На светодиодном дисплее 8x32 сначала видна температура в градусах Цельсия и Фаренгейта, затем

5.0(50)
66
Регистратор температуры на ESP32 (DHT22 + DS1307 RTC + microSD + OLED)
#144Продвинутый
Premium

Регистратор температуры на ESP32 (DHT22 + DS1307 RTC + microSD + OLED)

Собери на ESP32 регистратор температуры с отметками времени! Каждую минуту читай температуру и влажность с DHT22, бери реальное время из DS1307 RTC и записывай всё на карту microSD в формате CSV. OLED показывает текущую температуру (крупным шрифтом), влажность в процентах, время и счётчик записей. Файл /temp_log.csv на SD-карте открывается в Excel, Google Sheets или Python pandas — идеально для теплиц, комнат, лабораторий и хранения продуктов.

4.7(7)
12
ESP32 HTTPClient + API погоды (с OLED-дисплеем SSD1306)
#134Продвинутый
Premium

ESP32 HTTPClient + API погоды (с OLED-дисплеем SSD1306)

В этом проекте ESP32 через HTTPClient.h делает вызов REST API к сервису погоды (wttr.in). В отличие от прошлой версии с I2C LCD, здесь используется OLED SSD1306 (128×64), который позволяет крупные шрифты, векторные значки и богатую картинку на одном экране. Семь векторных значков погоды (солнечно, переменная облачность, облачно, дождь, снег, гроза, туман) рисуются во время работы (растровые файлы не нужны). Температура выводится крупными цифрами.

4.3(10)
19
Асинхронный веб-сервер на ESP32 (с выводом IP на LCD)
#129Продвинутый

Асинхронный веб-сервер на ESP32 (с выводом IP на LCD)

В этом проекте ESP32 превращается в асинхронный веб-сервер, которым можно управлять с телефона или компьютера в той же сети Wi-Fi. Благодаря библиотеке ESPAsyncWebServer сервер может обрабатывать несколько запросов одновременно (в отличие от классического WebServer.h, который блокируется на одном запросе). Открыв IP-адрес ESP32 в браузере, ты нажимаешь кнопки ON/OFF (маршруты /on и /off) и включаешь и выключаешь светодиод на GPIO 5.

4.8(16)
26
ESP32 HTTPClient + API погоды (с экраном LCD)
#133Продвинутый
Premium

ESP32 HTTPClient + API погоды (с экраном LCD)

В этом проекте ESP32 через HTTPClient.h делает вызов REST API к сервису погоды (wttr.in) через интернет. Полученный ответ в JSON разбирается с помощью ArduinoJson v7, а город, температура, влажность, погода и ветер выводятся на I2C LCD на турецком и английском. wttr.in бесплатен и не требует ключа API — достаточно указать любое название города, чтобы получить текущую погоду. Данные автоматически обновляются каждые 30 минут.

4.7(13)
20
I2C LCD-дисплей (16x2) на ESP32
#119Средний
Premium

I2C LCD-дисплей (16x2) на ESP32

В этом проекте символьный LCD-экран 16x2 подключается к ESP32 по протоколу I2C и выводит текст. Благодаря I2C экраном можно управлять всего двумя пинами данных (SDA и SCL), не тратя 6+ пинов, как при классическом параллельном подключении. Код печатает постоянное сообщение в первой строке и счётчик, растущий каждую секунду, во второй. Кроме того, при запуске работает сканер I2C, который выводит адрес дисплея в монитор порта.

4.9(23)
40