[Tutorial] Control Dooya RF433 Blinds with ESP32 + CC1101 via ESPHome
Introduction
Hey everyone!
Sharing my setup for controlling Dooya roller blinds (RF433 MHz motors) from Home Assistant using an ESP32 and a CC1101 module running ESPHome.
Dooya blinds (and rebrands like Zemismart, AM43, etc.) use RF 433 MHz remotes with a proprietary protocol. The idea: replace all DC90 remotes with a single ESP32 that transmits the same RF codes.
Result: 8 blinds controlled from HA, no cloud, no proprietary hub, for ~$10 worth of hardware.
Hardware
| Component | Approx. Price | Source |
|---|---|---|
| ESP32 DevKit V1 (or clone) | ~$4 | AliExpress |
| CC1101 433MHz module (green E07, 8 pins) | ~$3 | AliExpress |
| Dupont jumper wires (female-female) | ~$2 | AliExpress |
| Micro-USB cable + 5V charger | already have | - |
Total: ~$10
Optional for final installation
- Plastic enclosure (~83x58x33mm)
- PG7 cable gland (USB cable passthrough)
- 3M 9448A double-sided tape (mounting inside enclosure)
Wiring: ESP32 + CC1101
Identify your CC1101 module
There are two common versions of the 8-pin CC1101 module. Check pin 2:
- GREEN module (E07): Pin 2 = VCC (3.3V)
- BLUE module (Standard): Pin 2 = GND
Wiring for GREEN module (E07) — most common on AliExpress
| CC1101 Pin | Name | ESP32 GPIO | Function |
|---|---|---|---|
| 1 | GND | GND | Ground |
| 2 | VCC | 3V3 | 3.3V Power |
| 3 | GDO0 | GPIO 4 | RX/TX (RF data) |
| 4 | CSN | GPIO 5 | SPI Chip Select |
| 5 | SCK | GPIO 18 | SPI Clock |
| 6 | MOSI | GPIO 23 | SPI MOSI |
| 7 | MISO | GPIO 19 | SPI MISO |
| 8 | GDO2 | (not used) | - |
Wiring for BLUE module (Standard)
| CC1101 Pin | Name | ESP32 GPIO | Function |
|---|---|---|---|
| 1 | VCC | 3V3 | 3.3V Power |
| 2 | GND | GND | Ground |
| 3 | MOSI | GPIO 23 | SPI MOSI |
| 4 | SCLK | GPIO 18 | SPI Clock |
| 5 | MISO | GPIO 19 | SPI MISO |
| 6 | GDO2 | (not used) | - |
| 7 | GDO0 | GPIO 4 | RX/TX |
| 8 | CSN | GPIO 5 | SPI Chip Select |
Warning: The CC1101 runs on 3.3V only. Do NOT connect it to 5V or you’ll fry it.
Step 1: Sniff your remote codes
Before you can transmit, you need to capture the RF codes from each Dooya remote. Each remote has a unique ID (24-bit) that is paired to the blind motor.
ESPHome RX config (sniffer)
Critical: Reception only works when
remote_receiveris used alone, withoutremote_transmitterin the same config. This is a limitation of sharing GPIO4 between RX and TX.
substitutions:
device_name: esp32-rf433-sniffer
friendly_name: "RF433 Sniffer"
esphome:
name: ${device_name}
friendly_name: ${friendly_name}
on_boot:
priority: -100
then:
- lambda: id(mycc1101).recv();
esp32:
board: esp32dev
framework:
type: esp-idf
external_components:
- source: github://juanboro/esphome-radiolib-cc1101@main
components: [ radiolib_cc1101 ]
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
fast_connect: true
ap:
ssid: "${device_name}-fallback"
password: "12345678"
captive_portal:
logger:
level: DEBUG
api:
ota:
- platform: esphome
password: "1234"
web_server:
port: 80
spi:
id: spi_bus
type: single
clk_pin: GPIO18
mosi_pin: GPIO23
miso_pin: GPIO19
radiolib_cc1101:
id: mycc1101
cs_pin: 5
rx_pin:
number: 4
allow_other_uses: true
frequency: 433.9205
filter: 468khz
bitrate: 5
# Receiver ONLY - NO remote_transmitter!
remote_receiver:
id: rf_receiver
pin:
number: 4
allow_other_uses: true
dump: all
How to sniff
- Flash this config to the ESP32
- Open the ESPHome logs (via dashboard or
esphome logs) - Press a button on your DC90 remote (Up, Stop, or Down)
- Look for a log line like:
Received Dooya: id=0x00D1C917, channel=5, button=1, check=1
- Write down
id,channel,button, andcheckfor each button - Repeat for each remote
Dooya protocol
- id: 24-bit, unique per remote (paired to the motor)
- channel: usually 5 for single-channel remotes
- button: 1 = Up, 3 = Down, 5 = Stop
- check: usually equals button value
Step 2: Configure TX (transmitter)
Once you’ve captured all codes, switch to the TX config. Remove remote_receiver and add remote_transmitter instead.
ESPHome TX config
substitutions:
device_name: esp32-blinds-rf433
friendly_name: "Dooya Blinds RF433"
esphome:
name: ${device_name}
friendly_name: ${friendly_name}
esp32:
board: esp32dev
framework:
type: esp-idf
external_components:
- source: github://juanboro/esphome-radiolib-cc1101@main
components: [ radiolib_cc1101 ]
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
power_save_mode: none
fast_connect: true
ap:
ssid: "${device_name}-fallback"
password: "12345678"
captive_portal:
logger:
level: DEBUG
api:
ota:
- platform: esphome
password: "1234"
web_server:
port: 80
# SPI config for CC1101
spi:
id: spi_bus
type: single
clk_pin: GPIO18
mosi_pin: GPIO23
miso_pin: GPIO19
# RadioLib CC1101 - Dooya settings
radiolib_cc1101:
id: mycc1101
cs_pin: 5
rx_pin:
number: 4
allow_other_uses: true
frequency: 433.9205
filter: 468khz
bitrate: 5
# RF transmitter on GDO0 (GPIO4)
remote_transmitter:
- id: rf_transmitter
pin:
number: 4
allow_other_uses: true
carrier_duty_percent: 100%
non_blocking: false
on_transmit:
then:
- lambda: id(mycc1101).xmit();
on_complete:
then:
- lambda: id(mycc1101).recv();
# === Buttons per blind ===
# Duplicate this block for each blind, changing the name and ID
button:
# --- Living Room Blind ---
- platform: template
name: "Living Room Blind - Up"
id: blind_living_up
icon: "mdi:arrow-up-bold-box"
on_press:
- remote_transmitter.transmit_dooya:
transmitter_id: rf_transmitter
id: 0x00D1C917 # <-- Replace with YOUR remote's ID
channel: 5
button: 1
check: 1
repeat: { times: 5, wait_time: 1ms }
- platform: template
name: "Living Room Blind - Stop"
id: blind_living_stop
icon: "mdi:stop-circle"
on_press:
- remote_transmitter.transmit_dooya:
transmitter_id: rf_transmitter
id: 0x00D1C917
channel: 5
button: 5
check: 5
repeat: { times: 5, wait_time: 1ms }
- platform: template
name: "Living Room Blind - Down"
id: blind_living_down
icon: "mdi:arrow-down-bold-box"
on_press:
- remote_transmitter.transmit_dooya:
transmitter_id: rf_transmitter
id: 0x00D1C917
channel: 5
button: 3
check: 3
repeat: { times: 5, wait_time: 1ms }
Key points
repeat: { times: 5, wait_time: 1ms }sends the code 5 times for reliable motor reception- The
on_transmit/on_completelambdas switch the CC1101 between TX mode and idle — this is mandatory - ESP-IDF framework required: the Arduino framework does not work correctly for TX with the CC1101
Step 3: Home Assistant integration
Buttons automatically appear in HA via the ESPHome integration. You’ll get 3 buttons per blind: Up, Stop, Down.
Create a Cover entity (optional)
For a proper blind entity in HA with up/down/stop controls, create a template cover in your configuration.yaml:
cover:
- platform: template
covers:
blind_living_room:
device_class: shutter
friendly_name: "Living Room Blind"
open_cover:
- action: button.press
target:
entity_id: button.living_room_blind_up
close_cover:
- action: button.press
target:
entity_id: button.living_room_blind_down
stop_cover:
- action: button.press
target:
entity_id: button.living_room_blind_stop
Note: Without RF state feedback, the blind position is unknown. The cover will always show “unknown” state but commands work fine.
Enclosure (final installation)
Once everything works on the breadboard:
- Solder wires directly onto the ESP32 pins (component side)
- Cut the protruding pins on the back with flush cutters (or bend them flat if you want to keep the breadboard option)
- Mount the ESP32 and CC1101 inside the enclosure with 3M 9448A double-sided tape
- Drill a hole for the USB cable (PG7 cable gland = clean and solid)
- The CC1101 antenna can stay inside if the enclosure is plastic (RF signal passes through)
Placement
Place the enclosure centrally relative to your blinds for optimal range. The CC1101 has a range of ~30-50m indoors, more than enough for a house.
Gotchas & tips
RX + TX together = doesn’t work
remote_receiver and remote_transmitter share the same GPIO4. When both are in the same config, reception stops working, even when not transmitting. Solution: two separate YAML files — one for sniffing, one for transmitting.
Arduino framework = unstable TX
With the Arduino framework, TX freezes after a few transmissions. Switch to ESP-IDF, it’s the only stable config.
Dooya frequency = 433.9205 MHz
This is NOT standard 433.92 MHz. The exact frequency is 433.9205 MHz. If your blinds don’t respond, check this setting.
allow_other_uses: true is mandatory
GPIO4 is used by both radiolib_cc1101 (rx_pin) and remote_transmitter (pin). You must add allow_other_uses: true on both declarations, otherwise ESPHome refuses to compile.
The repeat matters
Dooya motors need to receive the code multiple times to react. repeat: { times: 5 } is a good balance between reliability and speed.
External component used
- esphome-radiolib-cc1101 by juanboro
- Based on the RadioLib library, enables CC1101 usage in OOK direct mode with ESPHome
- The Dooya protocol is natively supported by ESPHome (
remote_transmitter.transmit_dooya)
Conclusion
This works flawlessly for my setup of 8 Dooya blinds. The CC1101 is far more reliable than a basic 433 MHz transmitter because it allows precise frequency tuning (433.9205 MHz vs generic 433.92).
Total cost is ridiculously low (~$10) compared to proprietary Zigbee/WiFi solutions, and everything stays 100% local via Home Assistant.
Feel free to ask if you have any questions! ![]()
Tested with: ESPHome 2025.x / ESP-IDF 5.x / ESP32 DevKit v1 / CC1101 E07 green module / Home Assistant 2025.x+