Controlar la velocidad de ventiladores PWM de 12 V con ESPHome, ESP32 y Home Assistant
Tabla de contenidos
Introducción
En muchos racks domésticos, armarios técnicos o pequeños homelabs, la ventilación suele resolverse conectando varios ventiladores de PC directamente a 12 V. El problema es que, funcionando siempre al máximo, pueden generar más ruido del necesario. La alternativa es aprovechar ventiladores PWM de 4 pines, como los de una caja de PC, para regular su velocidad desde Home Assistant mediante un ESP32 y ESPHome.
En este montaje se controlan varios ventiladores de 12 V con un único ESP32. Los ventiladores reciben alimentación fija de 12 V y la velocidad se regula mediante la señal PWM del cuarto cable. Este enfoque es el adecuado para ventiladores PWM, ya que el control de velocidad no debe hacerse cortando o modulando la línea de alimentación, sino usando el pin PWM dedicado. Noctua, en su guía para microcontroladores, explica que el PWM controla la velocidad sin modificar la alimentación del ventilador y advierte expresamente de que no se debe aplicar PWM sobre la línea de alimentación, porque puede dañar el ventilador.
Objetivo del montaje
El objetivo es conseguir:
- Controlar la velocidad de tres ventiladores PWM de 12 V.
- Exponerlos en Home Assistant como una entidad
fan. - Regularlos manualmente desde Home Assistant.
- Automatizar la velocidad según la temperatura del rack.
- Mantener alimentación independiente para motores y electrónica.
- Evitar que los ventiladores se paren completamente, usando su velocidad mínima real como ventilación base.
En el caso práctico documentado, los ventiladores se comportan aproximadamente así:
| Valor en Home Assistant | Velocidad real aproximada |
|---|---|
| 0 % / apagado lógico | 360 rpm |
| 50 % | 860 rpm |
| 100 % | 1500 rpm |
Esto es importante, porque aunque Home Assistant muestre el ventilador como apagado, físicamente los ventiladores siguen girando a baja velocidad.
Material necesario
Para este montaje se ha usado:
- 1 ESP32.
- 3 ventiladores PWM de 12 V y 4 pines.
- 1 fuente de 12 V para los motores.
- 1 fuente de 5 V para el ESP32, o un buck de 12 V a 5 V.
- Cableado adecuado para alimentación.
- Resistencias de 1 kΩ a 4,7 kΩ para la señal PWM.
- Opcional: cable para leer RPM de un único ventilador.
El ESP32 resulta especialmente adecuado para esta tarea porque ESPHome dispone del componente ledc, que expone un canal PWM hardware del ESP32 como una salida configurable. La documentación oficial indica que ledc permite seleccionar pin, frecuencia y canal, y que el rango de frecuencia del LEDC va de 10 Hz a 40 MHz.
Pinout típico de un ventilador PWM de PC
En un ventilador PWM de 4 pines, lo habitual es encontrar:
| Cable | Función |
|---|---|
| Negro | GND |
| Amarillo | +12 V |
| Verde | TACH / RPM |
| Azul | PWM |
La guía de Noctua identifica estos cuatro cables como masa, alimentación, señal RPM y señal PWM, respectivamente. También indica que la frecuencia objetivo para la señal PWM es de 25 kHz, con un rango soportado de 21 a 28 kHz, y que una frecuencia fuera de ese rango puede provocar comportamientos imprevisibles.
Esquema eléctrico
La alimentación de los ventiladores debe ir directamente desde la fuente de 12 V:
Fuente 12 V + ───────────── +12 V ventiladores Fuente 12 V GND ──────────── GND ventiladores
El ESP32 solo envía la señal de control PWM:
ESP32 GPIO25 ── resistencia 1k/4.7k ── PWM azul ventiladores ESP32 GND ─────────────────────────── GND común
La masa común es imprescindible. Si el ventilador usa una fuente externa, la masa de esa fuente debe estar unida a la masa del microcontrolador para que exista una referencia común de señal. Noctua lo indica expresamente en su guía: cuando se usa una fuente externa, debe conectarse el GND del ventilador al GND del dispositivo controlador para que el PWM funcione correctamente.
El esquema general quedaría así:
FUENTE 12 V+12 V ─────────────── +12 V motor ventilador 1 +12 V ─────────────── +12 V motor ventilador 2 +12 V ─────────────── +12 V motor ventilador 3 GND ─────────────── GND motor ventilador 1/2/3 ESP32 GPIO25 ── 1k/4.7k ─── PWM ventilador 1/2/3 GND ─────────────── GND común
No se deben unir las señales TACH/RPM de los tres ventiladores. Si se quiere medir RPM, lo recomendable es leer la señal TACH de un solo ventilador.
Si la conexión no es estandar, el sistema de conexión de ventiladores con 4 hilos es el de la siguiente imagen:

