Home Automation

My ongoing project to instrument and automate my home

Controlling a Hampton Bay Ceiling Fan with Home Assistant

Original Version: 2024-March-21

Background

We live in a split-entry home. Our basement family room has a pellet stove. In the winter, the pellet stove heats the room up nicely. I would like to be able to move some of the warm air into the basement foyer where it would naturally rise to the first floor.

The first part of the solution was to install a fan in the doorway. This fan is controlled by a smart switch and a thermostatic control on the home assistant. Details can be found in the blog post “Door Fan to Move Warm Air From Basement to First Floor.”

The second part of moving the air around is to turn on the ceiling fan in the family room. We have been doing it manually (via the remote on the wall). It really isn’t much trouble, but we turn it off when we go to bed and I’d like it to continue to run until the room is cooled down, without continuing to run all night. So, how to get the home assistant to be able to turn the fan on and off.

A Universal Remote?

I purchased a universal remote (Broadlink RM4 Pro). It was advertised to be able to replace both RF and IR remotes. In trying to clone my fan’s remote control, I found that the Broadlink did not support the frequency that my remote uses (303.85 MHz).

Craft My Own Remote?

An Internet search uncovered a couple of projects that used an ESP8266 to communicate with a CC1101 transmitter. These are the two projects that offered the most help:

Hampton Bay Fan MQTT

Ceiling Fan Remote Hacking

My Journey

The biggest problem I had with the circuit was the pinouts for the CC1101. There are many variants of this little board. Some of them have eight pins and others have ten. They all seem to have slightly different pinouts. My board came with no documentation and I tried pinouts from boards that looked similar from the Internet. It took me several tries, but eventually I was able to get the circuit to recognize the signal from my remote control!

This is what my board looks like:

