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.

RTOS/CC2640: CCCD could not be found at client side

Part Number: CC2640

Tool/software: TI-RTOS

Hello,

I am using multi_role example code for launchpad and I am connecting one peripheral directly using GAP commands,

here I want to receive notification from peripheral to multirole client, here my peripheral sends notification from Charecteristic 4,

I am trying to enabling CCCD from client side,

but it is not getting enabled, here I am sharing code snippets.

        else if (discInfo[connIndex].discState == BLE_DISC_STATE_CHAR)
        {

            // Characteristic descriptors found
            if (pMsg->method == ATT_FIND_INFO_RSP &&
                    pMsg->msg.findInfoRsp.numInfo > 0)
            {
              uint8_t i;
              // For each handle/uuid pair
              for (i = 0; i < pMsg->msg.findInfoRsp.numInfo; i++)
              {
                if(pMsg->msg.findInfoRsp.format == ATT_HANDLE_BT_UUID_TYPE)
                {
                  // Look for CCCD
                  if (ATT_BT_PAIR_UUID(pMsg->msg.findInfoRsp.pInfo, i) ==
                          GATT_CLIENT_CHAR_CFG_UUID)
                  {
                    // CCCD found
                    if (noCharCCCDHdl < MAX_CCCD_HANDLES){
                        charCCCDHdl[noCharCCCDHdl] = ATT_BT_PAIR_HANDLE(pMsg->msg.findInfoRsp.pInfo, i);
                        noCharCCCDHdl++;
                    }
                  }
                }
              }
            }

This code is inside multi_role_processGATTDiscEvent

static uint8_t multi_role_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,
                                   MR_CONN_EVT_END_EVT) == SUCCESS)
    {
              System_printf("Inside conn event notice command\n");
              System_flush();
      // First free any pending response
      multi_role_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, 0, 0, "FC Violated: %d", pMsg->msg.flowCtrlEvt.opcode);
  }
  else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
  {
    // MTU size updated
    Display_print1(dispHandle, 0, 0, "MTU Size: %d", pMsg->msg.mtuEvt.MTU);
  }

  //messages from GATT server
  if (linkDB_NumActive() > 0)
  {
//      System_printf("Inside linkDB_NumActive>0\n");
//      System_flush();
    //find index from connection handle
    connIndex = multi_role_mapConnHandleToIndex(pMsg->connHandle);

    if ((pMsg->method == ATT_READ_RSP) ||
            (pMsg->method == ATT_HANDLE_VALUE_NOTI) ||
            ((pMsg->method == ATT_ERROR_RSP) &&
                    (pMsg->msg.errorRsp.reqOpcode == ATT_READ_REQ)))
    {
      if (pMsg->method == ATT_ERROR_RSP)
      {
//        Display_print1(dispHandle, 0, 0, "Read Error %d", pMsg->msg.errorRsp.errCode);
      }
      else
      {
        // After a successful read, display the read value
//        Display_print1(dispHandle, 0, 0, "Read rsp: %d", pMsg->msg.readRsp.pValue[0]);
        uint8_t *notificationdata = pMsg->msg.handleValueNoti.pValue;
      }

    }
    else if ((pMsg->method == ATT_WRITE_RSP)  ||
             ((pMsg->method == ATT_ERROR_RSP) &&
              (pMsg->msg.errorRsp.reqOpcode == ATT_WRITE_REQ)))
    {
      if (pMsg->method == ATT_ERROR_RSP == ATT_ERROR_RSP)
      {
//        Display_print1(dispHandle, 0, 0, "Write Error %d", pMsg->msg.errorRsp.errCode);
      }
      else
      {
        // After a succesful write, display the value that was written and
        // increment value
//        Display_print1(dispHandle, 0, 0, "Write sent: %d", charVal++);
      }
    }
    else if (discInfo[connIndex].discState != BLE_DISC_STATE_IDLE)
    {
      multi_role_processGATTDiscEvent(pMsg);
    }
  } // else - in case a GATT message came after a connection has dropped, ignore it.

  // 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);
}

This is multi_role_processGATTMsg function

static void multi_role_EnableNotification( uint16 connHandle, uint16 attrHandle )
{
    attWriteReq_t req;
//    System_printf("Inside enable notification\n");
//    System_flush();
    req.pValue = GATT_bm_alloc( connHandle, ATT_WRITE_REQ, 2, NULL );
    if ( req.pValue != NULL )
    {
      uint8 notificationsOn[] = {0x01, 0x00};
  //    attAttribute_t
      req.handle = attrHandle;

      req.len = 2;
      memcpy(req.pValue, notificationsOn, 2);

      req.sig = FALSE;
      req.cmd = TRUE;

      if ( GATT_WriteNoRsp( connHandle, &req ) != SUCCESS )
      {
        GATT_bm_free( (gattMsg_t *)&req, ATT_WRITE_REQ );
      }

    }
}

This is enable notification function

  Util_constructClock(&startNotiEnableClock, multi_role_startNotiHandler,
                        DEFAULT_NOTI_ENABLE_DELAY, 0, false, SBC_START_NOTIFICATION);

    if (currentCharCCCDHdlIndex < noCharCCCDHdl)
    {
      multi_role_EnableNotification( connHandle, charCCCDHdl[currentCharCCCDHdlIndex ] );
      currentCharCCCDHdlIndex ++;

       //Enable notification on peripheral(after a few seconds delay, let it finish connection/discovery process)
       {
         Util_startClock(&startNotiEnableClock);
       }
    }

This is timer for some delay for connection establishment.

here in multi_role_processGATTDiscEvent while debugging i found that It's not going into loop where I find CCCD.

Please provide some guidance so I can resolve this issue as soon as possible.

regards,

