In my house, there are 3 of the same type of fan. These fans do not seem to branded/common, nor are the remotes, which are stuck in the wall. I found myself wanting to control these fans remotely, both for the purposes of convenience and energy savings. To do this, I will need to find a way, or develop a solution to connect these "dumb" fans to the IoT, with a focus on local control.
In order to control the fan using our own means, we must first identify how the fan is controlled normally.
With a quick Google Lens search and visual inspection, we can identify that it is highly likely this fan is controlled using RF. Typically, for appliances like fans, this can be in the frequency range of anywhere from 300MHz to 500MHz.
To identify the exact frequency, l used an RTL-SDR module, which is a cheap and cost effective way to inspect RF at the sub-GHz level. Using the software SDR++, I cycled through the most common frequencies while pressing buttons on the remote. Eventually, I identified the remote communicating through 303.84MHz, a very odd frequency, which we'll have to deal with later.
Now that we have identified the actual frequency, we will have to actually figure out the message the fan is sending.
RF communications are not much different from wired digital communications. Devices talk to each other in binary, using a sequence of ones and zeroes. The significance of those bits carry different meanings depending on the implementation, but one of the most important things is how those ones and zeroes are represented over radio waves. There are two commons ways that appliances do this:
In Amplitude Shift Keying (ASK), each symbol in the message signal gives a unique amplitude to the carrier wave.
This is the method used by my fans.
In Phase Shift Keying (PSK), each symbol in the message signal gives a unique phase shift to the carrier wave. This type of modulation is more resistant to RF noise.
One of my big goals for this project was to obviously develop a solution that was economical, logical (no giant transceivers), and compact. There is no point to DIYing something if it is more expensive and less useful than something you can just buy off Amazon. Fortunately, the kind of hardware you need for this is not too expensive nowadays.
For this set up, I chose an Arduino Uno R4 Wi-Fi for about $30, and for the actual transmission I bought a 2x set of CC1101 sub-GHz transceivers for about $15. That makes the cost of this whole project (not including my time) about $45, which is a good $70 less than something like the BOND bridge.
After quickly modifying the rc-switch library ReceiveDemo_Advanced example to initialize our sub-GHz radio, we are able to see that when we press the light button on the remote, the library will demodulate the radio signal into binary code, and will go into detail about it's pulse length and protocol.
To quickly test this, I utilized the SendDemo example to quickly input the signal's parameters and parrot it back out using the CC1101 module, and sure enough, and sure enough, my uncomfortably bright light turned on overhead.
I then recorded the data from all the buttons on the remote, and was quickly able to notice a pattern, which is that the signal being sent is a 16-bit fan ID, followed by a 8-bit command. This pattern is backed up by looking at signals from other fans, with all their commands being identical but the IDs being different.
We can use this to our advantage to simplify our code and the recording process, as I only need to get one signal from a fan, and we can send out any assortment of commands based on combining the fan's ID and the command we want to issue.
Now that I had a proof of concept that worked, it was time to actually develop the software that I would run on this board day and night. I started off with a simple Serial CLI that would send out a command depending on what I sent to the board over Serial.
I started by creating a couple of functions, one to decode incoming radio transmissions and put it into information I store in the corresponding fan struct, and one to assemble outgoing radio transmissions using the fan's information and the requested command.
I also configured the board to actively listen for radio transmissions when it is not transmitting commands. This will allow me to (semi-)sync the Arduino's state to the state of the fan, as it will update the fan struct whenever it hears the dumb remote's signal.
After I had that working, I moved to implement MQTT functionality into the code, which would allow me to integrate the Arduino board into a standard smart home solution. If implemented right, it should just show up like a normal "smart" ceiling fan might show up.
Obviously, I am not going to sit and send serial commands over USB every time I want to control my fans, and it needs to be something a little more seamless.
This is where Home Assistant comes in. HA is a very common piece of software among tinkerers and smart home power users alike because it allows you to pretty easily bring everything together under one app/platform, which means less apps and less annoyances when trying to program automations.
To bring the Arduino and Home Assistant together, I used the Mosquitto MQTT broker plugin for Home Assistant, and configured it to subscribe to the Arduino's state topics. After some tweaking, it came together, and the Arduino was exposed on the Home Assistant interface like any other device would be. My favorite part about this implementation is that it will also update the state in real time if the dumb remote is pressed, since the Arduino is listening.
In conclusion, I thoroughly enjoyed just about every minute on this project. I have been using it constantly for just a little over two months as of writing and everything works. I did discover some problems with my implementation that I saw coming:
The controller will sometimes lose state with the fans direction and light.
Because the light can be on or off, and the controller can only send a toggle signal, it is impossible to know what state the light is in, short of just implying. I have seen some DIYers fix this by placing another Arduino in the room as a light sensor, but this is too involved for what I need.
The Arduino will register held button as button being pressed over and over, which changes the state on the Arduino but will just dim on the fan.
Despite these issues, I think this solution works perfectly for automating the fan and allowing me to control it from the bed for maximum laziness or away from home.