Smart fan control for your Home Assistant setup
A CE-certified, ESP32-based PWM fan controller designed for home server racks, media cabinets, and smart home projects. Control up to 4 fans with temperature-based automation, remote monitoring, and seamless Home Assistant integration.
Intelligent Climate Control: Automatically adjust fan speeds based on temperature and humidity readings from the integrated HDC1080 sensor. Turn fans off when equipment is idle, ramp up when things heat up.
Home Assistant Native: Built on ESPHome for seamless integration with Home Assistant. Monitor temperatures, adjust fan speeds, and create automations directly from your smart home dashboard.
Hackable & Expandable: Fully customizable ESPHome configuration with Qwiic and I2C expansion ports, NeoPixel output, user buttons, and GPIO breakouts. Add sensors, displays, or integrate with your existing projects.
Professional Quality: CE certified hardware with RGB status LEDs showing system status at a glance. Standard 12V barrel jack power input works with commonly available power supplies.
Ready to upgrade your setup?
Buy on Elecrow - $35.99 - Boards and 3D-printed cases available
Complete Documentation & Web Installer - Flash firmware directly from your browser
DIY Case: Print your own case using the WiFi Fancontroller Case design on Printables.com.
- Power
- 12V DC Barrel Input (5.5x2.1mm)
- Low power operation: 0.25W typical, 0.07W deep sleep
- Fan Control
- 4× PWM Fan Outputs with RPM monitoring
- Supports standard 12V 4-pin PWM fans
- Sensors & I/O
- Integrated HDC1080 Temperature & Humidity Sensor
- RGB Status LEDs for board and fan ports (Rev 3.x)
- Qwiic Expansion Port for easy sensor additions
- I2C Expansion Port (2.54mm Header)
- 5V NeoPixel Output Port
- 3 User Buttons for custom functions
- GPIO Expansion Pads (2.54mm SMD Header)
- Connectivity
- WiFi (ESP32/ESP32-S2)
- USB-C for programming and power (Rev 2.0+)
The power usage of the device is dominated by the fans. As a standalone device, these are the measured power figures in different operation modes.
| Mode | Power Draw |
|---|---|
| Active Wifi Transfer (e.g. OTA Upload) | 0.6W |
| Wifi connected, power save mode deactivated (not default) | 0.52W |
Default Wifi connected, LEDs showing status (one-colored) |
0.25W |
| Wifi connected, LEDs off | 0.2W |
| Deep Sleep | 0.07W |
Disabling Wifi while not in deep sleep does not reduce power consumption, as the default light power save mode already does a good job.
These values have been measured via a DPS3005 power supply, therefore, their accuracy cannot be guaranteed.
All boards come pre-flashed with an ESPHome factory image that's ready to use:
- Power up the board via USB-C or 12V DC (USB-C is for flashing only, power via 12V DC barrel jack is required for fans)
- Connect to the WiFi hotspot (e.g.
fancontroller-r3-1-abcdef) - Configure your WiFi through the captive portal
- Done! Board appears in Home Assistant via ESPHome
What you get out of the box: The factory firmware exposes all 4 fan channels as speed controls in Home Assistant, along with temperature and humidity readings from the onboard sensor. Fan speeds can be adjusted manually from 0–100% or automated via Home Assistant. RPM monitoring is available for each fan port.
For custom firmware or troubleshooting, see Installation Methods below.
To choose the correct firmware, identify your board revision using the connectors:
| Revision | Left | Fan Ports | Right | Details | Notes |
|---|---|---|---|---|---|
| 1.0 | DC 12V | Fans | nothing | Link | |
| 2.0 | DC 12V, Status LED | Fans | USB-C | Link | |
| 3.0 | DC 12V, Status LED, QWIIC | Fans | USB-C | Link | |
| 3.1 | DC 12V, RGB Status LED, QWIIC | Fans, RGB Status LEDs | USB-C | Link | |
| 3.2 | DC 12V, RGB Status LED, QWIIC | Fans, RGB Status LEDs | USB-C | Link | Compatible firmware and pinout — hardware-only revisions |
| 3.3 | DC 12V, RGB Status LED, QWIIC | Fans, RGB Status LEDs | USB-C | Link | Compatible firmware and pinout — hardware-only revisions |
Flash pre-built firmware directly from your browser at fancontroller.arthofer.dev
Note for ESP32-S2 boards (Rev 2.0, 3.x): Web-based flashing may have connectivity issues. The most reliable web tool is ESPWEBTOOL by Spacehuhn. If the browser flash fails, use Method 2 (esptool) instead.
Reliable flashing method, especially for ESP32-S2 boards:
- Install esptool:
pip install esptool - Download prebuilt binary from GitHub Releases
- Connect board via USB-C, hold BOOT button, press RESET
- Flash:
esptool.py --chip esp32s2 write_flash 0x0 firmware.bin - Press RESET to boot
Once connected to WiFi, update wirelessly via ESPHome dashboard or web interface at http://[board-ip]
For users who want to customize their configuration while keeping hardware setup simple. The hardware packages automatically configure all board-specific features for your revision.
This works by adding the following lines to your esphome config:
# Import the hardware package for your board revision
packages:
fancontroller: github://zeroflow/wifi-fancontroller/hardware-rev-3.3.yaml@mainHere's a complete example showing how to create a custom configuration using the hardware package:
# Import the hardware package for your board revision
packages:
fancontroller: github://zeroflow/wifi-fancontroller/hardware-rev-3.3.yaml@main
esphome:
name: my-fancontroller
friendly_name: My Fan Controller
esp32:
board: esp32-s2-saola-1 # Use esp32dev for Rev 1.0
framework:
type: arduino
# Enable logging
logger:
# Enable Home Assistant API
api:
# Enable OTA updates
ota:
- platform: esphome
# Configure your WiFi
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Enable fallback hotspot
ap: {}
captive_portal:- Rev 1.0:
hardware-rev-1.0.yaml(ESP32, esp32dev board) - Rev 2.0:
hardware-rev-2.0.yaml(ESP32-S2, esp32-s2-saola-1 board) - Rev 3.0:
hardware-rev-3.0.yaml(ESP32-S2, esp32-s2-saola-1 board) - Rev 3.1, 3.2, 3.3:
hardware-rev-3.3.yaml(ESP32-S2, esp32-s2-saola-1 board)
The hardware package automatically configures all board features:
- 4× PWM fan outputs with speed control and RPM monitoring
- HDC1080 temperature & humidity sensor
- User buttons (USR1, USR2, USR3) for custom functions
- Status LEDs (RGB on Rev 3.x)
- NeoPixel output port (Rev 3.x)
- WiFi signal strength sensor
- All expansion ports and GPIO
No manual pin configuration needed - just import the package and customize your automations!
Fan Control Modules (advanced — click to expand)
Beyond the basic hardware configuration, this project includes optional control modules that implement different fan control strategies. These modules can be imported into your configuration to add temperature-based fan control or status displays.
Visual feedback module that updates each fan's RGB LED based on its RPM reading. LEDs transition from red (stopped) through orange to green (full speed).
Configuration Variables:
full_rpm: Maximum RPM for color scaling (default: 2500). At 0 RPM = red, at full_rpm = green
Example:
packages:
rpm_status_leds:
url: https://github.com/zeroflow/wifi-fancontroller
ref: main
files:
- path: modules/rpm_status_leds.yaml
vars:
full_rpm: 2500 # RPM value considered as 100% speedSimple linear fan control based on temperature. Creates a temperature curve with three zones:
- Below t_off: Fans off (0%)
- Between t_off and t1: Constant speed at fanpercent1
- Between t1 and t2: Linear ramp from fanpercent1 to fanpercent2
- Above t2: Constant speed at fanpercent2
Configuration Variables:
friendly_name: Device name prefix (default: "fancontroller")t_off: Temperature below which fans turn off (default: 25.0°C)t1: Lower temperature setpoint (default: 30.0°C)t2: Upper temperature setpoint (default: 50.0°C)fanpercent1: Fan speed % at t1 (default: 30.0%)fanpercent2: Fan speed % at t2 and above (default: 100.0%)
Features:
- Individual auto-control switches for each of 4 fans
- Output value sensor showing current calculated fan speed
Example:
packages:
temperature_linear:
url: https://github.com/zeroflow/wifi-fancontroller
files:
- path: modules/temperature_linear.yaml
vars:
friendly_name: "Server Rack"
t_off: "25.0" # Fans off below 25°C
t1: "30.0" # Start ramping at 30°C
t2: "50.0" # Full speed at 50°C
fanpercent1: "30.0" # Minimum speed: 30%
fanpercent2: "100.0" # Maximum speed: 100%Advanced PID (Proportional-Integral-Derivative) control for precise temperature regulation. Automatically adjusts fan speeds to maintain a target temperature with minimal overshoot.
Shoutout to patrickcollins12/esphome-fan-controller for the example code.
A PID simulator is available at https://fancontroller.arthofer.dev/pid-simulator. It uses simplified thermal models — treat results as a starting point for tuning.
Configuration Variables:
friendly_name: Device name prefix (default: "fancontroller")kp: Proportional gain coefficient (default: 0.39509)ki: Integral gain coefficient (default: 0.00470)kd: Derivative gain coefficient (default: 20.74267)max_integral: Maximum integral term value (default: 0.0)output_averaging_samples: Output averaging samples (default: 1)derivative_averaging_samples: Derivative averaging samples (default: 5)
Features:
- Climate entity (thermostat) for Home Assistant integration
- Live PID tuning via Home Assistant number entities
- Auto-tune button for automatic PID parameter calculation
- Deadband control to reduce oscillation near target
- Manual override fan control
- Individual PID control switches for each of 4 fans
- Real-time monitoring of P, I, D terms and error values
Example:
packages:
temperature_pid:
url: https://github.com/zeroflow/wifi-fancontroller
files:
- path: modules/temperature_pid.yaml
vars:
friendly_name: "Server Rack"
kp: "0.39509"
ki: "0.00470"
kd: "20.74267"
Tuning Tips:
- Use the "PID Climate Autotune" button for automatic tuning
- Start with low kp/ki/kd values and increase gradually
- Monitor the P/I/D term sensors to understand controller behavior
PI (Proportional-Integral) controller that regulates PWM output to achieve and maintain precise target RPM for each fan. Unlike percentage-based control, this module uses closed-loop feedback to compensate for fan aging, voltage variations, and load changes.
Important: This module uses the existing fan speed sensors from the hardware package. Since the default hardware sensors update every 10s with heavy filtering (too slow for stable PI control), you must override the sensor definitions in your config to use faster updates (2s recommended) and minimal filtering. See the example configuration below.
Configuration Variables:
friendly_name: Device name prefix (default: "fancontroller")kp: Proportional gain coefficient (default: 0.005)ki: Integral gain coefficient (default: 0.001)update_interval: Control loop update interval in seconds (default: 2, matches fast sensor update)
Features:
- Individual target RPM setpoints for each of 4 fans (0-4000 RPM range)
- Enable/disable PI control per fan with switches
- Live tuning of PI parameters via Home Assistant
- Real-time monitoring of error, P-term, I-term, and PWM output
- Anti-windup protection for integral term
- Configurable minimum PWM to prevent stalling
- Reset button for integral terms
Example:
packages:
hardware:
url: https://github.com/zeroflow/wifi-fancontroller
ref: main
files: [hardware-rev-3.1.yaml]
rpm_pi_control:
url: https://github.com/zeroflow/wifi-fancontroller
ref: main
files:
- path: modules/rpm_pi_control.yaml
vars:
friendly_name: "Server Rack"
kp: "0.005" # Proportional gain
ki: "0.001" # Integral gain
update_interval: "2" # Update every 2 seconds (matches sensor update)
# REQUIRED: Override hardware sensors for faster updates
sensor:
- platform: pulse_counter
id: fan1_speed
pin: {number: GPIO16, mode: INPUT_PULLUP}
update_interval: 2s
filters:
- multiply: 0.5
- sliding_window_moving_average: {window_size: 3, send_every: 1}
# Repeat for fan2_speed, fan3_speed, fan4_speed (see full example)How to Use:
- Enable "PI Control Fan X" switch in Home Assistant
- Set "Fan X Target RPM" to your desired speed (e.g., 1500 RPM)
- The controller automatically adjusts PWM to maintain that speed
- Monitor debug sensors (Error, P-Term, I-Term, PWM Output) to verify operation
- Fine-tune Kp and Ki parameters if needed
Tuning Tips:
- Start with small kp (0.003-0.01) and ki (0.0005-0.002) values
- If fans respond too slowly: Increase Kp
- If fans oscillate around target: Decrease Kp or Ki
- If there's persistent error (actual RPM stays below/above target): Increase Ki
- Use "Reset PI Integrators" button when changing parameters or after long idle periods
- Monitor the debug sensors to understand controller behavior
- Linear Control: Best for simple setups where you want predictable fan behavior based on temperature zones. Easy to understand and configure.
- PID Temperature Control: Best for precise temperature control and systems where you want to maintain a specific target temperature with minimal fluctuation.
- RPM PI Control: Best when you need precise fan speed control in RPM, regardless of temperature. Ideal for applications requiring consistent airflow, compensating for fan aging, or when you want to set exact fan speeds.
- RPM Status LEDs: Can be combined with any control module to add visual feedback.
You can combine multiple modules in your configuration. For example:
- Use PID temperature control with RPM status LEDs for precise temperature regulation with visual feedback
- Use RPM PI control with temperature sensors to create a custom automation that sets target RPM based on temperature thresholds
- Combine linear temperature control with RPM status LEDs for simple automatic control with visual feedback
Over-the-Air Updates: After initial setup, your board will appear in the ESPHome dashboard for wireless updates. No need to connect USB again.
Troubleshooting Discovery Issues: If your board doesn't appear in ESPHome after connecting to WiFi, check for mDNS issues on your network. You can work around this by manually specifying the board's IP address using the manual_ip configuration in your WiFi settings.
Need Help? Open an issue on GitHub or consult the ESPHome documentation.
Found a bug or have a feature request? Open an issue or submit a pull request on GitHub.
Copyright (C) 2026 Arthofer Engineering e.U. Licensed under the GNU General Public License v3.0 — see LICENSE for details.
