[藍牙] 6、基於nRF51822的藍牙心率計工程消息流Log分析(詳細)


 

 

開機初始化Log


 

Log編號                       函數名                             所在文件名

 1 000001:                                          main                                                        ..\main.c
 2 000002:                                   timers_init                                                        ..\main.c
 3 000003:                                   gpiote_init                                                        ..\main.c
 4 000004:                                  buttons_init                                                        ..\main.c
 5 000005:                                ble_stack_init                                                        ..\main.c
 6 000006:                             bond_manager_init                                                        ..\main.c
 7 000007:                               gap_params_init                                                        ..\main.c
 8 000008:                              advertising_init                                                        ..\main.c
 9 000009:                                 services_init                                                        ..\main.c
10 000010: ble_hrs_init ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c 11 000011: heart_rate_measurement_char_add ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c 12 000012: hrm_encode ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c 13 000013: body_sensor_location_char_add ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c 14 000014: ble_bas_init ..\..\..\..\..\Source\ble\ble_services\ble_bas.c 15 000015: battery_level_char_add ..\..\..\..\..\Source\ble\ble_services\ble_bas.c 16 000016: ble_dis_init ..\..\..\..\..\Source\ble\ble_services\ble_dis.c 17 000017: char_add ..\..\..\..\..\Source\ble\ble_services\ble_dis.c 18 000018:                              conn_params_init                                                        ..\main.c
19 000019:                               sec_params_init                                                        ..\main.c
20 000020:                             advertising_start                                                        ..\main.c
21 000021: led_start ..\led.c 22 000022: ppi_init ..\led.c 23 000023: timer1_init ..\led.c 24 000024:                                   gpiote_init                                                         ..\led.c

下面是main函數對應的初始化函數:

 

上面Log中第九行server_init中會跳轉到hrs,bas,dis中進行相關操作,我們重點分析——

