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.

CC2640R2F: Enabling Notification in Simple Central?

Part Number: CC2640R2F

Hello, 

I am trying to enable notifications from my simple_central based program but I am having some issues. I have read many posts about it and see that it consists of three modifications:

1. Call GATT_DiscAllCharDescs to get handle of CCCD

2. Write 0x01 to CCCD handle to enable notification

3. Add ATT_HANDLE_VALUE_NOTI to event handler in main loop

I am trying to add the first part but it always returns 0x016 (blePending error). Below is my discovery function which I have modified to handle 128 bit uuids:

static void SimpleCentral_processGATTDiscEvent(gattMsgEvent_t *pMsg)
{
  if (discState == BLE_DISC_STATE_MTU)
  {
    // MTU size response received, discover simple service
    if (pMsg->method == ATT_EXCHANGE_MTU_RSP)
    {
      uint8_t uuid[ATT_UUID_SIZE] = {FANGRECEIVER_SERV_UUID};

      // Just in case we're using the default MTU size (23 octets)
      sprintf(displayBuff, "MTU Size: %d", ATT_MTU_SIZE);
      queueDisplayString(displayBuff);

      discState = BLE_DISC_STATE_SVC;

      // Discovery simple service
      VOID GATT_DiscPrimaryServiceByUUID(connHandle, uuid, ATT_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)
      {

        discState = BLE_DISC_STATE_CHAR;

        //Discover All Characteristics
        status =  GATT_DiscAllChars(connHandle, svcStartHdl, svcEndHdl, selfEntity);

        sprintf(displayBuff, "Disconnections: %d", disconnections);
        queueDisplayString(displayBuff);

      }
    }
  }
  else if (discState == BLE_DISC_STATE_CHAR)
  {
    // Characteristic found, store handle
    if ((pMsg->method == ATT_READ_BY_TYPE_RSP) &&
        (pMsg->msg.readByTypeRsp.numPairs > 0))
    {

        uint16_t len = pMsg->msg.readByTypeRsp.len;
        uint8_t i;
        uint8_t j;
        uint8_t k = 0;
        uint8_t uuidChar[ATT_UUID_SIZE]= {0};

        uint8_t uuidChar1[ATT_UUID_SIZE] = {FANGRECEIVER_CHAR1_UUID};
        uint8_t uuidChar2[ATT_UUID_SIZE] = {FANGRECEIVER_CHAR2_UUID};
        uint8_t uuidChar3[ATT_UUID_SIZE] = {FANGRECEIVER_CHAR3_UUID};
//        uint8_t uuidChar4[ATT_UUID_SIZE] = {FANGRECEIVER_CHAR4_UUID};

        for(i=0; pMsg->msg.readByTypeRsp.numPairs > i; i++)
        {
            if(i==0)
                memcpy(uuidChar, uuidChar1, 17);
            if(i==1)
                memcpy(uuidChar, uuidChar2, 17);
            if(i==2)
                memcpy(uuidChar, uuidChar3, 17);
//            if(i==3)
//                memcpy(uuidChar, uuidChar4, 17);

            //check out uuid of char:
            for(j=0; j<=16; j++)
            {
                //start at 5 and go to 20 plus offset (i*len)
                if(pMsg->msg.readByTypeRsp.pDataList[(5+j)+(i*len)] == uuidChar[j])
                    k++;
            }

            if(k==16)
            {

                if(i==0)
                    char1Hdl =  pMsg->msg.readByTypeRsp.pDataList[3+(len*i)];
                if(i==1)
                    char2Hdl =  pMsg->msg.readByTypeRsp.pDataList[3+(len*i)];
                if(i==2)
                    char3Hdl =  pMsg->msg.readByTypeRsp.pDataList[3+(len*i)];
//                if(i==3)
//                    char4Hdl =  pMsg->msg.readByTypeRsp.pDataList[3+(len*i)];
            }
            k=0;

        }

        if(!char1Hdl||!char2Hdl||!char3Hdl)
            queueDisplayString("Char Dsc Error");


      queueDisplayString("Simple Svc Found");

      //Discover all characteristic descriptors
      //status =  GATT_DiscAllCharDescs(connHandle, svcStartHdl, svcEndHdl, selfEntity);

      procedureInProgress = FALSE;
    }

    discState = BLE_DISC_STATE_IDLE;
  }
}

