Please note as of Wednesday, August 15th, 2018 this wiki has been set to read only. If you are a TI Employee and require Edit ability please contact x0211426 from the company directory.

Tutorial: How to Create a Custom Bluetooth Smart Embedded Application with the CC2650DK

From Texas Instruments Wiki
Jump to: navigation, search
This guide is deprecated and is superceded by the new SimpleLink Academy Training modules in Code Composer Studio (resource explorer). Follow the link below for more information.
SimpleLink Academy Info
</div>

Introduction

This guide is for use with the IAR Embedded Workbench IDE. For the Code Composer Guide go here: Tutorial: How to Create a Custom Bluetooth Smart Embedded Application with the CC2650DK (CCS)

This is a highly detailed step by step guide on how to create your own custom BLE profile and run it on the CC2650 device. This tutorial will take about 1 hour to complete and requires very little knowledge except some basic familiarity with embedded programming (see more details in Prerequisites). We will define our own custom profile ( the sunlight profile ) and modify the application source code in the SimpleBLEPeripheral project to include the required services and modify the behavior according to our own specification. This page is divided into all the steps required from the very beginning on just how to get the development tools up and running up to the end when you will verify the correct behavior of your application.

Compatible with BLE-STACK V2
ble_cc26xx_setupwin32_2_00_00_42893 installer (February, 2015)

NOTE:
Currently Guide refers to the use of IAR EMbedded Workbench, but guide for Code Composer Studio will be added in the next version.

Bluetooth® Developer Studio

There is a new tool from Bluetooth SIG called Bluetooth® Developer Studio. This is a graphical, GATT-based application development and debugging tool to help developers cut their education time. This will automatically generate the service source files which you have to create manually in this guide. If you just want to get quickly started with implementing your custom service, check out the Bluetooth® Developer Studio Home Page. You will need to install the Bluetooth® Developer Studio Texas Instruments Plugins to use it with the CC26xx parts.

Prerequisites

In this chapter the required hardware and software is listed in separate sub chapters. It can be useful to have a little knowledge on Bluetooth Low Energy, Embedded programming and TI-RTOS (Real Time Operating System). Please Consider looking at some of the documentation that comes with the BLE stack installer.

If you comply with the following criterias you are good to go!

  1. Basic knowledge of Bluetooth Low Energy.
  2. Basic C-programming knowledge.
  3. Basic knowledge of IAR Embedded workbench for ARM.
  4. 1-2 Hours available to complete this tutorial.


Hardware

The following hardware is required to complete this guide all of which can be found in the CC2650 Development kit (CC2650DK):

  1. CC2650EM (7X7)
  2. SmartRF06EB (PCB v1.2 or later)
  3. Micro USB cable
  4. Computer with at least two available USB ports
CC26xx Development Kit Content

The Evaluation Modules (EM) have a on-board PCB antenna meaning that you do not need any external antenna attached. Just plug the EM on the Evaluation Board (EB), plug in a micro-USB cable from your computer to the EB and turn it on.



