BLE master/slave, GATT client/server, and data RX/TX basics
08/218/2015 | 11:42 AM
Bluetooth Low Energy is a powerful technology, but not always the easiest to understand and use effectively. It is not like classic Bluetooth where you have a predefined set of official profiles to choose from; although there are predefined (a.k.a. "adopted") profiles specified by the Bluetooth SIG, these are just the tip of the iceberg, a small subset of the kind of functionality you can achieve with BLE.
In many (or even most) cases, the best option is to create your own custom profile(s) for your application. This gives you ultimate flexibility, and it doesn't even cost anything. In fact, it can be even easier than using one of the adopted profiles, since you get to define exactly how everything works together rather than conforming your application into something that is already defined. Also, since there is no official generic "serial port profile" in the BLE world like SPP in classic Bluetooth, sometimes a custom implementation is the only option to do what you need.
In order to do this effectively, it is important to understand how a BLE connection works, what roles are played by the devices involved, and how exactly data is transferred from one device to the other over the air. Many terms are used, and they are usually not interchangeable: master, slave, central, peripheral, client, server, advertise, scan, read, write, notify, and indicate all mean different things. Knowing the difference will make it easier to describe and build your BLE application.
Quick Overview
Here's what this article covers:
Master (or "central") devices scan for other devices. Usually, the master is the smartphone/tablet/PC.
Slave (or "peripheral") devices advertise and wait for connections. Usually, the slave is the BLE112/BLE113 module.
Client devices access remote resources over a BLE link using the GATT protocol. Usually, the master is also the client.
Server devices have a local database and access control methods, and provide resources to the remote client. Usually, the slave is also the server.
You can use read, write, notify, or indicate operations to move data between the client and the server.
Read and write operations are requested by the client and the server responds (or acknowledges).
Notify and indicate operations are enabled by the client but initiated by the server, providing a way to push data to the client.
Notifications are unacknowledged, while indications are acknowledged. Notifications are therefore faster, but less reliable
Example gatt.xml content with GATT server structure for a "typical" custom BLE peripheral device.
Master vs. Slave - Connection Roles
One important concept in BLE connectivity is the difference between a master device and a slave device. What do each of these terms imply? First, realize that they are not interchangeable with client/server, which will be explained below. Actually, in the BLE world, the master/slave difference is very easy to define and recognize:
Master / Central - the BLE device which initiates an outgoing connection request to an advertising peripheral device
Slave / Peripheral - the BLE device which accepts an incoming connection request after advertising
These pairs of terms are the only ones in the list above which are actually interchangeable. References to a "master" or "central" device are describing the same thing, and references to a "slave" or "peripheral" device are also each describing the same thing. In the CoreBluetooth APIs provided by Apple for iOS development, the "Central" and "Peripheral" nomenclature is used; you will not generally see references to master or slave devices in this context.
One important distinction between the master and slave device in a BLE network is that a slave may only be connected to a single master, but a master may be connected to multiple slaves. The BLE specification does not limit the number of slaves a master may connect to, but there is always a practical limitation, especially on small embedded modules. Our v1.1 BLE stack can support up to 8 simultaneous connections as a master device, when properly configured.
The connection role--whether a device is a master or slave--is defined the moment the connection is made. Our stack is capable of acting either as a master or as a slave device. If a device is operating as a slave, it needs to advertise (accomplished in our BLE stack with the gap_set_mode command); if it is operating as a master, it will optionally scan for devices (accomplished in our stack with gap_discover) and initiate a connection request to another device (accomplished in our stack with gap_connect_direct).
Client vs. Server - GATT Functionality
Another important concept in a BLE design is the difference between a GATT server and a GATT client. These roles are not mutually exclusive, though typically your device will only be one or the other. Which role(s) your device takes depends on how you need it to work. Here is a basic summary of each kind of functionality:
GATT client - a device which accesses data on the remote GATT server via read, write, notify, or indicate operations
GATT server - a device which stores data locally and provides data access methods to a remote GATT client
Unlike the master/slave distinction defined previously, it is easy to see that one device might actually be both of these things at the same time, based on how your application defines the data structure and flow for each side of the connection. While it is most common for the slave (peripheral) device to be the GATT server and the master (center) device to be the GATT client, this is not required. The GATT functionality of a device is logically separate from the master/slave role. The master/slave roles control how the BLE radio connection is managed, and the client/server roles are dictated by the storage and flow of data.
Most of our example projects in the SDK archive and online implement slave (peripheral) devices designed to be GATT servers. These are easy to test with many of the freely available scanning apps for the iOS platform, and upcoming apps for the Android platform now that official BLE APIs have been implemented in Android 4.3. However, we do have a few examples which implement the master (central) end of the connection, designed to be GATT clients. These include the "thermometer-demo" project in the /src folder of the SDK archive, and this BGScript-based Health Thermometer Collector.
There are also some 3rd-party unofficial BLE master examples available for Python and Visual C# .NET designed to control our modules externally using the BGAPI protocol (see this KB article for more info).
Receive vs. Transmit - Moving Data
Now that we have established the difference between a master and slave device, and a GATT client and GATT server, how do we actually define the GATT structure, and how to we use it to move data from Device A to Device B, whether that means server-to-client or client-to-server? Both directions are easily possible, of course.
In BLE projects built using our SDK, the GATT structure is defined in the "gatt.xml" file that is part of your project's source files. The structure of this file is documented in the Profile Development Kit Developer Guide, available on the BLE112 and BLE113 pages of our Tech Forum. The structure and flow of data is always defined on the GATT server, and the client simply gets to make use of whatever is exposed by the server.
GATT structure
A GATT database implements one or more profiles, and each profile is made up of one or more services, and each service is made up of one or more characteristics. For example, in outline form:
Profile 1
Service A
Characteristic a
Characteristic b
Characteristic c
Service B
Characteristic d
Characteristic e
Profile 2
Service C
Characteristic f
Characteristic g
Characteristic h
Service D
Characteristic i
Characteristic j
Profile 3
Service E
Characteristic k
Characteristic l
Characteristic m
Service F
Characteristic n
Characteristic o
You can implement as many profiles, services, and characteristics as you need. These may be entirely customized, in which case they would use your own 128-bit UUIDs generated at a site such as guidgenerator.com. Or, you can create a project which implements adopted specifications by referencing the Bluetooth SIG's online definitions of profiles, services, and characteristics. (If you do this, it usually makes the most sense to start at the profile level and drill down from there, since the full list of characteristics includes many things that will undoubtedly be irrelevant to your design.) You can combine these two as well, using some adopted profiles/services and some of your own proprietary ones.
Every BLE device acting as a GATT server must implement the official Generic Access service. This includes two mandatory characteristics: Device Name and Appearance.These are similar to the Friendly Name and Class of Device values used by classic Bluetooth. Here is an example definition of an absolutely minimal GATT definition using our gatt.xml format and providing the Generic Access service entries:
This alone is enough to work with, but it won't let you do anything useful other than tell the GATT client who and what you are. We still need to explore basic data transfer methods that we can use. First though, one minor clarification between attributes and characteristics.
Attributes and Characteristics
You may occasionally hear or see the terms "attribute" and "characteristic" used interchangeably, and while this isn't entirely wrong, it isn't totally accurate and it can be confusing. Remember that a service is made up of one or more characteristics. However, one single characteristic--generally the most specific level down to which we define our GATT structure--may be comprised of many different attributes.
Each attribute is given a unique numerical handle which the GATT client may use to reference, access, and modify it. Every characteristic has one main attribute which allows access to the actual value stored in the database for that characteristic. When you read about "writing a value to this characteristic" or "reading that characteristic's value," the read/write operations are really being done to the main data attribute.
Some other related attributes are read-only (such as a Characteristic User Description attribute), some control the behavior of the characteristic (such as the Client Characteristic Configuration attribute which is used to enable notify or indicate operations). Our BLE stack and SDK tools generate these as necessary based on the contents of your project's gatt.xml file.
Every attribute has a UUID. These may be either 16 bits (e.g. "180a") or 128 bits (e.g. "e7add780-b042-4876-aae1-112855353cc1"). All 16-bit UUIDs are defined by the Bluetooth SIG and are known as adopted UUIDs. All 128-bit UUIDs are custom and may be used for any purpose without approval from the Bluetooth SIG. Two very common 16-bit UUIDs that you will see are 2901, the Characteristic User Description attribute (defined in gatt.xml with the <description> tag), and 2902, the Client Characteristic Configuration attribute (created by our SDK when either "notify" or "indicate" is enabled on a characteristic).
One important note is that some attribute UUIDs do not technically need to be unique. Their handles are always unique, but the UUIDs occasionally overlap. For example, every Client Characteristic Configuration attribute has a UUID of 0x2902, even though there may be a dozen of them in a single GATT database. You should always give your own custom characteristics fully unique UUIDs, but don't be alarmed if you are testing out your prototype with BLEGUI, you perform a Descriptor Discovery operation, and suddenly see multiple instances of the same UUID for certain things. This is normal.
Data Transfer Methods
There are four basic operations for moving data in BLE: read, write, notify, and indicate. The BLE protocol specification requires that the maximum data payload size for these operations is 20 bytes, or in the case of read operations, 22 bytes. BLE is built for low power consumption, for infrequent short-burst data transmissions. Sending lots of data is possible, but usually ends up being less efficient than classic Bluetooth when trying to achieve maximum throughput.
Here are a few general guidelines about what kind of data transfer you will need to use:
If the client needs to send data to the server, use write.
If the client needs to get data from the server on-demand (i.e. polling), use read.
If the server needs to send data to the client without the client requesting it first, use notify or indicate. (The client must subscribe to these updates before any data will be transferred.)
The server cannot pull data from the client directly. If this is necessary, the server must use notify or indicate to send pre-arranged request data to the client, and then the client may follow up with a write operation to send back whatever data that the server needs.
One quick note about how the various BGAPI functions are named: if it starts with "attclient_", then it relates to an operation performed on or by the GATT client. If it stars with "attributes_", then it relates to an operation performed on or by the GATT server.
The above four BLE data transfer operations are described here. Commands which you must send are shown separately from the events which the stack generates:
Read - this operation is requested by the GATT client on a specific attribute exposed by the GATT server, and the server then responds with the requested value. In our BLE stack, these API methods are typically involved in read operations:
attclient_read_by_handle command - Reads a remote attribute's value with the given handle. This can be used to read attributes up to 22 bytes in length. Read operations requested by the client are the only ones which can request this much data in one packet; other operations (write/notify/indicate) are limited to 20 bytes total. This is the most common method used by a GATT client to read individual attribute values.
attclient_read_by_type command - Reads the value of each attribute of a given type (UUID) and in a given attribute handle range. For example, you can use this to read all the characteristic declarations (UUID = 0x2803) of a given service after discovering the start/end handle boundaries with the attclient_find_by_group_type command. If you are building a GATT client device for a specific kind of application, you may never need this.
attclient_read_long command - Starts a procedure where the client first sends normal read request to the server, and if the server returns an attribute value with a length equal to the BLE MTU (22 bytes), then the client continues to send "read long" requests until rest of the attribute is read. This only applies if you are reading attributes that are longer than 22 bytes. It is often simpler to construct your GATT server such that there are no long attributes, for simplicity. Note that the BLE protocol still requires that data is packetized into max. 22-byte chunks, so using "read long" does not save transmission time.
attclient_read_multiple command - Reads multiple attributes from a GATT server. This takes a list of attribute handles for input and requests all of the resulting values back at once in a single response packet. Values are sent back in the order they are requested, with no delineation between different values. Any data beyond the maximum read packet size (22 bytes) will be silently dropped. This also requires that all requested attributes (except optionally the last one) have a known size, since no value delineation is used, and that all values combined fit within 22 bytes or less.
attclient_read_multiple_response event - Generated when the above attclient_read_multiple command is used. This event will occur exactly once for the entire "read multiple" request, delivering a maximum of 22 bytes.
attributes_user_read_request event - Generated on the GATT server when a client requests reading data from a characteristic whose "type" value is set to "user" in gatt.xml. Normally, this operation is handled by the stack automatically, and the characteristic value stored in RAM right on the module is sent back to the client. But with "user" characteristics, this data storage and retrieval is entirely up to you. The stack simply lets you know when it has been requested, and then you must get it (or generate it), then send it back (or not) in whatever format is desired. This can be useful for immediate I/O status reads, for example, so that instead of maintaining a value in memory or polling all the time so it is kept "fresh," you can instead just wait for the request and read the I/O status on the spot when it comes in. (ONLY applies if type="user" is set on the characteristic's <value> tag in gatt.xml)
attributes_user_read_response command - Used to send back the desired data when an attributes_user_read_request is generated by a client's request on a "user" characteristic. This command takes a result code and the actual value, if any, to send back. (ONLY applies if type="user" is set on the characteristic's <value> tag in gatt.xml)
Write - this operation is requested by the GATT client on a specific attribute exposed by the GATT server, and a new value to write is provided at the same time. The server then stores the new value and (optionally) acknowledges the write operation back to the client. In our BLE stack, these API methods are typically involved in write operations:
attclient_attribute_write command - Writes a remote attribute's value on a GATT server, up to 20 bytes in length. This performs an acknowledged write operation, so the server will respond when the value has been successfully written. This is the most common method used by a GATT client to write individual attribute values.
attclient_procedure_completed event - Triggered on the GATT client when the server acknowledges a standard (acknowledged) write operation, performed with the attclient_attribute_write command directly above. This event does not occur when using the unacknowledged attclient_write_command command directly below. This event is useful for controlling program flow based on whether a critical write operation has finished yet or not.
attclient_write_command command - Writes a remote attribute's value on a GATT server, up to 20 bytes in length. This is exactly the same as the above command except that it is unacknowledged. There will be no response upon completion. Like the "notify" operation, this means that it is faster (multiple unacknowledged writes may be performed within a single connection interval), but less reliable than acknowledged writes.
attclient_prepare_write command - Queue a particular write operation for execution later as part of a group of writes. It is effectively the same as a single write operation, but not executed immediately. You can queue only up to 18 bytes at a time to be written, and you specify the offset as well as the data. The attclient_procedure_completed event occurs when the preparation is done. This only necessary when you need to write more than 20 bytes to a single attribute, since it gives you the ability to write at a certain offset. You have to break up the write operation into 18-byte (or smaller) chunks.
Here is an example of using prepare/execute write to write a 30-byte value to the attribute with handle=10, first 18 bytes at offset 0 and then 12 bytes at offset 18:
attclient_execute_write command - Executes (argument=1) or cancels (argument=0) any pending write operations queued with attclient_prepare_write. The stack handles sending the queued writes in multiple packets automatically when you run this command, and it will take as many connection intervals as necessary. The "attclient_procedure_completed" event occurs when the execution is done. On the server side, this looks like a rapid series of write operations, and is handled the same way (acknowledging each one).
attributes_value event - Triggered on the GATT server when the client writes a new value to a particular characteristic. This is the event you can use to tell when an characteristic has been updated, and then respond accordingly (parse the new value for command data, set I/O pins based on the stored data, close the connection, begin reading ADC measurements, etc.). Note that if type="user" is set on the characteristic's <value> tag in gatt.xml, then the new written value from the client will be provided as one of the parameters of this event, but the value itself will not be stored in RAM automatically by the stack. It is up to you to store or otherwise process the value, and acknowledge as necessary.
attributes_user_write_response command - Used to send back an appropriate acknowledgement result code when an attributes_value event is generated by a client's write operation on a "user" characteristic. If this manual acknowledgement is necessary, then you should typically call this command in the same code as the event handler for the related attributes_value event. (ONLY applies if type="user" is set on the characteristic's <value> tag in gatt.xml)
Notify - this operation is initiated by the server when a new value is written to a notify-enabled characteristic. If the client has subscribed to notifications on that characteristic, then the new value is pushed to the client when it is written. Notifications are not acknowledged, but you may sent more than one notification in a single connection interval, which can be helpful for maximizing throughput. Notifications cannot be enabled by the server; they must be enabled by the client in order for the actual data transmission to occur. In our BLE stack, these API methods are typically involved in notify operations:
attributes_writecommand- Writes a local attribute's value on the GATT server, up to 255 bytes in length (BGAPI internal parser limits this to 64 bytes at a time, though offsets may be used). This command alone only affects the data stored locally, but if the GATT client has subscribed to notifications, then this also triggers pushing the data to the remote GATT client. This is the command you will use to push data once notifications (or indications, see below) have been enabled by the client.
attributes_status event - Triggered on the GATT server when the remote GATT client changes the notification (or indication) subscription status. If the updated status equals 1, then notifications are enabled. If it equals 2, then indications are enabled. If it equals 0, then neither are enabled. It is technically possible to use a value of 3 to enable both, but this is not typically useful because GATT servers generally only enable one or the other. (iOS devices will only use notifications if both are available.)
attclient_attribute_value event - Triggered on the GATT client when the remote GATT server pushes a new value via notification (or indication). This event is how the client knows it has received an updated value. The pushed data will be made available as part of the event's parameters. Remember that for notifications and indications, only up to 20 bytes may be pushed. It is not possible to push more than 20 bytes.
Indicate - an indicate operation is identical to a notify operation except that indications are acknowledged, while notifications are not. This increases reliability at the expense of speed. In our BLE stack, these API methods are typically involved in indicate operations:
attributes_write command - Writes a local attribute's value on the GATT server, up to 255 bytes in length (BGAPI internal parser limits this to 64 bytes at a time, though offsets may be used). This alone only affects the data stored locally, but if the GATT client has subscribed to indications, then this also triggers pushing the data to the remote GATT client. This is the command you will use to push data once indications (or notifications, see above) have been enabled by the client. Remember that indications are acknowledged, so you may do this only once within a given connection interval. It will be followed up by the attclient_indicated event when the acknowledgement comes back from the GATT client.
attributes_status event - Triggered on the GATT server when the remote GATT client changes the indication (or notification) subscription status. If the updated status equals 1, then notifications are enabled. If it equals 2, then indications are enabled. If it equals 0, then neither are enabled. It is technically possible to use a value of 3 to enable both, but this is not typically useful because GATT servers generally only enable one or the other. (iOS devices will only use notifications if both are available.)
attclient_attribute_value event - Triggered on the GATT client when the remote GATT server pushes a new value via indication (or notification). This event is how the client knows it has received an updated value. The pushed data will be made available as part of the event's parameters. Remember that for notifications and indications, only up to 20 bytes may be pushed. It is not possible to push more than 20 bytes.
attclient_indicate_confirm command - Sends an indication confirmation from the GATT client back to the remote GATT server, after an indicated value has arrived at the client (with an attclient_attribute_value event). Normally, this is handled automatically by the BLE stack running on the module. However, it is possible to configure this to require a manual command, using the <manual_confirm> tag in your project's config.xml file. Why? Sometimes there are specific data flow control triggers you need to deal with very carefully, such as in the "cable_replacement" example project in our BLE SDK archive. Controlling precisely when the confirmation occurs and what happens before and after it may be valuable.
attclient_indicated event - Triggered on the GATT server when the remote GATT client confirms having received the data previously pushed via an indication. Very often, this event is ignored, but as mentioned in the previous item, sometimes it is valuable to know precisely when this occurs for data flow control purposes (as in the "cable_replacement" example project).
Typically, the GATT server functionality is provided by one side and the client functionality is entirely on the other side, but it could be that both sides provide both kinds of functionality. This does not usually pose any advantages for a well-designed and efficient BLE project—it usually complicates the implementation needlessly—so we will not go into it here.
Example Custom GATT Server Definition
Let us consider the very typical example case of the Bluegiga BLE112 module being used as a peripheral device, meant to be controlled from a BLE-enabled smartphone such as an iPhone 5 or Android 4.3+ device. Further, we will say that the following is true about the application:
The BLE112 will wait for a connection from the smartphone.
The smartphone will send simple mode-changing commands to the BLE112 to enable certain features. These commands are 4 bytes long.
The smartphone will be able to get or set digital I/O pin states on the BLE112. There will be one byte for each port on the module, or 3 bytes.
The BLE112 will report its own battery status periodically to the smartphone, without this status being requested every time. Only 1 byte, for %.
This gives us enough information to plan out and then build the GATT structure:
From point #1, we see that the BLE112 will advertise, and the smartphone will scan and initiate the connection. This means that the BLE112 is the slave (peripheral), and the smartphone is the master (central).
The data flow is largely controlled by the smartphone, but the data in question is actually generated by the BLE112, and/or meant to be used on it. This is a very good indication that the BLE112 is the GATT server, and the smartphone is the GATT client. Remember, the server holds and manages the data, while the client requests and works with it.
From point #2, we see that the BLE112 (GATT server) will need a 4-byte writable characteristic so the client can send commands.
From point #3, we see that the BLE112 (GATT server) will need a 3-byte characteristic that is both readable and writable, for I/O pin states. Furthermore, this characteristic will use a user-type value, so we can directly read I/O pin states when they are requested by the client, and directly write I/O pin states when they are sent from the client. Remember that user-type values are not automatically maintained in RAM for reading or processed when written, and that we are left to implement that part of the functionality.
From point #4, we see that the BLE112 (GATT server) will need a 1-byte characteristic that supports pushing data to the client (e.g. notifications).
Now that we have this basic plan, we should consider the easiest way to accomplish it. Can we reuse anything that has already been defined by the Bluetooth SIG? There isn't any "General IO Pin" service, or "General Mode Config" service, so those will need to be custom. However, there is an official Battery service already, which we can (and should) use. This service has a UUID of 0x180F. It has a single 1-byte characteristic which must be readable (for polling the battery level) and may optionally support notifications for pushing battery updates. This will be perfect. (There is an example project in the latest BLE SDK found in the "/example/battery" folder which implements exactly this.)
We will also need to include at least the universal Generic Access service to include the device name and appearance, as well as a custom service for the missing pieces (points #2 and #3 above). For the custom part, we will need a total of three 128-bit UUIDs: one for the service, and two for the two characteristics the service will contain. We can use these UUIDs, freshly minted from guidgenerator.com:
624e957f-cb42-4cd6-bacc-84aeb898f69b
e4c937b3-7f6d-41f9-b997-40c561f4453b
df342b03-53f9-43b4-acb6-62a63ca0615a
Also, we'll want to make our custom service UUID advertised automatically by the BLE stack. This will make it easy to filter while scanning on the smartphone so that only peripheral devices which include that unique ID will appear in the scan results. This is done by adding ' advertise="true" ' to the <service> tag in our GATT definition.
Our final gatt.xml content to include all of these things will look like this:
For a detailed explanation of exactly what all of the different tags here mean, please read through the Profile Developer Toolkit Guide available on the Tech Forum. In this document, you will find definitions of everything used here.
Bluetooth Low Energy
Knowledge Base Articles
Bluetooth Classic
Is it possbble that we can have slave of other the Silicon labs?
Can two modules one from silicon labs and another from some other company can communicate?
0
yes Usman, I've tested ESP32 with blue gecko, both can communicate, any BLE device should be able to send/receive data to silabs BLE devices.
0
Hello Bluegiga Team,
I am using BLE112 to communicate from multiple sensor to one central master which handle all the data. I would also like to create an Android App that will communicate with this central master to display all the data coming from the sensor.
What would be the proper architecture? I think that making all the sensors + the tablet the peripherals, and keeping my central system as a master would work but how notify from the central to the android app. Can the central be both server and client or should I use the write functionnality to display data on my android App? What if I would like to use the app to configure my Central device (with the read functionnality?)?
BLE master/slave, GATT client/server, and data RX/TX basics
Bluetooth Low Energy is a powerful technology, but not always the easiest to understand and use effectively. It is not like classic Bluetooth where you have a predefined set of official profiles to choose from; although there are predefined (a.k.a. "adopted") profiles specified by the Bluetooth SIG, these are just the tip of the iceberg, a small subset of the kind of functionality you can achieve with BLE.
In many (or even most) cases, the best option is to create your own custom profile(s) for your application. This gives you ultimate flexibility, and it doesn't even cost anything. In fact, it can be even easier than using one of the adopted profiles, since you get to define exactly how everything works together rather than conforming your application into something that is already defined. Also, since there is no official generic "serial port profile" in the BLE world like SPP in classic Bluetooth, sometimes a custom implementation is the only option to do what you need.
In order to do this effectively, it is important to understand how a BLE connection works, what roles are played by the devices involved, and how exactly data is transferred from one device to the other over the air. Many terms are used, and they are usually not interchangeable: master, slave, central, peripheral, client, server, advertise, scan, read, write, notify, and indicate all mean different things. Knowing the difference will make it easier to describe and build your BLE application.
Quick Overview
Here's what this article covers:
Master vs. Slave - Connection Roles
One important concept in BLE connectivity is the difference between a master device and a slave device. What do each of these terms imply? First, realize that they are not interchangeable with client/server, which will be explained below. Actually, in the BLE world, the master/slave difference is very easy to define and recognize:
These pairs of terms are the only ones in the list above which are actually interchangeable. References to a "master" or "central" device are describing the same thing, and references to a "slave" or "peripheral" device are also each describing the same thing. In the CoreBluetooth APIs provided by Apple for iOS development, the "Central" and "Peripheral" nomenclature is used; you will not generally see references to master or slave devices in this context.
One important distinction between the master and slave device in a BLE network is that a slave may only be connected to a single master, but a master may be connected to multiple slaves. The BLE specification does not limit the number of slaves a master may connect to, but there is always a practical limitation, especially on small embedded modules. Our v1.1 BLE stack can support up to 8 simultaneous connections as a master device, when properly configured.
The connection role--whether a device is a master or slave--is defined the moment the connection is made. Our stack is capable of acting either as a master or as a slave device. If a device is operating as a slave, it needs to advertise (accomplished in our BLE stack with the gap_set_mode command); if it is operating as a master, it will optionally scan for devices (accomplished in our stack with gap_discover) and initiate a connection request to another device (accomplished in our stack with gap_connect_direct).
Client vs. Server - GATT Functionality
Another important concept in a BLE design is the difference between a GATT server and a GATT client. These roles are not mutually exclusive, though typically your device will only be one or the other. Which role(s) your device takes depends on how you need it to work. Here is a basic summary of each kind of functionality:
Unlike the master/slave distinction defined previously, it is easy to see that one device might actually be both of these things at the same time, based on how your application defines the data structure and flow for each side of the connection. While it is most common for the slave (peripheral) device to be the GATT server and the master (center) device to be the GATT client, this is not required. The GATT functionality of a device is logically separate from the master/slave role. The master/slave roles control how the BLE radio connection is managed, and the client/server roles are dictated by the storage and flow of data.
Most of our example projects in the SDK archive and online implement slave (peripheral) devices designed to be GATT servers. These are easy to test with many of the freely available scanning apps for the iOS platform, and upcoming apps for the Android platform now that official BLE APIs have been implemented in Android 4.3. However, we do have a few examples which implement the master (central) end of the connection, designed to be GATT clients. These include the "thermometer-demo" project in the /src folder of the SDK archive, and this BGScript-based Health Thermometer Collector.
There are also some 3rd-party unofficial BLE master examples available for Python and Visual C# .NET designed to control our modules externally using the BGAPI protocol (see this KB article for more info).
Receive vs. Transmit - Moving Data
Now that we have established the difference between a master and slave device, and a GATT client and GATT server, how do we actually define the GATT structure, and how to we use it to move data from Device A to Device B, whether that means server-to-client or client-to-server? Both directions are easily possible, of course.
In BLE projects built using our SDK, the GATT structure is defined in the "gatt.xml" file that is part of your project's source files. The structure of this file is documented in the Profile Development Kit Developer Guide, available on the BLE112 and BLE113 pages of our Tech Forum. The structure and flow of data is always defined on the GATT server, and the client simply gets to make use of whatever is exposed by the server.
GATT structure
A GATT database implements one or more profiles, and each profile is made up of one or more services, and each service is made up of one or more characteristics. For example, in outline form:
You can implement as many profiles, services, and characteristics as you need. These may be entirely customized, in which case they would use your own 128-bit UUIDs generated at a site such as guidgenerator.com. Or, you can create a project which implements adopted specifications by referencing the Bluetooth SIG's online definitions of profiles, services, and characteristics. (If you do this, it usually makes the most sense to start at the profile level and drill down from there, since the full list of characteristics includes many things that will undoubtedly be irrelevant to your design.) You can combine these two as well, using some adopted profiles/services and some of your own proprietary ones.
Every BLE device acting as a GATT server must implement the official Generic Access service. This includes two mandatory characteristics: Device Name and Appearance.These are similar to the Friendly Name and Class of Device values used by classic Bluetooth. Here is an example definition of an absolutely minimal GATT definition using our gatt.xml format and providing the Generic Access service entries:
This alone is enough to work with, but it won't let you do anything useful other than tell the GATT client who and what you are. We still need to explore basic data transfer methods that we can use. First though, one minor clarification between attributes and characteristics.
Attributes and Characteristics
You may occasionally hear or see the terms "attribute" and "characteristic" used interchangeably, and while this isn't entirely wrong, it isn't totally accurate and it can be confusing. Remember that a service is made up of one or more characteristics. However, one single characteristic--generally the most specific level down to which we define our GATT structure--may be comprised of many different attributes.
Each attribute is given a unique numerical handle which the GATT client may use to reference, access, and modify it. Every characteristic has one main attribute which allows access to the actual value stored in the database for that characteristic. When you read about "writing a value to this characteristic" or "reading that characteristic's value," the read/write operations are really being done to the main data attribute.
Some other related attributes are read-only (such as a Characteristic User Description attribute), some control the behavior of the characteristic (such as the Client Characteristic Configuration attribute which is used to enable notify or indicate operations). Our BLE stack and SDK tools generate these as necessary based on the contents of your project's gatt.xml file.
Every attribute has a UUID. These may be either 16 bits (e.g. "180a") or 128 bits (e.g. "e7add780-b042-4876-aae1-112855353cc1"). All 16-bit UUIDs are defined by the Bluetooth SIG and are known as adopted UUIDs. All 128-bit UUIDs are custom and may be used for any purpose without approval from the Bluetooth SIG. Two very common 16-bit UUIDs that you will see are 2901, the Characteristic User Description attribute (defined in gatt.xml with the <description> tag), and 2902, the Client Characteristic Configuration attribute (created by our SDK when either "notify" or "indicate" is enabled on a characteristic).
One important note is that some attribute UUIDs do not technically need to be unique. Their handles are always unique, but the UUIDs occasionally overlap. For example, every Client Characteristic Configuration attribute has a UUID of 0x2902, even though there may be a dozen of them in a single GATT database. You should always give your own custom characteristics fully unique UUIDs, but don't be alarmed if you are testing out your prototype with BLEGUI, you perform a Descriptor Discovery operation, and suddenly see multiple instances of the same UUID for certain things. This is normal.
Data Transfer Methods
There are four basic operations for moving data in BLE: read, write, notify, and indicate. The BLE protocol specification requires that the maximum data payload size for these operations is 20 bytes, or in the case of read operations, 22 bytes. BLE is built for low power consumption, for infrequent short-burst data transmissions. Sending lots of data is possible, but usually ends up being less efficient than classic Bluetooth when trying to achieve maximum throughput.
Here are a few general guidelines about what kind of data transfer you will need to use:
One quick note about how the various BGAPI functions are named: if it starts with "attclient_", then it relates to an operation performed on or by the GATT client. If it stars with "attributes_", then it relates to an operation performed on or by the GATT server.
The above four BLE data transfer operations are described here. Commands which you must send are shown separately from the events which the stack generates:
attclient_read_by_handle
command - Reads a remote attribute's value with the given handle. This can be used to read attributes up to 22 bytes in length. Read operations requested by the client are the only ones which can request this much data in one packet; other operations (write/notify/indicate) are limited to 20 bytes total. This is the most common method used by a GATT client to read individual attribute values.attclient_read_by_type
command - Reads the value of each attribute of a given type (UUID) and in a given attribute handle range. For example, you can use this to read all the characteristic declarations (UUID = 0x2803) of a given service after discovering the start/end handle boundaries with the attclient_find_by_group_type command. If you are building a GATT client device for a specific kind of application, you may never need this.attclient_read_long
command - Starts a procedure where the client first sends normal read request to the server, and if the server returns an attribute value with a length equal to the BLE MTU (22 bytes), then the client continues to send "read long" requests until rest of the attribute is read. This only applies if you are reading attributes that are longer than 22 bytes. It is often simpler to construct your GATT server such that there are no long attributes, for simplicity. Note that the BLE protocol still requires that data is packetized into max. 22-byte chunks, so using "read long" does not save transmission time.attclient_read_multiple
command - Reads multiple attributes from a GATT server. This takes a list of attribute handles for input and requests all of the resulting values back at once in a single response packet. Values are sent back in the order they are requested, with no delineation between different values. Any data beyond the maximum read packet size (22 bytes) will be silently dropped. This also requires that all requested attributes (except optionally the last one) have a known size, since no value delineation is used, and that all values combined fit within 22 bytes or less.attclient_read_multiple_response
event - Generated when the above attclient_read_multiple command is used. This event will occur exactly once for the entire "read multiple" request, delivering a maximum of 22 bytes.attributes_user_read_request
event - Generated on the GATT server when a client requests reading data from a characteristic whose "type" value is set to "user" in gatt.xml. Normally, this operation is handled by the stack automatically, and the characteristic value stored in RAM right on the module is sent back to the client. But with "user" characteristics, this data storage and retrieval is entirely up to you. The stack simply lets you know when it has been requested, and then you must get it (or generate it), then send it back (or not) in whatever format is desired. This can be useful for immediate I/O status reads, for example, so that instead of maintaining a value in memory or polling all the time so it is kept "fresh," you can instead just wait for the request and read the I/O status on the spot when it comes in. (ONLY applies if type="user" is set on the characteristic's <value> tag in gatt.xml)attributes_user_read_response
command - Used to send back the desired data when an attributes_user_read_request is generated by a client's request on a "user" characteristic. This command takes a result code and the actual value, if any, to send back. (ONLY applies if type="user" is set on the characteristic's <value> tag in gatt.xml)attclient_attribute_write
command - Writes a remote attribute's value on a GATT server, up to 20 bytes in length. This performs an acknowledged write operation, so the server will respond when the value has been successfully written. This is the most common method used by a GATT client to write individual attribute values.attclient_procedure_completed
event - Triggered on the GATT client when the server acknowledges a standard (acknowledged) write operation, performed with the attclient_attribute_write command directly above. This event does not occur when using the unacknowledged attclient_write_command command directly below. This event is useful for controlling program flow based on whether a critical write operation has finished yet or not.attclient_write_command
command - Writes a remote attribute's value on a GATT server, up to 20 bytes in length. This is exactly the same as the above command except that it is unacknowledged. There will be no response upon completion. Like the "notify" operation, this means that it is faster (multiple unacknowledged writes may be performed within a single connection interval), but less reliable than acknowledged writes.attclient_prepare_write
command - Queue a particular write operation for execution later as part of a group of writes. It is effectively the same as a single write operation, but not executed immediately. You can queue only up to 18 bytes at a time to be written, and you specify the offset as well as the data. The attclient_procedure_completed event occurs when the preparation is done. This only necessary when you need to write more than 20 bytes to a single attribute, since it gives you the ability to write at a certain offset. You have to break up the write operation into 18-byte (or smaller) chunks.Here is an example of using prepare/execute write to write a 30-byte value to the attribute with handle=10, first 18 bytes at offset 0 and then 12 bytes at offset 18:
attclient_prepare_write(0, 10, 0, 18, "abcdefghijklmnopqr")
attclient_prepare_write(0, 10, 18, 12, "stuvwxyz1234")
attclient_execute_write(1)
attclient_execute_write
command - Executes (argument=1) or cancels (argument=0) any pending write operations queued with attclient_prepare_write. The stack handles sending the queued writes in multiple packets automatically when you run this command, and it will take as many connection intervals as necessary. The "attclient_procedure_completed" event occurs when the execution is done. On the server side, this looks like a rapid series of write operations, and is handled the same way (acknowledging each one).attributes_value
event - Triggered on the GATT server when the client writes a new value to a particular characteristic. This is the event you can use to tell when an characteristic has been updated, and then respond accordingly (parse the new value for command data, set I/O pins based on the stored data, close the connection, begin reading ADC measurements, etc.). Note that if type="user" is set on the characteristic's <value> tag in gatt.xml, then the new written value from the client will be provided as one of the parameters of this event, but the value itself will not be stored in RAM automatically by the stack. It is up to you to store or otherwise process the value, and acknowledge as necessary.attributes_user_write_response
command - Used to send back an appropriate acknowledgement result code when an attributes_value event is generated by a client's write operation on a "user" characteristic. If this manual acknowledgement is necessary, then you should typically call this command in the same code as the event handler for the related attributes_value event. (ONLY applies if type="user" is set on the characteristic's <value> tag in gatt.xml)attributes_write
command - Writes a local attribute's value on the GATT server, up to 255 bytes in length (BGAPI internal parser limits this to 64 bytes at a time, though offsets may be used). This command alone only affects the data stored locally, but if the GATT client has subscribed to notifications, then this also triggers pushing the data to the remote GATT client. This is the command you will use to push data once notifications (or indications, see below) have been enabled by the client.attributes_status
event - Triggered on the GATT server when the remote GATT client changes the notification (or indication) subscription status. If the updated status equals 1, then notifications are enabled. If it equals 2, then indications are enabled. If it equals 0, then neither are enabled. It is technically possible to use a value of 3 to enable both, but this is not typically useful because GATT servers generally only enable one or the other. (iOS devices will only use notifications if both are available.)attclient_attribute_value
event - Triggered on the GATT client when the remote GATT server pushes a new value via notification (or indication). This event is how the client knows it has received an updated value. The pushed data will be made available as part of the event's parameters. Remember that for notifications and indications, only up to 20 bytes may be pushed. It is not possible to push more than 20 bytes.attributes_write
command - Writes a local attribute's value on the GATT server, up to 255 bytes in length (BGAPI internal parser limits this to 64 bytes at a time, though offsets may be used). This alone only affects the data stored locally, but if the GATT client has subscribed to indications, then this also triggers pushing the data to the remote GATT client. This is the command you will use to push data once indications (or notifications, see above) have been enabled by the client. Remember that indications are acknowledged, so you may do this only once within a given connection interval. It will be followed up by the attclient_indicated event when the acknowledgement comes back from the GATT client.attributes_status
event - Triggered on the GATT server when the remote GATT client changes the indication (or notification) subscription status. If the updated status equals 1, then notifications are enabled. If it equals 2, then indications are enabled. If it equals 0, then neither are enabled. It is technically possible to use a value of 3 to enable both, but this is not typically useful because GATT servers generally only enable one or the other. (iOS devices will only use notifications if both are available.)attclient_attribute_value
event - Triggered on the GATT client when the remote GATT server pushes a new value via indication (or notification). This event is how the client knows it has received an updated value. The pushed data will be made available as part of the event's parameters. Remember that for notifications and indications, only up to 20 bytes may be pushed. It is not possible to push more than 20 bytes.attclient_indicate_confirm
command - Sends an indication confirmation from the GATT client back to the remote GATT server, after an indicated value has arrived at the client (with an attclient_attribute_value event). Normally, this is handled automatically by the BLE stack running on the module. However, it is possible to configure this to require a manual command, using the <manual_confirm> tag in your project's config.xml file. Why? Sometimes there are specific data flow control triggers you need to deal with very carefully, such as in the "cable_replacement" example project in our BLE SDK archive. Controlling precisely when the confirmation occurs and what happens before and after it may be valuable.attclient_indicated
event - Triggered on the GATT server when the remote GATT client confirms having received the data previously pushed via an indication. Very often, this event is ignored, but as mentioned in the previous item, sometimes it is valuable to know precisely when this occurs for data flow control purposes (as in the "cable_replacement" example project).Typically, the GATT server functionality is provided by one side and the client functionality is entirely on the other side, but it could be that both sides provide both kinds of functionality. This does not usually pose any advantages for a well-designed and efficient BLE project—it usually complicates the implementation needlessly—so we will not go into it here.
Example Custom GATT Server Definition
Let us consider the very typical example case of the Bluegiga BLE112 module being used as a peripheral device, meant to be controlled from a BLE-enabled smartphone such as an iPhone 5 or Android 4.3+ device. Further, we will say that the following is true about the application:
This gives us enough information to plan out and then build the GATT structure:
Now that we have this basic plan, we should consider the easiest way to accomplish it. Can we reuse anything that has already been defined by the Bluetooth SIG? There isn't any "General IO Pin" service, or "General Mode Config" service, so those will need to be custom. However, there is an official Battery service already, which we can (and should) use. This service has a UUID of 0x180F. It has a single 1-byte characteristic which must be readable (for polling the battery level) and may optionally support notifications for pushing battery updates. This will be perfect. (There is an example project in the latest BLE SDK found in the "/example/battery" folder which implements exactly this.)
We will also need to include at least the universal Generic Access service to include the device name and appearance, as well as a custom service for the missing pieces (points #2 and #3 above). For the custom part, we will need a total of three 128-bit UUIDs: one for the service, and two for the two characteristics the service will contain. We can use these UUIDs, freshly minted from guidgenerator.com:
Also, we'll want to make our custom service UUID advertised automatically by the BLE stack. This will make it easy to filter while scanning on the smartphone so that only peripheral devices which include that unique ID will appear in the scan results. This is done by adding ' advertise="true" ' to the <service> tag in our GATT definition.
Our final gatt.xml content to include all of these things will look like this:
For a detailed explanation of exactly what all of the different tags here mean, please read through the Profile Developer Toolkit Guide available on the Tech Forum. In this document, you will find definitions of everything used here.
Is it possbble that we can have slave of other the Silicon labs?
Can two modules one from silicon labs and another from some other company can communicate?
Hello Bluegiga Team,
I am using BLE112 to communicate from multiple sensor to one central master which handle all the data. I would also like to create an Android App that will communicate with this central master to display all the data coming from the sensor.
What would be the proper architecture? I think that making all the sensors + the tablet the peripherals, and keeping my central system as a master would work but how notify from the central to the android app. Can the central be both server and client or should I use the write functionnality to display data on my android App? What if I would like to use the app to configure my Central device (with the read functionnality?)?
Thanks for your advices.