更新时间:2023-06-01 06:19:38下载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 节点 node ID | 2 字节,node ID 用于区分每个 Mesh 设备在 Mesh 网中的 唯一标识。当您想控制某个设备时,就向 Mesh 网发此设备对应的 nodeId 命令即可 |
本地连接 | 已配网设备通过蓝牙连接,来控制 Mesh 和指令操作。 |
网关连接 | 已配网设备通过网关连接(网关需和设备在一起,距离不能太远),来控制 Mesh 和指令操作。 |
因为设备的操作,例如增删操作、群组操作,都需要本地蓝牙命令执行一次、云端记录一次。因此,向本地 Mesh 网同步操作信息的同时,也需要向云端同步操作信息。
组成:设备类别值由 设备类型
+ 大类
+ 小类
组成后小端排列。
举例:
产品 | 类别值 | 说明 |
---|---|---|
标准五路灯 | 1510 | 小端转换后得到 1015:
|
标准四路插座 | 2410 | 小端转换后得到 1024:
|
透传型设备 | xx20 | 小端转换后得到 20xx:2 表示透传设备 |
手机系统要求:蓝牙使用需要安卓 4.3 以及以上版本,智能生活 App SDK 从安卓 4.4 开始支持。
Manifest 权限:
<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" />
蓝牙权限检测:每次扫描和连接前,都要进行检测,否则 App 无法正常使用蓝牙。
该部分检查逻辑,智能生活 SDK 未提供 API,您可自行检测。
初始化家庭:开发蓝牙 Mesh 时,请先熟悉智能生活 App SDK。蓝牙 Mesh 中所有操作都建立在家庭数据已经初始化的基础上。完全初始化家庭操作,请参考 家庭管理。
一个家庭里可以拥有多个蓝牙 Mesh,但建议只创建一个 Mesh。
接口说明
void createSigMesh(IThingResultCallback<SigMeshBean> callback);
示例代码
ThingHomeSdk.newHomeInstance(123xxx) // 参数为 long 类型的 homeId
.createSigMesh(new IThingResultCallback<SigMeshBean>() {
@Override
public void onError(String errorCode, String errorMsg) {
}
@Override
public void onSuccess(SigMeshBean sigMeshBean) {
}
});
删除 Mesh 时,如果 Mesh 组里有子设备,会被同时移除。
接口说明
void removeMesh(IResultCallback callback);
示例代码
ThingHomeSdk.newSigMeshDeviceInstance(meshId).removeMesh(new IResultCallback() {
@Override
public void onError(String errorCode, String errorMsg) {
}
@Override
public void onSuccess() {
}
});
通过初始化 home
实例后,可以查询到对应家庭下的 mesh
列表。
接口说明
List<SigMeshBean> getSigMeshList();
示例代码
IThingHome mThingHome = ThingHomeSdk.newHomeInstance(123xxx); // 参数为 homeId
if (mThingHome.getHomeBean() != null){
List<SigMeshBean> meshList = ThingHomeSdk.getSigMeshInstance().getSigMeshList()
}
接口说明
List<DeviceBean> getMeshSubDevList();
示例代码
List<DeviceBean> meshSubDevList = ThingHomeSdk.newSigMeshDeviceInstance("meshIdxxxxx").getMeshSubDevList();
初始化 Mesh 网络,并注册来自云端状态变化的监听。在切换、初始化 Mesh 网络时调用该接口。
接口说明
void initMesh(String meshId);
示例代码
ThingHomeSdk.getThingSigMeshClient().initMesh("meshIdxxxxx");
建议在切换家庭时,销毁当前 Mesh,然后重新初始化家庭中的 Mesh。
接口说明
void destroyMesh();
示例代码
ThingHomeSdk.getThingSigMeshClient().destroyMesh();
IThingBlueMeshClient
提供开始连接、断开连接、开启扫描、停止扫描能力。
示例代码
// 开启连接
ThingHomeSdk.getThingSigMeshClient().startClient(mSigMeshBean);
// 开启连接,指定扫描的时间
ThingHomeSdk.getThingSigMeshClient().startClient(mSigMeshBean,searchTime);
// 断开连接
ThingHomeSdk.getThingSigMeshClient().stopClient();
// 开启扫描
ThingHomeSdk.getThingSigMeshClient().startSearch()
// 停止扫描
ThingHomeSdk.getThingSigMeshClient().stopSearch();
后台一直扫描会消耗资源,可以通过开启扫描和停止扫描来控制后台的扫描:
startClient(mSigMeshBean)
开启连接后,会在后台不断地扫描周围可连接设备,直到连接成功为止。startClient(mSigMeshBean,searchTime)
开启连接后,searchTime
时间内没有扫描到设备,就停止扫描。startClient()
时,调用 startSearch()
和 stopSearch()
是没有效果的。startSearch
和 stopSearch
是没有效果的。配网,就是把未加入到 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 = ThingHomeSdk.getThingBlueMeshConfig().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 |
示例代码
ThingHomeSdk.getActivatorInstance().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 基本信息
.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 = ThingHomeSdk.getThingBlueMeshConfig().newSigActivator(ThingSigMeshActivatorBuilder);
// 开启配网
iThingBlueMeshActivator.startActivator();
// 停止配网
iThingBlueMeshActivator.stopActivator();
参数说明
参数 | 说明 |
---|---|
mSearchDeviceBeanList | 待配网的设备集合,通过 startSearch 扫描得到的 |
timeout | 配网的超时时间设置,默认是 100s,如果配网设备过多,建议增加超时时长 |
sigMeshBean | SigMeshBean |
出参说明
参数 | 说明 |
---|---|
DeviceBean | 设备信息数据类型,参考 DeviceBean 数据模型 |
errorCode | 参考 配网错误码 章节 |
蓝牙 Mesh 网关本质上为双模设备;
使用 Wi-Fi 设备的配网,请参考 Wi-Fi 快连模式。
使用单点蓝牙双模配网,请参考 双模设备配网。
蓝牙 Mesh 子设备可以通过网关直接给子设备配网,配网的方式与 Zigbee 子设备入网方法一致。详情请参考 子设备配网。
错误码 | 错误信息 |
---|---|
21002 | invite 失败 |
21004 | provision 失败 |
21006 | send public key 失败 |
21008 | conform 失败 |
210010 | random conform 失败 |
210014 | send data 失败 |
210016 | composition data 失败 |
210018 | add appkey 失败 |
210020 | bind model 失败 |
210022 | publication model 失败 |
210024 | network transmit 失败 |
210026 | 云端注册失败 |
210027 | 设备地址分配已满 |
210034 | notify 失败 |
20021 | 配网超时 |
IThingBlueMeshDevice
类封装了对指定 Mesh 内所有设备的操作。
示例代码
IThingBlueMeshDevice mThingBlueMeshDevice = ThingHomeSdk.newSigMeshDeviceInstance("meshIdxxxx");
示例代码
DeviceBean deviceBean=ThingHomeSdk.getDataInstance().getDeviceBean(mDevId);
DeviceBean gwBean=ThingHomeSdk.getDataInstance().getDeviceBean(deviceBean.getParentId());
// 综合在线状态 (包括本地在线和网关在线)
boolean online=deviceBean.getIsOnline()
// 设备本地蓝牙在线状态
boolean localOnline=deviceBean.getIsLocalOnline()
// 设备网关在线状态 (需要网关在线且子设备在线 才认为网关真实在线)
boolean wifiOnline=deviceBean.isCloudOnline() && gwBean.getIsOnline()
示例代码
DeviceBean deviceBean=ThingHomeSdk.getDataInstance().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 | 回调 |
示例代码
IThingBlueMeshDevice mThingBlueMeshDevice= ThingHomeSdk.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 | 回调 |
示例代码
IThingBlueMeshDevice mThingBlueMeshDevice = ThingHomeSdk.newSigMeshDeviceInstance("meshIdxxxx");
mThingBlueMeshDevice.querySubDevStatusByLocal(devBean.getCategory(), devBean.getNodeId(), new IResultCallback() {
@Override
public void onError(String code, String errorMsg) {
// 查询错误
}
@Override
public void onSuccess() {
// 查询成功,见 IMeshDevListener 的回调
}
});
接口说明
void removeMeshSubDev(String devId, IResultCallback callback);
参数说明
参数 | 说明 |
---|---|
devId | 设备 ID |
pcc | 设备大小类 |
callback | 回调 |
示例代码
IThingBlueMeshDevice mThingBlueMeshDevice = ThingHomeSdk.newSigMeshDeviceInstance("meshIdxxxx");
mThingBlueMeshDevice.removeMeshSubDev(devBean.getDevId(), devBean.getCategory(), new IResultCallback() {
@Override
public void onError(String code, String errorMsg) {
}
@Override
public void onSuccess() {
}
});
IThingGroup
类提供了对蓝牙 Mesh 群组的操作。
可以通过群组中是否具备 MeshId
来区分 Mesh 群组和普通 Wi-Fi 群组。
示例代码
GroupBean groupBean=ThingHomeSdk.getDataInstance().getGroupBean("groupId");
if(!TextUtils.isEmpty(groupBean.getMeshId())){
// This group is mesh group"
}
一个 Mesh 网内支持创建 16128 个群组,返回时 ID 范围为 16 进制的 C000
~ FEFF
字符串,由本地进行维护。
接口说明
void addGroup(String name, String pcc, String localId,IAddGroupCallback callback);
参数说明
参数 | 说明 |
---|---|
name | 群组名称 |
pcc | 群组中设备的大小类,支持跨小类创建 |
localId | 群组的 localId,范围为 16 进制的 C000 ~ FEFF 字符串 |
callback | 回调 |
示例代码
IThingBlueMeshDevice mThingSigMeshDevice= ThingHomeSdk.newSigMeshDeviceInstance("meshId");
mThingSigMeshDevice.addGroup("群组名称","大小类", "C001", new IAddGroupCallback() {
@Override
public void onError(String errorCode, String errorMsg) {
// 创建群组失败
}
@Override
public void onSuccess(long groupId) {
// 创建群组成功
}
});
接口说明
void addDevice(String devId,IResultCallback callback);
参数说明
参数 | 说明 |
---|---|
devId | 设备 ID |
callback | 回调 |
示例代码
IThingGroup mGroup = ThingHomeSdk.newSigMeshGroupInstance(groupId);
mGroup.addDevice("devId", new IResultCallback() {
@Override
public void onError(String code, String errorMsg) {
// 添加设备到群组失败
}
@Override
public void onSuccess() {
// 添加设备到群组成功
}
});
接口说明
void removeDevice(String devId,IResultCallback callback);
参数说明
参数 | 说明 |
---|---|
devId | 设备 ID |
callback | 回调 |
示例代码
IThingGroup mGroup = ThingHomeSdk.newSigMeshGroupInstance(groupId);
mGroup.removeDevice("devId", new IResultCallback() {
@Override
public void onError(String code, String errorMsg) {
// 移除子设备失败
}
@Override
public void onSuccess() {
// 移除子设备成功
}
});
接口说明
void dismissGroup(IResultCallback callback);
参数说明
参数 | 说明 |
---|---|
callback | 回调 |
示例代码
IThingGroup mGroup = ThingHomeSdk.newSigMeshGroupInstance(groupId);
mGroup.dismissGroup(new IResultCallback() {
@Override
public void onError(String code, String errorMsg) {
// 解散群组失败
}
@Override
public void onSuccess() {
// 解散群组成功
}
});
接口说明
void renameGroup(String groupName,IResultCallback callback);
参数说明
参数 | 说明 |
---|---|
groupName | 新的群组名称 |
callback | 回调 |
示例代码
IThingGroup mGroup = ThingHomeSdk.newSigMeshGroupInstance(groupId);
mGroup.renameGroup("群组名称",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};
IThingBlueMeshDevice mThingSigMeshDevice=ThingHomeSdk.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 成功
}
});
接口说明
void multicastDps(String localId, String pcc, String dps, IResultCallback callback)
参数说明
参数 | 说明 |
---|---|
localId | 群组本地编号 |
pcc | 设备产品大小类 |
dps | 设备功能的 DP ID 列表,更多详情请参考 设备控制 |
callback | 回调 |
示例代码
String dps = {"1":false};
IThingBlueMeshDevice mThingSigMeshDevice= ThingHomeSdk.newSigMeshDeviceInstance("meshId");
mThingSigMeshDevice.multicastDps(groupBean.getLocalId(), devBean.getCategory(), dps, new IResultCallback() {
@Override
public void onError(String errorCode, String errorMsg) {
// 发送 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 | 检查回调 |
示例代码
ThingHomeSdk.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 = ThingHomeSdk.newMeshOtaManagerInstance(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) {
// 升级进度
}
};
IThingOta iThingOta = ThingHomeSdk.newOTAInstance(devId);
iThingOta.setOtaListener(mOtaListener);
// 开始升级
iThingOta.startOta();
// 销毁升级
iThingOta.onDestroy();
该内容对您有帮助吗?
是意见反馈该内容对您有帮助吗?
是意见反馈