中文字幕理论片,69视频免费在线观看,亚洲成人app,国产1级毛片,刘涛最大尺度戏视频,欧美亚洲美女视频,2021韩国美女仙女屋vip视频

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
藍(lán)牙BLE協(xié)議分析【附代碼實例】

來源:https://blog.csdn.net/weixin_35016347/article/details/108011878

整理:技術(shù)讓夢想更偉大 | 李肖遙

Part101 藍(lán)牙概述

藍(lán)牙技術(shù)起源于愛立信在1994年提出的方案,旨在解決移動電話和其他配件之間進行低功耗、低成本的無線通信連接的方法。

藍(lán)牙發(fā)展歷史
  • 第一代藍(lán)牙主要是指90年代的V1.0~V1.2版本,是關(guān)于段距離通信的早期探索,此時還存在許多問題,應(yīng)用不是特別廣泛
  • 第二代藍(lán)牙主要是00年中V2.0~V2.1版本,新增了EDR(Enhanced Data Rate)技術(shù)提高傳輸速率,以及體驗及安全
  • 第三代藍(lán)牙主要是00年末V3.0版本,新增了802.11 WiFi協(xié)議,引入了AMP(Generic Alternate MAC/PHY)交替射頻技術(shù),極大的提高了傳輸速率并降低功耗
  • 第四代藍(lán)牙是10年以來的V4.0~V4.2版本,主推LE(Low Energy)低功耗,大約僅消耗十分之一,將三種規(guī)格,包括經(jīng)典藍(lán)牙、高速藍(lán)牙、和藍(lán)牙低功耗,集中在一起形成一套綜合協(xié)議規(guī)范
  • 第五代藍(lán)牙是16年開始提出的V5.0版本,主要是為了支持物聯(lián)網(wǎng),在功耗、傳輸速率、有效傳輸距離、數(shù)據(jù)包容量方面都做了極大的提升

下面的分析都是基于V4.1版本,方便入門,可以理解很多核心協(xié)議的設(shè)計思想

Part202 藍(lán)牙技術(shù)分類

藍(lán)牙技術(shù)包含藍(lán)牙發(fā)展過程中的兩套技術(shù),但是這兩套原理和實現(xiàn)都不一樣,也無法實現(xiàn)互通

Basic Rate(BR)/AMP

最初的藍(lán)牙技術(shù),包括可選的EDR(Enhanced Data Rate)技術(shù)和交替使用的MAC層和PHY層擴展 AMP(Alternate MAC and PHY layer extension)【優(yōu)化傳輸速度的過程】

解釋:藍(lán)牙誕生之初使用的BR技術(shù),傳輸速率很低,隨著發(fā)展而變得無法支持,所以引入了EDR,這時還沒有修改軟硬件架構(gòu),但是之后又落伍了,所以直接引入了WiFi的底層協(xié)議,也就是MAC/PHY擴展,但這部分的實現(xiàn)就無法直接更替,所以BR/EDR只能與AMP交替使用

Low Energy(LE)

藍(lán)牙低功耗,則不關(guān)心傳輸速率,而是從降低功耗的角度實現(xiàn)的另一套技術(shù),跟前面的協(xié)議沒有絲毫關(guān)系

Part303 藍(lán)牙架構(gòu)

structure

藍(lán)牙協(xié)議將藍(lán)牙整體分成了兩層架構(gòu),底層是核心協(xié)議,描述了藍(lán)牙核心技術(shù)的基礎(chǔ)和規(guī)范,應(yīng)用層協(xié)議則基于具體需求,使用核心協(xié)議提供的機制,實現(xiàn)不同的功能策略

核心協(xié)議包含兩部分,Host和Controller,這兩部分在不同的藍(lán)牙協(xié)議版本中略有區(qū)別,但大致上是,Controller完成硬件側(cè)的規(guī)范制訂,包括信號調(diào)制解調(diào),會抽象出用于通信的邏輯鏈路,可能存在一個或多個,如LE Controller、BR/EDR Controller;Host則在邏輯鏈路的基礎(chǔ)上完成更友好的封裝,屏蔽掉技術(shù)細(xì)節(jié),方便應(yīng)用層對數(shù)據(jù)的使用

Part404 藍(lán)牙協(xié)議

藍(lán)牙協(xié)議也采用層次結(jié)構(gòu),自下而上依次為物理層、邏輯層、L2CAP層和應(yīng)用層

protocol

應(yīng)用層(App Layer)為不同場景定義規(guī)范,提出Profile(一項服務(wù))的概念,實現(xiàn)各種應(yīng)用功能

L2CAP(Logical Link Control and Adaptation Protocol Layer)

  • 邏輯鏈路控制和適配協(xié)議,負(fù)責(zé)管理邏輯鏈路,使得不同應(yīng)用可共享一個邏輯鏈路,類似端口的實現(xiàn)
  • 在邏輯鏈路的基礎(chǔ)上,抽象出與具體技術(shù)無關(guān)的數(shù)據(jù)傳輸信道,如單/廣播,然后對上以L2CAP channel endpoints的概念,為不同應(yīng)用程序提供獨立的傳輸通道

邏輯層(Logical Layer)

  • 提供設(shè)備對象之間邏輯傳輸,在物理層的基礎(chǔ)上,建立邏輯信道,主要基于傳輸類型來劃分,包括控制類傳輸(負(fù)責(zé)底層物理鏈路的管理)、用戶類傳輸(負(fù)責(zé)用戶數(shù)據(jù)傳輸)和其他特殊類型的傳輸
  • 不同的邏輯信道(Logical Link)會在下層對應(yīng)Logical Transport,實現(xiàn)流控、應(yīng)答、重傳等機制

