Smart blinds you control, schedule, and calibrate end-to-end — a Flutter app talks to an Express server over Socket.IO, which relays to ESP32-C6 hubs, with BLE-only provisioning for the first handshake.
A Flutter app talks to an Express server over Socket.IO/HTTPS, which relays commands to ESP32-C6 hubs — also over Socket.IO — with BLE used only for the initial Wi-Fi + auth handshake. Blind position is unified across all three codebases as an 11-step model (0–10, where 5 = fully open).
A multi-stage exchange (calib_start → stage1 → stage2 → calib_done): the device
records "up" then "down" encoder ticks as the user moves the blind, and the tick→position
mapping is saved to NVS — the server is a pure relay.
Wi-Fi creds + JWT go over a custom NimBLE service; once provisioned, BLE goes dormant and all real-time traffic switches to Socket.IO. Trade-off: LE Secure Connections without bonding, assuming physical proximity at setup.
Dynamic frequency scaling (80–160 MHz), light sleep when idle, GPIO-gated servo power, and a MAX17048 fuel gauge for state-of-charge telemetry and a low-battery warning under 20%.
Layered rate limits (HTTP, auth, WS connects/messages, email), JWT + Argon2 auth, and boot-time cleanup of stale connections and expired tokens so restarts don't leave zombie devices.
taskdrivenpowersave branch (deeper power management) will replace main.