ESP32 Fan RPM / Tachometer with Home Assistant & ESPHome

Quick Summary (TL;DR):
You can monitor the real speed of a 3-wire or 4-wire DC fan with an ESP32 and ESPHome by reading the fan’s tachometer / RPM output. On standard PC-style fans, the tach wire is typically an open-collector output: the fan does not drive a HIGH voltage itself, so the ESP32 should pull the tach line up to 3.3 V using the internal pull-up or, preferably for a robust installation, an external resistor. Do not pull the tach line up to the 12 V fan supply and connect it directly to an ESP32 GPIO. Most 4-wire PWM fans produce two tach pulses per revolution; many 3-wire fans do too, but verify the exact fan datasheet before assuming it. ESPHome’s pulse_meter and pulse_counter sensors both report pulses per minute by default, so for a two-pulse-per-revolution fan RPM = pulses/min ÷ 2, which is simply a multiply: 0.5 filter. For fan RPM, pulse_meter is usually my first choice because it measures the time between pulses and gives smoother/higher-resolution results at low speeds. Set a realistic timeout so a stopped fan reaches 0 RPM promptly instead of waiting for the 5-minute default. Once RPM is in Home Assistant you can detect a stalled fan, verify that PWM speed commands actually change physical speed, alarm if commanded speed is non-zero but tach stays at zero, and track cooling-system health over time. A 2-wire fan has no separate tach wire, so you normally cannot read RPM without an internal FG/test point or an external sensor.

Materials You’ll Need

ItemWhy you need it
ESP32 development boardCounts tach pulses and sends RPM to Home Assistant
3-wire or 4-wire fan with tach outputProvides the RPM pulse signal
Correct fan power supplyUsually 5 V, 12 V or 24 V depending on fan
External pull-up resistorPulls open-collector tach line to safe 3.3 V logic
Jumper wires / terminal blockReliable signal and ground connection
MultimeterChecks fan supply, tach idle voltage and common ground
ESPHomepulse_meter / pulse_counter, filtering and stall logic
Home AssistantRPM graphs, alarms and cooling automations
Optional MOSFET / PWM interfaceIf you also want fan speed control

Fan Wire Types: 2-Wire, 3-Wire and 4-Wire

Fan typeTypical wiresRPM feedback?
2-wirePower + GroundNo separate tach output
3-wirePower + Ground + TachYes
4-wire PWMPower + Ground + Tach + PWM controlYes

The RPM guide applies directly to 3-wire and 4-wire fans. A 2-wire fan may still contain an internal Hall/commutation signal, but it is not normally available outside the fan.

Typical 4-Wire PC Fan Pin Functions

SignalPurpose
GNDFan and controller reference
+12 VFan motor power on standard PC fans
TACH / SENSEOpen-collector speed feedback
PWMSpeed-control command input

Wire colours vary between manufacturers, especially outside PC fans. Verify the fan pinout before connecting anything.

The Tachometer Signal Is Not an Analog Voltage

The tach wire produces a digital pulse waveform whose frequency is proportional to shaft speed.

Fan rotating

TACH  ────┐    ┌────┐    ┌────┐
          └────┘    └────┘    └────
           pulse    pulse

You count these transitions. You do not read the tach wire with the ESP32 ADC.

Open-Collector Output Explained

A typical PC-fan tach output uses an open-collector transistor inside the fan. The fan can pull the tach line LOW, but it does not actively drive it HIGH.

3.3 V
  │
 pull-up resistor
  │
  ├──────── ESP32 GPIO
  │
TACH wire
  │
internal fan transistor
  │
 GND

When the internal transistor is OFF, the pull-up raises the GPIO to 3.3 V. When it turns ON, the line is pulled toward ground.

Why This Is Perfect for ESP32

Because the tach transistor does not force a HIGH voltage, you can choose a pull-up voltage that is safe for the receiving controller. For ESP32, that means pulling the tach line up to 3.3 V.

Do Not Pull Tach to 12 V

WRONG:
12V ─ pull-up ─ TACH ─ ESP32 GPIO