Any guidance on where I should be calling the GATT_DiscAllCharDescs so I don't get a blePending error would be greatly appreciated. 

Thanks!

  • Hi Joshua,

    I did the same as you - well, I modified simple_central to do DiscAllChars instead of DiscCharsByUUID.. Had no issue getting a response. The status was 0x00 and I end up in the "Simple Svc Found" block.

    On the other hand, this isn't how you get the CCCD handle. You are only discovering characteristic declaration handle/value, where the value of the declaration contains the handle of the value attribute.

    You are not finding handle of the CCCD. Admittedly you can cheat and just "know" that it's the characteristic value attribute handle + 1. This would work as long as you control both sides.

    So, these are the attributes of a service

    hdl - UUID - value
    40 - 0x2800 - 0xFFF0 (service declaration attribute)
    41 - 0x2803 - 0xFFF1 + value attribute handle + properties (char declaration attr)
    42 - 0xFFF1 - Whatever value this has (characteristic value attribute)
    43 - 0x2902 - 0x0000 (CCCD attribute)
    .. repeat with new char

    And to get the CCCD you would either replace your DiscAllChars with an ATT FindInfoRequest to just get all the attributes in the service up front, or you would follow up with a DiscAllCharDescs for the service as a whole, or for each char individually.

    All this is to say, I can't reproduce your problem, sorry. Is it possible you have some other GATT thing running concurrently in your application? How about if you try to re-send the request later?

    Best regards,
    Aslak
  • Hello Aslak,
    Thank you so much for your thorough reply. I am still a bit confused on a few points.

    First, regarding the "cheat" with adding 1 to the attribute handle, is this not the handle of the characteristic declaration? I understand how I am getting the handles for each characteristic from the DiscAllChars returned message. Does this returned message also contain the attribute handle? I have full control over the peripheral side and the central will only ever connect to that. My received characteristic handles are consistently 30, 33, and 36.

    Secondly, I have solved the blepending error by making a timer that calls it 500ms after the characteristic declaration handles are received. However, it is returing some strange things or maybe I don't fully understand what it should be returning.
    I have added this to bit of code to the processGATTDiscEvent function:

    // Characteristic descriptors found
    if ( pMsg->method == ATT_FIND_INFO_RSP &&
    pMsg->msg.findInfoRsp.numInfo > 0 &&
    pMsg->msg.findInfoRsp.format == ATT_HANDLE_BT_UUID_TYPE )
    {
    attFindInfoRsp_t *pRsp = &(pMsg->msg.findInfoRsp);

    // For each handle/uuid pair
    for ( i = 0; i < pMsg->msg.findInfoRsp.numInfo; i++ )
    {
    // Look for CCCD
    if ( ATT_BT_PAIR_UUID( pRsp->pInfo, i ) == GATT_CLIENT_CHAR_CFG_UUID ) //getting stuck here
    {
    // CCCD found
    charCCCDHdl = ATT_BT_PAIR_HANDLE( pRsp->pInfo, i );

    break;
    }
    }

    It does get in here after calling DiscAllCharDescs, but it never gets into the CCCD found area. The values of the first 8 bytes of pRSP->pInfo are 00, 00, 40, 29, 00, 03, 40, 32. Are the 29 and the 32 the handles of the CCCD? Additionally, the pMsg->msg.findInfoRsp.numInfo is 2.
    I am not sure what to make of this information. Can you tell what the expected CCCD handles should be with this information? What would you reccommend me do from here?

    Thanks so much,
    Josh
  • Hi,

    If you have the handle of the value (not the decl) then typically the CCCD is the handle after. But like I said, this can only be trusted if you have made both sides or if the service specification for that service does not allow optional descriptor attributes for a characteristic.

    It's not something we have documented very well at all, but I took a stab at doing what you want;

    uint8_t status = 99;
    uint16_t handles[20];
    uint16_t handleIdx = 0;
    uint16_t cccdHandle = 0;
    static void SimpleCentral_processGATTDiscEvent(gattMsgEvent_t *pMsg)
    {
      if (discState == BLE_DISC_STATE_MTU)
      {
        // MTU size response received, discover simple 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) };
    
          // Just in case we're using the default MTU size (23 octets)
          Display_print1(dispHandle, 4, 0, "MTU Size: %d", ATT_MTU_SIZE);
    
          discState = BLE_DISC_STATE_SVC;
    
          // Discovery simple 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)
          {
            attReadByTypeReq_t req;
    
            // Discover characteristic
            discState = BLE_DISC_STATE_CHAR;
    
            req.startHandle = svcStartHdl;
            req.endHandle = svcEndHdl;
            req.type.len = ATT_BT_UUID_SIZE;
            req.type.uuid[0] = LO_UINT16(SIMPLEPROFILE_CHAR1_UUID);
            req.type.uuid[1] = HI_UINT16(SIMPLEPROFILE_CHAR1_UUID);
    
    //        VOID GATT_DiscCharsByUUID(connHandle, &req, selfEntity);
            status =  GATT_DiscAllChars(connHandle, svcStartHdl, svcEndHdl, selfEntity);
          }
        }
      }
      else if (discState == BLE_DISC_STATE_CHAR)
      {
        // Characteristic found, store handle
        if ((pMsg->method == ATT_READ_BY_TYPE_RSP) &&
            (pMsg->msg.readByTypeRsp.numPairs > 0))
        {
            uint8_t *pairs = &pMsg->msg.readByTypeRsp.pDataList[3];
            uint16_t numPairs = pMsg->msg.readByTypeRsp.numPairs;
            uint16_t pairLen = pMsg->msg.readByTypeRsp.len;
    
            for (uint16_t idx = 0; idx < numPairs; ++idx, pairs += pairLen)
            {
                uint16_t handle = *(uint16_t *)&pairs[0];
                uint16_t uuid = *(uint16_t *)&pairs[2];
    
                handles[handleIdx++] = charHdl = handle;
    
                if (uuid == 0xFFF4) {
                    cccdHandle = charHdl + 1;
                }
            }
        }
    
        if (pMsg->hdr.status == bleProcedureComplete) {
            discState = BLE_DISC_STATE_DESC;
            status = GATT_DiscAllCharDescs(connHandle, handles[3], handles[4], selfEntity);
        }
      }
      else if (discState == BLE_DISC_STATE_DESC)
      {
    
          if ((pMsg->method == ATT_FIND_INFO_RSP) &&
              (pMsg->msg.findInfoRsp.numInfo > 0))
          {
              uint8_t *info = pMsg->msg.findInfoRsp.pInfo;
              uint16_t numInfo = pMsg->msg.findInfoRsp.numInfo;
              size_t entrySize = pMsg->msg.findInfoRsp.format == 0x01 ? 2 : 18;
    
              for (uint16_t i = 0; i < numInfo; ++i, info += entrySize)
              {
                  uint16_t handle = *(uint16_t *)&info[0];
                  uint16_t uuid = *(uint16_t *)&info[2];
    
                  if (uuid == 0x2902)
                  {
                      cccdHandle = handle;
                      Display_print1(dispHandle, 4, 0, "CCCD Handle: %d", cccdHandle);
                  }
              }
          }
    
          if (pMsg->hdr.status == bleProcedureComplete ||
             pMsg->method == ATT_ERROR_RSP)
          {
              procedureInProgress = FALSE;
              discState = BLE_DISC_STATE_IDLE;
          }
      }
    }

    It's not nice, and I can't really explain why the offset for the read by type response, but it seems to work.

    Best regards,
    Aslak

  • Thank you so much for your thoughrough reply. This was instrumental in helping me solve the problem. Thanks again!