MQTT + VK бот — управление десятком устройств через единый хаб сообщений

Админ·12 апреля 2026 г.·8 мин чтения

Когда датчиков больше десяти, прямые HTTP-запросы в мессенджер перестают справляться. Ставим MQTT-брокер Mosquitto, подключаем все ESP32 и управляем фермой через VK-бота на Node.js.

MQTT + VK бот — управление десятком устройств через единый хаб сообщений

Пока датчиков на ферме два-три, система из предыдущих статей работает отлично: ESP32 напрямую отправляет данные в Telegram (или VK, или email). Но когда датчиков становится десять? Двадцать? Метеостанция в теплице, автополив на грядках, влагомер в поле, трекер на тракторе, весы на приёмке — и каждый шлёт HTTP-запросы напрямую в мессенджер?

Начинаются проблемы. ESP32 тратит 2–3 секунды на каждый HTTP-запрос и не может в это время делать замеры. Wi-Fi буфер переполняется. Бот получает 200 сообщений в час и превращается в свалку. Вы не понимаете, какое из устройств отправило алерт, а какое просто прислало плановый отчёт.

Решение — MQTT-брокер как центральный хаб. Все устройства публикуют данные в топики, а один сервер обрабатывает их и отправляет уведомления через VK-бота — структурированно, с фильтрацией и приоритетами.

Архитектура: почему MQTT, а не прямые запросы

MQTT — это лёгкий протокол обмена сообщениями, придуманный специально для IoT. Три ключевых преимущества для фермера:

  • Мгновенная отправка. MQTT-пакет — 50–200 байт. HTTP-запрос в Telegram — 2–5 КБ + SSL-хендшейк. На нестабильном Wi-Fi (а в поле он всегда нестабильный) разница между «отправил за 50 мс» и «отправлял 3 секунды» — критична
  • Отделение устройств от логики. ESP32 не знает про VK, Telegram, email. Он просто публикует данные в топик: ferma/teплица1/temp = 24.5. А сервер решает — отправить алерт, записать в базу, нарисовать график
  • Масштабирование. Добавить новый датчик = воткнуть ESP32 и настроить топик. Сервер подхватит автоматически. Не нужно менять код бота, не нужно добавлять новые Chat ID

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

  • ESP32 — любые платы из предыдущих проектов (уже с датчиками)
  • MQTT-брокер Mosquitto — бесплатный, ставится за минуту. Работает на любом Linux-сервере, Raspberry Pi или даже на старом ноутбуке
  • Node.js — для серверной логики (обработка сообщений + VK-бот)
  • VK Community (группа) — через неё работает бот

Бюджет: 0 ₽ (всё бесплатное ПО). Сервер — любой Linux-компьютер с постоянным интернетом. Подойдёт даже Raspberry Pi 3 за 3 000 ₽.

Шаг 1: Mosquitto — MQTT-брокер за 2 минуты

На Ubuntu/Debian:

sudo apt update
sudo apt install -y mosquitto mosquitto-clients
sudo systemctl enable mosquitto
sudo systemctl start mosquitto

Брокер запущен на порту 1883. Проверяем:

# В одном терминале — подписка
mosquitto_sub -t "ferma/#" -v

# В другом — публикация
mosquitto_pub -t "ferma/test" -m "Привет, MQTT!"

В первом терминале появится: ferma/test Привет, MQTT! — брокер работает.

Безопасность: по умолчанию Mosquitto принимает подключения без пароля. Для локальной сети это нормально, но если брокер доступен из интернета — обязательно настройте аутентификацию:

# Создаём файл паролей
sudo mosquitto_passwd -c /etc/mosquitto/passwd ferma_user

# В /etc/mosquitto/conf.d/auth.conf:
listener 1883
allow_anonymous false
password_file /etc/mosquitto/passwd

Шаг 2: ESP32 → MQTT — 40 строк кода

Устанавливаем библиотеку: Arduino IDE → Tools → Manage Libraries → ищем PubSubClient by Nick O'Leary → Install.

Добавляем MQTT в любой существующий проект (метеостанция, автополив, и т.д.):

#include <WiFi.h>
#include <PubSubClient.h>
#include <DHT.h>

const char* WIFI_SSID     = "Название_сети";
const char* WIFI_PASSWORD  = "пароль";
const char* MQTT_SERVER    = "192.168.1.100"; // IP вашего сервера
const int   MQTT_PORT      = 1883;
const char* DEVICE_ID      = "teplica1";      // уникальный ID устройства

WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
DHT dht(4, DHT22);

