{"id":4281,"date":"2015-12-24T13:24:46","date_gmt":"2015-12-24T21:24:46","guid":{"rendered":"http:\/\/www.corbinstreehouse.com\/blog\/?p=4281"},"modified":"2018-11-03T13:35:39","modified_gmt":"2018-11-03T20:35:39","slug":"tricks-for-fast-bluetooth-le-data-transfers","status":"publish","type":"post","link":"https:\/\/www.corbinstreehouse.com\/blog\/2015\/12\/tricks-for-fast-bluetooth-le-data-transfers\/","title":{"rendered":"Arduino: Tricks for fast Bluetooth LE data transfers"},"content":{"rendered":"<p id=\"top\" \/>\n<p>I\u2019ve been using the Adafruit LE friend to do some data transfer tests. With the basic setup, I was sending 1 kB of data in about 9.5 to 10 seconds. That is horribly slow! About 100 bytes per second.\u00a0<\/p>\n<p>I did some research and found a couple of good links.\u00a0<a href=\"https:\/\/developer.mbed.org\/forum\/team-63-Bluetooth-Low-Energy-community\/topic\/5082\/\">This link<\/a>\u00a0about BLE\u00a0discusses using the Nordic nRF chip and mbed to send data to a peripheral (i.e.: from the computer to the device).\u00a0<a href=\"https:\/\/devzone.nordicsemi.com\/question\/1741\/dealing-large-data-packets-through-ble\/\">This link about sending data from the peripheral<\/a>\u00a0has some good tips when going in the other direction.\u00a0\u00a0<\/p>\n<p>I gave up trying to get the Adafruit LE friend to be fast and started working with the <a href=\"http:\/\/redbearlab.com\/blenano\/\">RedBearLab BLE nano<\/a> (v1.5). It has been tough getting Xcode setup to compile code right, and \u00a0I ended up using \u201cembedXcode\u201d as a starting point, and the Arduino plugin for RedBearLab. I\u2019d rather just use mbed, but the web experience is terrible, and I must use Xcode. However, I couldn\u2019t get their scripts to compile..mainly do to case sensitivity issues (BLE.h and ble.h both exist, and my drive partition is insensitive).\u00a0<\/p>\n<p>For the Nano, the bottom line was I had to edit the projectconfig.h file and use these settings:<\/p>\n<p style=\"margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: #008400;\"><span style=\"font-variant-ligatures: no-common-ligatures; color: #78492a;\">\u00a0 \u00a0\u00a0#define CFG_GAP_CONNECTION_MIN_INTERVAL_MS \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #272ad8;\">20<\/span>\/\/ corbin!! fYI, 40 worked. 50 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/**&lt; Minimum acceptable connection interval *\/<\/p>\n<p style=\"margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: #008400;\"><span style=\"font-variant-ligatures: no-common-ligatures; color: #78492a;\">\u00a0 \u00a0 #define CFG_GAP_CONNECTION_MAX_INTERVAL_MS\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #272ad8;\">40<\/span>\/\/ corbin!! 500 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/**&lt; Maximum acceptable connection interval *\/<\/p>\n<p style=\"margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: #008400;\"><span style=\"font-variant-ligatures: no-common-ligatures; color: #78492a;\">\u00a0 \u00a0 #define CFG_GAP_CONNECTION_SUPERVISION_TIMEOUT_MS\u00a0 <\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #272ad8;\">500<\/span>\/\/ corbin!! 4000 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \/**&lt; Connection supervisory timeout *\/<\/p>\n<p>The key part was not just the min interval, but the max; 500 was too large, and there is no API to control it (I can add some in for myself). 40 is required. I didn\u2019t test lower\u2026but lower might be better too. Too low, and the Mac (or iOS device) might not be able to keep up.<\/p>\n<p>\u00a0When sending from an OS X app, I discovered I could get the most data by doing a \u201cwrite without response\u201d:<\/p>\n<p style=\"margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0<span style=\"font-variant-ligatures: no-common-ligatures; color: #4f8187;\">peripheral<\/span>.<span style=\"font-variant-ligatures: no-common-ligatures; color: #3d1d81;\">writeValue<\/span>(subData, forCharacteristic: <span style=\"font-variant-ligatures: no-common-ligatures; color: #4f8187;\">_uartTransmitCharacteristic<\/span>!, type: <span style=\"font-variant-ligatures: no-common-ligatures; color: #703daa;\">CBCharacteristicWriteType<\/span>.<span style=\"font-variant-ligatures: no-common-ligatures; color: #3d1d81;\">WithoutResponse<\/span>)<\/p>\n<p>\u00a0And batching the 20 byte data chunks every 2 nano seconds:<\/p>\n<p style=\"margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: #008400;\"><span style=\"font-variant-ligatures: no-common-ligatures; color: #000000;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0\u00a0<\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #bb2ca2;\">let<\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #000000;\"> delay: <\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #703daa;\">Int64<\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #000000;\"> = <\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #703daa;\">Int64<\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #000000;\">(<\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #703daa;\">NSEC_PER_MSEC<\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #000000;\">)*<\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #272ad8;\">2<\/span>\/\/ At about 4ns, it starts to get slower on the recieving end.<\/p>\n<p style=\"margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: #3d1d81;\"><span style=\"font-variant-ligatures: no-common-ligatures; color: #000000;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <\/span>dispatch_after<span style=\"font-variant-ligatures: no-common-ligatures; color: #000000;\">(<\/span>dispatch_time<span style=\"font-variant-ligatures: no-common-ligatures; color: #000000;\">(<\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #703daa;\">DISPATCH_TIME_NOW<\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #000000;\">, delay), <\/span>dispatch_get_main_queue<span style=\"font-variant-ligatures: no-common-ligatures; color: #000000;\">(), { () -&gt; <\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #703daa;\">Void<\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #bb2ca2;\">in<\/span><\/p>\n<p style=\"margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 <span style=\"font-variant-ligatures: no-common-ligatures; color: #bb2ca2;\">self<\/span>.<span style=\"font-variant-ligatures: no-common-ligatures; color: #31595d;\">sendMoreData<\/span>();<\/p>\n<p style=\"margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo;\">\u00a0 \u00a0 \u00a0 \u00a0 \u00a0 \u00a0 })<\/p>\n<p>\u00a0Anything longer and it would be slower. This doesn\u2019t make sense\u2026as the BLE stack can\u2019t really be faster than 7.5ms, but I\u2019m sure the computer is doing a bit of processing and handling the BLE stack separately from my process. I have yet to test the code on iOS\u2026it might need a larger delay, and might be slower. That is fine\u2026 my goal is to send files from the desktop to the device, and potentially vice-versa.\u00a0<\/p>\n<p>With my above changes, a raw read of 1024 bytes (1kB) took 185ms; and it was fairly consistent. That\u2019s about <strong>5.4kB<\/strong> per second! I could probably improve it 6x more by using multiple characteristics to write at simultaneously, but I\u2019ll test that later. 5.4kB\/second is fast enough to start with.<\/p>\n<p>\u00a0<\/p>\n<p>\u00a0<\/p>\n<p>I was curious if the <strong>Adafruit Bluefruit LE friend<\/strong> could be made faster. I know the GAP interval can be changed, so I made it match with:<\/p>\n<p style=\"margin: 0px; font-size: 11px; line-height: normal; font-family: Menlo; color: #d12f1b;\"><span style=\"font-variant-ligatures: no-common-ligatures; color: #000000;\">\u00a0 \u00a0 \u00a0 \u00a0\u00a0<\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #4f8187;\">m_ble<\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #000000;\">.<\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #31595d;\">sendCommandCheckOK<\/span><span style=\"font-variant-ligatures: no-common-ligatures; color: #000000;\">(<\/span>&#8220;AT+GAPINTERVALS=20,40,25,30&#8221;<span style=\"font-variant-ligatures: no-common-ligatures; color: #000000;\">);<\/span><\/p>\n<div>\u00a0<\/div>\n<div>Indeed, this did help! the UART service can read 1024 bytes in 3.5 seconds, instead of 10 seconds, or about <strong>0.3kB<\/strong>\/second versus 0.1kB\/second. \u00a0This is reading the data from a teensy, which means the Adafruit BLE friend first reads the data, and then transfers it to the teensy over SPI. That protocol is somewhat slow, and it has a 16 byte payload (it really needs to be 20 bytes to transfer complete packets from the BLE).\u00a0<\/div>\n<div>\u00a0<\/div>\n<div>Of course\u2026my test is ignoring the SPI transfer from the nRF chip to the teensy chip. This is also going to take some time, and might slow my numbers down a bit. I also need to learn how to write SPI code and make my own mechanism for transferring data. Lots more coding to do&#8230;<\/div>\n","protected":false},"excerpt":{"rendered":"<p>I\u2019ve been using the Adafruit LE friend to do some data transfer tests. With the basic setup, I was sending 1 kB of data in about 9.5 to 10 seconds. That is horribly slow! About&#8230; <a class=\"read-more\" href=\"https:\/\/www.corbinstreehouse.com\/blog\/2015\/12\/tricks-for-fast-bluetooth-le-data-transfers\/\">[read more]<\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[6,86],"tags":[126,127,129,72],"class_list":["post-4281","post","type-post","status-publish","format-standard","hentry","category-cocoa","category-coding","tag-arduino","tag-bluetooth","tag-c","tag-programming"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/posts\/4281","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/comments?post=4281"}],"version-history":[{"count":2,"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/posts\/4281\/revisions"}],"predecessor-version":[{"id":5321,"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/posts\/4281\/revisions\/5321"}],"wp:attachment":[{"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/media?parent=4281"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/categories?post=4281"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.corbinstreehouse.com\/blog\/wp-json\/wp\/v2\/tags?post=4281"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}