RIGHT:
3.3V ─ pull-up ─ TACH ─ ESP32 GPIO

The fan motor can still run from 12 V. The tach logic pull-up is a separate low-voltage logic domain.

Common Ground Is Required

The fan power supply ground and ESP32 ground must share a reference unless you deliberately use galvanic isolation.

12V PSU + ───── Fan +12V
12V PSU GND ─── Fan GND
       │
       └──────── ESP32 GND

ESP32 3.3V ─ pull-up ─ Fan TACH ─ ESP32 GPIO

Without common ground, the ESP32 cannot reliably interpret the tach LOW level.

What Pull-Up Resistor Should You Use?

For a normal ESP32 tach input, a value in the 4.7 kΩ to 10 kΩ range is a sensible starting point. Many fans also work with the ESP32 internal pull-up, but an external resistor gives a stronger and more predictable logic HIGH, especially with longer wiring.

Some manufacturer guidance uses lower resistor values for specific fan/tach electrical limits. Always verify the exact fan if you are designing a commercial product.

Can You Use the ESP32 Internal Pull-Up?

Often yes, because the tach output is open collector. For a short bench connection, enabling pullup: true is a convenient first test.

pin:
  number: GPIO25
  mode:
    input: true
    pullup: true

For permanent wiring, I prefer an external pull-up because internal pull-ups are relatively weak and less tightly specified.

How Many Pulses per Revolution?

Standard 4-wire PC fans are specified to generate two tach pulses per revolution. Many 3-wire PC fans use the same convention.

2 pulses = 1 revolution

1000 revolutions/min
→ 2000 tach pulses/min

Do not assume every industrial blower or non-PC fan uses two pulses per revolution. Check the datasheet for PPR, pulses/rev, tach cycles/rev or FG pulse specification.

RPM Formula

RPM = pulses per minute ÷ pulses per revolution

For a two-PPR fan:

RPM = pulses/min ÷ 2
RPM = pulses/min × 0.5

Frequency Formula

If you are working in pulses per second (Hz):

RPM = frequency(Hz) × 60 ÷ PPR

For 2 PPR:
RPM = Hz × 30
Tach frequency2-PPR fan RPM
20 Hz600 RPM
40 Hz1200 RPM
60 Hz1800 RPM
100 Hz3000 RPM

ESPHome pulse_meter vs pulse_counter

Featurepulse_meterpulse_counter
Measurement methodMeasures time between pulsesCounts pulses during an interval
Low RPM resolutionBetterMore quantised at short intervals
Response to each pulseYesReports on update interval
ESP32 hardware PCNTNo same fixed-window modelUses hardware counter by default
Best for fan RPMUsually my choiceExcellent for stable/fast pulse counting

Current ESPHome explicitly describes pulse_meter as a drop-in alternative that provides higher resolution at low pulse rates because it measures pulse intervals rather than only counting how many occurred during a fixed time window.

Recommended ESPHome RPM Sensor: pulse_meter

sensor:
  - platform: pulse_meter
    pin:
      number: GPIO25
      mode:
        input: true
        pullup: true
    name: "Fan RPM"
    id: fan_rpm
    unit_of_measurement: "RPM"
    accuracy_decimals: 0
    timeout: 5s
    filters:
      - multiply: 0.5

ESPHome reports pulse_meter in pulses/min by default. For a 2-PPR fan, multiplying by 0.5 produces RPM.

Why I Set timeout: 5s

Current ESPHome’s pulse_meter default timeout is 5 minutes. That is appropriate for some slow utility meters but far too long for fan-stall detection.

Fan stops now
Default pulse_meter timeout
→ could remain non-zero for minutes

timeout: 5s
→ practical zero-RPM detection

For a high-speed fan, even 2–3 seconds may be reasonable. For an extremely slow fan, make sure your timeout remains longer than the normal interval between valid tach pulses.

Example: 300 RPM Minimum Fan Speed

300 RPM ÷ 60 = 5 rev/s
5 rev/s × 2 pulses/rev = 10 pulses/s
→ one pulse every 0.1 s

