本文章主要講下藍牙串口協議SPP(Serial Port Profile)連接/接受數據/發送數據/斷開連接的流程介紹,可能之前的寫的底層文章你看的云里霧里,此小節就是開發從應用Profile層面來把整個地方串起來,讓你們對協議棧有一個更深刻的認識。
本專欄文章我們會以連載的方式持續更新,本專欄計劃更新內容如下:

第一篇:藍牙綜合介紹 ,主要介紹藍牙的一些概念,產生背景,發展軌跡,市面藍牙介紹,以及藍牙開發板介紹。
第二篇:Transport層介紹,主要介紹藍牙協議棧跟藍牙芯片之前的硬件傳輸協議,比如基于UART的H4,H5,BCSP,基于USB的H2等
第三篇:傳統藍牙controller介紹,主要介紹傳統藍牙芯片的介紹,包括射頻層(RF),基帶層(baseband),鏈路管理層(LMP)等
第四篇:傳統藍牙host介紹,主要介紹傳統藍牙的協議棧,比如HCI,L2CAP,SDP,RFCOMM,HFP,SPP,HID,AVDTP,AVCTP,A2DP,AVRCP,OBEX,PBAP,MAP等等一系列的協議吧。
第五篇:低功耗藍牙controller介紹,主要介紹低功耗藍牙芯片,包括物理層(PHY),鏈路層(LL)
第六篇:低功耗藍牙host介紹,低功耗藍牙協議棧的介紹,包括HCI,L2CAP,ATT,GATT,SM等
第七篇:藍牙芯片介紹,主要介紹一些藍牙芯片的初始化流程,基于HCI vendor command的擴展
第八篇:附錄,主要介紹以上常用名詞的介紹以及一些特殊流程的介紹等。
另外,開發板如下所示,對于想學習藍牙協議棧的最好人手一套。以便更好的學習藍牙協議棧,相信我,學完這一套視頻你將擁有修改任何協議棧的能力(比如Linux下的bluez,Android下的bluedroid)。

-------------------------------------------------------------------------------------------------------------------------
CSDN學院鏈接(進入選擇你想要學習的課程):
藍牙交流扣扣群:970324688
Github代碼:
入手開發板:
藍牙學習目錄:
--------------------------------------------------------------------------------------------------------------------------
在上面章節我們鋪墊了很多底層協議,可能你們看的可能會云里霧里,此章節就是開發從應用Profile層面來把整個地方串起來,讓你們對協議棧有一個更深刻的認識。此小節我們每個協議只是會講解到一次raw data分析,其他的封包你們要反復參照之前講解的協議來分析,如果一旦搞清楚,會對藍牙協議棧交互有一個深刻的認識
本小節主要通過一個SPP的應用例程來測試開發板跟手機SPP交互的流程,其中流程包括:
分析工具:Ellisys(打開方式參照工具的使用)
分析封包:spp連接_發送_接受_斷開.log(在資料中STM32_UBUNTU_BLUETOOTH\2-藍牙資料\藍牙協議分析)
設計到的工具:Android “藍牙串口”?apk(在資料中STM32_UBUNTU_BLUETOOTH\3-軟件工具\bt_spp_apk)
本分析為了簡化流程,所以采用Base on pincode配對方式,如果想看SSP的配對方式,可以參照HCI章節的SSP配對
整個流程如下(初始化部分不講解,可以參照HCI章節的初始化流程,直接從手機連接開發板步驟說起):