物理層(Physical Layer)

  • 負(fù)責(zé)提供數(shù)據(jù)傳輸?shù)奈锢硗ǖ?/section>
  • 物理鏈路(Physical Link):對物理信道的進一步封裝
  • 物理信道(Physical channel):三種藍(lán)牙技術(shù)都使用相同的頻段和頻率范圍,但是具體實現(xiàn)都不一樣
    • Advertisement Broadcast Channel:用于設(shè)備間無連接廣播通信,包括發(fā)現(xiàn)/連接操作
    • Piconet channel:用于連接狀態(tài)下通信
    • Inquiry Scan Physical Channel:用于發(fā)現(xiàn)操作,即搜索/被搜索
    • Page Scan Physical Channel:用于連接操作,即連接/被連接
    • Basic Piconet Physical Channel:用于連接狀態(tài)下通信,使用79個跳頻點
    • Adapted Piconet Physical Channel:用于連接狀態(tài)下通信,使用較少RF跳頻點
    • Synchronization Scan Channel:用于無連接的廣播通信
    • BR/EDR頻段分成了79個channel,每個占1M帶寬;采用跳頻技術(shù)(Hopping),即物理信道隨機占用某一channel;定義了五種物理信道,每次只能在一種物理信道上通信,采用時分方式
    • AMP直接使用WIFI的物理層規(guī)范,只有一個物理信道,用于已連接設(shè)備之間的高速數(shù)據(jù)通信
    • LE頻段分成了40個channel,每個占2M帶寬;有兩種物理信道,每次只能在一種物理信道上通信,采用時分方式

Part505 BLE協(xié)議棧

實現(xiàn)一個BLE應(yīng)用,需要一個支持BLE射頻的芯片,然后基于一個與芯片配套的協(xié)議棧,開發(fā)藍(lán)牙應(yīng)用。

協(xié)議棧的作用就是軟件和硬件之間的橋梁,對應(yīng)用數(shù)據(jù)進行封包然后生成可以通過射頻發(fā)送的空中數(shù)據(jù)包及其逆向過程。

BLE-protocol

Physical Layer(PHY)

  • 藍(lán)牙通信系統(tǒng)的物理層,是免費ISM頻段,整個頻帶分成40份,每份帶寬2MHz;此外還定義了RF收發(fā)相關(guān)的特性,如發(fā)射功率、調(diào)制解調(diào)方式等

Link Layer(LL)

  • 解決在有限物理信道上傳輸遠(yuǎn)多實際信道數(shù)量的數(shù)據(jù),即信道共享,然后為通信實體創(chuàng)建看似獨享的邏輯信道,以及解決傳輸過程中的校驗、重傳等問題
  • LL中的信道設(shè)計:BLE系統(tǒng)基于通信場景,在40個物理信道中選取三個作為廣播信道,處理數(shù)據(jù)量小、發(fā)送不頻繁、時延不敏感的場景,存在的問題就是不可靠、效率低、不安全;另外的場景則在剩下的37個信道中選取一個為雙方建立單獨信道,并且為了抗干擾采用跳頻技術(shù)
  • 為此,LL為通信雙方實體定義了以下狀態(tài)及切換條件
BLE-state
- Standby:初始狀態(tài),不收發(fā)數(shù)據(jù),接受上層協(xié)議命令與其他狀態(tài)切換
- Advertising:通過廣播發(fā)送數(shù)據(jù)的狀態(tài),建立連接后可進入Connection
- Scanning:接收廣播的數(shù)據(jù)的狀態(tài)
- Initiating:特殊的接收狀態(tài),類似Scanning,接收Advertiser廣播的連接數(shù)據(jù),建立連接后進入Connection
- Connection:建立連接后擁有單獨的通道
12345
  • 這里會使用空中接口協(xié)議(Air Interface Protocol,AIP)來負(fù)責(zé)實體之間的數(shù)據(jù)交換和狀態(tài)切換

Host Controller Interface(HCI)

  • 定義Host和Contorller之間的通信協(xié)議,如兩個芯片之間的串口

L2CAP

  • 邏輯控制和適配協(xié)議的工作就是實現(xiàn)邏輯信道的多路復(fù)用(multiplexing),對上層數(shù)據(jù)進行分割和重組,以及后續(xù)的流控、錯誤控制和重傳等
  • 多路復(fù)用思想:將要發(fā)送的數(shù)據(jù)分割成一個個數(shù)據(jù)包(Packet Data Unit,PDU),添加包含特定ID的頭部,接收方解析頭部ID進行重組
  • 多路復(fù)用實現(xiàn)
    • 基于連接:L2CAP會為每個邏輯信道分配一個編號(Channel ID,CID),有些CID會有固定用途
    • 基于協(xié)議(略)

Attribute Protocol(ATT)

  • 屬性協(xié)議主要是針對物聯(lián)網(wǎng)場景,核心思想就是將采集的信息或控制的命令以屬性的形式抽象出來,提供接口供遠(yuǎn)端設(shè)備讀寫
  • 采用C/S形式,信息提供方為ATT Server,如傳感器,訪問方為ATT Client
  • 為每個Attribute定義了三個屬性
    • Type,即Attribute的類型,使用UUID區(qū)分
    • Handle,服務(wù)端用來唯一標(biāo)識Attribute的16-bit數(shù)值
    • Value,Attribute的值
  • 為每個Attribute定義了一系列權(quán)限,方便服務(wù)端控制客戶端的行為,包括訪問/加密/認(rèn)證/授權(quán)
  • 對于不同的Attribute,客戶端對服務(wù)端的訪問方式也不一樣,包括Find/Read/Write
  • 傳輸過程是在L2CAP的基礎(chǔ)上,使用基于通道的多路復(fù)用,CID為0x0004

Generic Attribute Profile(GATT)

  • 通用屬性配置文件,Attribute只是將信息(或者說通信數(shù)據(jù))做一下抽象,但是真正對抽象的信息做分類管理則是GATT來完成,形成profile的概念(解決了很多無線協(xié)議的兼容問題),profile可以理解成應(yīng)用場景或者使用方式
  • GATT提供了這樣一種通用的、信息存儲與共享的profile framework,實現(xiàn)BLE雙向通信
  • GATT的層次結(jié)構(gòu)