A 2–5 second timeout gives enormous margin at that speed.

Using pulse_counter Instead

sensor:
  - platform: pulse_counter
    pin:
      number: GPIO25
      mode:
        input: true
        pullup: true
    name: "Fan RPM"
    unit_of_measurement: "RPM"
    accuracy_decimals: 0
    update_interval: 5s
    filters:
      - multiply: 0.5

pulse_counter also reports pulses/min by default. The same 0.5 filter is therefore correct for a 2-PPR fan.

Why pulse_counter Can Be Less Smooth at Low Speed

A fixed-window counter can only count whole pulses. At lower RPM and short reporting intervals, a difference of one pulse creates a visible step in calculated RPM.

pulse_meter measures the interval between pulses, so it usually produces smoother low-speed RPM.

Do Not Put pulse_meter and pulse_counter on the Same GPIO

Current ESPHome documentation explicitly warns that both sensor types cannot be used on the same pin at the same time. Choose one.

Internal Pulse Filter

ESPHome provides an internal_filter to reject very short noise pulses. Both pulse-meter and pulse-counter components default around 13 µs.

For normal PC-fan tach signals, start with the default. Increase filtering only if scope/log evidence shows narrow false pulses and you know the real tach pulse width remains comfortably longer.

Why Excessive Filtering Can Break RPM

Real tach pulse width < filter time
→ real pulse discarded
→ RPM reads too low

Do not copy a 20 ms debounce value from a slow electricity meter into a 3000 RPM fan tach configuration.

pulse_meter EDGE vs PULSE Filter Mode

Current pulse_meter supports EDGE and PULSE filtering modes.

  • EDGE compares time between rising edges and rejects edges arriving too quickly.
  • PULSE requires a pulse to remain valid for the filter duration and is useful for noisier inputs with bouncing edges.

For a clean fan tach output, the default EDGE mode is normally appropriate.

Use an External Pull-Up for Long Tach Wiring

If the fan is several metres from the ESP32 or runs alongside motor/power wiring, use an external pull-up near the ESP32 rather than relying on the internal pull-up.

Twisting tach with ground and keeping the signal away from high-current PWM/motor conductors can also reduce noise.

Optional Capacitor for Noisy Tach Signals

Some fan manufacturers recommend adding a small filtering capacitor between tach and ground for noisy microcontroller installations. Do this only when you understand the resulting RC time constant and the maximum tach frequency; an oversized capacitor can round the pulses until the ESP32 misses edges.

Maximum Tach Frequency Is Usually Modest

Even a very fast 6000 RPM fan at 2 PPR only produces:

6000 RPM ÷ 60 = 100 rev/s
100 × 2 = 200 pulses/s
→ 200 Hz

That is trivial for ESP32 pulse counting. Most tachometer problems are electrical/wiring problems, not CPU-speed problems.

3-Wire Fan Wiring Example

12V PSU + ───────── fan power
12V PSU GND ─────── fan ground ───── ESP32 GND

ESP32 3.3V ─ 4.7kΩ ─┐
                     ├──── fan tach
ESP32 GPIO25 ────────┘

The fan spins from its normal supply. The ESP32 only observes the tach line.

4-Wire PWM Fan Wiring Example

Fan pin 1 GND  ───── common GND
Fan pin 2 +12V ───── 12V PSU
Fan pin 3 TACH ───── GPIO25 + 3.3V pull-up
Fan pin 4 PWM  ───── suitable open-drain / transistor PWM control stage

The PWM control line is separate from the tach feedback line. Do not confuse “PWM frequency” with tach pulse frequency.

Why 4-Wire Fans Should Keep Constant 12 V Power

A standardized 4-wire fan is designed to receive constant motor supply while the fourth wire carries the speed command. This lets the fan’s internal electronics maintain clean commutation and tach output at reduced speed.

If you repeatedly chop the 12 V power instead, the tach signal and fan electronics may behave differently from the intended PWM-control specification.

Monitoring a Fan You Already Control with ESPHome

