The Brain Node: Orchestrating the UPS & Network Servers

Saurab ThakurSaurab Thakur
5 min read

In our previous articles, we established that a smart UPS needs to be physically resilient, leaning on the Brawn node to deterministically flip relays and measure voltages without the risk of Wi-Fi timeouts.

But brute force and relays aren’t enough to make a UPS “smart.” A modern homelab isn’t just a router; it’s a collection of expensive Network Attached Storage (NAS) drives, Docker containers, and database servers. If you simply cut power to a hard drive while it’s writing a database transaction, you risk catastrophic data corruption.

This is where the Brain Node (ESP32-S3) steps in. It is the intelligence of the operation. It sits one layer above the Brawn node, acting as the conductor of the orchestra. It evaluates power states, executes complex timeout logics, interfaces with external APIs, and ensures that servers go down gracefully before the batteries run dry.


1. Hardware Overview: Why the ESP32-S3?

For the Brain, I selected the Espressif ESP32-S3. Unlike the older ESP-WROOM-32 used for the Brawn, the S3 features a more powerful dual-core processor, vector instructions (great for fast data parsing), and crucially, more PSRAM (Pseudo-Static RAM).

This extra memory is vital because the Brain node has to juggle multiple heavy tasks simultaneously:

  1. Asynchronous Web Server: Serving the HTML/CSS/JS for the local Astro dashboard.
  2. JSON Parsing: Serializing and deserializing massive telemetry payloads every 500ms.
  3. ICMP Pinging: Continually sending ICMP echo requests (pings) to devices on the network.
  4. API Clients: Firing outbound HTTP requests to the Synology NAS API to trigger shutdowns.

2. UART Telemetry: The Lifeline

The Brain and Brawn nodes communicate over a hardware Serial bus (UART) using pins TX2 and RX2 at 115200 baud.

Every half-second, the Brawn node shouts its current status as a minified JSON string: {"ac":1,"nv":13.42,"uv":12.80,"r1":0,"r2":1}

The Brain reads this incoming buffer asynchronously. We use the incredible ArduinoJson library to parse this string into memory without fragmenting the heap.

Once parsed, the Brain feeds these values into a sophisticated State Machine.

3. The Power State Machine

The logic dictating how a UPS reacts to a power outage must be robust against edge cases. For instance, what happens if the AC power drops for exactly 1 second (a brownout)? We don’t want to trigger a massive server shutdown for a passing anomaly.

The Brain node handles this using debouncing and state timers.

Phase 1: The Brownout Buffer

When the telemetry indicates ac: 0 (AC Grid is offline), the Brain starts a hidden timer. It does absolutely nothing for 10 seconds. If power returns within this window, the timer resets. This prevents the UPS from panicking during minor grid fluctuations.

Phase 2: Sustained Outage

If the timer crosses 10 seconds, the Brain declares a true “Power Outage.” It updates the local UI to display warning banners, changes the physical NeoPixel LED ring from green to pulsating red, and queues an alert to be sent to the user’s phone via the Firebase Sync node.

Phase 3: Battery Monitoring & Swap

As the outage continues, the Brain monitors the nv (Network Voltage) value. Let’s say the threshold is 11.5V. When the network battery drops to 11.5V, the Brain formats a JSON command: {"cmd":"swap_ups"} It shoots this over UART to the Brawn node. The Brawn node instantly triggers the mechanical relay, swapping the power source over to the larger backup UPS battery pack, extending the runtime seamlessly.

4. Graceful Degradation: Managing the Homelab

The most critical feature of the Brain node is its ability to talk to the network.

Using the ESP32Ping library, the Brain constantly pings the IP addresses of the Desktop PC and the NAS. It maintains a boolean state (isNasOnline, isPcOnline).

As the secondary UPS battery pack depletes and approaches a critical cut-off voltage (e.g., 10.8V), the Brain knows the system has maybe 5 minutes of juice left. It enters the Graceful Degradation phase.

Instead of just killing the latching relay and hard-crashing the servers, the Brain initiates an HTTP POST request to the NAS’s REST API. It authenticates with an API token and triggers the system_shutdown endpoint.

The Brain then goes into a waiting loop. It continues to ping the NAS. Ping... Success. Ping... Success. Ping... Timeout.

Once the pings time out, the Brain knows the NAS has completely spun down its hard drives and halted the OS. Only then does it send the final command to the Brawn node: {"cmd":"kill_latch"}. The Brawn node fires the latching relay, cutting all power to the racks to save the batteries from deep discharge.

5. Resurrection: Wake-On-LAN

When AC power finally returns, the Brawn node detects it and passes it to the Brain. The Brain waits for the battery chargers to push the voltages back up to a safe level (e.g., 13.0V).

Once deemed safe, the Brain constructs a “Magic Packet”—a specific UDP broadcast packet containing the MAC addresses of the NAS and PC. It broadcasts this packet across the local subnet. The servers receive the Wake-On-LAN (WOL) packet and automatically power themselves back on.

The entire blackout lifecycle—from detection, to battery swap, to graceful shutdown, to automatic recovery—is handled entirely autonomously by the Brain node without human intervention.


In the next article, we will explore the Firebase Sync Node, detailing why cloud connectivity was segregated and how we push instant notifications to Android devices when the Brain detects an anomaly!

Share this article

You might also like

Table of contents