GATT_profile_hierarchy
  • Profile位于最頂層,不是真正存在的配置文件,而是一個或多個場景相關(guān)的service的抽象集合
  • Service(服務(wù))是一種行為的抽象,具有唯一標(biāo)識UUID,每個service包含一個或多個Characteristic,也可以通過include的方式包含其他service
  • Characteristic(特征)可以理解成一個屬性,是真正與設(shè)備通信相關(guān)的,數(shù)據(jù)發(fā)送和接收的最基本單位,通過對特征的讀寫實現(xiàn)藍(lán)牙雙向通信,它由一個Propertities(定義Value的使用規(guī)范和Descriptor的訪問規(guī)范)、一個Value(特征的實際取值)和一個或多個Descriptor(Value相關(guān)的描述信息)組成,每個特征也具有自己的唯一標(biāo)識,但是有三種形式:
    • 16-bit是官方認(rèn)證,收費,Bluetooth_Base_UUID 為 00000000-0000-1000-8000-00805F9B34FB
    • 16-bit轉(zhuǎn)128-bit,格式為 0000xxxx-0000-1000-8000-00805F9B34FB
    • 32-bit轉(zhuǎn)128-bit,格式為 xxxxxxxx-0000-1000-8000-00805F9B34FB
  • 事實上,目前幾乎所有的BLE應(yīng)用都基于GATT實現(xiàn)通信
  • GATT通信基于C/S模型,外圍設(shè)備作為Server端,維護ATT結(jié)構(gòu)及產(chǎn)出數(shù)據(jù),中心設(shè)備作為client端,請求連接獲取數(shù)據(jù)
  • GATT連接對外圍設(shè)備是獨占的,即一個外圍設(shè)備同時與一個中心設(shè)備建立連接,一個中心設(shè)備可同時與多個外圍設(shè)備建立連接

Security Manager(SM)

  • 安全管理協(xié)議主要負(fù)責(zé)BLE通信過程中安全相關(guān)的內(nèi)容,包括認(rèn)證、加密這些過程

Generic Access Profile(GAP)

  • 通用訪問配置文件,定義了藍(lán)牙設(shè)備的通用的訪問功能,與GATT的數(shù)據(jù)通信過程對應(yīng),處理無連接連接建立過程的通信,也就是為廣播、掃描、發(fā)起連接這些過程定義統(tǒng)一規(guī)范
  • 定義了用戶接口的基本參數(shù),包括藍(lán)牙地址、名稱、pincode、class等概念
  • 定義了設(shè)備的角色:
    • Broadcaster Role:正在發(fā)送advertising events的設(shè)備
    • Observer Role:正在接收advertising events的設(shè)備
    • Peripheral Role:接受Link Layer連接的設(shè)備(對應(yīng)Link Layer的slave角色)
    • Central Role,發(fā)起Link Layer連接的設(shè)備(對應(yīng)Link Layer的master角色)
  • 定義了通信的過程和操作模式:
    • Broadcast mode and observation procedure:實現(xiàn)單向的、無連接的通信
    • Discovery modes and procedures:實現(xiàn)藍(lán)牙設(shè)備的發(fā)現(xiàn)操作
    • Connection modes and procedures:實現(xiàn)藍(lán)牙設(shè)備的連接操作
    • Bonding modes and procedures:實現(xiàn)藍(lán)牙設(shè)備的配對操作

Part606 BLE的廣播

使用場景

  1. 單向、無連接的數(shù)據(jù)通信,發(fā)送者使用廣播信道發(fā)送數(shù)據(jù),接受者掃描接收數(shù)據(jù)
  2. 連接建立階段

協(xié)議層次

  • GAP:以應(yīng)用程序角度進行功能封裝,提供一套統(tǒng)一的、通用的廣播規(guī)范
  • HCI:將LL提供的功能抽象成Command/Events的形式,供上層使用
  • LL:負(fù)責(zé)廣播通信相關(guān)功能的定義和實現(xiàn),包括信道選擇、鏈路狀態(tài)定義、PDU定義、設(shè)備過濾機制等

LL

  • 信道選擇。BLE將藍(lán)牙頻段分成了40個物理信道,綜合考慮(抗干擾等)后將其中三個作為廣播信道,頻段為0/12/39,編號是37-39
  • 鏈路狀態(tài)。參與廣播的BLE設(shè)備,總是處于這三種狀態(tài)之一
    • Advertising:廣播狀態(tài),周期性地廣播,數(shù)據(jù)發(fā)送方
    • Scanning:掃描狀態(tài),掃描并接受廣播數(shù)據(jù),數(shù)據(jù)接收方
    • Initiating:初始化狀態(tài),掃描到可連接的廣播時,發(fā)起連接請求,連接發(fā)起方
  • PDU(Packet Data Unit)格式
pdu
  • Type是指PDU的類型,如不同的狀態(tài)下也有不同的消息類型,TxAdd和RxAdd都是地址類型flag,針對不同的type有不同的含義,RFU都是保留字段,Length標(biāo)明payload的長度

  • Payload內(nèi)容

    StateTypeDescriptionsPayloadlengthDescriptions
    AdvertisingADV_IND常規(guī)廣播,可連接可掃描AdvA6address of broadcaster


    【后續(xù)建立點對點連接,監(jiān)聽CONNECT_REQ請求】AdvData0~31Broadcast data

    ADV_NONCONN_IND同ADV_IND,不可連接不可掃描AdvA6address of broadcaster


    【用于定時傳輸簡單數(shù)據(jù)】AdvData0~31Broadcast data

    ADV_SCAN_IND同ADV_IND,不可連接可掃描AdvA6address of broadcaster


    【用于傳輸額外數(shù)據(jù),監(jiān)聽SCAN_REQ請求】AdvData0~31Broadcast data

    ADV_DIRECT_IND點對點連接,已知雙方藍(lán)牙地址,無廣播數(shù)據(jù),可被指定設(shè)備連接不可掃描AdvA6address of broadcaster


    【快速建立連接,不關(guān)心廣播數(shù)據(jù),監(jiān)聽CONNECT_REQ請求】InitA6address of receiver/initiater
    ScanningSCAN_REQ接收ADV_IND/ADV_SCAN_IND后,請求更多信息ScanA6address of scanne r


    【接收廣播數(shù)據(jù)后請求更多信息】AdvA6address of broadcaster

    SCAN_RSPSCAN_REQ的響應(yīng),返回更多信息AdvA6address of broadcaster



    ScanRspData0~31response data
    InitiatingCONNECT_REQ接收ADV_IND/ADV_DIRECT_IND后,請求建立連接InitA6address of receiver/initiater


    【請求建立連接】AdvA6address of broadcaster



    LLData22parameters of connection
  • BLE設(shè)備地址類型

    • Static Device Address:上電生成,46-bit的random+11,斷電后可變
    • Private Device Address:進一步提供定時更新和地址加密提高可靠行和安全性
    • Non-resolvable Private Address:按周期定時更新,46-bit的random+00
    • Resolvable Private Address:通過隨機數(shù)和IRK(Identity Resolving Key)生成,24-bit的hash+22-bit的random+10
    • Public Device Address:IEEE分配,24-bit的company_id+24-bit的company_assigned,類似MAC
    • Random Device Address:隨機生成,解決費用和維護、設(shè)備身份綁定的問題

