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-CC26X2R1: Multi_role / GATT CLIENT and GATT Server Simultaneously

Part Number: LAUNCHXL-CC26X2R1
Other Parts Discussed in Thread: UNIFLASH

Hi all!

So I don't want you guys to get bored, so I have a new question :-)

In the past Simple Link Academy has been a massive help, but I did notice in the last days, that the information of using simple_central / multi_role as a GATT Client is very minimal. Therefor I do have some questions and yes I read chapter "Generic Attribute Profile (GATT)" and "ATT and GATT Introduction" multiple times, but I'm still stuck with the following question and problem.

1. When I use 2 multi_role devices that advertise and scan Simultaneously and connect to each other, how can I check which device is the Gatt Client and which the Gatt Server in this particular connection.

2. Without knowing how to solve no. 1 I try to write as a GATT Client to the GATT Server to a custom profile and I always get as a return "Write Error 1" when using GATT_WriteCharValue(), Here is my code where I try to write the Value to a custom profile after I press a button: 

 status_t status7;
                       uint8_t charVals[4] = { 0x04, 0x04, 0x04, 0x04 }; // Should be consistent with
                                                                                               // those in scMenuGattWrite
                       attWriteReq_t req;

                       req.pValue = GATT_bm_alloc(connHandle2, ATT_WRITE_REQ, 1, NULL);

                       if ( req.pValue != NULL )
                        {
                              uint8_t connIndex = multi_role_getConnIndex(connHandle2);

                              // connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
                              MULTIROLE_ASSERT(connIndex < MAX_NUM_BLE_CONNS);

                              req.handle = connList[connIndex].charHandle;
                              req.len = 1;
                              charVal = charVals[1];
                              req.pValue[0] = charVal;
                              req.sig = 0;
                              req.cmd = 0;


                              status7 = GATT_WriteCharValue(connHandle2, &req, selfEntity);
                              if ( status7 != SUCCESS )
                              {
                                  GATT_bm_free((gattMsg_t *)&req, ATT_WRITE_REQ);
                              }else
                              {
                                  Display_printf(dispHandle, MR_ROW_MYADDRSS, 0,  "Write Request Sent to connhandle %i", connHandle2);
                              }
                          }

This Is my multi_role_processGATTDiscEvent:

static void multi_role_processGATTDiscEvent(gattMsgEvent_t *pMsg)
{
  uint8_t connIndex = multi_role_getConnIndex(pMsg->connHandle);
  MULTIROLE_ASSERT(connIndex < MAX_NUM_BLE_CONNS);

  if (connList[connIndex].discState == BLE_DISC_STATE_MTU)
  {
    // MTU size response received, discover Custom service
    if (pMsg->method == ATT_EXCHANGE_MTU_RSP)
    {
      uint8_t uuid[ATT_BT_UUID_SIZE] = { LO_UINT16(CUSTOMSERVICE_SERV_UUID),
                                         HI_UINT16(CUSTOMSERVICE_SERV_UUID) };

      connList[connIndex].discState = BLE_DISC_STATE_SVC;

      // Discovery Custom service
      VOID GATT_DiscPrimaryServiceByUUID(pMsg->connHandle, uuid,
                                         ATT_BT_UUID_SIZE, selfEntity);
    }
  }
  else if (connList[connIndex].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
        connList[connIndex].discState = BLE_DISC_STATE_CHAR;

        req.startHandle = svcStartHdl;
        req.endHandle = svcEndHdl;
        req.type.len = ATT_BT_UUID_SIZE;
        req.type.uuid[0] = LO_UINT16(CUSTOM_SERV6_UUID); 
        req.type.uuid[1] = HI_UINT16(CUSTOM_SERV6_UUID);

        VOID GATT_DiscCharsByUUID(pMsg->connHandle, &req, selfEntity);
      }
    }
  }
  else if (connList[connIndex].discState == BLE_DISC_STATE_CHAR)
  {
    // Characteristic found, store handle
    if ((pMsg->method == ATT_READ_BY_TYPE_RSP) &&
        (pMsg->msg.readByTypeRsp.numPairs > 0))
    {
      uint8_t connIndex = multi_role_getConnIndex(connHandle2);

      // connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
      MULTIROLE_ASSERT(connIndex < MAX_NUM_BLE_CONNS);

      // Store the handle of the QW characteristic 1 value
      connList[connIndex].charHandle
        = BUILD_UINT16(pMsg->msg.readByTypeRsp.pDataList[3],
                       pMsg->msg.readByTypeRsp.pDataList[4]);

      Display_printf(dispHandle, 13, 0, "Custom Svc Found");

      // Now we can use GATT Read/Write
      tbm_setItemStatus(&mrMenuPerConn,
                        MR_ITEM_GATTREAD | MR_ITEM_GATTWRITE, MR_ITEM_NONE);
    }

    connList[connIndex].discState = BLE_DISC_STATE_IDLE;
  }
}

