Project

How to Add Bluetooth to a Ceiling Fan

September 25, 2015 by Travis Fagerness

How to add BLE capabilities to a wireless ceiling fan by incorporating the nRF51 in the remote control. Part of our series on the nRF51.

How to add BLE capabilities to a wireless ceiling fan by incorporating the nRF51 in the remote control.

Overview

This is part of a series of articles on the nRF51. The nRF51 is a system-on-chip with a Cortex M0 and a BLE radio chip all in one. This article outlines my experiences using a custom peripheral service to add BLE capability to a wireless ceiling fan.

Previous articles: 
BLE using nRF51: ARM-GCC Build Environment
 

Requirements

  • Device that has the nRF51
  • Mobile app based on tutorial here.
    • Used in article: Android v5.1.1
  • Wireless ceiling fan remote
  • NMOS transistors
  • Small gauge wire
  • The toolchain setup in the previous article.

BLE in a Ceiling Fan

The premise of this article is adding BLE to a ceiling fan so that it can be controlled by a mobile device. There are a few ways you could do this:

  1. Add a BLE capable controller to the inside of the outlet box. This would only allow you to turn the fan on and off. It would require working with mains electricity.
  2. Add a BLE controller inside the fan itself and tap into the circuit board in the fan. This would require a lot of work getting the fan down and modifying the circuitry. The voltage is likely high as well.
  3. Modify a ceiling fan that already has wireless capability. Usually these fans have some sort of remote and work with momentary switches. You can simulate these momentary switches with transistors connected to a separate microcontroller.

This article will focus on the third option.

Reverse Engineering the Remote

The remote my ceiling fan uses was made by Emerson. I have no idea where it came from because it came with the house. I can tell it operates on the 300MHz band from the label, so I won't have to worry about it interfering with BLE at all. It also runs at around 6V from 4-AAA batteries, which I can use to also power the nRF51. 

 

 

I opened the device and found a radio, some buttons and some sort of microcontroller or encoder.

It seems that the easiest way to hack into the circuit is through the buttons. I can simulate button presses with the nRF51. The advantage of this method is that I can still use the normal buttons. The goal with investigating the circuit is to figure out how the button presses are translated into commands that are transmitted over the air. Typically buttons are just pulling a voltage high or low. I followed the traces from ground and found that all of the buttons connected to ground. This is a good indicator that the buttons are pulling the voltage low when pressed. I used a multimeter to measure the voltage on the button trace that feeds the microcontroller. It was connected to the positive supply voltage, which is around 6V. I can't connect that directly to the nRF51 or it will damage it. I have to use NMOS transistors to pull the voltage low. The transistors need to have a VGS on threshold that is lower than 1.8V, a VDSmax that is higher than 6V, and that the current flowing through the button circuit is less than IDmax. 

Modifying the Remote

I soldered the transistors and some wires to the circuit so that the drain was connected to the MCU lines, the source to ground, and the gate to the wires. I then soldered the wires to the nRF51 GPIO pins. To power the nRF51, I needed to drop the voltage a little bit since the onboard regulator only can handle 5.5V. I used two regular diodes in series, which gave me about a 1V drop. This 1V drop isn't going to be the same in all situations, but it gives enough margin below 5.5V to be reasonable. You could also solder some other regulator on the board if you had one lying around. The diodes also offer reverse polarity protection for the nRF51 in case someone puts the batteries in backwards.

 

After programming and testing, I taped up the nRF51 to prevent shorts and put it inside the case. It just fit up towards the radio.

Modifying the BLE Peripheral Code

The code is nearly identical from before except this time something is done with the data. I also put the MCU to sleep when not in use and lowered the advertising rate to save battery life. I used the gpio library from the SDK to control the lines. I found that holding the button pin low for 100ms worked well to simulate a button press from a person.

ble_nordic_ceiling_fan_app.zip


#include "nrf_gpio.h"

#define BUTTON_UP_PIN                    15
#define BUTTON_DOWN_PIN                  16
#define BUTTON_HIGH_PIN                  17
#define BUTTON_MED_PIN                   18
#define BUTTON_LOW_PIN                   19
#define BUTTON_OFF_PIN                   20
#define BUTTON_PRESS_TIME_MS             100

#define DEVICE_NAME                      "Den Fan"                               /**< Name of device. Will be included in the advertising data. */
#define APP_ADV_INTERVAL                 1600                                        /**< The advertising interval (in units of 0.625 ms.)*/
#define APP_ADV_TIMEOUT_IN_SECONDS       0                                         /**< The advertising timeout in units of seconds. */

Immediately on power up, I set the pins to outputs and held them low. Normally you would put pull-down resistors on the NMOS gate to prevent unwanted activations, but I didn't want to solder 6 small resistors on the board. It's not a big deal to me if a button gets pressed when the batteries are first put in.


