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.

CC2652R7: sending multiple packets within one connection interval

Part Number: CC2652R7

Tool/software:

Hi

I have two devices with my own project based on the Multi_role example.
One device acts as a master, and the other as a slave. On both devices, INIT_PHYPARAM_MIN_CONN_INT/INIT_PHYPARAM_MAX_CONN_INT equals 80 (i.e. 80*1.25ms = 100ms)

DEFAULT_INIT_PHY = INIT_PHY_1M
MAX_NUM_PDU = 5
MAX_PDU_SIZE = 255

The master connects to the other device and starts sending data to the characteristic using GATT_WriteCharValue. I monitor the result of successful sending using the ATT_WRITE_REQ event in GATT. Judging by the execution time, about 165ms (+-) passes between sending and acknowledgment.

It looks like the data packet is sent on one connection interval, and the acknowledgment on another.
Having studied the information, I understand that in theory BLE should be able to send multiple packets within one connection interval. This in turn increases the exchange rate.
What actions should I take to see the speed increase?
If I decrease the value of the connection interval, the exchange rate will increase. If I use recording without confirmation, the speed will increase.
I am interested in how to increase the exchange rate by sending multiple packets within one interval (without changing the interval time and with confirmation of successful recording)

  • Hello Nick,

    Thanks for reaching out. I think you have covered the possibilities here. Are you able to see that your write packet is actually the size you intent (251 approx without header)? I would also suggest to take a look at the GATT_WriteLongCharDesc() function, which will do the fragmentation of the data packet for you, however with the max pdu size supported of 255 bytes (for Data Lenght Extension). You could also use a GATT_WriteNoResponse() function as you mentioned, and after all the data has been transmitted, provide some sort of acknowledgement to the central through the use of a notification.

    BR,

    David.

  • Unfortunately, I am interested in the write function with a subsequent response, the option without a response does not work. Using notifications as confirmation of a received packet seems to me to take longer.
    Do I understand correctly that the exchange of packets between two devices can be depicted as in image #1 (I reduced the packet size from 251 to 32 bytes, but the exchange also takes about 160-200ms, which is equal to 2 connection intervals)? And is it impossible to achieve an exchange as in image #2 using only the provided stack, without additional operations (notifications or something similar)?

    Image #1

    Image #2

  • Hello Nick,

    Apologies for the delay.

    It is possible for the central to transmit several TX packets if the MD (more data flag is set) - this is taken care by the stack itself. You can take a look at section 4.5.6 Closing Connection Events from the BLE 5.0 core spec. The MD bit of the Header of the Data Channel PDU is used to indicate that the
    device has more data to send. If neither device has set the MD bit in their packets, the packet from the slave closes the connection event. If either or both of the devices have set the MD bit, the master may continue the connection event by sending another packet, and the slave should listen after sending its packet.

    BR,

    David.

  • Hi

    you say "this is taken care by the stack itself"

    Is there any way I can track that this bit is being set? Is there any way I can configure (both central and peripheral devices) to be sure that this bit will be set when possible? The documentation you provided says the following:

    The MD bit of the Header of the Data Physical Channel PDU is used to indicate
    that the device has more data to send. If neither device has set the MD bit in
    their packets, the packet from the Peripheral closes the connection event. If
    either or both of the devices have set the MD bit, the Central may continue the
    connection event by sending another packet, and the Peripheral should listen
    after sending its packet

    How do devices decide that there is more data? I found a define for the MD bit in the source libraries, but they are not used anywhere.

  • Hello Nick,

    Apologies for the delay. Do you have a bluetooth LE sniffer? The MD bit is set at a link layer level. This bit will be set based on the MTU packet size, the amount of data it is requested to be send, and the time left to transmit during the connection interval. May I ask what is the throughput you are aiming for?

    BR,

    David.

  • Hi

    I took two devices for exchanging packets. One device (master) sends a data packet to the characteristic, the other device (slave) simply receives it. On the master side, I record the time between sending a message and confirming its sending (the countdown starts after calling the GATT_WriteCharValue function and the countdown ends when receiving the ATT_WRITE_RSP event in GATT msg at the application level). As a result of the calculations, I get about 185ms.

    In the master settings, the connection interval (max/min) is 80 (80 * 1.25 = 100ms). That is, it looks like I send data at one interval and receive it during another (that's why I usually do not go beyond 2 * interval). The size of the data that I send is 32 bytes, but I tried 250 and the result is the same.

    I tried to scan the data exchange, tracking packets for the slave device. I see my data being sent from the master to the slave. After that, two Empty PDU packets follow (I don't understand why yet) and only after that, it seems, the slave sends confirmation of successful recording to the master. Judging by the analyzer time, the time delta is 100 ms. At the same time, the MD bit is not set to 1 in all packets. For some reason, the data in the measurements diverge.

    1. Why do the time calculations by the analyzer and inside the application diverge by almost two times? Is it possible that this time is spent on internal operations inside the stack?

    2. Why is the MD bit not set?

    3. Why are there some more packets between sending and confirmation?

    for packet master --> slave as example

  • Hi

    My question regarding the strange time measured in the app and via the sniffer MAYBE be explained by the real-time system and tasks. I tried to exclude some tasks (lower priority) and measured the time in the app again. I see that now the time changes in steps. That is, on the first transmission and acknowledgement I measured 190ms, on the next iteration 183ms, etc. up to 100ms, after which again about 190ms and so on down.

    If I return all the necessary tasks to the code, the time becomes stable 183ms.

    But this still does not explain why the stack does not set the MD bit?

  • Hello Nick,

    What is the MTU size both devices have agreed to work with?

    I have build this example where you can see how the MD bit is set. I am transmitting audio data with a connection interval of 11.25 ms and an MTU size of 125 bytes. During the first notifications we send the entire MTU size you can see the MD bit is not set and the entire packet is send during 1 connection event.

    After a while, you can see that the MD bit starts to being set and several packets are send over one connection interval. This is due to the fact that the ble stack queues other tasks that may have higher priority and preempt the notification task to execute and transmit the packet at the correct time so in this way the link layer attempts to maintain the throughput.

    BR,

    David.

  • Hi

    I have max pdu value (255) on both devices, i.e. max mtu value is 251. But I tried to send 30 bytes and 251 bytes packet but in either case MD bit is set.

  • Hello Nick,

    I would suggest queuing more GATT operations (notifications/writes) while keeping the same connection interval.

    BR,

    David.

  • Hello

    What does your phrase mean?

    "I would suggest queuing more GATT operations (notifications/writes) while keeping the same connection interval."

    I tried to simulate this by creating my own test event in the application and calling the multi_role_doGattWrite function twice in a row, which was taken from the multirole example and actually writes to another device. In practice, I see that the first write operation is successful right away, but when calling the second function, I need to make a number of attempts, because the stack returns the BLE pending bit. On the BLE analyzer from NRF, I also see that the message was transmitted, after 100 ms a confirmation came, after another 100 ms I sent the second message, after another 100 ms I received confirmation again. Thus, I still do not see that the MD bit is set and the packets are transmitted within one connection interval.

  • Hello Nick,

    By queuing more GATT operations I mean to do more GATT writes or GATT notifications back to back. In my application, what I am streaming audio from an analog microphone sampled at 16 KHz. I use the ADCBuff driver for this purpose like this:

    uint8_t Audio_enable(void)
    {
        ADCBuf_init();
        
        //Configure & Open ADC Buffer driver
        adcBufParams.callbackFxn = adcBufCallback;
        adcBufParams.recurrenceMode = ADCBuf_RECURRENCE_MODE_CONTINUOUS;
        adcBufParams.returnMode = ADCBuf_RETURN_MODE_CALLBACK;
        adcBufParams.samplingFrequency = 16000;
        adcBuf = ADCBuf_open(CONFIG_ADCBUF_0, &adcBufParams);
    
        //Configure the conversion struct
        continuousConversion.arg = NULL;
        continuousConversion.adcChannel = CONFIG_ADCBUF_0;
        continuousConversion.sampleBuffer = sampleBufferOne;
        continuousConversion.sampleBufferTwo = sampleBufferTwo;
        continuousConversion.samplesRequestedCount = ADCSAMPLESIZE;
    
        if (ADCBuf_convert(adcBuf, &continuousConversion, 1) != 0) {
                uint8_t retVal = 1;
        }
    
        return retVal;
    }

    Then, every time the DMA completes filling its buffer with ADC data it will generate an interrupt that will trigger  the callback function where we change from interrupt to task context using the BLEAppUtil_invokeFunctionNoData() function.

    void adcBufCallback(ADCBuf_Handle handle, ADCBuf_Conversion *conversion, void *completedADCBuffer, uint32_t completedChannel, int_fast16_t status)
    {
        memcpy(fullBuffer, completedADCBuffer, ADCSAMPLESIZE*sizeof(int16_t));
        BLEAppUtil_invokeFunctionNoData(Audio_sendBufferOverBLE);
    }

    Inside the Audio_sendBufferOverBLE function we send the notifications as follows:

    bStatus_t Audio_sendBufferOverBLE(uint16_t connHandle, void *data, uint16_t len)
    {
    
        bStatus_t ret = SUCCESS;
        attHandleValueNoti_t noti;
        linkDBInfo_t connInfo;
    
            uint16_t allocLen = len;
    
            noti.len = 0;
            noti.pValue = (uint8 *)GATT_bm_alloc( connHandle, ATT_HANDLE_VALUE_NOTI,
                                                  allocLen, &noti.len );
            notificationAllocationsAttempted++;
    
            // If allocation was successful, copy out data out of the buffer and send it
            if (noti.pValue)
            {
                notificationAllocationsSuccess++;
    
                memcpy(noti.pValue, (void *) ((uint8_t *) data), noti.len);
    
                lengthOfNotification = noti.len;
                payloadNotification = noti.pValue;
    
                noti.handle = (GATTServApp_FindAttr(audioProfileAttrTbl, GATT_NUM_ATTRS(audioProfileAttrTbl), audioProfileRead))->handle;
     
                ret = GATT_Notification( connHandle, &noti, FALSE );
                totalNotificationsAttempted++;
    
                // If unable to send the message, free allocated buffers and return
                if ( ret != SUCCESS )
                {
                    GATT_bm_free( (gattMsg_t *)&noti, ATT_HANDLE_VALUE_NOTI );
                    notificationsFailed++;
                }
                else
                {
                    notificationsSent++;
                }
            }
            else
            {
                // Unable to allocate space for a notification, return failure
                ret = bleMemAllocError;
                notificationAllocationsFailed++;
            }
    
        return ret;
    
    }

    The buffer length is 129 bytes which means that a buffer will be ready to be transmitted every 8 ms and my connection interval is 30 ms. We have more chances of having stack setting the MD bit to 1 if the connection interval is longer enough for the link layer to calculate that there is enough time to send multiple packets during the conn interval.

    BR,

    David.

  • Hello.

    Sorry for the long silence.

    I see that you are using notifications, while I use a write operation with confirmation. I tried to do a similar experiment with my application, when one device subscribes to notifications, and the second one starts sending a lot of data in a row. However, I still do not see any hints that the MD bit is set and I cannot send more than one packet per connection period. In any case, I can not solve this issue yet and do not understand how to make the device report that it can send more data within one connection period.