Files
ponics-protocol/MQTT.md
T
progl cd7497b014 fix: pump ID расхождение устранено, протокол актуализирован
Прошивка исправлена (aa5db288): валидация 0-7 → 1-8.
Документация обновлена — убраны предупреждения, добавлены ссылки на фикс.
2026-05-05 13:15:19 +03:00

9.6 KiB
Raw Blame History

MQTT Protocol

Общее

  • Брокер: Mosquitto 2.x (порт 1883)
  • Topic prefix: {UID} — UUID токен устройства (из БД GrowBox.token)
  • QoS: 1 для команд, 0 для данных датчиков
  • Subscribe: сервер подписан на # (все топики)

Flow: подключение устройства

sequenceDiagram
    participant ESP32 as WegaBox (ESP32)
    participant MQTT as MQTT Broker
    participant Django as ponics.online (Django)
    participant Redis

    ESP32->>MQTT: publish {UID}/status = "connected"
    MQTT->>Django: forward
    Django->>Django: push_box_settings(box)
    Django->>MQTT: publish {UID}/set/preferences/all = {JSON}
    MQTT->>ESP32: apply calibration & config

    loop Каждые N секунд
        ESP32->>MQTT: publish {UID}/data-timescale/wEC = "1.85"
        ESP32->>MQTT: publish {UID}/data-timescale/wpH = "6.2"
        ESP32->>MQTT: publish {UID}/data-timescale/wNTC = "22.5"
        MQTT->>Django: forward
        Django->>Redis: update live cache (boxconfig-{token})
        Django->>Django: batch → TimescaleDB (каждые 25с или 100 метрик)
    end

Flow: команда с веб-интерфейса

sequenceDiagram
    participant User as Браузер
    participant Django as ponics.online (Django)
    participant MQTT as MQTT Broker
    participant ESP32 as WegaBox (ESP32)

    User->>Django: POST /api/box/{id}/pump/run/ {pump_id:1, time_ms:5000}
    Django->>MQTT: publish {UID}/set/pump/1/run = "5000"
    MQTT->>ESP32: execute pump 1 for 5000ms
    Django->>User: 200 {"success": true}
    Note over ESP32: Команды fire-and-forget, ответа нет

Топики: устройство → сервер (Subscribe)

{UID}/data-timescale/{METRIC_NAME}

Основной поток данных датчиков. Каждый датчик — отдельное сообщение.

Payload: строка с числом (например "6.24", "1.85", "22.5")

{UID}/data-timescale/wEC         → EC (мСм/см, с температурной компенсацией)
{UID}/data-timescale/wECnt       → EC без температурной компенсации
{UID}/data-timescale/wpH         → pH
{UID}/data-timescale/wNTC        → температура воды (°C, откалиброванная)
{UID}/data-timescale/RootTemp    → температура субстрата/корней (°C, DS18B20)
{UID}/data-timescale/AirTemp     → температура воздуха (°C)
{UID}/data-timescale/AirHum      → влажность воздуха (%)
{UID}/data-timescale/AirPress    → давление (гПа)
{UID}/data-timescale/wLevel      → уровень воды (% или см, HC-SR04)
{UID}/data-timescale/Light       → освещённость (%)
{UID}/data-timescale/RSSI        → уровень WiFi сигнала (dBm)
{UID}/data-timescale/uptime      → аптайм устройства (мс)
{UID}/data-timescale/CPUTemp     → температура CPU ESP32 (°C)
{UID}/data-timescale/FreeHeap    → свободная RAM (байт)
{UID}/data-timescale/Vcc         → напряжение питания (мВ)
{UID}/data-timescale/MixerWeight → вес миксера (г, HX711)
{UID}/data-timescale/mcp/gpio    → битовая маска GPIO MCP23017 (0-65535)
{UID}/data-timescale/CO2         → CO2 (ppm, SCD30)
{UID}/data-timescale/eCO2        → CO2 эквивалент (ppm, CCS811)
{UID}/data-timescale/TVOC        → летучие вещества (ppb, CCS811)

Маппинг имён на сервере: сервер дополнительно принимает синонимы EC, ph, ec, calc_dist и маппит их в канонические имена через field_mappings в config.py.

{UID}/status

Payload: строка "connected" — устройство подключилось к брокеру.
Триггер: сервер отправляет настройки (set/preferences/all).

{UID}/status/sensors

Ответ на {UID}/cmd/status/sensors. Поля зависят от подключённых датчиков.

{
  "rootTemp": 21.0,
  "waterTemp": 22.5,
  "airTemp": 24.1,
  "airHumidity": 65,
  "pressure": 1013,
  "ntcTemp": 22.4,
  "EC": 1.85,
  "pH": 6.24,
  "light": 80.0,
  "waterLevel": 45.2,
  "weight": 0.0
}

{UID}/status/mixer

Ответ на {UID}/cmd/status/mixer.

{
  "running": false,
  "phase": "idle",
  "currentPump": 0,
  "weight": 480.5,
  "pumps": [
    {"index": 1, "total_dispensed": 480.5},
    {"index": 2, "total_dispensed": 320.0}
  ]
}

Примечание: поле name в элементах pumps отсутствует в ответе прошивки. Названия помп берутся из БД сервера.

{UID}/status/full

Полный статус устройства (всё включая actuators + config). Ответ на cmd/status.

{UID}/status/actuators

Состояние реле, GPIO, помп.

{UID}/main (legacy)

