A dedicated Network UPS Tools (NUT) server running on a Waveshare ESP32-P4-ETH with PoE, communicating with APC Back-UPS devices via USB HID over Ethernet on port 3493.
POE-NUT turns a $30 ESP32-P4 board into a standalone NUT server. It connects to an APC UPS via USB HID, reads real battery/power data, and serves it over Ethernet using the standard NUT protocol. Powered entirely by PoE — one Ethernet cable does everything.
- Real USB HID data from APC Back-UPS devices (not mock/simulated)
- PoE powered — single Ethernet cable for power and network
- Standard NUT protocol — works with upsc, upsmon, PeaNUT, Home Assistant, etc.
- Multi-client support — concurrent TCP connections via task-per-connection model
- DHCP networking — plug and play on any network
- ESP-IDF v5.4.3 on ESP32-P4 (RISC-V, 360MHz)
| Component | Description |
|---|---|
| Waveshare ESP32-P4-ETH | ESP32-P4 dev board with Ethernet PHY (IP101) |
| Waveshare PoE Module (B) | IEEE 802.3af/at PoE power module |
| USB 2.0 female Type-A screw-terminal breakout | For wiring USB signals to GPIO pins |
| APC Back-UPS XS/RS 1500G | APC UPS with RJ50 USB port |
| PoE switch or injector | To provide power over Ethernet |
Connect the USB breakout to ESP32-P4 USB Port 1 pins:
USB Breakout ESP32-P4-ETH
──────────── ────────────
Pin 2 (D-) → GPIO 26
Pin 3 (D+) → GPIO 27
Pin 4 (GND) → GND
The APC UPS connects via its RJ50 USB cable to the screw-terminal breakout. VCC is not needed — the UPS provides its own power. See docs/WIRING.md for details.
Requires ESP-IDF v5.4 or later. See docs/SETUP.md for full setup.
cd esp-idf
source ~/esp/esp-idf/export.sh
idf.py set-target esp32p4
idf.py build
idf.py -p /dev/ttyACM0 flash monitorThe device gets an IP via DHCP. Check serial output for the assigned address.
# List all variables
echo "LIST VAR apc" | nc -w 5 <DEVICE_IP> 3493
# Or use upsc
upsc apc@<DEVICE_IP>| Variable | Description | Example |
|---|---|---|
battery.charge |
Battery charge percentage | 100 |
battery.runtime |
Estimated runtime (seconds) | 1755 |
input.voltage |
Input AC voltage | 121.0 |
input.frequency |
Input frequency (Hz) | 60.0 |
ups.load |
Load percentage | 22 |
ups.status |
UPS status | OL |
ups.model |
UPS model name | Back-UPS RS 1500G |
ups.serial |
UPS serial number | (from device) |
battery.voltage |
Battery voltage | 0.0 * |
output.voltage |
Output voltage | 0.0 * |
ups.firmware |
Firmware version | Unknown * |
* Not available in APC Back-UPS HID descriptor for these collections. See roadmap below.
| Command | Description |
|---|---|
VER |
Server version |
NETVER |
Protocol version |
LIST UPS |
List available UPS devices |
LIST VAR <ups> |
List all variables for a UPS |
GET VAR <ups> <var> |
Get a specific variable |
LOGIN <ups> |
Login to UPS for monitoring |
LOGOUT |
End session |
Add the device in PeaNUT's settings.yml:
- host: <DEVICE_IP>
port: 3493
device: apcAdd to /etc/nut/upsmon.conf:
MONITOR apc@<DEVICE_IP> 1 upsmon pass master
MINSUPPLIES 1
SHUTDOWNCMD "/sbin/shutdown -h +0"Add to configuration.yaml:
sensor:
- platform: nut
host: <DEVICE_IP>
port: 3493
resources:
- battery.charge
- battery.runtime
- ups.load
- ups.statuspoe-nut/
├── esp-idf/ # ESP-IDF project
│ ├── CMakeLists.txt
│ ├── sdkconfig.defaults
│ ├── partitions.csv
│ ├── main/
│ │ ├── main.c # Application entry point
│ │ ├── config.c # Configuration (NVS)
│ │ └── config.h
│ └── components/
│ ├── apc_ups/ # APC UPS USB HID driver + HID parser
│ ├── nut_server/ # NUT protocol TCP server (multi-client)
│ └── ethernet_manager/ # Ethernet PHY (IP101) management
├── docs/
│ ├── SETUP.md # Development environment setup
│ └── WIRING.md # Hardware wiring guide
├── agent-instructions.md # AI assistant development context
└── LICENSE.md # CC BY-NC 4.0
Additional APC UPS values that can be added (present in the HID descriptor):
- Battery voltage (BATTV) — requires collection-aware HID lookup fix
- Transfer voltage thresholds (LOTRANS, HITRANS)
- Sensitivity setting (SENSE)
- Nominal input voltage (NOMINV)
- Self-test status (SELFTEST)
- Nominal power (NOMPOWER)
- Battery date (BATTDATE)
- Last transfer reason (LASTXFER)
- Firmware version — from USB string descriptor
- Time on battery / cumulative (TONBATT, CUMONBATT) — derived, needs NVS
Other planned improvements:
- Authentication support
- NVS-based configuration via serial console
- OTA firmware updates
This server does not implement authentication — it accepts any username/password. Deploy only on trusted networks. Use firewall rules to restrict access to port 3493.
CC BY-NC 4.0 — free for non-commercial use with attribution.
- Network UPS Tools (NUT)
- ESP-IDF
- Waveshare — ESP32-P4-ETH hardware
- PeaNUT — NUT web dashboard