【第一段黃色地帶講解】

 1 uint32_t ble_hrs_init(ble_hrs_t * p_hrs, const ble_hrs_init_t * p_hrs_init)
 2 {
 3     LOG(__FUNCTION__);
 4     uint32_t   err_code;
 5     ble_uuid_t ble_uuid;
 6 
 7     // Initialize service structure
 8     p_hrs->evt_handler                 = p_hrs_init->evt_handler;
 9     p_hrs->is_sensor_contact_supported = p_hrs_init->is_sensor_contact_supported;
10     p_hrs->conn_handle                 = BLE_CONN_HANDLE_INVALID;
11     p_hrs->is_sensor_contact_detected  = false;
12     p_hrs->rr_interval_count           = 0;

這一部分主要是初始化p_hrs結構體,p_hrs_init的內容在server_init賦值的,如下:


其中第433行設置hrs的事件句柄,這樣可以在其中監聽hrs的相關事件,如下:
這個和ble_evt_dispatch傳送BLE協議棧事件給hrs模塊的ble_hrs_on_ble_evt函數稍有不同
在on_xxxx中主要處理on_connect、disconnect和write事件
注:其中case的兩個事件類型是自定義的枚舉型

上面437~443是設置hrs的attribute的可讀可寫等屬性的
其中hrs_hrm_attr_md是Initial security level for heart rate service measurement attribute
其中hrs_bsl_attr_md是Initial security level for body sensor location attribute

上面89、90行的紅色部分的結構體為:
可見,cccd比無cccd的多了個cccd_write_perm
這里的cccd為:客戶端特性配置描述符(Client Characteristic Configuration Descriptor,CCCD)
這個帶cccd的結構體在SDK描述如下:
Security settings structure.
This structure contains the security options needed during initialization of the service. It can be used when the charecteristics contains cccd. 
我的理解是attribute中的屬性有兩種:屬性值或描述符。如果想支持通知或指示notify,那么就得選用cccd的這種~
注:關於BLE的一些名詞[1] profile\service\characteristic\uuid關系[2]


綜上:HRS服務配置了兩個characteristic,他們分別是heart rate measurement characteristic(簡稱HRMC)和body sensor location characteristic。其中HRMC具有notify功能,不具有讀寫功能,即:他的值不能被其他藍牙設備請求讀寫,只能主動發送給設備(稱為notify,類似於web中的推送~)

14     // Add service
15     BLE_UUID_BLE_ASSIGN(ble_uuid, BLE_UUID_HEART_RATE_SERVICE);
16
17
err_code = sd_ble_gatts_service_add(BLE_GATTS_SRVC_TYPE_PRIMARY, &ble_uuid, &p_hrs->service_handle);
18 if (err_code != NRF_SUCCESS)
19 {
20 return err_code;
21 }

其中
BLE_UUID_BLE_ASSIGN是一個給uuid初始賦值的宏定義:

而sd_ble_gatts_service_add負責:Add a service declaration to the local server ATT table.

談到sd_ble_gatts_service_add,就必須談一下Function packet format:

綜上:上面14~21行代碼就實現了將BLE_UUID_HEART_RATE_SERVICE加入到藍牙協議棧中~

23
// Add heart rate measurement characteristic 24 err_code = heart_rate_measurement_char_add(p_hrs, p_hrs_init); 25 if (err_code != NRF_SUCCESS) 26 { 27 return err_code; 28 }

這里heart_rate_measurement_char_add是向上面添加的服務中添加characteristic的,非API函數,具體拆分講解如下:
該函數前面都是一些賦值操作,最后調用了一個API函數:

如下,該API實現將一個屬性聲明,一個屬性值聲明和一個可選屬性描述符聲明添加進ATT表。
這個添加的屬性會添加進最近添加的服務中,當然perminssions需要統一等情況要注意~
該函數的參數情況如下:
· 第一個service_handle指向該屬性所在的服務
· 第二個p_char_md是屬性的原數據
· 第三個p_attr_char_value是指向attribute結構體所對應的屬性值
· 第四個p_handles是指向指定句柄所存儲的結構體


在調用上面API之前,首先對p_char_md屬性原數據進行配置attribute


接着配置p_attr_char_value進行配置:


其實經過前面的這些配置之后,再調用API函數sd_ble_gatts_characteristic_add就會形成一個下面格式的包:
注:sd_ble_gatts_characteristic_add 功能的包格式[3]


30
if (p_hrs_init->p_body_sensor_location != NULL) 31 { 32 // Add body sensor location characteristic 33 err_code = body_sensor_location_char_add(p_hrs, p_hrs_init); 34 if (err_code != NRF_SUCCESS) 35 { 36 return err_code; 37 } 38 }
最后判斷是否要把body sensor location characteristic加入到hrs服務中~
40 return NRF_SUCCESS; 41 }

 

連接藍牙后的效果


 

 1 000025:                              ble_evt_dispatch                                                        ..\main.c
 2 000026: ble_hrs_on_ble_evt ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c  3 000027: on_connect ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c  4 000028: ble_bas_on_ble_evt ..\..\..\..\..\Source\ble\ble_services\ble_bas.c  5 000029: on_connect ..\..\..\..\..\Source\ble\ble_services\ble_bas.c  6 000030:                                    on_ble_evt                                                        ..\main.c
 7 000031: led_stop ..\led.c  8 000032: application_timers_start ..\main.c  9 000033:               heart_rate_meas_timeout_handler                                                        ..\main.c
10 000034: ble_hrs_heart_rate_measurement_send ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c 11 000035: hrm_encode ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c 12 000036:               heart_rate_meas_timeout_handler                                                        ..\main.c
13 000037: ble_hrs_heart_rate_measurement_send ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c 14 000038: hrm_encode ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c 15 000039:            battery_level_meas_timeout_handler                                                        ..\main.c
16 000040: battery_start ..\battery.c 17 000041: ADC_IRQHandler ..\battery.c 18 000042: ble_bas_battery_level_update ..\..\..\..\..\Source\ble\ble_services\ble_bas.c 19 000043:               heart_rate_meas_timeout_handler                                                        ..\main.c
20 000044:           ble_hrs_heart_rate_measurement_send                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
21 000045:                                    hrm_encode                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
22 000046:               heart_rate_meas_timeout_handler                                                        ..\main.c
23 000047:           ble_hrs_heart_rate_measurement_send                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
the same...

 

如Log第25行手機連接上nRF51822之后,BLE協議棧把事件通過ble_evt_dispatch分配到每個模

 1 /**@brief Function for dispatching a BLE stack event to all modules with a BLE stack event handler.
 2  *
 3  * @details This function is called from the BLE Stack event interrupt handler after a BLE stack
 4  *          event has been received.
 5  *
 6  * @param[in]   p_ble_evt   Bluetooth stack event.
 7  */
 8 static void ble_evt_dispatch(ble_evt_t * p_ble_evt)
 9 {
10     LOG(__FUNCTION__);
11     ble_bondmngr_on_ble_evt(p_ble_evt);
12     ble_hrs_on_ble_evt(&m_hrs, p_ble_evt);

先進入ble_hrs_on_ble_evt,進而解析事件執行on_connect,如下:eto


13     ble_bas_on_ble_evt(&bas, p_ble_evt);
14     ble_conn_params_on_ble_evt(p_ble_evt);
15  on_ble_evt(p_ble_evt);

在on_ble_evt中當檢測到connected事件,
則關閉標志廣播進行中的燈的定時器,
start在timer初始化中設置的hrs和bas定時器,也因此在Log中接着會輪流周期性觸發hrs和bas的timeout句柄


16 }

 

 

Heart Rate Measurement啟動或終止notify監聽時的Log 


 

1 002044:                              ble_evt_dispatch                                                        ..\main.c
2 002045: ble_hrs_on_ble_evt ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c 3 002046: on_write ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c 4 002047: on_hrm_cccd_write ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c 5 002048:                             hrs_event_handler                                                        ..\main.c
6 002049:                            ble_bas_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
7 002050:                                      on_write                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c

當上位機(手機端)點擊start notify時,其實有一個交互過程的!

如上面的Log:

1、首先BLE協議棧把該事件通過dispatch分派給模塊ble_hrs_on_ble_evt 

2、ble_hrs_on_ble_evt 對事件解析發現是BLE_GATTS_EVT_WRITE,並調用on_write

3、從on_write又進入on_hrm_cccd_write,在其中判斷並發送相應的evt_handler事件消息

4、該消息會發送到其接受函數hrs_event_handler中,並根據情況設置notify

 

Heart Rate Measurement啟動監聽中的Log


 

 1 000895:               heart_rate_meas_timeout_handler                                                        ..\main.c
 2 000896:           ble_hrs_heart_rate_measurement_send                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
 3 000897:                                    hrm_encode                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
 4 000898: ble_evt_dispatch ..\main.c  5 000899:                            ble_hrs_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
 6 000900:                            ble_bas_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
 7 000901:                                    on_ble_evt                                                        ..\main.c
 8 000902:               heart_rate_meas_timeout_handler                                                        ..\main.c
 9 000903:           ble_hrs_heart_rate_measurement_send                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
10 000904:                                    hrm_encode                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
11 000905:            battery_level_meas_timeout_handler                                                        ..\main.c
12 000906:                                 battery_start                                                     ..\battery.c
13 000907:                                ADC_IRQHandler                                                     ..\battery.c
14 000908:                  ble_bas_battery_level_update                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
15 000909: ble_evt_dispatch ..\main.c 16 000910:                            ble_hrs_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
17 000911:                            ble_bas_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
18 000912:                                    on_ble_evt                                                        ..\main.c

和連接藍牙后的Log的12-18行對比,由於這里開啟了notify,所以出現了些許不一樣的地方:

即:紅色字體的部分!

根據Log猜測(暫時沒有器材,無法調試,到早上試,再給出最終結果~):

1、可能是因為上位機使能了notification,執行了紅線那句話

2、之前由於沒有執行這句話使ble_hrs_heart_rate_measurement_send和ble_bas_battery_level_update中的sd_ble_gatts_hvx函數沒有被執行,而一旦紅線那句話執行后sd_ble_gatts_hvx將被周期性地執行:

3、而每次sd_ble_gatts_hvx函數都會觸發ble協議棧產生相應的消息,通過ble_evt_dispatch派送到各個模塊,最終被on_ble_evt解析到BLE_GAP_EVT_SEC_PARAMS_REQUEST消息並執行相關操作,把消息發送出去:

 

 

藍牙斷開時的Log


 

1 008913:                              ble_evt_dispatch                                                        ..\main.c
2 008914:                            ble_hrs_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
3 008915:                                 on_disconnect                 ..\..\..\..\..\Source\ble\ble_services\ble_hrs.c
4 008916:                            ble_bas_on_ble_evt                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
5 008917:                                 on_disconnect                 ..\..\..\..\..\Source\ble\ble_services\ble_bas.c
6 008918:                                    on_ble_evt                                                        ..\main.c
7 008919:                         system_off_mode_enter                                                        ..\main.c

斷開藍牙比較簡單,消息從協議棧經dispatch分發到hrs、bas和on_ble_evt分別做相應處理! 

 

 

注:


[1]、關於BLE的一些名詞:

每個attribute屬性被UUID(通用唯一標識符)唯一標識 ,UUID是標准128-bit格式的ID用來唯一標識信息。attributes 被 ATT 格式化characteristics和services形式進行傳送。
· 特征(Characteristics)— 一個characteristics包含一個單獨的value值和0 –n個用來描述characteristic 值(value)的descriptors。一個characteristics可以被認為是一種類型的,類似於一個類。
· 描述符(descriptor)   descriptor是被定義的attributes,用來描述一個characteristic的值。例如,一個descriptor可以指定一個人類可讀的描述中,在可接受的范圍里characteristic值,或者是測量單位,用來明確characteristic的值。
· 服務(service)    —service是characteristic的集合。例如,你可以有一個所謂的“Heart RateMonitor”service,其中包括characteristic,如“heart rate measurement ”。你可以在 bluetooth.org找到關於一系列基於GATT的profile和service。

  

他們關系為:藍牙設備可以包括多個Profile,一個Profile中有多個Service,一個Service中有多個Characteristic,一個Characteristic中包括一個value和多個Descriptor。

[2]、profile\service\characteristic\uuid關系:

1、profile  
  profile可以理解為一種規范,一個標准的通信協議,它存在於從機中。藍牙組織規定了一些標准的profile,例如 HID OVER GATT ,防丟器 ,心率計等。每個profile中會包含多個service,每個service代表從機的一種能力。
2、service
  service可以理解為一個服務,在ble從機中,通過有多個服務,例如電量信息服務、系統信息服務等,每個service中又包含多個characteristic特征值。每個具體的characteristic特征值才是ble通信的主題。比如當前的電量是80%,所以會通過電量的characteristic特征值存在從機的profile里,這樣主機就可以通過這個characteristic來讀取80%這個數據
3、characteristic
  characteristic特征值,ble主從機的通信均是通過characteristic來實現,可以理解為一個標簽,通過這個標簽可以獲取或者寫入想要的內容。
4、UUID
  UUID,統一識別碼,我們剛才提到的service和characteristic,都需要一個唯一的uuid來標識
 
每個從機都會有一個叫做profile的東西存在,不管是上面的自定義的simpleprofile,還是標准的防丟器profile,他們都是由一些列service組成,然后每個service又包含了多個characteristic,主機和從機之間的通信,均是通過characteristic來實現。
實際產品中,每個藍牙4.0的設備都是通過服務和特征來展示自己的,服務和特征都是用UUID來唯一標識的。一個設備必然包含一個或多個服務,每個服務下面又包含若干個特征。特征是與外界交互的最小單位。藍牙設備硬件廠商通常都會提供他們的設備里面各個服務(service)和特征(characteristics)的功能,比如哪些是用來交互(讀寫),哪些可獲取模塊信息(只讀)等。比如說,一台藍牙4.0設備,用特征A來描述自己的出廠信息,用特征B來與收發數據等。
 
[3]、sd_ble_gatts_characteristic_add 功能的包格式:
 
1、Frame format encoding of the sd_ble_gatts_characteristic_add packet.

2、Operation Code = 0xA2 (162) for sd_ble_gatts_characteristic_add, see BLE_GATTS_SVCS.

3、The parameters provided as input to sd_ble_gatts_characteristic_add are encoded in the following order

- 1 byte:  

Operation Code, Value = 0xA2 (162)

- 2 bytes:  

Service Handle

- 1 byte:   Metadata Present
    0x00 Field not present
    0x01 Field present and follows immediately in the packet
- 11..539 bytes: ble_gatts_char_md_t

Conditional: Characteristic Metadata

- 1 byte:   Characteristic Attribute Present
    0x00 Field not present
    0x01 Field present and follows immediately in the packet
- 9..527 bytes: ble_gatts_attr_t

Conditional: Characteristic Attribute

- 1 byte:   Handles Present
    0x00 Field not present
    0x01 Field present on Application Chip 

 



 

 

 

 

 

 

 

 

 

 

 

 

其包格式圖像表示為:(其中characteristic metadata和char attributes展開太多,請參看SDK)

 

 

注:

本篇講了nrf51822藍牙ble工程的消息流
至此整個藍牙心率計工程分析得差不多了
本工程鏈接:http://pan.baidu.com/s/1dEalb6h

 

More:

[藍牙] 1、藍牙核心技術了解(藍牙協議、架構、硬件和軟件筆記)

[藍牙] 2、藍牙BLE協議及架構淺析&&基於廣播超時待機說廣播事件

[藍牙] 3、 剖析BLE心率檢測工程

[藍牙] 4、Heart Rate Service module

[藍牙] 5、Battery Service module

@beautifulzzzz 2015-12-17 continue~ 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM