
没有返回状态说明连接成功。
也就是说链路层的数据只存在发送成功(可能经过重发N次后成功),或者连接断开,不存在失败的可能性,也就无所谓返回状态的区别。
onCharacteristicWrite返回的status状态看一下API就知道了,都是GATT开头的各种常量,也就是说从设备一定是接收到了数据,但是数据从链路层转发到GATT层的过程中可能存在各种异常情况,导致返回错误。需要注意的是应用层无法决定这个返回状态,比如说如果从设备的应用层接收到数据后发现不符合自己定义的接口数据协议规范,那么也只能通过另外发起notify来通知主设备,而无法通过status来返回错误类型。
文档以Github为主 博客文章不会经常更新。
Github仓库地址 >
//初始化蓝牙
initBlue() {
var that = this;
wxopenBluetoothAdapter({ //调用微信小程序api 打开蓝牙适配器接口
success: function (res) {
consolelog('1初始化蓝牙成功')
},
//监听手机蓝牙的开关
monitorTheBlue:function(){
var that =this;
wxonBluetoothAdapterStateChange(function(res){
})
},
//开始获取附近的蓝牙设备
// 获取到附近的蓝牙数组 通过蓝牙特定的名称获取自己想要连接的蓝牙设备
// 获取附近蓝牙设备的数组
findBlue() {
consolelog(new Date())
var that = this
wxstartBluetoothDevicesDiscovery({
allowDuplicatesKey: false,
interval: 0,
success: function (res) {
consolelog('2正在搜索设备')
if (thatdataisFirestShow) {
wxshowLoading({
title: '正在搜索设备'
})
}
},
//搜索获取附近的所有蓝牙设备 获取附近所有的蓝牙设备的相关信息 获取需要连接蓝牙设备的deviceID
// 通过bluetoothDeviceName 和 localName 来确定制定蓝牙
// 一般根据制定设备的名字去连接 设备的名字 是出产厂家设定
getBlue() {
var that = this
wxgetBluetoothDevices({
success: function (res) {
consolelog('3找到设备列表')
wxhideLoading()
// return false
var index = 10
for (var i = 0; i < resdeviceslength; i++) {
if (resdevices[i]name && resdevices[i]localName) {
var arr = resdevices[i]namesplit("-")
var secArr = resdevices[i]localNamesplit("-")
if (arr[0] == thatdatabluetoothDeviceName || secArr[0] == thatdatabluetoothDeviceName) {
},
//连接蓝牙设备
//通过deviceId 连接蓝牙
/
},
//6 连接上需要的蓝牙设备之后,获取这个蓝牙设备的服务uuid
//获取设备的uuid
getServiceId() {
var that = this
wxgetBLEDeviceServices({
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId: thatdatadeviceId,
success: function (res) {
var model = resservices[1]
thatsetData({
servicesUUID: modeluuid
})
consolelog('7获取设备 uuid 成功')
thatgetCharacteId() //60
}
})
},
//7 如果一个蓝牙设备需要进行数据的写入以及数据传输,就必须具有某些特征值,所以通过上面步骤获取的id可以查看当前蓝牙设备的特征值
//notify write read 当只有 notify为true的时候才能 接收蓝牙设备传来的数据,
//write 为true 才能传入数据
//read 为true 才能读取设备数据
getCharacteId() {
var that = this
wxgetBLEDeviceCharacteristics({
// 这里的 deviceId 需要已经通过 createBLEConnection 与对应设备建立链接
deviceId: thatdatadeviceId,
// 这里的 serviceId 需要在上面的 getBLEDeviceServices 接口中获取
serviceId: thatdataservicesUUID,
success: function (res) {
for (var i = 0; i < rescharacteristicslength; i++) { //2个值
var model = rescharacteristics[i]
if (modelpropertiesnotify == true) {
thatsetData({
characteristicId: modeluuid //监听的值
})
consolelog('8modelpropertiesnotify == true')
thatstartNotice(modeluuid) //70
}
// if (modelpropertiesread == true) {
// thatreadData(modeluuid)
// }
// if (modelpropertieswrite == true) {
// thatsetData({
// writeId: modeluuid//用来写入的值
// })
// }
}
}
})
},
fordateTime1(){
let now = new Date(),hour = nowgetHours()
consolelog(hour)
let str = ''
if(hour < 7){str = '早餐前'}
else if ((7< hour) && (hour<= 9)){str = '早餐后'}
else if ((9< hour) && (hour<= 11)){str = '午餐前'}
else if ((11< hour) && (hour<= 13)){str = '午餐后'}
else if ((13< hour) && (hour<= 17)){str = '晚餐前'}
else if ((17< hour) && (hour<= 19)){str = '晚餐后'}
else if ((19< hour) && (hour<= 24)){str = '睡觉前'}
return str
},
//8 如果一个蓝牙设备需要进行数据的写入以及数据传输,就必须具有某些特征值,所以通过上面步骤获取的id可以查看当前蓝牙设备的特征值
//开启设备数据监听 监听蓝牙设备返回来的数据
startNotice(uuid) {
var that = this;
wxnotifyBLECharacteristicValueChanged({
state: true, // 启用 notify 功能
deviceId: thatdatadeviceId,
serviceId: thatdataservicesUUID,
characteristicId: uuid, //第一步 开启监听 notityid 第二步发送指令 write
success: function (res) {
// thatcloseConnect(thatdatadeviceId)
// 设备返回的方法
let tip = 0
wxonBLECharacteristicValueChange(res1 => {
},
/
//监听蓝牙设备是否会异常断开
getTheBlueDisConnectWithAccident() {
},
// 断开设备连接
closeConnect: function(v) {
var that = this
if (v) {
wxcloseBLEConnection({
deviceId: v,
success: function(res) {
consolelog("蓝牙断开连接")
thatcloseBluetoothAdapter()
},
fail(res) {
}
})
} else {
thatcloseBluetoothAdapter()
}
},
// 关闭蓝牙模块
closeBluetoothAdapter:function () {
wxcloseBluetoothAdapter({
success: function(res) {
consolelog("关闭蓝牙模块")
},
fail: function(err) {
}
})
},
我实现的小程序模块自动连接(根据需要,可改手动),是在小程序初始化完成时开始自动调用执行。
大致流程:
开启蓝牙适配;
获取蓝牙适配器状态,判断设备蓝牙是否可用;
判断蓝牙适配器可用时开启扫描蓝牙设备和开启获取已连接的蓝牙设备;
如果开启扫描蓝牙设备失败 5 s 后,自动再次开启扫描;
开启扫描蓝牙设备成功后,开启监听已扫描的设备;
如果已扫描到的新设备,包含特定名称规律,则开始连接该设备;
开启获取已连接蓝牙设备,成功后,连接包含特定名称规律的设备;
两者都无法搜索到相应设备,则等待 5 s,重新搜索;
开始连接某设备时停止扫描设备,停止循环获取已连接设备;
连接成功后停止扫描设备,停止循环获取已连接设备。
下面,我们一步步来完成这个流程。
开始连接蓝牙设备
1 开启连接
appjs的onLaunch()方法里中,我们调用thisstartConnect();来开启连接,d出提示框,进行配对。如果失败,则提示设备蓝牙不可用,同时开启蓝牙适配器状态监听。
2 获取本机蓝牙状态
调用thisgetBluetoothAdapterState()获取本机蓝牙适配器状态,判断是否可用。若available为false,则为用户没有开启系统蓝牙。
同时,判断程序还没有开始搜索蓝牙设备,调用thisstartBluetoothDevicesDiscovery()开始扫描附近的蓝牙设备,以及thisgetConnectedBluetoothDevices(),获取本机已配对的蓝牙设备。
3 开始搜索新设备
开始搜索蓝牙设备startBluetoothDevicesDiscovery(),提示蓝牙搜索。
4 获取已配对的蓝牙设备
需要注意的是,参数services(Array)是必填的,但是官方示例中以及各种坑爹 demo 里从没见过有谁填写。不填写这个属性此方法,将无法获取到任何已配对设备。
如果要调用此方法,则代表需要连接特定设备,并且知道该设备的一个主服务serviceId。
如暂时不知道这个 ID,可以先手动连接一次想要连接的设备,然后获取service列表,记录属性primary为true的值至少一个。5 处理搜索功能开启失败的情况
如果搜索功能启动失败,回到第 2 步,重新检查蓝牙适配器。如果可用,开启蓝牙搜索功能并开启发现附近蓝牙设备事件监听:thisonBluetoothDeviceFound()。
此方法可自定义过滤一些无效的蓝牙设备,比如name为空的,或是产品开发中,需要过滤设备名称不含有特定规律字符串的设备。
6 自动配对设备
在第 5 步中发现了某个想配对的设备,则获取到该设备的deviceId,然后用thisstartConnectDevices()接口,开始配对该设备。
开启连接后,为了避免出现冲突,一旦开启连接,则需要终止扫描附近蓝牙设备、终止读取本机已配对设备
7 连接成功后握手
连接成功后,使用thisgetService(deviceId)接口,获取设备的所有服务。
8 读取服务的特征值
9 意外处理
如果扫描到的设备中没有想要连接的设备,可以尝试使用系统蓝牙手动配对,然后再小程序中调用getConnectedBluetoothDevices()获取本机已配对的蓝牙设备,然后过滤设备(可能获取多个已配对的蓝牙设备)。
然后,将已获取的蓝牙设备deviceId列表放入到一个数组中,然后调用自定义方法thisloopConnect();
思路:通过递归调用获取已配对蓝牙设备的,如果获取到了就去连接,如果devicesId[x]为空,说明上传调用时,获取到的已配对设备全部连接失败了。
这时候,我们需要则开启重新获取已配对蓝牙设备,并开启扫描附近蓝牙设备。
10 自动循环重试
startConnectDevices('loop', array)方法,是当获取已配对蓝牙设备进行连接时调用。
其中的处理逻辑上文已经贴出,意思就是在连接失败后fail方法里累加一个全局变量,然后回调loopConnect(array)方法。
11 手动连接
上文介绍的方法是为了直接自动连接,如果不需要自动连接,可使用方法getBluetoothDevices(),获取已扫描到的蓝牙设备的列表。
开发者可以做个页面显示出设备名,用户点击某个设备后,才开始连接。
需要注意的事项
thatserviceId是在初始化时设置的,由于对需要连接设备的主服务serivceId和各种特征值都是已知的,因此可以这样做。如果不可知,可以做一个扫描方法自己检查特征值的用途。
连接成功后的writeBLECharacteristicValue和openNotifyService *** 作需要注意,如果同时开启这两项 *** 作要先调用wirte再开启notify(原因未知,个人心得)。
3、经提醒,我发现还可以再完善一下在onBlueToothAdapterStateChange()监听蓝牙适配器状态,以此判断连接过程中、连接后用户开关了设备蓝牙。如果判断到关了蓝牙,发出开启蓝牙的提示;如果监听到开启了,就重新回到第 1 步。
openBluetoothAdapter() {
uniopenBluetoothAdapter({
success: e => {
// consolelog('初始化蓝牙成功:' + eerrMsg);
thisonBluetoothDeviceFound()
unigetBluetoothAdapterState({
success: function(res) {
// 初始化完毕开始搜索
if (resavailable) {
if (resdiscovering) {
// consolelog('停止连接');
thiscloseBluetooth()
}
unistartBluetoothDevicesDiscovery({
success: res => {
},
fail: res => {
onsolelog("查找设备失败!");
unishowToast({
icon: "none",
title: "查找设备失败!",
duration: 3000,
})
}
})
} else {
consolelog('本机蓝牙不可用')
}
}
})
},
fail: e => {
if (eerrCode == 10001) {
unishowModal({
title: "提示",
content: "您的蓝牙没有打开,请打开蓝牙",
success: function(res) {
if (resconfirm == true) {
if (unigetSystemInfoSync()platform == 'ios') {
} else if (unigetSystemInfoSync()platform === 'android') {
}
} else {
uninavigateBack({
url: '//detail3/detail3'
})
}
},
});
}
consolelog('初始化蓝牙失败,错误码:' + (eerrCode || eerrMsg));
}
});
},
onBluetoothDeviceFound() {
// consolelog("监听寻找新设备");
unionBluetoothDeviceFound(devices => {
// consolelog('开始监听寻找到新设备的事件');
// consolelog(deviceslength);
thisgetBluetoothDevices();
});
},
getBluetoothDevices() {
// consolelog("获取蓝牙设备");
unigetBluetoothDevices({
success: res => {
// consolelog('获取蓝牙设备');
thisresdic = res
// consolelog(res)
// consolelog('deviceslength===='+thisresdicdeviceslength);
for (let i = 0; i < thisresdicdeviceslength; i++) {
let devicesDic = thisresdicdevices[i];
if (devicesDicname && devicesDicname == '<Dobiy>' && devicesDicadvertisServiceUUIDs) {
thisdeviceIdStr = devicesDicdeviceId
thisstopBluetooth()
thiscreateBLEConnection(devicesDicdeviceId)
}
}
}
});
},
createBLEConnection(deviceIds) {
consolelog("连接蓝牙---------------" + deviceIds)
// setTimeout(function(){
// thatcloseBLEConnection();
// thatcreateBLEConnection(deviceIds)
// },5000)
unicreateBLEConnection({
deviceId: deviceIdstoString(),
success: (res) => {
consolelog('蓝牙连接成功');
// thisgetBLEDeviceServices(deviceIds)
thisstopBluetooth()
var that = this
unigetSystemInfo({
success(res) {
if (resplatform == 'ios') {
setTimeout(() => {
thatgetBLEDeviceServices(deviceIds)
}, 2000)
} else {
setTimeout(() => {
thatnogetBLEDeviceServices(deviceIds)
}, 3000)
}
},
fail(res) {
consolelog(777777777777777)
}
})
},
fail: (res) => {
consolelog('蓝牙连接失败:' + reserrCode)
thatcloseBLEConnection();
thatcreateBLEConnection(deviceIds)
},
});
},
async getBLEDeviceServices(deviceIds) {
var that = this
let deviceServices = await unigetBLEDeviceServices({
deviceId: deviceIds
})
if (deviceServices[0] == null) {
let services = deviceServices[1]services
if (serviceslength == 0) {
setTimeout(() => {
thatgetBLEDeviceServices(deviceIds)
}, 1500)
} else {
consolelog(services, 88888888888)
for (let i = 0; i < serviceslength; i++) {
let serviceId = services[i]uuid
// consolelog('serviceId====' + serviceId);
let characteristicsDic = await unigetBLEDeviceCharacteristics({
deviceId: deviceIds,
serviceId: serviceId,
})
then(data => {
// consolelog(JSONstringify(data))
if (data[0] == null) {
let characteristicsArr = data[1]characteristics
let properties = characteristicsArr[0]properties
consolelog(properties)
if (propertieswrite == true) {
thatserviceIdStr = serviceId;
thatcharacteristicsIdStr = data[1]characteristics[0]uuid;
thatnotifyBLECharacteristicValueChange(thatcharacteristicsIdStr)
// consolelog(deviceIds)
// consolelog(thatserviceIdStr)
// consolelog(thatcharacteristicsIdStr)
thatwriteBLECharacteristic(deviceIds, serviceId, thatcharacteristicsIdStr)
} else {
consolelog('不可编辑', data[1]characteristics[0]uuid)
}
}
})
}
}
} else {
unishowModal({
title: "提示",
content: "获取设备服务信息失败,请重新获取",
showCancel: false,
success(res) {
uninavigateBack({
url: '/uploadImg/uploadImg'
})
}
})
}
},
nogetBLEDeviceServices(deviceIds) {
var that = this
consolelog(777777)
unigetBLEDeviceServices({
deviceId: deviceIds,
success(res) {
let services = resservices
consolelog(services, 55555)
if (serviceslength == 0) {
setTimeout(() => {
thatnogetBLEDeviceServices(deviceIds)
}, 3000)
} else {
for (let i = 0; i < serviceslength; i++) {
let serviceId = services[i]uuid
//这个比对serviceId,设备厂家会告诉,因为安卓获取特征值都是不可写入的,iOS 的可以写入的,所以安卓直接判断serviceId是不是厂家发的serviceId
if (serviceId == '') {
unigetBLEDeviceCharacteristics({
deviceId: deviceIds,
serviceId: serviceId,
success(result) {
let propertiesDic = resultcharacteristics[0]
consolelog(propertiesDic, 55555)
// if (propertiesDicpropertieswrite == true) {
thatserviceIdStr = serviceId;
thatcharacteristicsIdStr = propertiesDicuuid;
thatnotifyBLECharacteristicValueChange(thatcharacteristicsIdStr)
setTimeout(() =>{
thatwriteBLECharacteristic(deviceIds, serviceId, thatcharacteristicsIdStr)
}, 2000)
// } else {
// consolelog('不可编辑', propertiesDicuuid)
// }
},
fail(result) {
consolelog(result, 2222222)
}
})
}
}
}
},
fail(res) {
consolelog(res, 8888888)
}
})
},
一定要调用这个方法,否则无法监听设备返回的数据,之前一直以为在uniwriteBLECharacteristicValue的回调里能得到返回的值,这个方法没有用,结果试了几天都不行,
notifyBLECharacteristicValueChange(characteristicId) {
consolelog(characteristicId, 'characteristicId')
uninotifyBLECharacteristicValueChange({
state: true, // 启用 notify 功能
deviceId: thisdeviceIdStr,
serviceId: thisserviceIdStr,
characteristicId: thischaracteristicsIdStr,
success: (res) => {
// consolelog(res)
// consolelog(thischaracteristicId)
consolelog('notifyBLECharacteristicValueChange success', res)
// thatwriteBLECharacteristic(deviceIds, serviceId, thatcharacteristicsIdStr)
thisonBLECharacteristicValue()
},
fail: (res) => {
consolelog('notifyBLECharacteristicValueChange success2', reserrMsg)
}
})
},
onBLECharacteristicValue() {
var that = this
unionBLECharacteristicValueChange(function(res) {
// consolelog(`characteristic ${rescharacteristicId} has changed, now is ${resvalue}`)
let resultStr = thatbufferString(resvalue)
// consolelog(resultStr,111)
if (resultStr && resultStr != '') {
//resultStr就是设备上返回的数据,根据设备不同,获取的设备格式也就不同,自己筛选吧
}
})
},
writeBLECharacteristic(deviceId, serviceId, characteristicId) {
var that = this
consolelog(1111111111)
let sgInt = thatstring2buffer('<sAg>')
// consolelog(sgIntbyteLength)
uniwriteBLECharacteristicValue({
deviceId: thatdeviceIdStr,
serviceId: thatserviceIdStr,
characteristicId: thatcharacteristicsIdStr,
value: sgInt,
success: function(res) {
consolelog(typeof(res))
consolelog('writeBLECharacteristicValue success', reserrMsg)
// thatcloseBluetooth()
},
fail: function(res) {
consolelog(typeof(res))
consolelog('writeBLECharacteristicValue fail==', reserrCode, reserrMsg)
}
})
},
//字符串转arraybuffer
string2buffer: function(str) {
// 首先将字符串转为16进制
let val = ""
for (let i = 0; i < strlength; i++) {
if (val === '') {
val = strcharCodeAt(i)toString(16)
} else {
val += ',' + strcharCodeAt(i)toString(16)
}
}
// consolelog(val)
// 将16进制转化为ArrayBuffer
return new Uint8Array(valmatch(/[\da-f]{2}/gi)map(function(h) {
return parseInt(h, 16)
}))buffer
},
//arraybuffer 转字符串
bufferString: function(str) {
// ArrayBuffer转16进度字符串示例
function ab2hex(buffer) {
const hexArr = Arrayprototypemapcall(
new Uint8Array(buffer),
function(bit) {
return ('00' + bittoString(16))slice(-2)
}
)
return hexArrjoin('')
}
//16进制
let systemStr = ab2hex(str)
// consolelog(hexCharCodeToStr(systemStr),99)
function hexCharCodeToStr(hexCharCodeStr) {
var trimedStr = hexCharCodeStrtrim();
var rawStr =
trimedStrsubstr(0, 2)toLowerCase() === "0x"
trimedStrsubstr(2) :
trimedStr;
var len = rawStrlength;
if (len % 2 !== 0) {
alert("Illegal Format ASCII Code!");
return "";
}
var curCharCode;
var resultStr = [];
for (var i = 0; i < len; i = i + 2) {
curCharCode = parseInt(rawStrsubstr(i, 2), 16); // ASCII Code Value
let str5 = StringfromCharCode(curCharCode)
if (str5startsWith('\n') == false) {
resultStrpush(StringfromCharCode(curCharCode));
}
}
return resultStrjoin("");
}
// consolelog(hexCharCodeToStr(systemStr),888)
return hexCharCodeToStr(systemStr)
},
//监听低功耗蓝牙连接状态的改变事件
onBLEConnectionStateChange() {
unionBLEConnectionStateChange(function(res) {
if (!resconnected) {
// consolelog('蓝牙断开链接')
thiscloseBluetooth()
}
})
},
stopBluetooth() {
unistopBluetoothDevicesDiscovery({
success: e => {
// consolelog('停止搜索蓝牙设备:' + eerrMsg);
},
fail: e => {
// consolelog('停止搜索蓝牙设备失败,错误码:' + eerrCode);
}
});
},
//关闭蓝牙模块
closeBluetooth() {
unicloseBluetoothAdapter({
success(res) {
consolelog(res)
}
})
}
},
这次介绍一下蓝牙协议栈(BLE)的基础知识,蓝牙协议栈组成如下图所示,首先我们说说GAP和GATT
GAP层是负责连接的,其中包含广播、扫描、连接、断开的过程和参数
11 角色
蓝牙设备的角色主要有中心(Central)和外围设备(Peripheral)两种,中心设备向外围设备发起连接。链路层的主机(Master)和从机(Slave)的概念跟中心和外围的概念是对应的。
除了中心和外围之外,还有Observer和Broadcaster角色,Observer一直进行监听,Broadcaster一直进行发送,这两种角色都只广播,不能发起连接或者被连接,这里就不详细说了
12 广播
建立连接之前,外围设备每隔一段时间发送一个广播包,让正在扫描的设备知道这是一个可以连接的设备,扫描设备才能对外围设备开始连接,这个广播包的时间间隔叫advertising interval,这个间隔可以在10ms到1024s之间,间隔的长短会影响建立连接所花时间。
中心要收到广播包之后才能发送连接请求,相应的,外围设备在发送广播包之后会等待连接请求。
蓝牙广播包最多可以包含31字节数据,包含设备名称和一些标志。中心收到广播包之后可以发送Scan Request以请求更多的广播信息,外围设备会回复一个同样是31个字节的Scan Response。
BLE有37个数据信道和3个广播信道,广播包,Scan Request,Scan Response使用广播信道
13 扫描
扫描是指中心监听广播包并且发送Scan request。扫描中有两个参数:Scan window(扫描窗口) 和Scan interval(扫描间隔)
14 发起连接
中心发起连接的过程类似于扫描的过程,中心收到广播包之后会发送一个连接请求给外围设备
15 连接
中心和外围设备连接上之后,中心会按照一定的连接间隔(Connection interval)向外围设备请求数据,这个间隔是由中心定的,在75ms到4s之间。
如果要提高吞吐量,可以在一个连接间隔中传输多个数据包,每个包最多可以包含20字节数据。如果要省电,并且外围设备并没有什么数据需要传输,可以跳过几个连接间隔,这称之为Slave latency。
如果外围设备没有在规定时间内响应中心的请求就会认为是连接中断,称之为Supervision Time-out(01s-32s),所以在使用Slave latency的时候要注意不要引起Supervision Time-out
这些参数是建立连接的时候中心确定的,如果建立连接之后外围设备想要更改,可以向中心发送Connection parameter update request。
整个数据交换的过程会在不同的频段中跳频(除了广播频段),跳频是自动完成的,不需要我们去管。
设备之间的数据传输是在GATT这层完成的
21 角色
除了GAP里面的角色,BLE在GATT里面也定义了两个角色,分别是GATT Server 和GATT Client,一般来说产生数据的设备是GATT Server,访问数据的设备是GATT Client,一个设备可以既是GATT Server也是GATT Client。这两个角色跟GAP中的角色并没有关系。
22 GATT结构
GATT Server通过属性表(Attribute table)来组织数据,
221 Attribute
上图中每一行就是一个属性Attribute。
每个属性具有一个handle,一个UUID,和一个Value。
Handle是Attribute的一个索引,每个Attribute的Handle都是唯一的。
UUID(universal unique identifier)表示的是Attribute中数据的类型信息。UUID在一个设备里面不是唯一的,可以有多个Attribute的UUID都一样
222 Characteristic
可以把Characteristic看作是上图中几行的集合(几个Attribute的集合)
每个Characteristic至少有两个Attribute,其中一个是声明,另一个包含数据。前面说过,蓝牙传输数据是通过GATT来传的,更具体的来说,是通过一个个Characteristic来传的。
223 Descriptors 描述符
Characteristic除了包含声明和数据之外,还可以有描述符(Descriptors,但不是必须有),Descriptors是用来进一步描述Characteristic(但不提供数据)的Attribute,比如说用自然语言描述该Characteristic是用来干什么的。
有一类特殊的描述符,叫CCCD(Client Characteristic Configuration Descriptor),支持Notify和Indicate的Characteristic 必须包含CCCD
224 Service
Service 是一个或者多个Characteristic的逻辑组合
一个GATT Service 通常包含了一些相关的功能,举例来说,一个人机界面Service包含了各种人机交互输入输出的数据,而其中的每个Characteristic 是一类信号或者设备
介绍几个常见的Service。
GAP GATT Service:作为Central或者Peripheral的BLE设备都需要有这个Service。这个Service包含了如何发现和连接设备的信息
Generic Attribute Service: GATT Server都要有这个Service,这个Service包含了GATT Server的信息
225 Profile
一个或者多个Service组合在一起称为Profile。Profile 是逻辑上的概念,自己并不具备单独的Attribute。
23 标准和自定义的Service和Characteristic
蓝牙联盟定义了一些标准的Profile, Service, Characteristic 和Attribute。由于Profile和Service是在具体应用中规定的,所以用户可以进行自定义
24 UUID
图2中每一个Attribute都有UUID,UUID是一个128位的数字,是用来描述Attribute的类型的
241 蓝牙联盟UUID
蓝牙联盟把UUID分为基础UUID(base UUID)和16位UUID。
蓝牙联盟规定的UUID都具有同样的base UUID:
0x0000xxxx-0000-1000-8000-00805F9B34FB
蓝牙联盟规定了一些16位UUID,替换掉base UUID中的xxxx就组成一个完整的128位UUID了。举例来说,0x2A37是一个16位Heart Rate Measurement Characteristic,那么对应的128位的UUID就是:
0x00002A37-0000-1000-8000-00805F9B34FB。
由于所有蓝牙联盟的UUID都具有同样的base UUID,所以用16位UUID就可以区分蓝牙联盟规定的Attribute了
但我们自定义的Attribute, Characteristic, Service,不能使用这个base UUID,因此也不能用16位UUID,只能使用制造商自定义的128位UUID。
25 空中接口 *** 作和属性
由于每个Attribute具有唯一的Handle,所以几乎所有的 *** 作都是通过Handle去完成,Characteristic常用的属性有:Write, Write without response, Read, Notify, Indicate。 Characteristic的属性决定了我们可以如何使用它
Write, Write without response
这两种属性允许GATT Client 对Server的characteristic的值进行写 *** 作,两者的区别是Write without response对写 *** 作不会有确认
Read
GATT Client可以读Server的Characteristic的值。
Notify 和Indicate
这两种属性表示的是当GATT Server 中该Characteristic 的值发生变化的时候会通知Client。两者的区别是Indicate 会进行确认但Notify不会
接下来举例说明一下前面介绍的概念
Thingy 的Services,作为Server需要有GAP Service和GATT Service,另外还有一个标准的Battery Service,后面是几个自定义的服务
详细的看看GAP Service,这个服务的Handle是1到9,那么把这些Characteristics和Attribute都打出来
可以看到这个Service的结构是这样的
这是一个标准的Service,使用标准Service/Characteristic的时候要符合蓝牙SIG对这个Service/Characteristic的规定,我们可以到 蓝牙SIG 看看是不是一致
以上就是BLE的一些基础知识和示例
以上就是关于android蓝牙连接成功后没有返回状态全部的内容,包括:android蓝牙连接成功后没有返回状态、Android 低功耗Ble 蓝牙4.0多连接 开源框架、小程序 蓝牙连接等相关内容解答,如果想了解更多相关内容,可以关注我们,你们的支持是我们更新的动力!
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)