2f783dbc41
3-zone PWM fan controller (intake/central/outtake) with DS18B20 temp sensors, RPM monitoring, ST7789 LCD display, and Home Assistant integration via ESPHome native API. USB-C PD powered. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
4.3 KiB
4.3 KiB
Rack Fan Controller
ESP32-C6 based smart rack/cabinet fan controller with temperature monitoring, automatic PWM fan speed control, and Home Assistant integration via ESPHome.
Features
- 3-zone fan control: Intake, Central, Outtake (2 fans per zone, 6 total)
- Temperature monitoring: 3x DS18B20 sensors (one per zone)
- RPM monitoring: Tachometer reading from one fan per zone
- Auto speed control: Temperature-based PWM ramping with configurable thresholds
- Negative pressure: Outtake fans run slightly faster for dust control
- 1.47" LCD display: Real-time temps, RPMs, and fan duty on Waveshare ST7789
- Home Assistant: Full integration via ESPHome native API
- USB-C powered: 65W PD charger -> ZY12PDN 12V trigger -> fans + ESP32
Hardware
| Component | Qty | Notes |
|---|---|---|
| Waveshare ESP32-C6 LCD 1.47" | 1 | No-touch version, ST7789 172x320 |
| Arctic P12 PWM 120mm | 6 | 4-pin, 2 per zone |
| ZY12PDN USB-C PD trigger | 1 | Set to 12V output |
| LM2596 buck converter | 1 | 12V -> 5V for ESP32 |
| DS18B20 waterproof probe | 3 | 1m cable, 1-Wire bus |
| PWM Y-splitter (4-pin) | 3 | 1-to-2 per zone |
| KF301 screw terminals | 10 | PCB connections |
| 4.7kOhm resistor | 1 | DS18B20 pull-up |
| 5x7cm prototype PCB | 1 | Main board |
GPIO Mapping (Waveshare ESP32-C6 LCD 1.47")
LCD (occupied - do not use)
| Function | GPIO |
|---|---|
| LCD MOSI | 6 |
| LCD CLK | 7 |
| LCD CS | 14 |
| LCD DC | 15 |
| LCD RST | 21 |
| LCD BLK | 22 |
| RGB LED | 8 |
| SD MISO | 5 |
| SD CS | 4 |
Project Pin Assignment
| Function | GPIO | Notes |
|---|---|---|
| PWM Intake | 0 | Zone 1 - via Y-splitter to 2 fans |
| PWM Central | 1 | Zone 2 - via Y-splitter to 2 fans |
| PWM Outtake | 2 | Zone 3 - via Y-splitter to 2 fans |
| Tach Intake | 3 | RPM from 1 fan in zone 1 |
| Tach Central | 10 | RPM from 1 fan in zone 2 |
| Tach Outtake | 11 | RPM from 1 fan in zone 3 |
| DS18B20 bus | 9 | All 3 sensors on single 1-Wire bus |
Wiring Diagram
USB-C PD Charger (65W+)
|
USB-C cable
|
v
ZY12PDN (set to 12V) ----+------ 12V Rail
| |
v +--- Y-splitter --- Fan1 + Fan2 (INTAKE)
LM2596 (out=5V) | |
| +--- Y-splitter --- Fan3 + Fan4 (CENTRAL)
v | |
ESP32-C6 (5V in) +--- Y-splitter --- Fan5 + Fan6 (OUTTAKE)
|
+-- GPIO0 (PWM) ----> Intake splitter PWM pin
+-- GPIO1 (PWM) ----> Central splitter PWM pin
+-- GPIO2 (PWM) ----> Outtake splitter PWM pin
|
+-- GPIO3 (TACH) <--- Fan1 tach (yellow wire)
+-- GPIO10 (TACH) <-- Fan3 tach (yellow wire)
+-- GPIO11 (TACH) <-- Fan5 tach (yellow wire)
|
+-- GPIO9 (1-Wire) --+-- DS18B20 (intake)
+-- DS18B20 (central)
+-- DS18B20 (outtake)
|
4.7k pull-up to 3.3V
4-Pin Fan Connector Pinout
Pin 1 (Black) = GND
Pin 2 (Yellow) = +12V
Pin 3 (Green) = Tach (RPM signal)
Pin 4 (Blue) = PWM control
Temperature Control Logic
| Temp Range | Intake | Central | Outtake | Notes |
|---|---|---|---|---|
| < 28C | 20% | 20% | 25% | Near-silent baseline |
| 28-35C | 20-50% | 20-50% | 25-55% | Gentle ramp |
| 35-42C | 50-80% | 50-80% | 55-85% | Medium cooling |
| 42-50C | 80-100% | 80-100% | 85-100% | Full cooling |
| > 50C | 100% | 100% | 100% | Emergency + HA alert |
Outtake always runs +5% over intake for slight negative pressure (dust control).
Build & Flash
# Install ESPHome
pip install esphome
# Validate config
esphome config esphome/rack-fan-controller.yaml
# Flash (first time via USB)
esphome run esphome/rack-fan-controller.yaml
# Subsequent updates via OTA
esphome run esphome/rack-fan-controller.yaml --device rack-fan-ctrl.local
Home Assistant
After flashing, the device auto-discovers in HA via ESPHome integration. Exposed entities:
sensor.rack_temp_intake/central/outtakesensor.rack_fan_rpm_intake/central/outtakesensor.rack_fan_duty_intake/central/outtakeswitch.rack_fan_auto_mode(enable/disable auto control)number.rack_fan_target_temp(target temperature setpoint)number.rack_fan_min_duty/max_duty(manual override range)
License
MIT