Паять датчики температуры — интересно, но не обязательно. В любом 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для measurementxiaomi_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 ₽ — три радиопротокола.