Software

  1. IAR Embedded Workbench for ARM (version 7.40.1 or later)
  2. BLE SDK 2.0 Stack Installer (http://www.ti.com/tool/ble-stack)
  3. BTool (Will be installed by the BLE SDK Installer)
  4. Section 4 ("Using BTool") in the CC2541 Evaluation Module Kit User’s Guide describes how to use BTool in General. There will also be a very brief guide at the end of this page showing you all you need to know to complete this tutorial.


Getting Started

This chapter will guide you trough setting up all the required equipment and software


Installing and setting up the software environment

  1. Install IAR EW ARM version 7.40.1
  2. Install TI emupack
    1. Run the emupack installer found in the IAR installation: <iar_install>\arm\drivers\ti-xds
    2. Install it to the default location
  3. Run the BLE-STACK-2 Installer
    1. Install it to the default location (C:\ti\simplelink\.)

In the installed directory you will find the following folders:

  • Accessories: Installer for BTool and Boundary tool as well as hex files.
  • Components: source files, headers, and library headers which are shared among the BLE projects
  • Documents: Several important user guides with relevant info for developers
  • Projects: the projects which are currently supported.

After you install the stack, the USB drivers should automatically be associated with the debugger and CC2650 device when the SmartRF06EB it is connected to your USB port through a micro USB cable.

Known issues:

  • none

Known warnings:

  • When you download the stack, you may get 2 pop-up warnings, which you can safely ignore.



Application and project overview

selectProject.png

The SimpleBLEPeripheral project can be found here: Projects\ble\SimpleBLEPeripheral\CC26xx\IAR\SimpleBLEPeripheral.eww
The SimpleBLEPeripheral project implements a very simple BLE peripheral device (GATT server) with GATT services. This project can be used as a framework for developing many different peripheral-role applications. More information on the general software architecture for this project can be found in the software developer's Guide at \Documents\TI_BLE_Software_Developer’s_Guide.pdf.
The SimpleBLEPeripheral project is composed of 2 IAR Projects grouped into one workspace. These two projects are the CC2650App and the CC2650Stack and can be selected by right click at the project and select "Set Active" (You need to debug from workspace showing both Application and Stack, i.e. do not open Application Project only):



Compile & Download to Device

dWarning.png

The steps to load the project onto a CC2650 are as follows:

  1. Compile the CC2650App project.
The first time you run the compiler or do a rebuild, the pre-build command will take quite a long time (minutes) as it is building the TI-RTOS kernel which is used in the project.
  1. Compile the CC2650Stack project.
  2. Download the application project. It is absolutely necessary to load the application project BEFORE the stack project to ensure that there is a safe program flow.
  3. Download the stack project. During the download you may see a warning which is safe to ignore (see image on the right).
  4. Select the CC2650App project and choose “Project -> Debug without Downloading” to begin debugging.



Tutorial Step 0: Create Custom Profile, Service and Application

This step only explains the application you will make in this tutorial and does not require any actions besides reading. We will create a new custom profile that contains a new custom service and specification for the sunlightSensor Application that you will make. In case you are already familiar with some of these topics you can see through the list below that present each subchapter in this chapter, explain the content and gives a link to let you skip to the parts you find interesting:

This subsection will explain what a profile and service is and how they relate to each other.
Explains the custom sunlight profile which is made only for this tutorial as an example with no connection to any real implementation (that the author know of at least).
This details on of the services that is contained within the sunlight profile.
Here the specific application that will be implemented is described. This will have a functionality very similar to the simpleBLEPeripheral project and if you have detailed knowledge on the workings of this project you can basically just look at the sunlight profile image overview and characteristics table shown in the chapters below to quickly get an overview of what we will make.


Custom Profile/Service

Bluetooth SIG defines profiles as definitions of possible applications and specify general behaviors that Bluetooth enabled devices use to communicate with other Bluetooth devices. The GATT based profiles is used in Bluetooth Low Energy (Bluetooth Smart Devices) and it is possible to define custom profiles that is not adopted by Bluetooth SIG. For a list of the adopted profiles, services and more look GATT Based here. Each profile define the total solution by defining the behavior of both ends of the link, The GATT server and GATT client as well as the containing services in the GATT server. In simple terms Bluetooth Low Energy is built around concrete values in a GATT server called attributes. These attributes can be a single byte or an array of many bytes. Several of these attributes constitute a characteristic which can be for example a single byte value where the other attributes in the same characteristic describe if the value can be read or written, give a description of the characteristic in string format and more. A service defines a collection of characteristics and how they are used. A profile defines a collection one or more services and define how services can be used to enable an application or use case. A precise description of GATT, Services, attributes, etc and how they are related to each other here. You can also read more about ​Bluetooth Interoperability and Profiles here.

The GATT server basically contain a chunk of memory that the GATT client can access. This data chunk contains attributes where every attribute has a UUID that identifies how to interpret that specific data, a unique handle that identify where the attribute "is" in the attribute table, permissions that define if the GATT client has read or write access and a pointer (within the GATT server memory space) to the actual attribute value or variable. A simple analogy can be text files in a FTP server (File Transfer Protocol, ignore all details related to FTP as it is just used as an example), except that ATT Protocol is optimized to run on Low Energy devices with low throughput. Every text file can be thought of as a characteristic which have some common attributes such as read/write access, address, etc and every file contains strings or data of different or same length.

If we step up one level in the abstraction level from single attributes we get the characteristic. A single characteristic can be a single byte value or an array of several bytes where the attributes within describe the type and permissions. But certain characteristics that contain a configuration attribute can also enable notifications or indications for the characteristic value. Usually the GATT client takes the initiative and the GATT server answers, but with indication and notifications the server will take the initiative of notifying a GATT client that an characteristic value has changed, saving the client from having to poll the value attribute.

Profile Hierarchy

In the image above the generic hierarchy for GATT based profiles can be seen. The profile can be described as a parent of the service, while a service is lower level and defines content within the profile. As a reference look at the Blood Pressure Profile and Blood Pressure Service specifications to get an understanding of definitions and the relationship between the two. The sole building block of this, the ATT protocol, is the attribute. An attribute is composed by three elements:

  • A 16-bit handle.
  • An UUID which defines the attribute type.
  • A handle that uniquely identify the attribute within a single GATT server.
  • A pointer to a value of a certain length.

To create a custom profile you basically just need to define a role and service relationship. To create a custom service you need to define a specific GATT attribute table with the required characteristics and how these are used. Also for new characteristics that is not already defined by Bluetooth SIG you need to create new unique 128-bit The Universally Unique identifiers (UUID) for the attributes. The next section will go through this in detail.


UUID

We need a way to identify all the different attributes that are exposed in a GATT server. The Universally Unique identifier (UUID) is a 128-bit number that uniquely identify every different attribute type. All attribute types that is part of an adopted profile or service, a shorter 16-bit value can be sent between devices and then recombined with the Bluetooth Base UUID (00000000-0000-1000-8000-00805F9B34FB). This reduces the overhead in communication between devices. All short UUID (16 bits) are reserved pending future revisions of the BT services specs. When defining a custom service you usually define new attribute types where you will need to transmit the whole 128-bit value because the GATT client (which for example might reside on your smart phone) does not know about the base UUID for this specific attribute type. This is one of the disadvantage of using custom services as it will increase overhead slightly.

In this tutorial we will use the same base UUID as we used in the SensorTag project. (Read more about the detail in the SensorTag User Guide on our external wiki:

  • TI Base 128-bit UUID : F000-0000-0451-4000-B000-000000000000.

For the different attributes we will define different 16-bit values ( highlighted with green ) that maps into the base UUID. For example:

  • 0xEE01 maps as F000-EE01-0451-4000-B000-000000000000.

If you want to create a random base UUID you can use this Generator: http://www.guidgenerator.com/online-guid-generator.aspx But the generator cannot guarantee a unique number.

The Sunlight Profile (Custom)

For use in this tutorial, we will create the custom profile called "Sunlight Profile" and implement it. We define the following role and service relationship:

  • Sunlight Sensor - shall be a GATT server.
This is the application called sunlightSensor that you will implement on the CC2650EM.
  • Sunlight Collector - shall be a GATT client.
This will be simulated by one of the boards (SmartRF06EB+CC2650EM) using BTool. To be able to use Btool on this platform, program one of your boards with the CC2640_SmartRF_HostTestRelease_All.hex file (hex file can be found here: C:\ti\simplelink\ble_cc26xx_2_00_00_42893\Accessories\HexFiles). SmartRF Flash Programmer 2 can be used to program hex files to the EM.

This is also illustrated in the image seen below.

The Custom Sunlight Profile



The Sunlight Service (Custom)

The following characteristics are defined for our sunlight service:

Characteristic Requirements Mandatory Properties Security Permissions Description
Characteristic 1 M Read None Sunlight Value
Characteristic 2 M Notify None Sunlight Value Notification
Characteristic 2 Client Characteristic Configuration descriptor M Read, Write None Sunlight Value Notification Configuration

The Characteristic 2 Client Characteristic Configuration descriptor is actually not a separate characteristic. It is a single attribute within Characteristic 2. It is listed to enlighten the reader about the possibility to enable notifications from the GATT client. This specific example does not really utilize characteristic 2 in a particular useful way as it simply copies the value from characteristic 1 at a given interval. It is possible to enable both Read permission and notification on the same characteristic.


The sunlightSensor Application

Basic Flowchart for periodicTask in the SunlightSensor App

The sunlightSensor implements the GATT server and is basically just targeted to be an ambient light sensor. We nevertheless chose the sunlight name hoping it does not relate to an actual product to avoid any confusion. It is simply chosen just to be used as an example in this tutorial and might not be the best suiting name for an ambient light sensor application (ambiLightSensor might have been a more suiting name).

We have used the simpleBLEPeripheral project as a base for our application. It is one of the most often used projects with which developers start as a base for their development. In reality the SensorTag project is actually more suited to follow for implementing a simple sensor application as we are doing here. The sunlightSensor will just read the sunlight value (controlled by buttons, read red box below), store that value in characteristic 1. Every 5 seconds the value in characteristic 1 will be copied to characteristic 2 and a notification will be sent to a connected GATT client if notifications is enabled. We will not implement the collector as this will be simulated with the SmartRF06EB+CC2650EM (running CC2640_SmartRF_HostTestRelease_All.hex) and BTool. The functionality just mentioned is illustrated in the flow chart on the right. An extensive flow chart showing all the inner working of this example code would be too big for any screen so we keep it simple.


This initial version will not use an actual light sensor, but will instead simulate the sunlight value by using the UP and DOWN buttons on the SmartRF06EB. In future updates to this tutorial we will instead use the actual light sensor located on the SmartRF06EB when the drivers for doing this in the BLE stack is complete.



Tutorial Step 1: Set up Project Settings, File and Folder Structure

In this section you will set up the file structure, add/replace files and modify the project settings to create our sunlightSensor application that follows our custom sunlight profile specifications. By following this procedure you will get a new project that leaves the original SimpleBLEPeripheral project intact an ready to be used as a base for all your future projects as well. If you at any time find yourself unable to compile any of the projects residing in the $PROJECTS$ folder, maybe because of changes you have done in the install folder, you can uninstall and then run the stack installer again to get a fresh start. Just make sure to copy any containing folders or files that you want to keep. In the step by step list below the following abbreviations are used to shorten the path names:

$INSTALL_DIR$ = C:\ti\simplelink\ble_cc26xx_2_00_00_42893
$PROJECTS$ = $InstallDir$\Projects\ble


Step by Step Start

In case you want to use this guide as a guide on how to set up your own project, with your own names, (based on simpleBLEPeriheral) just replace with the names of your choice instead of "sunlight" and "sunlightSensor".


The IAR Workspace after following the instructions
  1. Create the sunlight profile folder:
    • Copy the folder $Projects$\Profiles\SimpleProfile and rename the copied folder to Sunlight.
    • Within that folder rename simpleGATTprofile.h to sunlightservice.h
    • In the CC26xx folder you find the file "simpleGATTprofile.c". Rename this to sunlightservice.c.
  2. Create the sunlightSensor application folder:
    • Make a copy of the $PROJECTS$\SimpleBLEPeripheral folder within the same path.
    • Rename the new folder to SunlightSensor.
  3. Rename simpleBLEPeripheral.c and simpleBLEPeripheral.h:
    • These are found in your newly created folder $Projects$\SunlightSensor\CC26xx\Source\Application).
    • Rename them to sunlightSensor.c and sunlightSensor.h.
  4. Change the IAR project name:
    • The project file can be found in $Projects$\sunlightSensor\CC26xx\IAR Projects\.
    • Change the name from SimpleBLEPeripheral.eww to SunlightSensor.eww.
  5. Rename SimpleBLEPeripheral.custom_argvars:
    • Rename file to SunlightSensor.custom_argvars.
  6. Setup the IAR Workspace:
    • Open the IAR workspace (SunlightSensor.eww.):
    • Select the CC2650App project.
    • Remove the two files simpleBLEPeripheral.c and simpleBLEPeripheral.h from the Application workspace folder.
    • Add the two files SunlightSensor.c and SunlightSensor.h from $PROJECTS$\SunlightSensor\CC26xx\Source\Application to your project Application folder.
    • Add the two files board_key.c and board_key.h from $PROJECTS$\common\cc26xx to your project Application folder.
    • In the "PROFILES" workspace folder, remove simpleGATTprofile.c and simpleGATTprofile.h.
    • Add the file sunlightservice.h from $PROJECTS$\Profiles\Sunlight to your project PROFILES folder.
    • Add the file sunlightservice.c from $PROJECTS$\Profiles\Sunlight\CC26xx to your project PROFILES folder.
  7. Change the include directories in the IAR Application workspace:
    • Go into the project options either by right-clicking on the CC2650App-FlashROM project in the workspace and click on Options. You can quickly enter this by pressing the short-cut keys ALT+F7. Then enter the C/C++ Compiler category and then the Preprocessor tab. There you will find the Additional include directories. This is where the C-compiler search for example after function prototypes in header files and more. Change the path names with SimpleProfile to Sunlight as shown in the images below.


$PROJ_DIR$/../../../../../../../Projects/ble/Profiles/SimpleProfile/CC26xx 
$PROJ_DIR$/../../../../../../../Projects/ble/Profiles/SimpleProfile        

$PROJ_DIR$/../../../../../../../Projects/ble/Profiles/Sunlight/CC26xx 
$PROJ_DIR$/../../../../../../../Projects/ble/Profiles/Sunlight             

The IAR .........




Your workspace should now look exactly like shown in the image on the top right in this subsection. If it does you can continue to Tutorial Step 2 further down on this page. If not look through the steps again to find out what you have missed.


Tutorial Step 2: Edit Application Source Code

In this step you will edit the following source code files according to the instructions given in this step:

  1. main.c
  2. sunlightSensor.h
  3. sunlightSensor.c
  4. sunlightservice.h
  5. sunlightservice.c

Each sub-chapter covers one file each (every chapter starts with Edit) and all must be edited according to this guide to be able to compile the project successfully in the end. You should follow this chronologically step by step. The parts of code that will be changed will be displayed in "from ⇒ to" code boxes which has the following format as illustrated in the box below. On the left you will find the original code (some changes might be in the original as we will do global name change operations with the CTRL+H operation) and on the right the new code will be Displayed. Background color highlighting is used to illustrate parts that is removed, altered or added to help you to quickly visualize the change.

Original Code

original code line;

Unaltered code;
Unaltered code;
Unaltered code;

original code line that will be removed;

Altered/New Code

Altered code line;

Unaltered code;
Unaltered code;
Unaltered code;

Added code line;
Added code line;

It should be easy to find the original code that needs to be altered. As every chapter below is divided into each source file you can just look in the file mentioned in the section heading to find the correct code parts. If you cannot find it you can press CTRL+ALT+F in IAR to find search strings in your project files.

How to find code in IAR workspace


Edit main.c

The main.c file can be found in the Startup folder in the CC2650App workspace.

  1. Use CTRL+H to replace all occurrences of the string simpleBLEPeripheral to sunlightSensor
  2. Use CTRL+H to replace all occurrences of the string SimpleBLEPeripheral" to SunlightSensor

See the image below on how to use the replace all function in a single file in IAR (CTRL+H). It is also possible to use the Replace in Files function to replace strings in all the project source files at once (CTRL+SHIFT+ALT+H), but in this guide we will cover one file at a time. A good approach is to do a search for the string that you will replace in the file (CTRL+F) to make sure that you for example do not have additional white spaces after the string you want to replace. After you exit the find dialog and press CTRL+H the string searched for will automatically show up in the "Find what" box.

How to find the correct COM port for the CC2540 USB Dongle


Edit sunlightservice.h

  1. Change Characteristic definitions and UUID:
  2. As mentioned before we will use 2 characteristics (one read only for the sunlight value and second one (which will simply copy the value from the first) for use with notifications). As we are using a custom service with custom characteristics we will need to use 128 bit UUID ( instead of 16 bits which can be used by defined adopted services and it's characteristics). We will use the random TI Base 128-bit UUID and define 16 bit nuggets for each different attribute. You can also remove the SIMPLEPROFILE_SERVICE and SIMPLEPROFILE_CHAR5_LEN defines as we will not need them in this application.

    // Profile Parameters                                  
    
    #define SIMPLEPROFILE_CHAR1                 0          
    #define SIMPLEPROFILE_CHAR2                 1          
    #define SIMPLEPROFILE_CHAR3                 2          
    #define SIMPLEPROFILE_CHAR4                 3          
    #define SIMPLEPROFILE_CHAR5                 4          
                                                           
    // Simple Profile Service UUID                         
    #define SIMPLEPROFILE_SERV_UUID             0xFFF0     
                                                           
    // Key Pressed UUID                                    
    #define SIMPLEPROFILE_CHAR1_UUID            0xFFF1     
    #define SIMPLEPROFILE_CHAR2_UUID            0xFFF2     
    #define SIMPLEPROFILE_CHAR3_UUID            0xFFF3     
    #define SIMPLEPROFILE_CHAR4_UUID            0xFFF4     
    #define SIMPLEPROFILE_CHAR5_UUID            0xFFF5     
                                                           
    // Simple Keys Profile Services bit fields             
    #define SIMPLEPROFILE_SERVICE               0x00000001 
                                                           
    // Length of Characteristic 5 in bytes                 
    #define SIMPLEPROFILE_CHAR5_LEN           5            
    
    
    // Profile Parameters                                 
    
    #define SUNLIGHT_CHAR1                      0         
    #define SUNLIGHT_CHAR2                      1         
                                                          
    // UUID for sunlight service                          
    #define SUNLIGHT_SERV_UUID                  0xEE00    
    /* The 16 bit UUID listen above is only a part of the 
     * full TI random 128 bit UUID:                       
     * F000EEE00-0451-4000-B000-00000000-00000. */        
                                                          
    //  Sunlight service characteristic UUID              
    #define SUNLIGHT_CHAR1_UUID                 0xEE01    
    #define SUNLIGHT_CHAR2_UUID                 0xEE02    
    
    


  3. Use CTRL+H to replace all occurrences of the string SimpleProfile to Sunlight.
  4. Use CTRL+H to replace all occurrences of the string simpleProfile to sunlight.
  5. Use CTRL+H to replace all occurrences of the string SIMPLEGATTPROFILE_H to SUNLIGHTSERVICE_H.
  6. Use CTRL+H to replace all occurrences of the string SIMPLEPROFILE_ to SUNLIGHT_.



Edit sunlightservice.c

This file can be found in the PROFILES folder in the CC2650App workspace.

  1. Change define:
  2. #include  "simpleGATTprofile.h" 
    
    #include  "sunlightservice.h" 
    

  3. Change number of supported attributes from 17 to 8:
  4. The sunlight profile use 2 specific characteristics which needs 7 attributes (3 for the read only characteristic 1 and 4 for characteristic 2) + the attribute for the primary Service declaration. Then we end up with 8 in total.

    #define SERVAPP_NUM_ATTR_SUPPORTED       17 
    
    #define SERVAPP_NUM_ATTR_SUPPORTED       8 
    

  5. Change the global variables:
  6. We are going to use 2 characteristics for our sunlight service. sunlightServicechar1UUID will have hold the sunlight value/level and sunlightServicechar1UUID will be used to send notifications to the GATT client if enabled.

    // Simple GATT Profile Service UUID: 0xFFF0                                
    CONST uint8 simpleProfileServUUID[ATT_BT_UUID_SIZE] =                      
    {                                                                          
      LO_UINT16(SIMPLEPROFILE_SERV_UUID), HI_UINT16(SIMPLEPROFILE_SERV_UUID)   
    };                                                                         
                                                                               
    // Characteristic 1 UUID: 0xFFF1                                           
    CONST uint8 simpleProfilechar1UUID[ATT_BT_UUID_SIZE] =                     
    {                                                                          
      LO_UINT16(SIMPLEPROFILE_CHAR1_UUID), HI_UINT16(SIMPLEPROFILE_CHAR1_UUID) 
    };                                                                         
                                                                               
    // Characteristic 2 UUID: 0xFFF2                                           
    CONST uint8 simpleProfilechar2UUID[ATT_BT_UUID_SIZE] =                     
    {                                                                          
      LO_UINT16(SIMPLEPROFILE_CHAR2_UUID), HI_UINT16(SIMPLEPROFILE_CHAR2_UUID) 
    };                                                                         
                                                                               
    // Characteristic 3 UUID: 0xFFF3                                           
    CONST uint8 simpleProfilechar3UUID[ATT_BT_UUID_SIZE] =                     
    {                                                                          
      LO_UINT16(SIMPLEPROFILE_CHAR3_UUID), HI_UINT16(SIMPLEPROFILE_CHAR3_UUID) 
    };                                                                         
                                                                               
    // Characteristic 4 UUID: 0xFFF4                                           
    CONST uint8 simpleProfilechar4UUID[ATT_BT_UUID_SIZE] =                     
    {                                                                          
      LO_UINT16(SIMPLEPROFILE_CHAR4_UUID), HI_UINT16(SIMPLEPROFILE_CHAR4_UUID) 
    };                                                                         
                                                                               
    // Characteristic 5 UUID: 0xFFF5                                           
    CONST uint8 simpleProfilechar5UUID[ATT_BT_UUID_SIZE] =                     
    {                                                                          
      LO_UINT16(SIMPLEPROFILE_CHAR5_UUID), HI_UINT16(SIMPLEPROFILE_CHAR5_UUID) 
    };                                                                         
    
    // Sunlight Service UUID: 0xEE00                      
    CONST uint8 sunlightServUUID[ATT_UUID_SIZE] =         
    {                                                     
        TI_BASE_UUID_128(SUNLIGHT_SERV_UUID),             
    };                                                    
                                                          
    // Characteristic 1 UUID: 0xEE01                      
    CONST uint8 sunlightchar1UUID[ATT_UUID_SIZE] =        
    {                                                     
        TI_BASE_UUID_128(SUNLIGHT_CHAR1_UUID),            
    };                                                    
    // Characteristic 2 UUID: 0xEE02                      
    CONST uint8 sunlightchar2UUID[ATT_UUID_SIZE] =        
    {                                                     
        TI_BASE_UUID_128(SUNLIGHT_CHAR2_UUID),            
    };                                                    
    

  7. Change the service attribute variables:
  8. These are the actual variables used in the value attribute in each characteristic.

    /*********************************************************************                          
     * Profile Attributes - variables                                                               
     */                                                                                             
                                                                                                    
    // Simple Profile Service attribute                                                             
    static CONST gattAttrType_t simpleProfileService = { ATT_BT_UUID_SIZE, simpleProfileServUUID }; 
                                                                                                    
                                                                                                    
    // Simple Profile Characteristic 1 Properties                                                   
    static uint8 simpleProfileChar1Props = GATT_PROP_READ | GATT_PROP_WRITE;                        
                                                                                                    
    // Characteristic 1 Value                                                                       
    static uint8 simpleProfileChar1 = 0;                                                            
                                                                                                    
    // Simple Profile Characteristic 1 User Description                                             
    static uint8 simpleProfileChar1UserDesp[17] = "Characteristic 1\0";                             
                                                                                                    
                                                                                                    
    // Simple Profile Characteristic 2 Properties                                                   
    static uint8 simpleProfileChar2Props = GATT_PROP_READ;                                          
                                                                                                    
    // Characteristic 2 Value                                                                       
    static uint8 simpleProfileChar2 = 0;                                                            
                                                                                                    
    // Simple Profile Characteristic 2 User Description                                             
    static uint8 simpleProfileChar2UserDesp[17] = "Characteristic 2\0";                             
                                                                                                    
                                                                                                    
    // Simple Profile Characteristic 3 Properties                                                   
    static uint8 simpleProfileChar3Props = GATT_PROP_WRITE;                                         
                                                                                                    
    // Characteristic 3 Value                                                                       
    static uint8 simpleProfileChar3 = 0;                                                            
                                                                                                    
    // Simple Profile Characteristic 3 User Description                                             
    static uint8 simpleProfileChar3UserDesp[17] = "Characteristic 3\0";                             
                                                                                                    
                                                                                                    
    // Simple Profile Characteristic 4 Properties                                                   
    static uint8 simpleProfileChar4Props = GATT_PROP_NOTIFY;                                        
                                                                                                    
    // Characteristic 4 Value                                                                       
    static uint8 simpleProfileChar4 = 0;                                                            
                                                                                                    
    // Simple Profile Characteristic 4 Configuration Each client has its own                        
    // instantiation of the Client Characteristic Configuration. Reads of the                       
    // Client Characteristic Configuration only shows the configuration for                         
    // that client and writes only affect the configuration of that client.                         
    static gattCharCfg_t *simpleProfileChar4Config;                                                 
                                                                                                    
    // Simple Profile Characteristic 4 User Description                                             
    static uint8 simpleProfileChar4UserDesp[17] = "Characteristic 4\0";                             
                                                                                                    
                                                                                                    
    // Simple Profile Characteristic 5 Properties                                                   
    static uint8 simpleProfileChar5Props = GATT_PROP_READ;                                          
                                                                                                    
    // Characteristic 5 Value                                                                       
    static uint8 simpleProfileChar5[SIMPLEPROFILE_CHAR5_LEN] = { 0, 0, 0, 0, 0 };                   
                                                                                                    
    // Simple Profile Characteristic 5 User Description                                             
    static uint8 simpleProfileChar5UserDesp[17] = "Characteristic 5\0";                             
    
    /*********************************************************************                    
     * Service Attributes - variables                                                         
     */                                                                                       
                                                                                              
    // Sunlight Service attribute                                                             
    static CONST gattAttrType_t sunlightService = { ATT_UUID_SIZE, sunlightServUUID};     
                                                                                              
    // Sunlight Service Characteristic 1 Properties                                           
    static uint8 sunlightChar1Props = GATT_PROP_READ | GATT_PROP_WRITE;                       
                                                                                              
    // Characteristic 1 Value                                                                 
    static uint8 sunlightChar1 = 0;                                                           
                                                                                              
    // Sunlight Service Characteristic 1 User Description                                     
    static uint8 sunlightChar1UserDesp[17] = "Sunlight Value\0";                              
                                                                                              
    // Sunlight Service Characteristic 2 Properties                                           
    static uint8 sunlightChar2Props = GATT_PROP_NOTIFY;                                       
                                                                                              
    // Characteristic 2 Value                                                                 
    static uint8 sunlightChar2 = 0;                                                           
                                                                                              
    // Sunlight Service Characteristic 2 Configuration.                                       
    static gattCharCfg_t *sunlightChar2Config;                                                
                                                                                              
    // Sunlight Service Characteristic 2 User Description                                     
    static uint8 sunlightChar2UserDesp[] = "Sunlight Value Notification\0";            
    


  9. Change the service attribute table:
  10. /*********************************************************************
     * Profile Attributes - Table
     */
    
    static gattAttribute_t simpleProfileAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] =       
    {                                                                               
      // Simple Profile Service                                                     
      {                                                                             
        { ATT_BT_UUID_SIZE, primaryServiceUUID }, /* type */                        
        GATT_PERMIT_READ,                         /* permissions */                 
        0,                                        /* handle */                      
        (uint8 *)&simpleProfileService            /* pValue */                      
      },                                                                            
                                                                                    
      // Characteristic 1 Declaration                                               
      {                                                                             
        { ATT_BT_UUID_SIZE, characterUUID },                                        
        GATT_PERMIT_READ,                                                           
        0,                                                                          
        &simpleProfileChar1Props                                                    
      },                                                                            
                                                                                    
      // Characteristic Value 1                                                     
      {                                                                             
        { ATT_BT_UUID_SIZE, simpleProfilechar1UUID },                               
          GATT_PERMIT_READ | GATT_PERMIT_WRITE,                                     
          0,                                                                        
          &simpleProfileChar1                                                       
      },                                                                            
                                                                                    
      // Characteristic 1 User Description                                          
      {                                                                             
        { ATT_BT_UUID_SIZE, charUserDescUUID },                                     
        GATT_PERMIT_READ,                                                           
        0,                                                                          
        simpleProfileChar1UserDesp                                                  
      },                                                                            
                                                                                    
      // Characteristic 2 Declaration                                               
      {                                                                             
        { ATT_BT_UUID_SIZE, characterUUID },                                        
        GATT_PERMIT_READ,                                                           
        0,                                                                          
        &simpleProfileChar2Props                                                    
      },                                                                            
                                                                                    
       // Characteristic Value 2                                                    
       {                                                                            
         { ATT_BT_UUID_SIZE, simpleProfilechar2UUID },                              
          GATT_PERMIT_READ,                                                         
          0,                                                                        
          &simpleProfileChar2                                                       
       },                                                                           
                                                                                    
       // Characteristic 2 User Description                                         
       {                                                                            
         { ATT_BT_UUID_SIZE, charUserDescUUID },                                    
         GATT_PERMIT_READ,                                                          
         0,                                                                         
         simpleProfileChar2UserDesp                                                 
       },                                                                           
                                                                                    
       // Characteristic 3 Declaration                                              
       {                                                                            
         { ATT_BT_UUID_SIZE, characterUUID },                                       
         GATT_PERMIT_READ,                                                          
         0,                                                                         
         &simpleProfileChar3Props                                                   
       },                                                                           
                                                                                    
       // Characteristic Value 3                                                    
       {                                                                            
         { ATT_BT_UUID_SIZE, simpleProfilechar3UUID },                              
         GATT_PERMIT_WRITE,                                                         
         0,                                                                         
         &simpleProfileChar3                                                        
       },                                                                           
                                                                                    
       // Characteristic 3 User Description                                         
       {                                                                            
         { ATT_BT_UUID_SIZE, charUserDescUUID },                                    
         GATT_PERMIT_READ,                                                          
         0,                                                                         
         simpleProfileChar3UserDesp                                                 
       },                                                                           
                                                                                    
       // Characteristic 4 Declaration                                              
       {                                                                            
         { ATT_BT_UUID_SIZE, characterUUID },                                       
          GATT_PERMIT_READ,                                                         
          0,                                                                        
          &simpleProfileChar4Props                                                  
       },                                                                           
                                                                                    
       // Characteristic Value 4                                                    
       {                                                                            
         { ATT_BT_UUID_SIZE, simpleProfilechar4UUID },                              
         0,                                                                         
         0,                                                                         
         &simpleProfileChar4                                                        
       },                                                                           
                                                                                    
       // Characteristic 4 configuration                                            
       {                                                                            
         { ATT_BT_UUID_SIZE, clientCharCfgUUID },                                   
         GATT_PERMIT_READ | GATT_PERMIT_WRITE,                                      
         0,                                                                         
         (uint8 *)&simpleProfileChar4Config                                         
       },                                                                           
                                                                                    
       // Characteristic 4 User Description                                         
       {                                                                            
         { ATT_BT_UUID_SIZE, charUserDescUUID },                                    
         GATT_PERMIT_READ,                                                          
         0,                                                                         
         simpleProfileChar4UserDesp                                                 
       },                                                                           
                                                                                    
       // Characteristic 5 Declaration                                              
       {                                                                            
         { ATT_BT_UUID_SIZE, characterUUID },                                       
         GATT_PERMIT_READ,                                                          
         0,                                                                         
         &simpleProfileChar5Props                                                   
       },                                                                           
                                                                                    
       // Characteristic Value 5                                                    
       {                                                                            
         { ATT_BT_UUID_SIZE, simpleProfilechar5UUID },                              
         GATT_PERMIT_AUTHEN_READ,                                                   
         0,                                                                         
         simpleProfileChar5                                                         
       },                                                                           
                                                                                    
       // Characteristic 5 User Description                                         
       {                                                                            
         { ATT_BT_UUID_SIZE, charUserDescUUID },                                    
         GATT_PERMIT_READ,                                                          
         0,                                                                         
         simpleProfileChar5UserDesp                                                 
       },                                                                           
    };                                                                              
    
    /*********************************************************************
    * Profile Attributes - Table
    */
    
    static gattAttribute_t sunlightAttrTbl[SERVAPP_NUM_ATTR_SUPPORTED] =
    {
      // sunlight Service                                             
      {                                                               
        { ATT_BT_UUID_SIZE, primaryServiceUUID },   /* type */        
        GATT_PERMIT_READ,                           /* permissions */ 
        0,                                          /* handle */      
        (uint8 *)&sunlightService                   /* pValue */      
      },                                                              
      // Characteristic 1 Declaration                                 
      {                                                               
        { ATT_BT_UUID_SIZE, characterUUID },                          
        GATT_PERMIT_READ,                                             
        0,                                                            
        &sunlightChar1Props                                           
      },                                                              
                                                                      
      // Characteristic Value 1                                       
      {                                                               
        { ATT_UUID_SIZE, sunlightchar1UUID },                         
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,                         
        0,                                                            
        &sunlightChar1                                                
      },                                                              
                                                                      
      // Characteristic 1 User Description                            
      {                                                               
        { ATT_BT_UUID_SIZE, charUserDescUUID },                       
        GATT_PERMIT_READ,                                             
        0,                                                            
        sunlightChar1UserDesp                                         
      },                                                              
                                                                      
                                                                      
      // Characteristic 2 Declaration                                 
      {                                                               
        { ATT_BT_UUID_SIZE, characterUUID },                          
        GATT_PERMIT_READ,                                             
        0,                                                            
        &sunlightChar2Props                                           
      },                                                              
                                                                      
      // Characteristic Value 2                                       
      {                                                               
        { ATT_UUID_SIZE, sunlightchar2UUID },                         
        0,                                                            
        0,                                                            
        &sunlightChar2                                                
      },                                                              
                                                                      
      // Characteristic 2 configuration                               
      {                                                               
        { ATT_BT_UUID_SIZE, clientCharCfgUUID },                      
        GATT_PERMIT_READ | GATT_PERMIT_WRITE,                         
        0,                                                            
        (uint8 *)&sunlightChar2Config                                 
      },                                                              
                                                                      
      // Characteristic 2 User Description                            
      {                                                               
        { ATT_BT_UUID_SIZE, charUserDescUUID },                       
        GATT_PERMIT_READ,                                             
        0,                                                            
        sunlightChar2UserDesp                                         
      },                                                              
    };                                                                
    


  11. Change the SimpleProfile_AddService function:
  12. bStatus_t SimpleProfile_AddService( uint32 services )                               
    {                                                                                   
      uint8 status;                                                                     
                                                                                        
      // Allocate Client Characteristic Configuration table                             
      simpleProfileChar4Config = (gattCharCfg_t *)ICall_malloc( sizeof(gattCharCfg_t) * 
                                                                linkDBNumConns );       
      if ( simpleProfileChar4Config == NULL )                                           
      {                                                                                 
        return ( bleMemAllocError );                                                    
      }                                                                                 
                                                                                        
      // Initialize Client Characteristic Configuration attributes                      
      GATTServApp_InitCharCfg( INVALID_CONNHANDLE, simpleProfileChar4Config );          
                                                                                        
      if ( services & SIMPLEPROFILE_SERVICE )                                           
      {                                                                                 
        // Register GATT attribute list and CBs with GATT Server App                    
        status = GATTServApp_RegisterService( simpleProfileAttrTbl,                     
                                              GATT_NUM_ATTRS( simpleProfileAttrTbl ),   
                                              &simpleProfileCBs );                      
      }                                                                                 
      else                                                                              
      {                                                                                 
        status = SUCCESS;                                                               
      }                                                                                 
                                                                                        
      return ( status );                                                                
    }                                                                                   
    
    bStatus_t Sunlight_AddService( uint32 services )                                      
    {                                                                                     
      uint8 status;                                                                       
                                                                                          
      // Allocate Client Characteristic Configuration table                               
      sunlightChar2Config = (gattCharCfg_t *)ICall_malloc( sizeof(gattCharCfg_t) *        
                                                                linkDBNumConns );         
      if ( sunlightChar2Config == NULL )                                                  
      {                                                                                   
        return ( bleMemAllocError );                                                      
      }                                                                                   
                                                                                          
      // Initialize Client Characteristic Configuration attributes                        
      GATTServApp_InitCharCfg( INVALID_CONNHANDLE, sunlightChar2Config );                 
                                                                                          
      // Register GATT attribute list and CBs with GATT Server App                        
      status = GATTServApp_RegisterService( sunlightAttrTbl,                              
                                            GATT_NUM_ATTRS( sunlightAttrTbl ),            
                                            &sunlightCBs );                               
                                                                                          
      return ( status );                                                                  
    }                                                                                     
    


  13. Update Sunlight_SetParameter function:
  14.  bStatus_t SimpleProfile_SetParameter( uint8 param, uint8 len, void *value )                       
    {                                                                                                 
      bStatus_t ret = SUCCESS;                                                                        
      switch ( param )                                                                                
      {                                                                                               
        case SIMPLEPROFILE_CHAR1:                                                                     
          if ( len == sizeof ( uint8 ) )                                                              
          {                                                                                           
            simpleProfileChar1 = *((uint8*)value);                                                    
          }                                                                                           
          else                                                                                        
          {                                                                                           
            ret = bleInvalidRange;                                                                    
          }                                                                                           
          break;                                                                                      
                                                                                                      
        case SIMPLEPROFILE_CHAR2:                                                                     
          if ( len == sizeof ( uint8 ) )                                                              
          {                                                                                           
            simpleProfileChar2 = *((uint8*)value);                                                    
          }                                                                                           
          else                                                                                        
          {                                                                                           
            ret = bleInvalidRange;                                                                    
          }                                                                                           
          break;                                                                                      
                                                                                                      
        case SIMPLEPROFILE_CHAR3:                                                                     
          if ( len == sizeof ( uint8 ) )                                                              
          {                                                                                           
            simpleProfileChar3 = *((uint8*)value);                                                    
          }                                                                                           
          else                                                                                        
          {                                                                                           
            ret = bleInvalidRange;                                                                    
          }                                                                                           
          break;                                                                                      
                                                                                                      
        case SIMPLEPROFILE_CHAR4:                                                                     
          if ( len == sizeof ( uint8 ) )                                                              
          {                                                                                           
            simpleProfileChar4 = *((uint8*)value);                                                    
                                                                                                      
            // See if Notification has been enabled                                                   
            GATTServApp_ProcessCharCfg( simpleProfileChar4Config, &simpleProfileChar4, FALSE,         
                                        simpleProfileAttrTbl, GATT_NUM_ATTRS( simpleProfileAttrTbl ), 
                                        INVALID_TASK_ID, simpleProfile_ReadAttrCB );                  
          }                                                                                           
          else                                                                                        
          {                                                                                           
            ret = bleInvalidRange;                                                                    
          }                                                                                           
          break;                                                                                      
                                                                                                      
        case SIMPLEPROFILE_CHAR5:                                                                     
          if ( len == SIMPLEPROFILE_CHAR5_LEN )                                                       
          {                                                                                           
            VOID memcpy( simpleProfileChar5, value, SIMPLEPROFILE_CHAR5_LEN );                        
          }                                                                                           
          else                                                                                        
          {                                                                                           
            ret = bleInvalidRange;                                                                    
          }                                                                                           
          break;                                                                                      
                                                                                                      
        default:                                                                                      
          ret = INVALIDPARAMETER;                                                                     
          break;                                                                                      
      }                                                                                               
                                                                                                      
      return ( ret );                                                                                 
    }                                                                                                 
    
     bStatus_t Sunlight_SetParameter( uint8 param, uint8 len, void *value )                       
    {                                                                                             
      bStatus_t ret = SUCCESS;                                                                    
      switch ( param )                                                                            
      {                                                                                           
        case SUNLIGHT_CHAR1:                                                                      
          if ( len == sizeof ( uint8 ) )                                                          
          {                                                                                       
            sunlightChar1 = *((uint8*)value);                                                     
          }                                                                                       
          else                                                                                    
          {                                                                                       
            ret = bleInvalidRange;                                                                
          }                                                                                       
          break;                                                                                  
                                                                                                  
        case SUNLIGHT_CHAR2:                                                                      
          if ( len == sizeof ( uint8 ) )                                                          
          {                                                                                       
            sunlightChar2 = *((uint8*)value);                                                     
                                                                                                  
            // See if Notification has been enabled                                               
            GATTServApp_ProcessCharCfg( sunlightChar2Config, &sunlightChar2, FALSE,               
                                        sunlightAttrTbl, GATT_NUM_ATTRS( sunlightAttrTbl ),       
                                        INVALID_TASK_ID, sunlight_ReadAttrCB );                   
          }                                                                                       
          else                                                                                    
          {                                                                                       
            ret = bleInvalidRange;                                                                
          }                                                                                       
          break;                                                                                  
                                                                                                  
        default:                                                                                  
          ret = INVALIDPARAMETER;                                                                 
          break;                                                                                  
      }                                                                                           
                                                                                                  
      return ( ret );                                                                             
    }                                                                                             
    


  15. Change the SimpleProfile_GetParameter function:
  16. bStatus_t SimpleProfile_GetParameter( uint8 param, void *value )         
    {                                                                        
      bStatus_t ret = SUCCESS;
      switch ( param )                                                       
      {                                                                      
        case SIMPLEPROFILE_CHAR1:                                            
          *((uint8*)value) = simpleProfileChar1;                             
          break;                                                             
                                                                             
        case SIMPLEPROFILE_CHAR2:                                            
          *((uint8*)value) = simpleProfileChar2;                             
          break;                                                             
                                                                             
        case SIMPLEPROFILE_CHAR3:                                            
          *((uint8*)value) = simpleProfileChar3;                             
          break;                                                             
                                                                             
        case SIMPLEPROFILE_CHAR4:                                            
          *((uint8*)value) = simpleProfileChar4;                             
          break;                                                             
                                                                             
        case SIMPLEPROFILE_CHAR5:                                            
          VOID memcpy( value, simpleProfileChar5, SIMPLEPROFILE_CHAR5_LEN ); 
          break;                                                             
                                                                             
        default:                                                             
          ret = INVALIDPARAMETER;                                            
          break;                                                             
      }                                                                      
                                                                             
      return ( ret );                                                        
    }                                                                        
    
    bStatus_t  Sunlight_GetParameter( uint8 param, void *value )        
    {                                                                  
      bStatus_t ret = SUCCESS;                                         
      switch ( param )                                                 
      {                                                                
        case SUNLIGHT_CHAR1:                                           
          *((uint8*)value) = sunlightChar1;                            
          break;                                                       
                                                                       
        case SUNLIGHT_CHAR2:                                           
          *((uint8*)value) = sunlightChar2;                            
          break;                                                       
                                                                       
        default:                                                       
          ret = INVALIDPARAMETER;                                      
          break;                                                       
      }                                                                
                                                                       
      return ( ret );                                                  
    }                                                                  
    


  17. Add the following function at the top of the file. It is used below for extracting a 16-bit version off the full 128-bit UUID:
  18. bStatus_t utilExtractUuid16(gattAttribute_t *pAttr, uint16_t *pUuid)
    {
      bStatus_t status = SUCCESS;
    
      if (pAttr->type.len == ATT_BT_UUID_SIZE )
      {
        // 16-bit UUID direct
        *pUuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);
    #ifdef GATT_TI_UUID_128_BIT
      }
      else if (pAttr->type.len == ATT_UUID_SIZE)
      {
        // 16-bit UUID extracted bytes 12 and 13
        *pUuid = BUILD_UINT16( pAttr->type.uuid[12], pAttr->type.uuid[13]);
    #endif
      } else {
        *pUuid = 0xFFFF;
        status = FAILURE;
      }
    
      return status;
    }
    


  19. Change the simpleProfile_ReadAttrCBfunction:
  20. Have to add check on extracting the 16 bit UUID from the 128 bit long UUID before entering switch-case.

    static bStatus_t simpleProfile_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,    
                                               uint8 *pValue, uint8 *pLen, uint16 offset,    
                                               uint8 maxLen, uint8 method )                  
    {                                                                                        
      bStatus_t status = SUCCESS;                                                            
                                                                                             
      // If attribute permissions require authorization to read, return error                
      if ( gattPermitAuthorRead( pAttr->permissions ) )                                      
      {                                                                                      
        // Insufficient authorization                                                        
        return ( ATT_ERR_INSUFFICIENT_AUTHOR );                                              
      }                                                                                      
                                                                                             
      // Make sure it's not a blob operation (no attributes in the profile are long)         
      if ( offset > 0 )                                                                      
      {                                                                                      
        return ( ATT_ERR_ATTR_NOT_LONG );                                                    
      }                                                                                      
    
      if ( pAttr->type.len == ATT_BT_UUID_SIZE )                                             
      {                                                                                      
        // 16-bit UUID                                                                       
        uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);               
        switch ( uuid )                                                                      
        {                                                                                    
          // No need for "GATT_SERVICE_UUID" or "GATT_CLIENT_CHAR_CFG_UUID" cases;           
          // gattserverapp handles those reads                                               
                                                                                             
          // characteristics 1 and 2 have read permissions                                   
          // characteritisc 3 does not have read permissions; therefore it is not            
          //   included here                                                                 
          // characteristic 4 does not have read permissions, but because it                 
          //   can be sent as a notification, it is included here                            
          case SIMPLEPROFILE_CHAR1_UUID:                                                     
          case SIMPLEPROFILE_CHAR2_UUID:                                                     
          case SIMPLEPROFILE_CHAR4_UUID:                                                     
            *pLen = 1;                                                                       
            pValue[0] = *pAttr->pValue;                                                      
            break;                                                                           
                                                                                             
          case SIMPLEPROFILE_CHAR5_UUID:                                                     
            *pLen = SIMPLEPROFILE_CHAR5_LEN;                                                 
            VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR5_LEN );                   
            break;                                                                           
                                                                                             
          default:                                                                           
            // Should never get here! (characteristics 3 and 4 do not have read permissions) 
            *pLen = 0;                                                                       
            status = ATT_ERR_ATTR_NOT_FOUND;                                                 
            break;                                                                           
        }                                                                                    
      }                                                                                      
      else                                                                                   
      {                                                                                      
        // 128-bit UUID                                                                      
        *pLen = 0;                                                                           
        status = ATT_ERR_INVALID_HANDLE;                                                     
      }                                                                                      
                                                                                             
      return ( status );                                                                     
    }                                                                                        
    
    static bStatus_t  sunlight_ReadAttrCB( uint16 connHandle, gattAttribute_t *pAttr,        
                                               uint8 *pValue, uint8 *pLen, uint16 offset,   
                                               uint8 maxLen, uint8 method )                 
    {                                                                                       
      uint16 uuid;
      bStatus_t status = SUCCESS;                                                           
                                                                                            
      // If attribute permissions require authorization to read, return error               
      if ( gattPermitAuthorRead( pAttr->permissions ) )                                     
      {                                                                                     
        // Insufficient authorization                                                       
        return ( ATT_ERR_INSUFFICIENT_AUTHOR );                                             
      }                                                                                     
                                                                                            
      // Make sure it's not a blob operation (no attributes in the profile are long)        
      if ( offset > 0 )                                                                     
      {                                                                                     
        return ( ATT_ERR_ATTR_NOT_LONG );                                                   
      }                                                                                     
                                                                                            
    
      if (utilExtractUuid16(pAttr,&uuid) == FAILURE) {                                      
        // Invalid handle                                                                   
        *pLen = 0;                                                                          
        return ATT_ERR_INVALID_HANDLE;                                                      
      }                                                                                     
                                                                                            
      switch ( uuid )                                                                       
      {                                                                                     
        // No need for "GATT_SERVICE_UUID" or "GATT_CLIENT_CHAR_CFG_UUID" cases;            
        // gattserverapp handles those reads                                                
                                                                                            
        // characteristics 1 has read permissions                                           
        // characteristic 2 does not have read permissions, but because it                  
        //   can be sent as a notification, it is included here                             
      case SUNLIGHT_CHAR1_UUID:                                                             
      case SUNLIGHT_CHAR2_UUID:                                                             
        *pLen = 1;                                                                          
        pValue[0] = *pAttr->pValue;                                                         
        break;                                                                              
                                                                                            
      default:                                                                              
        // Should never get here! (characteristics 3 and 4 do not have read permissions)    
        *pLen = 0;                                                                          
        status = ATT_ERR_ATTR_NOT_FOUND;                                                    
        break;                                                                              
      }                                                                                     
      return ( status );                                                                    
    }                                                                                       
    


  21. Change the simpleProfile_WriteAttrCBfunction function:
  22. To change attribute values directly within the application the function Sunlight_SetParameter is used. sunlightService_WriteAttrCB is execute when a GATT client is "trying" to write to a attribute in the GATT server. Because SUNLIGHT_CHAR1 is read-only and the SUNLIGHTSERVICE_CHAR2 value is transferred as notifications when enabled we only need to enable function for writing to the configuration of SUNLIGHTSERVICE_CHAR2 to enable or disable notifications (GATT_CLIENT_CHAR_CFG_UUID).

    In the old code, the following function-call is executed if there has been a change in a characteristic value (attribute):

    • sunlightService_AppCBs->pfnSimpleProfileChange( notifyApp );

    This is mapped to the function SunlightSensor_charValueChangeCB in sunlightSensor.c in the following function call:

    • SunlightService_RegisterAppCBs(&SunlightSensor_simpleProfileCBs);

    When the SunlightSensor_charValueChangeCB function is executed it sends a message in a RTOS queue with the SBP_CHAR_CHANGE_EVT event argument. The function SunlightSensor_processAppMsg will process this message according to the event type (only parameter in function). Originally this is implemented for the simpleBLEPeripheral to call the SunlightSensor_processCharValueChangeEvt to update teh LCD display whenever either the characteristic 1 or characteristic 2 value is changed. For this version of our sunlight profile we will update the LCD in button push call backs instead so we can comment out this for now.

    static bStatus_t simpleProfile_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,                
                                                uint8 *pValue, uint8 len, uint16 offset,                  
                                                uint8 method )                                            
    {                                                                                                     
      bStatus_t status = SUCCESS;                                                                         
      uint8 notifyApp = 0xFF;
                                                                                                          
      // If attribute permissions require authorization to write, return error                            
      if ( gattPermitAuthorWrite( pAttr->permissions ) )                                                  
      {                                                                                                   
        // Insufficient authorization                                                                     
        return ( ATT_ERR_INSUFFICIENT_AUTHOR );                                                           
      }                                                                                                   
     
      if ( pAttr->type.len == ATT_BT_UUID_SIZE )                                                          
      {                                                                                                   
        // 16-bit UUID                                                                                    
        uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);                            
        switch ( uuid )                                                                                   
        {                                                                                                 
          case SIMPLEPROFILE_CHAR1_UUID:                                                                  
          case SIMPLEPROFILE_CHAR3_UUID:                                                                  
                                                                                                          
            //Validate the value                                                                          
            // Make sure it's not a blob oper                                                             
            if ( offset == 0 )                                                                            
            {                                                                                             
              if ( len != 1 )                                                                             
              {                                                                                           
                status = ATT_ERR_INVALID_VALUE_SIZE;                                                      
              }                                                                                           
            }                                                                                             
            else                                                                                          
            {                                                                                             
              status = ATT_ERR_ATTR_NOT_LONG;                                                             
            }                                                                                             
                                                                                                          
            //Write the value                                                                             
            if ( status == SUCCESS )                                                                      
            {                                                                                             
              uint8 *pCurValue = (uint8 *)pAttr->pValue;                                                  
              *pCurValue = pValue[0];                                                                     
                                                                                                          
              if( pAttr->pValue == &simpleProfileChar1 )                                                  
              {                                                                                           
                notifyApp = SIMPLEPROFILE_CHAR1;                                                          
              }                                                                                           
              else                                                                                        
              {                                                                                           
                notifyApp = SIMPLEPROFILE_CHAR3;                                                          
              }                                                                                           
            }                                                                                             
                                                                                                          
            break;                                                                                        
                                                                                                          
          case GATT_CLIENT_CHAR_CFG_UUID:                                                                 
            status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,                      
                                                     offset, GATT_CLIENT_CFG_NOTIFY );                    
            break;                                                                                        
                                                                                                          
          default:                                                                                        
            // Should never get here! (characteristics 2 and 4 do not have write permissions)             
            status = ATT_ERR_ATTR_NOT_FOUND;                                                              
            break;                                                                                        
        }                                                                                                 
      }                                                                                                   
      else                                                                                                
      {                                                                                                   
        // 128-bit UUID                                                                                   
        status = ATT_ERR_INVALID_HANDLE;                                                                  
      }                                                                                                   
                                                                                                          
      // If a charactersitic value changed then callback function to notify application of change         
      if ( (notifyApp != 0xFF ) && simpleProfile_AppCBs && simpleProfile_AppCBs->pfnSimpleProfileChange ) 
      {                                                                                                   
        simpleProfile_AppCBs->pfnSimpleProfileChange( notifyApp );                                        
      }                                                                                                   
                                                                                                          
      return ( status );                                                                                  
    }                                                                                                     
    
    static bStatus_t sunlight_WriteAttrCB( uint16 connHandle, gattAttribute_t *pAttr,        
                                                uint8 *pValue, uint8 len, uint16 offset,     
                                                uint8 method )                               
    {                                                                                        
      uint16 uuid;                                                    
      bStatus_t status = SUCCESS;                                                            
                                                                                             
      // If attribute permissions require authorization to write, return error               
      if ( gattPermitAuthorWrite( pAttr->permissions ) )                                     
      {                                                                                      
        // Insufficient authorization                                                        
        return ( ATT_ERR_INSUFFICIENT_AUTHOR );                                              
      }                                                                                      
    
     
      if (utilExtractUuid16(pAttr,&uuid) == FAILURE) {                                       
        // Invalid handle                                                                    
        return ATT_ERR_INVALID_HANDLE;                                                       
      }                                                                                      
                                                                                             
      // 16-bit UUID                                                                         
      // uint16 uuid = BUILD_UINT16( pAttr->type.uuid[0], pAttr->type.uuid[1]);                 
      switch ( uuid )                                                                        
      {                                                                                      
      case GATT_CLIENT_CHAR_CFG_UUID:                                                        
        status = GATTServApp_ProcessCCCWriteReq( connHandle, pAttr, pValue, len,             
                                                offset, GATT_CLIENT_CFG_NOTIFY );            
        break;                                                                               
                                                                                             
      default:                                                                               
        // Should never get here! (characteristics 2 and 4 do not have write permissions)    
        status = ATT_ERR_ATTR_NOT_FOUND;                                                     
        break;                                                                               
      }                                                                                      
      return ( status );                                                                     
    }                                                                                        
    

  23. Use CTRL+H to replace all strings with "simpleProfileChar4Config" to "sunlightChar2Config"
  24. Use CTRL+H to replace all strings with "simpleProfile" to "sunlight"
  25. Use CTRL+H to replace all strings with "SimpleProfile_" to "Sunlight_"



