Поставил четыре датчика влажности на одну шину I2C — и они разошлись на 10%

от автора

У каждого датчика температуры и влажности в даташите написана своя погрешность. Но честная проверка -не читать даташит, а положить несколько штук в один и тот же воздух и снять показания в один и тот же момент. Я взял четыре популярных копеечных модуля с AliExpress — BME280, BMP280, AHT20 и HDC1080 — посадил их на одну шину I2C к одной ESP-шке и вывел всё на одну веб-страницу: у каждого датчика живое значение и график тренда, рядом.

Температуры совпали в пределах полуградуса. А вот с влажностью получилось интересно: один датчик показал на почти 10 процентных пунктов больше соседнего, который лежал в пяти миллиметрах от него. Ниже — как это выглядело, почему так вышло и на чём собран сам дашборд (спойлер: это несколько строк C++, без единой строчки фронтенда).

Зачем вообще сравнивать датчики

Если вы логируете теплицу, серверную, террариум или просто следите за микроклиматом в комнате, сдвиг влажности в 10% — это разница между «норма» и «тревога». Проблема в том, что по одному голому модулю вы никогда не узнаете, врёт он или нет: цифра выглядит правдоподобно, график ровный, всё «работает». Расхождение вылезает только когда рядом стоит второй датчик и показывает другое.

Так что цель была простая: поставить четыре разных чипа бок о бок и посмотреть, кто с кем согласен.

Железо

Всё подключение — это буквально четыре провода. В этом и весь смысл I2C: все датчики делят одни и те же SDA, SCL, 3V3 и GND, а различаются только адресом на шине.

ESP8266 (Wemos D1 mini) или ESP32 — 200–400 ₽

BME280 (0x76) — температура + влажность + давление

BMP280 (0x77) — давление + температура, влажности нет

AHT20 (0x38) — температура + влажность

HDC1080 (0x40) — температура + влажность

На ESP8266 шина — это SDA = D2 (GPIO4), SCL = D1 (GPIO5); на ESP32 — GPIO21 / GPIO22. Адреса у всех четырёх разные, так что конфликта нет.

Отдельная история — вот эта фиолетовая плата:

Классическая ловушка AliExpress: на шелкографии написано сразу пять названий — SI7021 / HTU21D / SHT21 / SHT20 / HDC1080. Это одна и та же печатная плата, на которую распаивают любой из пяти разных чипов, и у них разные драйверы. Если не знаете, что вам приехало, — сканер I2C покажет адрес, а по адресу уже можно опознать чип. У меня на 0x40 сидел HDC1080 (библиотека ClosedCube HDC1080).

Результаты

Все четыре вышли на связь одновременно. Вот что показал живой дашборд:

| Датчик  | Адрес | Температура | Влажность | Давление |

|—————|———|—————-|————-|—————|

| BME280  | 0x76  | 28.5 °C     | 30.3 %    | 967 hPa  |

| BMP280  | 0x77  | 29.0 °C     | —         | 968 hPa  |

| AHT20   | 0x38  | 28.6 °C     | 32.3 %    | —        |

| HDC1080 | 0x40  | 28.7 °C    | 39.7 %    | —        |

Температуры — плотная кучка в пределах 0.5 °C. Два датчика давления (BME280 и BMP280) сошлись до 1 hPa. А влажность разбежалась: BME280, AHT20 и BMP-компания держались около 30–32%, а HDC1080 сел на 39.7% — почти на десять пунктов выше.

Кто из них «прав»? Честный ответ — без эталонного гигрометра не сказать. Это ровно тот межэкземплярный разброс, который производитель закладывает в ±2…3% RH по даташиту, плюс разница в прогреве, самонагреве чипа и свежести калибровки. Ни один из них не сломан — просто им нельзя слепо доверять поодиночке. Отсюда практический вывод: если влажность у вас что-то решает, калибруйте по эталону, а не по одному голому модулю. И да — держать несколько датчиков рядом и смотреть на них вместе оказалось на удивление залипательно.

Код: дашборд в несколько строк

Теперь самое интересное для Хабра — как это выведено в браузер. Никакого HTML, CSS, JS и вебсокетов руками я не писал. Весь UI описывается декларативно: объявляешь виджет — библиотека сама генерит разметку, стили, скрипт и WebSocket-протокол, и всё это отдаёт сама ESP, без интернета и без облака.