RPM feedback becomes most valuable when you also control fan speed. Home Assistant can compare requested speed with actual mechanical speed.

Command = 70%
Expected physical state = fan spinning
Actual RPM = 0
→ stall / disconnected fan / failed supply

Simple Stall Binary Sensor

binary_sensor:
  - platform: template
    name: "Fan Stalled"
    id: fan_stalled
    device_class: problem
    lambda: |-
      return id(fan_command).state > 0.20 && id(fan_rpm).state < 100;
    filters:
      - delayed_on: 5s
      - delayed_off: 2s

In this example, fan_command represents the commanded 0.0–1.0 output level. Adjust the threshold to match your configuration.

Why the Stall Delay Matters

A fan takes time to start, especially from a low PWM duty cycle. Without a delayed-on, Home Assistant may briefly report “stalled” every time the fan starts.

Command rises
→ RPM still zero for 0.5–2s
→ normal startup

RPM zero after 5–10s
→ meaningful failure

Home Assistant Stall Alert

alias: Cooling fan stall warning
triggers:
  - trigger: state
    entity_id: binary_sensor.fan_stalled
    to: "on"
    for: "00:00:10"

actions:
  - action: notify.mobile_app_phone
    data:
      title: "Cooling Fan Fault"
      message: "Fan is commanded on but tachometer reports no RPM."

RPM Too Low Warning

A fan can still rotate but be failing mechanically. Bearings may drag, blades may be obstructed, or the supply may be low.

Expected at 100% ≈ 1800 RPM
Actual = 850 RPM
→ not technically stalled
→ still abnormal

Use a minimum-RPM alarm when the system depends on airflow.

Home Assistant Low-RPM Binary Sensor

template:
  - binary_sensor:
      - name: "Cooling Fan RPM Low"
        device_class: problem
        state: >
          {{ states('sensor.cooling_fan_rpm')|float(0) < 1000
             and is_state('fan.cooling_fan', 'on') }}
        delay_on: "00:00:15"

Compare RPM to Requested Speed

Fan speed is not perfectly linear with PWM duty cycle, but a baseline relationship is useful for diagnostics.

PWM commandExample healthy RPM
20%450 RPM
40%800 RPM
60%1150 RPM
80%1500 RPM
100%1800 RPM

Measure your own fan. Do not use this example table as a specification.

Build a Fan Calibration Curve

  • Set fan to 20%.
  • Wait for speed to stabilise.
  • Record RPM.
  • Repeat at 30/40/50…100%.
  • Note minimum duty cycle where the fan reliably starts.
  • Note minimum duty cycle where it keeps spinning after startup.

The start duty and run duty can be different. A fan may need 35% to start but continue spinning at 20%.

Home Assistant RPM Graphs Are Excellent for Diagnostics

A long-term RPM graph can reveal gradual fan degradation that a simple stall alarm misses.

  • RPM falling at same PWM duty
  • intermittent zero-RPM spikes
  • increasing start time
  • speed oscillation
  • thermal-control instability

This is particularly useful for server cabinets, amplifiers, inverter cooling, electronics enclosures and ventilation systems.

Add Temperature to the Same Cooling Node

A useful ESP32 cooling node can measure both temperature and fan RPM.

Temperature sensor
      ↓
ESP32 control logic
      ↓ PWM
Fan
      ↓ tach
ESP32 RPM feedback
      ↓
Home Assistant

This creates a closed monitoring loop: Home Assistant knows the thermal condition, command and actual fan response.

Closed-Loop vs Open-Loop Fan Control

ControlMeaning
Open loopSet 50% PWM and assume fan behaves normally
Closed monitored loopSet PWM and verify actual RPM
Closed-loop RPM controlController actively adjusts PWM to maintain target RPM

The guide focuses on monitoring, but the same tach signal can be used for true RPM regulation if you build appropriate control logic.

Can ESPHome Control to a Target RPM?

Yes, with template/PID-style logic or a dedicated fan controller, but target-RPM regulation needs careful tuning and is more complex than simply exposing speed percentage.

