diff --git a/.gitignore b/.gitignore index 381fa0e..a60ac9a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,4 +7,10 @@ vesper/CLAUDE.md vesper/flutter/ vesper/docs_manual/ Doxyfile -vesper/.claude/ \ No newline at end of file +vesper/.claude/ + +# PlatformIO — build output and downloaded libraries (never commit these) +vesper/.pio/ + +# Claude Code memory/session files +.claude/ \ No newline at end of file diff --git a/vesper/documentation/HEARTBEAT_FEATURE.md b/vesper/documentation/HEARTBEAT_FEATURE.md new file mode 100644 index 0000000..f9abfe6 --- /dev/null +++ b/vesper/documentation/HEARTBEAT_FEATURE.md @@ -0,0 +1,139 @@ +# 💓 MQTT Heartbeat Feature + +## Overview +Implemented a **retained MQTT heartbeat** system that sends periodic status updates every 30 seconds when the controller is connected to MQTT. + +## What It Does + +### Heartbeat Message +Every 30 seconds, the controller publishes a **retained** message to: +``` +vesper/{deviceID}/status/heartbeat +``` + +### Message Format +```json +{ + "status": "INFO", + "type": "heartbeat", + "payload": { + "device_id": "VESPER-ABC123", + "firmware_version": "130", + "timestamp": "Uptime: 5h 23m 45s", + "ip_address": "192.168.1.100", + "gateway": "192.168.1.1", + "uptime_ms": 19425000 + } +} +``` + +### Key Features +✅ **Retained Message** - Only the LAST heartbeat stays on the broker +✅ **Auto-Start** - Begins when MQTT connects +✅ **Auto-Stop** - Stops when MQTT disconnects +✅ **30-Second Interval** - Periodic updates +✅ **First Beat Immediate** - Sends first heartbeat right after connecting +✅ **QoS 1** - Reliable delivery + +## Why This is Awesome + +### For Your Flutter App +1. **Immediate Status** - Any new connection gets the last known status instantly +2. **Stale Detection** - Can detect if controller went offline (timestamp too old) +3. **Device Discovery** - Apps can subscribe to `vesper/+/status/heartbeat` to find all controllers +4. **No Polling** - Just subscribe once and get automatic updates + +### Example App Logic +```dart +// Subscribe to heartbeat +mqtt.subscribe('vesper/DEVICE-123/status/heartbeat'); + +// On message received +if (heartbeat.uptime_ms > lastSeen.uptime_ms + 120000) { + // No heartbeat for 2+ minutes = controller offline + showOfflineWarning(); +} +``` + +## Implementation Details + +### Files Modified +1. **MQTTAsyncClient.hpp** - Added heartbeat timer and methods +2. **MQTTAsyncClient.cpp** - Implemented heartbeat logic +3. **Networking.hpp** - Added `getGateway()` method +4. **Networking.cpp** - Implemented `getGateway()` method + +### New Methods Added +```cpp +void startHeartbeat(); // Start 30s periodic timer +void stopHeartbeat(); // Stop timer +void publishHeartbeat(); // Build and publish message +void heartbeatTimerCallback(); // Timer callback handler +``` + +### Timer Configuration +- **Type**: FreeRTOS Software Timer +- **Mode**: Auto-reload (repeating) +- **Period**: 30,000 ms (30 seconds) +- **Core**: Runs on Core 0 (MQTT task core) + +## Testing + +### How to Test +1. Flash the firmware +2. Subscribe to the heartbeat topic: + ```bash + mosquitto_sub -h YOUR_BROKER -t "vesper/+/status/heartbeat" -v + ``` +3. You should see heartbeats every 30 seconds +4. Disconnect the controller - the last message stays retained +5. Reconnect - you'll immediately see the last retained message, then new ones every 30s + +### Expected Serial Output +``` +💓 Starting MQTT heartbeat (every 30 seconds) +💓 Published heartbeat (retained) - IP: 192.168.1.100, Uptime: 45000ms +💓 Published heartbeat (retained) - IP: 192.168.1.100, Uptime: 75000ms +❤️ Stopped MQTT heartbeat (when MQTT disconnects) +``` + +## Future Enhancements (Optional) + +### Possible Additions: +- Add actual RTC timestamp (instead of just uptime) +- Add WiFi signal strength (RSSI) for WiFi connections +- Add free heap memory +- Add current playback status +- Add bell configuration version/hash + +### Implementation Example: +```cpp +// In publishHeartbeat() +payload["rssi"] = WiFi.RSSI(); // WiFi signal strength +payload["free_heap"] = ESP.getFreeHeap(); +payload["playback_active"] = player.isPlaying; +``` + +## Configuration + +### Current Settings (can be changed in MQTTAsyncClient.hpp): +```cpp +static const unsigned long HEARTBEAT_INTERVAL = 30000; // 30 seconds +``` + +To change interval to 60 seconds: +```cpp +static const unsigned long HEARTBEAT_INTERVAL = 60000; // 60 seconds +``` + +## Notes +- Message is published with **QoS 1** (at least once delivery) +- Message is **retained** (broker keeps last message) +- Timer starts automatically when MQTT connects +- Timer stops automatically when MQTT disconnects +- First heartbeat is sent immediately upon connection (no 30s wait) + +--- +**Feature Implemented**: January 2025 +**Version**: Firmware v130+ +**Status**: ✅ Production Ready diff --git a/vesper/documentation/project-vesper-plan.md b/vesper/documentation/project-vesper-plan.md new file mode 100644 index 0000000..18ecfa2 --- /dev/null +++ b/vesper/documentation/project-vesper-plan.md @@ -0,0 +1,463 @@ +# Project Vesper — Manufacturing Automation Master Plan + +> **How to use this document:** Work through each Phase in order. Each Phase has self-contained tasks you can hand directly to Claude Code. Phases 1–3 are foundational; don't skip ahead. Phases 4–6 build on top of them. + +--- + +## Current Stack (Reference) + +| Layer | Technology | +|---|---| +| Microcontroller | ESP32 (ESP8266 on older models, STM32 possible future) | +| MCU Firmware | Arduino / C++ (moving to PlatformIO) | +| Tablet App | Flutter / FlutterFlow (Android) | +| Phone App | Flutter / FlutterFlow (Android + iOS) | +| Admin Console Backend | Python 3.11+ / FastAPI | +| Admin Console Frontend | React + Tailwind CSS | +| Primary Database | Firebase Firestore (via Admin SDK) | +| Secondary Database | Local SQLite (MQTT logs, etc.) | +| File Storage | Firebase Storage | +| MQTT Broker | Mosquitto on VPS | +| Web Server | NGINX on VPS (OTA files, reverse proxy) | +| Deployment | Gitea → git pull on VPS, Docker Compose locally | + +--- + +## Serial Number Format (Locked In) + +``` +PV-YYMMM-BBTTR-XXXXX + +PV = Project Vesper prefix +YY = 2-digit year (e.g. 26) +MMM = 3-char date block = 2-digit month letter + 2-digit day (e.g. A18 = Jan 18) +BB = Board Type code (e.g. BC = BellCore, BP = BellPRO) +TT = Board Type revision (e.g. 02) +R = Literal "R" +XXXXX = 5-char random suffix (A-Z, 0-9, excluding 0/O/1/I for label clarity) + +Example: PV-26A18-BC02R-X7KQA +``` + +**Month Letter Codes:** +A=Jan, B=Feb, C=Mar, D=Apr, E=May, F=Jun, G=Jul, H=Aug, I=Sep, J=Oct, K=Nov, L=Dec + +--- + +## Phase 1 — PlatformIO Migration (Firmware Side) + +> **Goal:** Replace Arduino IDE with PlatformIO. All future firmware work happens here. This unlocks scripted builds and the ability to produce `.bin` files on demand. +> +> **Tell Claude Code:** *"Help me migrate my existing Arduino IDE ESP32 project to PlatformIO with multiple board environments."* + +### Tasks + +- [ ] Install PlatformIO extension in VS Code +- [ ] Create `platformio.ini` with one `[env]` block per hardware variant. Example: + +```ini +[env:bellcore-v2] +platform = espressif32 +board = esp32dev +board_build.partitions = partitions/custom_4mb.csv +board_build.flash_mode = dio +board_upload.flash_size = 4MB +build_flags = + -DBOARD_TYPE="BC" + -DBOARD_VERSION="02" + -DPSRAM_ENABLED=1 + +[env:bellcore-v1] +platform = espressif32 +board = esp32dev +board_build.partitions = partitions/custom_2mb.csv +board_build.flash_mode = dout +board_upload.flash_size = 2MB +build_flags = + -DBOARD_TYPE="BC" + -DBOARD_VERSION="01" + -DPSRAM_ENABLED=0 +``` + +- [ ] Move all `#include` library dependencies into `lib_deps` in `platformio.ini` (no more manual library manager) +- [ ] Verify `pio run -e bellcore-v2` compiles clean +- [ ] Confirm `.pio/build/bellcore-v2/firmware.bin` is produced +- [ ] Create a `/firmware` directory structure on the server (NGINX already serves this): + +``` +/srv/ota/ + bellcore-v1/ + latest.bin + v1.0.0.bin + v1.0.1.bin + bellcore-v2/ + latest.bin + ... +``` + +- [ ] Update your OTA logic in firmware to pull from `/ota/{board_type}/latest.bin` +- [ ] Add a `scripts/build_and_upload.sh` that compiles + copies the new `.bin` to the right NGINX folder (run this after every release) + +### NVS Partition Generator Setup + +- [ ] Install `esptool` and `esp-idf` NVS tool: `pip install esptool` +- [ ] Grab `nvs_partition_gen.py` from ESP-IDF tools (or install via `idf-component-manager`) +- [ ] Test generating a `.bin` from a CSV manually: + +```csv +key,type,encoding,value +serial_number,data,string,PV-26A18-BC02R-X7KQA +hw_type,data,string,BC +hw_version,data,string,02 +``` + +```bash +python nvs_partition_gen.py generate nvs_data.csv nvs_data.bin 0x6000 +``` + +- [ ] Confirm the ESP32 reads NVS values correctly on boot with this pre-flashed partition +- [ ] Note the NVS partition address for your board (check your partition table CSV — typically `0x9000`) + +--- + +## Phase 2 — MQTT Dynamic Auth (Backend Side) + +> **Goal:** Replace `mosquitto_passwd` manual SSH with automatic credential management. New devices are live on MQTT the moment they exist in your database. Per-device topic isolation enforced automatically. +> +> **Tell Claude Code:** *"Help me set up mosquitto-go-auth on my VPS with a FastAPI backend for dynamic MQTT authentication and ACL enforcement."* + +### Tasks + +#### 2a. Install mosquitto-go-auth on VPS + +- [ ] Install Go on VPS (required to build the plugin) +- [ ] Clone and build `mosquitto-go-auth`: + ```bash + git clone https://github.com/iegomez/mosquitto-go-auth + cd mosquitto-go-auth && make + ``` +- [ ] Update `mosquitto.conf` to load the plugin: + ``` + auth_plugin /path/to/go-auth.so + auth_opt_backends http + auth_opt_http_host localhost + auth_opt_http_port 8000 + auth_opt_http_getuser_uri /mqtt/auth/user + auth_opt_http_aclcheck_uri /mqtt/auth/acl + auth_opt_cache true + auth_opt_cache_host localhost + auth_opt_cache_reset true + auth_opt_auth_cache_seconds 300 + auth_opt_acl_cache_seconds 300 + ``` + +#### 2b. Add MQTT Auth Endpoints to FastAPI + +- [ ] Create `/mqtt/auth/user` endpoint — Mosquitto calls this on CONNECT: + - Receives: `username` (= device SN), `password` + - Checks Firestore/SQLite for device record + hashed password + - Returns: `200` (allow) or `403` (deny) + +- [ ] Create `/mqtt/auth/acl` endpoint — Mosquitto calls this on SUBSCRIBE/PUBLISH: + - Receives: `username`, `topic`, `acc` (1=sub, 2=pub) + - Rule: username must match the SN segment in the topic + - Topic pattern: `/vesper/{SN}/data` or `/vesper/{SN}/control` + - Extract `{SN}` from topic, compare to `username` + - Returns: `200` or `403` + +- [ ] **For user phone app clients:** Add a separate user auth flow + - Users authenticate with their Firebase UID as MQTT username + - ACL check: look up which devices this UID owns in Firestore, permit only those SNs in topics + +#### 2c. MQTT Password Strategy + +Use **HMAC-derived passwords** so you never have to store or manually set them: + +```python +import hmac, hashlib + +MQTT_SECRET = os.getenv("MQTT_SECRET") # Keep in .env, never commit + +def derive_mqtt_password(serial_number: str) -> str: + return hmac.new( + MQTT_SECRET.encode(), + serial_number.encode(), + hashlib.sha256 + ).hexdigest()[:32] +``` + +- Device SN is known → password is deterministic → firmware can compute it at boot +- No password storage needed in DB (just re-derive on auth check) +- Changing `MQTT_SECRET` rotates all passwords at once if ever needed + +- [ ] Add `MQTT_SECRET` to your `.env` and Docker Compose secrets +- [ ] Update firmware to derive its own MQTT password using the same HMAC logic (port to C++) +- [ ] Remove all existing `mosquitto_passwd` file entries and disable static auth + +#### 2d. Test + +- [ ] New device connects with correct SN + derived password → allowed +- [ ] Device tries to sub/pub on another device's topic → denied (403) +- [ ] Wrong password → denied +- [ ] Confirm cache is working (check logs, only 1-2 auth calls per session) + +--- + +## Phase 3 — Serial Number & Batch Management in Admin Console + +> **Goal:** SN generation, DB registration, and MQTT credential provisioning all happen in one flow in the React Console. The Flutter admin app is retired. +> +> **Tell Claude Code:** *"Add a Manufacturing / Batch Management section to our React+FastAPI admin console with the following features..."* + +### Tasks + +#### 3a. Backend — New API Routes in FastAPI + +- [ ] `POST /manufacturing/batch` — Create a new batch: + - Input: `board_type`, `board_version`, `quantity`, `subscription_plan`, `available_outputs` + - Generate N serial numbers using the `PV-YYMMM-BBTTR-XXXXX` format + - Check Firestore for collisions, regenerate if collision found + - Write N device documents to Firestore collection `devices`: + ```json + { + "serial_number": "PV-26A18-BC02R-X7KQA", + "hw_type": "BC", + "hw_version": "02", + "status": "manufactured", + "subscription_plan": "standard", + "available_outputs": 8, + "created_at": "...", + "owner": null, + "users_list": [] + } + ``` + - Returns: list of created SNs + +- [ ] `GET /manufacturing/batch/{batch_id}` — List devices in a batch with status +- [ ] `GET /manufacturing/devices` — List all devices with filters (status, hw_type, date range) +- [ ] `POST /manufacturing/devices/{sn}/assign` — Pre-assign device to a customer email +- [ ] `GET /manufacturing/firmware/{hw_type}/{hw_version}` — Return download URL for the correct `.bin` + +#### 3b. SN Generator Utility (Python) + +```python +# utils/serial_number.py +import random, string +from datetime import datetime + +MONTH_CODES = "ABCDEFGHIJKL" +SAFE_CHARS = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789" # No 0,O,1,I + +def generate_serial(board_type: str, board_version: str) -> str: + now = datetime.utcnow() + year = now.strftime("%y") + month = MONTH_CODES[now.month - 1] + day = now.strftime("%d") + random_suffix = "".join(random.choices(SAFE_CHARS, k=5)) + return f"PV-{year}{month}{day}-{board_type}{board_version}R-{random_suffix}" +``` + +#### 3c. NVS Binary Generation in FastAPI + +- [ ] Copy `nvs_partition_gen.py` into your FastAPI project +- [ ] Add endpoint `GET /manufacturing/devices/{sn}/nvs.bin`: + - Generates a temp CSV for this SN + - Runs `nvs_partition_gen.py` to produce the `.bin` + - Returns the binary file as a download +- [ ] Add endpoint `GET /manufacturing/devices/{sn}/firmware.bin`: + - Looks up device's `hw_type` and `hw_version` from Firestore + - Returns the correct firmware `.bin` from the NGINX folder (or redirects to NGINX URL) + +#### 3d. Label Sheet Generation + +- [ ] Add `POST /manufacturing/batch/{batch_id}/labels` endpoint + - Returns a PDF with one label per device + - Each label contains: SN (human readable), QR code of SN, HW Type, HW Version + - Use `reportlab` or `fpdf2` Python library for PDF generation + - QR code: use `qrcode` Python library + +#### 3e. Frontend — Manufacturing Section in React Console + +- [ ] New route: `/manufacturing` +- [ ] **Batch Creator:** Form with board type selector, quantity, subscription plan → calls `POST /manufacturing/batch` → shows created SNs + download label PDF button +- [ ] **Device List:** Filterable table of all devices with status badges (manufactured / sold / claimed / active) +- [ ] **Device Detail Page:** Shows all fields, allows status update, shows assignment history + +--- + +## Phase 4 — Browser-Based Flashing (The Provisioning Wizard) + +> **Goal:** A single browser tab handles the entire provisioning flow. Plug in ESP32, click through wizard, done. No Arduino IDE, no esptool CLI, no SSH. +> +> **Tell Claude Code:** *"Add a Device Provisioning Wizard to our React admin console using esptool-js and the Web Serial API."* +> +> **Browser requirement:** Chrome or Edge only (Web Serial API). Firefox not supported. This is fine for an internal manufacturing tool. + +### Tasks + +#### 4a. Add esptool-js to React Console + +- [ ] `npm install esptool-js` (or use the CDN build) +- [ ] Confirm Chrome is used on the manufacturing bench laptop + +#### 4b. Provisioning Wizard UI (React Component) + +Build a step-by-step wizard. Steps: + +**Step 1 — Select or Create Device** +- Search existing unprovisioned device by SN, OR +- Quick-create single device (calls `POST /manufacturing/batch` with qty=1) +- Displays SN, HW Type, HW Version + +**Step 2 — Flash Device** +- "Connect Device" button → triggers Web Serial port picker +- Fetches `nvs.bin` and `firmware.bin` from your FastAPI backend for this SN +- Shows two progress bars: NVS partition flash + Firmware flash +- Flash addresses (example for standard ESP32): + - NVS: `0x9000` (verify against your partition table) + - Firmware: `0x10000` +- On completion: updates device status to `flashed` in Firestore via API call + +**Step 3 — Verify** +- Prompt: "Power cycle device and wait for it to connect" +- Poll Firestore (or MQTT) for first heartbeat/connection from this SN +- Show green checkmark when device phone home +- Updates status to `provisioned` + +**Step 4 — Done** +- Show summary +- Option: "Provision next device" (loops back to Step 1 with same batch settings) +- Option: "Print label" (downloads single-device PDF label) + +#### 4c. esptool-js Flash Logic (Skeleton) + +```javascript +import { ESPLoader, Transport } from "esptool-js"; + +async function flashDevice(serialPort, nvsArrayBuffer, firmwareArrayBuffer) { + const transport = new Transport(serialPort); + const loader = new ESPLoader({ transport, baudrate: 460800 }); + + await loader.main_fn(); + await loader.flash_id(); + + await loader.write_flash({ + fileArray: [ + { data: nvsArrayBuffer, address: 0x9000 }, + { data: firmwareArrayBuffer, address: 0x10000 }, + ], + flashSize: "keep", + flashMode: "keep", + flashFreq: "keep", + eraseAll: false, + compress: true, + }); + + await transport.disconnect(); +} +``` + +#### 4d. NGINX CORS Headers + +Add to your NGINX config so the browser can fetch `.bin` files: + +```nginx +location /ota/ { + add_header Access-Control-Allow-Origin "https://your-console-domain.com"; + add_header Access-Control-Allow-Methods "GET"; +} +``` + +--- + +## Phase 5 — Email Notifications + +> **Goal:** Admin Console can send transactional emails (device assignment invites, alerts, etc.) +> +> **Tell Claude Code:** *"Add email sending capability to our FastAPI backend using Resend (or SMTP)."* + +### Tasks + +- [ ] Sign up for [Resend](https://resend.com) (free tier: 3000 emails/month, 100/day) +- [ ] Add `RESEND_API_KEY` to `.env` +- [ ] Install: `pip install resend` +- [ ] Create `utils/email.py`: + +```python +import resend +import os + +resend.api_key = os.getenv("RESEND_API_KEY") + +def send_device_invite(customer_email: str, serial_number: str, customer_name: str = None): + resend.Emails.send({ + "from": "noreply@yourcompany.com", + "to": customer_email, + "subject": "Your Vesper device is ready", + "html": f""" +
Serial Number: {serial_number}
+Open the Vesper app and enter this serial number to get started.
+ """ + }) +``` + +- [ ] Hook into `POST /manufacturing/devices/{sn}/assign` to send invite automatically +- [ ] Add basic email templates for: device assignment, welcome, error alerts + +--- + +## Phase 6 — Polish & Retire Legacy Tools + +> **Goal:** Clean up. Everything lives in the Console. Nothing is done manually. + +### Tasks + +- [ ] **Retire Flutter admin app** — confirm every function it had is now in the React Console +- [ ] **Remove static mosquitto password file** — all auth is dynamic now +- [ ] **Add device status dashboard** to Console home: counts by status, recent provisioning activity +- [ ] **Add audit log** — every manufacturing action (batch created, device flashed, device assigned) logged to SQLite with timestamp and admin user +- [ ] **Document your `platformio.ini` environments** — add a `FIRMWARE_VARIANTS.md` to the firmware repo +- [ ] **Set up Gitea webhook** → on push to `main`, VPS auto-pulls and restarts Docker containers (replaces manual `git pull`) + +--- + +## Gitea / Docker Compose Deployment Note + +Your local Docker Compose setup and VPS production setup are the same codebase — this is correct and will continue to work fine. A few tips: + +- Use a `.env.production` and `.env.development` file, never commit either +- Your `docker-compose.yml` should reference `${ENV_VAR}` from the env file +- The Gitea webhook for auto-deploy is a simple shell script triggered by the webhook: + ```bash + #!/bin/bash + cd /path/to/project + git pull origin main + docker compose up -d --build + ``` +- Protect this webhook endpoint with a secret token + +--- + +## Summary — What Gets Killed + +| Old Way | Replaced By | +|---|---| +| Arduino IDE | PlatformIO (VS Code) | +| Manual `mosquitto_passwd` via SSH | FastAPI dynamic auth endpoints | +| Flutter admin app | React Admin Console | +| Manual SN generation | Console batch creator | +| Manual DB entry per device | Auto-provisioned on batch creation | +| Manual firmware flash + config page | Browser provisioning wizard (esptool-js) | +| Manual NVS entry via HTTP config page | Pre-flashed NVS partition | + +## Estimated Time Per Device (After All Phases Complete) + +| Task | Time | +|---|---| +| Generate 15-device batch + print labels | ~2 min | +| Flash each device (plug in, click Flash, done) | ~3 min each (parallelizable) | +| Devices self-verify on lab WiFi | passive, ~1 min each | +| **Total for 15 devices** | **~20-25 min** | + +vs. current ~20 min per device = ~5 hours for 15. diff --git a/vesper/platformio.ini b/vesper/platformio.ini new file mode 100644 index 0000000..52e2324 --- /dev/null +++ b/vesper/platformio.ini @@ -0,0 +1,121 @@ +; ═══════════════════════════════════════════════════════════════════════════════ +; Project Vesper — PlatformIO Configuration +; ═══════════════════════════════════════════════════════════════════════════════ +; +; Hardware Variants: +; vesper-v1 — Kincony KC868-A6 (ESP32-S3, 4MB flash) — current production board +; +; Future variants (not yet active): +; vesper-plus-v1 — Vesper+ with RF remote support +; vesper-pro-v1 — Vesper Pro with onboard LCD +; +; Build: pio run -e vesper-v1 +; Upload: pio run -e vesper-v1 --target upload +; Monitor: pio device monitor +; Clean: pio run -e vesper-v1 --target clean +; ═══════════════════════════════════════════════════════════════════════════════ + +; ─────────────────────────────────────────────────────────────────────────────── +; SHARED SETTINGS — inherited by all environments +; ─────────────────────────────────────────────────────────────────────────────── +[common] +platform = espressif32 +framework = arduino +monitor_speed = 115200 + +; All external library dependencies +lib_deps = + ; WiFi provisioning portal + tzapu/WiFiManager @ ^2.0.17 + + ; Async web server + WebSocket support + ; NOTE: Use the ESP32-compatible fork, not the original + https://github.com/me-no-dev/ESPAsyncWebServer.git + https://github.com/me-no-dev/AsyncTCP.git + + ; JSON parsing + bblanchon/ArduinoJson @ ^7.0.0 + + ; I2C GPIO expanders (relay control) — PCF8575 header is bundled in same library + adafruit/Adafruit PCF8574 @ ^1.1.0 + + ; Real-time clock + adafruit/RTClib @ ^2.1.4 + + ; Async MQTT client + ; NOTE: Requires AsyncTCP (already listed above) + https://github.com/marvinroger/async-mqtt-client.git + +build_flags_common = + -DCORE_DEBUG_LEVEL=0 + -DCONFIG_ASYNC_TCP_RUNNING_CORE=0 + +; ─────────────────────────────────────────────────────────────────────────────── +; VESPER v1 — Kincony KC868-A6 (ESP32-S3, 4MB Flash) +; Current production board +; ─────────────────────────────────────────────────────────────────────────────── +[env:vesper-v1] +platform = ${common.platform} +framework = ${common.framework} +board = esp32-s3-devkitc-1 + +; Serial monitor +monitor_speed = ${common.monitor_speed} + +; Upload settings +upload_speed = 921600 +upload_protocol = esptool + +; Partition table — default 4MB with OTA support +; Provides: 1.8MB app slot + 1.8MB OTA slot + 64KB NVS + SPIFFS +board_build.partitions = default_8MB.csv + +; Build flags for this variant +build_flags = + ${common.build_flags_common} + -DBOARD_TYPE=\"VS\" + -DBOARD_VERSION=\"01\" + -DBOARD_NAME=\"Vesper\" + -DPSRAM_ENABLED=0 + -DHAS_RF=0 + -DHAS_LCD=0 + +lib_deps = ${common.lib_deps} + + +; ─────────────────────────────────────────────────────────────────────────────── +; VESPER+ v1 — Future: adds RF remote support +; ─────────────────────────────────────────────────────────────────────────────── +; [env:vesper-plus-v1] +; platform = ${common.platform} +; framework = ${common.framework} +; board = esp32-s3-devkitc-1 +; monitor_speed = ${common.monitor_speed} +; build_flags = +; ${common.build_flags_common} +; -DBOARD_TYPE=\"VP\" +; -DBOARD_VERSION=\"01\" +; -DBOARD_NAME=\"Vesper+\" +; -DPSRAM_ENABLED=0 +; -DHAS_RF=1 +; -DHAS_LCD=0 +; lib_deps = ${common.lib_deps} + + +; ─────────────────────────────────────────────────────────────────────────────── +; VESPER PRO v1 — Future: adds onboard LCD +; ─────────────────────────────────────────────────────────────────────────────── +; [env:vesper-pro-v1] +; platform = ${common.platform} +; framework = ${common.framework} +; board = esp32-s3-devkitc-1 +; monitor_speed = ${common.monitor_speed} +; build_flags = +; ${common.build_flags_common} +; -DBOARD_TYPE=\"VX\" +; -DBOARD_VERSION=\"01\" +; -DBOARD_NAME=\"VesperPro\" +; -DPSRAM_ENABLED=0 +; -DHAS_RF=0 +; -DHAS_LCD=1 +; lib_deps = ${common.lib_deps} diff --git a/vesper/src/main.cpp b/vesper/src/main.cpp new file mode 100644 index 0000000..b410a83 --- /dev/null +++ b/vesper/src/main.cpp @@ -0,0 +1,536 @@ +/* + + █████ █████ ██████████ █████████ ███████████ ██████████ ███████████ +▒▒███ ▒▒███ ▒▒███▒▒▒▒▒█ ███▒▒▒▒▒███▒▒███▒▒▒▒▒███▒▒███▒▒▒▒▒█▒▒███▒▒▒▒▒███ + ▒███ ▒███ ▒███ █ ▒ ▒███ ▒▒▒ ▒███ ▒███ ▒███ █ ▒ ▒███ ▒███ + ▒███ ▒███ ▒██████ ▒▒█████████ ▒██████████ ▒██████ ▒██████████ + ▒▒███ ███ ▒███▒▒█ ▒▒▒▒▒▒▒▒███ ▒███▒▒▒▒▒▒ ▒███▒▒█ ▒███▒▒▒▒▒███ + ▒▒▒█████▒ ▒███ ▒ █ ███ ▒███ ▒███ ▒███ ▒ █ ▒███ ▒███ + ▒▒███ ██████████▒▒█████████ █████ ██████████ █████ █████ + ▒▒▒ ▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒▒▒▒▒▒ ▒▒▒▒▒ ▒▒▒▒▒ + + * ═══════════════════════════════════════════════════════════════════════════════════ + * Project VESPER - BELL AUTOMATION SYSTEM - Main Firmware Entry Point + * ═══════════════════════════════════════════════════════════════════════════════════ + * + * 🔔 DESCRIPTION: + * High-precision automated bell control system with multi-protocol communication, + * real-time telemetry, OTA updates, and modular hardware abstraction. + * + * 🏗️ ARCHITECTURE: + * Clean modular design with dependency injection and proper separation of concerns. + * Each major system is encapsulated in its own class with well-defined interfaces. + * + * 🎯 KEY FEATURES: + * ✅ Microsecond-precision bell timing (BellEngine) + * ✅ Multi-hardware support (PCF8574, GPIO, Mock) + * ✅ Dual network connectivity (Ethernet + WiFi + Permanent AP Mode) + * ✅ Multi-protocol communication (MQTT + WebSocket + HTTP REST API) + * ✅ Web settings interface for network mode switching + * ✅ Real-time telemetry and load monitoring + * ✅ Over-the-air firmware updates + * ✅ SD card configuration and file management + * ✅ NTP time synchronization + * ✅ Comprehensive logging system + * + * 📡 COMMUNICATION PROTOCOLS: + * • MQTT (SSL/TLS via AsyncMqttClient on Core 0) + * • WebSocket (Real-time web interface) + * • HTTP REST API (Command execution via HTTP) + * • UDP Discovery (Auto-discovery service) + * • HTTP/HTTPS (OTA updates) + * + * 🔧 HARDWARE ABSTRACTION: + * OutputManager provides clean interface for different relay systems: + * - PCF8574OutputManager: I2C GPIO expander (8 outputs, 6 on Kincony A6 Board) + * - GPIOOutputManager: Direct ESP32 pins (for DIY projects) + * - MockOutputManager: Testing without hardware + * + * ⚡ PERFORMANCE: + * High-priority FreeRTOS tasks ensure microsecond timing precision. + * Core 1 dedicated to BellEngine for maximum performance. + * + * ═══════════════════════════════════════════════════════════════════════════════════ + */ + + + + + /* + * ═══════════════════════════════════════════════════════════════════════════════ + * 📋 VERSION CONFIGURATION + * ═══════════════════════════════════════════════════════════════════════════════ + * 📅 DATE: 2025-10-10 + * 👨💻 AUTHOR: BellSystems bonamin + */ + +#define FW_VERSION "154" + + +/* + * ═══════════════════════════════════════════════════════════════════════════════ + * 📅 VERSION HISTORY: + * NOTE: Versions are now stored as integers (v1.3 = 130) + * ═══════════════════════════════════════════════════════════════════════════════ + * v0.1 (100) - Vesper Launch Beta + * v1.2 (120) - Added Log Level Configuration via App/MQTT + * v1.3 (130) - Added Telemetry Reports to App, Various Playback Fixes + * v137 - Made OTA and MQTT delays Async + * v138 - Removed Ethernet, added default WiFi creds (Mikrotik AP) and fixed various Clock issues + * v140 - Changed FW Updates to Direct-to-Flash and added manual update functionality with version check + * v151 - Fixed Clock Alerts not running properly + * v152 - Fix RTC Time Reports, added sync_time_to_LCD functionality + * v153 - Fix Infinite Loop Bug and Melody Download crashes. + * ═══════════════════════════════════════════════════════════════════════════════ + */ + + + + + + + +// ═══════════════════════════════════════════════════════════════════════════════════ +// SYSTEM LIBRARIES - Core ESP32 and Arduino functionality +// ═══════════════════════════════════════════════════════════════════════════════════ +#include