Xiaomi-датчики + ESP32: мониторинг теплицы за 4000 ₽ без пайки

Фермозавр·9 апреля 2026 г.·11 мин чтения

Xiaomi LYWSD03MMC за 300 ₽ точнее самодельного DHT22 и работает полтора года от батарейки. ESP32 ловит BLE-пакеты с 10 датчиков без пайки и проводов — и отправляет всё в Grafana.

Xiaomi-датчики + ESP32: мониторинг теплицы за 4000 ₽ без пайки

Паять датчики температуры — интересно, но не обязательно. В любом DNS или Wildberries продаются Xiaomi LYWSD03MMC — крошечные BLE-термогигрометры за 250–400 ₽. Они точные (±0.1°C), компактные, работают от батарейки CR2032 полтора года и передают данные по Bluetooth Low Energy. Проблема одна: штатно они работают только через приложение Mi Home и не отдают данные в вашу систему мониторинга. Сегодня решаем это: ESP32 как BLE-сканер собирает показания с любого количества Xiaomi-датчиков и отправляет их в InfluxDB — без прошивки датчиков, без пайки, без модификаций.

Почему Xiaomi, а не DHT22

Самодельный датчик на DHT22 + ESP32 из наших предыдущих статей — рабочее решение. Но у Xiaomi-термометров есть преимущества для тех, кто не хочет паять:

  • Готовый корпус. Пластик, экранчик, крепление на магните. Кидаете в теплицу — работает
  • Батарея на 1.5 года. CR2032 стоит 30 ₽. DHT22 на ESP32 с deep sleep — максимум 8 месяцев на 18650
  • Калиброванный сенсор. Точность ±0.1°C, влажность ±2%. DHT22 даёт ±0.5°C и ±2–5%
  • Дёшево. 250 ₽ за штуку на распродажах. ESP32 + DHT22 + корпус + батарея = 800–1200 ₽
  • Экранчик. На месте видно текущие показания без телефона

Минус один: BLE-радиус — 10–15 метров в помещении, до 30 метров на открытом воздухе. Для теплицы, склада, хранилища — достаточно. Для поля в 5 км — нет, там нужен LoRa (и об этом мы уже писали).

Как это работает: BLE advertising

Xiaomi LYWSD03MMC каждые ~2.5 секунды отправляет BLE advertisement — короткий радиопакет, который может поймать любой Bluetooth-приёмник. Внутри пакета зашиты температура, влажность и уровень батареи. ESP32 имеет встроенный BLE-модуль — никакого дополнительного железа не нужно.

Схема:

[Xiaomi #1] ─── BLE ───┐
[Xiaomi #2] ─── BLE ───┤
[Xiaomi #3] ─── BLE ───┼──→ [ESP32-хаб] ──→ WiFi ──→ [InfluxDB/Grafana]
[Xiaomi #4] ─── BLE ───┤
[Xiaomi #5] ─── BLE ───┘

ESP32 не подключается к датчикам (нет pairing) — он просто слушает эфир и вылавливает пакеты с нужными MAC-адресами. Это называется passive scan — датчики даже не знают, что их читают.

Что понадобится

  • ESP32 DevKit V1 — любой модуль с WiFi + BLE, 350–500 ₽
  • Xiaomi LYWSD03MMC — от 1 до 10 штук, 250–400 ₽ каждый
  • USB-кабель + блок питания 5V — ESP32 будет работать постоянно (не на батарее)

Всё. Ни проводов, ни пайки, ни макетной платы. ESP32 стоит на полке, Xiaomi-датчики разбросаны по теплице.

Шаг 1: узнаём MAC-адреса датчиков

Каждый Xiaomi-датчик имеет уникальный MAC-адрес. Чтобы ESP32 знал, какие пакеты ловить, нужно узнать адреса. Самый простой способ — через приложение Mi Home или nRF Connect (бесплатно, Android/iOS):

  • Откройте nRF Connect → Scan
  • Ищите устройства с именем LYWSD03MMC
  • MAC-адрес выглядит как A4:C1:38:XX:XX:XX

Запишите MAC-адрес каждого датчика и дайте ему понятное имя: «Теплица-1», «Хранилище-картофель», «Рассадник». Нам это понадобится в коде.

Шаг 2: прошивка ESP32 — BLE-сканер

Используем Arduino IDE (или PlatformIO). Библиотеки BLE и WiFi встроены в ESP32 Arduino Core — устанавливать ничего не нужно. Для отправки в InfluxDB используем HTTP-запрос.

#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEScan.h>
#include <WiFi.h>
#include <HTTPClient.h>

// === WiFi ===
const char* WIFI_SSID = "FarmNet";
const char* WIFI_PASS = "your-password";

// === InfluxDB ===
const char* INFLUX_URL = "http://192.168.1.100:8086";
const char* INFLUX_TOKEN = "your-influx-token";
const char* INFLUX_ORG = "farm";
const char* INFLUX_BUCKET = "sensors";

// === Известные датчики (MAC → имя) ===
struct SensorInfo {
  String mac;
  String name;
};

SensorInfo knownSensors[] = {
  {"a4:c1:38:aa:bb:01", "greenhouse_1"},
  {"a4:c1:38:aa:bb:02", "greenhouse_2"},
  {"a4:c1:38:aa:bb:03", "storage_potato"},
  {"a4:c1:38:aa:bb:04", "seedling_room"},
  {"a4:c1:38:aa:bb:05", "barn"},
};
const int NUM_SENSORS = 5;

BLEScan* pBLEScan;

// Сервисные UUID для Xiaomi LYWSD03MMC
// Данные приходят в advertisement как Service Data (UUID 0x181A)
const BLEUUID serviceUUID((uint16_t)0x181A);

String findSensorName(String mac) {
  mac.toLowerCase();
  for (int i = 0; i < NUM_SENSORS; i++) {
    if (knownSensors[i].mac == mac) return knownSensors[i].name;
  }
  return "";
}

void sendToInflux(String name, float temp, float hum, int bat) {
  if (WiFi.status() != WL_CONNECTED) return;

  HTTPClient http;
  String url = String(INFLUX_URL) + "/api/v2/write?org="
    + INFLUX_ORG + "&bucket=" + INFLUX_BUCKET;
  
  // Line protocol: measurement,tag=value field=value
  String body = "xiaomi_ble,device=" + name
    + " temperature=" + String(temp, 1)
    + ",humidity=" + String(hum, 1)
    + ",battery=" + String(bat) + "i";

  http.begin(url);
  http.addHeader("Authorization", "Token " + String(INFLUX_TOKEN));
  http.addHeader("Content-Type", "text/plain");
  int code = http.POST(body);
  
  if (code == 204) {
    Serial.println("OK: " + name + " → " + String(temp) + "°C");
  } else {
    Serial.println("ERR " + String(code) + ": " + name);
  }
  http.end();
}

// Парсинг BLE advertisement от LYWSD03MMC
// Формат Service Data (UUID 0x181A):
// MAC(6) + temp_int16(2) + hum_uint16(2) + bat_uint8(1) + ...
void parseXiaomiAdv(BLEAdvertisedDevice device) {
  if (!device.haveServiceData()) return;
  
  String mac = device.getAddress().toString().c_str();
  String sensorName = findSensorName(mac);
  if (sensorName == "") return;  // Не наш датчик

  std::string data = device.getServiceData();
  if (data.length() < 13) return;

  // Температура: байты 6-7, в 0.01°C, знаковое
  int16_t rawTemp = (data[7] << 8) | data[6];
  float temp = rawTemp / 100.0;

  // Влажность: байты 8-9, в 0.01%
  uint16_t rawHum = (data[9] << 8) | data[8];
  float hum = rawHum / 100.0;

  // Батарея: байт 12, в процентах
  uint8_t bat = data[12];

  Serial.printf("[%s] %s: %.1f°C, %.0f%%, bat=%d%%\n",
    mac.c_str(), sensorName.c_str(), temp, hum, bat);

  sendToInflux(sensorName, temp, hum, bat);
}

class MyAdvertisedDeviceCallbacks : public BLEAdvertisedDeviceCallbacks {
  void onResult(BLEAdvertisedDevice device) {
    parseXiaomiAdv(device);
  }
};

void setup() {
  Serial.begin(115200);
  
  // WiFi
  WiFi.begin(WIFI_SSID, WIFI_PASS);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("\nWiFi OK: " + WiFi.localIP().toString());

  // BLE
  BLEDevice::init("FarmBLEHub");
  pBLEScan = BLEDevice::getScan();
  pBLEScan->setAdvertisedDeviceCallbacks(new MyAdvertisedDeviceCallbacks());
  pBLEScan->setActiveScan(false);  // Passive — не шлём запросы
  pBLEScan->setInterval(100);
  pBLEScan->setWindow(99);
}

void loop() {
  Serial.println("Scanning BLE...");
  pBLEScan->start(30, false);  // Сканируем 30 секунд
  pBLEScan->clearResults();
  delay(5000);  // Пауза 5 секунд между сканами
}

Код делает простую вещь: каждые 35 секунд (30 сканирование + 5 пауза) ESP32 ловит все BLE-пакеты вокруг, фильтрует по белому списку MAC-адресов, парсит температуру/влажность/батарею и отправляет в InfluxDB. Один ESP32 обслуживает до 10–15 датчиков без проблем.

Альтернативный прошивочный путь: кастомная прошивка для Xiaomi

Есть проект pvvx/ATC_MiThermometer на GitHub. Он перепрошивает LYWSD03MMC через веб-браузер (Web Bluetooth, Chrome) — без проводов, без паяльника, за 2 минуты. После прошивки датчик шлёт advertisement в открытом формате (без шифрования), данные легче парсить, и можно настроить интервал отправки.

Мы не прошиваем датчики в этой статье — стоковая прошивка работает. Но если у вас проблемы с парсингом (Xiaomi иногда меняет формат между партиями) — кастомная прошивка решает все вопросы раз и навсегда.

Настройка InfluxDB и Grafana

Если у вас уже настроен InfluxDB + Grafana по нашей инструкции — данные сразу появятся в bucket «sensors». Создайте панель с Flux-запросом:

from(bucket: "sensors")
  |> range(start: -24h)
  |> filter(fn: (r) => r._measurement == "xiaomi_ble")
  |> filter(fn: (r) => r._field == "temperature")
  |> aggregateWindow(every: 5m, fn: mean)

Отдельные панели для влажности и батареи — аналогично, меняете _field. Для батареи используйте тип Gauge с порогами: зелёный >50%, жёлтый 20–50%, красный <20%.

Алерт на критическую температуру

В Grafana создайте Alert Rule:

  • Условие: temperature > 38 ИЛИ temperature < 2 для measurement xiaomi_ble
  • Интервал проверки: 1 минута
  • Отправка: Telegram (настроили в статье про LoRa-шлюз)

Теперь если в теплице ночью температура упадёт ниже +2°C — вы узнаете об этом за минуту, а не утром, когда рассада уже замёрзла.

Комбо: BLE + LoRa для удалённых теплиц

Самое интересное — объединить BLE и LoRa на одном ESP32. Сценарий: теплица в 3 км от дома, WiFi там нет, но внутри стоят 5 Xiaomi-датчиков.

[Xiaomi ×5] ── BLE ──→ [ESP32 + SX1278] ── LoRa 5 км ──→ [Raspberry Pi шлюз] ──→ InfluxDB

ESP32 сканирует BLE, собирает сводку со всех датчиков и раз в 15 минут отправляет один LoRa-пакет с данными всех пяти. Формат пакета:

// Пакет: BLE_HUB_01|gh1:24.5:62:95|gh2:23.1:58:90|storage:4.2:85:78
String packet = "BLE_HUB_01";
for (int i = 0; i < collectedCount; i++) {
  packet += "|" + readings[i].name 
    + ":" + String(readings[i].temp, 1)
    + ":" + String((int)readings[i].hum)
    + ":" + String(readings[i].bat);
}
LoRa.beginPacket();
LoRa.print(packet);
LoRa.endPacket();

На стороне шлюза (Raspberry Pi) парсим пакет, разбиваем по |, каждый подпакет — отдельная запись в InfluxDB. Один LoRa-пакет (до 255 байт) вмещает данные от ~15 датчиков.

Этот подход даёт лучшее из двух миров: BLE для дешёвых датчиков в ближней зоне (10–30 м) и LoRa для доставки данных на километры. Батарея ESP32 + SX1278 с deep sleep между сканами — около 3 месяцев на 18650.

Типичные проблемы и решения

Датчик не находится при сканировании. Xiaomi LYWSD03MMC рекламирует себя каждые ~2.5 секунды, но бывают паузы. Увеличьте время скана с 30 до 60 секунд. Также проверьте, что датчик не подключён к телефону — BLE-соединение блокирует advertisement.

Температура приходит как 0.0°C. Некоторые партии LYWSD03MMC шифруют данные в advertisement (bind key). Решения два: перепрошить на кастомную прошивку (pvvx/ATC_MiThermometer) или извлечь bind key из Mi Home (утилита TelinkFlasher в том же репозитории).

WiFi и BLE мешают друг другу на ESP32. Они делят одну антенну. При одновременной работе возможны потери BLE-пакетов. Решение: сканируйте BLE 30 секунд, потом отключайте BLE, отправляйте данные по WiFi, снова включайте BLE. Код:

// Фаза 1: BLE-скан
BLEDevice::init("FarmBLEHub");
pBLEScan->start(30, false);
BLEDevice::deinit(false);  // Выключаем BLE

// Фаза 2: WiFi-отправка
WiFi.begin(WIFI_SSID, WIFI_PASS);
// ... отправляем данные ...
WiFi.disconnect(true);

// Фаза 3: снова BLE
BLEDevice::init("FarmBLEHub");

ESP32 перезагружается каждые несколько часов. Memory leak в BLE-стеке — известная проблема старых версий ESP32 Arduino Core. Обновите до версии 3.x. Также помогает pBLEScan->clearResults() после каждого скана (уже есть в нашем коде).

Один датчик из пяти пропадает. Вероятнее всего — расстояние. BLE теряет сигнал через 2 стены из кирпича. Переместите ESP32 ближе к центру теплицы или добавьте второй хаб.

Стоимость: 10 точек мониторинга за 4000 ₽

Считаем систему для двух теплиц по 5 датчиков:

  • 10 × Xiaomi LYWSD03MMC — 10 × 300 ₽ = 3000 ₽
  • 2 × ESP32 DevKit — 2 × 400 ₽ = 800 ₽
  • 2 × USB-блок питания 5V — 2 × 200 ₽ = 400 ₽

Итого: 4200 ₽ за 10 точек температуры + влажности + батареи с графиками и алертами. Для сравнения: профессиональная система мониторинга теплиц (Vaisala, Testo) с 10 датчиками стоит от 150 000 ₽.

Если теплицы далеко от WiFi — добавляете SX1278 на ESP32 (ещё 250 ₽ за модуль) и отправляете через LoRa на шлюз. Данные попадают в ту же Grafana.

Где ещё пригодятся BLE-сенсоры на ферме

Xiaomi — не единственные. ESP32 ловит любые BLE-устройства:

  • Xiaomi Mi Flora (400 ₽) — датчик для растений: влажность почвы, освещённость, температура, плодородность. Идеально для рассадника
  • Xiaomi Mi Body Scale 2 — весы, которые отлично взвешивают ящики с урожаем. ESP32 ловит BLE-данные и пишет массу в базу
  • BLE-термометры для мяса/молока (Meater, ThermoBeacon) — контроль температуры при хранении продукции
  • BLE-маячки (Tile, AirTag-аналоги) — прикрепляете к ящикам, ESP32-сканеры на складе определяют, в какой зоне ящик

Совет: платформы вроде ТерраКвант мониторят поля со спутника. BLE-датчики дополняют картину микроклиматом внутри строений — там, куда спутник не заглянет.

Продвинутая настройка: ESPHome вместо Arduino

Если ручной код на Arduino кажется сложным — есть ESPHome. Это YAML-конфигуратор, который генерирует прошивку для ESP32 автоматически. Весь BLE-хаб описывается в одном файле:

esphome:
  name: ble-hub-greenhouse

esp32:
  board: esp32dev

wifi:
  ssid: "FarmNet"
  password: "your-password"

sensor:
  - platform: xiaomi_lywsd03mmc
    mac_address: "A4:C1:38:AA:BB:01"
    temperature:
      name: "Теплица 1 — Температура"
    humidity:
      name: "Теплица 1 — Влажность"
    battery_level:
      name: "Теплица 1 — Батарея"

  - platform: xiaomi_lywsd03mmc
    mac_address: "A4:C1:38:AA:BB:02"
    temperature:
      name: "Теплица 2 — Температура"
    humidity:
      name: "Теплица 2 — Влажность"
    battery_level:
      name: "Теплица 2 — Батарея"

# Повторите для каждого датчика

influxdb:
  host: "192.168.1.100"
  port: 8086
  token: "your-influx-token"
  organization: "farm"
  bucket: "sensors"

Компилируете через esphome run ble-hub.yaml, прошиваете по USB — готово. ESPHome сам обрабатывает все нюансы: переподключение к WiFi, парсинг BLE-пакетов, bind key. Обновление прошивки — по воздуху (OTA), не нужно нести ноутбук в теплицу.

Минус ESPHome: тяжелее добавить LoRa (нет встроенного компонента для SX1278). Для чистого BLE → WiFi → InfluxDB — ESPHome проще. Для комбо BLE + LoRa — Arduino-код из нашей статьи гибче.

Мониторинг здоровья самого хаба

ESP32-хаб работает 24/7, и важно знать, что он жив. Добавьте в код отправку heartbeat каждые 5 минут:

void sendHeartbeat() {
  String body = "hub_status,device=ble_hub_01"
    " uptime=" + String(millis() / 1000) + "i"
    ",free_heap=" + String(ESP.getFreeHeap()) + "i"
    ",wifi_rssi=" + String(WiFi.RSSI()) + "i"
    ",sensors_seen=" + String(lastScanCount) + "i";
  // ... отправка в InfluxDB аналогично sendToInflux()
}

В Grafana добавьте панель «Hub Health» с алертом: если heartbeat не приходил больше 10 минут — ESP32 завис или потерял WiFi. Перезагрузите его (или добавьте hardware watchdog — таймер на 60 секунд, который перезапускает чип автоматически).

Итог

ESP32 + горстка Xiaomi-термометров — самый дешёвый и ленивый способ получить полноценный мониторинг теплицы. Без пайки, без программирования датчиков, без абонентской платы. Залили прошивку на один ESP32, разбросали датчики по полкам — и через минуту в Grafana появились графики. А если теплица далеко — комбинируете BLE и LoRa на том же ESP32, и данные летят на 10 км без WiFi. Одна плата за 400 ₽ — три радиопротокола.

Источник: Фермозавр

💬 Комментарии

Чтобы оставить комментарий, войдите или зарегистрируйтесь

Загрузка комментариев...

Похожие статьи