For many Home Assistant projects, temperature-based PWM plus RPM fault monitoring is simpler and more robust.

Dedicated Fan Controller: EMC2101

Current ESPHome also supports the EMC2101 I²C fan controller, which combines fan drive capability, temperature measurement and a tachometer speed sensor.

Use a dedicated controller when you want a purpose-built fan-control IC rather than constructing PWM/tach logic directly around ESP32 GPIOs.

When EMC2101 Is Better

  • PC-style fan controller PCB
  • temperature-controlled enclosure
  • clean integrated tach/RPM measurement
  • DAC/PWM control required
  • you want less low-level GPIO timing/wiring work

When Direct ESP32 Tach Is Better

  • you only need RPM monitoring
  • fan speed is controlled elsewhere
  • one or two fans
  • simple ESPHome node
  • lowest component count

Multiple Fans on One ESP32

Each tach output needs its own GPIO. Do not tie several open-collector tach wires together if you need individual RPM values.

Fan 1 tach → GPIO25
Fan 2 tach → GPIO26
Fan 3 tach → GPIO27

Current ESPHome pulse_counter can use the ESP32 hardware pulse-counter peripheral, but there are finite hardware channels. For ordinary Home Assistant systems with a few fans, that is not a practical limitation.

Example: Three Fans with pulse_meter

sensor:
  - platform: pulse_meter
    pin:
      number: GPIO25
      mode:
        input: true
        pullup: true
    name: "Intake Fan RPM"
    timeout: 5s
    unit_of_measurement: "RPM"
    accuracy_decimals: 0
    filters:
      - multiply: 0.5

  - platform: pulse_meter
    pin:
      number: GPIO26
      mode:
        input: true
        pullup: true
    name: "Exhaust Fan RPM"
    timeout: 5s
    unit_of_measurement: "RPM"
    accuracy_decimals: 0
    filters:
      - multiply: 0.5

  - platform: pulse_meter
    pin:
      number: GPIO27
      mode:
        input: true
        pullup: true
    name: "Cabinet Fan RPM"
    timeout: 5s
    unit_of_measurement: "RPM"
    accuracy_decimals: 0
    filters:
      - multiply: 0.5

Fan Stopped: What Does the Tach Line Do?

With an open-collector tach output, a stopped rotor can leave the tach transistor either ON or OFF depending on rotor position. You should not infer “stopped” from whether the logic level is HIGH or LOW.

Instead, infer stopped from the absence of pulse transitions. That is why the pulse-meter timeout is important.

Why a Simple GPIO Binary Sensor Is Not Enough

A binary sensor only tells you the instantaneous tach level. RPM requires measuring transition rate or interval.

GPIO state HIGH
≠ fan running

GPIO state LOW
≠ fan stopped

Repeated edges over time
= RPM information

Tach Signal Polarity Does Not Affect RPM if You Count One Edge

You can count rising or falling edges. What matters is that you count one consistent edge per tach cycle. Do not count both rising and falling edges unless you also double the pulses-per-revolution conversion.

pulse_counter Edge Configuration

If you explicitly customize count_mode, use one edge as INCREMENT and disable the other for a normal PPR calculation.

count_mode:
  rising_edge: INCREMENT
  falling_edge: DISABLE

Counting both edges would turn a two-cycle-per-revolution tach into four counted transitions per revolution.

Why Your RPM Reads Exactly Double

  • You counted both rising and falling edges.
  • Your fan is 1 PPR but YAML assumes 2 PPR.
  • Noise creates an extra edge per real pulse.

First verify the pulses-per-revolution specification and edge-count mode.

Why Your RPM Reads Exactly Half

  • Fan generates 4 PPR but YAML assumes 2 PPR.
  • One polarity/edge is being lost due to poor signal level.
  • Over-aggressive filtering rejects real pulses.

A clean exact factor-of-two error is usually math/configuration, not random electrical noise.

Why RPM Jumps Randomly High

