Loading model…

Blind
Master

full-stack IoT smart blinds · scroll

Click to replay the 3D · minimise ›
0%
press G to assign part groups
click a part…
✕ close
Copied to clipboard. Save this as groups.json in this folder, then reload.
Full-stack IoT · Solo · 3 codebases

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.

ESP32-C6 · ESP-IDFFreeRTOS · NimBLEExpress · Socket.IOFlutterPostgreSQL · MongoDB
Role
Solo · firmware + backend + app
Stack
ESP-IDF · Express/Socket.IO · Flutter
When
Ongoing · pre-release
Status
Functional prototype

Overview

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).

System architecture

BlindMaster system architecture The Flutter app and ESP32-C6 hubs communicate only through the Express server over Socket.IO/HTTP with JWT and TLS. BLE links the phone directly to the device for one-time provisioning. The server persists to PostgreSQL and MongoDB and sends email via Mailgun. BLE provisioning · setup only Socket.IO / HTTP JWT · TLS Socket.IO / HTTP JWT · TLS Flutter App blinds_flutter Express Server blinds_express XIAO ESP32-C6 Blinds_XIAO PostgreSQL users · devices · peripherals groups · tokens MongoDB Agenda jobs · schedules Mailgun verification · reset emails
Server-as-relay topology: the Flutter app and ESP32-C6 hubs talk only through the Express server (Socket.IO/HTTP over TLS); BLE links the phone to the device for one-time provisioning.
  • Server as relay — all app↔device traffic routes through the server after provisioning.
  • Dual-path updates — Socket.IO for real-time, HTTP polling as a fallback.
  • BLE-only provisioning — the phone delivers Wi-Fi creds + initial JWT straight to the ESP32, never via the server.
  • One 11-position model shared across HTTP, Socket.IO, and BLE keeps the firmware tiny and the UI legible.

Highlights

One drivetrain, closed-loop both ways

A continuous-rotation servo turns a gear that tilts the blind; a meshed encoder gear feeds a rotary encoder that counts ticks for absolute position. A second encoder is a manual wand — turn the blind by hand and the servo follows it.

Calibration as a Socket.IO handshake

A multi-stage exchange (calib_start → stage1 → stage2 → calib_done): the device records the "up" then "down" tick extremes as the user moves the blind, and the tick→position mapping is saved to NVS — the server is a pure relay.

BLE for provisioning only

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.

Power management on the ESP32-C6

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%.

Hardened backend

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.

Status

  • So close to having a demo! Functional prototype — provisioning, real-time control, scheduling, multi-device groups, and the full user lifecycle all work.
  • A taskdrivenpowersave branch (deeper power management) will replace main; then it may be released, with the app on TestFlight and the hardware open-sourced.
This is a condensed case study. Want more details? Reach out.