This thread has been locked.

If you have a related question, please click the "Ask a related question" button in the top right corner. The newly created question will be automatically linked to this question.

CC2640 device does not send every notification with stack-2.2?

Other Parts Discussed in Thread: CC2640, CC2540, CC2650, TEST2

Hi TI,

Your customer try to porting their application from stack-2.1.1 to stack-2.2. CC2640 will collect three different sensor data and notify to APP

sensor A -> 20 bytes/20mesc

sensor B -> 19 bytes/20msec

sensor C -> 6b ytes/100msec

Every packet include index to make sure the packet receive correctly. There is no data lost with stack-2.1.1 but we get data loss with stack-2.2. Do you have any suggestions? thanks 

  • It sounds like an issue was induced during the porting process. There is a significant amount of differences between 2.1.1 and 2.2.0. I would recommend starting with a sample 2.2.0 project and merging your custom modifications in.
  • Hi Tim,

    Thanks for your reply. I try to reproduce this problem, my setting is showing below

    HW : CC2650 7x7 EVM PG2.2, CC2540 dongle

    FW : BLE Stack-2.2 SimpleBLEPeripheral, BTool-1.41.09

    SimpleBLEPeripheral

    chara-1 ->notify 20bytes/20msec

    chara-2 ->notify 19bytes/20msec

    chara-4 ->notify 6bytes/100msec

    chara-3 ->

    write 0x01 start notify chara-1

    write 0x02 start notify chara-2

    write 0x04 start notify chara-4

    write 0x00 stop all notification

    I set connection interval as 7.5msec and I also see this problem, but same test with stack-2.1.1 work correctly. Sniffer log and program files are showing below. If you have any suggestion please let me know. Thanks.

    3482.My Capture Files.zip

    /******************************************************************************
    
     @file  simple_peripheral.c
    
     @brief This file contains the Simple BLE Peripheral sample application for use
            with the CC2650 Bluetooth Low Energy Protocol Stack.
    
     Group: WCS, BTS
     Target Device: CC2650, CC2640, CC1350
    
     ******************************************************************************
     
     Copyright (c) 2013-2016, Texas Instruments Incorporated
     All rights reserved.
    
     Redistribution and use in source and binary forms, with or without
     modification, are permitted provided that the following conditions
     are met:
    
     *  Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
    
     *  Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.
    
     *  Neither the name of Texas Instruments Incorporated nor the names of
        its contributors may be used to endorse or promote products derived
        from this software without specific prior written permission.
    
     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    
     ******************************************************************************
     Release Name: ble_sdk_2_02_00_31
     Release Date: 2016-06-16 18:57:29
     *****************************************************************************/
    
    /*********************************************************************
     * INCLUDES
     */
    #include <string.h>
    
    #include <ti/sysbios/knl/Task.h>
    #include <ti/sysbios/knl/Clock.h>
    #include <ti/sysbios/knl/Semaphore.h>
    #include <ti/sysbios/knl/Queue.h>
    
    #include "hci_tl.h"
    #include "gatt.h"
    #include "linkdb.h"
    #include "gapgattserver.h"
    #include "gattservapp.h"
    #include "devinfoservice.h"
    #include "simple_gatt_profile.h"
    
    #if defined(FEATURE_OAD) || defined(IMAGE_INVALIDATE)
    #include "oad_target.h"
    #include "oad.h"
    #endif //FEATURE_OAD || IMAGE_INVALIDATE
    
    #include "peripheral.h"
    #include "gapbondmgr.h"
    
    #include "osal_snv.h"
    #include "icall_apimsg.h"
    
    #include "util.h"
    
    #ifdef USE_RCOSC
    #include "rcosc_calibration.h"
    #endif //USE_RCOSC
       
    #include <ti/mw/display/Display.h>
    #include "board_key.h"
    
    #include "board.h"
    
    #include "simple_peripheral.h"
    
    
    /*********************************************************************
     * CONSTANTS
     */
    
    // Advertising interval when device is discoverable (units of 625us, 160=100ms)
    #define DEFAULT_ADVERTISING_INTERVAL          160
    
    // Limited discoverable mode advertises for 30.72s, and then stops
    // General discoverable mode advertises indefinitely
    #define DEFAULT_DISCOVERABLE_MODE             GAP_ADTYPE_FLAGS_GENERAL
    
    #ifndef FEATURE_OAD
    // Minimum connection interval (units of 1.25ms, 80=100ms) if automatic
    // parameter update request is enabled
    #define DEFAULT_DESIRED_MIN_CONN_INTERVAL     80
    
    // Maximum connection interval (units of 1.25ms, 800=1000ms) if automatic
    // parameter update request is enabled
    #define DEFAULT_DESIRED_MAX_CONN_INTERVAL     800
    #else //!FEATURE_OAD
    // Minimum connection interval (units of 1.25ms, 8=10ms) if automatic
    // parameter update request is enabled
    #define DEFAULT_DESIRED_MIN_CONN_INTERVAL     8
    
    // Maximum connection interval (units of 1.25ms, 8=10ms) if automatic
    // parameter update request is enabled
    #define DEFAULT_DESIRED_MAX_CONN_INTERVAL     8
    #endif // FEATURE_OAD
    
    // Slave latency to use if automatic parameter update request is enabled
    #define DEFAULT_DESIRED_SLAVE_LATENCY         0
    
    // Supervision timeout value (units of 10ms, 1000=10s) if automatic parameter
    // update request is enabled
    #define DEFAULT_DESIRED_CONN_TIMEOUT          1000
    
    // Whether to enable automatic parameter update request when a connection is
    // formed
    #define DEFAULT_ENABLE_UPDATE_REQUEST         FALSE
    
    // Connection Pause Peripheral time value (in seconds)
    #define DEFAULT_CONN_PAUSE_PERIPHERAL         6
    
    // How often to perform periodic event (in msec)
    #define SBP_PERIODIC_EVT_PERIOD1              20
    #define SBP_PERIODIC_EVT_PERIOD2              20
    #define SBP_PERIODIC_EVT_PERIOD4              100
    
    #ifdef FEATURE_OAD
    // The size of an OAD packet.
    #define OAD_PACKET_SIZE                       ((OAD_BLOCK_SIZE) + 2)
    #endif // FEATURE_OAD
    
    // Task configuration
    #define SBP_TASK_PRIORITY                     1
    
    
    #ifndef SBP_TASK_STACK_SIZE
    #define SBP_TASK_STACK_SIZE                   644
    #endif
    
    // Internal Events for RTOS application
    #define SBP_STATE_CHANGE_EVT                  0x0001
    #define SBP_CHAR_CHANGE_EVT                   0x0002
    #define SBP_PERIODIC1_EVT                     0x0004
    #define SBP_CONN_EVT_END_EVT                  0x0008
    #define SBP_PERIODIC2_EVT                     0x0010
    #define SBP_PERIODIC4_EVT                     0x0020
    
    /*********************************************************************
     * TYPEDEFS
     */
    
    // App event passed from profiles.
    typedef struct
    {
      appEvtHdr_t hdr;  // event header.
    } sbpEvt_t;
    
    /*********************************************************************
     * GLOBAL VARIABLES
     */
    
    // Display Interface
    Display_Handle dispHandle = NULL;
    
    /*********************************************************************
     * LOCAL VARIABLES
     */
    
    // Entity ID globally used to check for source and/or destination of messages
    static ICall_EntityID selfEntity;
    
    // Semaphore globally used to post events to the application thread
    static ICall_Semaphore sem;
    
    // Clock instances for internal periodic events.
    static Clock_Struct periodicClock1;
    // Clock instances for internal periodic events.
    static Clock_Struct periodicClock2;
    // Clock instances for internal periodic events.
    static Clock_Struct periodicClock4;
    
    // Queue object used for app messages
    static Queue_Struct appMsg;
    static Queue_Handle appMsgQueue;
    
    #if defined(FEATURE_OAD)
    // Event data from OAD profile.
    static Queue_Struct oadQ;
    static Queue_Handle hOadQ;
    #endif //FEATURE_OAD
    
    // events flag for internal application events.
    static uint16_t events;
    
    // Task configuration
    Task_Struct sbpTask;
    Char sbpTaskStack[SBP_TASK_STACK_SIZE];
    
    // Profile state and parameters
    //static gaprole_States_t gapProfileState = GAPROLE_INIT;
    
    // GAP - SCAN RSP data (max size = 31 bytes)
    static uint8_t scanRspData[] =
    {
      // complete name
      0x14,   // length of this data
      GAP_ADTYPE_LOCAL_NAME_COMPLETE,
      'S',
      'i',
      'm',
      'p',
      'l',
      'e',
      'B',
      'L',
      'E',
      'P',
      'e',
      'r',
      'i',
      'p',
      'h',
      'e',
      'r',
      'a',
      '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 - Advertisement data (max size = 31 bytes, though this is
    // best kept short to conserve power while advertisting)
    static uint8_t advertData[] =
    {
      // Flags; this sets the device to use limited discoverable
      // mode (advertises for 30 seconds at a time) instead of general
      // discoverable mode (advertises indefinitely)
      0x02,   // length of this data
      GAP_ADTYPE_FLAGS,
      DEFAULT_DISCOVERABLE_MODE | GAP_ADTYPE_FLAGS_BREDR_NOT_SUPPORTED,
    
      // service UUID, to notify central devices what services are included
      // in this peripheral
    #if !defined(FEATURE_OAD) || defined(FEATURE_OAD_ONCHIP)
      0x03,   // length of this data
    #else //OAD for external flash
      0x05,  // lenght of this data
    #endif //FEATURE_OAD
      GAP_ADTYPE_16BIT_MORE,      // some of the UUID's, but not all
    #ifdef FEATURE_OAD
      LO_UINT16(OAD_SERVICE_UUID),
      HI_UINT16(OAD_SERVICE_UUID),
    #endif //FEATURE_OAD
    #ifndef FEATURE_OAD_ONCHIP
      LO_UINT16(SIMPLEPROFILE_SERV_UUID),
      HI_UINT16(SIMPLEPROFILE_SERV_UUID)
    #endif //FEATURE_OAD_ONCHIP
    };
    
    // GAP GATT Attributes
    static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Simple BLE Peripheral";
    
    // Globals used for ATT Response retransmission
    static gattMsgEvent_t *pAttRsp = NULL;
    static uint8_t rspTxRetry = 0;
    
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    
    static void SimpleBLEPeripheral_init( void );
    static void SimpleBLEPeripheral_taskFxn(UArg a0, UArg a1);
    
    static uint8_t SimpleBLEPeripheral_processStackMsg(ICall_Hdr *pMsg);
    static uint8_t SimpleBLEPeripheral_processGATTMsg(gattMsgEvent_t *pMsg);
    static void SimpleBLEPeripheral_processAppMsg(sbpEvt_t *pMsg);
    static void SimpleBLEPeripheral_processStateChangeEvt(gaprole_States_t newState);
    static void SimpleBLEPeripheral_processCharValueChangeEvt(uint8_t paramID);
    static void SimpleBLEPeripheral_performPeriodicTask1(void);
    static void SimpleBLEPeripheral_performPeriodicTask2(void);
    static void SimpleBLEPeripheral_performPeriodicTask4(void);
    static void SimpleBLEPeripheral_clockHandler(UArg arg);
    
    static void SimpleBLEPeripheral_sendAttRsp(void);
    static void SimpleBLEPeripheral_freeAttRsp(uint8_t status);
    
    static void SimpleBLEPeripheral_stateChangeCB(gaprole_States_t newState);
    #ifndef FEATURE_OAD_ONCHIP
    static void SimpleBLEPeripheral_charValueChangeCB(uint8_t paramID);
    #endif //!FEATURE_OAD_ONCHIP
    static void SimpleBLEPeripheral_enqueueMsg(uint8_t event, uint8_t state);
    
    #ifdef FEATURE_OAD
    void SimpleBLEPeripheral_processOadWriteCB(uint8_t event, uint16_t connHandle,
                                               uint8_t *pData);
    #endif //FEATURE_OAD
    
    
    /*********************************************************************
     * PROFILE CALLBACKS
     */
    
    // GAP Role Callbacks
    static gapRolesCBs_t SimpleBLEPeripheral_gapRoleCBs =
    {
      SimpleBLEPeripheral_stateChangeCB     // Profile State Change Callbacks
    };
    
    // GAP Bond Manager Callbacks
    static gapBondCBs_t simpleBLEPeripheral_BondMgrCBs =
    {
      NULL, // Passcode callback (not used by application)
      NULL  // Pairing / Bonding state Callback (not used by application)
    };
    
    // Simple GATT Profile Callbacks
    #ifndef FEATURE_OAD_ONCHIP
    static simpleProfileCBs_t SimpleBLEPeripheral_simpleProfileCBs =
    {
      SimpleBLEPeripheral_charValueChangeCB // Characteristic value change callback
    };
    #endif //!FEATURE_OAD_ONCHIP
    
    #ifdef FEATURE_OAD
    static oadTargetCBs_t simpleBLEPeripheral_oadCBs =
    {
      SimpleBLEPeripheral_processOadWriteCB // Write Callback.
    };
    #endif //FEATURE_OAD
    
    /*********************************************************************
     * PUBLIC FUNCTIONS
     */
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_createTask
     *
     * @brief   Task creation function for the Simple BLE Peripheral.
     *
     * @param   None.
     *
     * @return  None.
     */
    void SimpleBLEPeripheral_createTask(void)
    {
      Task_Params taskParams;
    
      // Configure task
      Task_Params_init(&taskParams);
      taskParams.stack = sbpTaskStack;
      taskParams.stackSize = SBP_TASK_STACK_SIZE;
      taskParams.priority = SBP_TASK_PRIORITY;
    
      Task_construct(&sbpTask, SimpleBLEPeripheral_taskFxn, &taskParams, NULL);
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_init
     *
     * @brief   Called during initialization and contains application
     *          specific initialization (ie. hardware initialization/setup,
     *          table initialization, power up notification, etc), and
     *          profile initialization/setup.
     *
     * @param   None.
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_init(void)
    {
      // ******************************************************************
      // N0 STACK API CALLS CAN OCCUR BEFORE THIS CALL TO ICall_registerApp
      // ******************************************************************
      // Register the current thread as an ICall dispatcher application
      // so that the application can send and receive messages.
      ICall_registerApp(&selfEntity, &sem);
        
    #ifdef USE_RCOSC
      RCOSC_enableCalibration();
    #endif // USE_RCOSC
    
      // Create an RTOS queue for message from profile to be sent to app.
      appMsgQueue = Util_constructQueue(&appMsg);
    
      // Create one-shot clocks for internal periodic events.
      Util_constructClock(&periodicClock1, SimpleBLEPeripheral_clockHandler,
                          SBP_PERIODIC_EVT_PERIOD1, 0, false, SBP_PERIODIC1_EVT);
      // Create one-shot clocks for internal periodic events.
      Util_constructClock(&periodicClock2, SimpleBLEPeripheral_clockHandler,
                          SBP_PERIODIC_EVT_PERIOD2, 0, false, SBP_PERIODIC2_EVT);
      // Create one-shot clocks for internal periodic events.
      Util_constructClock(&periodicClock4, SimpleBLEPeripheral_clockHandler,
                          SBP_PERIODIC_EVT_PERIOD4, 0, false, SBP_PERIODIC4_EVT);
      
      dispHandle = Display_open(Display_Type_LCD, NULL);
    
      // Setup the GAP
      GAP_SetParamValue(TGAP_CONN_PAUSE_PERIPHERAL, DEFAULT_CONN_PAUSE_PERIPHERAL);
    
      // Setup the GAP Peripheral Role Profile
      {
        // For all hardware platforms, device starts advertising upon initialization
        uint8_t initialAdvertEnable = TRUE;
    
        // By setting this to zero, the device will go into the waiting state after
        // being discoverable for 30.72 second, and will not being advertising again
        // until the enabler is set back to TRUE
        uint16_t advertOffTime = 0;
    
        uint8_t enableUpdateRequest = DEFAULT_ENABLE_UPDATE_REQUEST;
        uint16_t desiredMinInterval = DEFAULT_DESIRED_MIN_CONN_INTERVAL;
        uint16_t desiredMaxInterval = DEFAULT_DESIRED_MAX_CONN_INTERVAL;
        uint16_t desiredSlaveLatency = DEFAULT_DESIRED_SLAVE_LATENCY;
        uint16_t desiredConnTimeout = DEFAULT_DESIRED_CONN_TIMEOUT;
    
        // Set the GAP Role Parameters
        GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t),
                             &initialAdvertEnable);
        GAPRole_SetParameter(GAPROLE_ADVERT_OFF_TIME, sizeof(uint16_t),
                             &advertOffTime);
    
        GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData),
                             scanRspData);
        GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData);
    
        GAPRole_SetParameter(GAPROLE_PARAM_UPDATE_ENABLE, sizeof(uint8_t),
                             &enableUpdateRequest);
        GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t),
                             &desiredMinInterval);
        GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t),
                             &desiredMaxInterval);
        GAPRole_SetParameter(GAPROLE_SLAVE_LATENCY, sizeof(uint16_t),
                             &desiredSlaveLatency);
        GAPRole_SetParameter(GAPROLE_TIMEOUT_MULTIPLIER, sizeof(uint16_t),
                             &desiredConnTimeout);
      }
    
      // Set the GAP Characteristics
      GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
    
      // Set advertising interval
      {
        uint16_t advInt = DEFAULT_ADVERTISING_INTERVAL;
    
        GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MIN, advInt);
        GAP_SetParamValue(TGAP_LIM_DISC_ADV_INT_MAX, advInt);
        GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MIN, advInt);
        GAP_SetParamValue(TGAP_GEN_DISC_ADV_INT_MAX, advInt);
      }
    
      // Setup the GAP Bond Manager
      {
        uint32_t passkey = 0; // passkey "000000"
        uint8_t pairMode = GAPBOND_PAIRING_MODE_WAIT_FOR_REQ;
        uint8_t mitm = TRUE;
        uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
        uint8_t bonding = TRUE;
    
        GAPBondMgr_SetParameter(GAPBOND_DEFAULT_PASSCODE, sizeof(uint32_t),
                                &passkey);
        GAPBondMgr_SetParameter(GAPBOND_PAIRING_MODE, sizeof(uint8_t), &pairMode);
        GAPBondMgr_SetParameter(GAPBOND_MITM_PROTECTION, sizeof(uint8_t), &mitm);
        GAPBondMgr_SetParameter(GAPBOND_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
        GAPBondMgr_SetParameter(GAPBOND_BONDING_ENABLED, sizeof(uint8_t), &bonding);
      }
    
       // Initialize GATT attributes
      GGS_AddService(GATT_ALL_SERVICES);           // GAP
      GATTServApp_AddService(GATT_ALL_SERVICES);   // GATT attributes
      DevInfo_AddService();                        // Device Information Service
    
    #ifndef FEATURE_OAD_ONCHIP
      SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile
    #endif //!FEATURE_OAD_ONCHIP
    
    #ifdef FEATURE_OAD
      VOID OAD_addService();                 // OAD Profile
      OAD_register((oadTargetCBs_t *)&simpleBLEPeripheral_oadCBs);
      hOadQ = Util_constructQueue(&oadQ);
    #endif //FEATURE_OAD
    
    #ifdef IMAGE_INVALIDATE
      Reset_addService();
    #endif //IMAGE_INVALIDATE
    
    
    #ifndef FEATURE_OAD_ONCHIP
      // Setup the SimpleProfile Characteristic Values
      {
        uint8_t charValue1[SIMPLEPROFILE_CHAR1_LEN] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 };
        uint8_t charValue2[SIMPLEPROFILE_CHAR2_LEN] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2 };
        uint8_t charValue3 = 0;
        uint8_t charValue4[SIMPLEPROFILE_CHAR4_LEN] = { 4, 4, 4, 4, 4, 4 };
        uint8_t charValue5[SIMPLEPROFILE_CHAR5_LEN] = { 1, 2, 3, 4, 5 };
    
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1, SIMPLEPROFILE_CHAR1_LEN,
                                   charValue1);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR2, SIMPLEPROFILE_CHAR2_LEN,
                                   charValue2);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR3, sizeof(uint8_t),
                                   &charValue3);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, SIMPLEPROFILE_CHAR4_LEN,
                                   charValue4);
        SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR5, SIMPLEPROFILE_CHAR5_LEN,
                                   charValue5);
      }
    
      // Register callback with SimpleGATTprofile
      SimpleProfile_RegisterAppCBs(&SimpleBLEPeripheral_simpleProfileCBs);
    #endif //!FEATURE_OAD_ONCHIP
    
      // Start the Device
      VOID GAPRole_StartDevice(&SimpleBLEPeripheral_gapRoleCBs);
    
      // Start Bond Manager
      VOID GAPBondMgr_Register(&simpleBLEPeripheral_BondMgrCBs);
    
      // Register with GAP for HCI/Host messages
      GAP_RegisterForMsgs(selfEntity);
    
      // Register for GATT local events and ATT Responses pending for transmission
      GATT_RegisterForMsgs(selfEntity);
    
      HCI_LE_ReadMaxDataLenCmd();
    
    #if defined FEATURE_OAD
    #if defined (HAL_IMAGE_A)
      Display_print0(dispHandle, 0, 0, "BLE Peripheral A");
    #else
      Display_print0(dispHandle, 0, 0, "BLE Peripheral B");
    #endif // HAL_IMAGE_A
    #else
      Display_print0(dispHandle, 0, 0, "BLE Peripheral");
    #endif // FEATURE_OAD
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_taskFxn
     *
     * @brief   Application task entry point for the Simple BLE Peripheral.
     *
     * @param   a0, a1 - not used.
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_taskFxn(UArg a0, UArg a1)
    {
      // Initialize application
      SimpleBLEPeripheral_init();
    
      // Application main loop
      for (;;)
      {
        // Waits for a signal to the semaphore associated with the calling thread.
        // Note that the semaphore associated with a thread is signaled when a
        // message is queued to the message receive queue of the thread or when
        // ICall_signal() function is called onto the semaphore.
        ICall_Errno errno = ICall_wait(ICALL_TIMEOUT_FOREVER);
    
        if (errno == ICALL_ERRNO_SUCCESS)
        {
          ICall_EntityID dest;
          ICall_ServiceEnum src;
          ICall_HciExtEvt *pMsg = NULL;
    
          if (ICall_fetchServiceMsg(&src, &dest,
                                    (void **)&pMsg) == ICALL_ERRNO_SUCCESS)
          {
            uint8 safeToDealloc = TRUE;
    
            if ((src == ICALL_SERVICE_CLASS_BLE) && (dest == selfEntity))
            {
              ICall_Stack_Event *pEvt = (ICall_Stack_Event *)pMsg;
    
              // Check for BLE stack events first
              if (pEvt->signature == 0xffff)
              {
                if (pEvt->event_flag & SBP_CONN_EVT_END_EVT)
                {
                  // Try to retransmit pending ATT Response (if any)
                  SimpleBLEPeripheral_sendAttRsp();
                }
              }
              else
              {
                // Process inter-task message
                safeToDealloc = SimpleBLEPeripheral_processStackMsg((ICall_Hdr *)pMsg);
              }
            }
    
            if (pMsg && safeToDealloc)
            {
              ICall_freeMsg(pMsg);
            }
          }
    
          // If RTOS queue is not empty, process app message.
          while (!Queue_empty(appMsgQueue))
          {
            sbpEvt_t *pMsg = (sbpEvt_t *)Util_dequeueMsg(appMsgQueue);
            if (pMsg)
            {
              // Process message.
              SimpleBLEPeripheral_processAppMsg(pMsg);
    
              // Free the space from the message.
              ICall_free(pMsg);
            }
          }
        }
        
        if (events & SBP_PERIODIC1_EVT)
        {
          events &= ~SBP_PERIODIC1_EVT;
    
          Util_startClock(&periodicClock1);
    
          // Perform periodic application task
          SimpleBLEPeripheral_performPeriodicTask1();
        }
        
        if (events & SBP_PERIODIC2_EVT)
        {
          events &= ~SBP_PERIODIC2_EVT;
    
          Util_startClock(&periodicClock2);
    
          // Perform periodic application task
          SimpleBLEPeripheral_performPeriodicTask2();
        }
        
        if (events & SBP_PERIODIC4_EVT)
        {
          events &= ~SBP_PERIODIC4_EVT;
    
          Util_startClock(&periodicClock4);
    
          // Perform periodic application task
          SimpleBLEPeripheral_performPeriodicTask4();
        }
    
    #ifdef FEATURE_OAD
        while (!Queue_empty(hOadQ))
        {
          oadTargetWrite_t *oadWriteEvt = Queue_dequeue(hOadQ);
    
          // Identify new image.
          if (oadWriteEvt->event == OAD_WRITE_IDENTIFY_REQ)
          {
            OAD_imgIdentifyWrite(oadWriteEvt->connHandle, oadWriteEvt->pData);
          }
          // Write a next block request.
          else if (oadWriteEvt->event == OAD_WRITE_BLOCK_REQ)
          {
            OAD_imgBlockWrite(oadWriteEvt->connHandle, oadWriteEvt->pData);
          }
    
          // Free buffer.
          ICall_free(oadWriteEvt);
        }
    #endif //FEATURE_OAD
      }
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_processStackMsg
     *
     * @brief   Process an incoming stack message.
     *
     * @param   pMsg - message to process
     *
     * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
     */
    static uint8_t SimpleBLEPeripheral_processStackMsg(ICall_Hdr *pMsg)
    {
      uint8_t safeToDealloc = TRUE;
    
      switch (pMsg->event)
      {
        case GATT_MSG_EVENT:
          // Process GATT message
          safeToDealloc = SimpleBLEPeripheral_processGATTMsg((gattMsgEvent_t *)pMsg);
          break;
    
        case HCI_GAP_EVENT_EVENT:
          {
            // Process HCI message
            switch(pMsg->status)
            {
              case HCI_COMMAND_COMPLETE_EVENT_CODE:
                // Process HCI Command Complete Event
                break;
    
              default:
                break;
            }
          }
          break;
    
        default:
          // do nothing
          break;
      }
    
      return (safeToDealloc);
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_processGATTMsg
     *
     * @brief   Process GATT messages and events.
     *
     * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
     */
    static uint8_t SimpleBLEPeripheral_processGATTMsg(gattMsgEvent_t *pMsg)
    {
      // See if GATT server was unable to transmit an ATT response
      if (pMsg->hdr.status == blePending)
      {
        // No HCI buffer was available. Let's try to retransmit the response
        // on the next connection event.
        if (HCI_EXT_ConnEventNoticeCmd(pMsg->connHandle, selfEntity,
                                       SBP_CONN_EVT_END_EVT) == SUCCESS)
        {
          // First free any pending response
          SimpleBLEPeripheral_freeAttRsp(FAILURE);
    
          // Hold on to the response message for retransmission
          pAttRsp = pMsg;
    
          // Don't free the response message yet
          return (FALSE);
        }
      }
      else if (pMsg->method == ATT_FLOW_CTRL_VIOLATED_EVENT)
      {
        // ATT request-response or indication-confirmation flow control is
        // violated. All subsequent ATT requests or indications will be dropped.
        // The app is informed in case it wants to drop the connection.
    
        // Display the opcode of the message that caused the violation.
        Display_print1(dispHandle, 5, 0, "FC Violated: %d", pMsg->msg.flowCtrlEvt.opcode);
      }
      else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
      {
        // MTU size updated
        Display_print1(dispHandle, 5, 0, "MTU Size: $d", pMsg->msg.mtuEvt.MTU);
      }
    
      // Free message payload. Needed only for ATT Protocol messages
      GATT_bm_free(&pMsg->msg, pMsg->method);
    
      // It's safe to free the incoming message
      return (TRUE);
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_sendAttRsp
     *
     * @brief   Send a pending ATT response message.
     *
     * @param   none
     *
     * @return  none
     */
    static void SimpleBLEPeripheral_sendAttRsp(void)
    {
      // See if there's a pending ATT Response to be transmitted
      if (pAttRsp != NULL)
      {
        uint8_t status;
    
        // Increment retransmission count
        rspTxRetry++;
    
        // Try to retransmit ATT response till either we're successful or
        // the ATT Client times out (after 30s) and drops the connection.
        status = GATT_SendRsp(pAttRsp->connHandle, pAttRsp->method, &(pAttRsp->msg));
        if ((status != blePending) && (status != MSG_BUFFER_NOT_AVAIL))
        {
          // Disable connection event end notice
          HCI_EXT_ConnEventNoticeCmd(pAttRsp->connHandle, selfEntity, 0);
    
          // We're done with the response message
          SimpleBLEPeripheral_freeAttRsp(status);
        }
        else
        {
          // Continue retrying
          Display_print1(dispHandle, 5, 0, "Rsp send retry: %d", rspTxRetry);
        }
      }
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_freeAttRsp
     *
     * @brief   Free ATT response message.
     *
     * @param   status - response transmit status
     *
     * @return  none
     */
    static void SimpleBLEPeripheral_freeAttRsp(uint8_t status)
    {
      // See if there's a pending ATT response message
      if (pAttRsp != NULL)
      {
        // See if the response was sent out successfully
        if (status == SUCCESS)
        {
          Display_print1(dispHandle, 5, 0, "Rsp sent retry: %d", rspTxRetry);
        }
        else
        {
          // Free response payload
          GATT_bm_free(&pAttRsp->msg, pAttRsp->method);
    
          Display_print1(dispHandle, 5, 0, "Rsp retry failed: %d", rspTxRetry);
        }
    
        // Free response message
        ICall_freeMsg(pAttRsp);
    
        // Reset our globals
        pAttRsp = NULL;
        rspTxRetry = 0;
      }
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_processAppMsg
     *
     * @brief   Process an incoming callback from a profile.
     *
     * @param   pMsg - message to process
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_processAppMsg(sbpEvt_t *pMsg)
    {
      switch (pMsg->hdr.event)
      {
        case SBP_STATE_CHANGE_EVT:
          SimpleBLEPeripheral_processStateChangeEvt((gaprole_States_t)pMsg->
                                                    hdr.state);
          break;
    
        case SBP_CHAR_CHANGE_EVT:
          SimpleBLEPeripheral_processCharValueChangeEvt(pMsg->hdr.state);
          break;
    
        default:
          // Do nothing.
          break;
      }
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_stateChangeCB
     *
     * @brief   Callback from GAP Role indicating a role state change.
     *
     * @param   newState - new state
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_stateChangeCB(gaprole_States_t newState)
    {
      SimpleBLEPeripheral_enqueueMsg(SBP_STATE_CHANGE_EVT, newState);
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_processStateChangeEvt
     *
     * @brief   Process a pending GAP Role state change event.
     *
     * @param   newState - new state
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_processStateChangeEvt(gaprole_States_t newState)
    {
    #ifdef PLUS_BROADCASTER
      static bool firstConnFlag = false;
    #endif // PLUS_BROADCASTER
    
      switch ( newState )
      {
        case GAPROLE_STARTED:
          {
            uint8_t ownAddress[B_ADDR_LEN];
            uint8_t systemId[DEVINFO_SYSTEM_ID_LEN];
    
            GAPRole_GetParameter(GAPROLE_BD_ADDR, ownAddress);
    
            // use 6 bytes of device address for 8 bytes of system ID value
            systemId[0] = ownAddress[0];
            systemId[1] = ownAddress[1];
            systemId[2] = ownAddress[2];
    
            // set middle bytes to zero
            systemId[4] = 0x00;
            systemId[3] = 0x00;
    
            // shift three bytes up
            systemId[7] = ownAddress[5];
            systemId[6] = ownAddress[4];
            systemId[5] = ownAddress[3];
    
            DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, systemId);
    
            // Display device address
            Display_print0(dispHandle, 1, 0, Util_convertBdAddr2Str(ownAddress));
            Display_print0(dispHandle, 2, 0, "Initialized");
          }
          break;
    
        case GAPROLE_ADVERTISING:
          Display_print0(dispHandle, 2, 0, "Advertising");
          break;
    
    #ifdef PLUS_BROADCASTER
        /* After a connection is dropped a device in PLUS_BROADCASTER will continue
         * sending non-connectable advertisements and shall sending this change of
         * state to the application.  These are then disabled here so that sending
         * connectable advertisements can resume.
         */
        case GAPROLE_ADVERTISING_NONCONN:
          {
            uint8_t advertEnabled = FALSE;
    
            // Disable non-connectable advertising.
            GAPRole_SetParameter(GAPROLE_ADV_NONCONN_ENABLED, sizeof(uint8_t),
                               &advertEnabled);
    
            advertEnabled = TRUE;
    
            // Enabled connectable advertising.
            GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t),
                                 &advertEnabled);
    
            // Reset flag for next connection.
            firstConnFlag = false;
    
            SimpleBLEPeripheral_freeAttRsp(bleNotConnected);
          }
          break;
    #endif //PLUS_BROADCASTER
    
        case GAPROLE_CONNECTED:
          {
            linkDBInfo_t linkInfo;
            uint8_t numActive = 0;
    
            //Util_startClock(&periodicClock);
            
            numActive = linkDB_NumActive();
    
            // Use numActive to determine the connection handle of the last
            // connection
            if ( linkDB_GetInfo( numActive - 1, &linkInfo ) == SUCCESS )
            {
              Display_print1(dispHandle, 2, 0, "Num Conns: %d", (uint16_t)numActive);
              Display_print0(dispHandle, 3, 0, Util_convertBdAddr2Str(linkInfo.addr));
            }
            else
            {
              uint8_t peerAddress[B_ADDR_LEN];
    
              GAPRole_GetParameter(GAPROLE_CONN_BD_ADDR, peerAddress);
    
              Display_print0(dispHandle, 2, 0, "Connected");
              Display_print0(dispHandle, 3, 0, Util_convertBdAddr2Str(peerAddress));
            }
    
            #ifdef PLUS_BROADCASTER
              // Only turn advertising on for this state when we first connect
              // otherwise, when we go from connected_advertising back to this state
              // we will be turning advertising back on.
              if (firstConnFlag == false)
              {
                uint8_t advertEnabled = FALSE; // Turn on Advertising
    
                // Disable connectable advertising.
                GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t),
                                     &advertEnabled);
    
                // Set to true for non-connectabel advertising.
                advertEnabled = TRUE;
    
                // Enable non-connectable advertising.
                GAPRole_SetParameter(GAPROLE_ADV_NONCONN_ENABLED, sizeof(uint8_t),
                                     &advertEnabled);
                firstConnFlag = true;
              }
            #endif // PLUS_BROADCASTER
          }
          break;
    
        case GAPROLE_CONNECTED_ADV:
          Display_print0(dispHandle, 2, 0, "Connected Advertising");
          break;
    
        case GAPROLE_WAITING:
          SimpleBLEPeripheral_freeAttRsp(bleNotConnected);
    
          Display_print0(dispHandle, 2, 0, "Disconnected");
    
          // Clear remaining lines
          Display_clearLines(dispHandle, 3, 5);
          break;
    
        case GAPROLE_WAITING_AFTER_TIMEOUT:
          SimpleBLEPeripheral_freeAttRsp(bleNotConnected);
    
          Display_print0(dispHandle, 2, 0, "Timed Out");
    
          // Clear remaining lines
          Display_clearLines(dispHandle, 3, 5);
    
          #ifdef PLUS_BROADCASTER
            // Reset flag for next connection.
            firstConnFlag = false;
          #endif //#ifdef (PLUS_BROADCASTER)
          break;
    
        case GAPROLE_ERROR:
          Display_print0(dispHandle, 2, 0, "Error");
          break;
    
        default:
          Display_clearLine(dispHandle, 2);
          break;
      }
    
      // Update the state
      //gapProfileState = newState;
    }
    
    #ifndef FEATURE_OAD_ONCHIP
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_charValueChangeCB
     *
     * @brief   Callback from Simple Profile indicating a characteristic
     *          value change.
     *
     * @param   paramID - parameter ID of the value that was changed.
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_charValueChangeCB(uint8_t paramID)
    {
      SimpleBLEPeripheral_enqueueMsg(SBP_CHAR_CHANGE_EVT, paramID);
    }
    #endif //!FEATURE_OAD_ONCHIP
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_processCharValueChangeEvt
     *
     * @brief   Process a pending Simple Profile characteristic value change
     *          event.
     *
     * @param   paramID - parameter ID of the value that was changed.
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_processCharValueChangeEvt(uint8_t paramID)
    {
    #ifndef FEATURE_OAD_ONCHIP
      uint8_t newValue;
    
      switch(paramID)
      {
        case SIMPLEPROFILE_CHAR1:
          SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR1, &newValue);
    
          Display_print1(dispHandle, 4, 0, "Char 1: %d", (uint16_t)newValue);
          break;
    
        case SIMPLEPROFILE_CHAR3:
          SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &newValue);
          
          if(newValue == 0) {
            Util_stopClock(&periodicClock1);
            Util_stopClock(&periodicClock2);
            Util_stopClock(&periodicClock4);
          }
          else if(newValue == 0x01) {
            Util_startClock(&periodicClock1);
          }
          else if(newValue == 0x02) {
            Util_startClock(&periodicClock2);
          }
          else if(newValue == 0x04) {
            Util_startClock(&periodicClock4);
          }
    
          //Display_print1(dispHandle, 4, 0, "Char 3: %d", (uint16_t)newValue);
          break;
    
        default:
          // should not reach here!
          break;
      }
    #endif //!FEATURE_OAD_ONCHIP
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_performPeriodicTask
     *
     * @brief   Perform a periodic application task. This function gets called
     *          every five seconds (SBP_PERIODIC_EVT_PERIOD). In this example,
     *          the value of the third characteristic in the SimpleGATTProfile
     *          service is retrieved from the profile, and then copied into the
     *          value of the the fourth characteristic.
     *
     * @param   None.
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_performPeriodicTask1(void)
    {
    /*  
    #ifndef FEATURE_OAD_ONCHIP
      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);
      }
    #endif //!FEATURE_OAD_ONCHIP
    */
      static uint8_t charValue1[SIMPLEPROFILE_CHAR1_LEN] = { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 };
      SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR1, SIMPLEPROFILE_CHAR1_LEN, charValue1);  
      charValue1[19]++;
    }
    
    static void SimpleBLEPeripheral_performPeriodicTask2(void)
    {
    /*  
    #ifndef FEATURE_OAD_ONCHIP
      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);
      }
    #endif //!FEATURE_OAD_ONCHIP
    */
      static uint8_t charValue2[SIMPLEPROFILE_CHAR2_LEN] = { 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0 };
      SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR2, SIMPLEPROFILE_CHAR2_LEN, charValue2);  
      charValue2[18]++;
    }
    
    static void SimpleBLEPeripheral_performPeriodicTask4(void)
    {
    /*  
    #ifndef FEATURE_OAD_ONCHIP
      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);
      }
    #endif //!FEATURE_OAD_ONCHIP
    */
      static uint8_t charValue4[SIMPLEPROFILE_CHAR4_LEN] = { 4, 4, 4, 4, 4, 0 };
      SimpleProfile_SetParameter(SIMPLEPROFILE_CHAR4, SIMPLEPROFILE_CHAR4_LEN, charValue4);  
      charValue4[5]++;
    }
    
    
    #ifdef FEATURE_OAD
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_processOadWriteCB
     *
     * @brief   Process a write request to the OAD profile.
     *
     * @param   event      - event type:
     *                       OAD_WRITE_IDENTIFY_REQ
     *                       OAD_WRITE_BLOCK_REQ
     * @param   connHandle - the connection Handle this request is from.
     * @param   pData      - pointer to data for processing and/or storing.
     *
     * @return  None.
     */
    void SimpleBLEPeripheral_processOadWriteCB(uint8_t event, uint16_t connHandle,
                                               uint8_t *pData)
    {
      oadTargetWrite_t *oadWriteEvt = ICall_malloc( sizeof(oadTargetWrite_t) + \
                                                 sizeof(uint8_t) * OAD_PACKET_SIZE);
    
      if ( oadWriteEvt != NULL )
      {
        oadWriteEvt->event = event;
        oadWriteEvt->connHandle = connHandle;
    
        oadWriteEvt->pData = (uint8_t *)(&oadWriteEvt->pData + 1);
        memcpy(oadWriteEvt->pData, pData, OAD_PACKET_SIZE);
    
        Queue_enqueue(hOadQ, (Queue_Elem *)oadWriteEvt);
    
        // Post the application's semaphore.
        Semaphore_post(sem);
      }
      else
      {
        // Fail silently.
      }
    }
    #endif //FEATURE_OAD
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_clockHandler
     *
     * @brief   Handler function for clock timeouts.
     *
     * @param   arg - event type
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_clockHandler(UArg arg)
    {
      // Store the event.
      events |= arg;
    
      // Wake up the application.
      Semaphore_post(sem);
    }
    
    /*********************************************************************
     * @fn      SimpleBLEPeripheral_enqueueMsg
     *
     * @brief   Creates a message and puts the message in RTOS queue.
     *
     * @param   event - message event.
     * @param   state - message state.
     *
     * @return  None.
     */
    static void SimpleBLEPeripheral_enqueueMsg(uint8_t event, uint8_t state)
    {
      sbpEvt_t *pMsg;
    
      // Create dynamic pointer to message.
      if ((pMsg = ICall_malloc(sizeof(sbpEvt_t))))
      {
        pMsg->hdr.event = event;
        pMsg->hdr.state = state;
    
        // Enqueue the message.
        Util_enqueueMsg(appMsgQueue, sem, (uint8*)pMsg);
      }
    }
    
    /*********************************************************************
    *********************************************************************/
    

    /******************************************************************************
    
     @file  simple_gatt_profile.c
    
     @brief This file contains the Simple GATT profile sample GATT service profile
            for use with the BLE sample application.
    
     Group: WCS, BTS
     Target Device: CC2650, CC2640, CC1350
    
     ******************************************************************************
     
     Copyright (c) 2010-2016, Texas Instruments Incorporated
     All rights reserved.
    
     Redistribution and use in source and binary forms, with or without
     modification, are permitted provided that the following conditions
     are met:
    
     *  Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
    
     *  Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.
    
     *  Neither the name of Texas Instruments Incorporated nor the names of
        its contributors may be used to endorse or promote products derived
        from this software without specific prior written permission.
    
     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
     AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
     THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
     CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
     OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
     WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
     OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
     EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
    
     ******************************************************************************
     Release Name: ble_sdk_2_02_00_31
     Release Date: 2016-06-16 18:57:29
     *****************************************************************************/
    
    /*********************************************************************
     * INCLUDES
     */
    #include <string.h>
    
    #include "bcomdef.h"
    #include "osal.h"
    #include "linkdb.h"
    #include "att.h"
    #include "gatt.h"
    #include "gatt_uuid.h"
    #include "gattservapp.h"
    #include "gapbondmgr.h"
    
    #include "simple_gatt_profile.h"
    
    /*********************************************************************
     * MACROS
     */
    
    /*********************************************************************
     * CONSTANTS
     */
    
    #define SERVAPP_NUM_ATTR_SUPPORTED        19
    
    /*********************************************************************
     * TYPEDEFS
     */
    
    /*********************************************************************
     * GLOBAL VARIABLES
     */
    // 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)
    };
    
    /*********************************************************************
     * EXTERNAL VARIABLES
     */
    
    /*********************************************************************
     * EXTERNAL FUNCTIONS
     */
    
    /*********************************************************************
     * LOCAL VARIABLES
     */
    
    static simpleProfileCBs_t *simpleProfile_AppCBs = NULL;
    
    /*********************************************************************
     * 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_NOTIFY;
    
    // Characteristic 1 Value
    static uint8 simpleProfileChar1[SIMPLEPROFILE_CHAR1_LEN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    
    // Simple Profile Characteristic 1 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 *simpleProfileChar1Config;
                                            
    // Simple Profile Characteristic 1 User Description
    static uint8 simpleProfileChar1UserDesp[17] = "Characteristic 1";
    
    
    // Simple Profile Characteristic 2 Properties
    static uint8 simpleProfileChar2Props = GATT_PROP_NOTIFY;
    
    // Characteristic 2 Value
    static uint8 simpleProfileChar2[SIMPLEPROFILE_CHAR2_LEN] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    
    // Simple Profile Characteristic 2 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 *simpleProfileChar2Config;
                                            
    // Simple Profile Characteristic 2 User Description
    static uint8 simpleProfileChar2UserDesp[17] = "Characteristic 2";
    
    
    // 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";
    
    
    // Simple Profile Characteristic 4 Properties
    static uint8 simpleProfileChar4Props = GATT_PROP_NOTIFY;
    
    // Characteristic 4 Value
    static uint8 simpleProfileChar4[SIMPLEPROFILE_CHAR4_LEN] = { 0, 0, 0, 0, 0, 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";
    
    
    // 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";
    
    /*********************************************************************
     * 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 },
            0, 
            0, 
            simpleProfileChar1 
          },
    
          // Characteristic 1 configuration
          { 
            { ATT_BT_UUID_SIZE, clientCharCfgUUID },
            GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
            0, 
            (uint8 *)&simpleProfileChar1Config 
          },
          
          // 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 },
            0, 
            0, 
            simpleProfileChar2 
          },
    
          // Characteristic 2 configuration
          { 
            { ATT_BT_UUID_SIZE, clientCharCfgUUID },
            GATT_PERMIT_READ | GATT_PERMIT_WRITE, 
            0, 
            (uint8 *)&simpleProfileChar2Config 
          },
          
          // 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 
          },
    };
    
    /*********************************************************************
     * LOCAL FUNCTIONS
     */
    static bStatus_t simpleProfile_ReadAttrCB(uint16_t connHandle,
                                              gattAttribute_t *pAttr, 
                                              uint8_t *pValue, uint16_t *pLen,
                                              uint16_t offset, uint16_t maxLen,
                                              uint8_t method);
    static bStatus_t simpleProfile_WriteAttrCB(uint16_t connHandle,
                                               gattAttribute_t *pAttr,
                                               uint8_t *pValue, uint16_t len,
                                               uint16_t offset, uint8_t method);
    
    /*********************************************************************
     * PROFILE CALLBACKS
     */
    
    // Simple Profile Service Callbacks
    // Note: When an operation on a characteristic requires authorization and 
    // pfnAuthorizeAttrCB is not defined for that characteristic's service, the 
    // Stack will report a status of ATT_ERR_UNLIKELY to the client.  When an 
    // operation on a characteristic requires authorization the Stack will call 
    // pfnAuthorizeAttrCB to check a client's authorization prior to calling
    // pfnReadAttrCB or pfnWriteAttrCB, so no checks for authorization need to be 
    // made within these functions.
    CONST gattServiceCBs_t simpleProfileCBs =
    {
      simpleProfile_ReadAttrCB,  // Read callback function pointer
      simpleProfile_WriteAttrCB, // Write callback function pointer
      NULL                       // Authorization callback function pointer
    };
    
    /*********************************************************************
     * PUBLIC FUNCTIONS
     */
    
    /*********************************************************************
     * @fn      SimpleProfile_AddService
     *
     * @brief   Initializes the Simple Profile service by registering
     *          GATT attributes with the GATT server.
     *
     * @param   services - services to add. This is a bit map and can
     *                     contain more than one service.
     *
     * @return  Success or Failure
     */
    bStatus_t SimpleProfile_AddService( uint32 services )
    {
      uint8 status;
      
      // Allocate Client Characteristic Configuration table
      simpleProfileChar1Config = (gattCharCfg_t *)ICall_malloc( sizeof(gattCharCfg_t) *
                                                                linkDBNumConns );
      if ( simpleProfileChar1Config == NULL )
      {     
        return ( bleMemAllocError );
      }
      
      // Allocate Client Characteristic Configuration table
      simpleProfileChar2Config = (gattCharCfg_t *)ICall_malloc( sizeof(gattCharCfg_t) *
                                                                linkDBNumConns );
      if ( simpleProfileChar2Config == NULL )
      {     
        return ( bleMemAllocError );
      }
    
      // 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, simpleProfileChar1Config );
      // Initialize Client Characteristic Configuration attributes
      GATTServApp_InitCharCfg( INVALID_CONNHANDLE, simpleProfileChar2Config );
      // 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 ),
                                              GATT_MAX_ENCRYPT_KEY_SIZE,
                                              &simpleProfileCBs );
      }
      else
      {
        status = SUCCESS;
      }
    
      return ( status );
    }
    
    /*********************************************************************
     * @fn      SimpleProfile_RegisterAppCBs
     *
     * @brief   Registers the application callback function. Only call 
     *          this function once.
     *
     * @param   callbacks - pointer to application callbacks.
     *
     * @return  SUCCESS or bleAlreadyInRequestedMode
     */
    bStatus_t SimpleProfile_RegisterAppCBs( simpleProfileCBs_t *appCallbacks )
    {
      if ( appCallbacks )
      {
        simpleProfile_AppCBs = appCallbacks;
        
        return ( SUCCESS );
      }
      else
      {
        return ( bleAlreadyInRequestedMode );
      }
    }
    
    /*********************************************************************
     * @fn      SimpleProfile_SetParameter
     *
     * @brief   Set a Simple Profile parameter.
     *
     * @param   param - Profile parameter ID
     * @param   len - length of data to write
     * @param   value - pointer to data to write.  This is dependent on
     *          the parameter ID and WILL be cast to the appropriate 
     *          data type (example: data type of uint16 will be cast to 
     *          uint16 pointer).
     *
     * @return  bStatus_t
     */
    bStatus_t SimpleProfile_SetParameter( uint8 param, uint8 len, void *value )
    {
      bStatus_t ret = SUCCESS;
      switch ( param )
      {
        case SIMPLEPROFILE_CHAR1:
          if ( len == SIMPLEPROFILE_CHAR1_LEN ) 
          {
            //simpleProfileChar4 = *((uint8*)value);
            VOID memcpy( simpleProfileChar1, value, SIMPLEPROFILE_CHAR1_LEN );
            
            // See if Notification has been enabled
            GATTServApp_ProcessCharCfg( simpleProfileChar1Config, simpleProfileChar1, FALSE,
                                        simpleProfileAttrTbl, GATT_NUM_ATTRS( simpleProfileAttrTbl ),
                                        INVALID_TASK_ID, simpleProfile_ReadAttrCB );
          }
          else
          {
            ret = bleInvalidRange;
          }
          break;
    
        case SIMPLEPROFILE_CHAR2:
          if ( len == SIMPLEPROFILE_CHAR2_LEN ) 
          {
            //simpleProfileChar4 = *((uint8*)value);
            VOID memcpy( simpleProfileChar2, value, SIMPLEPROFILE_CHAR2_LEN );
            
            // See if Notification has been enabled
            GATTServApp_ProcessCharCfg( simpleProfileChar2Config, simpleProfileChar2, FALSE,
                                        simpleProfileAttrTbl, GATT_NUM_ATTRS( simpleProfileAttrTbl ),
                                        INVALID_TASK_ID, simpleProfile_ReadAttrCB );
          }
          else
          {
            ret = bleInvalidRange;
          }
          break;
    
        case SIMPLEPROFILE_CHAR3:
          if ( len == sizeof ( uint8 ) ) 
          {
            simpleProfileChar3 = *((uint8*)value);
          }
          else
          {
            ret = bleInvalidRange;
          }
          break;
    
        case SIMPLEPROFILE_CHAR4:
          if ( len == SIMPLEPROFILE_CHAR4_LEN ) 
          {
            //simpleProfileChar4 = *((uint8*)value);
            VOID memcpy( simpleProfileChar4, value, SIMPLEPROFILE_CHAR4_LEN );
            
            // 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 );
    }
    
    /*********************************************************************
     * @fn      SimpleProfile_GetParameter
     *
     * @brief   Get a Simple Profile parameter.
     *
     * @param   param - Profile parameter ID
     * @param   value - pointer to data to put.  This is dependent on
     *          the parameter ID and WILL be cast to the appropriate 
     *          data type (example: data type of uint16 will be cast to 
     *          uint16 pointer).
     *
     * @return  bStatus_t
     */
    bStatus_t SimpleProfile_GetParameter( uint8 param, void *value )
    {
      bStatus_t ret = SUCCESS;
      switch ( param )
      {
        case SIMPLEPROFILE_CHAR1:
          VOID memcpy( value, simpleProfileChar1, SIMPLEPROFILE_CHAR1_LEN );
          break;
    
        case SIMPLEPROFILE_CHAR2:
          VOID memcpy( value, simpleProfileChar2, SIMPLEPROFILE_CHAR2_LEN );
          break;      
    
        case SIMPLEPROFILE_CHAR3:
          *((uint8*)value) = simpleProfileChar3;
          break;  
    
        case SIMPLEPROFILE_CHAR4:
          VOID memcpy( value, simpleProfileChar4, SIMPLEPROFILE_CHAR4_LEN );
          break;
    
        case SIMPLEPROFILE_CHAR5:
          VOID memcpy( value, simpleProfileChar5, SIMPLEPROFILE_CHAR5_LEN );
          break;      
          
        default:
          ret = INVALIDPARAMETER;
          break;
      }
      
      return ( ret );
    }
    
    /*********************************************************************
     * @fn          simpleProfile_ReadAttrCB
     *
     * @brief       Read an attribute.
     *
     * @param       connHandle - connection message was received on
     * @param       pAttr - pointer to attribute
     * @param       pValue - pointer to data to be read
     * @param       pLen - length of data to be read
     * @param       offset - offset of the first octet to be read
     * @param       maxLen - maximum length of data to be read
     * @param       method - type of read message
     *
     * @return      SUCCESS, blePending or Failure
     */
    static bStatus_t simpleProfile_ReadAttrCB(uint16_t connHandle,
                                              gattAttribute_t *pAttr,
                                              uint8_t *pValue, uint16_t *pLen,
                                              uint16_t offset, uint16_t maxLen,
                                              uint8_t method)
    {
      bStatus_t status = SUCCESS;
      
      // 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:
            *pLen = SIMPLEPROFILE_CHAR1_LEN;
            VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR1_LEN );
            break;
          case SIMPLEPROFILE_CHAR2_UUID:
            *pLen = SIMPLEPROFILE_CHAR2_LEN;
            VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR2_LEN );
            break;
          case SIMPLEPROFILE_CHAR4_UUID:
            *pLen = SIMPLEPROFILE_CHAR4_LEN;
            VOID memcpy( pValue, pAttr->pValue, SIMPLEPROFILE_CHAR4_LEN );
            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 );
    }
    
    /*********************************************************************
     * @fn      simpleProfile_WriteAttrCB
     *
     * @brief   Validate attribute data prior to a write operation
     *
     * @param   connHandle - connection message was received on
     * @param   pAttr - pointer to attribute
     * @param   pValue - pointer to data to be written
     * @param   len - length of data
     * @param   offset - offset of the first octet to be written
     * @param   method - type of write message
     *
     * @return  SUCCESS, blePending or Failure
     */
    static bStatus_t simpleProfile_WriteAttrCB(uint16_t connHandle,
                                               gattAttribute_t *pAttr,
                                               uint8_t *pValue, uint16_t len,
                                               uint16_t offset, uint8_t method)
    {
      bStatus_t status = SUCCESS;
      uint8 notifyApp = 0xFF;
      
      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 characteristic value changed then callback function to notify application of change
      if ( (notifyApp != 0xFF ) && simpleProfile_AppCBs && simpleProfile_AppCBs->pfnSimpleProfileChange )
      {
        simpleProfile_AppCBs->pfnSimpleProfileChange( notifyApp );  
      }
      
      return ( status );
    }
    
    /*********************************************************************
    *********************************************************************/
    

    4405.simple_gatt_profile.h

  • Hi Tim,
    About my sniffer log

    notify_test1_10971_handle_34 : packet 19701 get notification from handle:34, last byte is 0xDF. Next notification packet is 10990, but last byte is 0xE1

    notify_test2_5802_handle_34 : packet 5802 get notification from handle:34, last byte is 0xA8. Next notification packet is 5815, but last byte is 0xAA

    If you have any suggestions please let me know, Thanks.
  • Hi Tim

    I have a same issue too.

    My central device is iPhone 5s, peripheral device is sensorTag 2.0

    I have two service, each service have one characteristic can be read & notify, packet size are 20bytes, notify period are 20ms.

    And the data in packet is simply count up before every notify.

    When I enable two notification, I get data loss with stack 2.2.0, but in stack 2.1.1 have no data loss issue happen.

    Could you help me to solve this issue?

    Thanks.