≡ Menu

How to Develop Bluetooth Low Energy Stack TI CC2541 Custom Profile

BLE stands for Bluetooth Low Energy.

BLE is relatively newer technology compared to the classic Bluetooth we have come to know in recent years in terms of development and internal operation.

Also, BLE is not backwards compatible with classic Bluetooth. Hence, most device manufacturers couple classic and BLE on a single chip to allow users to use either.

CC2541 is a solution in the form of a SoC (system on chip) with related software from Texas Instruments Inc. to enable developers to leverage the main advantages of BLE and build proprietary applications.

An understanding of the BLE stack structure is essential in this regard. The following diagram shows the BLE stack.

Image Courtesy: Texas Instruments (http://www.ti.com/lit/ug/swru271f/swru271f.pdf)

Image Courtesy: Texas Instruments (http://www.ti.com/lit/ug/swru271f/swru271f.pdf)

The Physical Layer consists of BLE hardware including balun and antenna. Link Layer and all other upper layers are implemented in firmware (stack) residing on the SoC.

BLE Terminology


A set of rules that govern communication between two layers of the stack and with the external interfaces.


A set of specifications regarding an aspect of communication. All profiles use, at minimum, all layers of the stack except, maybe, Security Manager. Profiles in classic Bluetooth include AVRCP(Audio/Video remote control), A2DP(Advanced audio distribution profile), SPP (Serial-port Profile) etc. which enable developers to exchange specific or proprietary data between two or more devices in a star topology wireless network.

In BLE however, there are no such profiles with the conspicuous absence of serial-port profile which allowed proprietary data exchange. Instead, BLE provides a flexible Generic Attribute Profile (GATT) and a Generic Access Profile (GAP) that can be suited to an application’s requirements.

UUID (Universally Unique Identifier)

A 128-bit random number that identifies a known set of data. In the case of BLE, it identifies a service and a characteristic.

GATT Profile and Notifications

GATT Profile defines a set of guidelines for two connected BLE devices, one acting as a server and the other acting as a client. GATT profile contains a pseudo-database called “Attributes” within which data is grouped as shown below:

TI BLE Profile

Characteristics may have multiple descriptors and are all associated with a 128-bit UUID which can be likened to an address. This UUID provides the developer access to the services and the values contained within the characteristics. The method in which the characteristics are sent out from one BLE device (server) to another (client) is called a “Notification”.


A piece of data that encapsulates the pseudo-database shown in the figure above (consisting of Service, Characteristic, value and descriptor declarations and values).

CC2541 Firmware Summary

The CC2541 firmware block diagram is as shown below:

TI BLE Firmware

Operating-system abstraction layer

OSAL is a piece of firmware that controls all the stack layers, schedules their tasks and priorities, allows communication of higher stack layers with lower layers by providing the ability to register callback functions. The operating-system abstraction layer also allows developers to insert custom-applications into the system with the caveat that all BLE protocol stack-related tasks have a higher priority than the proprietary application. The OSAL also exposes proprietary methods that allow for system memory-handling tasks.


A set of libraries based on BLE device roles are available which are mandatorily used in all CC2541 application projects. These libraries implement the lower layers of BLE stack including Link Layer, HCI, L2CAP and parts of Security manager. The source code for these libraries however is not made available to developers at the time of writing this article.

Open-source stack and hardware Drivers

The open-source stack and driver firmware consists of the ATT, GAP and GATT layers, all governed by the OSAL is divided into the following modules:


Main module with all files and methods that initialize the hardware and OSAL

Hardware Abstraction Layer

Contains APIs that expose hardware configuration methods to the developer

Operating System Abstraction Layer

Contains APIs that expose most of the higher-level stack methods and provides an entry-point for the developer to insert proprietary tasks and applications.


Contains example applications, example custom- profiles and device information service APIs.

Creating Custom Profiles

To create a new application to exchange proprietary data between two BLE-enabled devices, it is necessary to create a custom profile. Texas instruments provides source-code for an example profile called the Simple BLE Peripheral profile.

In this example, a 16-bit UUID (which is converted to a 128-bit UUID by the library) is used to identify characteristics of a service, called the Simple BLE Peripheral. Four characteristics are defined under this service with the values of the first three being 1 byte long and the final one 5 bytes long.

This application is added as a task to the end of the OSAL queue, to be executed with least priority. During the initialization phase, a timer (time interval can be set by the user) is started to indicate execution time for the application. When the task is up for execution, the values of the characteristics are read and if, the device is in a connection with another device, these values are transmitted to the remote device.

Using this example, developers can create their own profiles, but with the following limitations:

  1. Custom-applications should have least priority in the OSAL queue
  2. The data transmitted by a custom application (characteristic-value) in one burst may be a maximum of 20-bytes

A good place to start would be creating custom-UUIDs and its associated data structures. One important pint to note here would be that the UUIDs are stored LSB first.

For example for a UUID like ffa39284-f059-4abd-980f-ef28e4de9f66, the array would be:

uint8 UUID_MYSERVICE[16] = { 0x66, 0x9F, 0xDE, 0xE4, 0x28, 0xEF, 0x0F, 0x98, 0xBD, 0x4A, 0x59, 0xF0, 0x84, 0x92, 0xA3, 0xFF }

Two UUID arrays wil have to be created and associated with a service and a characteristic respectively.

TI’s example uses 16-bit UUIDs and these can be re-used for the service-characteristics-values pseudo database as follows:

gattAttribute_t myCustomProfileAttributeTable[4] = {
  //my custom service
  {    { 2 , primaryServiceUUID }, /*length of UUID in bytes, type */
    GATT_PERMIT_READ,              /*permissions */
    0,                             /*handle */
    {16, myCustomServiceUUID }     /*length of UUID in bytes, value */

// my Custom Characteristic declaration
{  { 2, characterUUID },
      &myCustomCharacteristicProperties	/* Characteristic properties (Read/write)*/

// my Custom characteristic value
{  { 16, myCustomcharcteristicUUID },
  <Any data that needs to be transmitted, with a limitation => MAX SIZE of 20 bytes >

// my Custom Characteristic Descriptor
{  { 2, charUserDescUUID },
  <A brief descriptor string or char array less than 20 bytes>

In the above, All UUIDs in bold are already defined by TI. Those in bold italics need to be generated by the developer as mentioned before.

gattAttribute_t is a format defined by TI as:

typedef struct attAttribute_t
  gattAttrType_t type;  // Attribute type (2 or 16 octet UUIDs)
  uint8 permissions;    // Attribute permissions
  uint16 handle;        // Attribute handle - assigned internally by attribute server
  uint8* const pValue;  // Attribute value - encoding of the octet array is defined in 
                      	 // the applicable profile. The maximum length of an attribute 
                       // value shall be 512 octets.
} gattAttribute_t;

gattAttribute_t structure is reproduced from open-source Texas Instruments code subject to license terms as stipulated by TI.

Having these structures in place, use the API to pack and send your custom data from one device to another.

Note: Code used in this article includes Texas Instrument’s cc2541-related open_source code subject to all license terms stipulated by texas instruments inc. on thier website.

For additional information, refer to TI BLE firmware software stack and tools. Under the key document section of that page, it also has this document: CC2540/41 Bluetooth Low Energy Software Developer’s Guide v1.3.2.

Add your comment

If you enjoyed this article, you might also like..

  1. 50 Linux Sysadmin Tutorials
  2. 50 Most Frequently Used Linux Commands (With Examples)
  3. Top 25 Best Linux Performance Monitoring and Debugging Tools
  4. Mommy, I found it! – 15 Practical Linux Find Command Examples
  5. Linux 101 Hacks 2nd Edition eBook Linux 101 Hacks Book

Bash 101 Hacks Book Sed and Awk 101 Hacks Book Nagios Core 3 Book Vim 101 Hacks Book

Comments on this entry are closed.

  • laxmidhar July 17, 2014, 10:04 am

    I am developing a android application to communicate with Ble(cc2541).But i am facing problem o send data from my app to cc2541. Can anybody help me please.


  • wunnation January 12, 2015, 2:24 pm

    Great Tutorial. Thank you!

  • Waywash June 15, 2015, 6:55 am

    Thanks! Your tutorial was very helpful. But, I want to send a 16-bit data using the SimpleBLEPeripheral code, which, I am not being to figure out how to do.
    I would be grateful to you if you could help me with this problem.

  • Ronak December 8, 2015, 10:32 am


    The data that I want to send is in buffer array. So first i use SetPrameter() function of my custom service to put that data into one of the characteristic. This char has GATT_READ and GATT_NOTIFY permission so that I can update the new value to client. After that can I just use button press event to send this data? am I correct?