static void gpio_init(void)
{
    nrf_gpio_cfg_output(BUTTON_UP_PIN);
    nrf_gpio_cfg_output(BUTTON_DOWN_PIN);
    nrf_gpio_cfg_output(BUTTON_HIGH_PIN);
    nrf_gpio_cfg_output(BUTTON_MED_PIN);
    nrf_gpio_cfg_output(BUTTON_LOW_PIN);
    nrf_gpio_cfg_output(BUTTON_OFF_PIN);
    nrf_gpio_pin_clear(BUTTON_UP_PIN);
    nrf_gpio_pin_clear(BUTTON_DOWN_PIN);
    nrf_gpio_pin_clear(BUTTON_HIGH_PIN);
    nrf_gpio_pin_clear(BUTTON_MED_PIN);
    nrf_gpio_pin_clear(BUTTON_LOW_PIN);
    nrf_gpio_pin_clear(BUTTON_OFF_PIN);
}

I modified write event to only toggle the pins if the charcteristic matches the custom BLE peripheral.


        case BLE_GATTS_EVT_WRITE:
            DEBUG_PRINTF("BLE_GATTS_EVT_WRITE");
            for(i=0;i < p_ble_evt->evt.gatts_evt.params.write.len;i++) DEBUG_PRINTF("Data %d: 0x%x",i,p_ble_evt->evt.gatts_evt.params.write.data[i]);
            if (p_ble_evt->evt.gatts_evt.params.write.handle == periph_handles.char1_handles.value_handle)
            {
                on_custom_ble_write(p_ble_evt);
            }

I then had to write a new function to toggle the pin when a BLE write event occurs. I set it up to be generic so the button number can be modified on the Android app.


static void on_custom_ble_write(ble_evt_t * p_ble_evt)
{
    DEBUG_PRINTF("on_custom_ble_write");
    if(p_ble_evt->evt.gatts_evt.params.write.len>0)
    {
        if(p_ble_evt->evt.gatts_evt.params.write.data[0] >=0 && p_ble_evt->evt.gatts_evt.params.write.data[0] <= NUMBER_OF_PINS)
        {
            DEBUG_PRINTF("Toggle pin %d for %d ms",p_ble_evt->evt.gatts_evt.params.write.data[0],BUTTON_PRESS_TIME_MS);
            nrf_gpio_pin_set(p_ble_evt->evt.gatts_evt.params.write.data[0]);
            nrf_delay_ms(BUTTON_PRESS_TIME_MS);
            nrf_gpio_pin_clear(p_ble_evt->evt.gatts_evt.params.write.data[0]);
        }
    }
}

Android App

The Android app builds on the previous article. I added several buttons for each ceiling fan button.

I didn't have enough outputs to do the REV button, so I left that out. I then had to add functions for each button. They're the same as the char_write function from before, except this time they write the pin value for the desired button press.


    public void onClickUpLight(View v){
        if(mBluetoothLeService != null) {
            mBluetoothLeService.writeCustomCharacteristic(15);
        }
    }
    public void onClickLowLight(View v){
        if(mBluetoothLeService != null) {
            mBluetoothLeService.writeCustomCharacteristic(16);
        }
    }
    public void onClickHi(View v){
        if(mBluetoothLeService != null) {
            mBluetoothLeService.writeCustomCharacteristic(17);
        }
    }
    public void onClickMed(View v){
        if(mBluetoothLeService != null) {
            mBluetoothLeService.writeCustomCharacteristic(18);
        }
    }
    public void onClickLow(View v){
        if(mBluetoothLeService != null) {
            mBluetoothLeService.writeCustomCharacteristic(19);
        }
    }
    public void onClickOff(View v){
        if(mBluetoothLeService != null) {
            mBluetoothLeService.writeCustomCharacteristic(20);
        }
    }

BluetoothLeGatt_1.zip

Trying it Out

What else?

The nRF51 can be interfaced to other devices in a variety of ways. For example, you could use it as a sensor with the ADC, or control another device through I2C. You can also control many devices such as the one in this article with just GPIO! Go out and try adding BLE to something in your house!

Give this project a try for yourself! Get the BOM.

3 Comments
  • curio October 04, 2015

    wow the bluetooth fan is awesome!!!!!!!!!

    Like. Reply
  • Jean-Marc Beaujour August 03, 2016

    Great stuff!
    Is it a 2 way communication, i.e is it possible to display the status of the fan (light on/off, fan speed Low/Med/Hi?) on the cellphone.

    Like. Reply
  • Doktor Jones January 13, 2017

    @Jean-Marc I doubt it, since the remote has no way of knowing the state of the fan. You could try to guess it, but if someone used the remote directly instead of the app (or the controls on the fan itself) the app would have no way of knowing and would show the incorrect state.

    Like. Reply