Я использовал свою open-source библиотеку RisalDash (MIT). Вот, по сути, весь скетч — по блоку на датчик:

```cpp

#include <RisalDash.h>

#include <Wire.h>

#include <Adafruit_BME280.h>

#include <Adafruit_BMP280.h>

#include <Adafruit_AHTX0.h>

#include <ClosedCube_HDC1080.h>

RisalUI dash("Sensor Compare");

Adafruit_BME280    bme;   // 0x76

Adafruit_BMP280    bmp;   // 0x77

Adafruit_AHTX0     aht;   // 0x38

ClosedCube_HDC1080 hdc;   // 0x40

bool bmeOK, bmpOK, ahtOK, hdcOK;

float bmeT, bmeH, bmeP, bmpT, bmpP, ahtT, ahtH, hdcT, hdcH;

void setup() {

  Wire.begin();

  bmeOK = bme.begin(0x76);   // авто-детект: воткнул вышел на связь

  bmpOK = bmp.begin(0x77);

  ahtOK = aht.begin();       // 0x38

  hdcOK = i2cPresent(0x40);

  if (hdcOK) hdc.begin(0x40);

  dash.separator("BME280 · 0x76");

  dash.led("BME280 online", &bmeOK);

  dash.metric("BME280 temp", &bmeT, "°C").decimals(1);

  dash.chart("BME280 temp trend", &bmeT, "°C");

  dash.metric("BME280 humidity", &bmeH, "%").decimals(1);

  dash.metric("BME280 pressure", &bmeP, "hPa").decimals(0);

  // ...ещё три таких же блока: BMP280, AHT20, HDC1080

  dash.enableOTA();

  dash.apName("SensorCompare");

  dash.begin();              // captive-portal, затем подключается к вашему Wi-Fi

}

```

Виджет привязывается к переменной по указателю (&bmeT) — а в loop() вы просто раз в пару секунд обновляете эти переменные, и каждый открытый браузер синхронно перерисовывается по WebSocket:

```cpp

void loop() {

  dash.update();

  static uint32_t last = 0;

  if (millis() - last > 2000) {

    last = millis();

    if (bmeOK) { bmeT = bme.readTemperature();

                 bmeH = bme.readHumidity();

                 bmeP = bme.readPressure() / 100.0f; }

    // ...остальные датчики, с проверками isnan()

  }

}

```

dash.led("... online", &bmeOK) — тот же приём: булев флаг от begin() и управляет индикатором на карточке. Сняли датчик с шины — карточка гаснет в «offline», остальные работают дальше.

Wi-Fi без хардкода

Пароль от сети в код я не зашивал. При первом включении плата не находит сохранённой сети и поднимает точку доступа с captive-порталом (SensorCompare). Подключаешься телефоном — страница настройки открывается сама, выбираешь домашний Wi-Fi, вводишь пароль. ESP сохраняет данные, перезагружается и дальше подключается к сети сама. Дашборд ждёт по своему IP в локалке. Всё отдаётся с устройства — наружу ничего не уходит.

А поскольку в скетче включён dash.enableOTA(), следующие прошивки летят по воздуху прямо из браузера — USB-кабель нужен ровно один раз.

Итог

Четыре датчика, одна шина, одна страница — и наглядная картинка того, насколько по-разному копеечные модули видят один и тот же воздух. Температуре можно верить, давлению — тем более, а вот влажность стоит проверять хотя бы вторым датчиком, а лучше эталоном.

Полный, собранный и проверенный на ESP32 и ESP8266 исходник лежит здесь:

Пример Sensor Compare: https://github.com/ziyarago/RisalDash/tree/main/examples/06_Projects/SensorCompare

Библиотека RisalDash (MIT):
https://github.com/ziyarago/RisalDash

Сайт с примерами и живым демо: dash.risal.io

Если тоже ловили расхождения между модулями — расскажите в комментариях, какой чип у вас оказался самым честным. Мне интересно, HDC1080 всегда так завышает или это мне попался конкретно бодрый экземпляр.

ссылка на оригинал статьи https://habr.com/ru/articles/1061456/