Edit sunlightSensor.h

This file can be found in the Application folder in the CC2650App workspace.

  1. Use CTRL+H to replace all occurrences of the string SIMPLEBLEPERIPHERAL_ to SUNLIGHTSENSOR_
  2. Use CTRL+H to replace all occurrences of the string SimpleBLEPeripheral_ to SunlightSensor_


Edit sunlightSensor.c

  1. Change/add define:
  2. #include  "simpleGATTprofile.h" 
    
    #include  "sunlightservice.h" 
    

  3. Change/add define:
  4. #include  "simpleBLEPeripheral.h" 
    
    #include  "sunlightSensor.h" 
    

  5. Change the local name given in scan response data payload:
  6. We will leave the rest of the payload as this parameters that provide the desired and maximum allowed connection interval which a master device will use for initiating a connection with this BLE slave device.

    // GAP - SCAN RSP data (max size = 31 bytes)               
    static uint8_t scanRspData[] =                             
    {                                                          
      // complete name                                         
      0x14,   // length of this data                           
      GAP_ADTYPE_LOCAL_NAME_COMPLETE,                          
      0x53,   // 'S'                                           
      0x69,   // 'i'                                           
      0x6d,   // 'm'                                           
      0x70,   // 'p'                                           
      0x6c,   // 'l'                                           
      0x65,   // 'e'                                           
      0x42,   // 'B'                                           
      0x4c,   // 'L'                                           
      0x45,   // 'E'                                           
      0x50,   // 'P'                                           
      0x65,   // 'e'                                           
      0x72,   // 'r'                                           
      0x69,   // 'i'                                           
      0x70,   // 'p'                                           
      0x68,   // 'h'                                           
      0x65,   // 'e'                                           
      0x72,   // 'r'                                           
      0x61,   // 'a'                                           
      0x6c,   // 'l'                                           
                                                               
      // connection interval range                             
      0x05,   // length of this data                           
      GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE,                    
      LO_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL),   // 100ms 
      HI_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL),            
      LO_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL),   // 1s    
      HI_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL),            
                                                               
      // Tx power level                                        
      0x02,   // length of this data                           
      GAP_ADTYPE_POWER_LEVEL,                                  
      0       // 0dBm                                          
    };                                                         
    
    // GAP - SCAN RSP data (max size = 31 bytes)               
    static uint8_t scanRspData[] =                             
    {                                                          
      // complete name                                         
      0x0F,   // length of this data                           
      GAP_ADTYPE_LOCAL_NAME_COMPLETE,                          
      0x53,   // 'S'                                           
      0x75,   // 'u'                                           
      0x6e,   // 'n'                                           
      0x6c,   // 'l'                                           
      0x69,   // 'i'                                           
      0x67,   // 'g'                                           
      0x68,   // 'h'                                           
      0x74,   // 't'                                           
      0x53,   // 'S'                                           
      0x65,   // 'e'                                           
      0x6e,   // 'n'                                           
      0x73,   // 's'                                           
      0x6f,   // 'o'                                           
      0x72,   // 'r'                                           
                                                               
      // connection interval range                             
      0x05,   // length of this data                           
      GAP_ADTYPE_SLAVE_CONN_INTERVAL_RANGE,                    
      LO_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL),   // 100ms 
      HI_UINT16(DEFAULT_DESIRED_MIN_CONN_INTERVAL),            
      LO_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL),   // 1s    
      HI_UINT16(DEFAULT_DESIRED_MAX_CONN_INTERVAL),            
                                                               
      // Tx power level                                        
      0x02,   // length of this data                           
      GAP_ADTYPE_POWER_LEVEL,                                  
      0       // 0dBm                                          
    };                                                         
    

  7. Change the device name:
  8. static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] =  "Simple BLE Peripheral"; 
    
    static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] =  "SunlightSensor"; 
    

  9. Change the reset values within SimpleBLEPeripheral_init() for the attributes:
  10. // Setup the SimpleProfile Characteristic Values                             
      {                                                                          
        uint8_t charValue1 = 1;                                                  
        uint8_t charValue2 = 2;                                                  
        uint8_t charValue3 = 3;                                                  
        uint8_t charValue4 = 4;                                                  
        uint8_t charValue5[SIMPLEPROFILE_CHAR5_LEN] = { 1, 2, 3, 4, 5 };         
                                                                                 
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1, sizeof(uint8_t),         
                                   &charValue1);                                 
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR2, sizeof(uint8_t),         
                                   &charValue2);                                 
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR3, sizeof(uint8_t),         
                                   &charValue3);                                 
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, sizeof(uint8_t),         
                                   &charValue4);                                 
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN, 
                                   charValue5);                                  
                                                                                 
      }                                                                          
                                                                                 
      // Register callback with SimpleGATTprofile                                
      SimpleProfile_RegisterAppCBs(&SimpleBLEPeripheral_simpleProfileCBs);       
                                                                                 
      // Start the Device                                                        
      VOID GAPRole_StartDevice(&SimpleBLEPeripheral_gapRoleCBs);                 
                                                                                 
      // Start Bond Manager                                                      
      VOID GAPBondMgr_Register(&SunlightSensor_BondMgrCBs);                 
                                                                                 
    #if defined FEATURE_OAD                                                      
    #if defined (HAL_IMAGE_A)                                                    
      LCD_WRITE_STRING_VALUE("BLE Peri-A", OAD_VER_NUM(_imgHdr.ver), 16,         
                             LCD_PAGE0);                                         
    #else                                                                        
      LCD_WRITE_STRING_VALUE("BLE Peri-B", OAD_VER_NUM(_imgHdr.ver), 16,         
                             LCD_PAGE0);                                         
    #endif // HAL_IMAGE_A                                                        
    #else                                                                        
      LCD_WRITE_STRING("BLE Peripheral", LCD_PAGE0);                             
    #endif // FEATURE_OAD                                                        
    


      // Setup the Sunlight Service Characteristic Values                    
      {                                                                      
        uint8_t charValue1 = 0;                                              
        uint8_t charValue2 = 0;                                              
                                                                             
        Sunlight_SetParameter(SUNLIGHT_CHAR1, sizeof(uint8_t), 
                                   &charValue1);                             
        Sunlight_SetParameter(SUNLIGHT_CHAR2, sizeof(uint8_t), 
                                   &charValue2);                             
      }                                                                      
                                                                             
      // Register callback with SimpleGATTprofile                            
      Sunlight_RegisterAppCBs(&sunlight_AppCBs);                  
    #endif //!FEATURE_OAD                                                    
                                                                             
      // Start the Device                                                    
      VOID GAPRole_StartDevice(&SunlightSensor_gapRoleCBs);                  
                                                                             
      // Start Bond Manager                                                  
      VOID GAPBondMgr_Register(&SunlightSensor_BondMgrCBs);                  
                                                                             
    #if defined FEATURE_OAD                                                  
    #if defined (HAL_IMAGE_A)                                                
      LCD_WRITE_STRING("BLE SunlightSensor A", LCD_PAGE0);                   
    #else                                                                    
      LCD_WRITE_STRING("BLE SunlightSensor B", LCD_PAGE0);                   
    #endif // HAL_IMAGE_A                                                    
    #else                                                                    
      LCD_WRITE_STRING("BLE SunlightSensor", LCD_PAGE0);                     
    #endif // FEATURE_OAD                                                    
    


  11. Comment out the content of SunlightSensor_processCharValueChangeEvt
  12. This function should never be called if your already have removed the call to this function in sunlightservice.c (which you should have done as instructed earlier in this guide). But we will comment out the content as this is referring to characteristics that no longer exists and will also result in a compilation error. In IAR you can easily comment out blocks if you mark it and press CTRL+K. To un-comment press CTRL+SHIFT+K.

    static void SimpleBLEPeripheral_processCharValueChangeEvt(uint8_t paramID) 
    {                                                                                  
      uint8_t newValue;                                                                
                                                                                       
      switch(paramID)                                                                  
      {                                                                                
        case SIMPLEPROFILE_CHAR1:                                                      
          SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR1, &newValue);                  
                                                                                       
          LCD_WRITE_STRING_VALUE("Char 1:", (uint16_t)newValue, 10, LCD_PAGE4);        
          break;                                                                       
                                                                                       
        case SIMPLEPROFILE_CHAR3:                                                      
          SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &newValue);                  
                                                                                       
          LCD_WRITE_STRING_VALUE("Char 3:", (uint16_t)newValue, 10, LCD_PAGE4);        
          break;                                                                       
                                                                                       
        default:                                                                       
          // should not reach here!                                                    
          break;                                                                       
      }                                                                                
    }                                                                                  
    
    static void SimpleBLEPeripheral_processCharValueChangeEvt(uint8_t paramID) 
    {                                                                                  
    //  uint8_t newValue;                                                                
    //                                                                                   
    //  switch(paramID)                                                                  
    //  {                                                                                
    //    case SIMPLEPROFILE_CHAR1:                                                      
    //      SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR1, &newValue);                  
    //                                                                                   
    //      LCD_WRITE_STRING_VALUE("Char 1:", (uint16_t)newValue, 10, LCD_PAGE4);        
    //      break;                                                                       
    //                                                                                   
    //    case SIMPLEPROFILE_CHAR3:                                                      
    //      SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &newValue);                  
    //                                                                                   
    //      LCD_WRITE_STRING_VALUE("Char 3:", (uint16_t)newValue, 10, LCD_PAGE4);        
    //      break;                                                                       
    //                                                                                   
    //    default:                                                                       
    //      // should not reach here!                                                    
    //      break;                                                                       
    //  }                                                                                
    }                                                                                  
    

  13. Change the content of SunlightSensor_performPeriodicTask:
  14. This task will be called at a interval given in SBP_PERIODIC_EVT_PERIOD (set to 5000 ms = 5 seconds). This measn that every 5 seconds the value in characteristic 1 is copied to characteristic 2. If notifications is enabled for characteristic 2 then a notification will be sent to the GATT client if this value is changed.

     static void SimpleBLEPeripheral_performPeriodicTask(void)                      
    {                                                                               
      uint8_t valueToCopy;                                                          
                                                                                    
      // Call to retrieve the value of the third characteristic in the profile      
      if (SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &valueToCopy) == SUCCESS) 
      {                                                                             
        // Call to set that value of the fourth characteristic in the profile.      
        // Note that if notifications of the fourth characteristic have been        
        // enabled by a GATT client device, then a notification will be sent        
        // every time this function is called.                                      
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, sizeof(uint8_t),            
                                   &valueToCopy);                                   
      }                                                                             
    }                                                                               
    
    static void SunlightSensor_performPeriodicTask(void)                                
    {                                                                                   
      uint8_t valueToCopy;                                                              
                                                                                        
      // Call to retrieve the value of the first characteristic in the service          
      if (Sunlight_GetParameter(SUNLIGHT_CHAR1, &valueToCopy) == SUCCESS) 
      {                                                                                 
        // Call to set that value of the second characteristic in the service.          
        // Note that if notifications of the fourth characteristic have been            
        // enabled by a GATT client device, then a notification will be sent            
        // every time this function is called.                                          
        Sunlight_SetParameter(SUNLIGHT_CHAR2, sizeof(uint8_t),            
                                   &valueToCopy);                                       
      }                                                                                 
    }                                                                                   
    

  15. Use CTRL+H to replace all occurrences of the string SIMPLEPROFILE_ to SUNLIGHT_
  16. Use CTRL+H to replace all occurrences of the string SimpleProfile to Sunlight
  17. Use CTRL+H to replace all occurrences of the string simpleProfile to sunlight
  18. Use CTRL+H to replace all occurrences of the string SimpleBLEPeripheral to SunlightSensor_
  19. Use CTRL+H to replace all occurrences of the string SBP_ to SLS_


