Повностекова IoT-система для віддаленого моніторингу та керування гідропонними баштами. Складається зі спільного бекенду Firebase Realtime Database, прошивки ESP32 на мікроконтролері кожної башти та двох фронтендів над одним бекендом: розгорнутого web-застосунку (React на Firebase Hosting) і застосунку Flutter, який залишено як основу для майбутніх збірок Android/iOS.
Примітка про платформи. Публікація в App Store вимагає платної участі в Apple Developer Program, що суперечить концепції проєкту — нульові затрати на інфраструктуру. Тому операторський інтерфейс реалізовано як web (Firebase Hosting) із входом через Google OAuth, у межах безкоштовних лімітів Firebase. Код Flutter збережено як основу для майбутніх Android/iOS, але зараз не розгортається.
- Моніторинг у реальному часі — покази вологості ґрунту та рівня води в резервуарі надходять кожні 30 секунд
- Авто-режим — насос вмикається/вимикається за налаштовуваними інтервалами часу без ручного втручання
- Ручний режим — увімкнення/вимкнення насоса та задання швидкості на вимогу
- Керування швидкістю через PWM — швидкість 0–100% відображається у 8-бітну шпаруватість PWM (0–255)
- Захисне блокування — насос примусово вимикається, коли резервуар порожніє, захищаючи його від роботи «на суху»
- Стійкість до збоїв зв'язку — прошивка працює на останньому відомому стані під час перебоїв WiFi/Firebase; клієнт показує банер про втрату з'єднання
- Автентифікація через Google OAuth — вхід через Google із авторизацією за списком доступу (allowlist)
- Сповіщення моніторингу в Google Chat — критичні події (низький рівень води, відновлення, пристрій офлайн, зміна режиму) надсилаються власнику через Cloud Function із edge-тригером (без спаму)
Клієнти Бекенд Пристрій
┌──────────────────────┐
│ Web App (React/TS) │──┐
│ Firebase Hosting │ │ ┌──────────────────────────┐ ┌─────────────────────┐
│ Google OAuth │ │ │ Firebase Realtime DB │ │ ESP32 Firmware │
└──────────────────────┘ │ │ │ ──────▶│ Stream callback │
├──────▶ │ farms/farm_id_1/ │ │ Auto mode timer │
┌──────────────────────┐ │ │ towers/tower_1/ │ ◀───── │ Safety interlock │
│ Flutter App │──┘ │ allowlist/{uid} │ │ PWM controller │
│ (майбутні Android/ │ └──────────────────────────┘ │ Sensor reporter │
│ iOS; не деплоїться)│ └─────────────────────┘
└──────────────────────┘
Клієнти ніколи не спілкуються з прошивкою напряму — уся координація проходить через Firebase, який діє як спільна машина станів. Прошивка є авторитетним джерелом для pump_state та показів датчиків; клієнти — авторитетне джерело наміру оператора (pump_mode, pump_switch, pump_speed, interval_on_min, interval_off_min) і ніколи не записують поля, якими володіє прошивка.
- Вхід — через Google OAuth (Firebase Auth).
- Авторизація — за списком доступу (allowlist): користувач може читати/керувати
farms/лише якщо/allowlist/{uid} === true. - Правило застосовується авторитетно в
firebase/database.rules.json; web-клієнт додатково перевіряє членство для дружнього екрана «Немає доступу». Список керується поза застосунком (Terraform/консоль) і недоступний клієнтам для запису. - Прошивка використовує привілейований credential і обходить ці правила — вона не залежить від allowlist.
Деталі — у специфікації .kiro/specs/web-app-google-auth/.
├── firebase/ # Схема БД, правила безпеки (allowlist) та початкові дані
│ ├── seed_data.json # Початкова структура БД для імпорту
│ ├── database.rules.json # Правила безпеки Firebase (гейт через allowlist)
│ └── README.md # Інструкції з імпорту та довідник полів
│
├── firmware/ # Прошивка ESP32 на C++/Arduino (PlatformIO)
│ ├── src/ # Продакшн-код
│ ├── test/ # Юніт- та property-based тести (GoogleTest + rapidcheck)
│ └── platformio.ini # Конфігурація збірки для esp32dev та native
│
├── web/ # Web-застосунок оператора (React + TypeScript + Vite) — розгортається
│ ├── src/ # firebase, auth (Google OAuth + allowlist), data, components
│ └── test/ # Юніт- та property-based тести (Vitest + fast-check)
│
├── mobile/ # Застосунок Flutter — основа для майбутніх Android/iOS (не деплоїться)
│ ├── lib/ # Код застосунку (моделі, репозиторій, провайдери, UI)
│ └── test/ # Юніт-, property- та widget-тести
│
├── functions/ # Cloud Functions (TypeScript): сповіщення моніторингу в Google Chat
│ ├── src/ # тригери RTDB + scheduled, логіка сповіщень, відправка у webhook
│ └── test/ # Юніт- та property-based тести (Vitest + fast-check)
│
├── infra/terraform/ # Інфраструктура як код: провіженінг Firebase/GCP + деплой web
│
├── hardware/ # Схема підключення та список компонентів
│ └── README.md
│
├── firebase.json # Мапінг Hosting → web/dist та Database → правила
└── .firebaserc # Прив'язка проєкту Firebase
| Рівень | Технологія |
|---|---|
| База даних | Firebase Realtime Database |
| Автентифікація | Firebase Auth (Google OAuth) + allowlist |
| Прошивка | C++ / фреймворк Arduino на ESP32 (PlatformIO) |
| Бібліотеки прошивки | Firebase-ESP-Client, ArduinoJson, rapidcheck |
| Web-застосунок | React 18 + TypeScript + Vite, Firebase JS SDK |
| Тестування web | Vitest + fast-check (property-based) + Testing Library |
| Мобільний застосунок | Flutter 3 / Dart, flutter_riverpod |
| Тестування застосунку | flutter_test, mockito |
| Хостинг | Firebase Hosting |
| Сповіщення | Cloud Functions (2nd gen, TypeScript) → Google Chat webhook |
| Інфраструктура | Terraform (провайдери google / google-beta) |
| CI | GitHub Actions |
Пристрій побудований навколо ESP32 DevKit v1; усі компоненти доступні на AliExpress за ~$15–25 USD.
| Компонент | Призначення |
|---|---|
| ESP32 DevKit v1 (38-pin) | Мікроконтролер — WiFi, Firebase, PWM, ADC |
| Капацитивний датчик вологості ґрунту v1.2 | Зчитування вологості на GPIO 34 (ADC) |
| Безконтактний датчик рівня рідини XKC-Y25 | Виявлення рівня води на GPIO 19 |
| MOSFET-модуль IRF520 | Ключ із PWM-керуванням для насоса 12V на GPIO 18 |
| Занурювальний насос 12V DC (3–5W) | Подача води до башти |
| Блок живлення 12V 2A (DC адаптер) | Живлення насоса |
| Понижуючий модуль LM2596 DC-DC | Знижує 12V до 5V для ESP32 |
Повний список компонентів, схему підключення та примітки зі збирання див. у hardware/README.md.
| GPIO | Режим | Підключено до |
|---|---|---|
| GPIO 18 | Вихід PWM | IRF520 MOSFET SIG (керування насосом) |
| GPIO 34 | Вхід ADC | Датчик вологості AOUT |
| GPIO 19 | Цифровий вхід | Датчик рівня води XKC-Y25 OUT |
Рекомендовано — через Terraform (провіженить проєкт, Firebase, RTDB, Google-провайдер, Hosting і виконує деплой):
cd infra/terraform
cp terraform.tfvars.example terraform.tfvars # впишіть значення
terraform init -backend-config="bucket=<your-tf-state-bucket>" -backend-config="prefix=tower"
terraform applyПередумови: білінг-акаунт (Blaze), OAuth client (GCP OAuth consent screen), GCS-бакет для стейту, Firebase CLI. Деталі — у infra/terraform/README.md.
Або вручну: створіть проєкт Firebase, увімкніть Realtime Database та провайдер Google в Authentication, імпортуйте firebase/seed_data.json, засідьте allowlist і розгорніть правила:
npm install -g firebase-tools && firebase login
firebase deploy --only databaseДокладніше — у firebase/README.md.
Передумови: Node.js ≥ 20.
cd web
npm install
cp .env.example .env # заповніть VITE_FB_* (Terraform генерує це автоматично)
npm run dev # локальна розробка
npm run test:run # тести (Vitest + fast-check)
npm run build # продакшн-збірка у web/distРозгортання виконується Terraform (terraform apply) або вручну: firebase deploy --only hosting.
Передумови: PlatformIO (розширення для VS Code або CLI)
- Скопіюйте
firmware/src/config.h.exampleуfirmware/src/config.hта впишіть свої облікові дані:#define SSID "your_wifi_ssid" #define PASSWORD "your_wifi_password" #define FIREBASE_HOST "your-project-id-default-rtdb.firebaseio.com" #define FIREBASE_AUTH "your_database_secret_or_id_token"
- Прошийте ESP32 та спостерігайте за виводом:
pio run --target upload --environment esp32dev pio device monitor
- Тести прошивки на хості (без обладнання):
pio test --environment native
Збережено для майбутніх Android/iOS; наразі не розгортається.
cd mobile
flutter pub get
flutter run
flutter testfarms/
farm_id_1/
towers/
tower_1/
pump_speed: Integer (0–255, шпаруватість PWM — записує клієнт)
pump_mode: String ("auto" | "manual" — записує клієнт)
pump_state: Boolean (фактичний стан on/off — записує прошивка)
pump_switch: Boolean (намір оператора в ручному режимі — записує клієнт)
interval_on_min: Integer (хвилин увімкнено в авто-режимі — записує клієнт)
interval_off_min: Integer (хвилин вимкнено в авто-режимі — записує клієнт)
sensors/
moisture: Integer (сире значення ADC 0–4095 — записує прошивка)
water_level_low: Boolean (true = резервуар порожніє — записує прошивка)
allowlist/
<uid>: true (авторизовані користувачі — керується поза застосунком)
Коли water_level_low дорівнює true, прошивка негайно встановлює PWM у 0 та записує pump_state = false у Firebase — незалежно від pump_mode, pump_switch чи стану авто-таймера. Це запобігає роботі насоса «на суху». Нормальна робота відновлюється автоматично, щойно рівень води буде відновлено.
Cloud Functions (2nd gen, functions/) стежать за RTDB та надсилають сповіщення власнику через incoming webhook Google Chat. Логіка edge-тригерна — повідомлення йде лише на зміні стану, а не щопокази кожні 30 с.
Події:
| Подія | Тригер | Приклад |
|---|---|---|
| Низький рівень води (критично) | water_level_low false → true |
🚨 Насос вимкнено для захисту від роботи «на суху» |
| Рівень води відновлено | water_level_low true → false |
✅ Нормальна робота відновлена |
| Пристрій офлайн | немає записів датчиків > 3 хв (scheduled) | |
| Пристрій знову онлайн | записи датчиків відновились | 🟢 Дані знову надходять |
| Зміна режиму помпи | pump_mode змінився |
ℹ️ Режим: auto → manual |
Налаштування: створіть incoming webhook у просторі Chat власника (Space → Apps & integrations → Webhooks), передайте URL у Terraform-змінну google_chat_webhook_url — вона зберігається в Secret Manager і читається функціями як секрет GOOGLE_CHAT_WEBHOOK_URL. Регіон RTDB-тригерів має збігатися з rtdb_region (за замовч. europe-west1).
Проєкт наскрізно використовує property-based тестування (PBT) для перевірки властивостей коректності на випадково згенерованих вхідних даних (мінімум 100 ітерацій кожна).
Прошивка (native-збірка):
cd firmware && pio test --environment nativeWeb-застосунок (Vitest + fast-check):
cd web && npm run test:run && npm run typecheckCloud Functions (Vitest + fast-check):
cd functions && npm run test:run && npm run typecheckЗастосунок Flutter:
cd mobile && flutter testКлючові властивості коректності, що перевіряються тестами:
- Кругова серіалізація
TowerStateчерез знімок Firebase (прошивка, web, Flutter) - Захисне блокування переважає над будь-яким увімкненням насоса за низького рівня води
- Авто-таймер циклює за коректними інтервалами на основі
millis() - Шпаруватість PWM завжди відповідає стану насоса on/off
- Валідація інтервалів відхиляє нецілі та поза-діапазонні значення
- Видимість панелей режиму є взаємовиключною
- Доступ надається лише за наявності в allowlist; клієнт ніколи не пише поля прошивки
CI (GitHub Actions, .github/workflows/ci.yml) виконує тести й збірку web та terraform fmt/validate.
Проєкт розробляється за специфікаціями (Kiro-стиль), що зберігаються в .kiro/specs/:
hydroponics-farm-management/— базова система (Firebase, прошивка, Flutter)web-app-google-auth/— web-застосунок, Google OAuth та доставка через Terraform
Кожна специфікація містить requirements.md, design.md і tasks.md.
| Інструмент | Призначення |
|---|---|
| Kiro | ШІ-IDE для розробки базової системи за специфікаціями (v1.0.0) |
| Claude Code | ШІ-агент, яким додано web-застосунок, автентифікацію та інфраструктуру Terraform |
| PlatformIO | Збірка прошивки ESP32, залежності та запуск тестів |
| Vite | Збірник web-застосунку |
| Firebase CLI | Розгортання Hosting та правил БД |
| Terraform | Провіженінг інфраструктури Firebase/GCP |
| Flutter SDK | Інструментарій збірки мобільного застосунку |
| Git | Контроль версій |
Базову систему (прошивка, Flutter, Firebase, апаратна документація) створено за допомогою Kiro за робочим процесом специфікацій. Web-рівень, модель доступу через Google OAuth і доставку через Terraform додано пізніше за допомогою Claude Code.
Цей проєкт ліцензовано за MPL-2.0. Ви можете використовувати, змінювати та поширювати цей код, але будь-які зміни до файлів під ліцензією MPL мають випускатися під тією ж ліцензією. Ширші твори можуть поєднувати цей код із кодом під іншими ліцензіями.