For all the parts to this project, click here! Smart Doorbell System
Introduction
This blog post contains various topics related to Bluetooth LE including how to use it and monitor it.
The information here is standalone so it can be read even if you have not followed the project, but if you’re interested to see more, please click on the link above!
I'm working on a project called the Smart Doorbell, centered around the Texas Instruments CC2640R2 chip which contains Bluetooth LE (BLE) functionality. But the beauty of Bluetooth LE is that there is compatibility, because it is a standard. It allows for the project to grow and use additional hardware and software, and have new functionality as a result.
The information here actually discusses several different chips and products, and what can be done with them. I figured it was worth documenting. There are many ways to do things. All the hardware here supports BLE.
The SensorPucksSensorPucks and the TI SensorTagTI SensorTag are quite incredible devices with many sensors that can be read by devices such as a PC or mobile phone. The Raspberry Pi 3BRaspberry Pi 3B, 3B+ and the BeagleBone Black Wireless all support BLE as standard. The CC2640R2 LaunchPadCC2640R2 LaunchPad from Texas Instruments was used for my Smart Doorbell project. I built a custom board for it (shown in the photo below too). The Cypress BLE KitCypress BLE Kit offers a lot of functionality; some of it is discussed further below.
What is Bluetooth LE?
Although the name inherits from the old Bluetooth that first became popular for hands-free earpieces for mobile phones, Bluetooth LE is quite a different mode of operation, a lot of the communication and procedures are different, and the underlying radio transmissions are different too but happen to be in the same area of radio spectrum.
Bluetooth LE, as its name implies, can use less energy because transmissions occur for shorter periods and devices can sleep to save additional energy. It has seen diverse uses such as in healthcare, wearables and headsets, indoor navigation, home automation, sensors in factories and so on. To do all this, at its heart the technology is used to transfer sets of digital data.
To get data across from one device to another, there is quite a lot of bits and bytes that are transferred and manipulated, and lots of encapsulation and de-encapsulation occurs. The core essentials are described here – simplified because there is way too much detail otherwise (thousands of pages..). The essentials are enough to do lots with BLE.
Physical Radio Transmission
BLE operates on 40 channels in the 2.4GHz spectrum, using frequency shift keying (FSK) in a scheme known as Gaussian FSK (GFSK) which reduces the sideband content outside of the channels, hence allowing all 40 channels to be packed quite closely together.
The bandwidth is quite high; bits are sent over the air at 1Mbps minimum, although 2Mbps is also possible. Each bit sent over the air doesn’t need to correspond to one bit of actual data however. It is possible to use two bits over the air to correspond to one actual bit for instance. This is known as a coded transmission (as opposed to an uncoded transmission), and is useful for communicating over noisy paths or longer range paths because the receiver can correlate the signal with the expected sequences and pull the information out of the uncorrelated noise.
Link Layer
The bits that are sent over the airwaves are in a format, containing a preamble (which allows the receiver side to ‘train’ and get ready for the real data bits. It allows things like radio gain to adjust itself, and determine the best settings for capturing the FSK signal) and then chunks of data that could be an advertisement on any of the channels, or data. The advertisement method can be used for broadcasts to many receivers. BLE Beacons rely on this. BLE Beacons are devices that send out advertisements containing useful information (such as a unique identifier) that many devices can receive. Combined with received signal strength, it is possible for a receiver to use it as a form of indoor GPS for instance.
Another BLE device can choose to respond to the advertisements and then switch to a bidirectional data transfer mode. A procedure called the Link Layer Control Protocol (LLCP) regulates the transfer of data in the two directions. In summary the link layer is responsible for shovelling bits of information in packets (with some redundancy through coded transmissions, and some error checking through checksums and some handshaking timers established with LLCP. If it helps as a comparison, the Link Layer is very much like Ethernet in functionality.
All of these bits and bytes are is very low-level and protocols or methods/procedures are needed for sending useful formatted and error-free information such as (say) 8-bit light levels, ASCII identifiers, 16-bit voltage measurements and so on. That is still to come and is discussed further below.
Logical Link Control and Adaptation (L2CAP)
Different streams of information may need to be sent across the same connection, such as an audio stream, and button-presses for instance. This implies that channels of information needs to be sent. Furthermore packets of data much larger that that possible at the logical link layer may need to be sent, and therefore the larger packets need to be fragmented into smaller chunks. All of this can be done at the L2CAP layer. It behaves very similarly to TCP/IP in that respect. Messages to co-ordinate the creation and management of channels, retransmissions, selection of quality of service (e.g. best effort, or guaranteed delivery through retransmits where needed) are generated at this layer.
Attribute Protocol (ATT)
As we move up the stack, we now come to the area of functionality that can support sending of values. But the information is quite basic. There are just a few important things at this layer in the stack.
The Attribute Protocol can share simple “things” that consist of an identifier (known as a UUID), and a corresponding identifier value, and a ‘handle’ which is typically just an incrementing value, used for referring to a line item. It could be considered like a line in table. The Attribute Protocol can be used to read or write the value by referencing the handle. That’s it!
Generic Attribute Profile (GATT)
At this level of the stack, finally the information is at a level useful for applications to interact with.
The GATT protocol is used to generate attribute protocol packets with defined identifiers and handles, in a defined sequence. To take an example; if a water pump was connected via BLE then perhaps flow and speed need to be measured and transferred. In that case, the information that could be needed would be (a) indication that the device is a water pump, (b) the flow rate and (c) the speed.
With BLE this information is arranged such that (a) is indicated to be a ‘primary service’ and (b) and (c) are the values of a ‘characteristics’. Everything becomes a line in the table. This means that even the indications about what the things (a), (b) and (c) are, become entries in the table. It is easier to see by looking at an example, but it still takes a while to get one’s head around it. This table will be walked through as an example:
The leftmost column is just an incrementing value usually, and this is used as a handle for any read/write operations.
The first row has a UUID of 0x2800, which is a pre-defined value which means that a primary service is about to be described. It has been given a user-defined value of 0xfff0 which will mean “water pump service”. There are also pre-defined values for services.
The next row (handle 0x0008) has a UUID of 0x2803 which is another pre-defined value that means that a characteristic is about to be described. That row has a user-defined value of 0xfff1 that we will take to mean flow rate. There are pre-defined characteristic definitions too.
The next row (handle 0x0009) now contains that 0xfff1 value as the UUID. The value is the actual flow rate, and in the example it is set at 5 (e.g. 5 litres per minute).
The next line has a UUID of 0x2901 which means user description and it can be set to ASCII text if desired to describe what the characteristic was all about.
The next three rows are similar, but cover the speed of the pump.
And that’s the heart of GATT. There are additional optional things, plus there is an attribute which indicates the start of the GATT information, and other attributes for product name, firmware version and so on. But the important takeaway is that the GATT protocol defines the core ‘things’ that exist or are measurable or controllable in a product, and provides the messages that can be used to read and write the values of these core things, or to request to be notified when values of the things change.
Generic Access Profile (GAP)
We are now at the top of the stack. GAP is used to choose what mode BLE ought to run in and there are four main modes. For the connectionless scenario such as with BLE Beacons as discussed earlier, the mode would be called broadcaster for the beacon, and the receiving end would be called the observer. For the connection-oriented mode, the advertising device (which cannot initiate the actual connection, but can advertise the presence of itself) the role is that of a peripheral device. The side that connects to it and then information can be sent in either direction, is called the central device. Any of these modes can be selected by sending a message to the GAP functionality in the stack, and it will internally set down message sequences and timer adjustments so that the BLE radio hardware can scan the various channels or advertise on channels. So for example in peripheral mode, the hardware will send out advertisements at intervals based on timer values, and in central mode the hardware will scan channels at particular intervals.
Putting the Stack Together
The diagram here shows the radio communication at the bottom, and end user applications would reside at the top. This is very simplified, but at least provides an overview of the information flows.
How Does Bluetooth 5.0 work?
The latest specification of Bluetooth contains some enhancements over the older version 4.2. In particular, there is the possibility to send data with higher throughput. This is achieved through increasing the bitrate from 1Mbps to 2Mbps. Another enhancement is the ability to use coded transmissions where the information rate is slowed down but in return the achievable range is increased as mentioned earlier. In summary Bluetooth 5.0 is the first BLE release with enhanced bitrate and longer range capabilities. The enhanced bitrate also means that advertisements can take less time, which allows for denser and better-performing indoor navigation systems using the beacon mode.
The Smart Doorbell project uses the latest 5.0 BLE stack, and therefore if/when it is enhanced with photo or even video capability, the high data rate feature of BLE 5.0 would be very handy.
Host-Controller Interface (HCI)
Much of the stack has been described above, but one important protocol has been left out until now. It is needed for practical realisations of Bluetooth LE, because all the functionality does not usually reside on a single chip. To take an example; the Raspberry Pi has a separate chip for wireless capability. Similarly, the BeagleBone Black needs a Bluetooth LE dongle to be plugged in.
The Host-Controller Interface has been devised to sit in-between L2CAP and the Link Layer. HCI can run over USB or over serial interfaces as an example. There is not much more to say about it, except that by making it a part of the standard, it means that BLE chips, modules and cards are interchangeable, which is great.
The diagram here shows how the stack is split up between the BeagleBone Black, and a BLE dongle. The diagram looks near-identical for a Raspberry Pi, except that with the Pi 3 the radio module is on-board, and the HCI connection is not across USB but across a serial interface.
For all Linux devices, the parts of the stack northbound of the HCI are implemented using the BlueZ library of code.
BlueZ is not the nicest code to work with (it is quite badly documented) but it is what it is, and we need to work with it, or with other libraries of code that connect to it. Thus for Python users, packages such as bluepy can be used. At its heart it interfaces to BlueZ, so we don’t have to : )
Getting Started Working with Bluetooth LE Devices
One of the first steps to working with Bluetooth LE peripherals (i.e. things that can be controlled or monitored by initiating a connection to them) is to explore what parameters the device expects to be read or written. The source code could be examined, but many commercial products do not come with that of course. However the GATT protocol can be used for this exploring task; it is one of it’s main purposes.
Many vendors such as Texas Instruments have tools and modules that can be used for some form of Bluetooth debugging. Capturing every single packet is not easy, but it is easier to capture content at the HCI layer in the stack, or higher up.
I like the Cypress Semiconductor CY8CKIT-042-BLECY8CKIT-042-BLE because it is extremely low cost (£37 from Farnell currently) and does a lot, and comes with a lot of cool hardware. There is actually a newer CY8CKIT-042-BLE-ACY8CKIT-042-BLE-A kit now that is advised, and it costs the same.
Part of the contained hardware in the kit is a nice Bluetooth LE dongle. It uses the CY5670CY5670 chip (or CY5677 chip in the newer kit). It plugs directly into the PC and there is free software to go with it called CySmart. Once installed you can do a BLE scan and see what devices are advertising. You can click on connect, and then read the parameters, which reveals all the services information including what characteristics are supported. Then you can read or write values as desired using CySmart and see the results immediately. It is very handy!
The screenshot below shows the software in action, capturing a record of GATT information from a TI SensorTagTI SensorTag.
Working with the Raspberry Pi and other Linux Devices
Since BLE is a standard and devices are therefore interchangeable, it becomes very attractive to use something like a Raspberry Pi for simulating your BLE product(s) until the hardware is ready. As an example, it could be possible to use one Raspberry Pi to act as a BLE Central device, and another as a BLE Peripheral device, and let them communicate with each other.
It is also possible to do more advanced things, like tunnel information from one location to another, to extend BLE to areas ordinarily not possible. If we take a doorbell scenario where there is a BLE button-push module, and a chime alert module, then I should be able to take a BLE Central device such as the button-push module, and use it with no modifications as an emergency alert button and install it at (say) an elderly relatives home, and place the BLE Peripheral device such as the chime module miles away. Provided there is a Raspberry Pi at both locations connected to the Internet, then I get the benefits of BLE (ultra-low power, fully mobile, nothing proprietary) and also the benefits of the Internet.
Or, manufacturers could sell just the button-press module, and not the chime module, to those who wish to use the Pi for audio alerts. Or sell the chime module only, for people who want a Raspberry Pi controlled and automated alerting system on a shop floor (such as for machine temperature overload alerts) and really do not need the manual bell push module. In other words, the possibilities are greatly expanded with Linux.
This blog post explores how to do this, because it is relevant to many BLE use-cases where a user may wish to expand the capabilities of their system. Code for a Linux based Peripheral, and for a Linux based Central device is described, as example building-blocks.
Creating a BLE Peripheral Device with a Raspberry Pi
Although the code here was tested on a Pi, the information is just as relevant to any other Linux system.
First off, I installed any libraries that were missing, and updated the Node.js environment. To do this, as root user (or prepend sudo to these commands) type the following:
apt-get install bluetooth bluez libbluetooth-dev libudev-dev apt-get install node
As a normal user, create the folder for app development, and install Bleno which is the library of code which will interface with the BlueZ BLE protocol stack in Linux:
mkdir -p development/bt-chime cd development/bt-chime npm install bleno
As root user, a couple of steps are required for Peripheral mode to become possible:
systemctl stop Bluetooth hciconfig hci0 up
That last command uses a parameter hci0, but if you’re using an external BLE dongle plugged into the USB port, then it may be hci1. To check, type the following:
hcitool dev
The output will specify (say) hci0 or hci1, and will also specify a 6-byte address of the BLE chip or module/dongle. For the testing, I used a BLE dongle from Laird Technologies, because it offers some advantages for the Pi (it frees up the serial port on the Pi for Linux console use).
The very small bit of code here, saved to a file called chime.js creates the Peripheral device. The code creates a chimeCharacteristic and a ChimeService. These refer to a GATT Characteristic Declaration and Primary Service entry as described in the BLE stack information earlier. There is a onWriteRequest method in the code too, which gets called by the Bleno library whenever the communicating Central device issues a write instruction on the characteristic. The code merely prints the value that the Central device sends, which could reflect which chime/announcement sound that is desired.
The code could be expanded to send that value over the Internet, or to log the time of the alert (this could be useful when a user is not at home but still wishes to know if they missed anyone, and at what time – ideal for subsequently checking any video camera recordings at that timestamp).
#!/usr/local/bin/node // bt-chime, Linux version // rev 1.0 shabaz July 2018 // var util = require('util'); var events = require('events'); var bleno = require('bleno'); function Chime() { events.EventEmitter.call(this); this.chimeval=0; } util.inherits(Chime, events.EventEmitter); Chime.prototype.dochime=function(val) { console.log('got chime '+val); self.emit('ready', 1); }; function chimeCharacteristic(chimeval) { bleno.Characteristic.call(this, { uuid: 'fff1', properties: ['read', 'write'], descriptors: [ new bleno.Descriptor({ uuid: '2901', value: 'sets chime sound' }) ] }); this.chimeval = chimeval; } util.inherits(chimeCharacteristic, bleno.Characteristic); chimeCharacteristic.prototype.onWriteRequest = function(data, offset, withoutResponse, callback) { if (offset) { callback(this.RESULT_ATTR_NOT_LONG); } else if (data.length !== 1) { callback(this.RESULT_INVALID_ATTRIBUTE_LENGTH); } else { var chimeval = data.readUInt8(0); console.log("chime = "+chimeval); callback(this.RESULT_SUCCESS); } }; chimeCharacteristic.prototype.onReadRequest = function(offset, callback) { if (offset) { callback(this.RESULT_ATTR_NOT_LONG, null); } else { var data = new Buffer(1); data.writeUInt8(this.chimeval, 0); callback(this.RESULT_SUCCESS, data); } }; function ChimeService(chime) { bleno.PrimaryService.call(this, { uuid: 'fff0', characteristics: [ new chimeCharacteristic(chime) ] }); } util.inherits(ChimeService, bleno.PrimaryService); var name='simplePeripheral'; var chimeService=new ChimeService(new Chime()); bleno.on('stateChange', function(state) { if (state == 'poweredOn') { bleno.startAdvertising(name, [chimeService.uuid], function(err) { if (err) { console.log(err); } }); } else { bleno.stopAdvertising(); } }); bleno.on('advertisingStart', function(err) { if (!err) { console.log('advertising...'); bleno.setServices([ chimeService ]); } });
To make the code executable, type:
chmod 755 chime.js
To run the code, as root user, type:
./chime.js
The code can be tested using (say) the CySmart application and the Cypress BLE dongle, or with another Pi, or with a real hardware bell push module from the Smart Doorbell System. Note that due to a limitation in the software library (Bleno) you may need to make some timer changes in CySmart if an error occurs during connection. To do that, click on Configure Master Settings and go to Connection Parameters and change Connection Interval Min to 50ms, and Connection Interval Max to 70ms, and Supervision Timeout to 20000ms. There is a reset button so it can always easily be reverted to the original values.
Creating a BLE Central Device with a Raspberry Pi
The Central device is responsible for connecting to a Peripheral device, and then sending or receiving data. In this example, the Pi is used to send a button press to the chime module.
First, create a folder for the code:
mkdir -p development/bt-bellpush cd development/bt-bellpush
Next, install the library of code which will talk to the BlueZ protocol stack:
npm install noble
The code here was saved to a file called bellpush.js and it is straightforward to follow based on the earlier GATT information. As can be seen, a couple of UUIDs are defined, one for a Primary Service, and one for a Characteristic Descriptor. The BLE radio channels are scanned for advertisements, and when the chime service is found, the characteristic descriptors are checked. If one matches the desired UUID, then a handle to the object for manipulating the characteristic is stored in chime_characteristic. The write method on it will instruct the stack to issue the attribute write command over the air, to the BLE peripheral device. The code merely sends the value 101, which could be an instruction to play a particular alert in this example.
Once saved, this code can be run in a similar manner as the previous example, and it will connect to Peripheral device and then send the bell push value as described.
#!/usr/local/bin/node // bellpush.js // rev 1.0 July 2018 Shabaz // var noble=require('noble'); var util = require('util'); var events = require('events'); var CHIME_SERVICE_UUID = 'fff0'; var CHIME_CHARACTERISTIC_UUID = 'fff1'; noble.on('stateChange', function(state){ if (state==='poweredOn'){ console.log('scanning..'); noble.startScanning([CHIME_SERVICE_UUID], false); } else { noble.stopScanning(); } }) noble.on('discover', function(peripheral){ noble.stopScanning(); console.log('found: ', peripheral.advertisement); peripheral.connect(function(err){ peripheral.discoverServices([CHIME_SERVICE_UUID], function(err, services){ services.forEach(function(service){ console.log('found service: ', service.uuid); service.discoverCharacteristics([], function(err, characteristics){ characteristics.forEach(function(characteristic) { console.log('found characteristic:', characteristic.uuid); if (CHIME_CHARACTERISTIC_UUID == characteristic.uuid){ chime_characteristic = characteristic; handleBellSystem(); } else { console.log('Error, some characteristics were missing'); } }) }) // end service.discoverCharacteristics }) // end services.forEach }) // end peripheral.discoverServices }) // end peripheral.connect }) // end noble.on function handleBellSystem() { // we just send a chime write instruction var chimeinfo = new Buffer(1); chimeinfo.writeUInt8(101, 0); chime_characteristic.write(chimeinfo, false, function(err) { if (!err) { console.log('button press sent!'); } else { console.log('button press error'); } }) }
Summary
The Bluetooth LE protocols are quite involved but in a nutshell they allow for data to be passed between devices that act in certain roles. One scenario is to have a Peripheral role device which periodically advertises its presence. A Central role device can connect to it, and then data can be transferred to control or monitor the services and attributes that the Peripheral role device provides. All of this occurs at high speed and low energy using a portion of spectrum at 2.4GHz.
One major advantage of using BLE is that because it is standards based, different vendor devices can work together. This was demonstrated by converting the Raspberry Pi into effectively the bell-push module, and another Raspberry Pi into a chime module. These are compatible with the developed hardware using the Texas Instruments CC2640R2. Combining these allows for new features, such as the ability to tunnel information over the Internet, or for new features that rely on Linux or the extra processing power that these single board computers provide, while maintaining the benefits of BLE.
The supplied code should also make testing Central and Peripheral devices easier, because there is no need to wait for the hardware to be constructed; just code and go with the Raspberry Pi.
Thanks for reading.
Top Comments