void reconnectMQTT() {
  while (!mqtt.connected()) {
    if (mqtt.connect(DEVICE_ID)) {
      Serial.println("MQTT подключён");
      // Подписка на команды
      mqtt.subscribe(("ferma/" + String(DEVICE_ID) + "/cmd").c_str());
    } else {
      delay(5000);
    }
  }
}

// Обработка входящих команд
void onMessage(char* topic, byte* payload, unsigned int length) {
  String msg = "";
  for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];
  Serial.println("Команда: " + msg);

  if (msg == "status") {
    float t = dht.readTemperature();
    float h = dht.readHumidity();
    String reply = "{\"temp\":" + String(t,1) + ",\"hum\":" + String(h,1) + "}";
    mqtt.publish(("ferma/" + String(DEVICE_ID) + "/data").c_str(), reply.c_str());
  }
  if (msg == "reboot") {
    ESP.restart();
  }
}

void setup() {
  Serial.begin(115200);
  dht.begin();
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  while (WiFi.status() != WL_CONNECTED) delay(500);

  mqtt.setServer(MQTT_SERVER, MQTT_PORT);
  mqtt.setCallback(onMessage);
}

void loop() {
  if (!mqtt.connected()) reconnectMQTT();
  mqtt.loop();

  // Отправка данных каждые 5 минут
  static unsigned long lastSend = 0;
  if (millis() - lastSend > 300000) {
    float t = dht.readTemperature();
    float h = dht.readHumidity();
    if (!isnan(t) && !isnan(h)) {
      String json = "{\"temp\":" + String(t,1) + ",\"hum\":" + String(h,1) + "}";
      mqtt.publish(("ferma/" + String(DEVICE_ID) + "/data").c_str(), json.c_str());
    }
    lastSend = millis();
  }
}

Важное отличие от прошлых проектов: ESP32 теперь не просто отправляет данные — он ещё слушает команды. Вы можете запросить текущие показания (status) или перезагрузить устройство (reboot) — прямо из VK-бота.

Шаг 3: VK-бот — получаем токен

VK Bot API — бесплатный и не требует регистрации домена или SSL-сертификата (в отличие от Telegram Webhook). Работает через Long Polling — бот сам периодически проверяет новые сообщения.

Создаём сообщество ВКонтакте (если нет): vk.com → Создать сообщество → Бизнес. Название — например, «Моя ферма IoT».

Получаем токен: Управление → Работа с API → Создать ключ. Даём права: «Разрешить сообщения сообщества», «Доступ к сообщениям». Копируем ключ — это VK_TOKEN.

Включаем Long Poll API: Управление → Работа с API → Long Poll API → Включено. Версия API: 5.199. Типы событий: «Входящее сообщение».

Шаг 4: Node.js сервер — мост между MQTT и VK

Вот полный код серверной части. Сохраняем в файл farm-hub.js:

const mqtt = require('mqtt');
const { VK } = require('vk-io');

// === НАСТРОЙКИ === //
const MQTT_URL   = 'mqtt://localhost:1883';
const VK_TOKEN   = 'ваш_токен_сообщества';
const ADMIN_ID   = 123456789; // ваш VK user ID

const vk = new VK({ token: VK_TOKEN });
const client = mqtt.connect(MQTT_URL);

// Хранилище последних данных
const devices = {};

// MQTT: подписка на все устройства
client.on('connect', () => {
  console.log('MQTT подключён');
  client.subscribe('ferma/#');
});

// MQTT: обработка входящих данных
client.on('message', (topic, message) => {
  const parts = topic.split('/');
  const deviceId = parts[1];
  const type = parts[2]; // "data", "alert", "status"

  if (type === 'data') {
    try {
      const data = JSON.parse(message.toString());
      devices[deviceId] = { ...data, updatedAt: new Date() };

      // Проверка порогов
      if (data.temp !== undefined) {
        if (data.temp < 5) sendAlert(deviceId, `❄️ ${deviceId}: ${data.temp}°C — холодно!`);
        if (data.temp > 35) sendAlert(deviceId, `🔥 ${deviceId}: ${data.temp}°C — перегрев!`);
      }
      if (data.hum !== undefined && data.hum > 90) {
        sendAlert(deviceId, `💦 ${deviceId}: влажность ${data.hum}% — выше нормы`);
      }
    } catch (e) {
      console.error('Parse error:', e);
    }
  }
});

