Build a Smart Light Dimmer with ESP32 and a Rotary Encoder in Home Assistant
I’ve always loved the tactile feel of a good knob. While voice commands and smartphone apps are convenient, there’s something deeply satisfying about physically turning a dial to dim the lights. I wanted to bridge that gap between the digital smarts of Home Assistant and the physical world. So, I set out to build a custom smart light dimmer with ESP32 and a rotary encoder.
The goal was simple: a knob that not only controls my smart lights locally but also stays perfectly in sync with their state in Home Assistant. If someone else turns the light on via the app, the knob’s position should reflect that. After some tinkering, I landed on a solution using ESPHome that works flawlessly. Here’s how you can build one too.
What You’ll Need
Before we start soldering and coding, let’s gather the components. I’ve included links to typical examples, but most generic components will work just fine.
- ESP32 Development Board: The brains of the operation. Its Wi-Fi capability and ample GPIO pins are perfect for this. I used a DOIT ESP32 DevKit V1 (affiliate link).
- Rotary Encoder: This is our knob. Get a KY-040 module; it’s cheap and widely available. It should have pins for CLK, DT, and SW (for the push-button function). Example KY-040 on Amazon.
- Breadboard and Jumper Wires: For prototyping. A starter kit has everything you need.
- USB Cable: To power the ESP32 and upload the code.
- A Smart Light: Any light that can be controlled by Home Assistant, like a Philips Hue, WiZ, or a generic light connected via Zigbee2MQTT.
Step 1: Wiring the Hardware
This is the easy part. The rotary encoder has five pins, but we only need three for this project: Ground (GND), VCC (+3.3V), and the two data pins (CLK and DT), plus the switch (SW).
Connect everything as shown in the table and diagram below. The ESP32’s 3.3V pin is perfect for powering the encoder.
Rotary Encoder (KY-040) | ESP32 GPIO Pin |
---|---|
GND | GND |
+ (VCC) | 3.3V |
SW (Switch) | GPIO 16 |
DT (Data) | GPIO 17 |
CLK (Clock) | GPIO 18 |

