This is a Bluetooth Low Energy (BLE) application to control a garage door drive from a smartphone. In contrast to many other projects, BLE advertising packets (PDUs) are used to avoid connection setup latency.

I often want to open my garage door but I rarely have the 433 MHz remote control transmitter in my pocket. Thus, I built a small BLE receiver that can be controlled by an Android App.

A particularity of this project is that BLE advertising PDUs are used, thereby avoiding connection setup delays. The system thus responds as quickly to a remote control command as a classical hardware garage door transmitter receiver combination would do. There is one-way communication from the smartphone to the receiver only, which is secured by HMAC. The idea was to keep everything simple, both the hardware as well as the software.

I share this project in the hope that you might find it useful or use it as a basis for similar projects.

The source code is available on https://github.com/kiffie/ble-garage-door

The protocol
To transmit a command, BLE advertising Protocol Data Units (PDUs) are used, which are described in Vol. 6, Part B, section 2.3 of the Bluetooth specification. For compatibility reasons, the short (legacy) advertising PDUs are used that contain an AdvData field of at most 31 octets. The AdvData field contains a sequence of AD Structures represented in a TLV-like coding (cf. Volume 3, Part C, section 11 of the Bluetooth specification). One possible AD structure is the Service Data AD structure (cf. Bluetooth Core Specification Supplement, Part A, Section 1). The Service Data AD structure includes a service UUID followed by an arbitrary number of octets.

The Service Data AD structure is used to transmit a remote control command. The service 128 bit UUID corresponds to a unique transmitter ID. In other words, each transmitter defines a specific BLE service. The data appended to the UUID is structured as follows

typedef struct {
    uint8_t cmd;       /* command (currently always 0) */
    uint8_t seq_no[3]; /* sequence number (big endian) */
    uint8_t digest[4]; /* first four octets of HMAC-SHA256 */
} gd_message_t;

The digest is calculated over the first four octets based on a transmitter key. The transmitter key is derived from a Receiver Master Key (RMkey) stored in the Receiver based on the transmitter UUID.

 Transmitter Key = HMAC-SHA256(RMkey, transmitter UUID)

The digest authenticates the message. The sequence number allows to detect duplicate PDUs and prevents replay attacks. Since BLE advertising PDUs are not encrypted, it is possible for others to receive both the UUID as well as the additional data.

An example Advertising PDU is shown in the attached Wireshark screenshot.

The transmitter
The transmitter is a simple Android App requiring API level 21 (corresponding to Android 5 “Lollipop”). When first started, the App calculates a random UUID and shows a setup dialog to get the RMkey as an base32 encoded string (see attached screenshot). The setup dialog shows the length of the key and a CRC32 checksum to help the user in avoiding typos when entering the key. The App calculates the transmitter key from the UUID and the RMkey and then throws the RMkey away. The tuple (UUID, transmitter key), corresponding to a transmitter identity, is then persistently stored. The transmitter identity is lost when the App is uninstalled or application data is manually deleted.

Once set up, the App shows a red button (see attached screenshot). When clicking the button, a BLE advertising procedure is configured to repeatedly transmit the above-described Service Data AD structure during an interval of a few seconds.

The receiver
The receiver has been built on a prototyping board around the Arinoco ACN52832 BLE module. It has a Nordic Semiconductor NRF52382 microcontroller, a push button, a LED and a relay to be connected to a garage door drive. The receiver needs a 9V to 24V DC power supply. If possible, the supply voltage can be taken from the garage door driver. Otherwise, a simple wall power supply would do as well.

When building the software for the microcontroller, a Python script creates file rxm_key.bin that contains the 20 byte RMkey. Moreover, a textual representation of the key is generated and stored in _build/rxm_key.txt. The latter contains the information to be entered into the App when first started.

The receiver maintains a database of transmitter UUIDs and sequence numbers. To add a new transmitter, the button must be pressed and then the transmitter to be added must be activated. When the button is pressed for more than five seconds, the database is cleared and the receiver will not respond to any transmitter anymore.

When the receiver gets an advertisement PDU containing the above-described Service Data AD structure, it checks the digest, whether it knows the transmitter UUID and whether the sequence number is correct. If all checks succeed, the relay will be activated for one second.