HCI

  • 將Link Layer提供的功能封裝成Command/Event組
  • Command格式
command

OCF(Opcode Command Field)表示特定的HCI命令,OGF(Opcode Group Field)表示該HCI命令所屬組別,他們共同組成16位操作碼;Parameter Total Length表示所有參數(shù)總長度

所有BLE相關(guān)的HCI Command的OGF都是0x08

  • Event格式
event
  • 這些Command/Event包括廣播、掃描、連接建立的相關(guān)操作,這些都可以通過hcitool命令進行測試

GAP

  • 會從應(yīng)用程序角度對各種狀態(tài)和操作再一次進行封裝,包括設(shè)備角色,通信的模式和操作的定義
  • 與GAP廣播通信相關(guān)的是廣播和發(fā)現(xiàn)模式
    • Broadcast mode and observation procedure,廣播模式及對應(yīng)的解析過程,對應(yīng)狀態(tài)下的角色雙方就是Broadcaster和Observer
    • Discovery modes and procedures,發(fā)現(xiàn)模式及對應(yīng)的發(fā)現(xiàn)過程,對應(yīng)的角色就是Peripheral和Central
  • 廣播數(shù)據(jù)格式
adv_data
  • 廣播/掃描應(yīng)答數(shù)據(jù),包含有意義部分和無意義部分(補齊為0),有意義部分是由一個個廣播塊(AD Structure)組成,每個廣播塊包含1字節(jié)長度(指示數(shù)據(jù)部分長度)和剩下的數(shù)據(jù)部分,數(shù)據(jù)部分又分為數(shù)據(jù)類型和數(shù)據(jù)內(nèi)容,數(shù)據(jù)類型會指示真實Data部分的內(nèi)容,例如0x01表示Data內(nèi)容是描述設(shè)備物理連接狀態(tài),再例如0x08表示Data內(nèi)容是設(shè)備名稱,更多可以參考generic-access-profile

  • 舉個廣播數(shù)據(jù)的例子

    02 01 06 03 03 aa fe 17 16 aa fe 00 -10 00 01 02 03 04 05 06 07 08 09 0a 0b 0e 0f 00 00 00 00
    1

    02 01 06是一個AD Structure,數(shù)據(jù)部分長度為2字節(jié),類型是0x01,描述設(shè)備物理連接狀態(tài),數(shù)據(jù)部分0x06,1字節(jié)8bit,每bit都是一個標(biāo)志位([預(yù)留]|[預(yù)留]|[預(yù)留]|[同時支持BLE和BR/EDR(Host)]|[同時支持BLE和BR/EDR(Controller)]|[不支持BR/EDR]|[普通發(fā)現(xiàn)模式]|[有限發(fā)現(xiàn)模式]),那么這個廣播就是普通發(fā)現(xiàn)模式,不支持BR/EDR

    03 03 aa fe是第二個AD Structure,數(shù)據(jù)部分長度為3字節(jié),類型是0x03,表示16-bits的Service UUID

    17 16 aa fe 00 -10 00 01 02 03 04 05 06 07 08 09 0a 0b 0e 0f 00 00 00 00是最后一個AD Structure,數(shù)據(jù)部分長度為0x17即23字節(jié),類型是0x16,表示服務(wù)數(shù)據(jù)

AD TypeDescriptionAD Data
0x01設(shè)備物理連接狀態(tài)1字節(jié)8bit,每個bit都是一個標(biāo)志位 [預(yù)留]|[預(yù)留]|[預(yù)留]|[同時支持BLE和BR/EDR(Host)]|[同時支持BLE和BR/EDR(Controller)]|[不支持BR/EDR]|[普通發(fā)現(xiàn)模式]|[有限發(fā)現(xiàn)模式]
0x02UUID非完整的16-bit UUID
0x03UUID完整的16-bit UUID
0x04UUID非完整的32-bit UUID
0x05UUID完整的32-bit UUID
0x06UUID非完整的128-bit UUID
0x07UUID完整的128-bit UUID
0x08設(shè)備名稱縮寫設(shè)備名稱
0x09設(shè)備名稱完整設(shè)備名稱
0x0aTX Power LevelTX Power Level
0xff廠商數(shù)據(jù)[廠商ID]|[廠商自定義數(shù)據(jù)]

Part707 BLE的連接

經(jīng)典藍(lán)牙中保持連接非常耗費資源,但是每次連接建立效率又非常低,為了優(yōu)化體驗,BLE簡化了連接過程(毫秒級),極大的降低了面向連接通信的代價

藍(lán)牙通信系統(tǒng)中,對于連接的定義是:在約定的時間段內(nèi),雙方都到一個指定的物理Channel上通信。

LL

  • 角色定義。BLE為處于連接狀態(tài)的兩個設(shè)備定義了兩個角色,Master和Slave。Master作為連接發(fā)起方,定義和連接相關(guān)的參數(shù),Slave是連接的接收方,請求連接參數(shù)
  • PDU(Packet Data Unit)格式
DC_PDU

面向連接的通信使用特定的PDU,稱為Data channel PDU

LLID指示Data Channel傳輸?shù)腜DU類型,傳輸數(shù)據(jù)是LL Data PDU,傳輸控制信息是LL Control PDU,NESN(Next Expected Sequence Number)和SN(Sequence Number)用于數(shù)據(jù)傳輸過程中的應(yīng)答和流控,MD(More Data)用于關(guān)閉連接,RFU是預(yù)留位,Length指示有效數(shù)據(jù)長度,包括Payload和MIC

