Daniel P. Gross

Building a battery-powered wireless sensor using Bluetooth Low-Energy (BLE) and Apple HomeKit (Part 3)

This is the third post in a series about building a simple wireless temperature and humidity sensor using inexpensive components, Bluetooth Low Energy (BLE) for communication, and Apple HomeKit to read sensor values. For the previous post, see Part 2.

In the previous post, we covered the hardware selection, hardware assembly, and software architecture. In this post, we'll cover the last, crucial piece needed to make everything work: the software implementation.

This post includes the source code and all the information you need in order to get your own sensor and bridge hardware running.

Source code repository

The source code for this project can be found on GitHub at danielpgross/homekit-ble-temp-humid-sensor.

If you'd like to follow along with the instructions using your own bridge and sensor hardware, you'll want to start by cloning that repository to a local place.

Sensor module

The sensor module runs on the WRL-13990 Bluetooth module, which is built around the nRF52832. Its purpose is to take readings from the temperature/humidity sensor and relay those readings back to the HomeKit bridge (see the bridge module below).

Implementation discussion

The nRF52832 is a system-on-a-chip (SoC) built around the ARM Cortex M4 processor. Nordic provides a software development kit (SDK) that allows developers to write C code to program the device. Additionally, the chip is programmed by default with a "softdevice" that manages the fine details of BLE communication, allowing the developer to focus on the business logic of the application at hand.

This diagram from the S132 SoftDevice specification illustrates the general architecture of software implementations on the nRF52832:

For more information on the nRF52832's software, check out the following resources:

The nRF52832 can be very tricky to program because there's very little transparency into what's going on inside the device. Although it's possible to set up a line-by-line debugger, this requires extra hardware, as far as I was able to find. Therefore, programming the devices requires lots of patience :-)...

Making the SDK code build and run on the WRL-13990

Nordic's SDK code is written to support different boards built on the nRF52832, but not including the WRL-13990. This meant that some wrangling was necessary to get the code to build and run properly on the WRL-13990. Luckily, I was able to get this working by cobbling together other pieces of code that I knew worked on the WRL-13990, including the nRF5 Arduino core code.

Temperature readings

The nRF5 SDK does not natively support reading data from the DHT-22 sensor. This turned out to be more complicated than I thought, but I fortunately stumbled across Gabor Paller's blog post on using the DHT-22 with an nRF51822. He wrote a complete implementation for reading DHT-22 sensor values and I was able to use this directly in my own implementation.

BLE broadcast format

The sensor module broadcasts readings with the BLE service ID 7B18 and with the actual temperature and humidity readings in the BLE GAP service data. Using this approach, a listening device does not need to pair with the sensor, it just needs to look for the appropriate service ID and then read the value of the service data. To see exactly how the temperature and humidity are encoded, see the decoding implementation.

Using it yourself

The following is copied from the README file in the source code repository. Please see that file to keep up to date with any changes.

This is firmware code for the nRF52832. To use it, you need to compile it to binary code and then flash it to your device.

Requirements

