Hampton Bay Fan RF/MQTT bridge

I have not solved the reception range issue. Even with a 1/4 wavelength antenna the reception is, at best, 5 feet from the remote. Transmission does not seem to be an issue. I’ve even experimented with one of those Bond Bridges but found out they only transmit (they do make a pro version now which looks like it is two way but costs $350) but they can learn remotes and transmit throughout a whole house. My next attempt will probably be a combination of an RTL-SDR and either the Bond or the cc1101 as just a transmitter.

Just to follow up on this real quick. I ordered 2x cc1101 boards. The first one was having issues with range; anything more than a few inches wouldn’t work.

In the process of doing some troubleshooting, i got the other one out and noticed much greater range. I’ve got this one working from 15 feet away. Haven’t tried to max out the range, but it’s def more than a few inches.

1 Like

I’ve tried two different boards but I can’t remember where I got them from. May I ask where you ordered yours from?

I got them off of amazon, this was going to be nothing more of a “hmm, wonder if this will work”, but now i’m off to bringing this fan into the 21st century.

https://www.amazon.com/dp/B01DS1WUEQ?psc=1&ref=ppx_yo2ov_dt_b_product_details

So a bit of an update. I started suspecting I may have fried something in the circuitry with the boards I had. Running them on 5V instead of 3V. I know dumb. Even though they transmitted fine the reception has never been good. So I bought new boards from the link below and a 9 inch quarter wave antenna meant for 315Mhz. So far the results are better from the tests I have done. The remotes are getting picked up from multiple rooms in the house with the unit in a central location. I am still working on the coding as it is quite difficult to ensure the states remain in sync when you are talking about using an RF remote, the HA iOS app, and a wall switch hooked to a Shelly 2.5.

Module: https://www.aliexpress.us/item/2251832619329110.html?spm=a2g0o.order_list.order_list_main.11.604f1802qON9eJ&gatewayAdapt=glo2usa&_randl_shipto=US

Antenna: https://www.aliexpress.us/item/3256802985171233.html?spm=a2g0o.order_list.order_list_main.5.604f1802qON9eJ&gatewayAdapt=glo2usa&_randl_shipto=US

I’m trying to get this up and running and seem to have the same model, if not a very similar model, remote that you have. How did you end up getting this to work? With the pin-out diagram from SmartRC the serial monitor stops working and uploads do as well so I’m flying blind as to what’s going on with the ESP8266 module. I can’t get the receive or send demos working and I’m completely at a loss. The only modicum of success I’ve had so far is when I boot up the module with it wired up to the CC1101 I get an “online” message from home/hamptonbay/status when listening for all MQTT messages in Home Assistant…I would love to hear how you were able to get this up and running as you may be my only hope lol

I’m working with a Wemoss D1 Mini V4.0, and my remote’s model number is TR220A.

I just came accross this searching! I just had to take apart my Hampton Bay fan remote to clean the buttons because some soda got spilled on it and they were sticky. But saw on the circuit board, its so simple, just the UC, a PIN selector and buttons with the RF antenea built into the PCB… Started to think about how to add an ESP32 to this…

While doing some research on the RF 303.9Mhz frequency that the fan uses, I came accross yoiur post and GIThub with all the info already done! LOL. Thank you for this!!! The GITHUB you posted shows the 303.9, but then the RF module in the requirements is a 315-900Mhz and seems like it mainly operates at 433Mhz. your code is for ESP8266, but I’ll just update it to port over for an ESP32… OR I just use up some of my old 8266 boards… But if this works, I’ll design my own PCB’s to plug and play all my fans (6) of them in each room. Since they are wifi, I just need to 3D print a box and run power to them. I may add a MMwave sensor if possible and temp sensor to it too, so I can automate to turn fan on to xx speed if room gets hotter then xx degrees and a the room is occupied. Awesome work on the feedback to be able to verify the fan setting actually set!

Did that board end up working for the RF? 10’ to the next room, was that the max you got, or was only able to test? I would like to build a single device for my house and put the sensor in a single location and then control each room via the PIN selection on the control. I have a 3800sqft house with 6 of the same Hampton Bay 303.9Mhz ceiling fans that are remote.

Is there any plans to update this to the new MQTT format in HA?

It looks like some of the properties are no longer available.

1 Like

Hello,