This is my multi_role_processGATTMsg

static uint8_t multi_role_processGATTMsg(gattMsgEvent_t *pMsg)
{

  // Get connection index from handle
  uint8_t connIndex = multi_role_getConnIndex(pMsg->connHandle);
  MULTIROLE_ASSERT(connIndex < MAX_NUM_BLE_CONNS);



  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_printf(dispHandle, MR_ROW_CUR_CONN, 0, "FC Violated: %d", pMsg->msg.flowCtrlEvt.opcode);

  }
  else if (pMsg->method == ATT_MTU_UPDATED_EVENT)
  {
    // MTU size updated
    Display_printf(dispHandle, MR_ROW_CUR_CONN, 0, "MTU Size: %d", pMsg->msg.mtuEvt.MTU);

  }


  // Messages from GATT server
  if (linkDB_Up(pMsg->connHandle))
  {

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

    }
    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)
      {
        Display_printf(dispHandle, 0, SP_NVS_DEBUG, "Write Error %d", pMsg->msg.errorRsp.errCode);

      }
      else
      {
        // After a succesful write, display the value that was written and
        // increment value
        Display_printf(dispHandle, SP_NVS_DEBUG, 0, "Write sent: %d", charVal);
      }

      tbm_goTo(&mrMenuPerConn);
    }
    else if (connList[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);
}

If I write a 0x04 to the characteristic Value CUSTOM_SERV6_UUID via the SimpleLink iOS app it works perfectly, but not if I do it from the multi_role -> multi_role.

I'm pretty sure I have a mistake there, I noticed that everything is very well explained, but I think the information how a GATT Client operates is very minimal and the past questions in the forum are not a big help.

Thanks in advance.


Kind Regards,

SPEEDBIRD

  • Hi Speedbird,

    You have very interesting questions. I'm assigning this thread internally for review and follow-up.

  • Hi,

    1. When you connect two devices who have GATT services (two multi role devices) they will both be GATT Severs. Both devices can also act as GATT client to their peer device. (So a GATT client-server relationship is not defined in terms of a connection, but in each GATT exchange.)

    If you are connecting to a peer device and you don't know whether they are a GATT server you can try sending a "Discover all services" command. If they don't have any services they're not a GATT server.

    2. What version of the SimpleLink CC13x2/CC26x2 SDK are you using?

    Can you check the return status of GATT_WriteCharValue()?

  • Hi,

    that sounds good and answered my first question.

    Now back to the problem:

    - I'm using the SDK 3.20.0.68

    - GATT_WriteCharValue() Returns 0x00 => SUCCESS 

    any idea maybe what the problem else could be?

    Thanks in advance.

    Best Regards,

    SPEEDBIRD

  • Hi,

    The error code you are seeing, Write Error: 1, if you look in att.h you see this is defines as the following:

    #define ATT_ERR_INVALID_HANDLE           0x01 //!< Attribute handle value given was not valid on this attribute server

    So it seems like the charHandle you are giving in the GATT_WriteCharValue() command is not correct. 

  • Hi, 

    I had a feeling it could be that, because the function multi_role_processGATTDiscEvent()  never gets called when connected to the device, so a Handle can not be loaded / generated... Do you maybe have an idea why itˋs not getting called?

    The Devices both successfully pair, so:

       case GAPBOND_PAIRING_STATE_COMPLETE:
          if (status == SUCCESS)
    {
    (...) }

     is called. But still no device discovery, even if I wipe the Flash with UniFlash... and re-Flash

    cheers you are a great help up to now!

  • Hi,

    Service discovery will begin with the MR_EVT_SVC_DISC event which seems to be posted in the multi_role_doSelectConn() function. Did you go through the menu and select Work with... and that device?

  • Marie, you are my Hero!

    Once the device is connected, I force it with the following command 

                        multi_role_startSvcDiscovery();
    

    to discover the Services, now both sides (like you said) act as a Client and a Server. Calling multiple times SvcDiscovery() (e.g. every 10 seconds) does not work for some reason...

    Kind Regards,

    SPEEDBIRD

     

  • Hi,

    Good to hear you got it to work :)

    If you follow the api calls and events that follow multi_role_startSvcDiscovery() you will see that there are a few places it can go wrong if events come in the wrong sequence. If you want to debug I would recommend using a sniffer to see where in the process the device is stuck.