A simple wiring diagram for the ESP32 and rotary encoder.
Pro Tip: If your encoder module has a built-in pull-up resistor (most KY-040s do), you’re good to go. If not, you might need to add external pull-up resistors to the CLK, DT, and SW pins. ESPHome can handle this in software, but hardware resistors are more reliable.
Step 2: Flashing ESPHome on the ESP32
We’ll use ESPHome, which is a fantastic system for configuring ESP boards without writing traditional Arduino code. It integrates seamlessly with Home Assistant.
- Install ESPHome: If you haven’t already, add the ESPHome integration to your Home Assistant OS via the Add-on Store. For other installations, follow the official ESPHome getting started guide.
- Create a New Device: In the ESPHome dashboard, click “New Device,” give it a name like
smart-light-dimmer
, and select your ESP32 board type. - Wireless Setup: Enter your Wi-Fi credentials. ESPHome will generate a basic configuration file.
Step 3: The Magic - ESPHome Configuration
Now, we edit the configuration file (.yaml
). This is where we define how the rotary encoder interacts with a light in Home Assistant. Replace the entire default configuration with the code below, but make sure to update the substitutions
section with your own details.
# Basic device info
substitutions:
device_name: "smart-light-dimmer"
friendly_name: "Smart Light Dimmer"
esphome:
name: $device_name
friendly_name: $friendly_name
esp32:
board: nodemcu-32s
framework:
type: arduino
# Enable logging and Wi-Fi
logger:
api:
ota:
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ap: {} # Fallback hotspot
captive_portal:
# Define the rotary encoder component
sensor:
- platform: rotary_encoder
id: rotary_encoder
pin_a: GPIO18 # CLK pin
pin_b: GPIO17 # DT pin
min_value: 0
max_value: 100
filters:
- delta: 5.0 # Adjust sensitivity. Higher value = bigger jumps per click.
on_value:
then:
- light.control:
id: ha_light_entity # This ID links to the 'light' component below
brightness: !lambda 'return (int)(x / 100.0 * 255);' # Convert 0-100 to 0-255
# Define the encoder's push button
binary_sensor:
- platform: gpio
id: encoder_button
pin:
number: GPIO16
inverted: true # Often needed as the button is active-low
on_click:
then:
- light.toggle: ha_light_entity
# Define a "dummy" light that represents your actual HA light
light:
- platform: api
id: ha_light_entity
name: "${friendly_name} Controller"
# Restore mode ensures the knob's state is correct after a reboot
restore_mode: RESTORE_AND_OFF
# Optional: A status LED
status_led:
pin: GPIO2 # Usually the built-in blue LED on ESP32 boards
Let’s break down the key parts:
rotary_encoder
sensor: This reads the turning motion. Theon_value
trigger is the heart of the project. Every time you turn the knob, it calculates the new brightness level and sends it to the light.binary_sensor
for the button: This handles the push-button function of the encoder. A simple click toggles the light on/off.api
light platform: This is crucial. It creates a “proxy” light entity in ESPHome that stays perfectly synchronized with the actual light in Home Assistant. When you turn the knob, it updates this proxy, which then tells Home Assistant to update the real light, and vice-versa.
Step 4: Upload and Integrate with Home Assistant
- Compile & Upload: Plug your ESP32 into your computer via USB. In the ESPHome dashboard, click “Install” next to your device and select “Plug into the computer running ESPHome Dashboard.” The firmware will compile and upload.
- Automatic Discovery: If you have the ESPHome integration installed in Home Assistant, the new device should appear automatically as a new device with a “light” entity called
light.smart_light_dimmer_controller
.
Step 5: Linking to Your Real Smart Light
The final step is to create an automation in Home Assistant that links our dummy controller light to the actual light we want to dim.
We’ll do this with a simple blueprint automation. Go to Settings > Automations & Scenes > Create Automation and use the “Convert device action to an automation” option.
- Trigger: State of the dummy light (
light.smart_light_dimmer_controller
) changes. - Action: Call the
light.turn_on
service for your real light (e.g.,light.living_room_lamp
).- In the service data, we’ll pass along the brightness from the trigger.
# Service data for the action brightness: "{{ trigger.to_state.attributes.brightness }}"
And that’s it! Now, when you turn the knob, it should control your smart light. Conversely, if you change the light’s state in Home Assistant, the knob’s “virtual” position will update accordingly.
Troubleshooting and Common Pitfalls
- The Knob is “Jumpy” or Reversed: This is usually a wiring issue. Swap the CLK (GPIO 18) and DT (GPIO 17) pins on the ESP32. You can also adjust the
delta
filter value in the YAML configuration to make it less sensitive. - Home Assistant Doesn’t Discover the Device: Check that the
api:
key is in your ESPHome YAML and that your ESP32 is connected to the same Wi-Fi network as your Home Assistant instance. Also, verify the ESPHome integration is configured correctly. - The Button Doesn’t Work: The
inverted: true
setting is often necessary. If it’s still not working, check your wiring and use a multimeter to see if the pin voltage changes when the button is pressed.
Taking It Further
This project is a fantastic foundation. Here are a few ideas to enhance it:
- Control Multiple Lights: Modify the automation in Home Assistant to control a group of lights instead of a single one.
- Change Light Color/Temperature: Use a long press or double press on the encoder button to cycle through color temperatures. This would require more complex logic in the ESPHome YAML using
on_multi_click
andon_click
triggers. - 3D-Print an Enclosure: Design and print a sleek case to make your dimmer look like a professional product. This is a great next step, much like when I built my Xiaomi Mijia temperature sensor network.
Frequently Asked Questions (FAQ)
Q: Can I use an ESP8266 instead of an ESP32?
A: Absolutely! The ESP8266 has enough GPIO pins for this project. Just change the esp32:
section in the YAML to esp8266:
and select an appropriate board like nodemcuv2
.
Q: My rotary encoder values are decreasing when I turn clockwise. How do I fix this? A: Simply swap the physical connections of the CLK and DT pins on the ESP32.
Q: Can I use this to control a light that’s not in Home Assistant? A: The beauty of this method is its tight integration with Home Assistant. For lights outside of HA, you would need a different approach, perhaps directly sending MQTT commands from the ESP32.
Q: Does this work with battery power? A: The ESP32 is not very power-efficient for battery operation. For a wireless dimmer, consider using an ESP32-S2 or ESP32-C3 with deep sleep, but you’d need a more complex power management strategy.
Building this smart light dimmer with ESP32 has been one of my most rewarding DIY smart home projects. It combines the best of both worlds: the reliability of a physical control and the intelligence of Home Assistant. Happy building