更新时间:2024-03-19 03:17:33下载pdf
蓝牙 Mesh,也就是将蓝牙设备组成网络,每个蓝牙设备可以通过网络内的蓝牙设备进行通讯,将一端的蓝牙信息通过 Mesh 网络传到较远的另一端。本文介绍安卓版 商用照明 App SDK 的蓝牙 Mesh 相关接口和调用示例。
蓝牙技术联盟(Bluetooth Special Interest Group,简称蓝牙 SIG)蓝牙技术全面支持 Mesh 网状网络。标准蓝牙 Mesh 也叫做 SIG Mesh,是蓝牙技术联盟提出的 Mesh 网络的通讯标准。使用蓝牙 Mesh 进行组网及设备功能的更新,均需要满足标准蓝牙 Mesh 的标准。
专有名词 | 说明 |
---|---|
大小类 | 每个 Mesh 设备都对应一款产品,每个产品都有自己的大小类标示,SDK 中以 pcc 、type 作为大小类标示。 |
Mesh 群组 localId | 2 字节,localId 用于区分每个 Mesh 群组在 Mesh 网中的 唯一标识。当您想控制某个群组中的设备时,就向 Mesh 网络发送此群组对应的 localId 命令即可。 |
Mesh 节点 nodeId | 2 字节,nodeId 用于区分每个 Mesh 设备在 Mesh 网中的 唯一标识。当您想控制某个设备时,就向 Mesh 网络发送此设备对应的 nodeId 命令即可 |
本地连接 | 已配网设备通过蓝牙连接,来控制 Mesh 和指令操作。 |
网关连接 | 已配网设备通过网关连接(网关需和设备在一起,距离不能太远),来控制 Mesh 和指令操作。 |
因为设备的操作,例如增删操作、群组操作,都需要本地蓝牙命令执行一次、云端记录一次。因此,向本地 Mesh 网同步操作信息的同时,也需要向云端同步操作信息。
组成:设备类别值由 设备类型 + 大类 + 小类 组成后小端排列。
举例:
产品 | 类别值 | 说明 |
---|---|---|
标准五路灯 | 1510 | 小端转换后得到 1015:
|
标准四路插座 | 2410 | 小端转换后得到 1024:
|
透传型设备 | xx20 | 小端转换后得到 20xx:2 表示透传设备 |
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.BLUETOOTH" />
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
<uses-permission android:name="android.permission.BLUETOOTH_SCAN" />
<uses-permission android:name="android.permission.BLUETOOTH_ADVERTISE" />
该部分检查逻辑,商用照明 SDK 未提供 API,您可自行检测。
配网就是把未加入到蓝牙 Mesh 网络的蓝牙设备,通过一定的通讯过程,将其加入到蓝牙 Mesh 网络中。
扫描附近符合 SIG 标准的蓝牙设备。当扫描到周围有符合协议规范的待配网设备后,可以对这些设备进行配网。
接口说明
// 开启扫描
void startSearch();
// 停止扫描
void stopSearch();
示例代码
IThingBlueMeshSearchListener iThingBlueMeshSearchListener=new IThingBlueMeshSearchListener() {
@Override
public void onSearched(SearchDeviceBean deviceBean) {
}
@Override
public void onSearchFinish() {
}
};
// 待配网的蓝牙 Mesh 设备 UUID 是固定的
UUID[] MESH_PROVISIONING_UUID = {UUID.fromString("00001827-0000-1000-8000-00805f9b34fb")};
SearchBuilder searchBuilder = new SearchBuilder()
.setServiceUUIDs(MESH_PROVISIONING_UUID) // 蓝牙 Mesh 的 UUID 是固定值
.setTimeOut(100) // 扫描时长,单位秒
.setThingBlueMeshSearchListener(iThingBlueMeshSearchListener).build();
IThingBlueMeshSearch mMeshSearch = ThingOSMesh.activator().newThingBlueMeshSearch(searchBuilder);
// 开启扫描
mMeshSearch.startSearch();
// 停止扫描
mMeshSearch.stopSearch();
扫描到目标设备以后,可以通过查询显示产品配置的名称和图标。
接口说明
void getActivatorDeviceInfo(String productId, String uuid, String mac, IThingDataCallback<ConfigProductInfoBean> callback);
参数说明
参数 | 类型 | 说明 |
---|---|---|
productId | String | SearchDeviceBean.productId ,注意需要将 byte[] 转换为 String 之后,再传入 |
uuid | String | 蓝牙 Mesh 设备为 null |
mac | String | SearchDeviceBean.productId |
示例代码
ThingOSActivator.deviceActivator().getActivatorDeviceInfo(
// btye[] to String
new String(bean.getProductId(), StandardCharsets.UTF_8),
// uuid
null,
// mac
scanDeviceBean.getMacAdress(),
// callback
new IThingDataCallback<ConfigProductInfoBean>() {
@Override
public void onSuccess(ConfigProductInfoBean result) {
}
@Override
public void onError(String errorCode, String errorMessage) {
}
});
回调说明
ConfigProductInfoBean
说明
属性 | 类型 | 说明 |
---|---|---|
name | String | 产品名称,云端配置,一般是用户首次创建产品时的名称 |
icon | String | 产品图标 |
子设备入网有两种方式,一种是通过 App 蓝牙入网,另一种是通过网关直接配子设备入网。
接口说明
// 开启配网
void startActivator();
// 停止配网
void stopActivator();
示例代码
ThingSigMeshActivatorBuilder ThingSigMeshActivatorBuilder = new ThingSigMeshActivatorBuilder()
.setSearchDeviceBeans(mSearchDeviceBeanList)
.setSigMeshBean(sigMeshBean) // 蓝牙 Mesh 基本信息
.setHomeId(gid)//当前配网所处的空间节点对应的 gid,必传
.setTimeOut(100) // 超时时间
.setThingBlueMeshActivatorListener(new IThingBlueMeshActivatorListener() {
@Override
public void onSuccess(String mac, DeviceBean deviceBean) {
L.d(TAG, "subDevBean onSuccess: " + deviceBean.getName());
}
@Override
public void onError(String mac, String errorCode, String errorMsg) {
L.d(TAG, "config mesh error" + errorCode + " " + errorMsg);
}
@Override
public void onFinish() {
L.d(TAG, "config mesh onFinish");
});
IThingBlueMeshActivator iThingBlueMeshActivator = ThingOSMesh.activator().newSigActivator()(ThingSigMeshActivatorBuilder);
// 开启配网
iThingBlueMeshActivator.startActivator();
// 停止配网
iThingBlueMeshActivator.stopActivator();
homeId
参数必须为当前配网所在区域对应的关系 ID,获取方式参考 获取区域对应的关系 ID。
参数说明
参数 | 说明 |
---|---|
mSearchDeviceBeanList | 待配网的设备集合,通过 startSearch 扫描得到的 |
timeout | 配网的超时时间设置,默认是 100s,如果配网设备过多,建议增加超时时长 |
sigMeshBean | SigMeshBean |
出参说明
参数 | 说明 |
---|---|
DeviceBean | 设备信息数据类型,参考 DeviceBean 数据模型 |
errorCode | 参考 配网错误码 章节 |
蓝牙 Mesh 网关本质上为双模设备。
蓝牙 Mesh 子设备可以通过网关直接给子设备配网,配网的方式与 Zigbee 子设备入网方法一致。详情请参考 子设备配网。
错误码 | 错误信息 |
---|---|
21002 | invite 失败 |
21004 | provision 失败 |
21006 | 发送 public key 失败 |
21008 | conform 失败 |
210010 | random conform 失败 |
210014 | 发送数据失败 |
210016 | composition data 失败 |
210018 | 添加 appkey 失败 |
210020 | 绑定 model 失败 |
210022 | publication model 失败 |
210024 | network transmit 失败 |
210026 | 云端注册失败 |
210027 | 设备地址分配已满 |
210034 | notify 失败 |
20021 | 配网超时 |
IThingBlueMeshDevice
类封装了对指定蓝牙 Mesh 内所有设备的操作。
示例代码
IThingBlueMeshPlugin plugin = PluginManager.service(IThingBlueMeshPlugin.class);
IThingBlueMeshDevice mThingBlueMeshDevice = plugin.newSigMeshDeviceInstance("meshIdxxxx");
示例代码
DeviceBean deviceBean = ThingOSDevice.getDeviceBean(mDevId);
DeviceBean gwBean = ThingOSDevice.getDeviceBean(deviceBean.getParentId());
// 综合在线状态 (包括本地在线和网关在线)
boolean online = deviceBean.getIsOnline()
// 设备本地蓝牙在线状态
boolean localOnline = deviceBean.getIsLocalOnline()
// 设备网关在线状态 (需要网关在线且子设备在线,才认为网关真实在线)
boolean wifiOnline = deviceBean.isCloudOnline() && gwBean.getIsOnline()
示例代码
DeviceBean deviceBean = ThingOSDevice..getDeviceBean(mDevId);
// 判读是否是蓝牙 Mesh 设备 (子设备 + 网关)
if(deviceBean.isSigMesh()){
// This device is sig mesh device"
}
// 判读是否是蓝牙 Mesh 网关设备
if(deviceBean.isSigMeshWifi()){
// This device is sig mesh Wi-Fi device
}
接口说明
void renameMeshSubDev(String devId, String name, IResultCallback callback);
参数说明
参数 | 说明 |
---|---|
devId | 设备 ID |
name | 重命名名称 |
callback | 回调 |
示例代码
IThingBlueMeshPlugin plugin = PluginManager.service(IThingBlueMeshPlugin.class);
IThingBlueMeshDevice mThingBlueMeshDevice = plugin.newSigMeshDeviceInstance("meshIdxxxx");
mThingBlueMeshDevice.renameMeshSubDev("devIdxxxx","设备新名称", new IResultCallback() {
@Override
public void onError(String code, String errorMsg) {
// 重命名失败
}
@Override
public void onSuccess() {
// 重命名成功
}
});
云端查询到的 DP 数据可能不是当前设备实时的数据,您可以通过该命令去查询设备的当前数据值。结果通过 IMeshDevListener
的 onDpUpdate
方法返回。
接口说明
void querySubDevStatusByLocal(String pcc, final String nodeId, final IResultCallback callback);
参数说明
参数 | 说明 |
---|---|
pcc | 设备大小类 |
nodeId | 设备的 nodeId |
callback | 回调 |
示例代码
IThingBlueMeshPlugin plugin = PluginManager.service(IThingBlueMeshPlugin.class);
IThingBlueMeshDevice mThingBlueMeshDevice = plugin.newSigMeshDeviceInstance("meshIdxxxx");
mThingBlueMeshDevice.querySubDevStatusByLocal(devBean.getCategory(), devBean.getNodeId(), new IResultCallback() {
@Override
public void onError(String code, String errorMsg) {
// 查询错误
}
@Override
public void onSuccess() {
// 查询成功,见 IMeshDevListener 的回调
}
});
当蓝牙 Mesh 成功组网之后,SDK 提供了若干种在离线策略。
以下是设置蓝牙 Mesh 在离线的策略的代码示例。
示例代码
// 设置采取查询蓝牙 Mesh 下所有子设备的在线状态策略
SigMeshConfiguration.OnlineMode onlineMode = SigMeshConfiguration.OnlineMode.RESPONSE_ONLINE;
ISigMeshControl sigMeshControl = ThingOSMesh.getSigMeshControl(mMeshId);
if (sigMeshControl != null){
SigMeshConfiguration sigMeshConfiguration = sigMeshControl.getSigMeshConfiguration();
if (sigMeshConfiguration != null){
sigMeshConfiguration.setOnlineMode(onlineMode);
}
}
在线状态策略
SigMeshConfiguration.OnlineMode
枚举值 | 说明 |
---|---|
DEFAULT | 组网成功之后,默认所有子设备在线 |
RESPONSE_ONLINE | 组网成功之后,查询蓝牙 Mesh 下所有的子设备的在线状态 |
方式一:根据蓝牙 Mesh 的在离线策略自动查询
根据选择蓝牙 Mesh 连接成功之后的在离线策略,进行智能查询子设备在线状态。根据 OnlineMode
查询设备在线状态:
OnlineMode
为 RESPONSE_ONLINE
时,自动查询所有设备的在线状态。OnlineMode
为 DEFAULT
时,自动只查询离线设备的在线状态。接口说明
ISigMeshControl sigMeshControl = ThingOSMesh.getSigMeshControl(mMeshId);
if (sigMeshControl != null){
sigMeshControl.queryAllOnLineStatusByLocal(new IResultCallback() {
@Override
public void onError(String code, String error) {
// 发送查询命令失败
}
@Override
public void onSuccess() {
// 发送查询命令成功
}
});
}
方式二:自定义查询设备在线状态
接口说明
ISigMeshControl sigMeshControl = ThingOSMesh.getSigMeshControl(mMeshId);
int queryStrategy = 0;
//查询策略,0:查询蓝牙 Mesh 下所有的子设备,1:仅查询离线设备
if (sigMeshControl != null){
sigMeshControl.queryMeshDeviceOnlineStatusByLocal(queryStrategy, new IResultCallback() {
@Override
public void onError(String code, String error) {
// 发送查询命令失败
}
@Override
public void onSuccess() {
// 发送查询命令成功
}
});
}
参数说明
枚举值 | 说明 |
---|---|
queryStrategy | 设置查询策略:
|
接口说明
void removeMeshSubDev(String devId, IResultCallback callback);
参数说明
参数 | 说明 |
---|---|
devId | 设备 ID |
pcc | 设备大小类 |
callback | 回调 |
示例代码
IThingBlueMeshPlugin plugin = PluginManager.service(IThingBlueMeshPlugin.class);
IThingBlueMeshDevice mThingBlueMeshDevice = plugin.newSigMeshDeviceInstance("meshIdxxxx");
mThingBlueMeshDevice.removeMeshSubDev(devBean.getDevId(), devBean.getCategory(), new IResultCallback() {
@Override
public void onError(String code, String errorMsg) {
}
@Override
public void onSuccess() {
}
});
由于涂鸦使用 DP 管理设备的功能,因此,蓝牙 Mesh 相关的控制是根据设备的 DP 信息来进行操作。IThingBlueMeshDevice
类提供了对蓝牙 Mesh 设备的操作。
按照以下格式,发送控制指令:
{
"(dpId1)":(dpValue1),
"(dpId2)":(dpValue2)
}
DP 指令详情,参考 设备控制。
接口说明
void publishDps(String nodeId, String pcc, String dps, IResultCallback callback);
参数说明
参数 | 说明 |
---|---|
nodeId | 子设备本地编号 |
pcc | 设备产品大小类 |
dps | 设备功能的 DP ID 列表,更多详情,参考 设备控制 |
callback | 回调 |
示例代码
String dps = {"1":false};
IThingBlueMeshPlugin plugin = PluginManager.service(IThingBlueMeshPlugin.class);
IThingBlueMeshDevice mThingBlueMeshDevice = plugin.newSigMeshDeviceInstance("meshIdxxxx");
mThingSigMeshDevice.publishDps(devBean.getNodeId(), devBean.getCategory(), dps, new IResultCallback() {
@Override
public void onError(String s, String s1) {
// 发送 DPs 失败
}
@Override
public void onSuccess() {
// 发送 DPs 成功
}
});
Mesh 网内相关信息,例如 DP 数据、状态变更、设备名称和设备移除等,会实时同步到 IMeshDevListener
。
示例代码
mThingSigMeshDevice.registerMeshDevListener(new IMeshDevListener() {
/**
* 数据更新
* @param nodeId 更新设备的 nodeId
* @param dps dp 数据
* @param isFromLocal 数据来源,true 表示从本地蓝牙,false 表示从云端
*/
@Override
public void onDpUpdate(String nodeId, String dps,boolean isFromLocal) {
// 可以通过 node 来找到相对应的 DeviceBean
DeviceBean deviceBean = mThingBlueMeshDevice.getMeshSubDevBeanByNodeId(nodeId);
}
/**
* 设备状态的上报
* @param online 在线设备列表
* @param offline 离线设备列表
*/
@Override
public void onStatusChanged(List<String> online, List<String> offline,String gwId) {
}
/**
* 网络状态变化
* @param devId
* @param status
*/
@Override
public void onNetworkStatusChanged(String devId, boolean status) {
}
/**
* raw 类型数据上报
* @param bytes
*/
@Override
public void onRawDataUpdate(byte[] bytes) {
}
/**
* 设备信息变更(名称等)
* @param bytes
*/
@Override
public void onDevInfoUpdate(String devId) {
}
/**
* 设备移除
* @param devId
*/
@Override
public void onRemoved(String devId) {
}
});
您可以通过 OTA(Over-the-air)的方式给蓝牙 Mesh 子设备进行升级。
蓝牙 Mesh 低功耗设备在升级之前,需要被唤醒。不同设备的唤醒方式不同。
接口说明
void requestUpgradeInfo(String devId, IRequestUpgradeInfoCallback callback);
参数说明
参数 | 说明 |
---|---|
devId | 需要升级的设备 ID |
callback | 检查回调 |
示例代码
IThingBlueMeshPlugin iThingBlueMeshPlugin = PluginManager.service(IThingBlueMeshPlugin.class);
iThingBlueMeshPlugin.getMeshInstance().requestUpgradeInfo(mDevID, new IRequestUpgradeInfoCallback() {
@Override
public void onSuccess(ArrayList<BLEUpgradeBean> bleUpgradeBeans) {
}
@Override
public void onError(String errorCode, String errorMsg) {
}
});
BLEUpgradeBean
返回固件升级的信息,提供以下信息:
字段 | 类型 | 描述 |
---|---|---|
upgradeStatus | int | 升级状态:
|
version | String | 最新版本 |
currentVersion | String | 当前版本 |
timeout | int | 超时时间,单位:秒 |
upgradeType | int |
|
type | int | 固件来源:
|
typeDesc | String | 固件来源描述 |
lastUpgradeTime | long | 上次升级时间,单位:毫秒 |
url | String | 新固件下载地址,当 type 取值为 1 或者 9 的时候,url 有值 |
fileSize | long | 新固件大小 |
md5 | String | 新固件 MD5 值 |
接口说明
void startOta();
示例代码
private MeshUpgradeListener mListener = new MeshUpgradeListener() {
@Override
public void onUpgrade(int percent) {
// 升级进度
}
@Override
public void onSendSuccess() {
// 固件数据发送成功
}
@Override
public void onUpgradeSuccess() {
// 升级成功
mMeshOta.onDestroy();
}
@Override
public void onFail(String errorCode, String errorMsg) {
// 升级失败
mMeshOta.onDestroy();
}
};
// 查询指定文件的字节流
byte data[] = getFromFile(path);
ThingBlueMeshOtaBuilder build = new ThingBlueMeshOtaBuilder()
.setData(data)
.setMeshId(mDevBean.getMeshId())
.setMac(mDevBean.getMac())
.setProductKey(mDevBean.getProductId())
.setNodeId(mDevBean.getNodeId())
.setDevId(mDevID)
.setVersion("version")
.setThingBlueMeshActivatorListener(mListener)
.bulid();
IThingBlueMeshOta mMeshOta = ThingOSMesh.newOTA(build);
// 开始升级
mMeshOta.startOta();
入参说明
ThingBlueMeshOtaBuilder
参数 | 类型 | 说明 |
---|---|---|
data | byte[] | 待升级固件的字节流 |
meshId | String | 设备蓝牙 meshId |
mac | String | 设备 MAC 地址 |
productKey | String | 设备产品 ID |
mNodeId | String | 设备 nodeId |
devId | String | 设备 ID |
version | String | 待升级固件的版本号 |
蓝牙 Mesh 网关升级和普通的 Wi-Fi 设备升级一样,以下是示例代码。
示例代码
private IOtaListener iOtaListener = new IOtaListener() {
@Override
public void onSuccess(int otaType) {
// 升级成功
}
@Override
public void onFailure(int otaType, String code, String error) {
// 升级失败
}
@Override
public void onProgress(int otaType, final int progress) {
// 升级进度
}
};
IThingOtaPlugin otaPlugin = PluginManager.service(IThingOtaPlugin.class));
IThingOta iThingOta = otaPlugin.newOTAInstance(devId);
iThingOta.setOtaListener(mOtaListener);
// 开始升级
iThingOta.startOta();
// 销毁升级
iThingOta.onDestroy();
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