PrabalOS
A connected ESP32 device for staying in touch
A physical device for family communication that had to keep working, unattended, from the other side of the world — so everything about it is designed around not being able to touch it again.
Problem
Moving thousands of kilometres away turns a hobby device into infrastructure. It has to stay up without physical access, survive a bad firmware flash, and be usable by people who are not going to debug it.
Every interesting decision in the project follows from that: the device has to be updatable remotely, and the update path has to be able to fail safely.
What I built
The hardware, the firmware, the backend and the control panel.
- ESP32 hardware with display, touch input, audio and physical buttons
- WiFi communication with backend APIs and device synchronisation
- Remote messaging, voice notes and drawings, with multi-sender interactions
- Discord integration
- Secure login with TOTP authentication and recovery codes
- Private blob storage and a remote firmware update flow
- Timezone handling
Designing for no physical access
Remote firmware delivery is the load-bearing feature. A device you cannot reach needs an update path that verifies what it downloaded and can roll back when the new image does not come up — which is the problem the HttpsOta library was extracted to solve.
A message reaching a device on another continent
The interesting part is not the feature. It is that every hop has to survive the device being unreachable, asleep, or on a network nobody controls.
Someone writes, draws or records a voice note
From the web control panel, or via the Discord integration.
Authenticated
TOTP, with recovery codes for the case where the authenticator is lost.
Not authenticated — the API returns 401 rather than a redirectStored
Private blob storage for drawings and voice notes; state in Redis.
The device polls and synchronises
Over WiFi, on its own schedule — the backend never assumes it is reachable.
It lands on the display
With touch, audio and physical buttons to respond.
Updating a device you cannot reach
The load-bearing feature. A failed update on a device thousands of kilometres away is the end of the device, which is why the OTA path was extracted into its own library.
A new firmware image is published
The device checks version, hash and size
Downloads to the inactive OTA slot
SHA-256 verified while downloading.
Hash mismatch or stall — nothing is installedReboots into the new image
Comes up and reports its versionDoes not — automatic rollback to the previous slot
The parts
| Layer | What is there |
|---|---|
| Device | ESP32, display, touch input, audio, physical buttons, WiFi |
| Firmware | Synchronisation, rendering, input handling, OTA client |
| Backend | APIs for messaging, voice notes, drawings and device sync; Redis state; private blob storage |
| Control panel | Web UI for sending, plus firmware delivery and device status |
| Auth | TOTP login, recovery codes, session guard on every write route |
| Integrations | Discord, and timezone handling for a device in another timezone |
Limitations and failure modes
- One deployed device. Nothing here is validated at fleet scale.
Gallery
Not yet captured
- The device
- The control panel
- Drawing and voice note arriving on the device
Attribution
- Built by me
- Hardware, firmware, backend, control panel and deployment.
Related
- HttpsOta
Over-the-air updates for ESP32, over TLS, with rollback