PVE-FANCTRL
ESP32 + 1.9" ST7789 TFT // Standalone Drive Cooling Fan Controller
WHAT IT DOES
Standalone PWM fan controller for drive bay cooling on a TrueNAS / Proxmox box. Drives a single 4-pin PWM fan from a 3.3V GPIO and reads the tach via interrupt. Live RPM and PWM% shown as twin arc gauges on the 1.9" TFT, with a full web UI for remote control. First-boot captive portal handles WiFi setup and theme selection so no credentials are baked into the source. 11 built-in themes from minimal grayscale to full Matrix kanji rain — switchable live from the web UI without a reboot.
WIRING DIAGRAM
SVG schematic hidden on mobile — see wiring tables below
DISPLAY PINOUT (handled by TFT_eSPI User_Setup.h)
| FUNCTION | GPIO | NOTES |
|---|---|---|
| LCD MOSI | GPIO 23 | VSPI MOSI |
| LCD SCLK | GPIO 18 | VSPI clock |
| LCD CS | GPIO 15 | strapping pin (OK) |
| LCD DC | GPIO 2 | strapping pin (OK) |
| LCD RST | GPIO 4 | reset |
| LCD BL | GPIO 32 | active HIGH — TFT_BACKLIGHT_ON HIGH |
Board: ideaspark ESP32 + 1.9" ST7789 170x320 TFT (same as CyberMon)
FAN WIRING (single 4-pin PWM fan)
| FROM | TO | SIGNAL |
|---|---|---|
| PSU +12V (yellow) | Fan pin 2 | +12V supply |
| PSU GND (black) | Fan pin 1 | shared with ESP32 GND |
| ESP32 GPIO 26 | Fan pin 4 | PWM out (3.3V, 25 kHz) |
| Fan pin 3 | ESP32 GPIO 27 | tach in (10 kΩ pull-up to 3.3V) |
| ESP32 GPIO 27 | ESP32 GND (closest pin) | 10 nF tach filter cap — kills PWM crosstalk; must use GND pin closest to GPIO 27 |
| ESP32 GND | PSU GND | common ground — required |
| USB-C (option A) | ESP32 board | default — PC or 5V/1A wall wart |
| Mobo USB hdr pin 1 (option B) | ESP32 VIN | +5V — onboard reg drops to 3.3V |
| Mobo USB hdr pin 7 (option B) | ESP32 GND | D+/D− unused. Pick A or B, not both. |
Tested with TL-9015B 92mm 4-pin PWM (15mm slim), 2700 RPM rated. Also confirmed with Corsair 4-pin PWM fans.
⚠ Tach filter cap is required. The 10 nF cap on GPIO 27 must connect to the GND pin physically closest to GPIO 27 on the dev board. A long GND trace forms an LC resonator with the cap and produces more noise, not less. Acceptable cap values: 10–22 nF. Avoid 100 nF — too much filtering attenuates the real tach signal.
"Off at 0%" is fan-dependent. The TL-9015B holds a hardware floor of ~450 RPM at 0% PWM. Fans with full-stop support (most Corsair, Noctua, etc.) will fully stop. To force a stop on a fan that doesn't, add a MOSFET on the +12 V rail.
FIRST-BOOT WORKFLOW
to the ESP32
"PVE-FANCTRL-Setup"
open WiFi
Enter SSID + password
Pick theme
joins WiFi, shows
twin arc gauges
BUILT-IN THEMES (11)
10 of these are lifted directly from the CyberNetView dashboard's theme system
FACTORY RESET / RE-PORTAL
> Hold BOOT button for 5 seconds while powering on the ESP32
> Serial log prints [portal] wifi credentials wiped
> Captive portal launches on the next boot
> Use this to change WiFi network or pick a different theme via portal
WEB CONTROL
> After WiFi setup, browse to http://<hostname>.local/ (default http://PVE-FANCTRL.local/)
> Control fan PWM with slider + preset buttons (0/25/50/75/100%)
> Live RPM telemetry polls every 2.5s
> Theme dropdown — pick any of 11 themes, applies live to the TFT (no reboot)
> JSON API: GET /api/status, POST /api/set?pct=N, GET /api/theme, POST /api/theme?name=<id>
KEY LIBRARIES
> LVGL 9.4+ (Library Manager — for the UI widgets and CJK font)
> TFT_eSPI (Library Manager — display driver, configured for the Ideaspark pinout)
> Built-in: WiFi, WebServer, ESPmDNS, DNSServer, Preferences
> SquareLine Studio 1.6+ (optional — only if you want to redesign the UI visually)