nRF52832 改變ATT_MTU提高藍牙數據發送速率(nRF5_SDK_14.2.0)

来源:https://www.cnblogs.com/jiangjiu/archive/2018/12/04/10063556.html
-Advertisement-
Play Games

nRF52832 作為一個低功耗藍牙晶元,其數據發送發送速率一直都偏低(高就不叫低功耗了^_^),作為初學者在網上找了很多資料,終於找到通過修改ATT_MTU來提升發送速率的方法,最快能達到8.2KB/s,現在就分享出來 ...


nRF52832 作為一個低功耗藍牙晶元,其數據發送發送速率一直都偏低(高就不叫低功耗了^_^),作為初學者在網上找了很多資料,終於找到通過修改ATT_MTU來提升發送速率的方法,最快能達到8.2KB/s,現在就分享出來

首先我用的協議棧是 nRF5_SDK_14.2.0 ,將\examples\ble_peripheral中的 ble_app_template 作為模板,以此進行修改

廢話不說,先上代碼,首先是定義

#define TIMER_INTERVAL        APP_TIMER_TICKS(29)      //定時器時間間隔

1
BLE_NUS_DEF(m_nus); //加入串口服務結構(修改) 2 BLE_CMD_DEF(m_cmd); //加入命令服務結構 3 APP_TIMER_DEF(m_timer1); //定時器1 4 5 uint8_t hr_data[250]; 6 uint8_t cmd_data; //接收的命令 7 bool send_state = false; //發送狀態,預設不發送 8 9 static uint16_t length = 244;

主函數基本沒修改,主要初始化了一組數據用來測試發送,加入了調度器,因為使用的定時器定時進行發送,而藍牙發送不好放在中斷里進行,定時器中斷就做一個接發送函數放入調度器的操作。定義了一個全局數組,用來存放發送的數據。

 1 int main(void)
 2 {
 3     bool erase_bonds;
 4 
 5     // Initialize.
 6     log_init();
 7     timers_init();
 8     buttons_leds_init(&erase_bonds);
 9     ble_stack_init();
10     gap_params_init();
11     gatt_init();
12     advertising_init();
13     services_init();
14     conn_params_init();
15     peer_manager_init();
16 
17     // Start execution.
18     NRF_LOG_INFO("Template example started.");
19 
20     advertising_start(erase_bonds);
21     
22     for(uint8_t i=0;i<250;i++) hr_data[i]=i;   //初始化數據包
23     SEGGER_RTT_printf(0, "\n");// 此處列印信息
24     
25     APP_SCHED_INIT(20, 2);       //初始化調度器  
26    
27     // Enter main loop.
28     for (;;)
29     {
30         app_sched_execute();      //調度
31 
32         if (NRF_LOG_PROCESS() == false)
33         {
34             power_manage();
35         }
36     }
37 }

先看定時器

1 static void timers_init(void)
2 {
3     // Initialize timer module.
4     uint32_t err_code = app_timer_init();
5     APP_ERROR_CHECK(err_code);
6 
7     err_code = app_timer_create(&m_timer1, APP_TIMER_MODE_REPEATED, timer_timeout_handler);
8     APP_ERROR_CHECK(err_code);  
9 }

定時器中斷服務函數

1 void timer_timeout_handler(void * p_context)
2 {
3     if ( send_state == true )
4     {
5         hr_data[0]++;
6         if(hr_data[0]>255) hr_data[0]=0;     //改變第一個位元組
7         app_sched_event_put(NULL, 0, ble_nus_send);  // 加入調度   
8     }       
9 }

藍牙發送函數

 1 void ble_nus_send(void)    
 2 {
 3     uint32_t err_code;
 4     do
 5     {
 6         err_code = ble_nus_string_send(&m_nus, hr_data, &length);   
 7         if ( (err_code != NRF_ERROR_INVALID_STATE) && (err_code != NRF_ERROR_BUSY) )
 8         {
 9             APP_ERROR_CHECK(err_code);
10         }
11     } while (err_code == NRF_ERROR_BUSY);
12 }

 

接下來是藍牙服務,這裡我使用了兩個自定義的服務,因為之前測試發送同一個串口服務進行收發的話,在連續接收數據時候,發送的命令會被堵塞,所以索性改了兩個服務,分別用來收發,不知道大家有碰到過這種情況沒,有的話歡迎一起交流交流。

 1 static void services_init(void)
 2 {
 3     uint32_t                err_code;
 4     ble_nus_init_t          nus_init;
 5     ble_cmd_init_t          cmd_init;
 6 
 7     /* 初始化串口服務  */
 8     memset(&nus_init, 0, sizeof(nus_init));
 9     err_code = ble_nus_init(&m_nus, &nus_init);
10     APP_ERROR_CHECK(err_code);
11 
12     /* 初始化自定義命令服務  */
13     memset(&cmd_init, 0, sizeof(cmd_init));
14     cmd_init.data_handler = cmd_data_handler;       //命令處理函數
15     err_code = ble_cmd_init(&m_cmd, &cmd_init);
16     APP_ERROR_CHECK(err_code);   
17 }

