本文正在參加星光計劃3.0–夏日挑戰賽
作者:巴延興
1. 簡介
随着現代移動通信和物聯網的快速發展,萬物互聯已經越來越成為一個基本需求,而近距離的無線通信更是在移動裝置之間有着及其廣泛的應用。藍牙技術是一種無線資料和語音傳輸的開放式标準,它是基于低成本的近距離無線連接配接,為固定和移動裝置建立通信的一種近距離無線技術連接配接。藍牙技術正廣泛運用于汽車領域、工業生産、醫療領域。
1.1 OpenHarmony架構圖
藍牙子系統所屬的分布式軟總線在整個鴻蒙系統中的位置在如下紅框處:
2. 基礎知識
2.1 藍牙概述
藍牙是一種支援裝置短距離通信(一般10m内)的無線電技術,能在包括行動電話、PDA、無線耳機、筆記本電腦、相關外設等衆多具有藍牙子產品的裝置之間進行短距離無線資訊交換,使得資料傳輸變得更加迅速高效。它是愛立信、IBM等5家公司在1998年聯合推出的一項無線通信技術,随後成立的藍牙技術特殊興趣組織(SIG)來負責該技術的開發和技術協定的制定,如今全世界已有1800多家公司加盟該組織。
藍牙工作在全球通用的2.4GHz ISM(即工業、科學、醫學)頻段,使用IEEE802.15協定。作為一種的短距離無線通信技術,在現在的生活中,正扮演着極其重要的角色。當然,在作業系統中,藍牙子產品正扮演着必不可少的基礎功能。
2.2 藍牙架構圖
在藍牙技術中,有**Application**,**Host**和**Controller**三個角色。這三個角色可以位于不同的裝置,也可以位于同一個裝置上。在一個系統中,Host隻有一個,但Controller可以一個也可以有多個。
Application:由不同的Profiles來限定,藍牙系統中的應用程式互操作性是由profiles完成的。profiles定義了藍牙系統中從PHY到L2CAP以及核心規範之外的任何其他協定所需的每層功能和特性。配置檔案定義了層之間的**垂直**互動以及**裝置之間特定層**的點對點互動。此外,應用程式的**行為**和**資料格式**也由profiles定義。
Host:負責在邏輯鍊路的基礎上,進行更為友好的封裝,這樣就可以屏蔽掉藍牙技術的細節,讓Bluetooth Application更為友善的使用。
Controller:負責定義RF、Baseband等底層的規範,并在這之上抽象出用于通信的邏輯鍊路。
2.3 藍牙子系統代碼架構圖
下圖為OpenHamony藍牙子系統的代碼結構,圖中可以看到從上層到底層的整個過程。
APP:藍牙應用程式,即使用藍牙API的程式,一般是在裝置的settings裡實作。通過調用bluetooth的interfaces下的對應接口來實作應用程式的功能。
bluetooth:藍牙架構層,主要包括interfaces和services,本篇主要針對該部分做詳細解讀。
HDF:硬體驅動架構,bluetooth的裝置驅動的開發是基于該架構的基礎上,結合作業系統适配層(OSAL)和平台驅動接口(比如I2C/SPI/UART總線等平台資源)能力,屏蔽不同作業系統和平台總線資源差異,實作bluetooth驅動"一次開發,多系統部署"的目标。
HardWare:各類具有藍牙子產品硬體實體的硬體裝置,如行動電話、PDA、無線耳機、筆記本電腦、相關外設等衆多裝置。
2.4 藍牙子系統代碼目錄
bluetooth 架構層的代碼目錄結構如下:
/foundation/communication/bluetooth
├── interfaces # 接口代碼
│ └── innerkits # 系統服務接口存放目錄
│ ├── native\_c # C接口存放目錄
│ │ └── include # C接口定義目錄
│ └── native\_cpp # C++接口存放目錄
│── sa\_profile # 藍牙服務定義目錄
│── services # 藍牙服務代碼目錄
└── LICENSE # 版權聲明檔案
interfaces,負責向上層應用程式提供相應的功能接口,使應用開發者實作具體藍牙業務功能,目前隻提供c/c++接口。
services負責interfaces接口的實作。系統提供C/C++接口定義及服務和協定棧的代碼,目前提供的隻有BLE相關的接口,包括BLE裝置GATT相關的操作,以及BLE廣播、掃描等功能,其它A2DP,AVRCP,HFP等相關接口在後續增量釋出。
services部分主要是通過bluetooth_standard目錄下的相關子產品來實作interfaces接口。相關的子產品有common、hardware、external、etc、ipc、stack、service、server。子產品間利用c++的相關特性,完成了各自的分層功能,最終實作了藍牙不同協定的場景功能。代碼架構如下:
└── services
├── bluetooth
│ └── BUILD.gn
└── bluetooth\_standard
├── common #定義藍牙相關的資料結構,供其他子子產品調用
├── etc #藍牙服務的配置檔案存放處,藍牙裝置、profile的配置檔案
├── external #擴充目錄
├── hardware #定義HCI的相關接口供其他子子產品調用
├── ipc #程序間通信,不同協定的proxy和stub之間的資料傳遞等
├── server #主從模式的server端代碼存放處
├── service #不同協定的服務接口及實作,供server調用
└── stack #正常stack、list、queue,信号量,互斥量的操作,不同協定棧的相關資料結構的建立、初始化等
2.5 藍牙子系統協定
藍牙相關協定棧在stack目錄下按照目錄進行協定分類,如下:
└── stack
└── src
├── att 屬性協定
├── avctp 音視訊控制傳輸協定
├── avdtp 音視訊分發協定
├── btm (Bluetooth manage藍牙配對與鍊路管理)
├── gap (Generic Access Profile通用通路協定)
├── hci 主機控制接口協定
├── l2cap 邏輯鍊路控制和适配協定
├── rfcomm 序列槽仿真協定
├── sdp 服務發現協定
└── smp(Security Manage Protoco藍牙安全管理協定)
3. 源碼分析
@ohos.bluetooth.d.ts檔案提供了供應用側調用的接口,我們以最常用的打開藍牙開關的操作來展示代碼的時序流程。
應用側調用了@ohos.bluetooth.d.ts中示例的enableBluetooth()接口來開啟藍牙,如下:
function enableBluetooth(): boolean;
實際上d.ts中的接口在代碼中僅僅隻是用來示例,真正起作用的是NAPI的接口EnableBluetooth,該接口對應的檔案為napi_bluetooth_host.cpp,如下:
napi\_value EnableBluetooth(napi\_env env, napi\_callback\_info info)
{
HILOGI("EnableBluetooth start");
BluetoothHost \*host = &BluetoothHost::GetDefaultHost();
bool enabled = host-\>EnableBt();
enabled |= host-\>EnableBle();
napi\_value result = nullptr;
napi\_get\_boolean(env, enabled, &result);
HILOGI("EnableBluetooth end");
return result;
}
napi接口調用的是BluetoothHost中的EnableBt()和EnableBle()方法分别打開經典藍牙和低功耗藍牙開關。我們以打開低功耗藍牙為例分析代碼流程。
3.1 打開低功耗藍牙代碼流程
BluetoothHost::EnableBle()接口如下:
bool BluetoothHost::EnableBle()
{
HILOGD("BluetoothHost::Enable BLE starts");
if (!pimpl) {
HILOGE("BluetoothHost::Enable BLE fails: no pimpl");
return false;
}
if (pimpl-\>proxy\_ == nullptr) {
HILOGE("BluetoothHost::Enable fails: no proxy");
return false;
}
return pimpl-\>proxy\_-\>EnableBle();
}
調用的是BluetoothHostProxy中的EnableBle()接口。如下所示。此時已經從interface目錄進入到service目錄下的ipc目錄。
bool BluetoothHostProxy::EnableBle()
{
MessageParcel data;
if (!data.WriteInterfaceToken(BluetoothHostProxy::GetDescriptor())) {
HILOGE("BluetoothHostProxy::EnableBle WriteInterfaceToken error");
return false;
}
MessageParcel reply;
MessageOption option = {MessageOption::TF\_SYNC};
int32\_t error = InnerTransact(IBluetoothHost::Code::BT\_ENABLE\_BLE, option, data, reply);
if (error != NO\_ERROR) {
HILOGE("BluetoothHostProxy::EnableBle done fail, error: %{public}d", error);
return false;
}
return reply.ReadBool();
}
Proxy端發送的消息ID為BT_ENABLE_BLE,并封裝為消息序列MessageParcel對象。
BluetoothHostStub的OnRemoteRequest将會收到code為9即攜帶BT_ENABLE_BLE的消息序列,并在BluetoothHostStub中解析處理,如下:
int32\_t BluetoothHostStub::OnRemoteRequest(
uint32\_t code, MessageParcel &data, MessageParcel &reply, MessageOption &option)
{
HILOGD("BluetoothHostStub::OnRemoteRequest, cmd = %{public}d, flags= %{public}d", code, option.GetFlags());
std::u16string descriptor = BluetoothHostStub::GetDescriptor();
std::u16string remoteDescriptor = data.ReadInterfaceToken();
if (descriptor != remoteDescriptor) {
HILOGE("BluetoothHostStub::OnRemoteRequest, local descriptor is not equal to remote");
return ERR\_INVALID\_STATE;
}
auto itFunc = memberFuncMap\_.find(code);
if (itFunc != memberFuncMap\_.end()) {
auto memberFunc = itFunc-\>second;
if (memberFunc != nullptr) {
return memberFunc(this, data, reply);
}
}
HILOGW("BluetoothHostStub::OnRemoteRequest, default case, need check.");
return IPCObjectStub::OnRemoteRequest(code, data, reply, option);
}
對應的映射關系如下:
const std::map\<uint32\_t, std::function\<ErrCode(BluetoothHostStub \*, MessageParcel &, MessageParcel &)\>\>
BluetoothHostStub::memberFuncMap\_ = {
...
{BluetoothHostStub::BT\_ENABLE\_BLE,
std::bind(&BluetoothHostStub::EnableBleInner, std::placeholders::\_1, std::placeholders::\_2,
std::placeholders::\_3)},
即将在EnableBleInner函數中向下調用:
ErrCode BluetoothHostStub::EnableBleInner(MessageParcel &data, MessageParcel &reply)
{
bool result = EnableBle();
bool ret = reply.WriteBool(result);
if (!ret) {
HILOGE("BluetoothHostStub: reply writing failed in: %{public}s.", \_\_func\_\_);
return TRANSACTION\_ERR;
}
return NO\_ERROR;
}
其中EnableBle()函數的實作是在BluetoothHostServer中,此時已經從ipc目錄執行到了server目錄,如下:
bool BluetoothHostServer::EnableBle()
{
HILOGD("[%{public}s]: %{public}s(): Enter!", \_\_FILE\_\_, \_\_FUNCTION\_\_);
return IAdapterManager::GetInstance()-\>Enable(BTTransport::ADAPTER\_BLE);
}
AdapterManager是IAdapterManager的實作類,AdapterManager中的Enable方法如下,其中傳入的參數為BTTransport::ADAPTER_BLE。此時,已經通過server目錄進入到了service核心目錄。
bool AdapterManager::Enable(const BTTransport transport) const
{
LOG\_DEBUG("%{public}s start transport is %{public}d", \_\_PRETTY\_FUNCTION\_\_, transport);
std::lock\_guard\<std::recursive\_mutex\> lock(pimpl-\>syncMutex\_);
if (PermissionUtils::VerifyDiscoverBluetoothPermission() == PERMISSION\_DENIED) {
LOG\_ERROR("Enable() false, check permission failed");
return false;
}
if (GetSysState() != SYS\_STATE\_STARTED) {
LOG\_ERROR("AdapterManager system is stoped");
return false;
}
if (pimpl-\>adapters\_[transport] == nullptr) {
LOG\_INFO("%{public}s BTTransport not register", \_\_PRETTY\_FUNCTION\_\_);
return false;
}
if (GetState(transport) == BTStateID::STATE\_TURN\_OFF) {
utility::Message msg(AdapterStateMachine::MSG\_USER\_ENABLE\_REQ);
pimpl-\>dispatcher\_-\>PostTask(std::bind(&AdapterManager::impl::ProcessMessage, pimpl.get(), transport, msg));
return true;
} else if (GetState(transport) == BTStateID::STATE\_TURN\_ON) {
LOG\_INFO("%{public}s is turn on", \_\_PRETTY\_FUNCTION\_\_);
return false;
} else {
LOG\_INFO("%{public}s is turning state %{public}d", \_\_PRETTY\_FUNCTION\_\_, GetState(transport));
return false;
}
}
此時藍牙為關閉狀态,即BTStateID::STATE_TURN_OF,是以進入子線程的ProcessMessage函數中處理。其中傳入的參數msg封裝了AdapterStateMachine::MSG_USER_ENABLE_REQ消息。其中AdapterStateMachine是繼承了StateMachine的狀态機。
void AdapterManager::impl::ProcessMessage(const BTTransport transport, const utility::Message &msg)
{
std::lock\_guard\<std::recursive\_mutex\> lock(syncMutex\_);
if (adapters\_[transport] == nullptr) {
LOG\_DEBUG("%{public}s adapter is nullptr", \_\_PRETTY\_FUNCTION\_\_);
return;
}
if (adapters\_[transport]-\>stateMachine\_ == nullptr) {
LOG\_DEBUG("%{public}s stateMachine\_ is nullptr", \_\_PRETTY\_FUNCTION\_\_);
return;
}
adapters\_[transport]-\>stateMachine\_-\>ProcessMessage(msg);
}
AdapterStateMachine狀态機裡面的ProcessMessage函數沒有重寫處理,會進入StateMachine的預設ProcessMessage函數進行分發。AdapterState繼承了utility::StateMachine::State,AdapterState的子類将對AdapterStateMachine::MSG_USER_ENABLE_REQ消息處理。AdapterStateMachine狀态機的初始化狀态為TURN_OFF_STATE,也是目前的狀态,最終在AdapterState的子類AdapterTurnOffState中分發AdapterStateMachine::MSG_USER_ENABLE_REQ消息。
AdapterTurnOffState的Dispatch方法處理如下:
bool AdapterTurnOffState::Dispatch(const utility::Message &msg)
{
switch (msg.what\_) {
case AdapterStateMachine::MSG\_USER\_ENABLE\_REQ:
Transition(TURNING\_ON\_STATE);
return true;
default:
return false;
}
}
AdapterTurnOffState對AdapterStateMachine::MSG_USER_ENABLE_REQ消息的處理就是将目前狀态從TURN_OFF_STATE狀态變為TURNING_ON_STATE狀态,是以狀态機自動進入AdapterTurningOnState狀态的Entry()函數。如下:
void AdapterTurningOnState::Entry()
{
BTTransport transport =
(adapter\_.GetContext()-\>Name() == ADAPTER\_NAME\_CLASSIC) ? BTTransport::ADAPTER\_BREDR : BTTransport::ADAPTER\_BLE;
AdapterManager::GetInstance()-\>OnAdapterStateChange(transport, BTStateID::STATE\_TURNING\_ON);
LOG\_DEBUG("AdapterStateMachine::Timer enable adapter start transport is %{public}d", transport);
adapterTimer\_-\>Start(ENABLE\_DISABLE\_TIMEOUT\_TIME, false);
adapter\_.GetContext()-\>Enable();
}
IAdapterBle和IAdapterClassic都是IAdapter的子類,目前adapter_對象是IAdapterBle的子類BleAdapter對象,是以進入BleAdapter的Enable()方法:
void BleAdapter::Enable()
{
LOG\_DEBUG("[BleAdapter] %{public}s:%{public}s", \_\_func\_\_, Name().c\_str());
GetDispatcher()-\>PostTask(std::bind(&BleAdapter::EnableTask, this));
}
此時進入任務線程中執行EnableTask方法,如下:
bool BleAdapter::EnableTask()
{
LOG\_DEBUG("[BleAdapter] %{public}s", \_\_func\_\_);
std::lock\_guard\<std::recursive\_mutex\> lk(pimpl-\>syncMutex\_);
bool ret = (BTM\_Enable(LE\_CONTROLLER) == BT\_NO\_ERROR);
if (!ret) {
pimpl-\>btmEnableFlag\_ = false;
LOG\_ERROR("[BleAdapter] %{public}s:BTM enable failed!", \_\_func\_\_);
} else {
pimpl-\>btmEnableFlag\_ = true;
LoadConfig();
ret = (InitBtmAndGap() == BT\_NO\_ERROR);
LOG\_DEBUG("[BleAdapter] %{public}s:BTM enable successfully!", \_\_func\_\_);
}
GetContext()-\>OnEnable(ADAPTER\_NAME\_BLE, ret);
return ret;
}
其中GetContext()->OnEnable(ADAPTER_NAME_BLE, ret)方法是對最終是否打開低功耗藍牙開關的一個回調觸發。而BTM_Enable方法是在stack目錄下的btm.c檔案中實作,将繼續實作打開低功耗藍牙操作。此時已經從service目錄進入到stack目錄了。
int BTM\_Enable(int controller)
{
LOG\_DEBUG("%{public}s start", \_\_FUNCTION\_\_);
if (controller != BREDR\_CONTROLLER && controller != LE\_CONTROLLER) {
return BT\_BAD\_PARAM;
}
if (!IS\_INITIALIZED()) {
return BT\_BAD\_STATUS;
}
int result = BT\_NO\_ERROR;
MutexLock(g\_modeLock);
if (controller == BREDR\_CONTROLLER) {
if (g\_currentMode == MODE\_NONE) {
result = BtmEnableBrEdrAndSharedModules();
} else if (g\_currentMode == MODE\_LE) {
result = BtmEnableBrEdrModules();
}
if (result == BT\_NO\_ERROR) {
g\_currentMode |= MODE\_BREDR;
}
} else if (controller == LE\_CONTROLLER) {
if (g\_currentMode == MODE\_NONE) {
result = BtmEnableLeAndSharedModules();
} else if (g\_currentMode == MODE\_BREDR) {
result = BtmEnableLeModules();
}
if (result == BT\_NO\_ERROR) {
g\_currentMode |= MODE\_LE;
}
}
#ifdef DEBUG
BtmOutputCurrentStatus();
#endif
MutexUnlock(g\_modeLock);
LOG\_DEBUG("%{public}s end", \_\_FUNCTION\_\_);
return result;
}
btm.c的流程相對繁瑣,後面3.3節用時序圖介紹。
3.2 流程時序圖
上面3.1對應的時序流程,如下:
1. napi調用host中的EnableBle()開始,最後在proxy中調用SendRequest,觸發stub中的OnRemoteRequest()。
2. Bluetooth_host_stub->i_bluetooth_host:stub中的OnRemoteRequest()會調用server中的EnableBle()的實作。
- bluetooth_host_server->state_machine:server調用adapter_manager的實作部分,最終調用狀态機的transition()。
- state_machine->btm:adapter_state_machine通過調用ble_adapter的Enable()來實作對btm中的BTM_enable()的調用。
3.3 stack目錄下btm使能時序圖
btm.c的BTM_Enable方法的詳細過程,請參考如下的流程圖:
啟動ble的所有modules;初始化HCI接口前,首先調用libbluetooth_hal.z.so,初始化提供的接口函數。
之後,初始化HCI:建立相關list,如Failure,Cmd,Event,Acl;初始化hci發送、接收隊列;初始化hciHal,HciInitHal信号量。如下:
BtmController的初始化,startAcl,startLeSecurity,startWhiteList。
啟動whitelist的詳細步如下:
whitelist啟動後,再啟動所有的btm modules,則BTM_Enable完成。
4. 藍牙接口說明
目前提供了基本的藍牙連接配接和profile 功能,LTS3.0.1僅包括A2DP, BLE, GATT, SPP等基本接口。其他的功能将會在後續版本中完善和提供。
| 接口檔案 | 功能 | 說明 |
| ------------------------------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
| bluetooth_a2dp_src.h | interface and callback function of a2dp source | A2DP,負責傳輸音頻的profile,手機和藍牙耳機建立連接配接後傳輸音頻就是通過A2DP |
| bluetooth_ble_advertiser.h | Defines advertiser, including avertise data and callbacks, and advertiser functions | BLE 中心裝置、外圍裝置相關回調,功能 |
| bluetooth_ble_central_manager.h | Central manager common functions | |
| bluetooth_def.h | Defined here are various status codes | bluetooth中的業務常量定義及枚舉定義 |
| bluetooth_device_class.h | Bluetooth device class | BluetoothDeviceClass的API接口類 |
| bluetooth_device.h | Bluetooth device major class | bluetooth device major class的static常量定義 |
| bluetooth_gatt_characteristic.h | Bluetooth gatt characteristic interface. | Gatt 相關的接口檔案 |
| bluetooth_gatt_client.h | Bluetooth gatt client interface | |
| bluetooth_gatt_descriptor.h | Bluetooth gatt descriptor interface | |
| bluetooth_gatt_manager.h | gatt manager interface | |
| bluetooth_gatt_server.h | gatt server interface | |
| bluetooth_gatt_service.h | gatt service interface | |
| bluetooth_host.h | bluetooth host, including observer and common functions | host本機管理相關接口,如enable |
| bluetooth_remote_device.h | Bluetooth Remote Device API | 遠端裝置的API接口 |
| bluetooth_socket.h | spp client socket functions | socket,序列槽相關功能 |
| bluetooth_socket_inputstream.h | spp inputstream framework functions, including basic functions | |
| bluetooth_socket_outputstream.h | spp outputstream framework functions, including basic functions | |
| bluetooth_types.h | macro definition | bluetooth中模闆相關的宏定義 |
| uuid.h | framework uuid interface. | uuid類,提供uuid相關接口功能。如字元串和uuid之間的互相轉化 |
5. 藍牙部分業務應用場景
LTS3.0.1藍牙部分目前僅提供c/c++應用開發的接口,由于底層的驅動程式尚待完善,目前提供的隻有BLE相關的接口,包括BLE裝置gatt相關的操作,以及BLE廣播、掃描等功能,其它A2DP,AVRCP,HFP等相關接口在後續增量釋出。
5.1 Host管理
藍牙host管理主要是針對藍牙本機的基本操作,包括打開和關閉藍牙(包括傳統藍牙和BLE),設定和擷取本機藍牙名稱,傳統藍牙的掃描和取消掃描周邊藍牙裝置、擷取本機藍牙profile service清單,擷取對其他裝置的連接配接狀态,擷取、移除本機藍牙(包括傳統藍牙和BLE)已配對的藍牙裝置或清單等。
主要接口如下表:
| 接口名 | 功能描述 |
| ------------------------------------------------------------ | ------------------------------------------------------------ |
| GetDefaultHost(); | 擷取BluetoothHost執行個體,去管理本機藍牙操作。 |
| EnableBt(); | 打開本機藍牙。 |
| DisableBt(); | 關閉本機藍牙。 |
| GetBtState() const; | 擷取本機傳統藍牙狀态: <br />* BTStateID::STATE_TURNING_ON; <br />* BTStateID::STATE_TURN_ON; <br />* BTStateID::STATE_TURNING_OFF; <br />* BTStateID::STATE_TURN_OFF. |
| SetLocalName(const std::string &name); | 設定本機藍牙名稱。 |
| GetLocalName() | 擷取本機藍牙名稱。 |
| int GetBtConnectionState() const; | bluetooth connects state. <br /> * BTConnectState::CONNECTING; <br /> * BTConnectState::CONNECTED; <br /> * BTConnectState::DISCONNECTING; <br /> * BTConnectState::DISCONNECTED. |
| startBtDiscovery() | 發起藍牙裝置掃描。 |
| cancelBtDiscovery() | 取消藍牙裝置掃描。 |
| isBtDiscovering() | 檢查藍牙是否在掃描裝置中。 |
| std::vector<uint32_t> GetProfileList() | 擷取 profile service ID 清單 |
| GetBtProfileConnState(uint32_t profileId) | 擷取本機藍牙profile對其他裝置的連接配接狀态。BTConnectState |
| getPairedDevices(int transport) | 擷取本機藍牙已配對的藍牙裝置列。 |
| bool BluetoothFactoryReset(); | Factory reset bluetooth service |
| GetLocalDeviceClass() const; | 擷取本機device class |
| SetLocalDeviceClass(const BluetoothDeviceClass &deviceClass); | 設定本機device class |
| DisableBle(); | 關閉本機BLE |
| EnableBle(); | 打開本機ble |
| IsBleEnabled() const; | 擷取ble狀态 |
5.1.1 打開/關閉傳統藍牙
主要步驟:
1. 調用BluetoothHost的GetDefaultHost()接口,擷取BluetoothHost執行個體,管理host藍牙操作;
2. 打開藍牙;
3. 查藍牙狀态,關閉藍牙。
5.1.2 本機傳統藍牙掃描
主要步驟:
1. 調用BluetoothHost的GetDefaultHost()接口,擷取BluetoothHost執行個體,管理host藍牙操作;
2. 打開藍牙;
3. 開始掃描;
4. 驗證掃描狀态,最後關閉藍牙。
5.2 BLE掃描/廣播
OpenHarmony低功耗藍牙的BLE裝置互動時,會分為不同的角色,即中心裝置和外圍裝置。其中,中心裝置負責掃描外圍裝置,發現廣播資訊;而外圍裝置負責發送廣播資訊。
主要功能是BLE廣播和掃描。根據指定狀态擷取外圍裝置;啟動或停止BLE掃描、廣播。
5.2.1 BLE廣播
主要步驟:
1. 進行BLE廣播前需要先繼承*Bluetooth::BleAdvertiseCallback*類實作OnStartResultEvent(int result)回調,用于擷取廣播結果;
2. 擷取廣播對象,構造廣播參數和廣播資料;
3. 調用StartAdvertising(const BleAdvertiserSettings &settings, const BleAdvertiserData &advData,
const BleAdvertiserData &scanResponse, BleAdvertiseCallback &callback)接口開始BLE廣播。
5.2.2 BLE掃描
主要步驟:
1. 進行BLE掃描之前先要繼承BleCentralManagerCallback類實作scanResultEvent和scanFailedEvent回調函數,用于接收掃描結果;
2. 調用BleCentralManager(BleCentralManagerCallback &callback)構造函數,擷取中心裝置管理對象;
3. 構造掃描過濾器(或不使用過濾器掃描);
4. 調用startScan()/StartScan(const BleScanSettings &settings)開始掃描BLE裝置,在回調中擷取掃描到的BLE裝置
5.3 GATT 服務端、用戶端
BLE外圍裝置和中心裝置建立GATT連接配接,通過該連接配接中心裝置可以擷取外圍裝置所支援的Service、Characteristic、Descriptor等資料。
同時,中心裝置可以向外圍裝置進行資料請求,并向外圍裝置寫入Characteristic、Descriptor等特征值資料。
兩台裝置建立連接配接後,其中一台作為GATT服務端,另一台作為GATT用戶端。通常發送廣播的外圍裝置作為服務端,負責掃描的中心裝置作為用戶端。
5.3.1 GATT服務端
BLE外圍裝置作為服務端,可以接收來自中心裝置(用戶端)的GATT連接配接請求,應答來自中心裝置的特征值内容讀取和寫入請求,并向中心裝置提供資料,進而實作資訊互動和消息同步。同時外圍裝置還可以主動向中心裝置發送資料。
主要步驟:
1. 和BLE類似,要想正常調用相關的接口調用,首先得實作GattServerCallback的中的相關的回調函數接口
2. 調用接口建立外圍裝置服務端并開啟服務。
3. 調用*GattService(UUID uuid, boolean isPrimary)*接口建立服務對象,向外圍裝置添加服務。
4. 從回調接口*OnCharacteristicWriteRequest*中擷取中心裝置發送來的消息,調用*NotifyCharacteristicChanged*接口向中心裝置發送通知。
5.3.2 GATT用戶端
BLE外圍裝置和中心裝置建立GATT連接配接,通過該連接配接,中心裝置可以擷取外圍裝置所支援的Service、Characteristic、Descriptor等資料。另外,中心裝置也可以向外圍裝置進行資料請求,并向外圍裝置寫入Characteristic、Descriptor等特征值資料。
主要步驟:
1. 調相關接口啟動BLE掃描來擷取外圍裝置(可參考BLE掃描部分);
2. 擷取外圍裝置後,初始化GattClient對象;
3. 調用Connect(GattClientCallback &callback, bool isAutoConnect, int transport)接口,建立與外圍BLE裝置的GATT連接配接,boolean參數isAutoConnect用于設定是否允許裝置在可發現距離内自動建立GATT連接配接。
4. 啟動GATT連接配接後,會觸發OnConnectionStateChanged(int connectionState, int ret)回調,根據回調結果判斷是否連接配接GATT成功。
5. 在GATT連接配接成功時,中心裝置可以調用DiscoverServices()接口,擷取外圍裝置支援的Services、Characteristics等特征值,在回調OnServicesDiscovered(int status)中擷取外圍裝置支援的服務和特征值,并根據UUID判斷是什麼服務。
6. 根據擷取到的服務和特征值,調用read和write方法可以讀取或者寫入對應特征值資料。
5.4 SPP
OpenHarmony藍牙 SPP分為服務端和用戶端程式。藍牙串行端口基于SPP協定(Serial Port Profile),能在藍牙裝置之間建立序列槽進行資料傳輸,手機一般以用戶端的角色主動連接配接SPP協定裝置。主要類對象有:SppClientSocket、SppServerSocket以及SocketFactory。
5.4.1 服務端
程式步驟:
1. 首先打開藍牙;
2. 建立SPP服務端對象指針,建立對象,并且打開監聽狀态
3. Accept一定條件的用戶端的請求
4. 滿足某些條件,關閉用戶端程式
5.4.2 用戶端
程式步驟:
1. 打開藍牙;
2. 建立SppClientSocket對象(或指針);
3. 連接配接server,connect();
4. 斷開連接配接,close()
更多原創内容請關注:深開鴻技術團隊
入門到精通、技巧到案例,系統化分享HarmonyOS開發技術,歡迎投稿和訂閱,讓我們一起攜手前行共建鴻蒙生态。