整個步驟比較復雜,我來一個個流程給你們拆解下,總結流程如下(備注:每個設備的交互流程稍有差別,但是總體框架一樣,另外每個步驟含有幾個子步驟,我們在后面介紹完整個流程再展開說明):
步驟1)手機發送連接請求,開發板接受連接,然后收到連接完成事件
步驟2)開發板主動問詢手機支持的feature,手機回復開發板
步驟3)開發板處理一些HCI command(Page scan reprtition Moide change/Max slot change/Link supervision timeout change)
步驟4)手機發起L2CAP information request,開發板回復(Extened Feature/Fixed channel support)
步驟5)手機主動連接SDP,然后L2CAP configure交互MTU參數
步驟6)手機主動問詢開發板SPP的SDP信息,開發板回復,問詢完后手機斷開SDP的連接
步驟7)手機主動連接RFCOMM,然后L2CAP configure交互MTU參數。
步驟8)手機主動連接RFCOMM signal通道,并且交互PN參數
步驟9)手機發起配對請求,配對完成后芯片上報給協議棧Link Key
步驟10)手機連接開發板SPP的RFCOMM server channel
步驟11)開發板跟手機交互RFCOMM Modem status參數
步驟12)開發板主動跟手機交互PnP SDP參數(此部分是DID協議部分,暫時略過)
步驟13)手機主動發送SPP字符串(上層決定)
步驟14)開發板主動發送SPP字符串(上層決定)
步驟15)手機主動開發與開發板的連接
下面我們就來一一分析下整個流程,
備注:關于HCI command/event,l2cap,rfcomm,sdp封包格式我會做簡短的介紹,詳細的可以分別看下我之前的文章
整個步驟1分為以下幾個小步驟組成

用另外一種flow表示如下:

① 手機發起藍牙連接,芯片收到后,發送給協議棧,相當于Remote chip -> Local Chip(Controller) -> Local BT stack(host)這個過程,在后面我們就直接說手機發起連接,協議棧收到來簡化描述首先我們來看下這個event的格式。



我們來分析下raw data
04 0A 0A 7F 24 DF 0C 9C 0C 02 5A 01 (hex)
04 -> HCI_Connection_Request event id
0A -> para的長度,我們也可以看到參數部分藍牙地址6個byte,cod 3個byte,link type 1byte
0A 7F 24 DF 0C 9C -> 藍牙地址(手機的)
0C 02 5A -> cod (關于cod的分析參照我另外的一篇文章)
01 -> link type,也就是acl
我們來看下btsnoop工具幫我們分析的結果,可以看到跟我們一樣

② 協議棧發送給芯片接受連接,Local芯片收到后,然后發送手機,相當于Local BT stack(host)->Local chip(Controller)->Remote chip這個過程,后續我們直接說協議棧發送給手機來簡化描述。
我們先看下這個HCI command的格式:



我們來看下raw data
09 04 07 0A 7F 24 DF 0C 9C 01
09 04 -> HCI opcode ,也就是HCI_Accept_Connection_Request,我們之前也有寫過怎么把OGF,OCF組裝成opcode,我在這里再貼下那這個命令是OGF=1,OCF=9,
Opcode[0] = OCF & 0xff = 0x09
Opcode[1] = (OCF >> 8) | (OGF << 2) = 0x04,組裝起來就是0x09,0x04
07 -> para len,也就是藍牙地址 link type的長度
0A 7F 24 DF 0C 9C -> 藍牙地址(手機的)
01 -> 保持slave角色,如果是Master,那么Local stack要發送Role switch的command
我們來看下btsnoop工具分析的結果:

③ 芯片發送給手機command status來表示收到的command,這個沒什么好講解的,直接就是我們發送的command的status,其他文章已經說過多次,我們不再重復

④ 芯片發送給手機connection complete
我們來看下這個event的格式以及參數



我們來分析下raw data:
03 0B 00 4C 00 0A 7F 24 DF 0C 9C 01?00
03 -> HCI event id
0b -> 后續參數長度
00 -> status success,在HCI core文檔error code中有集中說明每個value代表什么意思

4C 00 -> 連接句柄,注意,此部分重要,后續所以acl封包都會用到這個參數
0A 7F 24 DF 0C 9C -> 藍牙地址(手機的)
01 -> link type ,1代表acl
00 -> disable加密
我們來看下btsnoop的視圖分析:

后續的HCI command/event封包我們不再一一對raw data做分析,可以舉一反三,自己根據core文檔分析下。
整個步驟2分為以下幾個小步驟組成
?


① 協議棧獲取對方支持的Feature

