Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.Sign up
Introduction to Bluetooth LE
Bluetooth Low Energy compared with Bluetooth Classic
Bluetooth 4.0 includes both traditional Bluetooth, now labeled "Bluetooth Classic", and the new Bluetooth Low Energy (Bluetooth LE, or BLE). BLE is optimized for low power use at low data rates, and was designed to operate from simple lithium coin cell batteries.
If you've used other digital radios with a microcontroller, including classic Bluetooth radios, you're probably used to thinking about the radios as an asynchronous serial connection (usually called a UART, or Universal Asynchronous Receiver/Transmitter). It works similar to a phone call between two phones - once you establish a connection, each person talks as the other listens and vice versa. They stay connected, even if neither person is saying anything until they hang up and the call is ended. In systems like these, data is tranferred using a queue, and when data is read by the receiver, it's erased from the queue, just as once my words reach your ears over the phone line, they're out of the communications channel.
Serial radio devices communicate using point-to-point, dedicated sessions, like a telephone call.
But that's not how Bluetooth LE works.
Instead of communicating one-on-one like a phone, a Bluetooth LE radio acts like a community bulletin board. The computers that connect to it are like community members that read the bulletin board. Each radio acts as either the bulletin board or the reader. If your radio is a bulletin board (called a peripheral device in Bluetooth LE parlance) it posts data for all radios in the community to read. If your radio is a reader (called a central device in Blueooth LE terms) it reads from any of the bulletin boards (peripheral devices) that have information about which it cares. You can also think of peripheral devices as the servers in a client-server transaction, because they contain the information that reader radios ask for. Similarly, central devices are the clients of the Bluetooth LE world because they read information available from the peripherals.
Think of a Bluetooth LE peripheral device as a bulletin board and central devices as viewers of the board. Central devices view the services, get the data, then move on. Each transaction is quick (a few milliseconds), so multiple central devices can get data from one peripheral.
The information presented by a peripheral is structured as services, each of which is subdivided into characteristics. You can think of services as the notices on a bulletin board, and characteristics as the individual paragraphs of those notices. If you're a peripheral device, you just update each service characteristic when it needs updating and don't worry about whether the central devices read them or not. If you're a central device, you connect to the peripheral then read the boxes you want. If a given characteristic is readable and writable, then the peripheral and central can both change it.
There are two key differences between Bluetooth LE's model and a serial communication model: First, in Bluetooth LE data is exchanged asynchronously. You don't wait for it to come down to you. When you want it, you read it. If you're sending data, you don't bother to pair, you just publish it and let receivers get it when they want it. This makes Bluetooth LE devices low energy because their radios are on only on when a peripheral is advertising its services, or when a central device is reading from a peripheral. In contrast to this, a dedicated serial radio is on all the time, whether data's being transmitted or not. Second, data is not deleted when it's read, only when it's updated. This means that multiple central devices can read the same information from a peripheral, one after another.
So how do I know when to read a characteristic?
You could use a timer to periodically read a characteristic, for example 5 times a second. However, this would waste battery life if the data has not actually changed.
The Bluetooth LE specification includes a mechanism known as notify that lets you know when data's changed. When notify on a characteristic is enabled and the sender writes to it, the new value is automatically sent to the receiver, without the receiver explicitly issuing a read command. This is commonly used for streaming data such as accelerometer or other sensor readings. There's a variation on this specification called indicate which works similarly, but in the indicate specification, the reader sends an acknowledgement of the pushed data.
The client-server structure of Bluetooth LE, combined with the notify characteristic, is generally called a publish-and-subscribe model.
And how do I know when to update a characteristic?
Your peripheral should update characteristics when there's a significant change to them. For example, when a switch changes from off to on, update its characteristic. When an analog sensor changes by a significant amount, update its characteristic.
Just as with writing to a characteristic, you could update your characteristics on a regular interval, but this wastes processing power and energy if the characteristic has not changed.
But I Really Want A Serial Port!
Sorry, Bluetooth LE doesn't work that way. And once you get used to the publish and subscribe model, you probably won't miss the serial port. If you really have to make something like a serial port, you can create a virtual serial port service by writing an interface that wraps a notify characteristic in a byte buffer to "look like" a serial port. But that's more trouble than it's worth. You would also have to think about situations where bytes arrive out-of-order or there are missing bytes in the stream and handle them in a way that would not surprise users of serial ports.
Think about it this way: for most serial port applications, you either use a standard data packet that ends with a particular set of bytes (e.g. carriage return and newline), or you use a handshaking approach, in which the receiver sends a request for new data, then polls while the sender sends a single packet. Either way, you're effectively creating what the Bluetooth LE specification gives you. So let go of your serial port and embrace the publish and subscribe model.
Note: There are a number of boards including the RedBearLab BLE mini and the Adafruit Bluefruit LE that do implement UART services. However by using them in this manner you're negating the low energy part of Bluetooth LE as the radios will be constantly "on" all the time. As an alternative, you can heavily reduce the power consumption of both boards by implementing custom services. Examples of implementing custom services using the Adafruit Bluefruit LE board, nRFgo Studio, and an Arduino can be found in this Github repository.
Central and Peripheral Devices
As mentioned above, Bluetooth devices are divided into two groups, central devices and peripheral devices. Central devices are clients. They read and write data from peripheral devices. Peripheral devices are servers. They provide data from sensors as readable characteristics, and provide read/writable characteristics to control actuators like motors, lights, and so forth.
A device can theoretically be both central and peripheral, though in practice it depends on the radio and the software API. For example, the Nordic nRF8001 radio can only act as a peripheral. The Nordic nRF51822 can be either central or peripheral. Apple's iOS can be a central or peripheral at the same time, as can most Linux devices, but Android devices as of version 4.4 cannot be peripheral devices. Android 5.0 introduce act device as peripheral.
Services, characteristics, and UUIDs
A BLE peripheral will provide services, which in turn provide characteristics. You can define your own services, or use standard services.
Battery Service, Heart Rate Service and Current Time Service are examples of standard services already defined in the Bluetooth 4.0 specification. The standard Heart Rate Service provide 3 characteristics: Heart Rate Measurement, Body Sensor Location and Heart Rate Control Point.
You can make up your own custom services as well. For example, imagine a motor controller service. This would be a custom service with four characteristics: motor state, motor speed, motor directon, and encoder reading. The motor state, speed, and direction characteristics would be write-only (from the point of view of the central) and would be used to turn a motor on and off or set its speed or directoon. Encoder reading would be a read-only characteristic that gives you feedback from a rotary encoder attached to the motor to determine position or number of rotations.
Services are identified by unique numbers known as UUIDs. You know about UUIDs from other contexts. Standard services have a 16-bit UUID and custom services have a 128-bit UUID.
In order to write custom services and characteristics, you'll need the toolchain to customize the firmware for the radio you're working with. Below you'll find a list of various radios and their toolchains. Note that some of them are Windows-only, and some require workarounds to work cross-platform.
Service design patterns
Having too many services can fill the configuration memory of your BLE device and make it complicated to deliver or use the service. There are different patterns of how to define services and characteristics. Apple's Notification Center Service (ANCS), for example, has a very small number of characteristics, but can also be hard to understand.
A characteristic value can be up to 20 bytes long. This is a key constraint in designing services. Given this limit, you should consider how best to store data about your sensors and actuators most effectively for your application. Below are a few possible patterns.
The simplest design pattern is to store one sensor or actuator value per characteristic, in ASCII encoded values. For example:
This is also the most expensive in memory terms, and would take the longest to read. But it's the simplest for development and debugging.
You could also combine readings into a single characteristic, when a given sensor or actuator has multiple values associated with it. For example:
|Motor Speed, Direction||150,1|
|Accelerometer X, Y, Z||200,133,150|
This is more efficient, but you need to be careful not to exceed the 20-byte limit. The accelerometer characteristic above, for example, takes 11 bytes as a ASCII-encoded string.
You could also consider storing data for multiple sensors or actuators as binary rather than ASCII encoded values. This might complicate programming and debugging on the receiving end, but it saves you space. For example, imagine you are controlling a string of RGB lights. A binary string in which data is stored as repeating patterns of red, green, and blue values would look like this:
|3 channels of lights, RGB pattern||0x00,0x45,0xFF,0x34,0x9A,0x12,0x56,0x24,0x1A|
This pattern would allow you to store up to 6 channels of RGB lighting values per 20-byte characteristic. Each value in the example above represents a single byte value in hexadecimal notation, rather than an ASCII-encoded string.
The design patterns for sending data in Bluetooth LE characteristics are not unlike those for sending data over a serial port; you just have to consider the 20-byte limit per characteristic and plan accordingly.
There are many applications that have older standard data protocols that could fit in 20 bytes and could be implemented on top of Bluetooth LE. When designing a new service, it's worthwhile to look to see what industry standard already exists for your application. For example, the DMX-512 standard or ACN standard for lighting; the MIDI standard for musical controllers, samplers and synths; and so forth.
There are 4 things a central device can do with a characteristic:
- Read. Ask the peripheral to send back the current value of the characteristic. Often used for characteristics that don't change very often, for example characteristics used for configuration, version numbers, etc.
- Write. Modify the value of the characteristic. Often used for things that are like commands, for example telling the peripheral to turn a motor on or off.
- Indicate and Notify. Ask the peripheral to continuously send updated values of the characteristic, without the central having to constantly ask for it.
The direction of read and write can get a little confusing when you consider a system that includes a microcontroller like an Arduino. The microcontroller writes data from its sensors to the peripheral's characteristics so that central devices can read those characteristics. Alternately, a central device might write to a peripheral's characteristics, then the microcontroller would read from the peripheral to control its actuators. If one end writes, the other end reads. nRFgo Studio, Nordic Semiconductor's tool for configuring their radios, has helpful arrows indicating which direction data travels over the air.
Advertising and GAP
BLE devices let other devices know that they exist by advertising using the General Advertising Profile (GAP). Advertising packets can contain a device name, some other information, and also a list of the services it provides. iOS apps using Core Bluetooth will typically try to discover peripherals that advertise a particular service it is looking for, so that it will not have to sift through all the peripherals that might be in the area.
Advertising packets have a limited size. You will only be able to fit a single 128-bit service UUID in the packet. Make sure the device name is not too long, or you won't even be able to fit that.
You can provide additional services that are not advertised. Central devices will learn about these through the connection/bonding process. Non-advertised services cannot be used to discover devices, though. Sometimes this is not an issue. For example, you may have a custom peripheral device with a custom service, but in your central device app you may know that it also provides the Battery Service and other services.
GAP also supports so-called Broadcast packets, which send out information without expecting to open a connection with anyone. Apple's iBeacon technology relies on Broadcast packets.
The Bluetooth LE protocol operates on multiple layers. General Attribute Profile (GATT) is the layer that defines services and characteristics and enables read/write/notify/indicate operations on them. In nRFgo Studio, GATT-related things are defined in the GATT Services tab.
There are also lower layers of the specification, for example the L2CAP layer that handles connections. In most circumstances, you will not encounter BLE related acronyms other than GATT and GAP.
When reading more about GATT, you may encounter GATT concepts of a "server" and "client". These don't always correspond to central and peripherals. In most cases, though, the peripheral is the GATT server (since it provides the services and characteristics), while the central is the GATT client.