@@ -0,0 +1,6 @@ | |||
//app.js | |||
App({ | |||
onLaunch: function () { | |||
}, | |||
}) |
@@ -0,0 +1,20 @@ | |||
{ | |||
"pages":[ | |||
"pages/index/index" | |||
], | |||
"window": { | |||
"navigationBarBackgroundColor": "#0082FE", | |||
"navigationBarTextStyle": "white", | |||
"navigationBarTitleText": "AiFresh蓝牙连接Demo", | |||
"backgroundColor": "#eeeeee", | |||
"backgroundTextStyle": "light" | |||
}, | |||
"plugins": { | |||
"sdkPlugin": { | |||
"version": "1.1.0", | |||
"provider": "wx17e93aad47cdae1a" | |||
} | |||
}, | |||
"style": "v2", | |||
"sitemapLocation": "sitemap.json" | |||
} |
@@ -0,0 +1,2 @@ | |||
/**app.wxss**/ | |||
@@ -0,0 +1,305 @@ | |||
// const plugin = requirePlugin("myPlugin").AiFresh; | |||
const {AiFresh: plugin} = requirePlugin("sdkPlugin"); | |||
function inArray(arr, key, val) { | |||
for (let i = 0; i < arr.length; i++) { | |||
if (arr[i][key] === val) { | |||
return i; | |||
} | |||
} | |||
return -1; | |||
} | |||
// ArrayBuffer转16进度字符串示例 | |||
function ab2hex(buffer) { | |||
var hexArr = Array.prototype.map.call( | |||
new Uint8Array(buffer), | |||
function (bit) { | |||
return ('00' + bit.toString(16)).slice(-2) | |||
} | |||
) | |||
return hexArr.join(','); | |||
} | |||
Page({ | |||
data: { | |||
devices: [ | |||
// { | |||
// deviceId: "02:03:04:05:06:07", | |||
// name: "elink", | |||
// localName: "elink", | |||
// mac: "02:03:04:05:06:07", | |||
// RSSI: -69, | |||
// advertisServiceUUIDs:[ | |||
// "FFE0","FFC0" | |||
// ], | |||
// analyzeDataText:"0102003241123413" | |||
// } | |||
], | |||
mac: "", | |||
connected: false, | |||
text: "", | |||
unitList_all: ['g', 'ml', 'lb:oz', 'oz', 'kg', '斤', '牛奶 ml', '水 ml', '牛奶 floz', '水 floz', 'lb'], | |||
unitList: ['g', 'ml', 'lb:oz', 'oz'], | |||
weight: 0, | |||
unit: 'g', | |||
unitIndex: 0, | |||
min: 0, | |||
second: 0, | |||
}, | |||
onLoad: function () { | |||
}, | |||
clearDevices() { | |||
this.setData({ | |||
devices: [] | |||
}) | |||
this.closeBluetoothAdapter() | |||
wx.nextTick(()=>{ | |||
this.openBluetoothAdapter() | |||
}) | |||
}, | |||
tare() { | |||
console.log("去皮") | |||
plugin.cmdTare() | |||
}, | |||
getUnits() { | |||
console.log("获取设备支持的单位") | |||
plugin.cmdGetAllUnits() | |||
}, | |||
countUp() { | |||
console.log("正计时开始") | |||
plugin.cmdTimeCountUp() | |||
}, | |||
countUpPause() { | |||
console.log("正计时暂停") | |||
plugin.cmdTimeCountUpPause(this.data.min, this.data.second) | |||
}, | |||
countDown() { | |||
console.log("倒计时开始") | |||
if (this.data.min || this.data.second) { | |||
plugin.cmdTimeCountDown(this.data.min, this.data.second) | |||
} else { | |||
plugin.cmdTimeCountDown(5, 30) | |||
} | |||
}, | |||
countDownPause() { | |||
console.log("倒计时暂停") | |||
plugin.cmdTimeCountDownPause(this.data.min, this.data.second) | |||
}, | |||
resetTime() { | |||
console.log("重置时间") | |||
plugin.cmdTimeReset() | |||
this.setData({ | |||
min: 0, | |||
second: 0, | |||
}) | |||
}, | |||
selectUnit(e) { | |||
let unit = e.currentTarget.dataset.unit | |||
let index = e.currentTarget.dataset.index | |||
let idx = this.data.unitList_all.indexOf(unit) | |||
plugin.cmdSwitchUnit(idx) | |||
}, | |||
openBluetoothAdapter() { | |||
wx.openBluetoothAdapter({ | |||
success: (res) => { | |||
console.log('openBluetoothAdapter success', res) | |||
this.startBluetoothDevicesDiscovery() | |||
}, | |||
fail: (res) => { | |||
if (res.errCode === 10001) { | |||
wx.showToast({ | |||
title: '请打开蓝牙', | |||
icon:"none" | |||
}) | |||
wx.onBluetoothAdapterStateChange(function (res) { | |||
console.log('onBluetoothAdapterStateChange', res) | |||
if (res.available) { | |||
this.startBluetoothDevicesDiscovery() | |||
} | |||
}) | |||
} | |||
} | |||
}) | |||
}, | |||
getBluetoothAdapterState() { | |||
wx.getBluetoothAdapterState({ | |||
success: (res) => { | |||
console.log('getBluetoothAdapterState', res) | |||
if (res.discovering) { | |||
this.onBluetoothDeviceFound() | |||
} else if (res.available) { | |||
this.startBluetoothDevicesDiscovery() | |||
} | |||
} | |||
}) | |||
}, | |||
startBluetoothDevicesDiscovery() { | |||
if (this._discoveryStarted) { | |||
return | |||
} | |||
this._discoveryStarted = true | |||
wx.startBluetoothDevicesDiscovery({ | |||
allowDuplicatesKey: true, | |||
services: [ | |||
"FFB0", | |||
], | |||
success: (res) => { | |||
console.log('startBluetoothDevicesDiscovery success', res) | |||
this.onBluetoothDeviceFound() | |||
}, | |||
}) | |||
}, | |||
stopBluetoothDevicesDiscovery() { | |||
wx.stopBluetoothDevicesDiscovery() | |||
}, | |||
onBluetoothDeviceFound() { | |||
wx.onBluetoothDeviceFound((res) => { | |||
res.devices.forEach(device => { | |||
const foundDevices = this.data.devices | |||
const idx = inArray(foundDevices, 'deviceId', device.deviceId) | |||
const data = {} | |||
console.log(device) | |||
// console.log(ab2hex(device.advertisData)) | |||
if (idx === -1) { | |||
data[`devices[${foundDevices.length}]`] = device | |||
} else { | |||
data[`devices[${idx}]`] = device | |||
} | |||
this.setData(data) | |||
}) | |||
}) | |||
}, | |||
createBLEConnection(e) { | |||
const ds = e.currentTarget.dataset | |||
const deviceId = ds.deviceId | |||
const name = ds.name | |||
const index = ds.index | |||
this._device = this.data.devices[index] | |||
console.log(this._device) | |||
wx.createBLEConnection({ | |||
deviceId, | |||
success: (res) => { | |||
let buff = this._device.advertisData.slice(-6) | |||
let arr = new Uint8Array(buff).reverse() | |||
let mac = ab2hex(arr).replace(/,/g,":") | |||
this.setData({ | |||
connected: true, | |||
name, | |||
deviceId, | |||
mac: mac, | |||
}) | |||
console.log("createBLEConnection:success") | |||
wx.stopBluetoothDevicesDiscovery() | |||
this.getBLEDeviceServices(deviceId) | |||
} | |||
}) | |||
this.stopBluetoothDevicesDiscovery() | |||
}, | |||
closeBLEConnection() { | |||
wx.closeBLEConnection({ | |||
deviceId: this._deviceId | |||
}) | |||
this.setData({ | |||
connected: false, | |||
min: 0, | |||
second: 0, | |||
weight: 0, | |||
unitList: ['g', 'ml', 'lb:oz', 'oz'], | |||
}) | |||
}, | |||
getBLEDeviceServices(deviceId) { | |||
wx.getBLEDeviceServices({ | |||
deviceId, | |||
success: (res) => { | |||
console.log('getBLEDeviceServices:', res) | |||
for (let i = 0; i < res.services.length; i++) { | |||
if (res.services[i].isPrimary && res.services[i].uuid.indexOf('0000FFB0') != -1) { | |||
this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid) | |||
return | |||
} | |||
} | |||
} | |||
}) | |||
}, | |||
getBLEDeviceCharacteristics(deviceId, serviceId) { | |||
this._deviceId = deviceId | |||
this._serviceId = serviceId | |||
this._device.serviceId = serviceId | |||
wx.getBLEDeviceCharacteristics({ | |||
deviceId, | |||
serviceId, | |||
success: (res) => { | |||
console.log('getBLEDeviceCharacteristics success', res.characteristics) | |||
for (let i = 0; i < res.characteristics.length; i++) { | |||
let item = res.characteristics[i] | |||
if (item.uuid.indexOf('0000FFB1') != -1) { | |||
this.uuid1 = item.uuid //下发数据 | |||
} else if (item.uuid.indexOf('0000FFB2') != -1) { | |||
this.uuid2 = item.uuid //监听数据 | |||
} | |||
} | |||
// 打开监听 | |||
wx.notifyBLECharacteristicValueChange({ | |||
deviceId, | |||
serviceId, | |||
characteristicId: this.uuid2, | |||
state: true, | |||
}) | |||
plugin.initPlugin(res.characteristics, this._device) | |||
plugin.cmdGetAllUnits() | |||
wx.onBLECharacteristicValueChange((characteristic) => { | |||
let bleData = plugin.parseBleData(characteristic.value) | |||
if (bleData.status == 1) { | |||
this.setData({ | |||
text: bleData.data.text, | |||
weight: bleData.data.weight, | |||
unit: bleData.data.unit, | |||
unitIndex: bleData.data.unitIndex | |||
}) | |||
} else if (bleData.status == 2) { | |||
this.setData({ | |||
min: bleData.data.min, | |||
second: bleData.data.second, | |||
}) | |||
} else if (bleData.status == 3) {//设备支持的单位 | |||
// 使用返回的单位索引,从本地的所有单位列表中取出支持的单位名 | |||
let unitList = bleData.data.unitIndexs.map(i=>{ | |||
return this.data.unitList_all[i] | |||
}) | |||
this.setData({ | |||
// unitList: bleData.data.unitList, | |||
unitList, | |||
}) | |||
} else { | |||
console.log(bleData) | |||
} | |||
}) | |||
}, | |||
fail(res) { | |||
console.error('getBLEDeviceCharacteristics', res) | |||
} | |||
}) | |||
}, | |||
writeBLECharacteristicValue(buffer, uuid, deviceId, serviceId) { | |||
// 向蓝牙设备发送一个二进制流数据 | |||
wx.writeBLECharacteristicValue({ | |||
deviceId, | |||
serviceId, | |||
characteristicId: uuid, | |||
value: buffer, | |||
success(res) { | |||
console.log('writeBLECharacteristicValue success', res) | |||
console.log(plugin.ab2hex(buffer)) | |||
} | |||
}) | |||
}, | |||
closeBluetoothAdapter() { | |||
wx.closeBluetoothAdapter() | |||
this._discoveryStarted = false | |||
}, | |||
}); |
@@ -0,0 +1,4 @@ | |||
{ | |||
"usingComponents": {}, | |||
"navigationBarTitleText":"AiFresh" | |||
} |
@@ -0,0 +1,74 @@ | |||
<wxs module="utils"> | |||
module.exports.max = function(n1, n2) { | |||
return Math.max(n1, n2) | |||
} | |||
module.exports.len = function(arr) { | |||
arr = arr || [] | |||
return arr.length | |||
} | |||
module.exports.fill0 = function(num) { | |||
if (!num) { | |||
return '00' | |||
} | |||
return ('00' + num).slice(-2) | |||
} | |||
</wxs> | |||
<block wx:if="{{!connected}}"> | |||
<button bindtap="openBluetoothAdapter">开始扫描</button> | |||
<button bindtap="stopBluetoothDevicesDiscovery">停止扫描</button> | |||
<button bindtap="closeBluetoothAdapter">结束流程</button> | |||
<button bindtap="clearDevices">清空列表</button> | |||
<view class="devices_summary">已发现 {{devices.length}} 个外围设备:</view> | |||
<view class="device_list"> | |||
<view wx:for="{{devices}}" wx:key="index" | |||
class="device_item"> | |||
<view style="font-size: 16px; color: #333;">{{item.name || item.localName}}</view> | |||
<view style="font-size: 10px">信号强度: {{item.RSSI}}dBm ({{utils.max(0, item.RSSI + 100)}}%)</view> | |||
<view style="font-size: 10px">mac地址: {{item.deviceId}}</view> | |||
<view | |||
class="device_item_btn" | |||
hover-class="btn_hover" | |||
data-device-id="{{item.deviceId}}" | |||
data-name="{{item.name || item.localName}}" | |||
data-index="{{index}}" | |||
bindtap="createBLEConnection" | |||
>连接</view> | |||
</view> | |||
</view> | |||
</block> | |||
<view class="wrap" wx:else> | |||
<view class="stopConnect"> | |||
<view>{{mac}}</view> | |||
<view class="btn_connect" hover-class="btn_hover" bindtap="closeBLEConnection">断开连接</view> | |||
</view> | |||
<view class="weight_wrap"> | |||
<view class="weight_title">WEIGHT</view> | |||
<view class="weight_value"> | |||
<view class="value_item">{{weight}}</view> | |||
<view class="value_item">{{unit}}</view> | |||
</view> | |||
</view> | |||
<view class="timer_wrap"> | |||
<view class="timer_title">TIMER</view> | |||
<view class="timer_value"> | |||
<view class="value_item">{{utils.fill0(min)}}</view> | |||
<view>:</view> | |||
<view class="value_item">{{utils.fill0(second)}}</view> | |||
</view> | |||
</view> | |||
<view class="btn_wrap"> | |||
<button bindtap="tare">TARE(去皮)</button> | |||
<button bindtap="getUnits">获取设备支持的单位</button> | |||
<button bindtap="countUp">正计时开始</button> | |||
<button bindtap="countUpPause">正计时暂停</button> | |||
<button bindtap="countDown">倒计时开始</button> | |||
<button bindtap="countDownPause">倒计时暂停</button> | |||
<button bindtap="resetTime">重置时间</button> | |||
<view class="btn_unitList_title">切换单位</view> | |||
<view class="btn_unitList"> | |||
<view class="btn_unit" hover-class="btn_hover" wx:for="{{unitList}}" wx:key="index" bindtap="selectUnit" data-unit="{{item}}" data-index="{{index}}">{{item}}</view> | |||
</view> | |||
</view> | |||
<view class="btn_unitList_title">{{text}}</view> | |||
</view> |
@@ -0,0 +1,159 @@ | |||
page { | |||
color: #333; | |||
} | |||
.devices_summary { | |||
margin-top: 30px; | |||
padding: 10px; | |||
font-size: 16px; | |||
} | |||
.device_list { | |||
min-height: 300px; | |||
margin: 0 5px; | |||
border: 1px solid #EEE; | |||
width: auto; | |||
} | |||
.device_item { | |||
border-bottom: 1px solid #EEE; | |||
padding: 10px; | |||
color: #666; | |||
position: relative; | |||
} | |||
.device_item_btn{ | |||
position: absolute; | |||
right: 12px; | |||
top: 50%; | |||
transform: translateY(-50%); | |||
width: 60px; | |||
height: 30px; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
border: 1px solid #ddd; | |||
border-radius: 3px; | |||
} | |||
.connected_info { | |||
position: fixed; | |||
bottom: 0; | |||
width: 100%; | |||
background-color: #F0F0F0; | |||
padding: 10px; | |||
padding-bottom: 20px; | |||
margin-bottom: env(safe-area-inset-bottom); | |||
font-size: 14px; | |||
min-height: 100px; | |||
box-shadow: 0px 0px 3px 0px; | |||
} | |||
.connected_info .operation { | |||
position: absolute; | |||
display: inline-block; | |||
right: 30px; | |||
} | |||
.writeInputViewBg{ | |||
position: fixed; | |||
top: 0; | |||
left: 0; | |||
right: 0; | |||
bottom: 0; | |||
z-index: 1000; | |||
background-color: rgba(0,0,0,0.5); | |||
} | |||
.writeInputView{ | |||
position: fixed; | |||
top: 30%; | |||
left: 50%; | |||
z-index: 1001; | |||
background-color: #fff; | |||
width: 80vw; | |||
height: 300rpx; | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
transform: translateX(-50%); | |||
} | |||
.writeInput{ | |||
margin-top: 60rpx; | |||
width: 90%; | |||
height: 60rpx; | |||
border: 1rpx solid #ccc; | |||
} | |||
.writeInput input{ | |||
width: 100%; | |||
height: 100%; | |||
box-sizing: border-box; | |||
} | |||
.hint{ | |||
padding-top: 10rpx; | |||
font-size: 24rpx; | |||
color: #999; | |||
} | |||
.btns{ | |||
display: flex; | |||
align-items: center; | |||
justify-content: space-between; | |||
width: 60%; | |||
margin-top: 50rpx; | |||
} | |||
.stopConnect{ | |||
display: flex; | |||
align-items: center; | |||
justify-content: space-between; | |||
padding: 10px; | |||
} | |||
.btn_connect{ | |||
width: 20%; | |||
height: 30px; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
background: #f8f8f8; | |||
border: 1px solid #ddd; | |||
border-radius: 5px; | |||
} | |||
.weight_wrap, | |||
.timer_wrap{ | |||
padding: 10px; | |||
border-top: 1px solid #ddd; | |||
} | |||
.timer_value, | |||
.weight_value{ | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
} | |||
.value_item{ | |||
margin: 0 5px; | |||
padding: 4px 6px; | |||
border: 1px solid #eee; | |||
min-width: 60px; | |||
text-align: center; | |||
} | |||
.btn_wrap{ | |||
padding-top: 10px; | |||
} | |||
.btn_unitList_title{ | |||
padding: 10px; | |||
} | |||
.btn_unitList{ | |||
display: flex; | |||
flex-wrap: wrap; | |||
align-items: center; | |||
justify-content: space-around; | |||
} | |||
.btn_unit{ | |||
width: 20%; | |||
height: 40px; | |||
display: flex; | |||
align-items: center; | |||
justify-content: center; | |||
background: #f8f8f8; | |||
border: 1px solid #ddd; | |||
border-radius: 5px; | |||
margin: 10px 0; | |||
} | |||
.btn_hover{ | |||
background: rgba(0,0,0,0.1); | |||
} |
@@ -0,0 +1,72 @@ | |||
{ | |||
"description": "项目配置文件", | |||
"packOptions": { | |||
"ignore": [] | |||
}, | |||
"setting": { | |||
"urlCheck": true, | |||
"es6": true, | |||
"enhance": false, | |||
"postcss": true, | |||
"preloadBackgroundData": false, | |||
"minified": true, | |||
"newFeature": false, | |||
"coverView": true, | |||
"nodeModules": false, | |||
"autoAudits": false, | |||
"showShadowRootInWxmlPanel": true, | |||
"scopeDataCheck": false, | |||
"uglifyFileName": false, | |||
"checkInvalidKey": true, | |||
"checkSiteMap": true, | |||
"uploadWithSourceMap": true, | |||
"compileHotReLoad": false, | |||
"useMultiFrameRuntime": false, | |||
"useApiHook": true, | |||
"babelSetting": { | |||
"ignore": [], | |||
"disablePlugins": [], | |||
"outputPath": "" | |||
}, | |||
"enableEngineNative": false, | |||
"bundle": false, | |||
"useIsolateContext": true, | |||
"useCompilerModule": true, | |||
"userConfirmedUseCompilerModuleSwitch": false, | |||
"userConfirmedBundleSwitch": false, | |||
"packNpmManually": false, | |||
"packNpmRelationList": [], | |||
"minifyWXSS": true | |||
}, | |||
"compileType": "miniprogram", | |||
"libVersion": "2.14.1", | |||
"appid": "wx942afd64e1113ff3", | |||
"projectname": "aifresh_demo", | |||
"debugOptions": { | |||
"hidedInDevtools": [] | |||
}, | |||
"scripts": {}, | |||
"isGameTourist": false, | |||
"simulatorType": "wechat", | |||
"simulatorPluginLibVersion": {}, | |||
"condition": { | |||
"search": { | |||
"list": [] | |||
}, | |||
"conversation": { | |||
"list": [] | |||
}, | |||
"game": { | |||
"list": [] | |||
}, | |||
"plugin": { | |||
"list": [] | |||
}, | |||
"gamePlugin": { | |||
"list": [] | |||
}, | |||
"miniprogram": { | |||
"list": [] | |||
} | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
{ | |||
"desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", | |||
"rules": [{ | |||
"action": "allow", | |||
"page": "*" | |||
}] | |||
} |
@@ -0,0 +1,3 @@ | |||
module.exports = { | |||
} |
@@ -0,0 +1,5 @@ | |||
//app.js | |||
App({ | |||
onLaunch: function () { | |||
} | |||
}) |
@@ -0,0 +1,20 @@ | |||
{ | |||
"pages":[ | |||
"pages/index/index" | |||
], | |||
"window": { | |||
"navigationBarBackgroundColor": "#0082FE", | |||
"navigationBarTextStyle": "white", | |||
"navigationBarTitleText": "AiLink蓝牙连接Demo", | |||
"backgroundColor": "#eeeeee", | |||
"backgroundTextStyle": "light" | |||
}, | |||
"plugins": { | |||
"sdkPlugin": { | |||
"version": "1.1.0", | |||
"provider": "wx17e93aad47cdae1a" | |||
} | |||
}, | |||
"style": "v2", | |||
"sitemapLocation": "sitemap.json" | |||
} |
@@ -0,0 +1,2 @@ | |||
/**app.wxss**/ | |||
@@ -0,0 +1,313 @@ | |||
const plugin = requirePlugin("sdkPlugin").AiLink; | |||
function inArray(arr, key, val) { | |||
for (let i = 0; i < arr.length; i++) { | |||
if (arr[i][key] === val) { | |||
return i; | |||
} | |||
} | |||
return -1; | |||
} | |||
// ArrayBuffer转16进度字符串示例 | |||
function ab2hex(buffer) { | |||
var hexArr = Array.prototype.map.call( | |||
new Uint8Array(buffer), | |||
function (bit) { | |||
return ('00' + bit.toString(16)).slice(-2) | |||
} | |||
) | |||
return hexArr.join(','); | |||
} | |||
Page({ | |||
data: { | |||
showWriteInput:false, | |||
devices: [ | |||
// { | |||
// deviceId: "02:03:04:05:06:07", | |||
// name: "elink", | |||
// localName: "elink", | |||
// mac: "02:03:04:05:06:07", | |||
// RSSI: -69, | |||
// advertisServiceUUIDs:[ | |||
// "FFE0","FFC0" | |||
// ], | |||
// analyzeDataText:"0102003241123413" | |||
// } | |||
], | |||
connected: false, | |||
chs: [], | |||
}, | |||
onLoad: function () { | |||
}, | |||
openBluetoothAdapter() { | |||
wx.openBluetoothAdapter({ | |||
success: (res) => { | |||
console.log('openBluetoothAdapter success', res) | |||
this.startBluetoothDevicesDiscovery() | |||
}, | |||
fail: (res) => { | |||
if (res.errCode === 10001) { | |||
wx.showToast({ | |||
title: '请打开蓝牙', | |||
icon:"none" | |||
}) | |||
wx.onBluetoothAdapterStateChange(function (res) { | |||
console.log('onBluetoothAdapterStateChange', res) | |||
if (res.available) { | |||
this.startBluetoothDevicesDiscovery() | |||
} | |||
}) | |||
} | |||
} | |||
}) | |||
}, | |||
getBluetoothAdapterState() { | |||
wx.getBluetoothAdapterState({ | |||
success: (res) => { | |||
console.log('getBluetoothAdapterState', res) | |||
if (res.discovering) { | |||
this.onBluetoothDeviceFound() | |||
} else if (res.available) { | |||
this.startBluetoothDevicesDiscovery() | |||
} | |||
} | |||
}) | |||
}, | |||
startBluetoothDevicesDiscovery() { | |||
if (this._discoveryStarted) { | |||
return | |||
} | |||
this._discoveryStarted = true | |||
wx.startBluetoothDevicesDiscovery({ | |||
allowDuplicatesKey: true, | |||
services: [ | |||
"FFE0", | |||
"F0A0", | |||
], | |||
success: (res) => { | |||
console.log('startBluetoothDevicesDiscovery success', res) | |||
this.onBluetoothDeviceFound() | |||
}, | |||
}) | |||
}, | |||
stopBluetoothDevicesDiscovery() { | |||
wx.stopBluetoothDevicesDiscovery() | |||
}, | |||
onBluetoothDeviceFound() { | |||
wx.onBluetoothDeviceFound((res) => { | |||
res.devices.forEach(device => { | |||
if (!device.name && !device.localName) { | |||
return | |||
} | |||
const foundDevices = this.data.devices | |||
const idx = inArray(foundDevices, 'deviceId', device.deviceId) | |||
const data = {} | |||
console.log(device) | |||
// console.log(ab2hex(device.advertisData)) | |||
if(device.advertisServiceUUIDs[0].indexOf("F0A0") !== -1){ | |||
let parseDataRes = plugin.parseBroadcastData(device.advertisData) | |||
console.log(parseDataRes) | |||
if(parseDataRes.status == 1) { | |||
let analyzeData = plugin.analyzeBroadcastScaleData(parseDataRes) | |||
console.log(analyzeData) | |||
device.analyzeDataText = analyzeData.text | |||
} | |||
}else{ | |||
let buff = device.advertisData.slice(-6) | |||
device.mac = new Uint8Array(buff) | |||
} | |||
if (idx === -1) { | |||
data[`devices[${foundDevices.length}]`] = device | |||
} else { | |||
data[`devices[${idx}]`] = device | |||
} | |||
this.setData(data) | |||
}) | |||
}) | |||
}, | |||
createBLEConnection(e) { | |||
const ds = e.currentTarget.dataset | |||
const index = ds.index | |||
this._device = this.data.devices[index] | |||
console.log(this._device) | |||
const deviceId = ds.deviceId | |||
const name = ds.name | |||
this.mac = ds.mac | |||
wx.createBLEConnection({ | |||
deviceId, | |||
success: (res) => { | |||
this.setData({ | |||
connected: true, | |||
name, | |||
deviceId, | |||
}) | |||
console.log("createBLEConnection:success") | |||
wx.stopBluetoothDevicesDiscovery() | |||
this.getBLEDeviceServices(deviceId) | |||
} | |||
}) | |||
this.stopBluetoothDevicesDiscovery() | |||
}, | |||
closeBLEConnection() { | |||
wx.closeBLEConnection({ | |||
deviceId: this._deviceId | |||
}) | |||
this.setData({ | |||
connected: false, | |||
chs: [], | |||
}) | |||
}, | |||
getBLEDeviceServices(deviceId) { | |||
wx.getBLEDeviceServices({ | |||
deviceId, | |||
success: (res) => { | |||
for (let i = 0; i < res.services.length; i++) { | |||
if (res.services[i].isPrimary) { | |||
this.getBLEDeviceCharacteristics(deviceId, res.services[i].uuid) | |||
return | |||
} | |||
} | |||
} | |||
}) | |||
}, | |||
getBLEDeviceCharacteristics(deviceId, serviceId) { | |||
const _this = this | |||
this._deviceId = deviceId | |||
this._serviceId = serviceId | |||
this._device.serviceId = serviceId | |||
wx.getBLEDeviceCharacteristics({ | |||
deviceId, | |||
serviceId, | |||
success: (res) => { | |||
console.log('getBLEDeviceCharacteristics success', res.characteristics) | |||
// let uuid1, uuid2, uuid3; | |||
for (let i = 0; i < res.characteristics.length; i++) { | |||
let item = res.characteristics[i] | |||
if (item.uuid.indexOf('0000FFE1') != -1) { | |||
this.uuid1 = item.uuid //下发数据 | |||
} else if (item.uuid.indexOf('0000FFE2') != -1) { | |||
this.uuid2 = item.uuid //监听数据 | |||
} else if (item.uuid.indexOf('0000FFE3') != -1) { | |||
this.uuid3 = item.uuid //写入设置 | |||
} | |||
} | |||
// 打开监听 | |||
wx.notifyBLECharacteristicValueChange({ | |||
deviceId, | |||
serviceId, | |||
characteristicId: this.uuid2, | |||
state: true, | |||
}) | |||
wx.notifyBLECharacteristicValueChange({ | |||
deviceId, | |||
serviceId, | |||
characteristicId: this.uuid3, | |||
state: true, | |||
}) | |||
plugin.initPlugin(res.characteristics, this._device) | |||
wx.onBLECharacteristicValueChange((characteristic) => { | |||
let bleData = plugin.parseBleData(characteristic.value) | |||
if (bleData.status == 0) { | |||
console.log("握手成功") | |||
} else if (bleData.status == 1) { | |||
console.log(bleData) | |||
let parseData = bleData.data //对应协议中的payload数据,可以自行解析该数据 | |||
console.log(ab2hex(parseData)) | |||
console.log(ab2hex(bleData.completeData)) | |||
const idx = inArray(this.data.chs, 'uuid', characteristic.characteristicId) | |||
const data = {} | |||
if (idx === -1) { | |||
data[`chs[${this.data.chs.length}]`] = { | |||
uuid: characteristic.characteristicId, | |||
value: ab2hex(parseData) | |||
} | |||
} else { | |||
data[`chs[${idx}]`] = { | |||
uuid: characteristic.characteristicId, | |||
value: ab2hex(parseData) | |||
} | |||
} | |||
this.setData(data) | |||
} else { | |||
console.log(bleData) | |||
} | |||
}) | |||
}, | |||
fail(res) { | |||
console.error('getBLEDeviceCharacteristics', res) | |||
} | |||
}) | |||
}, | |||
writeBLECharacteristicValue(buffer, uuid, deviceId, serviceId) { | |||
// 向蓝牙设备发送一个二进制流数据 | |||
wx.writeBLECharacteristicValue({ | |||
deviceId, | |||
serviceId, | |||
characteristicId: uuid, | |||
value: buffer, | |||
success(res) { | |||
console.log('writeBLECharacteristicValue success', res) | |||
console.log(ab2hex(buffer)) | |||
} | |||
}) | |||
}, | |||
closeBluetoothAdapter() { | |||
wx.closeBluetoothAdapter() | |||
this._discoveryStarted = false | |||
}, | |||
showWriteInputView(){ | |||
this.setData({ | |||
showWriteInput: true | |||
}) | |||
}, | |||
hideWriteInputView(){ | |||
this.setData({ | |||
showWriteInput: false | |||
}) | |||
}, | |||
writingCmd(e){ | |||
this._cmd = e.detail.value | |||
}, | |||
submitCmd(){ | |||
if(!this._cmd){ | |||
return | |||
} | |||
let arr = [] | |||
if(this._cmd.indexOf(",") == -1){ | |||
arr = this._cmd.split(" ") | |||
} else { | |||
arr = this._cmd.split(",") | |||
} | |||
for(let i in arr){ | |||
arr[i] = parseInt(arr[i],16) | |||
} | |||
// let arr = [ | |||
// 0xA6, | |||
// 0x01, | |||
// 0x28, | |||
// 0x29, | |||
// 0x6A, | |||
// //A6 01 28 29 6A | |||
// ] | |||
if(arr[0] == 0xA6){ | |||
let payload = arr.slice(2,-2) | |||
plugin.sendDataOfA6(payload) | |||
} else if(arr[0] == 0xA7) { | |||
let cid = [arr[1], arr[2]] // 001E | |||
let payload = arr.slice(4,-2) | |||
plugin.sendDataOfA7(payload) | |||
// this.writeBLECharacteristicValue(buff, this.uuid1, this._deviceId, this._serviceId) | |||
} | |||
return | |||
}, | |||
}); |
@@ -0,0 +1,5 @@ | |||
{ | |||
"usingComponents": { | |||
}, | |||
"navigationBarTitleText":"AiLink" | |||
} |
@@ -0,0 +1,55 @@ | |||
<wxs module="utils"> | |||
module.exports.max = function(n1, n2) { | |||
return Math.max(n1, n2) | |||
} | |||
module.exports.len = function(arr) { | |||
arr = arr || [] | |||
return arr.length | |||
} | |||
</wxs> | |||
<button bindtap="openBluetoothAdapter">开始扫描</button> | |||
<button bindtap="stopBluetoothDevicesDiscovery">停止扫描</button> | |||
<button bindtap="closeBluetoothAdapter">结束流程</button> | |||
<view class="devices_summary">已发现 {{devices.length}} 个外围设备:</view> | |||
<scroll-view class="device_list" scroll-y scroll-with-animation> | |||
<view wx:for="{{devices}}" wx:key="index" | |||
data-device-id="{{item.deviceId}}" | |||
data-name="{{item.name || item.localName}}" | |||
data-mac="{{item.mac}}" | |||
data-index="{{index}}" | |||
bindtap="createBLEConnection" | |||
class="device_item" | |||
hover-class="device_item_hover"> | |||
<view style="font-size: 16px; color: #333;">{{item.name}}</view> | |||
<view style="font-size: 10px">信号强度: {{item.RSSI}}dBm ({{utils.max(0, item.RSSI + 100)}}%)</view> | |||
<view style="font-size: 10px">mac地址: {{item.deviceId}}</view> | |||
<!-- <view style="font-size: 10px">Service数量: {{utils.len(item.advertisServiceUUIDs)}}</view> --> | |||
<view style="font-size: 14px">广播数据:{{item.analyzeDataText}}</view> | |||
</view> | |||
</scroll-view> | |||
<view class="connected_info" wx:if="{{connected}}"> | |||
<view> | |||
<text>已连接到 {{name}}</text> | |||
<view class="operation"> | |||
<!-- <button wx:if="{{canWrite}}" size="mini" bindtap="writeBLECharacteristicValue">写数据</button> --> | |||
<button size="mini" bindtap="showWriteInputView">写入指令</button> | |||
<button size="mini" bindtap="closeBLEConnection">断开连接</button> | |||
</view> | |||
</view> | |||
<view wx:for="{{chs}}" wx:key="index" style="font-size: 12px; margin-top: 10px;"> | |||
<view>特性值: {{item.value}}</view> | |||
</view> | |||
</view> | |||
<view class="writeInputViewBg" wx:if="{{showWriteInput}}"></view> | |||
<view class="writeInputView" wx:if="{{showWriteInput}}"> | |||
<view class="writeInput"> | |||
<input bindinput="writingCmd"></input> | |||
</view> | |||
<view class="hint">请输入16进制数,Byte之间用空格或英文逗号分隔</view> | |||
<view class="btns"> | |||
<button size="mini" bindtap="submitCmd">确定</button> | |||
<button size="mini" bindtap="hideWriteInputView">取消</button> | |||
</view> | |||
</view> |
@@ -0,0 +1,86 @@ | |||
page { | |||
color: #333; | |||
} | |||
.devices_summary { | |||
margin-top: 30px; | |||
padding: 10px; | |||
font-size: 16px; | |||
} | |||
.device_list { | |||
height: 300px; | |||
margin: 50px 5px; | |||
margin-top: 0; | |||
border: 1px solid #EEE; | |||
border-radius: 5px; | |||
width: auto; | |||
} | |||
.device_item { | |||
border-bottom: 1px solid #EEE; | |||
padding: 10px; | |||
color: #666; | |||
} | |||
.device_item_hover { | |||
background-color: rgba(0, 0, 0, .1); | |||
} | |||
.connected_info { | |||
position: fixed; | |||
bottom: 0; | |||
width: 100%; | |||
background-color: #F0F0F0; | |||
padding: 10px; | |||
padding-bottom: 20px; | |||
margin-bottom: env(safe-area-inset-bottom); | |||
font-size: 14px; | |||
min-height: 100px; | |||
box-shadow: 0px 0px 3px 0px; | |||
} | |||
.connected_info .operation { | |||
position: absolute; | |||
display: inline-block; | |||
right: 30px; | |||
} | |||
.writeInputViewBg{ | |||
position: fixed; | |||
top: 0; | |||
left: 0; | |||
right: 0; | |||
bottom: 0; | |||
z-index: 1000; | |||
background-color: rgba(0,0,0,0.5); | |||
} | |||
.writeInputView{ | |||
position: fixed; | |||
top: 30%; | |||
left: 50%; | |||
z-index: 1001; | |||
background-color: #fff; | |||
width: 80vw; | |||
height: 300rpx; | |||
display: flex; | |||
flex-direction: column; | |||
align-items: center; | |||
transform: translateX(-50%); | |||
} | |||
.writeInput{ | |||
margin-top: 60rpx; | |||
width: 90%; | |||
height: 60rpx; | |||
border: 1rpx solid #ccc; | |||
} | |||
.writeInput input{ | |||
width: 100%; | |||
height: 100%; | |||
box-sizing: border-box; | |||
} | |||
.hint{ | |||
padding-top: 10rpx; | |||
font-size: 24rpx; | |||
color: #999; | |||
} | |||
.btns{ | |||
display: flex; | |||
align-items: center; | |||
justify-content: space-between; | |||
width: 60%; | |||
margin-top: 50rpx; | |||
} |
@@ -0,0 +1,72 @@ | |||
{ | |||
"description": "项目配置文件", | |||
"packOptions": { | |||
"ignore": [] | |||
}, | |||
"setting": { | |||
"urlCheck": true, | |||
"es6": true, | |||
"enhance": false, | |||
"postcss": true, | |||
"preloadBackgroundData": false, | |||
"minified": true, | |||
"newFeature": false, | |||
"coverView": true, | |||
"nodeModules": false, | |||
"autoAudits": false, | |||
"showShadowRootInWxmlPanel": true, | |||
"scopeDataCheck": false, | |||
"uglifyFileName": false, | |||
"checkInvalidKey": true, | |||
"checkSiteMap": true, | |||
"uploadWithSourceMap": true, | |||
"compileHotReLoad": false, | |||
"useMultiFrameRuntime": false, | |||
"useApiHook": true, | |||
"babelSetting": { | |||
"ignore": [], | |||
"disablePlugins": [], | |||
"outputPath": "" | |||
}, | |||
"enableEngineNative": false, | |||
"bundle": false, | |||
"useIsolateContext": true, | |||
"useCompilerModule": true, | |||
"userConfirmedUseCompilerModuleSwitch": false, | |||
"userConfirmedBundleSwitch": false, | |||
"packNpmManually": false, | |||
"packNpmRelationList": [], | |||
"minifyWXSS": true | |||
}, | |||
"compileType": "miniprogram", | |||
"libVersion": "2.14.1", | |||
"appid": "wx942afd64e1113ff3", | |||
"projectname": "ailink_demo", | |||
"debugOptions": { | |||
"hidedInDevtools": [] | |||
}, | |||
"scripts": {}, | |||
"isGameTourist": false, | |||
"simulatorType": "wechat", | |||
"simulatorPluginLibVersion": {}, | |||
"condition": { | |||
"search": { | |||
"list": [] | |||
}, | |||
"conversation": { | |||
"list": [] | |||
}, | |||
"game": { | |||
"list": [] | |||
}, | |||
"plugin": { | |||
"list": [] | |||
}, | |||
"gamePlugin": { | |||
"list": [] | |||
}, | |||
"miniprogram": { | |||
"list": [] | |||
} | |||
} | |||
} |
@@ -0,0 +1,7 @@ | |||
{ | |||
"desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html", | |||
"rules": [{ | |||
"action": "allow", | |||
"page": "*" | |||
}] | |||
} |
@@ -0,0 +1,4 @@ | |||
module.exports = { | |||
} |