Устаревший формат. Новые устройства не используют.

{
  "sensors": {"ph": "6.5", "ec": "1.8", "temp_cal": "22.5"},
  "system":  {"uptime": "3600000", "RSSI": "-65"}
}

Топики: сервер → устройство (Publish)

Команды (cmd/)

Топик Payload Описание
{UID}/cmd/reboot "1" Перезагрузка устройства
{UID}/cmd/gpio/{pin} "1" / "0" GPIO HIGH/LOW (MCP23017, pin 015)
{UID}/cmd/pump/{id}/stop "1" Стоп помпы (id 1–7, см. ниже)
{UID}/cmd/status "1" Запрос полного статуса
{UID}/cmd/status/sensors "1" Запрос данных датчиков
{UID}/cmd/status/actuators "1" Запрос статуса актуаторов
{UID}/cmd/status/mixer "1" Запрос статуса миксера
{UID}/cmd/ota "" или URL строка OTA обновление прошивки
{UID}/cmd/ota-storage "" или URL строка OTA обновление LittleFS

Управление помпами (set/pump/)

Топик Payload Ограничения
{UID}/set/pump/{id}/run "5000" (мс) id: 18, время: 160000 мс
{UID}/set/pump/{id}/dispense "50.5" (граммы) id: 18, граммы: 0.11000

Pump ID: единый стандарт 1–8 (1-based). Исправлено в прошивке commit aa5db288.

Настройки (set/preferences/all)

Полная конфигурация устройства. Все поля опциональны.

{
  "ntcDAC": 4095,
  "ntcB": 3950,
  "ntcValKorr": 0.0,
  "ntcType": "NTC3950",
  "R1": 10000,
  "Rx1": 1000,
  "Rx2": 1000,
  "Dr": 1.0,

  "ec1": 1.41,
  "ec2": 12.88,
  "ec3": 80.0,
  "ex1": 0.5,
  "ex2": 0.9,
  "ex3": 0.95,
  "ea": 0.0,
  "eb": 0.0,
  "kt": 0.019,
  "ecKorr": 1.0,

  "px1": 100,  "py1": 0.0,
  "px2": 580,  "py2": 7.0,
  "px3": 1023, "py3": 14.0,
  "pHlKorr": 0.0,

  "pR_DAC": 4095,
  "pR1": 10000,
  "pR_raw_p1": 0,   "pR_val_p1": 0,
  "pR_raw_p2": 512, "pR_val_p2": 50,
  "pR_raw_p3": 1023,"pR_val_p3": 100,
  "pR_Rx": 10000,
  "pR_T": 25,
  "pR_x": 0.5,
  "pRType": "linear",

  "maxLLevel": 30.0, "maxLRaw": 100,
  "minLLevel": 10.0, "minLRaw": 500,

  "hostname": "wega-box-1",
  "updateUrl": "https://ponics.online/static/wegabox/esp32-local/firmware.bin",
  "updateFsUrl": "https://ponics.online/static/wegabox/esp32-local/littlefs.bin",

  "ds18b20_enabled": true,
  "bme280_enabled": true,
  "aht10_enabled": false,
  "am2320_enabled": false,
  "scd30_enabled": false,
  "ccs811_enabled": false,
  "ads1115_enabled": false,
  "ec_enabled": true,
  "ntc_enabled": true,
  "pr_enabled": true,
  "hx711_enabled": false,
  "hx710b_enabled": false,
  "us025_enabled": false,
  "us025_reboot_on_error": false,
  "vl53l0x_enabled": false,
  "vcc_enabled": true,
  "cputemp_enabled": true,

  "mixer_enabled": false,
  "mixer_pumpCount": 8,
  "mixer_bidirectional": false,
  "mixer_dropThreshold": 0.5,
  "mixer_preloadMs": 0,

  "hx711_calibrationA": 1000.0,
  "hx711_calibrationB": 500.0,
  "hx711_offsetA": 0.0,
  "hx711_offsetB": 0.0,
  "hx711_updateMs": 500,
  "hx711_gpioDout": 4,
  "hx711_gpioSck": 5,
  "hx711_spsRate": 10,

  "ecDoser_enabled": false,
  "ecDoser_intervalSec": 3600,
  "ecDoser_limitEC": 3.0,
  "ecDoser_valueA": 10.0,
  "ecDoser_valueB": 10.0,

  "dallas_ecSensorIndex": 0
}

Обработка сообщений на сервере

flowchart TD
    MSG[MQTT сообщение] --> CHECK{Суффикс топика}
    CHECK -->|"main"| REDIS[Redis hash-store]
    CHECK -->|"status/mixer"| MIXER[Обновить статус миксера]
    CHECK -->|"status/sensors"| SENS[Обновить кеш датчиков]
    CHECK -->|"status" + "connected"| SYNC[push_box_settings → отправить преференции]
    CHECK -->|"data-timescale/{METRIC}"| PROC[MessageProcessor]

    PROC --> PARSE[Парсинг значения]
    PARSE --> MAP[Маппинг имени поля<br/>wEC→ec, wpH→ph, wLevel→calc_dist...]
    MAP --> BATCH[Batcher<br/>100 метрик / 25 сек]
    BATCH --> TSDB[(TimescaleDB)]
    MAP --> CACHE[Redis live cache<br/>boxconfig-{token}]
    CACHE --> COND{Проверка условий}
    COND -->|Сработало| TG[Telegram уведомление]