命令數據處理函數,定義了一個全局變數,用來指示發送狀態,根據接收的命令修改狀態,以及開關定時器。

 1 static void cmd_data_handler(ble_cmd_evt_t * p_evt)
 2 {
 3     ret_code_t err_code;
 4     SEGGER_RTT_printf(0, "Receive a command\n");// 此處列印信息
 5     cmd_data = p_evt->params.rx_data.p_data[0];   //接收1個位元組作為命令
 6         
 7     switch ( cmd_data )
 8     {
 9         case BLE_STOP_CMD :
10             send_state = false;    //停止發送
11             err_code = app_timer_stop(m_timer1);   //停止定時器
12             APP_ERROR_CHECK(err_code);  
13             break;
14         case BLE_SEND_CMD :
15             send_state = true;    //允許發送
16             err_code = app_timer_start(m_timer1, TIMER_INTERVAL, NULL);  //啟動定時器
17             APP_ERROR_CHECK(err_code);  
18             break;
19         default:
20             SEGGER_RTT_printf(0, "Invalid command\n");// 此處列印信息
21             break;
22     }    
23 }

整個程式的框架基本上就是這樣,通過藍牙接收的命令打開定時器,定時器中斷將發送函數加入調度,主迴圈輪轉到發送時進行數據發送

通過修改 length 改變發送包的大小

通過修改TIMER_INTERVAL  修改定時器時間

兩者配合改變數據發送速率。

 

到了這裡,就得修改ATT_MTU了,不然244個位元組根本發不了,打開sdk_config.h

 1 // <h> nRF_SoftDevice 
 2 
 3 //==========================================================
 4 // <e> NRF_SDH_BLE_ENABLED - nrf_sdh_ble - SoftDevice BLE event handler
 5 //==========================================================
 6 #ifndef NRF_SDH_BLE_ENABLED
 7 #define NRF_SDH_BLE_ENABLED 1
 8 #endif
 9 // <h> BLE Stack configuration - Stack configuration parameters
10 
11 // <i> These values are not used directly by the SoftDevice handler but the application or other libraries might depend on them.
12 // <i> Keep them up-to-date with the desired configuration.
13 //==========================================================
14 // <o> NRF_SDH_BLE_PERIPHERAL_LINK_COUNT - Maximum number of peripheral links. 
15 #ifndef NRF_SDH_BLE_PERIPHERAL_LINK_COUNT
16 #define NRF_SDH_BLE_PERIPHERAL_LINK_COUNT 1
17 #endif
18 
19 // <o> NRF_SDH_BLE_CENTRAL_LINK_COUNT - Maximum number of central links. 
20 #ifndef NRF_SDH_BLE_CENTRAL_LINK_COUNT
21 #define NRF_SDH_BLE_CENTRAL_LINK_COUNT 0
22 #endif
23 
24 // <o> NRF_SDH_BLE_TOTAL_LINK_COUNT - Maximum number of total concurrent connections using the default configuration. 
25 #ifndef NRF_SDH_BLE_TOTAL_LINK_COUNT
26 #define NRF_SDH_BLE_TOTAL_LINK_COUNT 1
27 #endif
28 
29 // <o> NRF_SDH_BLE_GAP_EVENT_LENGTH - The time set aside for this connection on every connection interval in 1.25 ms units. 
30 #ifndef NRF_SDH_BLE_GAP_EVENT_LENGTH
31 #define NRF_SDH_BLE_GAP_EVENT_LENGTH 8    //預設值3
32 #endif
33 
34 // <o> NRF_SDH_BLE_GATT_MAX_MTU_SIZE - Static maximum MTU size. 
35 #ifndef NRF_SDH_BLE_GATT_MAX_MTU_SIZE
36 #define NRF_SDH_BLE_GATT_MAX_MTU_SIZE  247       //預設值23
37 #endif
38 
39 // <o> NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE - Attribute Table size in bytes. The size must be a multiple of 4. 
40 #ifndef NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE
41 #define NRF_SDH_BLE_GATTS_ATTR_TAB_SIZE 1408
42 #endif
43 
44 // <o> NRF_SDH_BLE_VS_UUID_COUNT - The number of vendor-specific UUIDs. 
45 #ifndef NRF_SDH_BLE_VS_UUID_COUNT
46 #define NRF_SDH_BLE_VS_UUID_COUNT 1 //預設為0
47 #endif

這裡修改了3個地方,

NRF_SDH_BLE_GAP_EVENT_LENGTH    這個是每個間隔預留給連接的時間

NRF_SDH_BLE_GATT_MAX_MTU_SIZE   這個就是最大MTU了

NRF_SDH_BLE_VS_UUID_COUNT   這個因為我加了兩個自定義服務,所以也要改成1

 

接下來通過修改 length 和  TIMER_INTERVAL   編譯下載後來測試速率了

