cd7497b014
Прошивка исправлена (aa5db288): валидация 0-7 → 1-8. Документация обновлена — убраны предупреждения, добавлены ссылки на фикс.
292 lines
9.6 KiB
Markdown
292 lines
9.6 KiB
Markdown
# 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 0–15) |
|
||
| `{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: 1–8, время: 1–60000 мс |
|
||
| `{UID}/set/pump/{id}/dispense` | `"50.5"` (граммы) | id: 1–8, граммы: 0.1–1000 |
|
||
|
||
> ✅ Pump ID: единый стандарт 1–8 (1-based). Исправлено в прошивке commit aa5db288.
|
||
|
||
### Настройки (`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 уведомление]
|
||
```
|