Compilation & flashing

  1. Download the nRF5 SDK version 11.0.0.

    wget https://developer.nordicsemi.com/nRF5_SDK/nRF5_SDK_v11.x.x/nRF5_SDK_11.0.0_89a8197.zip
  2. Unzip the SDK.

    unzip nRF5_SDK_11.0.0_89a8197.zip
  3. Navigate to this path within the unzipped SDK folder: examples/ble_peripheral/.

    cd nRF5_SDK_11.0.0_89a8197/examples/ble_peripheral/
  4. There, copy the entire folder containing this file, with the directory name ble_app_temp_humid.

    cp -R /path/to/repo/sensor ./ble_app_temp_humid
  5. Build the code.

    cd ble_app_temp_humid/wrl13990/s132/armgcc
    make

    You should see build output as follows:

    rm -rf _build
    echo  Makefile
    Makefile
    mkdir _build
    Compiling file: app_button.c
    Compiling file: app_error.c
    Compiling file: app_error_weak.c
    Compiling file: app_fifo.c
    Compiling file: app_timer.c
    Compiling file: app_trace.c
    Compiling file: app_util_platform.c
    Compiling file: fstorage.c
    Compiling file: nrf_assert.c
    Compiling file: nrf_log.c
    Compiling file: retarget.c
    Compiling file: nrf_delay.c
    Compiling file: nrf_drv_common.c
    Compiling file: nrf_drv_gpiote.c
    Compiling file: pstorage.c
    Compiling file: bsp.c
    Compiling file: bsp_btn_ble.c
    Compiling file: main.c
    Compiling file: dht22.c
    Compiling file: RTT_Syscalls_GCC.c
    Compiling file: SEGGER_RTT.c
    Compiling file: SEGGER_RTT_printf.c
    Compiling file: ble_advdata.c
    Compiling file: ble_advertising.c
    Compiling file: ble_bas.c
    Compiling file: ble_bps.c
    Compiling file: ble_conn_params.c
    Compiling file: ble_dis.c
    Compiling file: ble_srv_common.c
    Compiling file: device_manager_peripheral.c
    Compiling file: system_nrf52.c
    Compiling file: softdevice_handler.c
    Assembly file: gcc_startup_nrf52.s
    Linking target: nrf52832_xxaa_s132.out
    Preparing: nrf52832_xxaa_s132.bin
    Preparing: nrf52832_xxaa_s132.hex
    
      text     data     bss     dec     hex filename
      9332      124     768   10224    27f0 _build/nrf52832_xxaa_s132.out
  6. Connect the WRL13990 to your computer and begin flashing the compiled firmware. I used the following command to accomplish that using a FT232RL-based USB-to-serial board.

    nrfutil dfu genpkg --application _build/*.hex pkg.zip && nrfutil dfu serial --package=pkg.zip --port=/dev/cu.usbserial-00000000 --baudrate=38400

    With the following output.

    Zip created at pkg.zip
    Upgrading target on /dev/cu.usbserial-00000000 with DFU package /Users/danielgross/Desktop/ard-temp-sensor/nrf52/nRF5_SDK_11.0.0_89a8197/examples/ble_peripheral/ble_app_temp_humid/wrl13990/s132/armgcc/pkg.zip. Flow control is disabled.
      [####################################]  100%          
    Device programmed.

Bridge module

The bridge module can run on any device capable of Bluetooth and wifi communication, as well as running node.js. The bridge's purpose is to receive the temperature readings broadcast by the sensor module and retransmit those over the HomeKit protocol.

Implementation discussion

HomeKit specification

Until earlier this year, the details of the HomeKit specification have been kept private by Apple and only made available to members of the MFi program. Since the MFi program requires submitting an application and paying a membership fee, this information was not available to hobbyists looking to play with HomeKit devices. Fortunately, however, earlier this year Apple made the HomeKit specification available for personal, non-commercial use. The HomeKit specification can now be accessed for free after creating an Apple Developer account and agreeing to use the documentation only for non-commercial purposes.

HomeKit framework

As discussed in the first post of this series, I decided to use HAP-NodeJS, a node.js-based implementation of the HomeKit protocol. Since it already implements the details of the HomeKit protocol, implementing our sensor bridge becomes as simple as listening for the BLE broadcasts from the sensor and then calling the appropriate HAP-NodeJS APIs.

HAP-NodeJS is meant to run as a single server instance as opposed to a separate instance for each accessory. Therefore, the accessory code for this module is written to be integrated into a HAP-NodeJS instance as opposed to standing on its own.

BLE framework

To listen for BLE broadcasts, I used the noble module, which was the only complete node.js BLE implementation I could find. noble is well designed and generally easy to work with, although as of this writing it does not yet support the latest version of macOS.

Using it yourself

The following is copied from the README file in the source code repository. Please see that file to keep up to date with any changes.

The code is a node.js module that is written to be integrated into an instance of HAP-NodeJS.

Requirements

The following applications and libraries need to be present in order for this module to work:

Installation

If you have an instance of HAP-NodeJS, you can skip to step 3.

1) Install HAP-NodeJS.

   git clone https://github.com/KhaosT/HAP-NodeJS.git
   cd HAP-NodeJS

2) (recommended) Remove sample accessories from HAP-NodeJS instance.

   rm accessories/*_accessory.js

3) Copy TempHumidSensor_accessory.js into accessories folder.

   cp /path/to/repo/bridge/TempHumidSensor_accessory.js accessories/

4) Install node modules.

   npm install

4) Manually install the noble BLE module in the HAP-NodeJS root. This is required for the temperature/humidity accessory.

   npm install noble

5) Finally, start HAP-NodeJS.

   node Core.js

End-to-end demonstration

At last! We've completed building the hardware and software for the project and it's time to see the fruits of our efforts. Once you've followed the instructions in the above sections to get the sensor and bridge modules running, the final step is to complete accessory pairing in the Home app on your iOS device.

  1. Turn on the sensor by connecting it to power.
  2. Activate the bridge module by starting HAP-NodeJS (see above).
  3. On your iOS device, open the "Home" app and follow along with the steps below:
Pairing step 1 Pairing step 2 Pairing step 3
Press "Add Accessory" Press "Temperature Humidity Sensor" When prompted, click "Add Anyway". Enter the HomeKit Code that's hardcoded here, the default is 031-45-154.
Pairing step 4 Pairing step 5 Pairing step 6
You should now see 2 new accessories and the humidity and temperature displayed at the top!

Thank you for reading this series of posts! Please feel free to share any questions or comments below.