Building an Industrial-Grade Smart Home UPS: The 4-Node ESP32 Architecture
Saurab ThakurWelcome to the ultimate deep dive into building a highly resilient, intelligent Uninterruptible Power Supply (UPS) for your homelab and smart home. When you have expensive equipment like a NAS (Network Attached Storage), heavy desktop PCs, and critical networking gear, a standard off-the-shelf UPS often falls short. They beep loudly, lack deep integration with your custom smart home scripts, and usually have sealed, non-expandable lead-acid batteries that die after two years.
I set out to solve this by building the V2.0 Dual-ESP32 UPS System. This isn’t your standard single-microcontroller Arduino project. To guarantee absolute stability, microsecond response times, and prevent cloud connectivity from blocking critical power switching, the system architecture is distributed across four specialized ESP32 nodes.
In this article—the first of a four-part series—we will explore why this distributed architecture was necessary, how the nodes are segregated, and the underlying philosophy of the system.
The Problem with Single-Node Architectures
If you’ve ever built an IoT project using an ESP8266 or ESP32, you probably know the standard architecture: you put everything on one chip. The microcontroller connects to Wi-Fi, runs a web server, polls some sensors, toggles a few relays, and maybe talks to an MQTT broker.
This monolithic approach is fantastic for a smart thermometer or a garage door opener. But for a mission-critical power supervisor, it is a recipe for disaster.
Here is what happens when you run everything on one ESP32:
- Wi-Fi Blocking: The ESP32’s Wi-Fi stack and FreeRTOS tasks can occasionally block execution. If the router reboots during a power outage, the ESP32 might hang for a few seconds trying to renegotiate a DHCP lease. If the batteries cross a critical low-voltage threshold during those exact seconds, your relays won’t switch in time. The NAS crashes.
- TLS/SSL Memory Fragmentation: Talking to modern cloud APIs (like Google Firebase or AWS) requires secure TLS sockets. Opening and maintaining TLS connections uses a massive amount of RAM on a microcontroller. This can lead to heap fragmentation and eventual kernel panics (crashes).
- Loop Delays: Running a slick web UI or processing heavy JSON payloads takes CPU cycles. You don’t want a UI rendering delay to prevent a fast-acting voltage protection circuit from tripping.
The Solution: A Distributed 4-Node Architecture
To solve these blocking and stability issues, I adopted an architecture commonly found in avionics and industrial PLCs: segregation of duties. The V2.0 UPS splits the workload across four distinct ESP32 microcontrollers, each tailored for a highly specific role.
1. The Brawn Node (ESP-WROOM-32)
The Pure Deterministic Workhorse
If the Brain is the CEO, the Brawn is the factory floor manager. The Brawn node is an ESP32-WROOM module that has absolutely no Wi-Fi, no Bluetooth, and no network stack running.
Its entire existence is a tight, deterministic loop that runs hundreds of times a second:
- It reads the Analog-to-Digital Converters (ADCs) connected to the voltage dividers on the 12V LiFePO4 battery packs.
- It polls the PC817 optocouplers to detect the exact moment the 220V AC Grid power drops.
- It triggers the BC547 NPN transistors which drive the physical switching relays (both solid-state and mechanical latching relays).
- It streams this real-time telemetry out via Hardware UART (Serial) to the Brain.
Because it never connects to a network, the Brawn node never hangs. It guarantees that if the power fails, the battery swap relay will engage in under 10 milliseconds, keeping the servers online.
2. The Brain Node (ESP32-S3)
The Central Intelligence and Local Dashboard
The Brain node is powered by a newer, more capable ESP32-S3. It is the maestro of the system. It takes the raw voltage and AC state data streaming in from the Brawn node over the TX/RX serial lines and runs it through a complex state machine.
The Brain asks the critical questions:
- Did the power just flicker, or is it a sustained outage?
- Is the Network Battery pack low enough that I should command the Brawn node to swap to the UPS Battery pack?
- Has the battery dropped so low that I need to send a shutdown API call to the Synology NAS before we lose power entirely?
The Brain also handles the local UI. It hosts an asynchronous web server directly on the local network (accessible via a local IP). This means if your fiber internet goes down, but your Wi-Fi is still up, you still have 100% control over your UPS from your smartphone. It also handles ICMP Pinging to verify if devices on the network are actively responding.
3. The Firebase Sync Node (ESP32)
The Cloud Bridge and Notification Engine
While local control is paramount, remote monitoring is highly desirable. I wanted to see my home’s power status while at work. Integrating the Firebase Realtime Database SDK directly into the Brain node caused the S3 to struggle with SSL handshakes while simultaneously trying to serve a fast local UI and process serial data.
The solution was to add a third ESP32 whose only job is to talk to the internet. This Firebase Sync node sits on the network and locally polls the Brain node’s HTTP JSON API every 5 seconds. It takes that massive payload of system telemetry and pushes it up to Google’s Firebase infrastructure.
If the Firebase node crashes due to a memory leak or a dropped internet connection, the Brain and Brawn nodes remain completely unaffected. Power routing continues perfectly.
4. The Environment Temp Node (ESP32)
Climate Context for the Server Rack
Batteries and servers generate heat, and high temperatures degrade LiFePO4 chemistry. The fourth node in the system is dedicated entirely to environmental monitoring. Using a DHT22 sensor, it calculates temperature, humidity, and heat index. It pushes this data to the local dashboard and integrates seamlessly with Google Home via Sinric Pro, allowing for voice queries (“Hey Google, what is the server rack temperature?”).
Inter-Node Communication: The Hardware Backbone
How do these nodes talk to each other without stepping on each other’s toes?
Brain ↔ Brawn (Hardware Serial UART)
The connection between the Brain and Brawn is the most critical link in the system. We bypass Wi-Fi entirely here. The two ESP32s are physically wired together using their Hardware UART pins (TX2/RX2). Every 500ms, the Brawn constructs a concise JSON string:
{
"ac_state": 1,
"net_v": 13.4,
"ups_v": 13.2,
"relay_1": 0
}
This is blasted over the serial line at 115200 baud. The Brain parses it, updates its internal state machine, and replies with command JSONs if a relay needs to flip.
Brain ↔ Firebase Node (Local HTTP API)
The Firebase node communicates with the Brain over the local Wi-Fi router. It performs standard GET requests to an endpoint hosted by the Brain (e.g., http://192.168.1.50/api/state). This decoupled approach means the Firebase node can restart, update its firmware via OTA, or hang, all without the Brain node caring.
Conclusion
By treating a smart UPS not as a single IoT project, but as a distributed microservice architecture in hardware, we achieve an incredibly high level of fault tolerance. The separation of concerns ensures that the Brawn node always protects the batteries, the Brain always manages the logic, and the Firebase node handles the heavy lifting of the cloud.
In Part 2 of this series, we will dive deeply into the Brawn Node, exploring the physical circuitry, the use of optocouplers for AC detection, and the magic of zero-power latching relays. Stay tuned!