LLIDtypeDescription
01bLL Data PDU空包或未傳輸完成的消息(被拆包)
10bLL Data PDU(不需拆包)完整消息或第一個包
11bLL Control PDU用于控制、管理LL連接的數(shù)據(jù)包,此時Payload為1字節(jié)Opcode和剩余的控制數(shù)據(jù)
  • 建立連接
    1. 可連接狀態(tài)的設(shè)備(Advertiser)按照一定周期廣播可連接數(shù)據(jù)包
    2. 主動連接的設(shè)備(Initiator)收到廣播包后回應(yīng)一個連接請求(約定時間點、物理信道等)
    3. Initiator發(fā)送完畢后進入連接狀態(tài),成為Master
    4. Advertiser接收到連接請求后也進入連接狀態(tài),成為Slave
    5. 雙方按照參數(shù)約定,定時切換到某一物理信道,開始依次收發(fā)數(shù)據(jù),直至連接斷開

HCI

  • 封裝Link Layer的功能,主要包括連接的建立、關(guān)閉、參數(shù)設(shè)置和管理,以及數(shù)據(jù)封裝和轉(zhuǎn)發(fā)

GAP

  • 定義設(shè)備具有的能力和操作

Part808 BLE安全機制

White List

白名單就是一組藍(lán)牙地址列表,通過設(shè)置白名單可以允許掃描、連接特定的藍(lán)牙設(shè)備,以及被掃描、連接

LL Privacy

在白名單的基礎(chǔ)上將設(shè)備地址進行加密,轉(zhuǎn)變成Resolvable Private addresses

  • 本地Resolving List需要保存每一對BLE設(shè)備的key/address信息,格式為:Local IRK|Peer IRK|Peer Device Identity Address|Address Type
  • LL Privacy機制在Controller中完成,即加解密操作對HCI上層是透明的

LL Encryption

數(shù)據(jù)發(fā)送和接收過程進行加解密

  • 加解密操作在Link Layer完成
  • Host會保存至少1280bit的LTK(根密鑰),啟動加密傳輸時,LL會首先協(xié)商出一個128-bit的隨機數(shù)SDK(Session Key Diversifier)和64-bit的iv,經(jīng)過Encryption Engine和LTK生成此次通信的會話密鑰SessionKey

SecurityManager

為BLE設(shè)備提供加密連接相關(guān)的key,包含以下規(guī)范:

  • 定義了配對(Pairing)的相關(guān)概念及過程
  • 定義了密碼工具箱,包含配對、加密所需的各種算法
  • 定義了安全管理協(xié)議(Security Manager Protocol,SMP),實現(xiàn)配對、密碼傳輸?shù)炔僮?/section>

SMP規(guī)范中,配對的定義是,Master和Slave通過協(xié)商確定用于加密通信的key的過程,包含三個階段:

  • 階段一、Pairing Feature Exchange,交換雙方配對相關(guān)的設(shè)置,如配對方法、鑒權(quán)方式等
    • 鑒權(quán)方式可以采用交換額外的信息(Out of band,OOB),或者彈窗引入人來判定,稱為MITM鑒權(quán),還有輸入配對碼進行對比(Passkey Entry/Numeric Comparison),或者不鑒權(quán)(Just Work)
  • 階段二、通過SMP協(xié)議進行配對操作,有兩種配對方案,LE Legacy Pairing和LE Secure Connections
  • 階段三、協(xié)商好密鑰key,建立加密連接,傳輸密鑰信息(可選)

Part909 Android中的BLE應(yīng)用

具體可以參考開發(fā)的藍(lán)牙測試工具:BLETool

BLE,藍(lán)牙低功耗(極低的運行和待機功耗)

Android 4.3(API 18) 開始引入 BLE ,即藍(lán)牙4.0

Android 4.3 的 BLE 只支持 Central Role(中心設(shè)備,掃描并連接外圍設(shè)備)

Android 5.0 開始同時支持 Central Role 和 Peripheral Role(外圍設(shè)備,向外廣播發(fā)送數(shù)據(jù))

1、權(quán)限

<!--藍(lán)牙權(quán)限-->
<uses-permission android:name='android.permission.BLUETOOTH'/>
<!--藍(lán)牙相關(guān)操作設(shè)置權(quán)限-->
<uses-permission android:name='android.permission.BLUETOOTH_ADMIN'/>
<!--位置權(quán)限,掃描時需要,Android 9-需要模糊定位,Android 10開始需要精確定位-->
<uses-permission android:name='android.permission.ACCESS_COARSE_LOCATION'/>
<uses-permission android:name='android.permission.ACCESS_FINE_LOCATION'/>
<!--聲明使用BLE硬件特性,僅系統(tǒng)支持時可安裝-->
<uses-feature android:name='android.hardware.bluetooth_le' android:required='true' />

2、開啟/關(guān)閉藍(lán)牙

// 判斷支持藍(lán)牙功能
if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    // 獲取藍(lán)牙管理服務(wù)
    BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    // 獲取藍(lán)牙適配器
    BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
    if (bluetoothAdapter != null) {
        // 判斷藍(lán)牙是否開啟
        if (bluetoothAdapter.isEnabled()) {
            // 關(guān)閉藍(lán)牙
            bluetoothAdapter.disable();
        } else {
            // 1、靜默開啟藍(lán)牙
            bluetoothAdapter.enable();
            // 2、顯式請求開啟藍(lán)牙
            Intent intent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(intent, REQUEST_BLUETOOTH_ENABLE);
        }
    }
}

3、掃描與監(jiān)聽

if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_BLUETOOTH_LE)) {
    // 獲取藍(lán)牙管理服務(wù)
    BluetoothManager bluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
    // 獲取藍(lán)牙適配器
    BluetoothAdapter bluetoothAdapter = bluetoothManager.getAdapter();
    if (bluetoothAdapter != null) {
        // 判斷藍(lán)牙是否開啟
        if (bluetoothAdapter.isEnabled()) {
           // 獲取掃描器實例
            BluetoothLeScanner bluetoothLeScanner = bluetoothAdapter.getBluetoothLeScanner();
            boolean isScanning = false;
            if (bluetoothLeScanner != null) {
                if (isScanning) {
                    // 停止掃描
                    isScanning = false;
                    bluetoothLeScanner.stopScan(scanCallback);
                } else {
                   // 開始掃描
                    isScanning = true;
                    bluetoothLeScanner.startScan(scanCallback);
                }
            }
        }
    }
}