註意的是,因為修改過NRF_SDH_BLE_GATT_MAX_MTU_SIZE,所以RAM的地址會發生改變,打開sdk_config.h,修改

1 //==========================================================
2 // <e> NRF_LOG_ENABLED - Logging module for nRF5 SDK
3 //==========================================================
4 #ifndef NRF_LOG_ENABLED
5 #define NRF_LOG_ENABLED 1
6 #endif
1 //==========================================================
2 // <e> NRF_LOG_BACKEND_RTT_ENABLED - nrf_log_backend_rtt - Log RTT backend
3 //==========================================================
4 #ifndef NRF_LOG_BACKEND_RTT_ENABLED
5 #define NRF_LOG_BACKEND_RTT_ENABLED 1
6 #endif

這樣就可以通過RTT列印的信息來調整RAM的地址和大小了

再次編譯,下載,沒問題後,在andorid手機上使用 ‘nRF Connect’ 進行調試

 

 

附上我的測試圖片

數據顯示

這裡使用的就是每29ms發送一次,每次發送244位元組,通過計算,(1000/29)*244 = 8413byte  約  8.2KB/s  ,這是我目前測出的最大傳輸速率,但是不停穩定,超出一米的距離基本就會失去連接,另外連接間隔我也有修改

1 #define MIN_CONN_INTERVAL               MSEC_TO_UNITS(8, UNIT_1_25_MS)      
2 #define MAX_CONN_INTERVAL               MSEC_TO_UNITS(12, UNIT_1_25_MS)      
3 #define SLAVE_LATENCY                   0                                   
4 #define CONN_SUP_TIMEOUT                MSEC_TO_UNITS(4000, UNIT_10_MS)      

因為初次接觸藍牙,這方面不是太懂,可能寫的不是太好,歡迎指正,或者有測出更高速率的朋友也可以一起交流下

 

對於改變MTU為什麼會提高發送速率,我抓包分析了一下

從機接收到命令開始發數據

第一個包,可以看出數據只發送到0x13,也就是20個位元組

第二個包,從0x14 發送到了0x2e, 也就是27個位元組

第三個包,從0x2f 到0x49, 也是27個位元組

也就是說,通過修改MTU後,每次發送從第二個包開始都可以達到每個包27個位元組,這樣一來自然就會比之前限定的每次只能發20個位元組要快一點了。

 

 先寫到這裡吧,如果有什麼寫的不對的,歡迎大家指正,後面還需要測試穩定性,距離,功耗,還好實際上也不需要用到8K的傳輸速率,還需要深入的學習BLE,加油!


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • Linux中的ps命令是Process Status的縮寫。ps命令用來列出系統中當前運行的那些進程。ps命令列出的是當前那些進程的快照,就是執行ps命令的那個時刻的那些進程,如果想要動態的顯示進程信息,就可以使用top命令。 要對進程進行監測和控制,首先必須要瞭解當前進程的情況,也就是需要查看當前 ...
  • 1.kangle官方腳本 linux下easypanel版本安裝及升級(集成了kangle web 伺服器和mysql,僅支持centos 5和centos 6)執行下麵的命令即可,安裝程式將自動安裝或者升級。 yum -y install wget;wget http://download.kan ...
  • 與jenkins構建項目記錄1不同的是通過tag拉去對應版本代碼 1、先安裝創建(git parameter) 2、general設置 name可任意命名,下麵源碼管理設置時變數會引用到。 3、源碼管理 ${tag}中tag名必須與上面Git Parameter中命名的name一致 4、設置完成進行 ...
  • 一、Spring Boot 入門 1、Hello World探究 1、POM文件 1、父項目 Spring Boot 版本仲裁中心: 以後我們導入依賴預設是不需要寫版本:(沒有在dependencies裡面管理的依賴自然需要寫版本號) 2、啟動器 spring-boot-starter-web: S ...
  • 有多種方式安裝jenkins 方式一、通過Tomcat發佈 安裝tomcat和jdk、maven1、安裝jdk環境jdk下載地址:https://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html( ...
  • ``` # 開啟防火牆 systemctl start firewalld.service # 防火牆開機啟動 systemctl enable firewalld.service # 關閉防火牆 systemctl stop firewalld.service # 查看防火牆狀態 firewall... ...
  • 本文最早發佈於我的51CTO博客,目前已遷移至博客園。 參考: "port的英文維基百科" ) 簡介 MAC地址定義了數據包的下一跳地址。 IP地址定義了數據包最終應該傳輸到哪台電腦上。 而埠(port)則定義了數據包中的數據應該由電腦上的哪個進程來接收。 埠是一種邏輯上的概念,用來識別一個 ...
  • 1. 部署cadvisor容器,用來收集host上的容器信息,該容器部署在需要收集容器信息的每一個主機上部署; docker run -v /:/rootfs:ro -v /var/run:/var/run:rw -v /sys:/sys:ro -v /var/lib/docker:/var/lib ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...