The Hampton Bay fans in my home use a remote with model number “HD3”. Looking up the FCCID: “2AAZPHD3” on this website:https://fccid.io/, it shows the frequency is 304.25MHz, which matched what I was seeing with my RTL-SDR using universal radio hacker.

I have been able to decipher the following protocol, and have it working on esphome using dbuezas’ cc1101 template: GitHub - dbuezas/esphome-cc1101

Below is the protocol for the Hampton Bay fans using the HD3 remote using esphome. I send 2 input_select variables to esphome from home assistant, device and command.

api:
  encryption:
    key: "key"
  services:
    - service: transmit
      variables: 
        command: string
        device: string
      then:
        - logger.log: "running script"
        - script.execute:
            id: transmit
            device: !lambda return device;
            command: !lambda return command;

script:
  - id: transmit
    mode: queued
    parameters:
      command: string
      device: string
    then:
      - if:
          condition:
            lambda: 'return ( device.substr(device.size() - 3) == "fan" );'            
          then:
            - lambda: get_cc1101(transceiver).setFreq(304.25);
            - lambda: get_cc1101(transceiver).beginTransmission();
            - remote_transmitter.transmit_rc_switch_raw:
                repeat:
                  times: 5
                  wait_time: 8.0ms

                code: !lambda |-
                        std::map<std::string, std::string> devices, commands;
                        devices["room1_fan"] = "0110";
                        devices["room2_fan"] = "1100";
                        devices["room3_fan"] = "0111";
                        devices["room4_fan"] = "0000";
                        devices["room5_fan"] = "1111";
                        commands["fan_high"] = "11111110";
                        commands["fan_medium"] = "11111101";
                        commands["fan_low"] = "11111100";
                        commands["fan_off"] = "11111111";
                        commands["off"] = "01100100";
                        commands["on"] =  "01100101";
                        commands["light_toggle"] = "11100100";
                        return ( "1000000000000" + id(devices[device]) + id(commands[command]) );

                protocol: 
                  pulse_length: 375
                  sync: [0,0]
                  zero: [2,1]
                  one: [1,2]
                  inverted: True
            - lambda: get_cc1101(transceiver).endTransmission();

#  Code breakdown:
#    - 13-bit preamble + 4-bit dip settings + 8-bit command
#      -  preamble:  1000000000000  (1x "1"-bit + 12x "0"-bits)
#      - dip settings - as seen in the remote - NOT inverted NOT reversed 
#      - 8-bit commands: 
#         - power on (light on, fan high): 01100101
#         - power off (light off, fan off): 01100100; 
#         - toggle light: 11100100
#         - fan high: 11111110
#         - fan medium 11111101
#         - fan low 11111100
#         - fan off 11111111; 

Again, a reminder if you have an HD3 remote, the frequency is 304.25MHz.

Also, I can control all 5 fans in my house with a single transmitter in the master bedroom - so it’s reaching about 40 ft with no issues - granted my home is only constructed with single wall wood.

I also have the same transmitter controlling my 433.83MHz string lights outside - with the
- lambda: get_cc1101(transceiver).setFreq(xxx.xx);
command prior to transmitting any signal. The same antenna handles both frequencies pretty well.

Hey,

I have the same remote, HD3, 2AAZPDH3 and 304.25MHz. I am trying to go through the process of going through capturing it with URH. Never done this before so I might just be missing something but I am not seeing the same patterns.

What I have captured for pressing the power button is:

11000100010001000100010001000100010001000100010001000101100010110001011011000100010110001011 [Pause: 8082 samples]
11000100010001000100010001000100010001000100010001000101100010110001011011000100010110001011 [Pause: 8083 samples]
1100010001000100010001000100010001000100010001000100010110001011000101101100010001011001011 [Pause: 8081 samples]
1100010001000100010001000100100010001000100010001000101100010110001011011000100010110001011 [Pause: 8078 samples]
110001000100010010001000100010001000100010001000100010110010110001011011000100010110001011 [Pause: 8081 samples]
11000100010001000100010001000100010001000100010001000101100010110001011011000100010110001011 [Pause: 8080 samples]
11000100010001000100010001000100010001000100010001000101100010110001011011000100010110001011 [Pause: 8078 samples]
11000100010001000100010001000100010001000100010001000101100010110001011011000100010110001011 [Pause: 8080 samples]
11000100010001000100010001000100010001000100010001000101100010110001011011000100010110001011 [Pause: 8079 samples]
11000100010001000100010001000100010001000100010001000101100010110001011011000100010110001011 [Pause: 845449 samples]

