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.

LP-CC2651R3SIPA: Performing periodicTask with GATT_ReadMultiCharValues causes DEFAULT SPINLOCK

Part Number: LP-CC2651R3SIPA


Hello,

I'm working on 2 multi_role base projects, although one acts as a client and another as a server. My SDK is CC13xx CC26xx 7.10.00.98 and I'm using 2 LP-CC2651R3SIPA.

My goal is to receive readings within a range of 0,5 to 1 second in order to update data in the client project as quickly as possible as it happens in the server (time is sitting between 1,8-2,2 seconds now, too slow). I am reading correct data, and I can lower the amount of data read or change some data types, if that's what I have to do. Display methods will also not be needed as currently It's only for my debug purposes, if that makes it feel slower.

I have a client where I am using GATT_ReadMultiCharValues in the multi_role_performPeriodicTask method, I've managed to set MR_PERIODIC_EVT_PERIOD to as low as 1000 without spinlock issues, but when it is set to 800 or lower (haven't tested other values between 1000 and 800), the error appears, and then the AssertHandler executes with parameters "assertCause" as 8 and "assertSubcause" as 0 (no matter the time I set sub-800 as MR_PERIODIC_EVT_PERIOD). Although the periodic event happens once each second, I get results each 1,8-2,2 seconds (approximately, calculated by telephone's chronometer) which is too slow for the purpose. Once It is sub-800, the program displays DEFAULT SPINLOCK! and becomes unresponsive.

Reading a lower amount of characteristics with MR_PERIODIC_EVT_PERIOD as 800 (or lower) doesn't help either, as the same error codes appear.

The code in multi_role_performPeriodicTask is as follows:

static void multi_role_performPeriodicTask(void)
{
    if (debug_isGATTavailable) {
        attReadMultiReq_t reqMulti;
        reqMulti.numHandles = 2;
        reqMulti.pHandles = GATT_bm_alloc(mrConnHandle, ATT_READ_MULTI_REQ, 4, NULL);
        reqMulti.pHandles[0] = LO_UINT16(connList[0].charHandle);
        reqMulti.pHandles[1] = HI_UINT16(connList[0].charHandle);
        reqMulti.pHandles[2] = LO_UINT16(connList[0].charHandle + 3);
        reqMulti.pHandles[3] = HI_UINT16(connList[0].charHandle + 3);
        reqMulti.pHandles[4] = LO_UINT16(connList[0].charHandle + 6);
        reqMulti.pHandles[5] = HI_UINT16(connList[0].charHandle + 6);
        reqMulti.pHandles[6] = LO_UINT16(connList[0].charHandle + 9);
        reqMulti.pHandles[7] = HI_UINT16(connList[0].charHandle + 9);
        reqMulti.pHandles[8] = LO_UINT16(connList[0].charHandle + 12);
        reqMulti.pHandles[9] = HI_UINT16(connList[0].charHandle + 12);
        reqMulti.pHandles[10] = LO_UINT16(connList[0].charHandle + 15);
        reqMulti.pHandles[11] = HI_UINT16(connList[0].charHandle + 15);
        reqMulti.pHandles[12] = LO_UINT16(connList[0].charHandle + 18); 
        reqMulti.pHandles[13] = HI_UINT16(connList[0].charHandle + 18);

        bStatus_t status = FAILURE;
        while (status != SUCCESS)
        {
            if (status == blePending) {
                break;
            }

            status = GATT_ReadMultiCharValues(mrConnHandle, &reqMulti, selfEntity);
            Display_print1(dispHandle, MR_ROW_CHAR_1, 0, "Read multi char status - %d", status);
        }
        Display_printf(dispHandle, MR_ROW_CHAR_1, 0, "Success. Status - %d", status);
        GATT_bm_free( (gattMsg_t *) &reqMulti, ATT_READ_MULTI_REQ);
    }
}

The part of the code that handles the responses (in multi_role_processGATTMsg) is as follows. Currently I am reading 1-byte characteristic and 5 4-byte characteristics (which I convert into a uint32_t after):

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 ( ... other conditions checking other kind of responses) {
        // methods just as the original code in multi_role is 
        
        }
        else if (((pMsg->method == ATT_READ_MULTI_RSP) && (pMsg->msg.readMultiRsp.len > 0))
            || ((pMsg->method == ATT_ERROR_RSP) && (pMsg->msg.errorRsp.reqOpcode == ATT_READ_MULTI_REQ)))
        {
            if (pMsg->method == ATT_ERROR_RSP)
            {
                Display_printf(dispHandle, MR_ROW_CHAR_2, 0, "ATT ERROR reqOpCode: %d, errCode: %d", pMsg->msg.errorRsp.reqOpcode, pMsg->msg.errorRsp.errCode);
            }
            else
            {
                Display_printf(dispHandle, MR_ROW_CHAR_2, 0,
                        "ATT_READ_MULTI_RSP, len: %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
                        pMsg->msg.readMultiRsp.len, 
                        pMsg->msg.readMultiRsp.pValues[0], pMsg->msg.readMultiRsp.pValues[1],
                        pMsg->msg.readMultiRsp.pValues[2], pMsg->msg.readMultiRsp.pValues[3],
                        pMsg->msg.readMultiRsp.pValues[4], pMsg->msg.readMultiRsp.pValues[5],
                        pMsg->msg.readMultiRsp.pValues[6], pMsg->msg.readMultiRsp.pValues[7],
                        pMsg->msg.readMultiRsp.pValues[8], pMsg->msg.readMultiRsp.pValues[9],
                        pMsg->msg.readMultiRsp.pValues[10], pMsg->msg.readMultiRsp.pValues[11],
                        pMsg->msg.readMultiRsp.pValues[12], pMsg->msg.readMultiRsp.pValues[13],
                        pMsg->msg.readMultiRsp.pValues[14], pMsg->msg.readMultiRsp.pValues[15],
                        pMsg->msg.readMultiRsp.pValues[16], pMsg->msg.readMultiRsp.pValues[17]);

                uint8_t resp_char1 = 0;
                uint32_t resp_char2 = 0;
                uint32_t resp_char3 = 0;
                uint32_t resp_char4 = 0;
                uint32_t resp_char5 = 0;
                uint32_t resp_char6 = 0;
                
                // Converting values
                resp_char1 = pMsg->msg.readMultiRsp.pValues[0];
                Util_convertUInt8arrToUInt32(&resp_char2, pMsg->msg.readMultiRsp.pValues, 1);
                Util_convertUInt8arrToUInt32(&resp_char3, pMsg->msg.readMultiRsp.pValues, 5);
                Util_convertUInt8arrToUInt32(&resp_char4, pMsg->msg.readMultiRsp.pValues, 9);
                Util_convertUInt8arrToUInt32(&resp_char5, pMsg->msg.readMultiRsp.pValues, 13);
                Util_convertUInt8arrToUInt32(&resp_char6, pMsg->msg.readMultiRsp.pValues, 17);
                Display_printf(dispHandle, MR_ROW_CHAR_1, 0, "[1]=%u, [2]=%u, [3]=%u, [4]=%u, [5]=%u, [6]=%u", resp_char1, resp_char2, resp_char3, resp_char4, resp_char5, resp_char6);
            }

I am a bit stuck on where to head to improve as I'm not exactly sure what is causing the DEFAULT SPINLOCK error to appear or how I can improve the reception speeds. I haven't modified much parameters from syscfg or know which documentation (from here) I need to read in order to know how to improve this part.

Edit: I also tried to only read a characteristic but timing is the same, takes more than 2-3 seconds to start and then it keeps receiving data each ~2 seconds.

Any help will be greatly appreciated and with any further details do not hesitate to ask.

Thank you in advance

  • Hi,

    Thank you for reaching out. We will look into this and get back to you. In the meantime, can you provide the connection interval you are using in your project?

    Best Regards,

    Jan

  • Hello,

    The settings are the default for the multi_role project. I did change some but I setted them back to their defaults. Just in case, my settings are:

    Server part:

    Client part:

    Another test I've done is using multi_role_doGattRead (which basically uses GATT_ReadCharValue) in multi_role_performPeriodicTask with a MR_PERIODIC_EVT_PERIOD of 100 and there's no issues with spinlock, but the answer is just as slow as using GATT_ReadMultiCharValues.

    While I would prefer to read multiple characteristics (or several single reads but at a faster pace), I could make a workaround reading a single characteristic and be able to obtain all my needed information.

    Thank you for your help.

  • Hello,

    First thing first, the reading you want to receive is on the other LaunchPad right? You're measuring the time it takes to transfer data from server to client side? What is the GUI you are using to read data on the client side? I'm just asking to know if this is a "protocol" request or just display one.

    As Jan mentioned, the connection interval could be an idea for your issue, try to reduce the connection interval to receive more packets between your expected 0.5 to 1s. (Currently, you're sending a event every second at maximum so if this event cannot contains all your data you'll need another)

    However, you'll consume more power.

    regards,

  • Hello Guillaume,

    First thing first, the reading you want to receive is on the other LaunchPad right?

    Yes, the LaunchPad acting as a server will be reading a sensor which I want to receive on the LaunchPad acting as a client. 

    You're measuring the time it takes to transfer data from server to client side? What is the GUI you are using to read data on the client side? I'm just asking to know if this is a "protocol" request or just display one.

    Currently I am using the Display methods that are used in the multi_role project. What I do is:

    1. Executing method multi_role_performPeriodicTask with either the code that is shown in the first post, or a single call to "multi_role_doGattRead(0)", which is the following code:

    bool multi_role_doGattRead(uint8_t index)
    {
      attReadReq_t req;
      uint8_t connIndex = multi_role_getConnIndex(mrConnHandle);
    
      // connIndex cannot be equal to or greater than MAX_NUM_BLE_CONNS
      MULTIROLE_ASSERT(connIndex < MAX_NUM_BLE_CONNS);
    
      req.handle = connList[connIndex].charHandle;
      Display_printf(dispHandle, MR_ROW_CHARSTAT, 0, "charHandle: %d | req.handle: %d", connList[connIndex].charHandle, req.handle);
    
      GATT_ReadCharValue(mrConnHandle, &req, selfEntity);
    
      return (true);
    }
    

    2. When a result is received, the method multi_role_processGATTMsg shows the result of either the single or multiple read. In the case for a single read, I've added a Display_printf in the original code that just displays the read value:

    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, MR_ROW_CUR_CONN, 0, "Read Error %d", pMsg->msg.errorRsp.errCode);
          }
          else
          {
            // After a successful read, display the read value
            Display_printf(dispHandle, MR_ROW_CHAR_2, 0, "Read rsp: %d | rsp len: %d", pMsg->msg.readRsp.pValue[0], pMsg->msg.readRsp.len);
          }
    
        }
    

    3. This is how I "measure" the time. I have also used the onboard LED to turn on when it receives a response, and I have yet to use an oscilloscope, but I believe if the Display methods are active here, I might not find much difference in timings. What I "measured" is the time since I sent a read request from client until I receive the answer on the Display, although the first answer does feel slower. Since it is performing the task periodically, I keep getting results back and those are coming back each 1,8-2 seconds, so time is as follows:

    Read request Wait for answer Receive read response Wait for next answer Receive read response Wait for next answer Repeats over and over
    2-3 seconds 1,8 - 2,2 seconds 1,8 - 2,2 seconds

    I'm aware that using the Display output may cause a delay, although I don't know up to which extent it may do.

    As Jan mentioned, the connection interval could be an idea for your issue, try to reduce the connection interval to receive more packets between your expected 0.5 to 1s. (Currently, you're sending a event every second at maximum so if this event cannot contains all your data you'll need another)

    About reducing the connection interval, you are referring to Connection Interval Min (ms) and Connection Interval Max (ms) at the central side, and Requested Min Conn. Interval (ms) and Requested Max Conn. Interval (ms) at the peripheral side, right?

    However, you'll consume more power.

    I'm aware that It will consume more power but the response times are more important on this matter. I will try what you suggested and keep you updated.

    Thank you for your help.

  • Thanks for all the detail, your application is clear now.

    Concerning the evaluation of Display time I don't have real figures but what you can do to have a very accurate time of read receiving is to use a BLE sniffer and see the BLE trace, thus you'll have the exact time of each packets. 

    Another method will be to look at the radio activity with a logic analyser, if you want more inputs on this go to the user's guide chapter debugging section debugging RF output.

    Yes, you could set the connection interval at the beginning from the central side, to save some power you could send a connection update param request, (change the connection interval during runtime to have more connection event during a selected period of time then go back to the "default" parameters to save some power)

    regards,

  • Hello,

    Seems like I'm headed to the right direction but there's something that's stopping it.

    I've managed to make the client read really quickly (more than I need) but after a few seconds, It goes back to receiving each ~2 seconds. Problem is I can't see a pattern of why or how this happens. 

    Situation now is as this:

    Client sometimes receives data quickly within the first seconds of receiving, but then It goes back to receiving once each ~2 seconds.

    Server has been changed to this:

    I am still using multi_role in both projects, should I change one of them?

    Unrelated question, when Client is configured with Address Mode as "Public Address" and Server is configured with Address Mode as "Publi Address" as well, client's multi_role seems to hang up when trying to connect to the server. Is this normal behaviour?

    Thanks in advance,

    Albert

  • Hello Albert,

    Seems that there is a connection parameters update after few seconds, to confirm this behaviour I need you to configure the GPIo to read the radio activity.

    To do so, follow the steps in the user's guide, debugging chapter, section debugging RF output.

    Concerning your second point, what do you mean by hang up? difficult to connect, try to connect multiple time before having a proper connection? Does your environnement is BLE crowded?

    regards,