Short noise spikes can be counted as extra tach edges.

  • use external pull-up
  • shorten/twist tach wiring with ground
  • separate from fan power/PWM switching wires
  • check grounding
  • use a suitable internal_filter
  • add modest RC filtering only when needed

Why RPM Randomly Drops to Zero

  • loose tach connection
  • weak pull-up
  • common ground missing/intermittent
  • fan actually stops at low PWM
  • pulse-meter timeout too aggressive for very slow fan
  • GPIO conflict/boot pin misuse

Fan Spins but Tach Always Reads Zero

  • Wrong wire identified as tach.
  • No pull-up on open-collector output.
  • No common ground.
  • Wrong ESP32 GPIO.
  • Fan is 2-wire and has no tach signal.
  • Fan tach output damaged.

Measure tach idle voltage with a multimeter. With a 3.3 V pull-up and fan connected, the line should not sit permanently at an invalid floating voltage.

Use an Oscilloscope or Logic Analyzer if Available

A scope immediately answers the critical questions:

  • Is the tach waveform present?
  • What is its HIGH voltage?
  • What is its frequency?
  • Are there ringing/noise spikes?
  • Does PWM switching corrupt the tach signal?

For difficult fan installations, ten seconds on a scope can replace an hour of YAML guessing.

Check RPM Math Manually

If the tach frequency is 60 Hz and the fan is 2 PPR:

60 pulses/s × 60 = 3600 pulses/min
3600 ÷ 2 = 1800 RPM

Compare that with ESPHome. If the scope and formula say 1800 but Home Assistant says 3600, your filter/edge counting is wrong.

2-Wire Fan: Can You Measure RPM Anyway?

Not from the two external power wires alone using normal tach counting. A basic 2-wire fan exposes no dedicated speed signal.

Options include:

  • replace it with a 3-/4-wire fan
  • use an optical reflective sensor
  • use a Hall sensor and magnet
  • access an internal FG/test point only if you know the fan electronics
  • infer operation from current/airflow rather than true RPM

Replacing the fan is usually the cleanest Home Assistant solution.

Optical RPM Sensor Alternative

An optical interrupter/reflective sensor can create one pulse per blade marker or revolution. The ESPHome pulse-meter approach is the same; only the PPR math changes.

1 reflective marker per revolution
→ 1 PPR
→ RPM = pulses/min × 1.0

Hall Sensor Alternative

A magnet on the rotor plus a Hall switch can provide a clean tach pulse on fans or pumps without built-in tach output.

Again, calculate the actual PPR from the number of magnets/pulses generated per revolution.

Pump and Motor Tachometers

The same ESPHome technique works for pumps, blowers and rotating equipment if the feedback signal is electrically compatible with ESP32 and has a known PPR.

Industrial 12/24 V sensor outputs may require level shifting, optocoupling or transistor interfaces. Do not assume every “tach” output is open collector at ESP32-safe voltage.

Home Assistant Dashboard Suggestions

  • Current RPM gauge
  • 24-hour RPM history graph
  • Fan command percentage
  • Temperature
  • Fan Stalled problem sensor
  • Low RPM warning

Putting command, RPM and temperature on the same graph is excellent for cooling diagnostics.

Example Cooling Dashboard Interpretation

Temperature rises
→ PWM command rises
→ RPM rises
→ temperature stabilises

If PWM rises but RPM does not
→ fan/system fault

Complete Basic ESPHome Tach Configuration

esphome:
  name: fan-rpm-monitor
  friendly_name: Fan RPM Monitor

esp32:
  board: esp32dev
  framework:
    type: esp-idf

logger:

api:

ota:
  - platform: esphome

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password

sensor:
  - platform: pulse_meter
    pin:
      number: GPIO25
      mode:
        input: true
        pullup: true
    name: "Cooling Fan RPM"
    id: cooling_fan_rpm
    unit_of_measurement: "RPM"
    icon: "mdi:fan"
    accuracy_decimals: 0
    internal_filter: 13us
    timeout: 5s
    filters:
      - multiply: 0.5
      - throttle_average: 2s

