RF Hacking My Cloud-Controlled Ceiling Fan

sammycdubs1 pts0 comments

RF Hacking my Cloud-Controlled Ceiling Fan · Sam Wilkinson

When we moved into our current place, we knew pretty quickly we'd want to change our bedroom ceiling fan. That thing easily covered a quarter of our ceiling, wobbled like crazy, and I could not figure out what any of the three (!) pull chains did. It had to go.

After some searching, this fan (the CLF513S) from Dreo seemed like a good option. It's reasonably priced, looks quite nice, and most importantly, it's a normal size.

I've been on a smart home kick over the past few years, with Home Assistant as my core platform, so I wanted to be able to add the new fan to my setup. Unfortunately, Dreo's smart home integration is cloud-only out of the box. They do get bonus points, though, for maintaining an official Home Assistant integration.

I really try to avoid cloud-based smart home controls whenever possible, for a number of reasons:

Internet outages stop them from working.

You often get locked into ad-filled proprietary apps, and integrations to get around that can be flaky.

It just generally feels weird to have external servers control physical things in your home?

The fan was sleek and affordable enough that I held my nose and bought it, with the hope of figuring out local control once I had it installed.

Choosing a Path to Local Control

When exploring my options for local control, I found this excellent project by ouaibe on GitHub where they used some very impressive firmware reverse engineering to get ESPHome running on a different Dreo fan.

While I could try to follow in their footsteps and attempt to replicate their methods on my ceiling fan, the prospect of bricking something hard-wired into my ceiling put me off.

The fan's only local control mechanism was its remote, so I wanted to try the "universal remote" path: decode the remote's commands and replay them. Universal remotes are famously reliable products, so clearly I was on the right track.

The remote did not require line of sight, so it was not infrared like a TV remote. Looking up the FCC ID listed on the back confirmed it was an RF transmitter.

To enable local control through Home Assistant, there were three steps I needed to take:

Decode the remote's RF commands.

Build something to replay those commands.

Let Home Assistant trigger those replays.

With the approach settled, the first step was understanding what the remote was actually sending.

Decoding Commands

To decode a (simple) RF control scheme, you need to figure out three things:

The carrier frequency · The base frequency used to communicate.

The modulation method · How 1s and 0s are being conveyed through that carrier frequency.

The payload(s) · The actual 1s and 0s being sent by the remote.

Having found the carrier frequency in the manufacturer's FCC documentation (433.92 MHz, a common frequency for remotes), the next step was figuring out the modulation method.

Two common digital modulation methods for remotes are:

Amplitude Shift Keying (ASK ) · The transmitter shifts the amplitude of the carrier up and down to represent 1 and 0. A simple version is On-Off Keying (OOK) , where the carrier just turns on and off.

Frequency Shift Keying (FSK ) · The transmitter shifts the signal frequency slightly above and below the carrier frequency to represent 1 and 0.

It's the same basic idea as AM vs FM radio, but with digital signals instead of analog audio. The FCC documentation says the remote uses FSK, but I wanted to verify the actual signal rather than take that at face value. Often these documents use generic values so they aren't the most reliable source.

I used an RTL-SDR (with gqrx) to capture RF spectrum data while pressing some of the remote buttons. This made it clear that the remote was using ASK (OOK specifically): bursts of high amplitude at the carrier frequency with silence in between. Below is a waterfall plot, rotated sideways, showing the spectrum captured when pressing the Fan On/Off button twice.

Looking at the capture in more detail with the plot below, the signal appeared to use short and long bursts of amplitude, kind of like Morse code's dots and dashes.

Zooming in on one of the button presses, I saw that a specific pattern was being repeated five times in quick succession. The plot below shows one of those repetitions, which I could then decode into logical 1s and 0s. I repeated this process for each button I wanted to replicate (fan on/off, fan speed presets, light on/off, and brightness up/down).

Each command followed the same structure, with a single packet repeated five times with an 8.8 ms gap between repetitions:

A preamble of 8 logical 0s.

A shared 20-bit synchronization phrase.

A unique 13-bit payload specifying the command.

For example, the Fan On/Off button's payload was:

0111110010000

As mentioned above, the logical bits were encoded as short OOK pulse patterns, with 1000 representing a logical 0 and 1110 representing a logical 1. Each pulse in those patterns was...

remote frequency home control carrier ceiling

Related Articles