如何使用NearLink Kit实现星闪广播 原创
一、星闪简介
讲解NearLink Kit之前,先简单介绍以下什么是星闪。
星闪是华为牵头与星闪联盟全栈原创的新一代先短距通信技术,星闪技术具备低时延、高可靠、高同步精度、支持多并发、高信息安全和低功耗等卓越技术特性。空口接入层技术是星闪无线通信系统的核心。根据星闪接入层的角色不同,星闪设备分为G(Grant)节点和T(Terminal) 节点,每个G节点可以管理一定数量的T结点,G节点以及与其连接的T节点共同组成一个通信域。为了满足不同场景下的通信需求,星闪技术提供了SLB(SparkLink Basic,星闪基础接入技术)和SLE(Sparklink Low Energy,星闪低功耗接入技术)两种无线通信接口。
SLB(SparkLink Basic,星闪基础接入技术)的技术内容由中YD/T 4007-2022规定。 SLB使用正交多载波(OFDM)波形,支持极低时延无线帧,空口单向数据传输时延小于 20.833us(业界最低时延),单载波支持20MHz带宽,最大支持16载波共320MHz带宽, 最高速率支持编码速率0.92的信道编码、1024QAM调制和8流多路并行传输,最深覆盖支 持编码速率1/8的信道编码和QPSK调制。SLB主要用于承载以车载主动降噪、无线投屏、工业机械运动控制等为代表的业务场景,其显著特征是低时延、高可靠、精同步和高并发等。
SLE(Sparklink Low Energy,星闪低功耗接入技术)的技术内容由T/XS 10002 2022规定,可提供低成本、低功耗的空口接入。SLE使用单载波传输,带宽支持1MHz、 2MHz和4MHz。SLE显著的特点是通过采用Polar信道编码提升传输可靠性,减少重传节省功耗,精简广播信道功能和业务以减少拥塞可能。SLE主要用于承载包括耳机音频传输、无 线电池管理系统、工业数据采集在内的具备低功耗要求的业务场景。SLE的性能指标分析见下图。
更多内容可参考星闪无线短距通信技术(SparkLink1.0)产业化推进白皮书。
二、NearLink Kit
NearLink Kit(星闪服务)是HarmonyOS提供的一种低功耗、高速率的短距离通信服务,支持星闪设备之间的连接、数据交互。可以注意到这里的星闪英文用NearLink而不是SparkLink。目前从API 13(Beta)开放的接口看,支持SLE设备间连接与数据交互。具体支持以下两种方式:
方式一:手机开启广播,被外围设备扫描
- 手机终端可开启星闪广播服务。
- 外围设备可扫描广播,可获取到手机的广播数据。
- 外围设备可连接手机,连接受进行相应的数据传输。
方式二:手机扫描广播,连接外围设备
- 外围设备开启广播,等待被连接。
- 手机终端作为中心设备,通过扫描,发现外围设备,获取其广播数据。
- 外围设备和手机连接之后可以进行相应的数据传输。
上述两种工作方式其实就是看手机和外围设备哪个作为中心设备,星闪中角色很重要,不同角色的能力不同,例如目前客户端可以连接多个服务端,但是服务端不可以连接多个客户端。类似于蓝牙客户端、服务端广播和连接协议,SLE也有相应的交互协议,星闪使用SSAP(SparkLink Service Access Protocol,星闪服务交互协议)描述服务端、客户端的交互。
简单来说,若手机作为星闪客户端,则需要创建SSAP客户端实例;若手机作为星闪服务端,则需要创建SSAP服务端实例。使用NearLink Kit场景有鼠标、手写笔、智能家电、车钥匙等。
三、NearLink 广播
本文内容介绍如何将手机作为服务端,开启一个简单的广播。实现如下功能:
-
可自定义广播UUID、广播内容
-
可订阅广播状态变化
-
可被外围设备扫描。
开发步骤如下。
1.准备软硬件环境
开发环境:DevEco Studio 5.0.1 Beta3 配套SDK为API 13 beta及以上。
终端设备:目前已知Mate 60 Pro、Pura70系列支持。确认方法:进入“设置 > 多设备协同”界面(不同产品或系统版本可能为“设置 > 更多设备”),确认“星闪”选项存在。若选项不存在,则设备不支持星闪功能。
新建应用:在满足以上要求的前提下,新建一个名为SparkLinkTool的空应用。
2.申请权限
请参考“应用开发准备”完成基本准备工作及指纹配置,再继续进行以下开发活动。需要开发者动态申请星闪权限ohos.permission.ACCESS_NEARLINK。
3.广播接口说明
常用接口说明如下:
接口名 | 描述 |
---|---|
startAdvertising(advertisingParams: AdvertisingParams): Promise<number> | 启动星闪广播。 |
stopAdvertising(advertisingId: number): Promise<void> | 停止星闪广播。 |
on(type: ‘advertisingStateChange’, callback: Callback<AdvertisingStateChangeInfo>): void | 订阅星闪广播状态变化事件。 |
off(type: ‘advertisingStateChange’, callback?: Callback<AdvertisingStateChangeInfo>): void | 取消订阅星闪广播状态变化事件。 |
4.广播实现
我们将所有功能封装在NearLinkAdvUtils类中,实现框架如下:
import Logger from '../common/Logger';
import { advertising } from '@kit.NearLinkKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { scan } from '@kit.NearLinkKit';
// 星闪广播工具
const TAG: string = '[NearLinkAdvUtils]';
export class NearLinkAdvUtil
{
// 订阅星闪广播状态变化事件。
public static SubscribeNearLinkAdvState()
{
}
// 取消订阅星闪广播状态变化事件。
public static UnsubscribeNearLinkState()
{
}
// 设置广播参数、数据
public SetNearLinkAdvData(UUID:string,ServerData:Uint8Array)
{
}
public StartNearLinkAdv(){
}
// 停止广播
public StopNearLinkAdv(){
}
}
export default new NearLinkAdvUtil();
下面我们展开讲解NearLinkAdvUtils类中的关键方法实现:
4.1 导入相关模块。
import { advertising } from '@kit.NearLinkKit';import { BusinessError } from '@kit.BasicServicesKit';
4.2 订阅星闪广播状态变化事件。
// 订阅星闪广播状态变化事件。
public static SubscribeNearLinkAdvState()
{
let onReceiveEvent:(data: advertising.AdvertisingStateChangeInfo) => void = (data: advertising.AdvertisingStateChangeInfo) => {
Logger.info(TAG,'advertisingId:'+ data.advertisingId);
Logger.info(TAG,'advertisingState:'+ data.state);
}
try {
advertising.on('advertisingStateChange', onReceiveEvent);
} catch (err) {
Logger.error(TAG,'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}
4.3 构造用户需要的广播参数及数据。
// 设置广播参数、数据
public SetNearLinkAdvData(input_serviceData_data:Uint8Array)
{
let manufactureValueBuffer = new Uint8Array(4);
manufactureValueBuffer[0] = 1;
manufactureValueBuffer[1] = 2;
manufactureValueBuffer[2] = 3;
manufactureValueBuffer[3] = 4;
let serviceValueBuffer = new Uint8Array(4);
serviceValueBuffer[0] = input_serviceData_data[0];
serviceValueBuffer[1] = input_serviceData_data[1];
serviceValueBuffer[2] = input_serviceData_data[2];
serviceValueBuffer[3] = input_serviceData_data[3];
Logger.info(TAG,'manufactureValueBuffer = '+ JSON.stringify(manufactureValueBuffer));
Logger.info(TAG,'serviceValueBuffer = '+ JSON.stringify(serviceValueBuffer));
let setting: advertising.AdvertisingSettings = {
interval:5000,
power:advertising.TxPowerMode.ADV_TX_POWER_LOW
};
let manufactureDataUnit: advertising.ManufacturerData = {
manufacturerId:4567,
manufacturerData:manufactureValueBuffer
};
let serviceDataUnit: advertising.ServiceData = {
serviceUuid:"37bea880-fc70-11ea-b720-000000001234",
serviceData:serviceValueBuffer
};
NearLinkAdvUtil.advData = {
serviceUuids:["37bea880-fc70-11ea-b720-000000001234"],
manufacturerData:[manufactureDataUnit],
serviceData:[serviceDataUnit]
};
NearLinkAdvUtil.advertisingParams= {
advertisingSettings: setting,
advertisingData: NearLinkAdvUtil.advData
};
}
4.4 开启星闪广播,返回advertisingId表示当前广播索引。
public StartNearLinkAdv(){
try { advertising.startAdvertising(NearLinkAdvUtil.advertisingParams).then((advertisingId:number) => {
NearLinkAdvUtil.advId = advertisingId;
Logger.info(TAG,'advertising id:'+ JSON.stringify(NearLinkAdvUtil.advId));
});
} catch (err) {
Logger.error(TAG,'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}
4.5 停止星闪广播,其中advId是步骤4开启广播后返回的advertisingId。
// 停止广播
public StopNearLinkAdv(){
try {
advertising.stopAdvertising(NearLinkAdvUtil.advId).then(() => {
Logger.info(TAG,"stop advertising success");
});
} catch (err) {
Logger.error(TAG,'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}
4.6 取消订阅星闪广播状态变化事件。 调用advertising.off实现
// 取消订阅星闪广播状态变化事件。
public static UnsubscribeNearLinkState()
{
let onReceiveEvent:(data: advertising.AdvertisingStateChangeInfo) => void = (data: advertising.AdvertisingStateChangeInfo) => {
Logger.info(TAG,'advertisingId:'+ data.advertisingId);
Logger.info(TAG,'advertisingState:'+ data.state);
}
try {
advertising.off('advertisingStateChange', onReceiveEvent);
} catch (err) {
Logger.error(TAG,'errCode: ' + (err as BusinessError).code + ', errMessage: ' + (err as BusinessError).message);
}
}