The throttle_average reduces update traffic while still averaging the rapid pulse-meter updates. Adjust it to the response speed you want.

Why throttle_average Can Help

pulse_meter can publish whenever pulses arrive. At normal fan speed that can generate more internal updates than Home Assistant needs.

A short throttle/average period gives a clean dashboard value without losing useful fault response.

Do Not Over-Smooth Stall Detection

If you average RPM over 60 seconds, a stopped fan may take far too long to appear abnormal. Use modest smoothing and a separate delayed fault sensor.

A Better Stall Architecture

pulse_meter timeout: 3–5s
RPM sensor smoothing: 1–3s
stall binary sensor delayed_on: 5–10s

→ quick zero detection
→ no startup false alarm

PWM Control + Tach Monitoring

If you also control fan speed with ESPHome, keep control and feedback as separate entities.

fan.cooling_fan
→ command 0–100%

sensor.cooling_fan_rpm
→ measured physical speed

binary_sensor.cooling_fan_stalled
→ health

That makes debugging obvious: command says what you requested, RPM says what actually happened.

Do Not Use RPM as a Safety-Certified Interlock

ESP32/Home Assistant tach monitoring is excellent for diagnostics and automation, but it should not replace required hardware thermal protection or safety interlocks in equipment where fan failure can create fire, injury or equipment hazards.

Keep independent thermal cut-outs and manufacturer protection systems intact.

Best Use Cases

ProjectWhy RPM helps
AV amplifier cabinetDetect failed cooling fan
Server/network rackVerify airflow fans are actually rotating
3D printer/electronics enclosureDetect cooling faults
Solar/inverter electronics cabinetMonitor thermal ventilation
Greenhouse ventilationVerify commanded fan operation
DIY air purifierTrack filter/fan mechanical performance
Home Assistant HVAC projectFeedback for variable-speed fan control

When RPM Monitoring Is Overkill

If the fan is non-critical, always runs at full speed and a failed fan would be obvious, a tach input may not add much value.

Use RPM monitoring when the fan is hidden, variable-speed, important for cooling, or part of an automated system.

Troubleshooting Flow

Fan spinning?
 └─ NO → solve fan power/control first
 └─ YES
     Tach wire identified?
      └─ NO → verify fan pinout
      └─ YES
          3.3V pull-up + common ground?
           └─ NO → fix wiring
           └─ YES
               pulses visible?
                └─ NO → fan/output/wiring issue
                └─ YES
                    RPM wrong by factor → PPR/math/edge mode
                    RPM noisy → pull-up/filter/routing/noise

My Recommended Setup

12V 4-wire fan
├─ 12V supply direct to fan
├─ PWM control through proper interface
└─ Tach open collector
      ↓ 3.3V pull-up
ESP32 GPIO25
      ↓
ESPHome pulse_meter
      ↓ ×0.5 for 2 PPR
Fan RPM
      ↓
Home Assistant
├─ graph
├─ low-RPM warning
└─ stall notification

Final Recommendation

Fan tachometer monitoring is one of the simplest ways to make an ESP32 cooling project much more trustworthy. Instead of assuming that a PWM command means the fan is spinning, Home Assistant can verify the real mechanical response.

For a standard 3-/4-wire PC-style fan, use the tach open-collector output with a 3.3 V pull-up, share ground with the fan supply, and read the pulses on an ESP32 GPIO. For a standard two-pulse-per-revolution fan, convert ESPHome’s pulses/min to RPM with multiply: 0.5.

Use pulse_meter when you want smooth low-speed readings and set its timeout to a few seconds so a stopped fan reaches zero quickly. Use pulse_counter if you prefer fixed-interval hardware-assisted counting; both work well when configured correctly.

The most useful final setup exposes three separate concepts: requested fan speed, measured RPM, and fan-fault state. That gives Home Assistant enough information to spot a disconnected fan, stalled rotor, failing bearing or cooling system that is no longer responding as expected.

Related ESP32 Guides

Datasheets & External Resources

All external manufacturer/framework references are collected here so the main article keeps readers inside esp32.co.uk.

Share your love