… and the pinout looks like this (note, this is the view from the back … pin #1 is in the lower-left in the photo and the lower-right in this pinout diagram):

This is what the ESP8266 looks like:

… and the pinouts for it are just like those documented in the links above:

CC1101ESP8266 D1 Mini
GNDG (GND)
VCC3V3 (3.3V)
GDO0D1 (GPIO5)
CSND8 (GPIO15)
SCKD5 (GPIO14)
MOSID7 (GPIO13)
MISOD6 (GPIO12)
GD02D2 (GPIO4)

I found the frequency of my remote by doing an internet search for the FCC page and the FCC id from the back of the remote (CHQ8BT7096T) … 303.85. After setting up WiFi, MQTT settings, and the frequency of my remote, the code from the “Hampton Bay Fan MQTT” project compiled and uploaded to the ESP8266 perfectly.

I used many of the ideas from the “Ceiling Fan Remote Hacking” project to create a control matrix on my home assistant dashboard to control the fan and light. Unfortunately, the fan just wouldn’t respond to any command. I wondered if the board was even transmitting.

Before trying to obtain an SDR to determine if there was actually RF being generated by my circuit, I added some debugging code to display the message being sent by my remote control.

Remote Control Code Details

Ben Owen documented the data his code would send as follows:

  • RF Protocol 6
  • Message length 21 bits
  • Format of the message
    • 000xxxx000000yzz00000
    • Where:
      • xxxx is a value based on the DIP switch settings assigned to the remote/fan combination
        (check Mr. Owen’s code for details about the conversion of DIP setting to message bits)
      • y is light on/off
      • zz indicates fan speed

The messages I was decoding had more information in some of the places that Mr. Owen indicated only 0’s.

  • The Format of my message
    • 000xxxxaaaaaaazzbbbbb
    • Where:
      • xxxx was the same as Mr. Owen’s documentation
      • aaaaaaa was some indication of light intensity.
        I have not, yet, been successful at decoding this value, but 0000001 and 0000000 work for 100% on and off, so I can turn the light on and off, even if I don’t, yet, know how to dim it.
      • zz was the same as Mr. Owen’s documentation
      • bbbbb I determined to be a checksum
        It is calculated by summing each 4-bit chunk from the left-most 16 bits and recording the remainder of a division by 16 in the message bits.

Here is the function I added to calculate this checksum:

int calculateChecksum(int number) {
  int sum = 0;

  while (number > 0) {
    sum += number & 0xF;
    number >>= 4;
  }

  return sum % 16;
}

In the transmitState function, I had to change the line of code that constructs rfCode to call and add the checksum, thus:

int rfCode = dipToRfIds[fanId] << 9 | fans[fanId].lightState << 2 | fanRf;
rfCode = rfCode << 5 | calculateChecksum(rfCode);

Connecting to Home Assistant

I created a card on a home assistant dashboard using documentation from the “Ceiling Fan Remote Hacking” link, above. Here is the code for it:

square: true
columns: 5
title: Family Room Fan
type: grid
cards:
  - show_name: false
    show_icon: true
    type: button
    tap_action:
      action: call-service
      service: fan.toggle
      data: {}
      target:
        entity_id: fan.family_room_fan
    entity: fan.family_room_fan
    icon: mdi:ceiling-fan
    show_state: true
    hold_action:
      action: none
  - show_name: false
    show_icon: true
    type: button
    tap_action:
      action: call-service
      service: light.toggle
      data: {}
      target:
        entity_id: light.family_room_fan_light
    entity: light.family_room_fan_light
    icon: mdi:ceiling-fan-light
    hold_action:
      action: none
    show_state: true
  - show_name: true
    show_icon: true
    type: button
    tap_action:
      action: call-service
      service: fan.set_preset_mode
      data:
        preset_mode: low
      target:
        entity_id: fan.family_room_fan
    entity: ''
    icon: mdi:fan-speed-1
    hold_action:
      action: none
  - show_name: true
    show_icon: true
    type: button
    tap_action:
      action: call-service
      service: fan.set_preset_mode
      data:
        preset_mode: medium
      target:
        entity_id: fan.family_room_fan
    entity: ''
    icon: mdi:fan-speed-2
    hold_action:
      action: none
  - show_name: true
    show_icon: true
    type: button
    tap_action:
      action: call-service
      service: fan.set_preset_mode
      data:
        preset_mode: high
      target:
        entity_id: fan.family_room_fan
    entity: ''
    icon: mdi:fan-speed-3
    hold_action:
      action: none

Now I could control the fan using my smartphone or web interface. Time to add the automation that prompted this whole thing.

The thermostat card for the door fan worked they way I wanted, so I created two automations to send the proper MQTT messages when the door fan turned on and off.

Turn on the ceiling fan when the door fan turns on:

alias: Family Room Ceiling Fan - on to cool room
description: ""
trigger:
  - platform: device
    type: turned_on
    device_id: 8d672c3ce4df60dbc1a41b4226574bd4
    entity_id: 7dc2667ebdf9eea1dacdb57d9eca16d4
    domain: switch
condition: []
action:
  - service: fan.turn_on
    target:
      entity_id: fan.family_room_fan
    data: {}
mode: single

Turn off the ceiling fan when the door fan turns off:

alias: Family Room Ceiling Fan - off when cooled
description: ""
trigger:
  - platform: device
    type: turned_off
    device_id: 8d672c3ce4df60dbc1a41b4226574bd4
    entity_id: 7dc2667ebdf9eea1dacdb57d9eca16d4
    domain: switch
condition: []
action:
  - service: fan.turn_off
    target:
      entity_id:
        - fan.family_room_fan
    data: {}
mode: single

To Do

I will wait until after heating season to clean up the implementation.

  • Solder the two boards together
    Currently I have a breadboard with the ESP8266 on it and some breadboard jumper wires.
  • 3D print a box to house the electronics
    There are a couple of projects on thingiverse.com that I will either make as-is or modify if necessary.

Leave a Reply

Your email address will not be published. Required fields are marked *