Files
ponics-protocol/MQTT.md
T
progl e69665273c update: актуализация по анализу прошивки growbox-espidf
- MQTT.md: реальные имена полей (wEC, wpH, wLevel, Light, mcp/gpio)
- Добавлены топики OTA, status/full, status/actuators
- Полный список preferences с полями прошивки (60+ параметров)
- Актуальная схема status/sensors и status/mixer
- Pump ID расхождение задокументировано (сервер 1-8 vs прошивка 0-7)
- DEVICE_BEHAVIOR.md: FSM, exponential backoff, HA интеграция
2026-05-05 12:01:52 +03:00

292 lines
9.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# MQTT Protocol
## Общее
- **Брокер**: Mosquitto 2.x (порт 1883)
- **Topic prefix**: `{UID}` — UUID токен устройства (из БД `GrowBox.token`)
- **QoS**: 1 для команд, 0 для данных датчиков
- **Subscribe**: сервер подписан на `#` (все топики)
---
## Flow: подключение устройства
```mermaid
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: команда с веб-интерфейса
```mermaid
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`. Поля зависят от подключённых датчиков.
```json
{
"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`.
```json
{
"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)
Устаревший формат. Новые устройства не используют.
```json
{
"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: 17 ⚠️, время: 160000 мс |
| `{UID}/set/pump/{id}/dispense` | `"50.5"` (граммы) | id: 1–7 ⚠️, граммы: 0.11000 |
> ⚠️ **Pump ID расхождение**: сервер (API) принимает id 1–8, прошивка валидирует `pumpId < 0 || pumpId > 7` — id=8 будет отклонён прошивкой. Нужно либо выровнять API на 1–7, либо исправить прошивку на 0–8. Текущее рабочее значение: **1–7**.
### Настройки (`set/preferences/all`)
Полная конфигурация устройства. Все поля опциональны.
```json
{
"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
}
```
---
## Обработка сообщений на сервере
```mermaid
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 уведомление]
```