Android 8 開始提供一個后臺持續(xù)掃描的API,應(yīng)用殺死后也可以繼續(xù)掃描,直到關(guān)閉藍(lán)牙【待驗證】

public int startScan (List<ScanFilter> filters, ScanSettings settings, PendingIntent callbackIntent);

// 設(shè)置攔截器和掃描選項
bluetoothLeScanner.startScan(scanFilters, scanSettings, scanCallback);

初始化掃描過濾器

scanFilters = new ArrayList<>();
ScanFilter scanFilter = new ScanFilter.Builder()
  .setDeviceName('lalala')
  .setServiceUuid(new ParcelUuid(UUID.randomUUID()))
  .build();
scanFilters.add(scanFilter);

初始化掃描設(shè)置

scanSettings = new ScanSettings.Builder()
  // 設(shè)置掃描模式
  .setScanMode(ScanSettings.SCAN_MODE_BALANCED)
  // 設(shè)置回調(diào)類型  
  .setCallbackType(ScanSettings.CALLBACK_TYPE_ALL_MATCHES)
  // 設(shè)置配對模式
  .setMatchMode(ScanSettings.MATCH_MODE_STICKY)
  // 設(shè)置報告延遲
  .setReportDelay(0)
  .build();

兩個類都是通過 Builder 構(gòu)造,提供系列函數(shù)用于參數(shù)設(shè)置,如 setDeviceName()、setScanMode()、setMatchMode()

4、掃描回調(diào)

scanCallback = new ScanCallback {
    @Override
    public void onScanResult(int callbackType, ScanResult result) {
        super.onScanResult(callbackType, result);
    }

    @Override
    public void onScanFailed(int errorCode) {
        super.onScanFailed(errorCode);
    }

    @Override
    public void onBatchScanResults(List<ScanResult> results) {
        super.onBatchScanResults(results);
    }
}

其中 onBatchScanResults() 是批量返回掃描結(jié)果??赏ㄟ^下面的接口判斷藍(lán)牙芯片是否支持批處理

Bluetoothadapter.isOffloadedScanBatchingSupported();
1

注意 onScanResult()onBatchScanResults() 是互斥的,ScanSettingssetReportDelay() 設(shè)置為0(默認(rèn))則通過 onScanResult() 返回掃描結(jié)果,否則開啟批處理掃描模式,并觸發(fā) onBatchScanResults() 回調(diào)。

5、廣播數(shù)據(jù)解析

掃描成功會返回 ScanResult 廣播數(shù)據(jù)類,然后進一步解析

// 返回遠(yuǎn)程設(shè)備類
BluetoothDevice device = scanResult.getDevice();
// 返回掃描記錄,包含廣播和掃描響應(yīng)
ScanRecord scanRecord = scanResult.getScanRecord();
// 返回信號強度,[-127, 126]
int rssi = scanResult.getRssi()

BluetoothDevice 是設(shè)備信息類,常用的方法有

// 獲取硬件地址
String address = device.getAddress();
// 獲取藍(lán)牙名稱
String name = device.getName();
// 獲取設(shè)備類型,如DEVICE_TYPE_CLASSIC、DEVICE_TYPE_LE、DEVICE_TYPE_DUAL、DEVICE_TYPE_UNKNOWN
int type = device.getType();
// 獲取綁定狀態(tài),如BOND_NONE、BOND_BONDING、BOND_BONDED
int state = device.getBondState();

6、連接設(shè)備

掃描返回的廣播消息中可以獲取到遠(yuǎn)程設(shè)備的MAC地址,可用于設(shè)備的連接

if (bluetoothAdapter.isEnabled()) {
  // 獲取遠(yuǎn)程設(shè)備對象
  BluetoothDevice bluetoothDevice = bluetoothAdapter.getRemoteDevice(address);
  if (bluetoothDevice != null) {
    handler.post(new Runnable() {
      @Override
      public void run() {
        BluetoothGatt bluetoothGatt;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
          // 連接遠(yuǎn)程設(shè)備
          bluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback, BluetoothDevice.TRANSPORT_LE);
        } else {
          bluetoothGatt = bluetoothDevice.connectGatt(context, false, bluetoothGattCallback);
        }
      }
    });
  }
}

bluetoothGatt 是藍(lán)牙通用屬性協(xié)議的封裝,定義了BLE通信的一些基本規(guī)則和連接通信操作

7、連接回調(diào)

bluetoothGattCallback 則是 bluetoothGatt 連接的回調(diào)類,通知客戶端連接狀態(tài)和結(jié)果

bluetoothGattCallback = new BluetoothGattCallback {
  @Override
  public void onConnectionStateChange(BluetoothGatt gatt, final int status, final int newState) {
    super.onConnectionStateChange(gatt, status, newState);
  }

  @Override
  public void onServicesDiscovered(BluetoothGatt gatt, final int status) {
    super.onServicesDiscovered(gatt, status);
  }

  @Override
  public void onCharacteristicChanged(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic) {
    super.onCharacteristicChanged(gatt, characteristic);
  }

  @Override
  public void onCharacteristicRead(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) {
    super.onCharacteristicRead(gatt, characteristic, status);
  }

  @Override
  public void onCharacteristicWrite(BluetoothGatt gatt, final BluetoothGattCharacteristic characteristic, final int status) {
    super.onCharacteristicWrite(gatt, characteristic, status);
  }

  @Override
  public void onDescriptorRead(BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int status) {
    super.onDescriptorRead(gatt, descriptor, status);
  }

  @Override
  public void onDescriptorWrite(BluetoothGatt gatt, final BluetoothGattDescriptor descriptor, final int status) {
    super.onDescriptorWrite(gatt, descriptor, status);
  }
}

連接成功及其他連接狀態(tài)改變都會調(diào)用 onConnectionStateChange() 方法。status 表示這個操作的狀態(tài),是 BluetoothGatt.GATT_SUCCESS 或者讀寫受限、超過范圍等其他錯誤狀態(tài)。newState 則表示當(dāng)前設(shè)備的連接狀態(tài),連接成功為 BluetoothProfile.STATE_CONNECTED,連接失敗是BluetoothProfile.STATE_DISCONNECTED。

8、發(fā)現(xiàn)服務(wù)

