In my first post on this project, I documented reverse-engineering the RF protocol behind my QazQa ceiling fan’s remote, using a CC1101 transceiver and ESPHome. The goal was full control from Home Assistant — fan on/off, five speeds, light, and forward/reverse — all cloned from the original remote and replayed on demand.
That transmit side never worked out. This is the short version of how the rest of it went, and where it actually landed.
What else I tried
After the first post, I kept chasing the transmit problem from a few more angles: testing signal inversion, ruling out ESP8266 timing jitter by moving to an ESP32 board with proper RMT hardware, and eventually buying an RTL-SDR dongle to measure the remote’s actual transmit frequency directly rather than assuming the nominal 433.92MHz. That measurement turned up a real ~30kHz offset — a genuinely promising lead — but transmitting at the corrected frequency still produced no response from the fan. Swapping in a second CC1101 module, moving the data pin to a different GPIO, and a full frequency sweep across a dozen nearby frequencies all came back silent too.
At that point, with two independent CC1101 modules and an ESP32 confirmed working correctly (SPI communication verified, clean radio configuration, frequency accurately set) both failing identically, it stopped looking like a fixable configuration problem and started looking like something deeper in the transmit chain I didn’t have the tools to fully diagnose without an oscilloscope.
Changing the goal
Rather than keep chasing transmit, I scaled the goal down to something the hardware had already proven it could do well: receiving. Every capture throughout this whole project came through cleanly — it was only sending that never worked.
So the final version of this project doesn’t control the fan at all. It listens.
What it does now
The ESP32 + CC1101 now runs purely as a receiver, watching for any RF activity from the physical remote. Two pieces make it useful in Home Assistant:
- A simple activity sensor that briefly turns on whenever the CC1101 detects a signal at all — regardless of which button was pressed.
- A classifier that inspects the raw pulse pattern of each press and identifies which specific button was used — Fan, Light, Direction, or one of the five speeds — based on the same gap-timing signature I’d originally identified while trying to solve the transmit side.
That second part uses ESPHome’s on_raw automation trigger, which hands the full raw pulse array to a small lambda instead of requiring an exact pre-defined code match. That distinction mattered: matching an exact, fixed-length code turned out to be unreliable, since the remote’s repeat count (and therefore total signal length) varies slightly from press to press. Scanning the array for the two specific gap values that distinguish each button, rather than requiring the whole buffer to match, turned out to be far more robust.
There’s one known limitation worth naming honestly: Light and Speed 4 share almost identical gap timing, so the classifier can’t fully tell them apart. Everything else — Fan, Direction, and Speeds 1, 2, 3, and 5 — classifies reliably.
The full ESPHome configuration is available here: qazqa-monitor.yaml
Where this leaves things
I now have real, working visibility in Home Assistant into exactly when and how the physical remote is used, even though I still can’t control the fan from HA directly. Not the outcome I set out for, but a genuinely useful one — and everything learned chasing the transmit side, including the dead ends, is documented here in case it’s useful groundwork for anyone else working with a CC1101 and an unsupported 433MHz remote, or for picking this back up myself down the line.


