← Selected Work Full-stack IoT · Solo · Embedded

BlindMaster

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
BlindMaster
Hub render coming
Role
Solo · all 3 codebases
Timeline
Ongoing · pre-release
Context
Personal project
Stack
ESP-IDF · Express.js · Flutter

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. Redrawn from the project's architecture diagram.

Highlights

Calibration as a Socket.IO handshake

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.

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

A full write-up with hardware build photos and hub renders is coming. Reach out for details.