// Дедупликация алертов: не чаще раза в 15 минут на устройство
const lastAlerts = {};
function sendAlert(deviceId, text) {
  const key = deviceId + text.substring(0, 2);
  const now = Date.now();
  if (lastAlerts[key] && now - lastAlerts[key] < 900000) return;
  lastAlerts[key] = now;

  vk.api.messages.send({
    user_id: ADMIN_ID,
    message: text,
    random_id: Math.floor(Math.random() * 1e9),
  }).catch(console.error);
}

// VK Bot: обработка команд
vk.updates.on('message_new', async (context) => {
  const text = context.text?.toLowerCase().trim();

  if (text === 'статус' || text === 'status') {
    const lines = Object.entries(devices).map(([id, d]) => {
      const age = Math.round((Date.now() - new Date(d.updatedAt).getTime()) / 60000);
      return `📍 ${id}: ${d.temp ?? '?'}°C, ${d.hum ?? '?'}%, ${age} мин назад`;
    });
    await context.send(lines.length
      ? '📊 Статус устройств:\n\n' + lines.join('\n')
      : '🔇 Нет данных от устройств');
  }

  else if (text?.startsWith('cmd ')) {
    // Формат: cmd teplica1 reboot
    const [, deviceId, ...cmdParts] = text.split(' ');
    const cmd = cmdParts.join(' ');
    client.publish(`ferma/${deviceId}/cmd`, cmd);
    await context.send(`✅ Команда "${cmd}" отправлена на ${deviceId}`);
  }

  else {
    await context.send(
      '🤖 Команды:\n' +
      '• статус — показать все устройства\n' +
      '• cmd [устройство] [команда] — отправить команду\n' +
      '  Пример: cmd teplica1 status'
    );
  }
});

// Запуск VK Long Poll
vk.updates.start().then(() => console.log('VK бот запущен'));
console.log('Farm Hub запущен');

Установка зависимостей и запуск:

npm init -y
npm install mqtt vk-io
node farm-hub.js

Для постоянной работы используйте PM2:

npm install -g pm2
pm2 start farm-hub.js --name farm-hub
pm2 save
pm2 startup

Шаг 5: используем бота

Откройте диалог с вашим сообществом ВКонтакте и напишите «статус». Бот ответит списком всех устройств с последними данными и временем обновления.

Напишите «cmd teplica1 status» — бот отправит команду на ESP32 через MQTT, устройство ответит свежими данными, и бот покажет их вам.

Напишите «cmd poliv1 reboot» — ESP32 автополива перезагрузится удалённо. Полезно, когда зависло соединение или нужно перечитать настройки.

Алерты приходят автоматически: если температура выходит за пороги — бот присылает сообщение. Дедупликация не даёт засыпать вас одинаковыми алертами — не чаще одного раза в 15 минут на каждый тип события.

Структура топиков: порядок вместо хаоса

Правильная иерархия топиков — залог масштабирования. Рекомендуемая схема:

ferma/
  teplica1/
    data    → {"temp":24.5,"hum":65}    (от ESP32)
    cmd     → "status" | "reboot"        (от бота к ESP32)
  teplica2/
    data
    cmd
  poliv1/
    data    → {"soilMoisture":45}
    cmd     → "start" | "stop"
  treker1/
    data    → {"lat":54.123,"lng":37.456,"speed":12}
  vesy1/
    data    → {"weight":45.6,"crop":"tomato"}

Каждое устройство — отдельная ветка. Сервер подписывается на ferma/# и получает всё. ESP32 подписывается только на свой cmd-канал и никогда не знает про соседей.

Почему VK, а не Telegram

В статье про блокировки мы разбирали риски. Telegram работает — но завтра может перестать. VK Bot API:

  • Работает без VPN и прокси на территории России
  • Не требует SSL-сертификата и белого IP (Long Poll)
  • Те же возможности: текст, кнопки, файлы, клавиатуры
  • Бесплатен для сообществ

Если у вас уже есть Telegram-бот — ничто не мешает подключить оба канала. Один MQTT-сервер, два отправщика. Добавить второй мессенджер — 20 строк кода.

Что дальше

MQTT + VK бот — это «мозг» фермерской IoT-системы. Устройства присылают данные, сервер обрабатывает, бот уведомляет и принимает команды. Но текстовые сообщения — не самый удобный интерфейс для мониторинга 20 датчиков.

В следующей статье серии — дашборд на Nuxt: собственный веб-интерфейс с графиками, картой устройств и push-уведомлениями. Полностью под вашим контролем, без Grafana и облачных сервисов.

Весь код из статьи — рабочий. Mosquitto, Node.js сервер, ESP32-клиент — всё проверено и работает вместе. Копируйте, настраивайте под своё хозяйство. Вопросы — в комментарии.

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

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

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

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