連接成功后就可以開始通信,從請求服務(wù)開始(Profile只是一系列具有共同業(yè)務(wù)需求的服務(wù)的抽象集合,服務(wù)才是實體)

bluetoothGatt.discoverServices();
1

發(fā)現(xiàn)服務(wù)后會觸發(fā) onServicesDiscovered() 回調(diào),然后繼續(xù)獲取服務(wù)

// 獲取所有服務(wù)
List<BluetoothGattService> bleServiceList = bluetoothGatt.getServices();
// 通過uuid獲取特定的服務(wù)
BluetoothGattService bleService = bluetoothGatt.getService(serviceUuid);

BluetoothGattService 是藍(lán)牙服務(wù)類,是與某個場景相關(guān)的一系列行為的抽象,具有一個唯一的UUID,然后服務(wù)類型,如SERVICE_TYPE_PRIMARY、SERVICE_TYPE_SECONDARY(主要服務(wù)可以包含二級服務(wù)),包含的特征列表

9、獲取特征

// 獲取所有特征
List<BluetoothGattCharacteristic> bleCharacteristicList = bleService.getCharacteristics();
// 通過uuid獲取特定的特征
BluetoothGattCharacteristic bleCharacteristic = bleService.getCharacteristic(characteristicUuid);

BluetoothGattCharacteristic 是藍(lán)牙特征類,是通信的基本數(shù)據(jù)單位,包含標(biāo)志特征的唯一的UUID,描述特征訪問權(quán)限的特性,如PROPERTY_BROADCAST、PROPERTY_READ、PROPERTY_WRITE等,特征的實際取值,以及特征的描述

10、讀寫特征

// 讀取特征
bluetoothGatt.readCharacteristic(bleCharacteristic);
// 設(shè)置并寫入特征
bleCharacteristic.setValue('XXX');
bluetoothGatt.writeCharacteristic(bleCharacteristic);

這里的讀寫特征函數(shù)都是返回布爾類型表示是否操作成功,如果成功真正的值會在 onCharacteristicRead/onCharacteristicWrite 回調(diào)中讀取/寫入

@Override
public void onCharacteristicRead(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
  switch (status) {
    case GATT_SUCCESS:
      String valueStr = BLEUtils.byte2HexString(characteristic.getValue());
      break;
    case GATT_READ_NOT_PERMITTED:
      ToastUtils.showShort(context, 'GATT_READ_NOT_PERMITTED');
      break;
    default:
      ToastUtils.showShort(context, 'CHARACTERISTIC_READ_FAILED');
      break;
  }
}

@Override
public void onCharacteristicWrite(BluetoothGatt gatt, BluetoothGattCharacteristic characteristic, int status) {
  switch (status) {
    case GATT_SUCCESS:
      String valueStr = BLEUtils.byte2HexString(characteristic.getValue());
      break;
    default:
      ToastUtils.showShort(context, 'CHARACTERISTIC_WRITE_FAILED');
      break;
  }
}

11、監(jiān)聽特征

真正要實現(xiàn)通信除了單方面讀寫,還需要對數(shù)據(jù)變化進行監(jiān)聽,這樣就可以進行數(shù)據(jù)交換

// 設(shè)置特征監(jiān)聽為true,且要求特征具有NOTIFY屬性
bluetoothGatt.setCharacteristicNotification(characteristic, true);

這樣,自己或?qū)Ψ教卣鞲淖儠r就會回調(diào)函數(shù)從而獲取改變后的特征值

@Override
public void onCharacteristicChanged(BluetoothGattCharacteristic characteristic) {
  Log.d('bledemo''uuid = ' + characteristic.getUuid().toString());
  Log.d('bledemo''value = 0x' + BLEUtils.byte2HexString(characteristic.getValue()));
}

12、獲取描述

// 獲取所有描述
List<BluetoothGattDescriptor> bleDescriptorList = bleCharacteristic.getDescriptors();
// 通過uuid獲取特定的描述
BluetoothGattDescriptor bleDescriptor = bleCharacteristic.getDescriptor(descriptorUuid);

BluetoothGattDescriptor 是藍(lán)牙特征描述類,包含對特征的一些額外描述信息

13、讀寫描述

// 讀取描述
bluetoothGatt.readDescriptor(bleDescriptor);
// 設(shè)置并寫入描述
bleDescriptor.setValue('XXX');
bluetoothGatt.writeDescriptor(bleDescriptor);

同樣讀寫成功會觸發(fā)onDescriptorRead/onDescriptorWrite 回調(diào)

@Override
public void onDescriptorRead(BluetoothGattDescriptor descriptor, int status) {
  switch (status) {
    case GATT_SUCCESS:
      String valueStr = BLEUtils.byte2HexString(descriptor.getValue());
      break;
    case GATT_READ_NOT_PERMITTED:
      ToastUtils.showShort(context, 'GATT_READ_NOT_PERMITTED');
      break;
    default:
      ToastUtils.showShort(context, 'DESCRIPTOR_READ_FAILED');
      break;
  }
}

@Override
public void onDescriptorWrite(BluetoothGattDescriptor descriptor, int status) {
  switch (status) {
    case GATT_SUCCESS:
      String valueStr = BLEUtils.byte2HexString(descriptor.getValue());
      break;
    default:
      ToastUtils.showShort(context, 'DESCRIPTOR_WRITE_FAILED');
      break;
  }
}

14、斷開連接

// 斷開連接,會觸發(fā)onConnectionStateChange()回調(diào)
bluetoothGatt.disconnect();
// 關(guān)閉連接,不會觸發(fā)回調(diào)
bluetoothGatt.close();
1234

15、開啟/關(guān)閉廣播

if (bluetoothAdapter.isEnabled()) {
  // 設(shè)置廣播設(shè)備的名稱,方便搜索
  bluetoothAdapter.setName('XXX');
  // 獲取廣播類
  BluetoothLeAdvertiser bluetoothLeAdvertiser = bluetoothAdapter.getBluetoothLeAdvertiser();
  if (bluetoothLeAdvertiser != null) {
    if (isAdvertising) {
      // 關(guān)閉廣播
      bluetoothLeAdvertiser.stopAdvertising(advertiseCallback);
    } else {
      // 開始廣播
      bluetoothLeAdvertiser.startAdvertising(advertiseSetting, advertiseData, advertiseCallback);
    }
  }
}

