Files
Arduino/pump_controller_8_2_OLED_DONE/README.md
T
oskarvitaliiandClaude Opus 5 75430a3732 Initial commit: контроллер насосов v8.2 + документация
Прошивка Wemos D1 Mini для мониторинга трёх насосов: OLED,
веб-панель, webhook-уведомления, сброс Wi-Fi кнопкой.

Адрес webhook задаётся через веб-панель и хранится в EEPROM;
в исходнике не хранится, так как содержит секретный токен.

Добавлены README.md (техдокументация) и MANUAL.md (руководство).

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-07 13:51:22 +07:00

251 lines
11 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.
# Pump Controller v8.2 (OLED)
Контроллер мониторинга насосной станции на **Wemos D1 Mini (ESP8266)**.
Следит за состоянием трёх насосов (два основных + резервный), выдаёт сухие
контакты аварии, показывает статус на OLED-дисплее, публикует веб-панель в
локальной сети и отправляет события на HTTPS-webhook (n8n).
---
## 1. Состав репозитория
| Файл | Назначение |
|---|---|
| `pump_controller_8_2_OLED_DONE.ino` | Весь прошивочный код (single-file Arduino sketch) |
| `README.md` | Техническая документация (этот файл) |
| `MANUAL.md` | Руководство пользователя / монтажника |
---
## 2. Аппаратная часть
### 2.1 Плата
Wemos D1 Mini (ESP8266, 4 МБ Flash). Питание 5 В по microUSB или 3.3 В на пин `3V3`.
### 2.2 Распиновка
| Пин | Имя в коде | Режим | Логика |
|---|---|---|---|
| `D5` | `IN_PUMP_1` | `INPUT_PULLUP` | `LOW` = насос 1 работает |
| `D6` | `IN_PUMP_2` | `INPUT_PULLUP` | `LOW` = насос 2 работает |
| `D7` | `IN_PUMP_EMERGENCY` | `INPUT_PULLUP` | `LOW` = резервный насос работает |
| `D3` | `BTN_WIFI_RESET` | `INPUT_PULLUP` | `LOW` (замыкание на GND) = кнопка нажата |
| `D8` | `OUT_ALARM_MAIN` | `OUTPUT` | `HIGH` = авария основных насосов |
| `D0` | `OUT_ALARM_EMERGENCY` | `OUTPUT` | `HIGH` = работает резерв |
| `D1` | I2C SDA | — | OLED |
| `D2` | I2C SCL | — | OLED |
Замечания по особенностям ESP8266:
* `D3` (GPIO0) и `D8` (GPIO15) участвуют в выборе режима загрузки. `D8` должен
быть подтянут к GND при старте — не вешайте на него нагрузку, тянущую пин вверх.
Кнопку на `D3` при старте держать нажатой нельзя (плата уйдёт в режим прошивки).
* `D0` (GPIO16) не имеет внутреннего pull-up (только pull-down) и не поддерживает
прерывания — используется только как выход.
### 2.3 Дисплей
SSD1306 128×64, I2C, адрес по умолчанию `0x3C` (константа `OLED_ADDR`).
Если экран не найден — в лог уходит сообщение, прошивка продолжает работать
без дисплея (`oledOK = false`).
### 2.4 Подключение датчиков
Датчик — сухой контакт (реле пускателя, датчик потока, контакт КМ).
Замкнут на GND → насос считается работающим. Внешние резисторы не нужны,
используется внутренняя подтяжка.
---
## 3. Зависимости
Устанавливаются через Arduino Library Manager:
* Adafruit SSD1306
* Adafruit GFX Library
* WiFiManager (tzapu)
Входят в ESP8266 core: `ESP8266WiFi`, `ESP8266WebServer`, `ESP8266HTTPClient`,
`WiFiClientSecure`, `EEPROM`, `Wire`.
Board: **LOLIN(WEMOS) D1 R2 & mini**, Upload speed 921600, Flash size 4MB.
---
## 4. Логика работы
### 4.1 Антидребезг
`updatePumpState()` для каждого входа:
```
если чтение изменилось -> сбросить таймер lastDebounceTime
если чтение стабильно > 3000 мс -> зафиксировать stableState
```
`debounceDelay = 3000 мс` — длинный намеренно: фильтрует пусковые дребезги
пускателя и кратковременные просадки.
### 4.2 Готовность системы
`systemReady` становится `true` через `debounceDelay + 500 мс` после старта.
До этого момента аварии не формируются и webhook не отправляется — иначе
при включении питания система рапортовала бы ложную аварию.
### 4.3 Основная авария
Условие: **оба** основных насоса стоят (`!p1 && !p2`).
```
bothStopped -> запуск таймера mainAlarmStartTime
выдержка alarmDelay = 5000 мс
-> OUT_ALARM_MAIN = HIGH
-> webhook {"event":"MAIN PUMPS","status":"ALARM"} (однократно)
любой насос запустился
-> OUT_ALARM_MAIN = LOW
-> webhook {"event":"MAIN PUMPS","status":"OK"} (однократно)
```
Суммарная задержка от факта остановки до аварии: `3 с` (дребезг) + `5 с`
(выдержка) ≈ **8 секунд**.
### 4.4 Резервный насос
Реакция без дополнительной выдержки, сразу после стабилизации входа:
```
pE = работает -> OUT_ALARM_EMERGENCY = HIGH, webhook EMERGENCY PUMP / STARTED
pE = стоит -> OUT_ALARM_EMERGENCY = LOW, webhook EMERGENCY PUMP / STOPPED
```
Флаги `mainAlarmSent` / `emergencyActiveSent` гарантируют отправку ровно
одного webhook на каждый переход состояния.
---
## 5. Webhook
`POST` на `webhook_url`, `Content-Type: application/json`:
```json
{"event":"MAIN PUMPS","status":"ALARM","device":"Wemos_D1_Pump"}
```
| `event` | `status` |
|---|---|
| `MAIN PUMPS` | `ALARM` / `OK` |
| `EMERGENCY PUMP` | `STARTED` / `STOPPED` |
TLS-соединение поднимается через `WiFiClientSecure` с `setInsecure()`
сертификат сервера **не проверяется**. Достаточно для отправки в доверенную
локальную/корпоративную инфраструктуру, но не защищает от MITM.
Отправка синхронная и блокирующая: на время POST (до нескольких секунд при
недоступном сервере) основной цикл приостанавливается. Дисплей в этот момент
не обновляется.
---
## 6. Хранение настроек (EEPROM)
Эмулируемая EEPROM, 256 байт.
| Адрес | Размер | Содержимое |
|---|---|---|
| `0` | 2 | Magic `0xA55A` — признак инициализации |
| `2` | 220 | Webhook URL, `\0`-терминированный |
При первом старте (magic не совпал) записывается значение константы
`webhook_url_default` — она намеренно пустая, чтобы секретный токен не попадал
в исходник и в git. Пока URL не задан через `/set_webhook`, `sendPostWebhook()`
сразу выходит и пишет в лог `[HTTP] Webhook URL не задан`.
Учётные данные Wi-Fi хранит WiFiManager в своей области флеша, не в этой EEPROM.
---
## 7. Веб-интерфейс
Сервер на порту `80`.
| Маршрут | Метод | Описание |
|---|---|---|
| `/` | `GET` | HTML-панель мониторинга |
| `/status` | `GET` | JSON `{"p1":0,"p2":0,"pE":0}` |
| `/reset_wifi` | `POST` | Стереть настройки Wi-Fi и перезагрузиться |
| `/set_webhook` | `POST` | Параметр `url` (≤220 симв.), сохранить в EEPROM |
Панель опрашивает `/status` каждые 2 секунды и перезагружает страницу при
изменении состояния. Шрифты подгружаются с Google Fonts — при отсутствии
интернета у клиента интерфейс отрисуется системным моноширинным шрифтом.
Аутентификации нет: любой в той же сети может сбросить Wi-Fi и подменить
webhook. Контроллер рассчитан на изолированный технологический сегмент.
---
## 8. OLED
Обновление раз в секунду (`DISPLAY_INTERVAL`).
```
PUMP CONTROLLER v8
────────────────────────
PUMP 1 : RUNNING
PUMP 2 : STOPPED
BACKUP : STANDBY
────────────────────────
192.168.1.42
STATUS: OK
```
Строка статуса:
| Условие | Текст |
|---|---|
| `!systemReady` | `INIT...` |
| оба основных стоят | `!! ALARM: NO PUMPS !!` (инверсия, мигание 500 мс) |
| работает резерв | `WARN: BACKUP RUNNING` |
| иначе | `STATUS: OK` |
---
## 9. Кнопка сброса Wi-Fi
Пин `D3`, удержание `BTN_HOLD_MS = 3000 мс`. Во время удержания на дисплее
рисуется прогресс-бар с обратным отсчётом. По достижении порога —
`wm.resetSettings()` и `ESP.restart()`. Отпускание до порога отменяет операцию.
---
## 10. Известные ограничения
* `setInsecure()` — TLS без проверки сертификата.
* Веб-интерфейс и `/reset_wifi` не защищены паролем.
* Webhook отправляется синхронно и блокирует цикл; при недоступном сервере
реакция контроллера замедляется на время таймаута.
* События, произошедшие без Wi-Fi, не буферизуются — webhook просто теряется
(в лог пишется `[HTTP] Нет Wi-Fi`).
* Логика аварии — «оба стоят»: остановка одного насоса штатной ситуацией
не считается и никак не сигнализируется.
* Webhook URL хранится в EEPROM в открытом виде и отдаётся веб-панелью всем,
кто может её открыть. Если URL содержит секретный токен — доступ к панели
равносилен доступу к токену.
---
## 11. Сборка
```bash
arduino-cli compile --fqbn esp8266:esp8266:d1_mini pump_controller_8_2_OLED_DONE.ino
```
```bash
arduino-cli upload -p COM3 --fqbn esp8266:esp8266:d1_mini pump_controller_8_2_OLED_DONE.ino
```
Монитор порта: 115200 бод.