Consideraciones sobre alimentación
Durante las pruebas se detectó que una fuente supuestamente suficiente no mantenía tensión bajo carga y los ventiladores se quedaban a unas 400 rpm. Con una fuente de laboratorio a 12 V, los mismos ventiladores alcanzaban 1500 rpm consumiendo aproximadamente 0,15 A. La conclusión fue que el problema no estaba en ESPHome ni en el PWM, sino en la fuente o el cableado de alimentación.
Para tres ventiladores de este tipo, si el consumo real ronda 0,15 A, una fuente de 12 V y 1 A ya podría ser suficiente. Aun así, es recomendable usar margen:
Mínimo razonable: 12 V 1 A Recomendado: 12 V 2 A Sobrado: 12 V 3 A
Más importante que la cifra de amperios es que la fuente mantenga 12 V reales bajo carga.
Configuración ESPHome
El componente output de ESPHome permite crear salidas binarias o salidas flotantes; las salidas flotantes, como PWM, pueden entregar valores entre 0 y 1. Además, admite opciones como min_power, max_power, inverted y zero_means_zero.
Sobre esa salida PWM se crea una entidad fan usando la plataforma speed. La documentación de ESPHome indica que speed fan permite representar cualquier salida flotante como un ventilador con control de velocidad y que speed_count: 100 permite trabajar en incrementos del 1 %.
Ejemplo de configuración:
substitutions:
device_name: "control-ventiladores-rack"
friendly_name: "Control Ventiladores Rack"
esphome:
name: ${device_name}
friendly_name: ${friendly_name}
esp32:
board: esp32dev
framework:
type: esp-idf
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap:
ssid: "${friendly_name} Fallback"
password: "cambia-esta-clave"
captive_portal:
logger:
api:
ota:
- platform: esphome
output:
- platform: ledc
id: fan_pwm_output
pin: GPIO25
frequency: 25000 Hz
channel: 0
min_power: 20%
zero_means_zero: true
fan:
- platform: speed
id: fan_rack
name: "Velocidad ventiladores"
output: fan_pwm_output
speed_count: 100
restore_mode: RESTORE_DEFAULT_ON
sensor:
- platform: pulse_counter
pin:
number: GPIO27
mode:
input: true
pullup: true
name: "RPM ventilador 1"
unit_of_measurement: "RPM"
accuracy_decimals: 0
update_interval: 10s
filters:
- multiply: 0.5
- platform: wifi_signal
name: "WiFi Control Ventiladores"
update_interval: 60s
button:
- platform: restart
name: "Reiniciar Control Ventiladores"
La parte de RPM es opcional. Los ventiladores suelen generar dos pulsos por vuelta en la señal TACH; por eso se aplica multiply: 0.5. Noctua indica que sus ventiladores generan dos pulsos por revolución y que la señal RPM es de tipo colector abierto, por lo que necesita pull-up en la entrada.
Comprobación inicial
Una vez cargado el firmware en el ESP32, en Home Assistant aparecerá una entidad similar a:
fan.control_ventiladores_rack_velocidad_ventiladores
Desde esa entidad se puede probar:
0 % 25 % 50 % 75 % 100 %
En este caso concreto, el 0 % no equivale a parada real, sino a unas 360 rpm. Esto es útil en un rack, porque mantiene una ventilación mínima incluso cuando Home Assistant muestra el ventilador como apagado.
Automatización por temperatura en Home Assistant
Además del control manual, se puede regular la velocidad usando un sensor de temperatura dentro del rack. En este caso se usa un sensor Zigbee colocado detrás de los ventiladores.
Home Assistant proporciona la acción fan.set_percentage, que permite fijar un porcentaje de velocidad en entidades de tipo fan. También se puede disparar una automatización por cambios de estado de una entidad, lo que encaja con un sensor de temperatura que actualiza su valor periódicamente.
Ejemplo de automatización:
alias: "Rack - Regular ventiladores por temperatura"
description: "Ajusta la velocidad de los ventiladores según la temperatura del rack"
mode: restart
triggers:
- trigger: state
entity_id: sensor.temperatura_rack
- trigger: time_pattern
minutes: "/2"
- trigger: homeassistant
event: startactions:
- variables:
temp: "{{ states('sensor.temperatura_rack') | float(none) }}"
fan_entity: fan.control_ventiladores_rack_velocidad_ventiladores
current_pct: "{{ state_attr('fan.control_ventiladores_rack_velocidad_ventiladores', 'percentage') | int(0) }}"
target_pct: >-
{% if temp is none %}
100
{% elif temp < 24 %}
0
{% elif temp < 26 %}
20
{% elif temp < 28 %}
35
{% elif temp < 30 %}
50
{% elif temp < 32 %}
65
{% elif temp < 34 %}
80
{% else %}
100
{% endif %}
- choose:
- conditions:
- condition: template
value_template: "{{ temp is none }}"
sequence:
- action: fan.set_percentage
target:
entity_id: fan.control_ventiladores_rack_velocidad_ventiladores
data:
percentage: 100
- conditions:
- condition: template
value_template: "{{ target_pct | int == 0 }}"
sequence:
- action: fan.turn_off
target:
entity_id: fan.control_ventiladores_rack_velocidad_ventiladores
- conditions:
- condition: template
value_template: >
{{ is_state(fan_entity, 'off') or ((current_pct | int - target_pct | int) | abs >= 5) }}
sequence:
- action: fan.set_percentage
target:
entity_id: fan.control_ventiladores_rack_velocidad_ventiladores
data:
percentage: "{{ target_pct | int }}"
La curva de funcionamiento queda así:
| Temperatura | Velocidad configurada |
|---|---|
| Sensor no disponible | 100 % |
| Menos de 24 ºC | 0 % |
| 24–26 ºC | 20 % |
| 26–28 ºC | 35 % |
| 28–30 ºC | 50 % |
| 30–32 ºC | 65 % |
| 32–34 ºC | 80 % |
| Más de 34 ºC | 100 % |
Como el sensor está situado justo detrás de los ventiladores, se evita una regulación demasiado nerviosa. La automatización solo cambia la velocidad si hay una diferencia de al menos 5 puntos porcentuales respecto al valor actual.
Problemas encontrados durante el montaje
Durante las pruebas aparecieron varios síntomas extraños:
- Los ventiladores no alcanzaban las RPM máximas.- Con una fuente llegaban a 1500 rpm y con otra solo a 400 rpm.- Al conectar o desconectar la iluminación ARGB cambiaba el comportamiento de los ventiladores.- Al desconectar masas compartidas, los ventiladores se iban a máxima velocidad.
La causa no estaba en ESPHome, sino en alimentación y referencias de masa. Los ventiladores PWM necesitan una masa común con el ESP32 para interpretar correctamente la señal PWM. Si se elimina esa masa común, el PWM deja de tener referencia y el ventilador puede comportarse como si no recibiera señal, y muchos ventiladores PWM pasan a velocidad máxima cuando no hay señal PWM conectada. Noctua también indica que, sin señal PWM conectada, el ventilador funciona a máxima velocidad.
Recomendaciones finales
Para un montaje estable:
1. Alimentar motores con una fuente de 12 V real y estable. 2. No alimentar motores desde el ESP32. 3. Usar cable adecuado para +12 V y GND. 4. Unir GND de fuente 12 V, fuente 5 V y ESP32. 5. No unir los cables TACH de varios ventiladores. 6. Usar 25 kHz como frecuencia PWM. 7. Si se detectan comportamientos raros, probar primero con una fuente de laboratorio.
También es recomendable montar el GND en estrella o con una bornera común robusta, evitando que la corriente de los motores retorne por cables finos o por el propio ESP32.
Conclusión
Con un ESP32 y ESPHome es posible crear un controlador de ventiladores PWM completamente integrado en Home Assistant. El resultado es una ventilación regulable, silenciosa y automatizable, especialmente útil para racks, armarios técnicos o pequeños servidores domésticos.
La clave del montaje no está solo en el YAML, sino en respetar tres principios eléctricos básicos:
12 V estables para motores GND común para señales PWM a 25 kHz por el pin dedicado
