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.

BLE transfer bottleneck?

Other Parts Discussed in Thread: CC2541, CC2540, CC2430, SIMPLICITI, CC2510

Hi All,

I send larger amount of data through BLE (I know that it wasn't desinged for this purpose, but know I have to do this). Currently the connection parameters are:

- min. connection interval: 6 (7.25 msec)

- max connection itnerval: 6 (7.25 msec)

- slave latency: 0

Therefore the effective connection interval is 7.25 msec. I transfer data by using SimpleProfile_SetParameter, and the appropriate characteristic value is 16 bytes long. These suggest me that the transfer rate should be ~2 kB/s. However, I measured 70 B/s. There is some overhead caused by another operations, but it wouldn't cause this. My questions are:

- Are indications and notifications sent in the same connection event in which the actual data is transferred?

- Are confirmations sent back in the same connection event.

- The most important: what other bottlenecks can be there?

Thanks,

Gergo

  • Hi Gergo,

    Do you have a USB sniffer? The first thing to check is that you are really getting 7.25msec connect interval. By default the connection starts at 100ms and you have to issue a connection update from the central device to change the connect interval e.g. 

    GAPCentralRole_UpdateLink(BLEConnHandle,5,5,0,1000);

    You also need to ensure that the minimum connect interval on the peripheral side is less than equal to 5. I think this is  the limit so you may want to try higher values to start.

    In practice you might find a connect interval of 5 does not work or is hard to achieve if your system has other processing overhead (reading / writing flash etc). I found the link could be unreliable if the interval was too low.

    As far as I know notifications can occur up to 4 times per interval, but confirmations for indications happen in the following timeslot - someone may be able to correct me here.

  • Hi Michael,

    Thanks for your suggestion, the lack of GAPCentralRole_UpdateLink(...) was the (first) problem. I set the parameters on the GAP peripheral side and thought that it would take effect. Now the transfer rate is about 700 Bytes/s, which is ten times faster than previously, but still slower than 2,1 kBytes/s. Do you think that it can be obtained, or is this just a theoretical upper limit for the speed?

    Now I am planning to change the GATT profile, too, and hope that it will multiply the speed by 4.

    Best regards,

    Gergo

  • The theoretical speed for this settings ~10 Kbytes/sec, because in each connection event, up to 4 packets can be sent in each direction.

    However, some messages, such as gatt write charachteristic are sequential, meaning only one message is sent in each connection event, afte a confirmation is recieved.

    You can use GATT_WriteNoRsp API function to transfer data faster.

    You can check with the sniffer how many messages are transferred on each connection event.

  • Hi Sasha,

    As far as I know the GATT_WriteNoRsp is tipically used on the client side. However, usually I send larger amount of data in the opposite direction using SimpleProfile_SetParameter(...).

    Do you think that the speed can be also improved in this case?

    Thanks, 

    Gergo

  • Hi Gergo,

    There are two ways to send data from the Peripheral (server) to the Central (Client). These are the client initiated "Read" and the sever initiated "Notify" (or "Indicate"). Which method are you using?

    The SimpleProfile_SetParameter uses notifications (Notify) to send data back to the Central (client) side. I found that there was some additional work required to get notifications working on the client side.

    Notifications do not require confirmation from the client so they should be as fast as GATT_WriteNoRsp in the opposite direction. I think reads are slower as they require a read request to be sent and the corresponding response to be returned.

    Mike

  • Hi Mike,

    I use both indications and notifications, but for separate characteristic values. Indications are used at a charvalue which is for transmitting larger amount of data. The mechanism is the following:

    1. Server sets charv to the next packet, and sends indication.
    2. Client receives indication, and handles incoming data.
    3. Server receives confirmation for indication, GOTO 1.
    The confirmation is needed because the server has to know when to send the next packet. I am not sure if it's the most appropriate way of doing this, but don't have any better ideas.
    What do you think, is there any way in this case to improve the speed?
    Thanks,
    Gergo
  • Hi Gergo,

    I havent used Indications before, but when I was using Write I was getting similar speeds to you. I moved to just using WriteNoRsp and Notify to send data each way. My application only sends data one way at a time & the application layer handles error checking and retries. I can move data at about 2KB/s but I may look at trying to optimize this further when I get the time

  • Hi All,

    Have a look at our wiki on some throughput tests we've done: http://processors.wiki.ti.com/index.php/CC2540_Data_Throughput

    Br

  • Thanks Nick,

    In the link there is a comment: "When sending the notifications, a check is made to see if a buffer is available"

    Can you please explain this a little further? Does this use the return code from the GATT_Notification call?

  • Hi,

    So every 7ms, the function below is called 4 times,

    static void sendData(void )
    {
      
        static uint16 counter=0;
        uint8 burstData[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
        burstData[0] = (counter & 0xFF00)>>8;
        burstData[1] = (counter & 0xFF);  
        attHandleValueNoti_t nData;
        nData.len = 20;
        nData.handle = 20;  
        //skKeyPressed = *((uint8*)pValue);
        osal_memcpy( &nData.value, &burstData, 20 );
        // Send the Notification
        if (GATT_Notification( 0, &nData, FALSE )==SUCCESS)
        {
          counter++;
        }    
        if(counter >1000) 
        {
          osal_stop_timerEx(simpleBLEPeripheral_TaskID,SBP_BURST_EVT);
          counter = 0;
        }
    }

    So regardless of the connection interval, we are always trying to keep the buffer full, which is as you say checked by using the return command of the GATT_Notification().

    Br

  • Hi Nick,

    This speed sounds promising, I am trying this solution.

    I noticed that you call GATT_Notification() with 0 connHandle, could you explain this? I didn't find anything in the documentation regarding to this, and neither can imagine what a 0 connection handle means.

    One more question: you use one characteristic value (with the handle 20) to send data 4 times in every 7 ms. I thought that it wasn't possible, can you explain this thing, too?

    Thanks,

    Gergo

  • Hi,

    If you have several peripherals connected to a central device you would have internal pointers to each connection, called connection handles. The first connection is always enumerated as handle 0x0000, second connection 0x0001 and so on.

    7ms is just a OSAL timer which is suppose to be less than the connection interval, so we always keep the output buffer full and thereby maximizing the throughput. since the minimum connection we usually recommend is 10ms you could just as well test 9ms OSAL timer. But if you'd like to test minimum possible connection interval of 7.5ms, you'd like to push as many packets (maximum allowed is currently 4) you can between every connection event, so they might be sent during the actual connection interval.

    Again, the OSAL timer is just used to put packets in the output buffer, and the buffer is emptied upon the connection event.

    BR

  • Hi Nick,

    Thank you very much for your quick response, I've understood the concept, and I am changing my sources to work in this way.

    Just one note: I see that you call the function int the following way: osal_memcpy( &nData.value, &burstData, 20 );. However, nData.value and burstData are both arrays, and therefore the operator '&' isn't needed for getting their adresses (which are expected as actual parameters for memcopy). Or did I miss something?

    Best regards,

    Gergo

  • Hi Nick ,
    I want to transmit 40Kb of data from Slave(GATT Server ) to Master ( GATT Client ) , So which is the best method ? GATT_notification or GATT_WriteNoRSp or GATT_WriteChar Value ??
    If i want to transmit 40Kb of data , i want that data to be local in the function where i am sending the data ,but IAR workbench says some warning "XDATA is greater than 0x280 and threshold is set to 90 % "
    i.e
    uint8 data_to_send[4000]; 
    uint16 i;
    for (i =0 ;i < 4000;i++ )
     data_to_send [i]=0x00; // Am initializing all the data to be zero.
    From this buffer am planning to transmit 20 bytes at a time using GATT_notification or GATT_writeNoRsp or GATT_writecharval .
    Then i have reduced the array size to 524 ( 90 % of 0x280 ) from 4000, after that i does not throw any error but my code will struck and notifications are not working .
    Finally I found that it is working when i have my data_to_send buffer size as 490 (which is 76.5625 % of 0x280 ) ??? why this is happening ? 
    One more question is the xdata limit can be set in IAR Options-> General Options -> Stack /heap . Shall i increase the limit , will it affect my performance some where ?
    How this limit (0x280) is calculated ? am having 256KB flash but this xdata limit seems to be much lower ?
    Kindly help me to understand this ,
    Thanks in advance ,
    Senthi
  • Senthi,

    XDATA uses RAM (Not Flash), which the CC2540 and CC2541 has 8KB of. To get more info on this, have a look at section 4.2.4 in the Software Developers Guide (found here). 

    Br

  • I've implemented an adaptation of this code, as part of the SimpleBLEPeripheral project, but find that there is no RF traffic apparent in PacketSniffer when the GATT_Notification method is invoked.  Running with the default connection parameters for this project, I'm triggering the notifications by writing a value to a particular attribute in the SimpleGATTProfile service - the simpleProfile_WriteAttrCB method has a case added to the switch for this new attribute UUID.  When that case is executed the connection handle is saved and osal_start_timerEx is invoked with an event code that I've defined.  That event is seen in simpleBLEPeripheral_ProcessEvent, which reacts to that event code by invoking the timer again and calling a method that is very much like Nick's sendData and invokes GATT_Notification, using that connection handle saved earlier.  Here's what the code looks like in simpleBLEPeripheral_ProcessEvent:

    // Process the burst event
    if ( events & SBP_BURST_EVT )
    {
      // Restart the timer
      osal_start_timerEx( simpleBLEPeripheral_TaskID, SBP_BURST_EVT, SBP_BURST_EVT_PERIOD );

      uint8 uiStartingAddress = 0;
      SimpleProfile_NextNotification(uiStartingAddress);
      SimpleProfile_NextNotification(uiStartingAddress += 20);
      SimpleProfile_NextNotification(uiStartingAddress += 40);
      SimpleProfile_NextNotification(uiStartingAddress += 60);

      return (events ^ SBP_BURST_EVT);
    }

    And here's what the code looks like in SimpleProfile_NextNotification:

    bStatus_t SimpleProfile_NextNotification(uint8 uiStartAddress)
    {
      bStatus_t status = SUCCESS;
      uint8 burstData[20] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};

      // Plug in the starting address
      burstData[0] = ((uiStartAddress & 0xFF00)>>8);
      burstData[1] = (uiStartAddress & 0xFF);

      attHandleValueNoti_t nData;
      nData.len = 20;
      nData.handle = 20;
      osal_memcpy(&nData.value[0], &burstData[0], 20);

      // Send the notification
      bStatus_t bStatus = GATT_Notification(simpleProfile_connHandle, &nData, FALSE);
      if (bStatus == SUCCESS)
      loopCounter += 20;

      // When the counter gets to the number of bytes I want to have sent turn off the timer
      if (loopCounter >= TEST_DATA_TRANSFER_SIZE)
      {
          osal_stop_timerEx(main_TaskID, SBP_BURST_EVT);
          loopCounter = 0;
      }

      return status;
    }

     

  • Now it seems to be working.  Replaced the battery in the CC2540-MINI key fob.  The old one measured 2.6 V, new one is 3.2.  Now transmitting notifications.

  • Thanks Nick for your prompt response ! 

    Senthil

  • Hi Nick ,

    I have two questions 

    Question 1:

    I have used the GATT_Notification to send data (in multiples of 20 bytes ) from the server to the client .I have sent 1020 bytes of data using GATT_Notification .In the Btool I can able to see the data am sending from the server .

    But I am planning to update the name of the GATT_server from the client, for that am planning to send the data ( name )through GATT_notification from the client and on the server side i want to know how to read the data sent by GATT_notification ?? 

    Question 2:

    Am awaiting reply for this post from an BLE expert like you , I hope you will spend your precious time on the following post too  ? 

    http://e2e.ti.com/support/low_power_rf/f/538/p/188188/677491.aspx#677491

    thanks in advance ,

    Senthil kumar 

  • Hi Senthil,

    Senthil kumar103423 said:

    I have used the GATT_Notification to send data (in multiples of 20 bytes ) from the server to the client .I have sent 1020 bytes of data using GATT_Notification .In the Btool I can able to see the data am sending from the server .

    But I am planning to update the name of the GATT_server from the client, for that am planning to send the data ( name )through GATT_notification from the client and on the server side i want to know how to read the data sent by GATT_notification ?? 

    To send data from Client to Server you should use GATT_WriteNoRsp (Corresponds to GATT_Notification, with no ACK back)

    Br

  • Hi Nick ,

    Thanks for your reply.

    But my question is how to read the data sent by GATT_notification or GATT_writeNoRsp?? 

    Regards ,

    Senthil kumar

  • senthil kumar69729 said:

    Hi Nick ,

    Thanks for your reply.

    But my question is how to read the data sent by GATT_notification or GATT_writeNoRsp?? 

    Regards ,

    Senthil kumar

    Notifications will cause a GATT message event which will cause BLECentralProcessGATTMsg() to be called. The received data is handled there after checking pMsg -> method == ATT_HANDLE_VALUE_NOTI.

    A GATT write will cause simpleProfileChangeCB() to be called. Recieved data is handled there

  • Hello everybody!

    I am doing my bachelor thesis with BLE CC2540 performance & Power consumption measurements versus CC2510 simpliciTi & CC2430 TI-MAC

    I use successfully the GATT_WriteNoRsp from my CENTRAL/MASTER NODE as the fastest way to maximize datarate by using multiple GATT write packets in one connection event with the above:

           req.cmd = 1;//ATT_WRITE_REQ;

           uint8 m = multiple_packets; //var from 1 to 4

     do{

            gattRW_status = GATT_WriteNoRsp( simpleBLEConnHandle, &req);
                       if(gattRW_status == SUCCESS)
                  --m;
          }while(m != 0);

    My question are:

    1)Is it possible to use multiple packets with GATT_WriteCharValue() function? I tried GATT_WriteCharValue with req.cmd = 1; but it didn't work to me.

    I need this because there is a limitation of the GATT_WriteNoRsp() as it can't write data to a peripheral UUID characteristic table bigger than (23 - 15) = 8bytes max and this making it not the most efficient way to achieve the highest possible data-rate.

    That means GATT_WriteNoRsp() can multiply the data-rate by 4 but with a maximum 8bytes peripheral UUID characteristic table and without ACK feature.

    2)Is any other way to achieve maximum datarate from CENTRAL > PERIPHERAL?

    2.1)For instance why not to have the GATT server functions on the central device, so we can use GATT_Notification with multiple packets per connection event because we can't use ?

    3)From the tests that i did until now the maximum peripheral characteristic table size it worked to me and i could use it to R & W from the Central Node was 19bytes but some of you use 20bytes maximum packet size. How is that possible on GATT layer because i think its 19bytes the maximum GATT packet size?

    3.1) Bug? i have noticed (1.2.0 stack version) when i make a GATT char value REQ from the CENTRAL to read my Peripheral 19bytes characteristic UUID table the last 3 bytes were not successfully predefined, like this for example:

    uint8 simpleProfileChar5[SIMPLEPROFILE_CHAR5_LEN] = {  0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0 , 1 , 2};

    With the packet sniffer i was reading this data after a Central's GATT_WriteCharValue() request: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0 , 0, 0

    4) Is it possible not to use the GATT layer and go 1 layer lower and use ATT for data communication both ways more efficient with less overhead?

    I will be very happy to see any replies/answers of these CRITICAL questions for my thesis.

    Thank you in advance!

  • The code below looks problematic:

    do{

            gattRW_status = GATT_WriteNoRsp( simpleBLEConnHandle, &req);
                       if(gattRW_status == SUCCESS)
                  --m;
          }while(m != 0);

    If the write fails due to lack of buffer (i.e. gattRW_status is not SUCCESS), then the code may loop infinitely as the stack will not be processing / sending the data while inside the while loop. You need to modify it to exit the loop as soon as the write fails.

    AFAIK this is the fastest way to send data.

    To send data from the peripheral to the central you need to use notifications, this can send data at the same rate as WriteNoRsp.

  • Nick,

    What were the power consumption figures when you tested with 5.9 K Bytes per second data rate?

    Thanks,

    -Nagaraj

  • Hi Nagaraj,

    I did not do any power consumption measurement during any of my throughput tests, although it might be interesting to see. Combine the throughput demo and Application Note AN092 for the answer. I would not expect a low number though, since the TX is gonna be on a lot. Note that CC2541 will be a much more suitable for these kind of applications as the TX power is optimized to a much lower figure (~18mA compared to CC2540's ~24mA). Adding a DCDC would optimize even further (Down to ~14mA in TX)

    Keep us updated on the results, if you're digging into this. 

    Best Regards 

  • I send data packets received via UART from a microcontroller via notifications from a peripheral to a central device.
    The data packets (18 Bytes) are transmitted in 10ms intervalls from the mircocontroller to a CC2541.

    Attached you can find an image from the packet sniffer I took during over-the-air transmission.
    Could someone please tell me what every second packet is about. I mean P.nbr 103, 105, 107,... which are always sent after notifications.

  • Hi rider,

    Those are packets from the master device. As you know, connection events consist of interleaved RX/TX parts from the master and the slave.

    To separate the packets, you can look at the time difference - a high difference is likely a new connection event which always starts from the master, a low delay is switching between master and slave TX.

    Best regards,
    Aslak

  • Hi Aslak,

    Thanks for your answer. I always thought that notifications do not have any kind of acknowledgment.
    If I understood it correctly from your answer this is SN and NESN acknowledgment.

    The longer interval occours in my case always after three notifications. Does that mean that I just send three notifications per connection event? All the other max throughput tests state 4 notifications, so I should find out why I don't send more.

  • Hi Rider,

    rider said:
    I always thought that notifications do not have any kind of acknowledgment.

    That is partly correct, Notifications are not acknowledged to the Application. At Link Layer level, you will always have acknowledgement. 

    Best Regards

  • Hi,

    I would like to know how you achieve moving data at about 2KB/s ? Do you use notifications to do that? Thanks !

  • Hi Wanobo,

    Please post new questions in new posts.

    Yes, you use notifications to up the speed. This is because according to the BLE spec you can't send more WriteReq or ReadReq without having received either a WriteRsp or ReadRsp first, ensuring that you are limited to one characteristic read and write at best every second connection interval.

    With notifications, you can send multiple every connection event. Note that the packet transmission is still ensured on the link layer, but you will not get an application callback on success.

    Best regards,
    Aslak 

  • Dear Nick,

    I used CC2540 MiniDK keyfob with your data throughput firmware (processors.wiki.ti.com/.../CC2540_Data_Throughput) and BTool on the USB CC2540 Dongle and was only able to achieve around 2k bytes per second.  My connection parameters are:

    - min. connection interval: 6 (7.25 msec)

    - max connection itnerval: 6 (7.25 msec)

    - slave latency: 0

    Can you tell me how do you achieve 6k bytes per second data throughput? What is your receiving device?

    Thanks,

    Zubin

  • Hi,

    For optimal throughput, you should use something like 15ms-20ms interval, as this reduces the amount of channel switching and post-event processing overhead.

    You also need to enable Adv. Command -> HCI Extended -> HCIExt_OverlappedProcessing on BTool. Otherwise the devices are limited to 4, mostly 3 packets per event.

    This must also be enabled on the peripheral device via HCI_EXT_OverlappedProcessingCmd(HCI_EXT_ENABLE_OVERLAPPED_PROCESSING);

    Note that BTool will probably not be able to keep up with a very high throughput for long, as the display printout is pretty slow.

    Best regards,
    Aslak

  • Dear Aslak,

    From checking the packet sniffer, with 7.5 ms connection interval, the idle time is 5.913 ms. For 15 ms connection interval, the idle time is 11.378 ms. With longer connection interval, there are more packets transferred per connection event with overlapped processing enabled. 

    Do you have an equation to calculate the theoretical data throughput with a given connection interval?

    Thanks,

    Zubin