Skip to content

RPM PI Control

The RPM PI Control module provides closed-loop RPM regulation that maintains exact fan speeds regardless of load changes, backpressure, or voltage variation. While temperature modules set fan speed based on heat, RPM PI control ensures fans actually reach and hold the commanded speed by continuously adjusting the PWM duty cycle.

Use RPM PI control when precise RPM targets matter — server cooling with specific airflow requirements, noise management at exact speed limits, or testing fans at defined operating points. This module can be combined with any temperature module: the temperature module sets the desired speed, and RPM PI ensures the fans actually reach it.

See the modules overview for a comparison of all available modules.

The module accepts these substitution variables in the packages: block:

VariableDefaultDescription
friendly_name"Fancontroller"Device name prefix for all HA entities
kp"50"Proportional gain (UI-scaled, see scaling section)
ki"10"Integral gain (UI-scaled, see scaling section)
update_interval"1"Control loop interval in seconds

Parameters like integral limit, deadband, PWM minimum, and setpoint change threshold are not YAML substitution variables. They are runtime-adjustable through Home Assistant number entities (see below).

These number entities are adjustable at runtime through Home Assistant — no reflashing needed. The first six control the PI algorithm behavior, while the last four set per-fan target speeds.

EntityRangeStepUnitDescription
PI Kp0—10001Proportional gain (UI-scaled)
PI Ki0—1001Integral gain (UI-scaled)
PI Integral Limit0.1—1.00.1Anti-windup clamp for I-term
PWM Minimum0—501%Minimum PWM when target RPM > 0
PI Deadband0—1001RPMError tolerance (no correction inside this range)
PI Setpoint Change Threshold0—100050RPMRPM change that resets I-term
Fan 1 Target RPM0—400010RPMTarget speed for fan 1
Fan 2 Target RPM0—400010RPMTarget speed for fan 2
Fan 3 Target RPM0—400010RPMTarget speed for fan 3
Fan 4 Target RPM0—400010RPMTarget speed for fan 4

Each fan exposes four diagnostic sensors for monitoring the control loop in real time:

EntityUnitDescription
Fan 1—4 ErrorRPMDifference between target and actual RPM
Fan 1—4 P-Term%Proportional contribution to output
Fan 1—4 I-Term%Integral contribution to output
Fan 1—4 PWM Output%Final PWM duty cycle sent to fan
EntityDefaultDescription
PI Control Fan 1OFFEnable/disable PI control for fan 1
PI Control Fan 2OFFEnable/disable PI control for fan 2
PI Control Fan 3OFFEnable/disable PI control for fan 3
PI Control Fan 4OFFEnable/disable PI control for fan 4
EntityDescription
Reset PI IntegratorsResets all four integral terms to zero (useful when tuning)
packages:
rpm_pi_control:
url: https://github.com/zeroflow/wifi-fancontroller
ref: main
files:
- path: modules/rpm_pi_control.yaml
vars:
friendly_name: "My Fan Controller"

Higher gains for fast, precise RPM tracking:

packages:
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: "80"
ki: "15"

After flashing, adjust deadband and PWM minimum via the Home Assistant number entities for fine-tuning.

UI Value (HA slider)Internal ValueCalculation
kp = 500.000550 / 100,000
kp = 1000.001100 / 100,000
ki = 100.000110 / 100,000
ki = 500.000550 / 100,000
ParameterUI RangeInternal Range
Kp0—10000—0.01
Ki0—1000—0.001

Recommended starting values: kp 30—50, ki 5—10 (UI-scaled).

The integral term is clamped to a configurable limit (default 0.5), preventing the I-term from accumulating excessively when the fan cannot reach the target RPM. This avoids large overshoot when conditions change.

A configurable RPM tolerance (default 5 RPM) where no correction is applied. When the actual RPM is within the deadband of the target, the error is treated as zero. This prevents constant small adjustments and reduces oscillation around the setpoint.

When the target RPM changes by more than the configured threshold (default 200 RPM), the I-term is automatically reset to zero. This prevents overshoot that would occur if a large accumulated I-term from the previous setpoint carried over to a very different target speed.

The control loop runs at a configurable interval (default 1 second), which matches the hardware fan speed sensor update rate. Faster intervals provide no benefit since the RPM reading only updates once per second.

Some fans produce unstable tachometer signals that cause the RPM reading to jump around, making the PI controller oscillate. You can smooth these readings by adding a sliding window filter using the !extend pattern:

sensor:
- id: !extend fan1_speed
filters:
- sliding_window_moving_average:
window_size: 3
send_every: 1

Repeat for fan2_speed, fan3_speed, and fan4_speed as needed. A window size of 3 provides good smoothing without adding significant delay.

  1. Start with defaults — kp=50, ki=10 (UI-scaled) work well for most 4-pin PWM fans
  2. Monitor the debug sensors — the 16 diagnostic sensors in Home Assistant show exactly what the control loop is doing (error, P-term, I-term, PWM output per fan)
  3. Adjust kp for response speed — increase kp for faster response to RPM changes, decrease if you see oscillation
  4. Adjust ki for steady-state accuracy — increase ki to eliminate persistent RPM offset, decrease if you see overshoot or slow oscillation
  5. Use the Reset PI Integrators button if the control loop gets stuck or after making large parameter changes
  6. Set a sensible deadband — 5—20 RPM prevents unnecessary micro-adjustments without sacrificing accuracy
  7. Enable one fan at a time when first tuning — this lets you isolate behavior without all four fans interacting