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.

LAUNCHXL-CC2650: how to enable notification in HID over BLE device with simple central project?

Part Number: LAUNCHXL-CC2650

Hello,

I'm using the Simple Central project with the cc2650lp and i'm trying to enable the HID notifications by enabling the CCC in the HID device.

For now i'm using the HidKbd project for the cc2650lp as the keyboard device but in the long run i want to do this with any HID device.

I have modified the SimpleCentral example based on the spp_ble client and it seems that i receive the "enable notification" write request in the HidKbd project but i can't catch any notification on the Central side.

i modified the 'SimpleBLECentral_processGATTDiscEvent' function

static void SimpleBLECentral_processGATTDiscEvent(gattMsgEvent_t *pMsg)
{
    if (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(HIDKBD_SERV_UUID),
                                               HI_UINT16(HIDKBD_SERV_UUID) };

            // Just in case we're using the default MTU size (23 octets)
            Display_print1(dispHandle, ROW_THREE, 0, "MTU Size: %d", ATT_MTU_SIZE);
            discState = BLE_DISC_STATE_SVC;

            // Discovery simple BLE service
            VOID GATT_DiscPrimaryServiceByUUID(connHandle, uuid, ATT_BT_UUID_SIZE,
                                               selfEntity);
        }
    }
    else if (discState == BLE_DISC_STATE_SVC)
    {
        // Service found, store handles
        if (pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
            pMsg->msg.findByTypeValueRsp.numInfo > 0)
        {
            svcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
            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 (svcStartHdl != 0)
            {
                GATT_DiscAllCharDescs(connHandle, svcStartHdl, svcEndHdl, selfEntity);
                discState = BLE_DISC_STATE_CHAR;
            }
        }
    }
    else if (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
                charCCCDHdl = ATT_PAIR_HANDLE(pMsg->msg.findInfoRsp.pInfo, i);
                break;
              }
            }
          }
        }

        // If procedure complete
         if ((pMsg->method == ATT_FIND_INFO_RSP  &&
              pMsg->hdr.status == bleProcedureComplete) ||
             (pMsg->method == ATT_ERROR_RSP))
         {
            SimpleBLECentral_EnableNotification( connHandle, charCCCDHdl );
            procedureInProgress = FALSE;
            discState = BLE_DISC_STATE_IDLE;
         }
    }
}


and the 'SimpleBLECentral_EnableNotification' is

static void SimpleBLECentral_EnableNotification( uint16 connHandle, uint16 attrHandle )
{
  attWriteReq_t req;

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

  }
}

I am able catch the write request of enable notification in the 'HidDev_WriteAttrCB' function in hidKbd project, after notifications are enabled i can't seem to catch (breakpoints and ex..) any Notification on the Central GATT Handlers.

what i'm missing?

Thanks for the help!

  • Hi Sharon,

    Let me look into this and I'll respond within the next 24 hours.
  • Sharon,

    To help, can you inform me of the following?
    Are you using BLE SDK V2.2.1? I'm not seeing a simple_central example in C:\ti\simplelink\ble_sdk_2_02_01_18\examples\cc2650lp
  • Hey Evan,

    The TI example project can be found on their github under ble_example_2.2 branch:

    https://github.com/ti-simplelink/ble_examples/tree/ble_examples-2.2

    And i have managed to make this work:

    1. HID profile contains multiple CCC Descriptors, therefore i need to catch them all and not only the last one.

    2. There is a mistake in the spp_ble example, while looking for 16bit UUID with ATT_BT_PAIR_UUID macro, the inne code take the handle of a 16 bytes UUID with ATT_PAIR_HANDLE, changing that to ATT_BT_PAIR_HANDLE solved the issue and the handle values were correct.

    This is the final code i used if someone will need it in the future:

    modified the 'SimpleBLECentral_processGATTDiscEvent' function:

    // Discovered characteristic CCCD handle
    #define MAX_CCCD_HANDLES 10
    static uint16_t charCCCDHdl[MAX_CCCD_HANDLES];
    static uint16_t noCharCCCDHdl = 0;
    static uint16_t currentCharCCCDHdlIndex = 0;
    
    static void SimpleBLECentral_processGATTDiscEvent(gattMsgEvent_t *pMsg)
    {
    	if (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(HIDKBD_SERV_UUID),
                                                   HI_UINT16(HIDKBD_SERV_UUID) };
    
    			// Just in case we're using the default MTU size (23 octets)
    			Display_print1(dispHandle, ROW_THREE, 0, "MTU Size: %d", ATT_MTU_SIZE);
    			discState = BLE_DISC_STATE_SVC;
    
    			// Discovery simple BLE service
    			VOID GATT_DiscPrimaryServiceByUUID(connHandle, uuid, ATT_BT_UUID_SIZE,
                                                   selfEntity);
    		}
    	}
    	else if (discState == BLE_DISC_STATE_SVC)
    	{
    		// Service found, store handles
    		if (pMsg->method == ATT_FIND_BY_TYPE_VALUE_RSP &&
    			pMsg->msg.findByTypeValueRsp.numInfo > 0)
    		{
    			svcStartHdl = ATT_ATTR_HANDLE(pMsg->msg.findByTypeValueRsp.pHandlesInfo, 0);
    			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 (svcStartHdl != 0)
    			{
                    GATT_DiscAllCharDescs(connHandle, svcStartHdl, svcEndHdl, selfEntity);
                    discState = BLE_DISC_STATE_CHAR;
    			}
    		}
    	}
    	else if (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++;
    	            }
    	          }
    	        }
    	      }
    	    }
    
    	    // 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;
    	         discState = BLE_DISC_STATE_IDLE;
    	     }
    	}
    
    }

    
    

    I created a delayed event handler for the notification to start after the connection process:

    // Simple BLE Central Task Events
    ...
    #define SBC_START_NOTIFICATION                0x0080
    ...
    
    static Clock_Struct startNotiEnableClock;
    Util_constructClock(&startNotiEnableClock, SimpleBLECentral_startNotiHandler,
                          DEFAULT_NOTI_ENABLE_DELAY, 0, false, SBC_START_NOTIFICATION);
    
    ...
    
    static void SimpleBLECentral_startNotiHandler(UArg arg)
    {
      // Store the event.
      events |= SBC_START_NOTIFICATION;
    
      // Wake up the application.
      Semaphore_post(sem);
    }

    and finally enbale the notification from the taskFXN:

    static void SimpleBLECentral_taskFxn(UArg a0, UArg a1) {
    ...
    //Original code
    ...
            if (currentCharCCCDHdlIndex < noCharCCCDHdl)
            {
                SimpleBLECentral_EnableNotification( connHandle, charCCCDHdl[currentCharCCCDHdlIndex ] );
                currentCharCCCDHdlIndex ++;
    
                //Enable notification on peripheral(after a few seconds delay, let it finish connection/discovery process)
                {
                  Util_startClock(&startNotiEnableClock);
                }
    
            }
    ...
    //Original code
    ...
    }

    while using the same  'SimpleBLECentral_EnableNotification' as in the original post

    static void SimpleBLECentral_EnableNotification( uint16 connHandle, uint16 attrHandle )
    {
      attWriteReq_t req;
    
      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 code supports up to 10 CCC descriptors - for me this was enough, and maybe the event generation can be handled more nicely but this worked :)

    Hope this helps someone

    Sharon