#夏日挑战赛#初识OpenHarmony蓝牙模块 原创 精华
本文正在参加星光计划3.0–夏日挑战赛
作者:巴延兴
1. 简介
随着现代移动通信和物联网的快速发展,万物互联已经越来越成为一个基本需求,而近距离的无线通信更是在移动设备之间有着及其广泛的应用。蓝牙技术是一种无线数据和语音传输的开放式标准,它是基于低成本的近距离无线连接,为固定和移动设备建立通信的一种近距离无线技术连接。蓝牙技术正广泛运用于汽车领域、工业生产、医疗领域。
1.1 OpenHarmony架构图
蓝牙子系统所属的分布式软总线在整个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开发技术,欢迎投稿和订阅,让我们一起携手前行共建OpenHarmony生态。
感谢老师对蓝牙部分详细的讲解。