I’m don’t think I am seeing the 13 bits of preamble, I can find my dip setting of 0101, but looking before and after it don’t line up.

Any advice on decoding this?

the data is there.
if you define the ‘one bit’ as ‘011’ and the ‘zero bit’ as ‘0001’ and realize that there is actually a 0 before the first 11 that doesn’t show up, then this signal;

11000100010001000100010001000100010001000100010001000101100010110001011011000100010110001011

Translates to the following data bits:
1000000000000010101100101

So preamble is: 100000000000
Then the dip pin is 0101
And the command is 01100101, which is power on.

The only difference between your result and mine is your zero bit has 4 characters where mine was three “001”
Try leaving sample rate as default in URH.

2 Likes

I was wanting to get this to work, but I dont understand how the MQTT FAN changes work and so the original script will not work when setup of the config.yaml file from it. Have you got this working in HA??

Can you share the whole esp code to see how you control the buttons?
I have a similar fan but the light is dimmeable and there is a code for each light status, but i cant find the pattern yet

Received RCSwitch Raw: protocol=6 data=‘000000000000110001100’ only light at 96%
Received RCSwitch Raw: protocol=6 data=‘000000000000110101101’ with fan high
Received RCSwitch Raw: protocol=6 data=‘000000000000111001110’ with fan med
Received RCSwitch Raw: protocol=6 data=‘000000000000111101111’ with fan low

Received RCSwitch Raw: protocol=6 data=‘000000000110010001010’ only light at 50%
Received RCSwitch Raw: protocol=6 data=‘000000000110010101011’ with fan high
Received RCSwitch Raw: protocol=6 data=‘000000000110011001100’ with fan med
Received RCSwitch Raw: protocol=6 data=‘000000000110011101101’ with fan low

my esp code:

esphome:
  name: d1-rf
  friendly_name: D1 RF

  includes:
    - cc1101.h
  libraries:
    - SPI
    - "SmartRC-CC1101-Driver-Lib"

esp8266:
  board: d1_mini

# Enable logging

logger:

# Enable Home Assistant API

api:

ota:

wifi:
  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: true
  power_save_mode: HIGH

  # Enable fallback hotspot (captive portal) in case wifi connection fails
  ap:

    ssid: "D1-Rf Fallback Hotspot"
    password: "0dsO0HAYrNTw"

captive_portal:

sensor:
  - platform: custom
    lambda: |-
      auto my_sensor = new CC1101(
        D5, // SCK
        D6, // MISO
        D7, // MOSI
        D3, // CSN
        D1, // GDO0
        96.96, // bandwidth_in_khz
        303.90 // freq_in_mhz
      );
      App.register_component(my_sensor);
      return {my_sensor};
    sensors:
      id: transciver_1
      name: "RSSI"
      unit_of_measurement: dBm
      entity_category: diagnostic
  # you can have multiple transcivers in the same board
  # with more than 2, an esp8266 will get out of RAM. You'll need to remove the logger and reduce the recevier buffer size to 200
number:
  - platform: template
    max_value: 812
    min_value: 450
    step: 1
    mode: slider
    optimistic: true
    unit_of_measurement: "kHz"
    name: BW
    on_value:
      then:
        - lambda: get_cc1101(transciver_1).setBW(x);
  - platform: template
    min_value: 304.2
    max_value: 348
    #min_value: 378
    #max_value: 464
    # min_value: 799
    # max_value: 928
    step: .001
    mode: box
    optimistic: true
    unit_of_measurement: "MHz"
    name: FREQ
    on_value:
      then:
        - lambda: get_cc1101(transciver_1).setFreq(x);
switch:
  - platform: template
    name: "RSSI"
    entity_category: diagnostic
    lambda: return get_cc1101(transciver_1).rssi_on;
    turn_on_action:
      lambda: get_cc1101(transciver_1).rssi_on = true;
    turn_off_action:
      lambda: get_cc1101(transciver_1).rssi_on = false;
