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?
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.
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.
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.
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.
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?
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.
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:
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
Have a look at our wiki on some throughput tests we've done: http://processors.wiki.ti.com/index.php/CC2540_Data_Throughput
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?
So every 7ms, the function below is called 4 times,
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().
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?
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.
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?
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).
All content and materials on this site are provided "as is". TI and its respective suppliers and providers of content make no representations about the suitability of these materials for any purpose and disclaim all warranties and conditions with regard to these materials, including but not limited to all implied warranties and conditions of merchantability, fitness for a particular purpose, title and non-infringement of any third party intellectual property right. TI and its respective suppliers and providers of content make no representations about the suitability of these materials for any purpose and disclaim all warranties and conditions with respect to these materials. No license, either express or implied, by estoppel or otherwise, is granted by TI. Use of the information on this site may require a license from a third party, or a license from TI.
TI is a global semiconductor design and manufacturing company. Innovate with 100,000+ analog ICs andembedded processors, along with software, tools and the industry’s largest sales/support staff.