Button Push Functionality

In this section you will only edit the sunlightSensor.c file to implement button push functionality. You will make the up and down button to change the sunlight value (sunlight service characteristic 1 value).

  1. Add the following include:
  2. #include "board_key.h"
    

  3. Add button related functions to LOCAL FUNCTIONS:
  4. void SunlightSensor_keyChangeHandler(uint8 keysPressed);
    static void SunlightSensor_handleKeys(uint8_t shift, uint8_t keys);
    

  5. Add the following define:
  6. #define SLS_KEY_CHANGE_EVT                    0x0010
    

  7. Add the function call seen below to SunlightSensor_init (previously called SimpleBLEPeripheral_init):
  8. // Initialize keys on SmartRF06EB
    Board_initKeys(SunlightSensor_keyChangeHandler);
    

  9. Add the following function (similar function can be found in the SimpleBLECentral Project)
  10. /*********************************************************************
     * @fn      SunlightSensor_handleKeys                                 
     *                                                                    
     * @brief   Handles all key events for this device.                   
     *                                                                    
     * @param   shift - true if in shift/alt.                             
     * @param   keys - bit field for key events. Valid entries:           
     *                 HAL_KEY_SW_2                                       
     *                 HAL_KEY_SW_1                                        
     *                                                                     
     * @return  none                                                      
     */                                                                     
    static void SunlightSensor_handleKeys(uint8_t shift, uint8_t keys)
    {
      (void)shift;  // Intentionally unreferenced parameter  
    
      uint8_t sunLight;
      Sunlight_GetParameter(SUNLIGHTSERVICE_CHAR1, &sunLight);
      if (keys & KEY_UP)
      {
        if (sunLight < 255)
        {
          sunLight++;
          Sunlight_SetParameter(SUNLIGHTSERVICE_CHAR1, sizeof(uint8_t),
                                       &sunLight);
        }
      }
    
      if (keys & KEY_DOWN)
      {
        if (sunLight > 0)
        {
          sunLight--;
          Sunlight_SetParameter(SUNLIGHTSERVICE_CHAR1, sizeof(uint8_t),
                                       &sunLight);
        }
      }
      LCD_WRITE_STRING_VALUE("Sunlight:", (uint16_t)sunLight, 10, LCD_PAGE5);
      return;
    }
    

  11. Add a case statement in the SunlightSensor_processAppMsg function to handle key press events
  12. static void SimpleBLEPeripheral_processAppMsg(sbpEvt_t *pMsg)
    {
      switch (pMsg->event)
      {
        case SBP_STATE_CHANGE_EVT:
          SimpleBLEPeripheral_processStateChangeEvt((gaprole_States_t)pMsg->status);
          break;
    
        case SBP_CHAR_CHANGE_EVT:
          SimpleBLEPeripheral_processCharValueChangeEvt(pMsg->status);
          break;
    
        default:
          // Do nothing.
          break;
      }
    }
    
    static void SunlightSensor_processAppMsg(sbpEvt_t *pMsg)
    {
      switch (pMsg->event)
      {
      case SBP_STATE_CHANGE_EVT:
        SunlightSensor_processStateChangeEvt((gaprole_States_t)pMsg->status);
        break;
     
      case SBP_CHAR_CHANGE_EVT:
        SunlightSensor_processCharValueChangeEvt(pMsg->status);
        break;
    
      case SLS_KEY_CHANGE_EVT:                    
      SunlightSensor_handleKeys(0, pMsg->status); 
      break;                                      
    
      default:
        // Do nothing.
        break;
      }
    }
    

  13. Add the following function to the end of the file
  14. /*********************************************************************
     * @fn      SunlightSensor__keyChangeHandler
     *
     * @brief   Key event handler function
     *
     * @param   a0 - ignored
     *
     * @return  none
     */
    void SunlightSensor_keyChangeHandler(uint8 keysPressed)
    {
      SunlightSensor_enqueueMsg(SLS_KEY_CHANGE_EVT, keysPressed);
    }
    



