CC2340R5: Central GATT find service handle range

Part Number: CC2340R5

Hello,

I was taking a look at the basic ble GATT client example and I think I understand the basic flow of it, but since the GATT table handles are hard coded, I'm trying to add code to make application a bit more flexible.

Looking through the BLE stack documentation, there are several possible APIs to go and query the GATT server for various data in the GATT table. However, there's not a good guide in our documentation on how to use the APIs to find a service handle by UUID, and then discover the associated characteristics within.

What's the recommended way to find both a service and characteristics within a service by UUID and return a handle for each?

I found GATT_DiscPrimaryServiceByUUID(), but this only gets me as far as identifying handle of the main service UUID. 

Regards,

Munan

  • Also I see there's an API called GATT_DiscCharsByUUID()

    https://dev.ti.com/tirex/explore/content/simplelink_lowpower_f3_sdk_7_40_00_64/docs/ble5stack/ble_user_guide/doxygen/ble/html/group___a_t_t___g_a_t_t.html#ga747eaf3fb0358c4e05171b40d09c1906

    But if you look at the arguments in the function call, I don't see how to specify an actual UUID here. In the arguments this function takes the attReadByTypeReq_t struct, but this only specifies a type of either 2 or 16 bit UUID octet.

    So how do you specify which UUID you're looking for? Or does the UUID get included in the ATT_FIND_BY_TYPE_VALUE_RSP somehow and the application is supposed to check there to see if there is a match? But in the description of GATT_DiscCharsByUUID() this seems more like an API to specify and look for a specific characteristic UUID since there is already another API to discover all characteristics within a range.

    Munan

  • Hi Munan,

    The GATT_DiscCharsByUUID() procedure leads the application to receive a ATT_READ_BY_TYPE_RSP event with a attReadByTypeRsp_t structure containing the list of UUIDs and handles.

    I hope this will help,

    Best regards,

  • Hi again,

    Please see the pseudo code in the attached files. They should show a bit more how to use GATT client>

    app_data.c
    /******************************************************************************
    
    @file  app_data.c
    
    @brief This file contains the application data functionality
    
    Group: WCS, BTS
    Target Device: cc23xx
    
    ******************************************************************************
    
     Copyright (c) 2022-2023, 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.
    
    ******************************************************************************
    
    
    *****************************************************************************/
    
    //*****************************************************************************
    //! Includes
    //*****************************************************************************
    #include <string.h>
    #include <ti/bleapp/ble_app_util/inc/bleapputil_api.h>
    #include <ti/bleapp/menu_module/menu_module.h>
    #include <app_main.h>
    
    //*****************************************************************************
    //! Defines
    //*****************************************************************************
    
    //*****************************************************************************
    //! Globals
    //*****************************************************************************
    
    static void GATT_EventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData);
    
    // Events handlers struct, contains the handlers and event masks
    // of the application data module
    BLEAppUtil_EventHandler_t dataGATTHandler =
    {
        .handlerType    = BLEAPPUTIL_GATT_TYPE,
        .pEventHandler  = GATT_EventHandler,
        .eventMask      = BLEAPPUTIL_ATT_FLOW_CTRL_VIOLATED_EVENT |
                          BLEAPPUTIL_ATT_MTU_UPDATED_EVENT |
                          BLEAPPUTIL_ATT_READ_RSP |
                          BLEAPPUTIL_ATT_WRITE_RSP |
                          BLEAPPUTIL_ATT_EXCHANGE_MTU_RSP|
                          BLEAPPUTIL_ATT_ERROR_RSP |
                          BLEAPPUTIL_ATT_HANDLE_VALUE_NOTI |
                          BLEAPPUTIL_ATT_HANDLE_VALUE_IND |
                          BLEAPPUTIL_ATT_READ_BY_GRP_TYPE_RSP |
                          BLEAPPUTIL_ATT_READ_BY_TYPE_RSP
    };
    
    //*****************************************************************************
    //! Functions
    //*****************************************************************************
    
    /*********************************************************************
     * @fn      GATT_EventHandler
     *
     * @brief   The purpose of this function is to handle GATT events
     *          that rise from the GATT and were registered in
     *          @ref BLEAppUtil_RegisterGAPEvent
     *
     * @param   event - message event.
     * @param   pMsgData - pointer to message data.
     *
     * @return  none
     */
    static void GATT_EventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData)
    {
      gattMsgEvent_t *gattMsg = ( gattMsgEvent_t * )pMsgData;
      switch ( gattMsg->method )
      {
      case ATT_FLOW_CTRL_VIOLATED_EVENT:
      {
          MenuModule_printf(APP_MENU_PROFILE_STATUS_LINE, 0, "GATT status: ATT flow control is violated");
      }
      break;
    
      case ATT_MTU_UPDATED_EVENT:
      {
          MenuModule_printf(APP_MENU_PROFILE_STATUS_LINE, 0, "GATT status: ATT MTU update to %d",
                            (( attMtuUpdatedEvt_t * )pMsgData)->MTU);
      }
      break;
    
      case ATT_READ_BY_GRP_TYPE_RSP:
      {
          attReadByGrpTypeRsp_t *rsp = ( attReadByGrpTypeRsp_t * )&(gattMsg->msg.readByGrpTypeRsp);
    
          MenuModule_printf(APP_MENU_PROFILE_STATUS_LINE, 0, "GATT status: ATT_READ_BY_GRP_TYPE_RSP");
    
          int i;
          for(i = 0; i<rsp->numGrps; i++)
          {
              uint16 attributeHandle = BUILD_UINT16( rsp->pDataList[i * rsp->len + 0],
                                                     rsp->pDataList[i * rsp->len + 1] );
              uint16 endGroupHandle  = BUILD_UINT16( rsp->pDataList[i * rsp->len + 2],
                                                     rsp->pDataList[i * rsp->len + 3] );
              uint8 *pServiceUUID = &(rsp->pDataList[i * rsp->len + 4]);
    
              uint8 uuidLength = ((rsp->len == ATT_BT_UUID_SIZE)? ATT_BT_UUID_SIZE : ATT_UUID_SIZE);
    
              // Check if the service is needed (that the service UUID matches the one expected)
              /* CustomProfile_checkIfGATTServiceIsNeeded(uuidLength, uint8 *pUuid)
               * {
               *    if (ATT_UUID_SIZE == uuidLength)
               *    {
               *        if (!memcmp( pUuid, CUSTOM_PROFILE_UUID, ATT_UUID_SIZE)
               *        {
               *            return SUCCESS;
               *        }
               *    }
               *    return FAILURE;
               * }
               *
               *
               */
              // store the service handles (the connection handle, the service start and service end)
              /*
               * typedef struct _customService
               * {
               *    connHandle;
               *    serviceHandleStart;
               *    serviceHandleEnd;
               *    discStage;
               *
               *
               *    charHandle1;
               *    charHandle2;
               *        .
               *        .
               *        .
               *    charHandleN;
               *
               * } CustomGattProfile;
               *
               * CustomProfile_storeServiceHandles(connHandle, serviceHandleStart, serviceHandleEnd)
               * {
               *    CustomGattProfile.connHandle = connHandle;
               *    CustomGattProfile.serviceHandleStart = serviceHandleStart;
               *    CustomGattProfile.serviceHandleEnd = serviceHandleEnd;
               * }
               *
               *
               */
    
              // initiate characteristic discovery. (calling GATT_DiscCharByUUID with the start and end service handles)
              /* attReadByTypeReq_t req;
               * CustomProfile_initiateCharDiscovery(connHandle)
               * {
               *    CustomGattProfile.discStage = 0;
               *    req.startHandle = CustomGattProfile.serviceHandleStart;
               *    req.endHandle = CustomGattProfile.serviceHandleEnd;
               *    req.type.len = ATT_UUID_SIZE;
               *    memcpy(req.type.uuid, CHARACTERISTICS_NEEDED[CustomGattProfile.discStage], ATT_UUID_SIZE);
               *
               *    status = GATT_DiscCharsByUUID(connHandle, &req, BLEAppUtil_getSelfEntity());
               * }
               *
               */
    
    
    
              /*
              if(CustomProfile_checkIfGATTServiceIsNeeded(uuidLength, pServiceUUID) == SUCCESS)
              {
                  CustomProfile_storeServiceHandles(gattMsg->connHandle, attributeHandle, endGroupHandle);
    
                  CustomProfile_initiateCharDiscovery(gattMsg->connHandle);
              }
              */
    
          }
    
      }
      break;
    
      case ATT_READ_BY_TYPE_RSP:
      {
          attReadByTypeRsp_t *rsp = ( attReadByTypeRsp_t * )&(gattMsg->msg.readByTypeRsp);
    
          MenuModule_printf(APP_MENU_PROFILE_STATUS_LINE, 0, "GATT status: ATT_READ_BY_TYPE_RSP");
    
          int i;
          for(i = 0; i<rsp->numPairs; i++)
          {
              volatile uint16 attributeHandle = BUILD_UINT16( rsp->pDataList[i * rsp->len + 0],
                                                     rsp->pDataList[i * rsp->len + 1] );
              volatile uint16 uuid   = BUILD_UINT16( rsp->pDataList[i * rsp->len + 2],
                                                     rsp->pDataList[i * rsp->len + 3] );
    
              // Check if the handle belongs (i.e. inbetween the expected service handle start and end)
              /* CustomProfile_checkIfCharHandleBelongs(connHandle, attributeHandle)
               *{
               *    if (attributeHandle >= CustomGattProfile.serviceHandleStart &&
               *        attributeHandle <= CustomGattProfile.serviceHandleEnd)
               *        return TRUE;
               *    return FALSE;
               *
               *}
               */
    
              /*
               * CustomProfile_charDiscovery(connHandle)
               * {
               *    req.startHandle = CustomGattProfile.serviceHandleStart;
               *    req.endHandle = CustomGattProfile.serviceHandleEnd;
               *    req.type.len = ATT_UUID_SIZE;
               *    memcpy(req.type.uuid, CHARACTERISTICS_NEEDED[CustomGattProfile.discStage], ATT_UUID_SIZE);
               *
               *    status = GATT_DiscCharsByUUID(connHandle, &req, BLEAppUtil_getSelfEntity());
               * }
               */
    
              // Store the characteristic handle (start and end)
              /* This can be done any sort of way.
               *
               * CustomProfile_storeCharHandle(connHandle, attributeHandle)
               * {
               *   switch(CustomGattProfile.discStage)
               *   {
               *      case 0:
               *      {
               *          CustomGattProfile.charHandle1 = charHandle;
               *          CustomGattProfile.discStage += 1;
               *          CustomProfile_charDiscovery(connHandle);
               *
               *          break;
               *      }
               *
               *        .
               *        .
               *        .
               *
               *      case N:
               *      {
               *         .
               *         .
               *         .
               *      }
               *   }
               *
               */
    
    
              /*
              if(CustomProfile_checkIfCharHandleBelongs(gattMsg->connHandle, attributeHandle) == SUCCESS)
              {
                  CustomProfile_storeCharHandle(gattMsg->connHandle, attributeHandle);
              }
              */
          }
      }
      break;
    
      case  ATT_HANDLE_VALUE_IND:
      case  ATT_HANDLE_VALUE_NOTI:
      {
          break;
      }
    
      case ATT_READ_RSP:
      {
    
          MenuModule_printf(APP_MENU_CONN_EVENT, 0, "Read rsp = 0x%02x",
                            gattMsg->msg.readRsp.pValue[0]);
    
          break;
      }
      case ATT_WRITE_RSP:
      {
          break;
      }
      case ATT_EXCHANGE_MTU_RSP:
      {
          MenuModule_printf(APP_MENU_CONN_EVENT, 0, "MTU max size client = %d MTU max size server = %d",
                            gattMsg->msg.exchangeMTUReq.clientRxMTU, gattMsg->msg.exchangeMTURsp.serverRxMTU);
          break;
      }
    
    
      case ATT_ERROR_RSP:
      {
          attErrorRsp_t  *pReq = (attErrorRsp_t  *)pMsgData;
    
          MenuModule_printf(APP_MENU_CONN_EVENT, 0, "Error %d",
                            pReq->errCode);
          break;
    
      }
    
        default:
          break;
      }
    }
    
    /*********************************************************************
     * @fn      Data_start
     *
     * @brief   This function is called after stack initialization,
     *          the purpose of this function is to initialize and
     *          register the specific events handlers of the data
     *          application module
     *
     * @return  SUCCESS, errorInfo
     */
    bStatus_t Data_start( void )
    {
      bStatus_t status = SUCCESS;
    
      // Register the handlers
      status = BLEAppUtil_registerEventHandler( &dataGATTHandler );
    
      // Return status value
      return( status );
    }
    
    app_connection.c
    /******************************************************************************
    
    @file  app_connection.c
    
    @brief This example file demonstrates how to activate the central role with
    the help of BLEAppUtil APIs.
    
    Group: WCS, BTS
    Target Device: cc23xx
    
    ******************************************************************************
    
     Copyright (c) 2022-2023, 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.
    
    ******************************************************************************
    
    
    *****************************************************************************/
    #if ( HOST_CONFIG & ( CENTRAL_CFG | PERIPHERAL_CFG ) )
    
    //*****************************************************************************
    //! Includes
    //*****************************************************************************
    #include <string.h>
    #include <stdarg.h>
    
    #include "ti_ble_config.h"
    #include <ti/bleapp/ble_app_util/inc/bleapputil_api.h>
    #include <ti/bleapp/menu_module/menu_module.h>
    #include <app_main.h>
    
    //*****************************************************************************
    //! Prototypes
    //*****************************************************************************
    void Connection_ConnEventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData);
    void Connection_HciGAPEventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData);
    
    static uint8_t Connection_addConnInfo(uint16_t connHandle, uint8_t *pAddr);
    static uint8_t Connection_removeConnInfo(uint16_t connHandle);
    
    //*****************************************************************************
    //! Globals
    //*****************************************************************************
    
    // Events handlers struct, contains the handlers and event masks
    // of the application central role module
    BLEAppUtil_EventHandler_t connectionConnHandler =
    {
        .handlerType    = BLEAPPUTIL_GAP_CONN_TYPE,
        .pEventHandler  = Connection_ConnEventHandler,
        .eventMask      = BLEAPPUTIL_LINK_ESTABLISHED_EVENT |
                          BLEAPPUTIL_LINK_TERMINATED_EVENT |
                          BLEAPPUTIL_LINK_PARAM_UPDATE_EVENT |
                          BLEAPPUTIL_LINK_PARAM_UPDATE_REQ_EVENT
    };
    
    BLEAppUtil_EventHandler_t connectionHciGAPHandler =
    {
        .handlerType    = BLEAPPUTIL_HCI_GAP_TYPE,
        .pEventHandler  = Connection_HciGAPEventHandler,
        .eventMask      = BLEAPPUTIL_HCI_COMMAND_STATUS_EVENT_CODE |
                          BLEAPPUTIL_HCI_LE_EVENT_CODE
    };
    
    // Holds the connection handles
    static App_connInfo connectionConnList[MAX_NUM_BLE_CONNS];
    
    //*****************************************************************************
    //! Functions
    //*****************************************************************************
    
    
    
    
    /*********************************************************************
     * @fn      Connection_ConnEventHandler
     *
     * @brief   The purpose of this function is to handle connection related
     *          events that rise from the GAP and were registered in
     *          @ref BLEAppUtil_registerEventHandler
     *
     * @param   event - message event.
     * @param   pMsgData - pointer to message data.
     *
     * @return  none
     */
    void Connection_ConnEventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData)
    {
        bStatus_t status;
        switch(event)
        {
            case BLEAPPUTIL_LINK_ESTABLISHED_EVENT:
            {
                gapEstLinkReqEvent_t *gapEstMsg = (gapEstLinkReqEvent_t *)pMsgData;
    
                // Add the connection to the connected device list
                Connection_addConnInfo(gapEstMsg->connectionHandle, gapEstMsg->devAddr);
    
                /*! Print the peer address and connection handle number */
                MenuModule_printf(APP_MENU_CONN_EVENT, 0, "Conn status: Established - "
                                  "Connected to " MENU_MODULE_COLOR_YELLOW "%s " MENU_MODULE_COLOR_RESET
                                  "connectionHandle = " MENU_MODULE_COLOR_YELLOW "%d" MENU_MODULE_COLOR_RESET,
                                  BLEAppUtil_convertBdAddr2Str(gapEstMsg->devAddr), gapEstMsg->connectionHandle);
    
                /*! Print the number of current connections */
                MenuModule_printf(APP_MENU_NUM_CONNS, 0, "Connections number: "
                                  MENU_MODULE_COLOR_YELLOW "%d " MENU_MODULE_COLOR_RESET,
                                  linkDB_NumActive());
    
    
                // Discover all primary services
                status = GATT_DiscAllPrimaryServices(gapEstMsg->connectionHandle, BLEAppUtil_getSelfEntity());
                if (status == SUCCESS)
                {
                    MenuModule_printf(APP_MENU_NUM_CONNS, 0, "Request Sent");
                }
    
                break;
            }
    
            case BLEAPPUTIL_LINK_TERMINATED_EVENT:
            {
                gapTerminateLinkEvent_t *gapTermMsg = (gapTerminateLinkEvent_t *)pMsgData;
    
                // Remove the connection from the conneted device list
                Connection_removeConnInfo(gapTermMsg->connectionHandle);
    
                /*! Print the peer address and connection handle number */
                MenuModule_printf(APP_MENU_CONN_EVENT, 0, "Conn status: Terminated - "
                                  "connectionHandle = " MENU_MODULE_COLOR_YELLOW "%d " MENU_MODULE_COLOR_RESET
                                  "reason = " MENU_MODULE_COLOR_YELLOW "%d" MENU_MODULE_COLOR_RESET,
                                  gapTermMsg->connectionHandle, gapTermMsg->reason);
    
                /*! Print the number of current connections */
                MenuModule_printf(APP_MENU_NUM_CONNS, 0, "Connections number: "
                                  MENU_MODULE_COLOR_YELLOW "%d " MENU_MODULE_COLOR_RESET,
                                  linkDB_NumActive());
    
                break;
            }
    
            case BLEAPPUTIL_LINK_PARAM_UPDATE_REQ_EVENT:
            {
                gapUpdateLinkParamReqEvent_t *pReq = (gapUpdateLinkParamReqEvent_t *)pMsgData;
    
                // Only accept connection intervals with slave latency of 0
                // This is just an example of how the application can send a response
                if(pReq->req.connLatency == 0)
                {
                    BLEAppUtil_paramUpdateRsp(pReq,TRUE);
                }
                else
                {
                    BLEAppUtil_paramUpdateRsp(pReq,FALSE);
                }
    
                break;
            }
    
            case BLEAPPUTIL_LINK_PARAM_UPDATE_EVENT:
            {
                gapLinkUpdateEvent_t *pPkt = (gapLinkUpdateEvent_t *)pMsgData;
    
                // Get the address from the connection handle
                linkDBInfo_t linkInfo;
                if (linkDB_GetInfo(pPkt->connectionHandle, &linkInfo) ==  SUCCESS)
                {
                  // The status HCI_ERROR_CODE_PARAM_OUT_OF_MANDATORY_RANGE indicates that connection params did not change but the req and rsp still transpire
                  if(pPkt->status == SUCCESS)
                  {
                      MenuModule_printf(APP_MENU_CONN_EVENT, 0, "Conn status: Params update - "
                                        "connectionHandle = " MENU_MODULE_COLOR_YELLOW "%d " MENU_MODULE_COLOR_RESET,
                                        pPkt->connectionHandle);
                  }
                  else
                  {
                      MenuModule_printf(APP_MENU_CONN_EVENT, 0, "Conn status: Params update failed - "
                                        MENU_MODULE_COLOR_YELLOW "0x%x " MENU_MODULE_COLOR_RESET
                                        "connectionHandle = " MENU_MODULE_COLOR_YELLOW "%d " MENU_MODULE_COLOR_RESET,
                                        pPkt->opcode, pPkt->connectionHandle);
                  }
                }
    
                break;
            }
    
            default:
            {
                break;
            }
        }
    }
    
    /*********************************************************************
     * @fn      Connection_HciGAPEventHandler
     *
     * @brief   The purpose of this function is to handle HCI GAP events
     *          that rise from the HCI and were registered in
     *          @ref BLEAppUtil_registerEventHandler
     *
     * @param   event - message event.
     * @param   pMsgData - pointer to message data.
     *
     * @return  none
     */
    void Connection_HciGAPEventHandler(uint32 event, BLEAppUtil_msgHdr_t *pMsgData)
    {
    
        switch (event)
        {
            case BLEAPPUTIL_HCI_COMMAND_STATUS_EVENT_CODE:
            {
                hciEvt_CommandStatus_t *pHciMsg = (hciEvt_CommandStatus_t *)pMsgData;
                switch ( event )
                {
                  case HCI_LE_SET_PHY:
                  {
                      if (pHciMsg->cmdStatus ==
                          HCI_ERROR_CODE_UNSUPPORTED_REMOTE_FEATURE)
                      {
                          MenuModule_printf(APP_MENU_CONN_EVENT, 0, "Conn status: Phy update - failure, peer does not support this");
                      }
                      else
                      {
                          MenuModule_printf(APP_MENU_CONN_EVENT, 0, "Conn status: Phy update - "
                                            MENU_MODULE_COLOR_YELLOW "0x%02x" MENU_MODULE_COLOR_RESET,
                                            pHciMsg->cmdStatus);
                      }
                      break;
                  }
    
    
                  default:
                  {
                      break;
                  }
                  break;
                }
            }
    
            case BLEAPPUTIL_HCI_LE_EVENT_CODE:
            {
                hciEvt_BLEPhyUpdateComplete_t *pPUC = (hciEvt_BLEPhyUpdateComplete_t*) pMsgData;
    
                if (pPUC->BLEEventCode == HCI_BLE_PHY_UPDATE_COMPLETE_EVENT)
                {
                  if (pPUC->status != SUCCESS)
                  {
                      MenuModule_printf(APP_MENU_CONN_EVENT, 0, "Conn status: Phy update failure - connHandle = %d",
                                        pPUC->connHandle);
                  }
                  else
                  {
    #if !defined(Display_DISABLE_ALL)
                      char * currPhy =
                              (pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_1M) ? "1 Mbps" :
                              (pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_2M) ? "2 Mbps" :
                              (pPUC->rxPhy == PHY_UPDATE_COMPLETE_EVENT_CODED) ? "CODED" : "Unexpected PHY Value";
                      MenuModule_printf(APP_MENU_CONN_EVENT, 0, "Conn status: Phy update - connHandle = %d PHY = %s",
                                        pPUC->connHandle, currPhy);
    #endif // #if !defined(Display_DISABLE_ALL)
                  }
                }
    
                break;
            }
    
            default:
            {
                break;
            }
    
        }
    }
    
    /*********************************************************************
     * @fn      Connection_addConnInfo
     *
     * @brief   Add a device to the connected device list
     *
     * @return  index of the connected device list entry where the new connection
     *          info is put in.
     *          If there is no room, MAX_NUM_BLE_CONNS will be returned.
     */
    static uint8_t Connection_addConnInfo(uint16_t connHandle, uint8_t *pAddr)
    {
      uint8_t i = 0;
    
      for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
      {
        if (connectionConnList[i].connHandle == LINKDB_CONNHANDLE_INVALID)
        {
          // Found available entry to put a new connection info in
          connectionConnList[i].connHandle = connHandle;
          memcpy(connectionConnList[i].peerAddress, pAddr, B_ADDR_LEN);
    
          break;
        }
      }
    
      return i;
    }
    
    /*********************************************************************
     * @fn      Connection_removeConnInfo
     *
     * @brief   Remove a device from the connected device list
     *
     * @return  index of the connected device list entry where the new connection
     *          info is removed from.
     *          If connHandle is not found, MAX_NUM_BLE_CONNS will be returned.
     */
    static uint8_t Connection_removeConnInfo(uint16_t connHandle)
    {
      uint8_t i = 0;
      uint8_t index = 0;
      uint8_t maxNumConn = MAX_NUM_BLE_CONNS;
    
      for (i = 0; i < maxNumConn; i++)
      {
        if (connectionConnList[i].connHandle == connHandle)
        {
          // Mark the entry as deleted
          connectionConnList[i].connHandle = LINKDB_CONNHANDLE_INVALID;
    
          break;
        }
      }
    
      // Save the index to return
      index = i;
    
      // Shift the items in the array
      for(i = 0; i < maxNumConn - 1; i++)
      {
          if (connectionConnList[i].connHandle == LINKDB_CONNHANDLE_INVALID &&
              connectionConnList[i + 1].connHandle == LINKDB_CONNHANDLE_INVALID)
          {
            break;
          }
          if (connectionConnList[i].connHandle == LINKDB_CONNHANDLE_INVALID &&
              connectionConnList[i + 1].connHandle != LINKDB_CONNHANDLE_INVALID)
          {
            memmove(&connectionConnList[i], &connectionConnList[i+1], sizeof(App_connInfo));
            connectionConnList[i + 1].connHandle = LINKDB_CONNHANDLE_INVALID;
          }
      }
    
      return index;
    }
    
    /*********************************************************************
     * @fn      Connection_getConnList
     *
     * @brief   Get the connection list
     *
     * @return  connection list
     */
    App_connInfo *Connection_getConnList(void)
    {
      return connectionConnList;
    }
    
    /*********************************************************************
     * @fn      Connection_getConnhandle
     *
     * @brief   Find connHandle in the connected device list by index
     *
     * @return  the connHandle found. If there is no match,
     *          MAX_NUM_BLE_CONNS will be returned.
     */
    uint16_t Connection_getConnhandle(uint8_t index)
    {
    
        if (index < MAX_NUM_BLE_CONNS)
        {
          return connectionConnList[index].connHandle;
        }
    
      return MAX_NUM_BLE_CONNS;
    }
    
    /*********************************************************************
     * @fn      Connection_start
     *
     * @brief   This function is called after stack initialization,
     *          the purpose of this function is to initialize and
     *          register the specific events handlers of the connection
     *          application module
     *
     * @return  SUCCESS, errorInfo
     */
    bStatus_t Connection_start()
    {
        bStatus_t status = SUCCESS;
        uint8 i;
    
        // Initialize the connList handles
        for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
        {
            connectionConnList[i].connHandle = LINKDB_CONNHANDLE_INVALID;
        }
    
        status = BLEAppUtil_registerEventHandler(&connectionConnHandler);
        if(status != SUCCESS)
        {
            return(status);
        }
    
        status = BLEAppUtil_registerEventHandler(&connectionHciGAPHandler);
        if(status != SUCCESS)
        {
            return(status);
        }
    
        return status;
    }
    
    /*********************************************************************
     * @fn      Connection_getConnIndex
     *
     * @brief   Find index in the connected device list by connHandle
     *
     * @return  the index of the entry that has the given connection handle.
     *          if there is no match, LL_INACTIVE_CONNECTIONS will be returned.
     */
    uint16_t Connection_getConnIndex(uint16_t connHandle)
    {
      uint8_t i;
    
      for (i = 0; i < MAX_NUM_BLE_CONNS; i++)
      {
        if (connectionConnList[i].connHandle == connHandle)
        {
          return i;
        }
      }
    
      return LL_INACTIVE_CONNECTIONS;
    }
    
    #endif // ( HOST_CONFIG & (CENTRAL_CFG | PERIPHERAL_CFG) )
    

    I hope this will help,

    Best regards,