還可以發(fā)送帶響應(yīng)報文的廣播包

bluetoothLeAdvertiser.startAdvertising(advertiseSetting, advertiseData, advertiseResData, advertiseCallback);
1

其中 advertiseSetting 為廣播設(shè)置類對象

advertiseSetting = new AdvertiseSettings.Builder()
  // 廣播模式,控制廣播功率和延遲
  .setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY)
  // 廣播發(fā)射功率級別
  .setTxPowerLevel(AdvertiseSettings.ADVERTISE_TX_POWER_HIGH)
  // 廣播超時時間,最大值為 3*60*1000 毫秒,為 0 時禁用超時,默認(rèn)無限廣播
  .setTimeout(advertiseTimeout)
  // 廣播連接類型
  .setConnectable(true)
  .build();
12345678910

advertiseData、advertiseResData 為廣播包

advertiseData = new AdvertiseData.Builder()
  // 廣播是否包含設(shè)備名稱
  .setIncludeDeviceName(true)
  // 廣播是否包含發(fā)射功率
  .setIncludeTxPowerLevel(true)
  // 添加服務(wù)uuid
  .addServiceUuid(new ParcelUuid(UUID.randomUUID()))
  .build();

advertiseResData = new AdvertiseData.Builder()
  // 添加自定義服務(wù)數(shù)據(jù)
  .addServiceData(new ParcelUuid(UUID.randomUUID()), new byte[]{1,2,3,4})
  // 添加自定義廠商數(shù)據(jù)
  .addManufacturerData(0x06new byte[]{5,6,7,8})
  .build();

16、廣播回調(diào)

advertiseCallback 是廣播回調(diào)

advertiseCallback = new AdvertiseCallback {
  @Override
  public void onStartSuccess(AdvertiseSettings settingsInEffect) {
    super.onStartSuccess(settingsInEffect);
  }

  @Override
  public void onStartFailure(int errorCode) {
    super.onStartFailure(errorCode);
  }
}

17、啟動GATT服務(wù)

只有廣播仍然不夠,作為外圍角色的設(shè)備還需要啟動GATT服務(wù),等待中心設(shè)備與之建立連接之后就可以通過服務(wù)通信

// 啟動 Gatt 服務(wù)
bluetoothGattServer = bluetoothManager.openGattServer(context, bluetoothGattServerCallback);
12

接下來可以向啟動的GATT服務(wù)中添加Service

// 構(gòu)造服務(wù)
BluetoothGattService bluetoothGattService = new BluetoothGattService(UUID.randomUUID(), BluetoothGattService.SERVICE_TYPE_PRIMARY);

// 構(gòu)造特征
BluetoothGattCharacteristic bluetoothGattCharacteristic = new BluetoothGattCharacteristic(UUID.randomUUID(), BluetoothGattCharacteristic.PROPERTY_READ | BluetoothGattCharacteristic.PERMISSION_WRITE | BluetoothGattCharacteristic.PROPERTY_NOTIFY, BluetoothGattCharacteristic.PERMISSION_WRITE | BluetoothGattCharacteristic.PERMISSION_READ);
bluetoothGattCharacteristic.setValue('character_test_value');

// 構(gòu)造描述
BluetoothGattDescriptor bluetoothGattDescriptor = new BluetoothGattDescriptor(UUID.randomUUID(), BluetoothGattDescriptor.PERMISSION_READ |BluetoothGattDescriptor.PERMISSION_WRITE);
bluetoothGattDescriptor.setValue('descriptor_test_value'.getBytes());

// 添加描述到特征中
bluetoothGattCharacteristic.addDescriptor(bluetoothGattDescriptor);
// 添加特征到服務(wù)中
bluetoothGattService.addCharacteristic(bluetoothGattCharacteristic);
// 添加服務(wù)
bluetoothGattServer.addService(bluetoothGattService);

18、GATT服務(wù)回調(diào)

bluetoothGattServerCallback 是GATT服務(wù)的回調(diào),當(dāng)設(shè)備被連接、通信(讀寫特征)時都會觸發(fā)響應(yīng)的回調(diào)函數(shù)

bluetoothGattServerCallback = new BluetoothGattServerCallback {
  @Override
  public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
    super.onConnectionStateChange(device, status, newState);
  }

  @Override
  public void onServiceAdded(int status, BluetoothGattService service) {
    super.onServiceAdded(status, service);
  }
  
  @Override
  public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset,
                                          BluetoothGattCharacteristic characteristic)
 
{
    super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
  }

  @Override
  public void onCharacteristicWriteRequest(BluetoothDevice device, int requestId,
                                           BluetoothGattCharacteristic characteristic,
                                           boolean preparedWrite, boolean responseNeeded,
                                           int offset, byte[] value)
 
{
    super.onCharacteristicWriteRequest(device, requestId, characteristic, preparedWrite,
                                       responseNeeded, offset, value);
  }

  @Override
  public void onDescriptorReadRequest(BluetoothDevice device, int requestId,
                                      int offset, BluetoothGattDescriptor descriptor)
 
{
    super.onDescriptorReadRequest(device, requestId, offset, descriptor);
  }

  @Override
  public void onDescriptorWriteRequest(BluetoothDevice device, int requestId,
                                       BluetoothGattDescriptor descriptor,
                                       boolean preparedWrite, boolean responseNeeded,
                                       int offset, byte[] value)
 
{
    super.onDescriptorWriteRequest(device, requestId, descriptor, preparedWrite, responseNeeded,
                                   offset, value);
  }


  @Override
  public void onNotificationSent(BluetoothDevice device, int status) {
    super.onNotificationSent(device, status);
  }

本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
深入淺出低功耗藍(lán)牙(BLE)協(xié)議棧
藍(lán)牙4.0協(xié)議詳解
BLE 藍(lán)牙4.0 介紹 基于TI CC2540
光知道藍(lán)牙,卻不懂藍(lán)牙協(xié)議?不能太OUT了,一文淺析BLE 5 藍(lán)牙協(xié)議棧
Android
[BLE]低功耗藍(lán)牙介紹
更多類似文章 >>
生活服務(wù)
熱點新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服