Edit devinfoservice.c (Optional)

You will find this file in the PROFILES folder in the CC2650App workspace. You do not have to change anything here besides the strings that describe your device. You can change the strings marked in red below to what ever you desire and they will show up in BTool when you read all the characteristic values. This service is adopted by bluetooth SIG and therefore you should keep it as it is so to ensure that it is compliant according to the specification. This means that any BLE Central (Master) deceive can connect to this sunlightSensor application without having any knowledge of the sunlight service and still be able to know how to handle and present the values in the device info service.

// Model Number String characteristic                          
static uint8 devInfoModelNumberProps = GATT_PROP_READ;         
static const uint8 devInfoModelNumber[] = "Model Number";      
                                                               
// Serial Number String characteristic                         
static uint8 devInfoSerialNumberProps = GATT_PROP_READ;        
static const uint8 devInfoSerialNumber[] = "Serial Number";    
                                                               
// Firmware Revision String characteristic                     
static uint8 devInfoFirmwareRevProps = GATT_PROP_READ;         
static const uint8 devInfoFirmwareRev[] = "Firmware Revision"; 
                                                               
// Hardware Revision String characteristic                     
static uint8 devInfoHardwareRevProps = GATT_PROP_READ;         
static const uint8 devInfoHardwareRev[] = "Hardware Revision"; 
                                                               
// Software Revision String characteristic                     
static uint8 devInfoSoftwareRevProps = GATT_PROP_READ;         
static const uint8 devInfoSoftwareRev[] = "Software Revision"; 
                                                               
// Manufacturer Name String characteristic                     
static uint8 devInfoMfrNameProps = GATT_PROP_READ;             
static const uint8 devInfoMfrName[] = "Manufacturer Name";     


Tutorial Step 3: Compile, Download and Debug Project

Now you are ready to compile and download the code to your CC2560EM.

  1. Compile by pressing F7.
  2. Here are some hints to what might be wrong if you get errors while running C/C++ Compiler
    1. Possible Naming Mismatch
    2. Error/Warnings:
      • Error[Pe020]: identifier "x" is undefined ...
      • Error[Pe114]: function "x" was referenced but not defined ...
      • Warning[Pe177]: function "x" was declared but never referenced ...
      • Warning[Pe223]: function "x" declared implicitly ...
      Solution:
      There might be a mismatch between the function declaration and the function prototype. This might have occurred during the renaming in one of the steps above.


    3. Missing or Incorrect Search Path
    4. Error/Warnings:
      • Fatal Error[Pe1696]: cannot open source file "sunlightservice.h" ...
      Solution:
      You forgot Forgot to add the following paths to your "Additional include directories" in Project options"->"C/C++ Compiler"->"Preprocessor":
      $PROJ_DIR$/../../../../../../../Projects/ble/Profiles/Sunlight/CC26xx
      $PROJ_DIR$/../../../../../../../Projects/ble/Profiles/Sunlight


    5. Too Many Characters in Project Name
    6. Error/Warnings:
      • cannot open source file "xdc/runtime/package/Package.defs.h
      Solution:
      The reason is because the project folder name is too long which results in really long search paths during compile. Example: SimpleBLEPeripheral_A1_BTN_LED_WayToLongProjectName


    7. Missing Custom Arguments
    8. Error/Warnings:
      • Error while compile “Variable expansion failed”
      Solution:
      The solution is to rename the SimpleBLEPeripheral.custom_argvars file to SunlightSensor.custom_argvars whoich you should have done in Tutorial Step 1.
  3. Download the application image
  4. IAR will likely crash at the end of the CC2640App download process. The solution is to uncheck TI RTOS in Project options -> Debugger -> Plugins.
  5. Download the stack image
  6. You may get a few warnings that is safe to ignore.

  7. Reset or Power cycle your board.
  8. You can either press the EM RESET button on the left on the smartRF06EB or power cycle the device by toggling the OFF-ON switch.


Tutorial Step 4: Verify Application Behavior with BTool

In this step you will verify the functionality of your application by using BTool to connect, read GATT attributes, and modify the characteristic 2 client configuration to enable notifications.


Start the SunlighSensor Application

Start SunlightSensor Application on CC2650EM. When you run the code the device address will show up on line number two from the top on the SmartRF06EB LCD display. You need to have this address available for the next step to know which peripheral device you will connect to in BTool. Also you can now press the up or down button to vary the sunlight value to a arbitrary number. In this case I set it to 7. This is illustrated in the picture below.

SunlighSensor application connected to Master


Setup BTool

There is a detailed guide on how to setup, run and use the BTool in chapter 4 in the CC2541 EMK User’s Guide. But you should be able to do the necessary steps by following the following guide:

  1. Make sure that the CC2640_SmartRF_HostTestRelease_All.hex is loaded on one of the CC2650EMs.
  2. Connect the SmartRF06EB+CC2650EM to your USB-port of the machine from which you will run BTool.
  3. Find the Correct COM Port.
  4. To do this just follow the steps illustrated in the picture below. In Win7 you can usually find the computer in the start menu, then:
    right-click->select properties->Device Manager->Ports(COM&LPT).

You can also use a CC2540Dongle with BTool. Make sure that the HostTestApp application is loaded on the device. The CC2540Dongle comes pre-loaded with this software (http://www.ti.com/tool/cc2540emk-usb), so if you haven't downloaded new firmware to the device you can move to the next step. If you have used the CC-Debugger or SmartRF05EB to load different firmware then just download the CC2540_USBdongle_HostTestRelease_All.hex firmware which can be found here:
C:\Texas Instruments\BLE-CC254x-1.4.0\Accessories\HexFiles

How to find the correct COM port for the CC2540 USB Dongle
  1. Start BTool.
  2. When you start Btool the following boc will chow up where you can set the serial settings used to communicate with the CC2540 trough the Host Controller Interface (HCI) API. Select the COM port you found in the previous step and set the remaining settings equal to what is shown here:

    How to find the correct COM port for the CC2540 USB Dongle



Connect with BTool

  1. Press The "Scan" button to discover your peripheral device.
  2. Set the Min Connection Interval and Max Connection Interval to 10 to speed up the data exchange.
  3. Select your SunlightSensor Device address in the Slave BDA drop down list.
  4. Press the Establish button.
  5. Then right-click on the peripheral device Handle and press Discover UUIDs.
  6. Read characteristic 1 value (Sunlight Value).
  7. Now all the different characteristics will show up in the bottom window in BTool. Reading the sunlight value from characteristic 1 in the sunlight service. You can do this either by left-clicking once on the sunlightService characteristic 1 value attribute or by using the Characteristic Read function found in the Read/Write tab. Because the characteristic is not part of a adopted service we will need to use the 128 bit UUID. The UUID is hown in the table below.

    The TI-Base Custom UUID F000XXXX-0451-4000-B000-000000000000
    The Specific UUID for characteristic1 in the sunlightservice: F000EE01-0451-4000-B000-000000000000
    The format to use for reading charateristic 1 in BTool 00:00:00:00:00:00:00:B0:00:40:51:04:01:EE:00:F0


    Guide: Connect to sunlightSensor with BTool and read characteristics


    Characteristics of the custom sunlight profile as seen in BTool


Enable Notifications

We have implemented the possibility in the sunlight service to enable notifications on characteristic 2. To enable this double left-click on the Client Characteristic Configuration attribute in sunlight service characteristic 2 and write 01:00 to this attribute. This can also be done in the Characteristic Write section under the Read/Write tab in BTool. Now whenever the value in characteristic 2 is changed a notification will be sent from the peripheral device to your BTool device (master/Central) device and the value will be updated in BTool application GUI.

You can test this by changing the sunlight value on your peripheral device (sunlightSensor) by pressing the up and down buttons on the evaluation board. This will immediately change the characteristic 1 value (Same as the Sunlight value printed on the LCD screen). For every 5 seconds the SunlightSensor_performPeriodicTask function will run where the value from characteristic 1 is copied to characteristic 2 and a notification will then be sent if this value is different than what was already previously stored in characteristic 2.

Congratulations!
You have now completed the Tutorial and can embark on new BLE adventures