jaydipsinh

  • Hi jaydipsinh,

    The multi_role example is pretty tailor made for discovering the simple_gatt_profile. If you are trying to discover a different service or characteristic, then you will need to modify the logic in multi_role_processGATTDiscEvent.

    What is the device you're trying to discover and what changes have you made to the multi_role example?
  • Hello sean,
    As I have written above in very first code snippet is from multi_role_processGATTDiscEvent.
    will you please check again and tell me why this is not working?
    I am trying to discover simple_peripheral CCCD to enable notification at client side I have used same module for peripheral that I have used for multirole(CC2640).
    regards,
    jaydipsinh

  • Hello sean,

    this is the reference thread that I have considered.

    regards,

    jaydipsinh

  • Hi Jaydipsinh,

    It doesn't look like you're calling GATT_DiscAllCharDescs() is this somewhere in a snippet that isn't in this thread?

    You'll need to call this in order to initiate finding the descriptors. Make sure the start and end handle correspond to the char that has the CCCD.
  • Hello sean,

    Yes I am calling GATT_DiscAllCharDescs inside multi_role_processGATTDiscEvent function,

    here is the snippet of that.

    static void multi_role_processGATTDiscEvent(gattMsgEvent_t *pMsg)
    { 
    
            //MTU update
            if (pMsg->method == ATT_MTU_UPDATED_EVENT)
            {
              // MTU size updated
              Display_print1(dispHandle, 0, 0, "MTU Size: %d", pMsg->msg.mtuEvt.MTU);
            }
            //if we've updated the MTU size
            else if (discInfo[connIndex].discState== BLE_DISC_STATE_MTU)
            {
              // MTU size response received, discover simple BLE service
              if (pMsg->method == ATT_EXCHANGE_MTU_RSP)
              {
                uint8_t uuid[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_SERV_UUID),
                                                   HI_UINT16(SIMPLEPROFILE_SERV_UUID) };
                //advance state
                discInfo[connIndex].discState= BLE_DISC_STATE_SVC;
    
                // Discovery simple BLE service
                VOID GATT_DiscPrimaryServiceByUUID(pMsg->connHandle, uuid, ATT_BT_UUID_SIZE,
                                                   selfEntity);
              }
            }
            else if (discInfo[connIndex].discState == BLE_DISC_STATE_SVC)
            {
                // Service found, store handles
                if (pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
                    pMsg->msg.findByTypeValueRsp.numInfo > 0)
                {
                    discInfo[connIndex].svcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
                    discInfo[connIndex].svcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
    
                }
    
                // If procedure complete
                if (((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP) &&
                     (pMsg->hdr.status == bleProcedureComplete))  ||
                     (pMsg->method == ATT_ERROR_RSP))
                {
                    if (discInfo[connIndex].svcStartHdl != 0)
                    {
                        attReadByTypeReq_t req;
    
                        // Discover characteristic
                        discInfo[connIndex].discState= BLE_DISC_STATE_CHAR;
                        req.startHandle = discInfo[connIndex].svcStartHdl;
                        req.endHandle = discInfo[connIndex].svcEndHdl;
                        req.type.len = ATT_BT_UUID_SIZE;
                        req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR4_UUID);
                        req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR4_UUID);
    
                        //send characteristic discovery request
                        VOID GATT_ReadUsingCharUUID(pMsg->connHandle, &req, selfEntity);
    
                        GATT_DiscAllCharDescs(connHandle, discInfo[connIndex].svcStartHdl, discInfo[connIndex].svcEndHdl, selfEntity);
    
    
                    }
                }
                discInfo[connIndex].discState = BLE_DISC_STATE_CHAR;
            }
            else if (discInfo[connIndex].discState == BLE_DISC_STATE_CHAR)
            {
    
                // Characteristic descriptors found
                if (pMsg->method == ATT_FIND_INFO_RSP &&
                        pMsg->msg.findInfoRsp.numInfo > 0)
                {
                  uint8_t i;
                  // For each handle/uuid pair
                  for (i = 0; i < pMsg->msg.findInfoRsp.numInfo; i++)
                  {
                    if(pMsg->msg.findInfoRsp.format == ATT_HANDLE_BT_UUID_TYPE)
                    {
                      // Look for CCCD
                      if (ATT_BT_PAIR_UUID(pMsg->msg.findInfoRsp.pInfo, i) ==
                              GATT_CLIENT_CHAR_CFG_UUID)
                      {
    
                        // CCCD found
                        if (noCharCCCDHdl < MAX_CCCD_HANDLES){
                            charCCCDHdl[noCharCCCDHdl] = ATT_BT_PAIR_HANDLE(pMsg->msg.findInfoRsp.pInfo, i);
                            noCharCCCDHdl++;
    //                        System_printf("CCCD Found\n");
    //                        System_flush();
                        }
                      }
                    }
                  }
                }
                // If procedure complete
                 if ((pMsg->method == ATT_FIND_INFO_RSP  &&
                      pMsg->hdr.status == bleProcedureComplete) ||
                     (pMsg->method == ATT_ERROR_RSP))
                 {
                     //Enable notification on peripheral(after a few seconds delay, let it finish connection/discovery process)
                     {
                       Util_startClock(&startNotiEnableClock);
                     }
    
    //                 procedureInProgress = FALSE;
                     discInfo[connIndex].discState = BLE_DISC_STATE_IDLE;
                 }
            }
    }

    regards,

    jaydipsinh

  • Hello,

    Here I am attaching multi_role.c file

    /*
     * Filename: multi_role.c
     *
     * Description: This file contains the multi_role sample application for use
     * with the CC2650 Bluetooth Low Energy Protocol Stack.
     *
     *
     * Copyright (C) 2016 Texas Instruments Incorporated - http://www.ti.com/
     *
     *
     *  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.
     *
     */
    
    /*********************************************************************
    * INCLUDES
    */
    #include <string.h>
    #include <xdc/runtime/System.h>
    #include <xdc/std.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 "bcomdef.h"
    #include "hci_tl.h"
    #include "linkdb.h"
    #include "gatt.h"
    #include "gapgattserver.h"
    #include "gattservapp.h"
    #include "devinfoservice.h"
    #include "simple_gatt_profile.h"
    #include "gatt_uuid.h"
    #include "multi.h"
    #include "gapbondmgr.h"
    
    #include "osal_snv.h"
    #include "icall_apimsg.h"
    
    #include "util.h"
    #include "board_key.h"
    #include <ti/mw/display/Display.h>
    #include "board.h"
    
    #include "multi_role.h"
    
    #include <ti/mw/lcd/LCDDogm1286.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
    
    // Connection parameters if automatic  parameter update request is enabled
    #define DEFAULT_DESIRED_MIN_CONN_INTERVAL     80
    #define DEFAULT_DESIRED_MAX_CONN_INTERVAL     800
    #define DEFAULT_DESIRED_SLAVE_LATENCY         0
    #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 parameters
    #define DEFAULT_CONN_INT                      200
    #define DEFAULT_CONN_TIMEOUT                  1000
    #define DEFAULT_CONN_LATENCY                  0
    
    // Default service discovery timer delay in ms
    #define DEFAULT_SVC_DISCOVERY_DELAY           1000
    
    // Scan parameters
    #define DEFAULT_SCAN_DURATION                 4000
    #define DEFAULT_SCAN_WIND                     80
    #define DEFAULT_SCAN_INT                      80
    
    // Maximum number of scan responses
    #define DEFAULT_MAX_SCAN_RES                  25
    
    // TRUE to filter discovery results on desired service UUID
    #define DEFAULT_DEV_DISC_BY_SVC_UUID          TRUE
    
    // Discovey mode (limited, general, all)
    #define DEFAULT_DISCOVERY_MODE                DEVDISC_MODE_ALL
    
    // TRUE to use active scan
    #define DEFAULT_DISCOVERY_ACTIVE_SCAN         TRUE
    
    // TRUE to use white list during discovery
    #define DEFAULT_DISCOVERY_WHITE_LIST          FALSE
    
    // TRUE to use high scan duty cycle when creating link
    #define DEFAULT_LINK_HIGH_DUTY_CYCLE          FALSE
    
    // TRUE to use white list when creating link
    #define DEFAULT_LINK_WHITE_LIST               FALSE
    
    #define DEFAULT_NOTI_ENABLE_DELAY             10000
    
    #define MAX_CCCD_HANDLES                      5
    #define SBC_START_NOTIFICATION                0x0080
    // Type of Display to open
    #if !defined(Display_DISABLE_ALL)
    #if !defined(BOARD_DISPLAY_EXCLUDE_LCD)
    #define SBC_DISPLAY_TYPE Display_Type_LCD
    #elif !defined (BOARD_DISPLAY_EXCLUDE_UART)
    #define SBC_DISPLAY_TYPE Display_Type_UART
    #else // BOARD_DISPLAY_EXCLUDE_LCD && BOARD_DISPLAY_EXCLUDE_UART
    #define SBC_DISPLAY_TYPE 0 // Option not supported
    #endif // BOARD_DISPLAY_EXCLUDE_LCD
    #else // Display_DISABLE_ALL
    #define SBC_DISPLAY_TYPE 0 // No Display
    #endif // Display_DISABLE_ALL
    
    // Task configuration
    #define MR_TASK_PRIORITY                     1
    #ifndef MR_TASK_STACK_SIZE
    #define MR_TASK_STACK_SIZE                   944
    #endif
    
    // Internal Events for RTOS application
    #define MR_STATE_CHANGE_EVT                  0x0001
    #define MR_CHAR_CHANGE_EVT                   0x0002
    #define MR_CONN_EVT_END_EVT                  0x0004
    #define MR_KEY_CHANGE_EVT                    0x0008
    #define MR_PAIRING_STATE_EVT                 0x0010
    #define MR_PASSCODE_NEEDED_EVT               0x0020
    
    
    
    // Discovery states
    typedef enum {
      BLE_DISC_STATE_IDLE,                // Idle
      BLE_DISC_STATE_MTU,                 // Exchange ATT MTU size
      BLE_DISC_STATE_SVC,                 // Service discovery
      BLE_DISC_STATE_CHAR                 // Characteristic discovery
    } discState_t;
    
    // Key states for connections
    typedef enum {
      SCAN,                    // Scan for devices
      CONNECT,                 // Establish a connection to a discovered device
      GATT_RW,                 // Perform GATT Read/Write
      CONN_UPDATE,             // Send Connection Parameter Update
      DISCONNECT,              // Disconnect
      ADVERTISE,               // Turn advertising on / off
    } keyPressConnOpt_t;
    
    // Key states for connections
    typedef enum {
      MAIN_MENU,               // top level menu
      DISC_MENU,               // Choose discovered device to connect to
      CONN_MENU                // Choose connected device to perform operation on               
    } menuLevel_t;
    
    // Key option state.
    static keyPressConnOpt_t keyPressConnOpt;
    // Menu level
    static menuLevel_t menuLevel = MAIN_MENU;
    
    // pointer to allocate the connection handle map
    static uint16_t *connHandleMap;
    uint8_t mr_Cache[13];
    /*********************************************************************
    * TYPEDEFS
    */
    
    // App event passed from profiles.
    typedef struct
    {
      uint16_t event;  // event type
      uint8_t *pData; // event data pointer
    } mrEvt_t;
    
    // pairing callback event
    typedef struct
    {
      uint16 connectionHandle; //!< connection Handle
      uint8 state;             //!< state returned from GAPBondMgr
      uint8 status;            //!< status of state
    } gapPairStateEvent_t;
    
    // discovery information
    typedef struct
    {
      discState_t discState;   //discovery state
      uint16_t svcStartHdl;    //service start handle     
      uint16_t svcEndHdl;      //service end handle
      uint16_t charHdl;        //characteristic handle
    //  uint16_t charCCCDHdl[MAX_CCCD_HANDLES]     //CCCD handle
    } discInfo_t;
    
    /*********************************************************************
    * GLOBAL VARIABLES
    */
    
    // Display Interface
    Display_Handle dispHandle = NULL;
    
    /*********************************************************************
    * LOCAL VARIABLES
    */
    
    /*********************************************************************
    * 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;
    
    // Queue object used for app messages
    static Queue_Struct appMsg;
    static Queue_Handle appMsgQueue;
    
    // Task configuration
    Task_Struct mrTask;
    Char mrTaskStack[MR_TASK_STACK_SIZE];
    
    static uint8_t scanRspData[] =
    {
      // complete name
      13,   // length of this data
      GAP_ADTYPE_LOCAL_NAME_COMPLETE,
      'M', 'u', 'l', 't', 'i', ' ', 'R', 'o', 'l', 'e', ':', ')',
      
      // 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
      0x03,   // length of this data
      GAP_ADTYPE_16BIT_MORE,      // some of the UUID's, but not all
      LO_UINT16(SIMPLEPROFILE_SERV_UUID),
      HI_UINT16(SIMPLEPROFILE_SERV_UUID)
    };
    
    // GAP GATT Attributes
    static uint8_t attDeviceName[GAP_DEVICE_NAME_LEN] = "Multi Role :)";
    
    // Globals used for ATT Response retransmission
    static gattMsgEvent_t *pAttRsp = NULL;
    static uint8_t rspTxRetry = 0;
    
    // Pointer to per connection discovery info
    discInfo_t *discInfo;
    
    // Maximim PDU size (default = 27 octets)
    static uint16 maxPduSize;  
    
    static uint16_t charCCCDHdl[MAX_CCCD_HANDLES];
    static uint16_t noCharCCCDHdl = 0;
    static uint16_t currentCharCCCDHdlIndex = 0;
    
    // Scanning state
    static bool scanningStarted = FALSE;
    
    // Number of scan results and scan result index for menu functionality
    static uint8_t scanRes;
    static uint8_t scanIdx;
    
    // Connected Device indices for menu functionality
    //static uint16_t searchIdx = 0x00;
    static uint16_t connIdx = INVALID_CONNHANDLE;
    
    // connecting state
    static uint8_t connectingState = 0;
    
    // Scan result list
    static gapDevRec_t devList[DEFAULT_MAX_SCAN_RES];
    
    // Value to write
    static uint8_t charVal = 0;
    uint8_t value_read;
    // Value read/write toggle
    static bool doWrite = FALSE;
    
    // Dummy parameters to use for connection updates
    gapRole_updateConnParams_t updateParams =
    {
      .connHandle = INVALID_CONNHANDLE,
      .minConnInterval = 80,
      .maxConnInterval = 150,
      .slaveLatency = 0,
      .timeoutMultiplier = 200
    };
    
    // connection index for mapping connection handles
    static uint16_t connIndex = INVALID_CONNHANDLE;
    
    static Clock_Struct startNotiEnableClock;
    // maximum number of connected devices
    static uint8_t maxNumBleConns = MAX_NUM_BLE_CONNS;
    static uint16_t connHandle = GAP_CONNHANDLE_INIT;
    /*********************************************************************
    * LOCAL FUNCTIONS
    */
    static void multi_role_init( void );
    static void multi_role_taskFxn(UArg a0, UArg a1);
    static uint8_t multi_role_processStackMsg(ICall_Hdr *pMsg);
    static uint8_t multi_role_processGATTMsg(gattMsgEvent_t *pMsg);
    static void multi_role_processAppMsg(mrEvt_t *pMsg);
    static void multi_role_processCharValueChangeEvt(uint8_t paramID);
    static void multi_role_processRoleEvent(gapMultiRoleEvent_t *pEvent);
    static void multi_role_sendAttRsp(void);
    static void multi_role_freeAttRsp(uint8_t status);
    static void multi_role_charValueChangeCB(uint8_t paramID);
    static uint8_t multi_role_enqueueMsg(uint16_t event, uint8_t *pData);
    static void multi_role_startDiscovery(uint16_t connHandle);
    static void multi_role_processGATTDiscEvent(gattMsgEvent_t *pMsg);
    static bool multi_role_findSvcUuid(uint16_t uuid, uint8_t *pData, uint8_t dataLen);
    static void multi_role_addDeviceInfo(uint8_t *pAddr, uint8_t addrType);
    //static void multi_role_handleKeys(void);
    static uint8_t multi_role_eventCB(gapMultiRoleEvent_t *pEvent);
    static void multi_role_sendAttRsp(void);
    static void multi_role_freeAttRsp(uint8_t status);
    static uint16_t multi_role_mapConnHandleToIndex(uint16_t connHandle);
    static void multi_role_keyChangeHandler(uint8 keysPressed);
    static uint8_t multi_role_addMappingEntry(uint16_t connHandle);
    static void multi_role_processPasscode(gapPasskeyNeededEvent_t *pData);
    static void multi_role_processPairState(gapPairStateEvent_t* pairingEvent);
    static void multi_role_passcodeCB(uint8_t *deviceAddr, uint16_t connHandle,
                                      uint8_t uiInputs, uint8_t uiOutputs, uint32_t numComparison);
    static void multi_role_pairStateCB(uint16_t connHandle, uint8_t state,
                                       uint8_t status);
    static void multi_role_EnableNotification( uint16 connHandle, uint16 attrHandle );
    static void multi_role_startNotiHandler(UArg arg);
    bool mr_doScan(void);
    bool mr_doConnect(void);
    
    /*********************************************************************
    * PROFILE CALLBACKS
    */
    
    // GAP Role Callbacks
    static gapRolesCBs_t multi_role_gapRoleCBs =
    {
      multi_role_eventCB,        // events to be handled by the app are passed through the GAP Role here
    };
    
    // Simple GATT Profile Callbacks
    static simpleProfileCBs_t multi_role_simpleProfileCBs =
    {
      multi_role_charValueChangeCB // Characteristic value change callback
    };
    
    // GAP Bond Manager Callbacks
    static gapBondCBs_t multi_role_BondMgrCBs =
    {
      (pfnPasscodeCB_t)multi_role_passcodeCB, // Passcode callback
      multi_role_pairStateCB                  // Pairing state callback
    };
    
    
    /*********************************************************************
    * PUBLIC FUNCTIONS
    */
    
    /*********************************************************************
    * @fn      multi_role_createTask
    *
    * @brief   Task creation function for the Simple BLE Peripheral.
    *
    * @param   None.
    *
    * @return  None.
    */
    void multi_role_createTask(void)
    {
      Task_Params taskParams;
      
      // Configure task
      Task_Params_init(&taskParams);
      taskParams.stack = mrTaskStack;
      taskParams.stackSize = MR_TASK_STACK_SIZE;
      taskParams.priority = MR_TASK_PRIORITY;
    
      Task_construct(&mrTask, multi_role_taskFxn, &taskParams, NULL);
    }
    
    /*********************************************************************
    * @fn      multi_role_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 multi_role_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);
      
      // Create an RTOS queue for message from profile to be sent to app.
      appMsgQueue = Util_constructQueue(&appMsg);
      
      //init keys and LCD
    //  Board_initKeys(multi_role_keyChangeHandler);
      // Open Display.
      dispHandle = Display_open(SBC_DISPLAY_TYPE, NULL);
    
      Util_constructClock(&startNotiEnableClock, multi_role_startNotiHandler,
                            DEFAULT_NOTI_ENABLE_DELAY, 0, false, SBC_START_NOTIFICATION);
    
    
      // Setup the GAP
      {
        /*-------------------PERIPHERAL-------------------*/
        // set advertising interval the same for all scenarios
        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);    
        GAP_SetParamValue(TGAP_CONN_ADV_INT_MIN, advInt);
        GAP_SetParamValue(TGAP_CONN_ADV_INT_MAX, advInt);    
        /*-------------------CENTRAL-------------------*/
        // set scan duration
        GAP_SetParamValue(TGAP_GEN_DISC_SCAN, DEFAULT_SCAN_DURATION);
        // scan interval and window the same for all scenarios
        GAP_SetParamValue(TGAP_CONN_SCAN_INT, DEFAULT_SCAN_INT);
        GAP_SetParamValue(TGAP_CONN_SCAN_WIND, DEFAULT_SCAN_WIND);
        GAP_SetParamValue(TGAP_CONN_HIGH_SCAN_INT, DEFAULT_SCAN_INT);
        GAP_SetParamValue(TGAP_CONN_HIGH_SCAN_WIND, DEFAULT_SCAN_WIND);
        GAP_SetParamValue(TGAP_GEN_DISC_SCAN_INT, DEFAULT_SCAN_INT);
        GAP_SetParamValue(TGAP_GEN_DISC_SCAN_WIND, DEFAULT_SCAN_WIND);
        GAP_SetParamValue(TGAP_LIM_DISC_SCAN_INT, DEFAULT_SCAN_INT);
        GAP_SetParamValue(TGAP_LIM_DISC_SCAN_WIND, DEFAULT_SCAN_WIND);
        GAP_SetParamValue(TGAP_CONN_EST_SCAN_INT, DEFAULT_SCAN_INT);
        GAP_SetParamValue(TGAP_CONN_EST_SCAN_WIND, DEFAULT_SCAN_WIND);
        // set connection parameters
        GAP_SetParamValue(TGAP_CONN_EST_INT_MIN, DEFAULT_CONN_INT);
        GAP_SetParamValue(TGAP_CONN_EST_INT_MAX, DEFAULT_CONN_INT);
        GAP_SetParamValue(TGAP_CONN_EST_SUPERV_TIMEOUT, DEFAULT_CONN_TIMEOUT);
        GAP_SetParamValue(TGAP_CONN_EST_LATENCY, DEFAULT_CONN_LATENCY);
        
        //register to receive GAP and HCI messages
        GAP_RegisterForMsgs(selfEntity);
    
      }
      
      // Setup the GAP Role Profile
      {
        /*--------PERIPHERAL-------------*/
        uint8_t initialAdvertEnable = TRUE;
        uint16_t advertOffTime = 0;
        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;
        
        // device starts advertising upon initialization
        GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t),
                             &initialAdvertEnable, NULL);
        // 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
        GAPRole_SetParameter(GAPROLE_ADVERT_OFF_TIME, sizeof(uint16_t),
                             &advertOffTime, NULL);
        // set scan response data
        GAPRole_SetParameter(GAPROLE_SCAN_RSP_DATA, sizeof(scanRspData),
                             scanRspData, NULL);
        // set advertising data
        GAPRole_SetParameter(GAPROLE_ADVERT_DATA, sizeof(advertData), advertData, NULL);
        // set connection parameters
        GAPRole_SetParameter(GAPROLE_MIN_CONN_INTERVAL, sizeof(uint16_t),
                             &desiredMinInterval, NULL);
        GAPRole_SetParameter(GAPROLE_MAX_CONN_INTERVAL, sizeof(uint16_t),
                             &desiredMaxInterval, NULL);
        GAPRole_SetParameter(GAPROLE_SLAVE_LATENCY, sizeof(uint16_t),
                             &desiredSlaveLatency, NULL);
        GAPRole_SetParameter(GAPROLE_TIMEOUT_MULTIPLIER, sizeof(uint16_t),
                             &desiredConnTimeout, NULL);
        /*--------------CENTRAL-----------------*/
        uint8_t scanRes = DEFAULT_MAX_SCAN_RES;
        //set the max amount of scan responses
        GAPRole_SetParameter(GAPROLE_MAX_SCAN_RES, sizeof(uint8_t), 
                             &scanRes, NULL);     
        
        // Start the GAPRole and negotiate max number of connections
        VOID GAPRole_StartDevice(&multi_role_gapRoleCBs, &maxNumBleConns);
        
        //allocate memory for index to connection handle map
        if (connHandleMap = ICall_malloc(sizeof(uint16_t) * maxNumBleConns))
        {
          // init index to connection handle map to 0's
          for (uint8_t i = 0; i < maxNumBleConns; i++)
          {
            connHandleMap[i] = INVALID_CONNHANDLE;
          }  
        }
        
        //allocate memory for per connection discovery information
        if (discInfo = ICall_malloc(sizeof(discInfo_t) * maxNumBleConns))
        {
          // init index to connection handle map to 0's
          for (uint8_t i = 0; i < maxNumBleConns; i++)
          {
            discInfo[i].charHdl = 0;
            discInfo[i].discState = BLE_DISC_STATE_IDLE;
            discInfo[i].svcEndHdl = 0;
            discInfo[i].svcStartHdl = 0;
    
          }  
        }
      }
      
      //GATT
      {
        /*---------------------SERVER------------------------*/
        // Set the GAP Characteristics
        GGS_SetParameter(GGS_DEVICE_NAME_ATT, GAP_DEVICE_NAME_LEN, attDeviceName);
        
        // Initialize GATT Server Services
        GGS_AddService(GATT_ALL_SERVICES);           // GAP
        GATTServApp_AddService(GATT_ALL_SERVICES);   // GATT attributes
        DevInfo_AddService();                        // Device Information Service
        SimpleProfile_AddService(GATT_ALL_SERVICES); // Simple GATT Profile
        
        // Setup Profile 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(&multi_role_simpleProfileCBs);
        
        /*-----------------CLIENT------------------*/
        // Initialize GATT Client
        VOID GATT_InitClient();
        
        // Register for GATT local events and ATT Responses pending for transmission
        GATT_RegisterForMsgs(selfEntity);
        
        // Register to receive incoming ATT Indications/Notifications
        GATT_RegisterForInd(selfEntity);
    
      }
      
      // Setup the GAP Bond Manager
      {
        uint8_t pairMode = GAPBOND_PAIRING_MODE_INITIATE;
        uint8_t mitm = TRUE;
        uint8_t ioCap = GAPBOND_IO_CAP_DISPLAY_ONLY;
        uint8_t bonding = TRUE;
        
        // set pairing mode
        GAPBondMgr_SetParameter(GAPBOND_PAIRING_MODE, sizeof(uint8_t), &pairMode);
        // set authentication requirements
        GAPBondMgr_SetParameter(GAPBOND_MITM_PROTECTION, sizeof(uint8_t), &mitm);
        // set i/o capabilities
        GAPBondMgr_SetParameter(GAPBOND_IO_CAPABILITIES, sizeof(uint8_t), &ioCap);
        // set bonding requirements
        GAPBondMgr_SetParameter(GAPBOND_BONDING_ENABLED, sizeof(uint8_t), &bonding);
        
        // Register and start Bond Manager
        VOID GAPBondMgr_Register(&multi_role_BondMgrCBs);  
    
      }  
    //
    //  Display_print0(dispHandle, 0, 0, "Scan ->");
    //  Display_print0(dispHandle, 0, 0, "<- Next Option");
    }
    
    /*********************************************************************
    * @fn      multi_role_taskFxn
    *
    * @brief   Application task entry point for the multi_role.
    *
    * @param   a0, a1 - not used.
    *
    * @return  None.
    */
    static void multi_role_taskFxn(UArg a0, UArg a1)
    {
      // Initialize application
      multi_role_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 & MR_CONN_EVT_END_EVT)
                {
                  // Try to retransmit pending ATT Response (if any)
                  multi_role_sendAttRsp();
                }
              }
              else
              {
                // Process inter-task message
                safeToDealloc = multi_role_processStackMsg((ICall_Hdr *)pMsg);
              }
            }
            
            if (pMsg && safeToDealloc)
            {
              ICall_freeMsg(pMsg);
            }
          }
          
          // If RTOS queue is not empty, process app message.
          while (!Queue_empty(appMsgQueue))
          {
            mrEvt_t *pMsg = (mrEvt_t *)Util_dequeueMsg(appMsgQueue);
            if (pMsg)
            {
              // Process message.
              multi_role_processAppMsg(pMsg);
              
              // Free the space from the message.
              ICall_free(pMsg);
            }
          }
        }
        if (currentCharCCCDHdlIndex < noCharCCCDHdl)
        {
          multi_role_EnableNotification( connHandle, charCCCDHdl[currentCharCCCDHdlIndex ] );
          currentCharCCCDHdlIndex ++;
    
           //Enable notification on peripheral(after a few seconds delay, let it finish connection/discovery process)
           {
             Util_startClock(&startNotiEnableClock);
           }
        }
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_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 multi_role_processStackMsg(ICall_Hdr *pMsg)
    {
      uint8_t safeToDealloc = TRUE;
      
      switch (pMsg->event)
      {
      case GATT_MSG_EVENT:
        // Process GATT message
        safeToDealloc = multi_role_processGATTMsg((gattMsgEvent_t *)pMsg);
    //    System_printf("Inside GATT_MSG_EVENT\n");
    //    System_flush();
    
        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;
        
      case GAP_MSG_EVENT:
        multi_role_processRoleEvent((gapMultiRoleEvent_t *)pMsg);
    //    System_printf("Inside GAP_MSG_EVENT\n");
    //    System_flush();
        break;        
        
      default:
        // do nothing
        break;
      }
      
      return (safeToDealloc);
    }
    
    /*********************************************************************
    * @fn      multi_role_processGATTMsg
    *
    * @brief   Process GATT messages and events.
    *
    * @return  TRUE if safe to deallocate incoming message, FALSE otherwise.
    */
    static uint8_t multi_role_processGATTMsg(gattMsgEvent_t *pMsg)
    {
    //    System_printf("Inside multi_role_processGATTMsg\n");
    //    System_flush();
      // See if GATT server was unable to transmit an ATT response
      if (pMsg->hdr.status == blePending)
      {
    //      System_printf("Inside ble pending\n");
    //      System_flush();
        // No HCI buffer was available. Let's try to retransmit the response
        // on the next connection event.
        if (HCI_EXT_ConnEventNoticeCmd(pMsg->connHandle, selfEntity,
                                       MR_CONN_EVT_END_EVT) == SUCCESS)
        {
    //              System_printf("Inside conn event notice command\n");
    //              System_flush();
          // First free any pending response
          multi_role_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, 0, 0, "FC Violated: %d", pMsg->msg.flowCtrlEvt.opcode);
    //    System_printf("Inside Flow violated\n");
    //    System_flush();
      }
      else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
      {
        // MTU size updated
    //    Display_print1(dispHandle, 0, 0, "MTU Size: %d", pMsg->msg.mtuEvt.MTU);
    //    System_printf("MTU updated\n");
    //    System_flush();
      }
    
      //messages from GATT server
      if (linkDB_NumActive() > 0)
      {
    //      System_printf("Inside linkDB_NumActive>0\n");
    //      System_flush();
        //find index from connection handle
        connIndex = multi_role_mapConnHandleToIndex(pMsg->connHandle);
    
        if ((pMsg->method == ATT_READ_RSP) ||
                (pMsg->method == ATT_HANDLE_VALUE_NOTI) ||
                ((pMsg->method == ATT_ERROR_RSP) &&
                        (pMsg->msg.errorRsp.reqOpcode == ATT_READ_REQ)))
        {
    //        System_printf("Inside Handle notification function\n");
    //        System_flush();
          if (pMsg->method == ATT_ERROR_RSP)
          {
    //        Display_print1(dispHandle, 0, 0, "Read Error %d", pMsg->msg.errorRsp.errCode);
          }
          else
          {
            // After a successful read, display the read value
    //        Display_print1(dispHandle, 0, 0, "Read rsp: %d", pMsg->msg.readRsp.pValue[0]);
            uint8_t *notificationdata = pMsg->msg.handleValueNoti.pValue;
            System_printf("Notification received :%d\n",*notificationdata);
            System_flush();
          }
    
        }
        else if ((pMsg->method == ATT_WRITE_RSP)  ||
                 ((pMsg->method == ATT_ERROR_RSP) &&
                  (pMsg->msg.errorRsp.reqOpcode == ATT_WRITE_REQ)))
        {
    //        System_printf("Inside write response\n");
    //        System_flush();
          if (pMsg->method == ATT_ERROR_RSP == ATT_ERROR_RSP)
          {
    //        Display_print1(dispHandle, 0, 0, "Write Error %d", pMsg->msg.errorRsp.errCode);
          }
          else
          {
            // After a succesful write, display the value that was written and
            // increment value
    //        Display_print1(dispHandle, 0, 0, "Write sent: %d", charVal++);
          }
        }
        else if (discInfo[connIndex].discState != BLE_DISC_STATE_IDLE)
        {
          multi_role_processGATTDiscEvent(pMsg);
    //      System_printf("Inside idle \n");
    //      System_flush();
        }
      } // else - in case a GATT message came after a connection has dropped, ignore it.
    
      // 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      multi_role_sendAttRsp
    *
    * @brief   Send a pending ATT response message.
    *
    * @param   none
    *
    * @return  none
    */
    static void multi_role_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
          multi_role_freeAttRsp(status);
        }
        else
        {
          // Continue retrying
    //      Display_print1(dispHandle, 0, 0, "Rsp send retry:", rspTxRetry);
        }
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_freeAttRsp
    *
    * @brief   Free ATT response message.
    *
    * @param   status - response transmit status
    *
    * @return  none
    */
    static void multi_role_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, 0, 0, "Rsp sent, retry: %d", rspTxRetry);
        }
        else
        {
          // Free response payload
          GATT_bm_free(&pAttRsp->msg, pAttRsp->method);
          
    //      Display_print1(dispHandle, 0, 0, "Rsp retry failed: %d", rspTxRetry);
        }
        
        // Free response message
        ICall_freeMsg(pAttRsp);
        
        // Reset our globals
        pAttRsp = NULL;
        rspTxRetry = 0;
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_processAppMsg
    *
    * @brief   Process an incoming callback from a profile.
    *
    * @param   pMsg - message to process
    *
    * @return  None.
    */
    static void multi_role_processAppMsg(mrEvt_t *pMsg)
    {
    //    mr_Application();
      switch (pMsg->event)
      {
      case MR_STATE_CHANGE_EVT:
        multi_role_processStackMsg((ICall_Hdr *)pMsg->pData);
        // Free the stack message
        ICall_freeMsg(pMsg->pData);
        break;
    
      case MR_CHAR_CHANGE_EVT:
          System_printf("Inside char change event\n");
          System_flush();
        multi_role_processCharValueChangeEvt(*(pMsg->pData));
        // Free the app data
        ICall_free(pMsg->pData);
        break;
    
      case MR_PAIRING_STATE_EVT:
        multi_role_processPairState((gapPairStateEvent_t*)pMsg->pData);
        // Free the app data
        ICall_free(pMsg->pData);
        break;
        
      case MR_PASSCODE_NEEDED_EVT:
          multi_role_processPasscode((gapPasskeyNeededEvent_t*)pMsg->pData);
          // Free the app data
          ICall_free(pMsg->pData);
          break;
    
      default:
        // Do nothing.
        break;
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_eventCB
    *
    * @brief   Central event callback function.
    *
    * @param   pEvent - pointer to event structure
    *
    * @return  TRUE if safe to deallocate event message, FALSE otherwise.
    */
    static uint8_t multi_role_eventCB(gapMultiRoleEvent_t *pEvent)
    {
      // Forward the role event to the application
      if (multi_role_enqueueMsg(MR_STATE_CHANGE_EVT, (uint8_t *)pEvent))
      {
        // App will process and free the event
        return FALSE;
      }
      
      // Caller should free the event
      return TRUE;
    }
    
    /*********************************************************************
    * @fn      multi_role_processRoleEvent
    *
    * @brief   Multi role event processing function.
    *
    * @param   pEvent - pointer to event structure
    *
    * @return  none
    */
    static void multi_role_processRoleEvent(gapMultiRoleEvent_t *pEvent)
    {
      switch (pEvent->gap.opcode)
      {
        // GAPRole started
      case GAP_DEVICE_INIT_DONE_EVENT:
        {
          //store max pdu size
          maxPduSize = pEvent->initDone.dataPktLen;
    
    //      Display_print0(dispHandle, 0, 0, "Connected to 0");
    //      Display_print0(dispHandle, 0, 0, Util_convertBdAddr2Str(pEvent->initDone.devAddr));
    //      Display_print0(dispHandle, 0, 0, "Initialized");
          System_printf("Initialized\n");
          System_flush();
          mr_doScan();
    
          //set device info characteristic
          DevInfo_SetParameter(DEVINFO_SYSTEM_ID, DEVINFO_SYSTEM_ID_LEN, pEvent->initDone.devAddr);
        }
        break;
    
        // advertising started
      case GAP_MAKE_DISCOVERABLE_DONE_EVENT:
        {
    //      Display_print0(dispHandle, 0, 0, "Advertising");
    //      System_printf("Advertising\n");
    //      System_flush();
    
        }
        break;
    
        // advertising ended
      case GAP_END_DISCOVERABLE_DONE_EVENT:
        {
          // display advertising info depending on whether there are any connections
          if (linkDB_NumActive() < maxNumBleConns)
          {
    //        Display_print0(dispHandle, 0, 0, "Ready to Advertise");
    //        System_printf("Ready to advertise\n");
    //        System_flush();
          }
          else
          {
    //        Display_print0(dispHandle, 0, 0, "Can't Adv : No links");
    //        System_printf("No links..\n");
    //        System_flush();
          }
        }
        break;
    
        // a discovered device report
      case GAP_DEVICE_INFO_EVENT:
        {
          // if filtering device discovery results based on service UUID
          if (DEFAULT_DEV_DISC_BY_SVC_UUID == TRUE)
          {
            //if the simpleGATTprofile was found
            if (multi_role_findSvcUuid(SIMPLEPROFILE_SERV_UUID,
                                       pEvent->deviceInfo.pEvtData,
                                       pEvent->deviceInfo.dataLen))
            {
              //store this device
              multi_role_addDeviceInfo(pEvent->deviceInfo.addr,
                                       pEvent->deviceInfo.addrType);
            }
          }
        }
        break;
    
        // end of discovery report
      case GAP_DEVICE_DISCOVERY_EVENT:
        {
          // discovery complete
          scanningStarted = FALSE;
    
          // if not filtering device discovery results based on service UUID
          if (DEFAULT_DEV_DISC_BY_SVC_UUID == FALSE)
          {
            // Copy all of the results
            scanRes = pEvent->discCmpl.numDevs;
            memcpy(devList, pEvent->discCmpl.pDevList,
                   (sizeof(gapDevRec_t) * scanRes));
          }
    
    //      Display_print1(dispHandle, 0, 0, "Devices Found %d", scanRes);
    //      System_printf("Devices Found:%d\n",scanRes);
    //      System_flush();
    
          // initialize scan index to last device
          scanIdx = scanRes;
          mr_doConnect();
        }
        break;
    
        // connection has been established
      case GAP_LINK_ESTABLISHED_EVENT:
        {
    //        System_printf("Inside gap established event\n");
    //        System_flush();
          // if successfully established
          if (pEvent->gap.hdr.status == SUCCESS)
          {
    //        Display_print0(dispHandle, 0, 0, "Connected!");
    //        Display_print1(dispHandle, 0, 0, "Connected to %d", linkDB_NumActive());
    //        System_printf("\nConnected to %d\n",linkDB_NumActive());
    //        System_flush();
            //update connecting state
            connectingState = 0;
            //add index-to-connHandle mapping entry
            multi_role_addMappingEntry(pEvent->linkCmpl.connectionHandle);
    
            //turn off advertising if no available links
            if (linkDB_NumActive() >= maxNumBleConns)
            {
              uint8_t advertEnabled = FALSE;
              GAPRole_SetParameter(GAPROLE_ADVERT_ENABLED, sizeof(uint8_t), &advertEnabled, NULL);
    //          Display_print0(dispHandle, 0, 0, "Can't adv: no links");
            }
    
            // Print last connected device
            Display_print0(dispHandle, 0, 0, "");
            Display_print0(dispHandle, 0, 0, Util_convertBdAddr2Str(pEvent->linkCmpl.devAddr));
    
            //start service discovery
            multi_role_startDiscovery(pEvent->linkCmpl.connectionHandle);
          }
          // if the connection was not successfully established
          else
          {
    //        Display_print0(dispHandle, 0, 0, "Connect Failed");
    //        Display_print1(dispHandle, 0, 0, "Reason: %d", pEvent->gap.hdr.status);
          }
        }
        break;
    
        // connection has been terminated
      case GAP_LINK_TERMINATED_EVENT:
        {
          //find index from connection handle
          connIndex = multi_role_mapConnHandleToIndex(pEvent->linkTerminate.connectionHandle);
          //check to prevent buffer overrun
          if (connIndex < maxNumBleConns)
          {
            //clear screen, reset discovery info, and return to main menu
            connHandleMap[connIndex] = INVALID_CONNHANDLE;
            discInfo[connIndex].charHdl = 0;
    //        Display_print1(dispHandle, 0, 0, "Connected to %d", linkDB_NumActive());
    //        Display_print0(dispHandle, 0, 0, "Disconnected!");
            // if it is possible to advertise again
            if (linkDB_NumActive() == (maxNumBleConns-1))
            {
    //          Display_print0(dispHandle, 0, 0, "Ready to Advertise");
    //          Display_print0(dispHandle, 0, 0, "Ready to Scan");
            }
            //reset discovery state
            discInfo[connIndex].discState= BLE_DISC_STATE_IDLE;
          }
        }
        break;
    
        //a parameter update has occurred
      case GAP_LINK_PARAM_UPDATE_EVENT:
        {
    //      Display_print1(dispHandle, 0, 0, "Param Update %d", pEvent->linkUpdate.status);
        }
        break;
    
      default:
        break;
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_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 multi_role_charValueChangeCB(uint8_t paramID)
    {
      uint8_t *pData;
      
      // Allocate space for the event data.
      if ((pData = ICall_malloc(sizeof(uint8_t))))
      {    
        *pData = paramID;  
        
        // Queue the event.
        multi_role_enqueueMsg(MR_CHAR_CHANGE_EVT, pData);
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_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 multi_role_processCharValueChangeEvt(uint8_t paramID)
    {
      uint8_t newValue;
      
      //print new value depending on which characteristic was updated
      switch(paramID)
      {
      case SIMPLEPROFILE_CHAR1:
        // get new value
        SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR1, &newValue);
        
    //    Display_print1(dispHandle, 0, 0, "Char 1: %d", (uint16_t)newValue);
        break;
        
      case SIMPLEPROFILE_CHAR3:
        // get new value
        SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR3, &newValue);
        
    //    Display_print1(dispHandle, 0, 0, "Char 3: %d", (uint16_t)newValue);
        break;
        
      case SIMPLEPROFILE_CHAR4:
        // get new value
        SimpleProfile_GetParameter(SIMPLEPROFILE_CHAR4, &newValue);
        System_printf("\n new value :%d",newValue);
        System_flush();
    //    Display_print1(dispHandle, 0, 0, "Char 4: %d", (uint16_t)newValue);
        break;
    
      default:
        // should not reach here!
        break;
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_enqueueMsg
    *
    * @brief   Creates a message and puts the message in RTOS queue.
    *
    * @param   event - message event.
    * @param   pData - pointer to data to be queued
    *
    * @return  None.
    */
    static uint8_t multi_role_enqueueMsg(uint16_t event, uint8_t *pData)
    {
      // allocate space for the message
      mrEvt_t *pMsg = ICall_malloc(sizeof(mrEvt_t));
      
      // if successfully allocated
      if (pMsg)
      {
        // fill up message
        pMsg->event = event;
        pMsg->pData = pData;
        
        // Enqueue the message.
        return Util_enqueueMsg(appMsgQueue, sem, (uint8*)pMsg);
      }
      
      return FALSE;
    }
    
    /*********************************************************************
    * @fn      multi_role_keyChangeHandler
    *
    * @brief   Key event handler function
    *
    * @param   a0 - ignored
    *
    * @return  none
    */
    void multi_role_keyChangeHandler(uint8 keys)
    {
      uint8_t *pData;
      
      // Allocate space for the event data.
      if ((pData = ICall_malloc(sizeof(uint8_t))))
      {    
        // store the key data
        *pData = keys;  
        
        // Queue the event.
        multi_role_enqueueMsg(MR_KEY_CHANGE_EVT, pData);
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_startDiscovery
    *
    * @brief   Start service discovery.
    *
    * @return  none
    */
    static void multi_role_startDiscovery(uint16_t connHandle)
    {
    //    System_printf("Inside multirole start discovery\n");
    //    System_flush();
      //exchange MTU request
      attExchangeMTUReq_t req;
      
      //map connection handle to index
      connIndex = multi_role_mapConnHandleToIndex(connHandle);
      
      //check to prevent buffer overrun
      if (connIndex < maxNumBleConns)
      {
        //update discovery state of this connection
        discInfo[connIndex].discState= BLE_DISC_STATE_MTU;
        // Initialize cached handles
        discInfo[connIndex].svcStartHdl = discInfo[connIndex].svcEndHdl = 0;
      }
      
      // Discover GATT Server's Rx MTU size
      req.clientRxMTU = maxPduSize - L2CAP_HDR_SIZE;
      
      // ATT MTU size should be set to the minimum of the Client Rx MTU
      // and Server Rx MTU values
      VOID GATT_ExchangeMTU(connHandle, &req, selfEntity);
    }
    
    /*********************************************************************
    * @fn      multi_role_processGATTDiscEvent
    *
    * @brief   Process GATT discovery event
    *
    * @return  none
    */
    static void multi_role_processGATTDiscEvent(gattMsgEvent_t *pMsg)
    { 
    
            //MTU update
            if (pMsg->method == ATT_MTU_UPDATED_EVENT)
            {
              // MTU size updated
              Display_print1(dispHandle, 0, 0, "MTU Size: %d", pMsg->msg.mtuEvt.MTU);
            }
            //if we've updated the MTU size
            else if (discInfo[connIndex].discState== BLE_DISC_STATE_MTU)
            {
              // MTU size response received, discover simple BLE service
              if (pMsg->method == ATT_EXCHANGE_MTU_RSP)
              {
                uint8_t uuid[ATT_BT_UUID_SIZE] = { LO_UINT16(SIMPLEPROFILE_SERV_UUID),
                                                   HI_UINT16(SIMPLEPROFILE_SERV_UUID) };
                //advance state
                discInfo[connIndex].discState= BLE_DISC_STATE_SVC;
    
                // Discovery simple BLE service
                VOID GATT_DiscPrimaryServiceByUUID(pMsg->connHandle, uuid, ATT_BT_UUID_SIZE,
                                                   selfEntity);
              }
            }
            else if (discInfo[connIndex].discState == BLE_DISC_STATE_SVC)
            {
                // Service found, store handles
                if (pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
                    pMsg->msg.findByTypeValueRsp.numInfo > 0)
                {
                    discInfo[connIndex].svcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
                    discInfo[connIndex].svcEndHdl = ATT_GRP_END_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
    
                }
    
                // If procedure complete
                if (((pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP) &&
                     (pMsg->hdr.status == bleProcedureComplete))  ||
                     (pMsg->method == ATT_ERROR_RSP))
                {
                    if (discInfo[connIndex].svcStartHdl != 0)
                    {
                        attReadByTypeReq_t req;
    
                        // Discover characteristic
                        discInfo[connIndex].discState= BLE_DISC_STATE_CHAR;
                        req.startHandle = discInfo[connIndex].svcStartHdl;
                        req.endHandle = discInfo[connIndex].svcEndHdl;
                        req.type.len = ATT_BT_UUID_SIZE;
                        req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR4_UUID);
                        req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR4_UUID);
    
                        GATT_DiscAllCharDescs(connHandle, discInfo[connIndex].svcStartHdl, discInfo[connIndex].svcEndHdl, selfEntity);
                        //send characteristic discovery request
                        VOID GATT_ReadUsingCharUUID(pMsg->connHandle, &req, selfEntity);
                    }
                }
                discInfo[connIndex].discState = BLE_DISC_STATE_CHAR;
            }
            else if (discInfo[connIndex].discState == BLE_DISC_STATE_CHAR)
            {
    //            System_printf("BLE DISC STATE CHAR\n");
    //            System_flush();
    
                // Characteristic descriptors found
                if (pMsg->method == ATT_FIND_INFO_RSP &&
                        pMsg->msg.findInfoRsp.numInfo > 0)
                {
                  uint8_t i;
                  System_printf("Characteristic descriptors found\n");
                  System_flush();
                  // For each handle/uuid pair
                  for (i = 0; i < pMsg->msg.findInfoRsp.numInfo; i++)
                  {
                    if(pMsg->msg.findInfoRsp.format == ATT_HANDLE_BT_UUID_TYPE)
                    {
                      // Look for CCCD
                      if (ATT_BT_PAIR_UUID(pMsg->msg.findInfoRsp.pInfo, i) ==
                              GATT_CLIENT_CHAR_CFG_UUID)
                      {
    
                        // CCCD found
                        if (noCharCCCDHdl < MAX_CCCD_HANDLES){
                            charCCCDHdl[noCharCCCDHdl] = ATT_BT_PAIR_HANDLE(pMsg->msg.findInfoRsp.pInfo, i);
                            noCharCCCDHdl++;
    //                        System_printf("CCCD Found\n");
    //                        System_flush();
                        }
                      }
                    }
                  }
                }
                else if ((pMsg->method == ATT_READ_BY_TYPE_RSP) &&
                        (pMsg->msg.readByTypeRsp.numPairs > 0))
                    {
                    discInfo[connIndex].charHdl = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[0],
                                             pMsg->msg.readByTypeRsp.pDataList[1]);
                      System_printf("CCCD Found: %d\n",discInfo[connIndex].charHdl);
                      System_flush();
    //                  procedureInProgress = FALSE;
                    }
    
                else{
                    System_printf("CCCD not found");
                    System_flush();
                }
                // If procedure complete
                 if ((pMsg->method == ATT_FIND_INFO_RSP  &&
                      pMsg->hdr.status == bleProcedureComplete) ||
                     (pMsg->method == ATT_ERROR_RSP))
                 {
                     //Enable notification on peripheral(after a few seconds delay, let it finish connection/discovery process)
                     {
                       Util_startClock(&startNotiEnableClock);
                     }
    
    //                 procedureInProgress = FALSE;
    //                 discInfo[connIndex].discState = BLE_DISC_STATE_IDLE;
                 }
            }
    }
    /*********************************************************************
    * @fn      multi_role_findSvcUuid
    *
    * @brief   Find a given UUID in an advertiser's service UUID list.
    *
    * @return  TRUE if service UUID found
    */
    static bool multi_role_findSvcUuid(uint16_t uuid, uint8_t *pData,
                                       uint8_t dataLen)
    {
      uint8_t adLen;
      uint8_t adType;
      uint8_t *pEnd;
      
      //find the end of data
      pEnd = pData + dataLen - 1;
      
      // While end of data not reached
      while (pData < pEnd)
      {
        // Get length of next AD item
        adLen = *pData++;
        if (adLen > 0)
        {
          adType = *pData;
          
          // If AD type is for 16-bit service UUID
          if ((adType == GAP_ADTYPE_16BIT_MORE) || 
              (adType == GAP_ADTYPE_16BIT_COMPLETE))
          {
            pData++;
            adLen--;
            
            // For each UUID in list
            while (adLen >= 2 && pData < pEnd)
            {
              // Check for match
              if ((pData[0] == LO_UINT16(uuid)) && (pData[1] == HI_UINT16(uuid)))
              {
                // Match found
                return TRUE;
              }
              
              // Go to next
              pData += 2;
              adLen -= 2;
            }
            
            // Handle possible erroneous extra byte in UUID list
            if (adLen == 1)
            {
              pData++;
            }
          }
          else
          {
            // Go to next item
            pData += adLen;
          }
        }
      }
      
      // Match not found
      return FALSE;
    }
    
    /*********************************************************************
    * @fn      multi_role_addDeviceInfo
    *
    * @brief   Add a device to the device discovery result list
    *
    * @return  none
    */
    static void multi_role_addDeviceInfo(uint8_t *pAddr, uint8_t addrType)
    {
      uint8_t i;
      
      // If result count not at max
      if (scanRes < DEFAULT_MAX_SCAN_RES)
      {
        // Check if device is already in scan results
        for (i = 0; i < scanRes; i++)
        {
          if (memcmp(pAddr, devList[i].addr , B_ADDR_LEN) == 0)
          {
            return;
          }
        }
        
        // Add addr to scan result list
        memcpy(devList[scanRes].addr, pAddr, B_ADDR_LEN);
        devList[scanRes].addrType = addrType;
        
        // Increment scan result count
        scanRes++;
      }
    }
    
    /*********************************************************************
    * @fn      gapRoleFindIndex
    *
    * @brief   to translate connection handle to index
    *
    * @param   connHandle (connection handle)
    *
    * @return  none
    */
    static uint16_t multi_role_mapConnHandleToIndex(uint16_t connHandle)
    {
      uint16_t index;
      //loop through connection
      for (index = 0; index < maxNumBleConns; index ++)
      {
        //if matching connection handle found
        if (connHandleMap[index] == connHandle)
        {
          return index;
        }
      }
      //not found if we got here
      return INVALID_CONNHANDLE;
    }
    
    /************************************************************************
    * @fn      multi_role_pairStateCB
    *
    * @brief   Pairing state callback.
    *
    * @return  none
    */
    static void multi_role_pairStateCB(uint16_t connHandle, uint8_t state,
                                       uint8_t status)
    {
      gapPairStateEvent_t *pData;
      
      // Allocate space for the passcode event.
      if ((pData = ICall_malloc(sizeof(gapPairStateEvent_t))))
      {    
        pData->connectionHandle = connHandle;    
        pData->state = state;
        pData->status = status;
        
        // Enqueue the event.
        multi_role_enqueueMsg(MR_PAIRING_STATE_EVT, (uint8_t *) pData);
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_passcodeCB
    *
    * @brief   Passcode callback.
    *
    * @return  none
    */
    static void multi_role_passcodeCB(uint8_t *deviceAddr, uint16_t connHandle,
                                      uint8_t uiInputs, uint8_t uiOutputs, uint32_t numComparison)
    {
      gapPasskeyNeededEvent_t *pData;
      
      // Allocate space for the passcode event.
      if ((pData = ICall_malloc(sizeof(gapPasskeyNeededEvent_t))))
      {
        memcpy(pData->deviceAddr, deviceAddr, B_ADDR_LEN);
        pData->connectionHandle = connHandle;    
        pData->uiInputs = uiInputs;
        pData->uiOutputs = uiOutputs;
        pData->numComparison = numComparison;
        
        // Enqueue the event.
        multi_role_enqueueMsg(MR_PASSCODE_NEEDED_EVT, (uint8_t *) pData);
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_processPairState
    *
    * @brief   Process the new paring state.
    *
    * @return  none
    */
    static void multi_role_processPairState(gapPairStateEvent_t* pairingEvent)
    {
      //if we've started pairing
      if (pairingEvent->state == GAPBOND_PAIRING_STATE_STARTED)
      {
    //    Display_print1(dispHandle, 0, 0,"Cxn %d pairing started", pairingEvent->connectionHandle);
      }
      //if pairing is finished
      else if (pairingEvent->state == GAPBOND_PAIRING_STATE_COMPLETE)
      {
        if (pairingEvent->status == SUCCESS)
        {
    //      Display_print1(dispHandle, 0, 0,"Cxn %d pairing success", pairingEvent->connectionHandle);
        }
        else
        {
    //      Display_print2(dispHandle, 0, 0, "Cxn %d pairing fail: %d", pairingEvent->connectionHandle, pairingEvent->status);
        }
      }
      //if a bond has happened
      else if (pairingEvent->state == GAPBOND_PAIRING_STATE_BONDED)
      {
        if (pairingEvent->status == SUCCESS)
        {
    //      Display_print1(dispHandle, 0, 0, "Cxn %d bonding success", pairingEvent->connectionHandle);
        }
      }
      //if a bond has been saved
      else if (pairingEvent->state == GAPBOND_PAIRING_STATE_BOND_SAVED)
      {
        if (pairingEvent->status == SUCCESS)
        {
    //      Display_print1(dispHandle, 0, 0, "Cxn %d bond save success", pairingEvent->connectionHandle);
        }
        else
        {
    //      Display_print2(dispHandle, 0, 0, "Cxn %d bond save failed: %d", pairingEvent->connectionHandle, pairingEvent->status);
        }
      }
    }
    
    /*********************************************************************
    * @fn      multi_role_processPasscode
    *
    * @brief   Process the Passcode request.
    *
    * @return  none
    */
    static void multi_role_processPasscode(gapPasskeyNeededEvent_t *pData)
    {
      //use static passcode
      uint32_t passcode = 123456;
    //  Display_print1(dispHandle, 0, 0, "Passcode: %d", passcode);
      //send passcode to GAPBondMgr
      GAPBondMgr_PasscodeRsp(pData->connectionHandle, SUCCESS, passcode);
    }
    
    /*********************************************************************
    * @fn      gapRoleFindIndex
    *
    * @brief   to translate connection handle to index
    *
    * @param   connHandle (connection handle)
    *
    * @return  none
    */
    static uint8_t multi_role_addMappingEntry(uint16_t connHandle)
    {
      uint16_t index;
      // loop though connections
      for (index = 0; index < maxNumBleConns; index ++)
      {
        //if there is an open connection
        if (connHandleMap[index] == INVALID_CONNHANDLE)
        {
          //store mapping
          connHandleMap[index] = connHandle;
          return index;
        }
      }
      //no room if we get here
      return bleNoResources;
    }        
    
    /*********************************************************************
    *********************************************************************/
    static void multi_role_EnableNotification( uint16 connHandle, uint16 attrHandle )
    {
        attWriteReq_t req;
    //    System_printf("Inside enable notification\n");
    //    System_flush();
        req.pValue = GATT_bm_alloc( connHandle, ATT_WRITE_REQ, 2, NULL );
        if ( req.pValue != NULL )
        {
          uint8 notificationsOn[] = {0x01, 0x00};
      //    attAttribute_t
          req.handle = attrHandle;
    
          req.len = 2;
          memcpy(req.pValue, notificationsOn, 2);
    
          req.sig = FALSE;
          req.cmd = TRUE;
    
          if ( GATT_WriteNoRsp( connHandle, &req ) != SUCCESS )
          {
            GATT_bm_free( (gattMsg_t *)&req, ATT_WRITE_REQ );
          }
    
        }
    }
    
    static void multi_role_startNotiHandler(UArg arg)
    {
      // Store the event.
      events |= SBC_START_NOTIFICATION;
    //  System_printf("Inside notification handler\n");
    //  System_flush();
      // Wake up the application.
      Semaphore_post(sem);
    }
    
    /*********************************************************************
    * @fn      mr_doScan
    *
    * @brief   Respond to user input to start scanning
    *
    * @param   index - not used
    *
    * @return  TRUE since there is no callback to use this value
    */
    bool mr_doScan(void)
    {
        //if we can connect to another device
        if (linkDB_NumActive() < maxNumBleConns)
        {
          //if we're not already scanning
          if (!scanningStarted)
          {
            // set scanning started flag
            scanningStarted = TRUE;
            // reset number of scanned devices
            scanRes = 0;
    
            Display_print0(dispHandle, 0, 0, "Discovering...");
            System_printf("Discovering.....\n");
            System_flush();
            Display_print0(dispHandle, 0, 0, "");
    
            //start scanning
            GAPRole_StartDiscovery(DEFAULT_DISCOVERY_MODE,
                                   DEFAULT_DISCOVERY_ACTIVE_SCAN,
                                   DEFAULT_DISCOVERY_WHITE_LIST);
    
          }
          // we're already scanning...so cancel
          else
          {
            // cancel scanning
            GAPRole_CancelDiscovery();
            // clear scanning started flag
            scanningStarted = FALSE;
          }
        }
        else // can't add more links at this time
        {
    //      System_printf("Can't scan: No links");
    //      System_flush();
        }
      return TRUE;
    }
    
    /*********************************************************************
    * @fn      mr_doConnect
    *
    * @brief   Respond to user input to form a connection
    *
    * @param   index - index as selected from the mrMenuConnect
    *
    * @return  TRUE since there is no callback to use this value
    */
    bool mr_doConnect(void)
    {
        System_printf("Inside do connect\n");
        System_flush();
        uint8_t ret;
            // If already connecting...cancel
            if (connectingState == TRUE){
                // Cancel connection request
                GAPRole_TerminateConnection(GAP_CONNHANDLE_INIT);
                // Clear connecting flag
                connectingState = FALSE;
              }
            // If attempting to connect
            else{
                uint8_t address[B_ADDR_LEN] = {0xC8,0x72,0xFF,0x48,0xB4,0xB0};
    
                for(scanIdx=0;scanIdx<DEFAULT_MAX_SCAN_RES;scanIdx++){
                    ret = GAPRole_EstablishLink(DEFAULT_LINK_HIGH_DUTY_CYCLE,
                                                DEFAULT_LINK_WHITE_LIST,
                                                0, address);
                }
    
              // Set connecting state flag
                  connectingState = TRUE;
            }
      return ret;
    }
    
    

    regards,

    jaydipsinh

  • Hi Jaydipsinh,

    Line 57 of the snippet isn't exactly the right place to call GATT_DiscAllCharDescs().
    With the code as is, you will actually find look for all char descriptors between the service start and end handle.
    Instead, you would like to do this between the start and end handle for char 4 only so that you can find CCCDs related to char 4.


    Instead, I would recommend changing the processing of discInfo[connIndex].discState == BLE_DISC_STATE_CHAR to parse the handle value pairs received to get the start and end handle of the characteristic itself (char 4).

    After you have the start and end handle of char 4, then you can call GATT_DiscAllCharDescs().
    I would make a new state for processing CCCDs and look for the CCCD info there.


    Also, it is important to check the return value of all of the GATT procedures as they are synchronous by nature and starting a second procedure when one is already in progress will result in blePending. This is another thing that is probably happening when you call GATT_DiscAllCharDescs right after GATT_ReadUsingCharUUID().


    In summary, you would need to wait until characteristic discovery is complete before starting descriptor discovery. Additionally you would want to do descriptor discovery on only the start and end handle of a given characteristic to prevent getting all CCCDs in the service back.