149 lines
6.5 KiB
Markdown
149 lines
6.5 KiB
Markdown
# Device Behavior
|
||
|
||
## Конечный автомат устройства
|
||
|
||
```mermaid
|
||
stateDiagram-v2
|
||
[*] --> Booting: Включение питания
|
||
Booting --> WiFiConnect: Инициализация периферии
|
||
WiFiConnect --> MQTTConnect: WiFi OK
|
||
WiFiConnect --> WiFiConnect: Retry (exponential backoff)
|
||
MQTTConnect --> Connected: MQTT OK
|
||
MQTTConnect --> MQTTConnect: Retry (exponential backoff)
|
||
|
||
Connected --> Publishing: Таймер сбора данных
|
||
Publishing --> Connected: Данные отправлены
|
||
|
||
Connected --> ExecutingCmd: Получена команда MQTT
|
||
ExecutingCmd --> Connected: Команда выполнена
|
||
|
||
Connected --> OTA: Получена cmd/ota
|
||
OTA --> [*]: Перезагрузка после обновления
|
||
|
||
Connected --> Disconnected: Потеря связи
|
||
Disconnected --> MQTTConnect: Reconnect (exponential backoff)
|
||
|
||
Connected --> [*]: cmd/reboot или WDT reset
|
||
```
|
||
|
||
---
|
||
|
||
## Последовательность запуска
|
||
|
||
1. **Boot** — инициализация I2C, SPI, OneWire, MCP23017
|
||
2. **WiFi** — подключение, получение IP (exponential backoff при неудаче)
|
||
3. **MQTT connect** — handshake с брокером
|
||
4. **LWT настройка** — `{UID}/availability = "offline"` при разрыве
|
||
5. **Publish `{UID}/status = "connected"`** — уведомление сервера
|
||
6. **Получить preferences** — сервер отвечает `{UID}/set/preferences/all`
|
||
7. **Применить калибровку** — записать коэффициенты в NVS/RAM без перезагрузки
|
||
8. **Запуск основного цикла** — периодический сбор и публикация данных
|
||
|
||
---
|
||
|
||
## Периодичность публикации данных
|
||
|
||
| Датчик | Интервал |
|
||
|--------|----------|
|
||
| pH, EC, wNTC | ~10 сек |
|
||
| AirTemp, AirHum, AirPress | ~30 сек |
|
||
| RootTemp (DS18B20) | ~15 сек |
|
||
| RSSI, CPUTemp, FreeHeap, Vcc | ~60 сек |
|
||
| uptime | ~60 сек |
|
||
| MixerWeight | ~5 сек (если mixer_enabled) |
|
||
| mcp/gpio | при изменении состояния |
|
||
|
||
---
|
||
|
||
## Обработка команд
|
||
|
||
Все команды **fire-and-forget** — ответ не отправляется.
|
||
|
||
### `cmd/reboot`
|
||
1. Завершить активные операции помп (стоп)
|
||
2. `ESP.restart()`
|
||
|
||
### `cmd/gpio/{pin}`
|
||
- Проверить pin ∈ [0, 15], payload ∈ {"0", "1"}
|
||
- MCP23017 → setPin(pin, state)
|
||
|
||
### `set/pump/{id}/run`
|
||
- Проверить id ∈ [1, 8], time ∈ [1, 60000]
|
||
- Включить помпу → таймер → выключить
|
||
|
||
### `set/pump/{id}/dispense`
|
||
- Проверить id ∈ [1, 8], grams ∈ [0.1, 1000]
|
||
- Рассчитать время через ml/sec калибровку помпы
|
||
- Включить → таймер → выключить
|
||
- Обновить счётчик `total_dispensed`
|
||
|
||
### `cmd/pump/{id}/stop`
|
||
- Проверить id ∈ [1, 8]
|
||
- Немедленно выключить помпу, сбросить таймер
|
||
|
||
### `set/preferences/all`
|
||
- Разобрать JSON, обновить только пришедшие поля в NVS / RAM
|
||
- Применить новые коэффициенты без перезагрузки
|
||
|
||
### `cmd/ota`
|
||
- Payload: пустой (использует `preferences.updateUrl`) или URL строка (override)
|
||
- Скачать и прошить firmware.bin
|
||
- Перезагрузиться
|
||
|
||
### `cmd/ota-storage`
|
||
- Аналогично `cmd/ota` но для LittleFS (`preferences.updateFsUrl`)
|
||
|
||
---
|
||
|
||
## Pump ID: единый стандарт 1–8
|
||
|
||
Сервер и прошивка используют 1-based индексацию (1–8).
|
||
Исправлено в прошивке: commit `aa5db288` — валидация `0-7` → `1-8` в `mqtt_callbacks.cpp`.
|
||
|
||
---
|
||
|
||
## Reconnect логика
|
||
|
||
- WiFi: встроенный авто-реконнект ESP-IDF
|
||
- MQTT: экспоненциальный backoff (1s → 2s → 4s → ... → max 60s)
|
||
- При восстановлении: публикует `{UID}/status = "connected"` → сервер снова шлёт preferences
|
||
|
||
> **Важно**: Публикация `status = "connected"` должна происходить в колбэке подключения MQTT,
|
||
> а не только при первом старте. Иначе после реконнекта устройство не получит настройки с сервера.
|
||
|
||
---
|
||
|
||
## Типичные ошибки реализации
|
||
|
||
### 1. Неверный payload в `{UID}/status`
|
||
```
|
||
❌ "online", "Online", "connected\n", "1"
|
||
✅ "connected"
|
||
```
|
||
Сервер проверяет точное совпадение байт. Неверный payload → `push_box_settings` не вызывается →
|
||
устройство работает без калибровок из БД до следующей перезагрузки.
|
||
|
||
### 2. `status` публикуется только при первом старте
|
||
При реконнекте MQTT (после обрыва WiFi, перезапуска брокера) нужно снова опубликовать
|
||
`{UID}/status = "connected"`. Иначе после восстановления связи настройки не придут.
|
||
|
||
### 3. Pump ID отсчёт
|
||
API использует 1-based индексацию (1–8). Прошивка внутренне хранит 0-based, но
|
||
MQTT-команды и HTTP API принимают 1-based. Проверять валидацию: `id ∈ [1, 8]`.
|
||
|
||
### 4. HTTP API требует `Accept: application/json`
|
||
При обращении к ponics.online API без этого заголовка сервер может вернуть 302 (редирект
|
||
на страницу логина) вместо JSON. Клиенты должны явно указывать `Accept: application/json`.
|
||
|
||
---
|
||
|
||
## Home Assistant интеграция (отдельный канал)
|
||
|
||
Прошивка поддерживает параллельную интеграцию с Home Assistant через MQTT.
|
||
Топики (не используются сервером ponics.online):
|
||
- `{UID}/availability` — online/offline
|
||
- `{UID}/pin/{i}/cmd` — управление GPIO
|
||
- `{UID}/pin/{i}/state` — состояние GPIO
|
||
- `{UID}/mixer/cmd` — управление миксером
|
||
- Autodiscovery топики для HA
|