可以看到,此部分的HCI command的參數就是上面鏈接完成的連接句柄
② 協議棧收到芯片發送的command status

③ 手機發送給協議棧封包,告知協議棧手機支持的Feature




?
Page scan等名詞參照附錄
Slots可以先不用關注
Link Supervison Timeout,大白話就是link lost的時間,在說這個timeout的時候,先來說下supervison time,比如兩臺設備連接,突然一臺斷電,可以看到另外一臺并不是直接斷開連接,而是過一段時間才會顯示斷開連接,而這個時間就是supervision time,這個supervison timeout后芯片就告知協議棧斷線了·,其中檢測原理我在附錄中講解
整個步驟4分為以下幾個小步驟組成


① 手機問詢L2CAP information with Extened Feature Support
我們來看下L2CAP information requese的格式(我們是用的basic mode)

其中Information payload就是下面的

那我們整個分析下raw data,包括L2CAP/HCI
4C 20 0A 00 06 00 01 00 0A 02 02 00 02 00
4C 20 -> HCl ACL header,格式如下

Packet Boundary Flag First automatically-flushable packet(0b10)
Broadcast Flag Point-to-point (0b00)
然后Handle就是connection complete的0x004C,然后整個值就是4C 20
0A 00 -> ACL數據的長度,也就是10byte
從這里開始就是L2CAP的數據
06 00 -> l2cap payload的長度,也就是6個byte
01 00 -> channel id(CID)為1,也就是signal channel(L2CAP命令通道)
0A -> L2CAP information requese code
02 -> id,requese跟response一樣
02 00 -> 后續的長度
后續是InfoType

02 00 -> Extended feature supported
下面我們來看下整個封包的btsnoop

L2CAP的拋磚引玉到這里,后續的幾個小步驟L2CAP封包自己舉一反三下
② 給手機回復L2CAP information with Extened Feature Support的response

③ 手機問詢L2CAP information with Fixed channel Support

④ 給手機回復L2CAP information with Fixed channel Support的response



① 手機主動來連接SDP
我們通過raw data來分析下整個連接PSM協議流程(HCI,L2CAP)
4C 20 0C 00 08 00 01 00 02 04 04 00 01 00 52 00
4C 20 ->
Packet Boundary Flag First automatically-flushable packet(0b10)
Broadcast Flag Point-to-point (0b00)
然后Handle就是connection complete的0x004C,然后整個值就是4C 20
0C 00 -> acl packet length
08 00 -> l2cap basic packet length
01 00 -> L2CAP signal通道
下面的數據就是參照此部分格式

02 -> l2cap connection request的code
04 -> id,response必須跟這個一致
04 00 -> 后續的length
01 00 -> PSM,SDP是0x0001,后續還會有RFCOMM的PSM,所以我一起截圖了

52 00 -> source ID為0x0052,記住后面這個值,我們會用到
我們來看下整個封包的btsnoop分析。

② 針對手機連接SDP回復response

此分析流程參照其他L2CAP的分析,但是以上我們要記住一點,就是Destination cid是0x0040
③ 手機主動來配置MTU,我們回復

④ 我們主動去配置MTU,手機回復



分為以下幾個小步驟:
① 手機發起SPP的相關SDP信息的問詢
我們先來看下raw data的分析(包含SDP/L2CAP/HCI)
4C 20 18 00 14 00 40 00 06 00 00 00 0F 35 03 19 11 01 03 F0 35 05 0A 00 00 FF FF 00
4C 20 ->
Packet Boundary Flag First automatically-flushable packet(0b10)
Broadcast Flag Point-to-point (0b00)
然后Handle就是connection complete的0x004C,然后整個值就是4C 20
18 00 -> acl packet length
14 00 -> L2CAP basic mode的length
40 00 -> Destination id,也就是上面L2CAP連線分配的
后面就是SDP的數據了,SDP的數據格式是:

06 -> SDP_SERVICE_SEARCH_ATTR_REQ