button:
  - platform: template
    name: Apaga Todo
    on_press:
      - lambda: get_cc1101(transciver_1).beginTransmission();
      - remote_transmitter.transmit_rc_switch_raw:
          code: '000000000000000000000'
          protocol: 6
          repeat:
            times: 3
            wait_time: 0s
      - lambda: get_cc1101(transciver_1).endTransmission();
  - platform: template
    name: Fan Off
    on_press:
      - lambda: get_cc1101(transciver_1).beginTransmission();
      - remote_transmitter.transmit_rc_switch_raw:
          code: '000000000110000000110'
          protocol: 6
          repeat:
            times: 3
            wait_time: 0s
      - lambda: get_cc1101(transciver_1).endTransmission();
  - platform: template
    name: Fan Low
    on_press:
      - lambda: get_cc1101(transciver_1).beginTransmission();
      - remote_transmitter.transmit_rc_switch_raw:
          code: '000000000110001101001'
          protocol: 6
          repeat:
            times: 3
            wait_time: 0s
      - lambda: get_cc1101(transciver_1).endTransmission();
  - platform: template
    name: Fan Med
    on_press:
      - lambda: get_cc1101(transciver_1).beginTransmission();
      - remote_transmitter.transmit_rc_switch_raw:
          code: '000000000110001001000'
          protocol: 6
          repeat:
            times: 3
            wait_time: 0s
      - lambda: get_cc1101(transciver_1).endTransmission();
  - platform: template
    name: Fan High
    on_press:
      - lambda: get_cc1101(transciver_1).beginTransmission();
      - remote_transmitter.transmit_rc_switch_raw:
          code: '000000000110000100111'
          protocol: 6
          repeat:
            times: 3
            wait_time: 0s
      - lambda: get_cc1101(transciver_1).endTransmission();
remote_transmitter:
  - pin: D1 # This is GDO0
    carrier_duty_percent: 100%
remote_receiver:
  - pin: D1 # This is GDO0
      # on the esp8266 use any of D1,D2,D5,D6,D7,Rx
      # Don't use D3,D4,D8,TX, boot often fails.
      # Can't be D0 or GPIO17 b/c no interrupts
    dump:
      - rc_switch
    # Settings to optimize recognition of RF devices
    tolerance: 50%
    filter: 250us
    idle: 4ms
    buffer_size: 2kb

@jbrande
Not sure whose yaml you wanted, but I added a bit more to my post so you can see how I tackled the issue.
I didn’t use buttons. I defined a service in esphome which is callable from inside HA with the parameters of ‘device’ and ‘command’. I then just call that service either in automations or with the UI.

@jbrande
Another note - with your receiver tolerance set at 50%, there is a very good chance your remote is not rc_switch protocol 6.
I recommend buying an rtl-sdr to analyze the signal before trying to duplicate it with a cc1101. It’ll save a lot of guesswork.

Just wanted to say thanks, this post pointed me in the right direction, and I finally got it working. SDR + URH + ESPHOME + dbuezas’ cc1101 template is the way. URH is really cool.

I made a more analog version of this. By means of slaving the remote to an ESP32 and adding a light sensor for the light sync check.



substitutions:
  device_name: 'mbr-fan-remote'
  friendly_name: mbr-fan-remote
  device_comment: "located in the bedroom"
  entity_prefix: "MBR"

packages:
  core: !include _config/core.yaml

esphome:
  name: mbr-fan-remote
  friendly_name: mbr-fan-remote
  on_boot:
    priority: 800
    then:
      - script.execute: check_light_sync

      
esp32:
  board: esp32dev
  framework:
    type: arduino

api:
  encryption:
    key: "kBt+Rva8i/RSsJNYNx+wnM5MhdGnfX8oEIcK0OcJe7o="

logger:
  level: DEBUG

i2c:
  sda: GPIO16
  scl: GPIO17
  scan: True

sensor:
  - platform: bh1750
    id: light
    device_class: illuminance
    state_class: measurement
    internal: True
    address: 0x23
    update_interval: 500ms
    accuracy_decimals: 1

output:
  - platform: template
    id: fanoutput
    type: float
    write_action:
      - lambda: ""
  - platform: gpio
    pin: 
      number: GPIO25
      inverted: true
      mode: OUTPUT_OPEN_DRAIN
    id: light_out
  - platform: gpio
    pin: 
      number: GPIO14
      inverted: true
      mode: OUTPUT_OPEN_DRAIN
    id: reverse_out
  - platform: gpio
    pin: 
      number: GPIO23
      inverted: true
      mode: OUTPUT_OPEN_DRAIN
    id: fan1_out
  - platform: gpio
    pin: 
      number: GPIO18
      inverted: true
      mode: OUTPUT_OPEN_DRAIN
    id: fan2_out
  - platform: gpio
    pin: 
      number: GPIO19
      inverted: true
      mode: OUTPUT_OPEN_DRAIN
    id: fan3_out
  - platform: gpio
    pin: 
      number: GPIO21
      inverted: true
      mode: OUTPUT_OPEN_DRAIN
    id: fan4_out
  - platform: gpio
    pin: 
      number: GPIO27
      inverted: true
      mode: OUTPUT_OPEN_DRAIN
    id: fan5_out
  - platform: gpio
    pin: 
      number: GPIO26
      inverted: true
      mode: OUTPUT_OPEN_DRAIN
    id: fan6_out
  - platform: gpio
    pin: 
      number: GPIO22
      inverted: true
      mode: OUTPUT_OPEN_DRAIN
    id: fan0_out

switch:
  - platform: template
    id: light_toggle
    name: Fan Light
    optimistic: True
    turn_on_action:
      - button.press: btn_light
      - delay: 1s
      - script.execute: check_light_sync
    turn_off_action:
      - button.press: btn_light
      - delay: 1s
      - script.execute: check_light_sync      

fan:
  - platform: speed
    output: fanoutput
    id: mbr_fan
    name: "Master Bedroom Ceiling Fan"
    speed_count: 6
    on_turn_off:
      - lambda: |-
          id(btn_fan0).press();
    on_speed_set:
      - lambda: |-
          if      (id(mbr_fan).state == 0) { /* Fan is off, do nothing */ }
          else if (id(mbr_fan).speed == 1) { id(btn_fan1).press(); }
          else if (id(mbr_fan).speed == 2) { id(btn_fan2).press(); }
          else if (id(mbr_fan).speed == 3) { id(btn_fan3).press(); }
          else if (id(mbr_fan).speed == 4) { id(btn_fan4).press(); }
          else if (id(mbr_fan).speed == 5) { id(btn_fan5).press(); }
          else if (id(mbr_fan).speed == 6) { id(btn_fan6).press(); }

button:
  - platform: output
    id: btn_light
    internal: False
    output: light_out
    duration: 250ms
  - platform: output
    id: btn_reverse
    name: Switch Direction
    internal: False
    output: reverse_out
    duration: 250ms  
  - platform: output
    id: btn_fan1
    internal: True
    output: fan1_out
    duration: 250ms  
  - platform: output
    id: btn_fan2
    internal: True
    output: fan2_out
    duration: 250ms  
  - platform: output
    id: btn_fan3
    internal: True
    output: fan3_out
    duration: 250ms     
  - platform: output
    id: btn_fan4
    internal: True
    output: fan4_out
    duration: 250ms  
  - platform: output
    id: btn_fan5
    internal: True
    output: fan5_out
    duration: 250ms  
  - platform: output
    id: btn_fan6
    internal: True
    output: fan6_out
    duration: 250ms 
  - platform: output
    id: btn_fan0
    internal: True
    output: fan0_out
    duration: 250ms 

script:
  - id: check_light_sync
    mode: single
    then:
      - if:
          condition:
            switch.is_on: light_toggle
          then:
            if:  
              condition:
                - lambda: 'return id(light).state < 5;'
              then:
                - button.press: btn_light
                - logger.log: "The light was off, turning ON"
                - delay: 2s   
              else:
                - logger.log: "The light was already ON"
                - delay: 2s    
          else:
            if:  
              condition:
                - lambda: 'return id(light).state > 5;'
              then:
                - button.press: btn_light
                - logger.log: "The light was on, turning OFF"
                - delay: 2s   
              else:
                - logger.log: "The light was already OFF"
                - delay: 2s

Hi Ryan, do you have any guide on how to use URH to find the signal modulation? I think I have the same remote type or similar you do by the way. If I hold down the light button it allows me to dim the light on the fan.

Hello @implicit_none , im a complete newbie.
I have 3 hampton bay fans, can you share your esphome with me plesse?
i only see a partial code on this thread.
thanks
Miguel