← All posts
Why I replaced my NUT servers with an ESP32-S3

Why I replaced my NUT servers with an ESP32-S3

Yes, this is another episode of "overengineering my way into my home lab with a bunch of ESP-IDF firmware I probably should not have written, but it looks super cool."

This one started with a single question that took me years to answer properly: how do I get the energy report out of my APC units, in real time, without a computer babysitting each one?

Years of fighting for one HID report

I started where everyone starts, with PowerChute. It is Windows only. So I went out and bought mini PC boxes, put a copy of Windows on them, and wired them to the UPS units purely to read their data. Visualizing any of it was its own problem: either I remote desktopped into a box to look at a window, or I parsed the numbers out of log files and shipped them to a central Postgres.

Years later I found NUT, and it was a breath of fresh air. Windows was finally gone, the data came out in a manageable shape, and a cheap Raspberry Pi could do the job instead of a mini PC.

It was still a whole computer per UPS. An operating system, an SD card that will eventually die, updates, a power draw, all of it sitting there to parse one HID report. For what the job actually is, it was overkill.

One board, in host mode

The answer was an ESP32-S3 Super Mini running as a USB HID host, plugged straight into the UPS service port. Host mode is where the real work happens, and it is the definitive game changer: the board talks to the UPS the same way the vendor software does, reads the report, and that is the whole dependency list. No operating system in between, no driver stack, no second machine.

It is a very low powered device on the UPS side, it is fast, it is purpose built, and it is small enough to hide anywhere. Including inside the UPS enclosure itself.

The status page shows the parsed metrics next to the raw HID reports coming off the wire, which was invaluable while I was working out the parsing logic.

The bridge status page: parsed UPS metrics, connection state, and the raw HID report log

Once it worked, I went all the way in. The device serves its own web UI for both the live data and its configuration, publishes to MQTT for Home Assistant, raises a SoftAP captive portal for first time setup, and takes OTA updates so I never have to open an enclosure to flash it.

The device config page: identity, WiFi, MQTT broker, publish interval and OTA firmware upload

In Home Assistant the UPS simply appears. Auto discovery over MQTT brings in the full sensor set, battery charge and runtime, input voltage, load, battery type and manufacture date, beeper state, the lot.

The UPS auto-discovered in Home Assistant over MQTT, with its full sensor list

That is PowerChute and the NUT servers gone from all three of my nodes.

Then I wanted all of them on one screen

Once each UPS was publishing on its own, the obvious next step was a single place to see the whole power picture. That is where HMS-NUT came from.

It ingests every node and gives me a live roll-up: per-unit battery and load meters, 24 hour trend sparklines, and a last-seen indicator so a silent device is obvious.

HMS-NUT live status: a fleet roll-up across every node with per-unit battery and load

Devices are managed in the UI and changes apply live, without restarting the collector. Worth saying plainly: it still speaks NUT. My own units are all ESP32-S3 bridges now, but if you have a NUT server you can add it to the same listing and it lands on the same dashboard as everything else.

Device management in HMS-NUT, where each node is registered and can be renamed or disabled

History is the part I use most. Every metric is stored, so I can pull trends across all nodes at once or drill into a single unit, and compare units against each other instead of guessing.

Historical charts across all nodes, for spotting trends and comparing units

There is also a daily summary. Once a day the previous day's aggregates for every device go to an LLM, and it writes up the state of my power infrastructure: voltage stability, load, battery health, anything worth watching. Summaries are kept, so I can scroll back through earlier days, and each one records the model that produced it.

The daily energy summary, written from the previous day's aggregates, with earlier days kept

Both halves are open source

  • hms-esp-apc, the ESP32-S3 USB host firmware that reads the UPS and publishes to MQTT
  • hms-nut, the C++ microservice that aggregates every node, stores the history and writes the daily summary

What is next

The enclosure. There is one wrinkle to design around: the HID port on the APC does not supply power, it expects a 5 V signal coming in, so the ESP32 needs its own source. That means an extra USB-C port on the board for power, and a case that routes both cables cleanly. Then I print it and stick it on the back of the UPS.

Hopefully this saves somebody else the mini PCs.

Want this built for you? Book a consult