00 00 -> TID
00 0F -> Length
35 03 19 11 01 -> ServiceSearchPattern,此部分是一個數據元,關于數據元的詳解介紹參照我SDP相關章節的介紹
此部分就是表示UUID 0x1101,也就是SPP

?03 F0 -> max count 1008
35 05 0A 00 00 FF FF -> AttributeIDList:是個數據元,range是0x0000~0xffff
00 -> ContinuationState,是0
我們來看下btsnoop

② 手機回復
此部分我們就不一一分析,可以按照前面的分析流程來做分析,btsnoop如下

另外,為什么是這個樣子呢,因為我們code中注冊的是這個樣子,code如圖:

注意啦:我們注冊的RFCOMM server channel(scn)是8,這個值要記住,后面手機過來連接rfcomm channel的時候就是通過這個值
③ 手機斷開SDP連接,我們回復response




?
此步驟L2CAP配置MTU我們在上面已經說明,所以不再重復說明,我們只來看下連接RFCOMM通道的步驟
① 手機過來連接RFCOMM

② 我們回復response



分為以下幾個小步驟:
① 手機過來連接RFCOMM signal通道
我們先來分析下raw data(RFCOMM/L2CAP/HCI):
4C 20 08 00 04 00 40 00 03 3F 01 1C
4C 20 ->
Packet Boundary Flag First automatically-flushable packet(0b10)
Broadcast Flag Point-to-point (0b00)
然后Handle就是connection complete的0x004C,然后整個值就是4C 20
08 00 -> acl packet length
04 00 -> l2cap basic mode length 4 byte
40 00 -> Destination id,就是上面l2cap rfcomm連線分配的
下面就是RFCOMM數據了,格式如下:

03 -> 這個是Address field,格式如下:

0x03 = 00000011b,也就是EA=1,C/R=1,也就是command,server channel是0,也就是signal channel
3F -> Control field,格式如下:

0x3f = 00111111b,也就是 SABM幀,P/F=1
01 -> length,格式如下:

0x01 = 00000001b,也就是EA=1,7個byte表示后續的長度,也就是0,后續無長度
1C -> FCS
我們來看下btsnoop

② 我們回復

③ 手機過來配置PN參數,并且攜帶credit

④ 我們回復PN參數,并且攜帶credit



?
分為以下幾個小步驟:
① 芯片給協議棧發HCI pin code requese的event

② 協議棧回復pincode,芯片上報command complete event

③ 芯片上報給協議棧link key



① 手機過來連接SPP server channel通道

注意此部分手機過來連接的server channel就是我們SDP回復給手機的RFCOMM channel 8
② 我們回復



此部分我們只列舉一個Modem status response


手機發送的字符串為:

其中RFCOMM的payload就是SPP發送的數據

我們來分析下raw data:
4C 20 32 00 2E 00 40 00 43 FF 53 03 68 65 6C 6C 6F 2C 49 20 61 6D 20 77 69 72 65 6C 65 73 73 20 6C 69 6E 6B 20 69 6E 20 61 6E 64 72 6F 69 64 20 70 68 6F 6E 65 38
4C 20 ->
Packet Boundary Flag First automatically-flushable packet(0b10)
Broadcast Flag Point-to-point (0b00)
然后Handle就是connection complete的0x004C,然后整個值就是4C 20
32 00 -> acl packet length,50byte
2E 00 -> l2cap basic mode length = 46byte
40 00 -> Destination id,也就是0x0040
43 -> RFCOMM address
FF -> RFCOMM control,此部分為UIH幀
53 -> RFCOMM length
03 -> give credit
68 65 6C 6C 6F 2C 49 20 61 6D 20 77 69 72 65 6C 65 73 73 20 6C 69 6E 6B 20 69 6E 20 61 6E 64 72 6F 69 64 20 70 68 6F 6E 65
-> 此部分就是我們發送的字符串![]()
38 -> FCS
![]()
我們發送的字符串為



希望通過這篇文章能讓你們熟悉藍牙芯片跟藍牙協議棧整個流程的交互方式!