設(shè)備賣給客戶,不能通過設(shè)備自動連接客戶家中的網(wǎng)絡(luò)乞旦,使用微信小程序連接樹莓派藍牙贼穆,并發(fā)送數(shù)據(jù)(wifi信息)給樹莓派
先點擊 ‘搜索藍牙設(shè)備’搜索藍牙
可通過uuid唯一搜索樹莓派(如果不知道,那就不填寫兰粉,就會導(dǎo)致搜索出來很多藍牙故痊。下面代碼中有備注)
如果不填寫local_name(需要在樹莓派中的藍牙配置中修改)會是導(dǎo)致搜索出來很多名稱為‘未知設(shè)備’的藍牙
9lr35-ouajf.gif
樹莓派上的接收
5c6c4fcdb637de74c4c39660b6baee3.jpg
該程序是使用taro編寫,大體上和wx小程序?qū)懛ㄒ粯泳凉茫凸β仕{牙api都是一樣的愕秫。
import Taro from '@tarojs/taro';
import React, { Component } from 'react';
import { View, Picker } from '@tarojs/components';
import { AtForm, AtInput, AtButton, AtMessage, AtList, AtListItem } from 'taro-ui';
import { string2buffer, ab2hex, isEmpty } from '../../utils/util';
import './index.scss';
class BlueTooth extends Component {
state = {
wifiName: '', // 當前手機連接的wifi信息
wifiPassWord: '',
logs: [],
deviceArray: [],
currDeviceID: '請選擇...'
}
componentWillMount() { }
componentDidMount() {
const that = this;
Taro.startWifi({
success() {
Taro.getConnectedWifi({
success: resp => {
console.log(resp);
that.setState({
wifiName: resp.wifi.SSID
})
},
fail: resf => {
if (resf.errCode == 12006) {
Taro.showModal({
title: '請打開GPS定位',
content: 'Android手機不打開GPS定位,無法搜索到藍牙設(shè)備.',
showCancel: false
})
}
}
})
}
})
}
componentWillUnmount() { }
componentDidShow() { }
componentDidHide() { }
delayTimer = ''; // 用來控制是否持續(xù)服務(wù)發(fā)現(xiàn)
isFound = false; // 藍牙是否被發(fā)現(xiàn)判斷標識
closeFlag = false; // 藍牙適配器是否被關(guān)閉標識
onSubmit = () => {
const { currDeviceID, wifiName, wifiPassWord } = this.state;
console.log("選中:" + currDeviceID);
if (isEmpty(currDeviceID) || currDeviceID == "請選擇...") {
Taro.atMessage({
'message': '請先搜索設(shè)備',
'type': 'warning',
})
return;
}
if (isEmpty(wifiName)) {
Taro.atMessage({
'message': '請輸入wifi名稱',
'type': 'warning',
})
return;
}
if (isEmpty(wifiPassWord)) {
Taro.atMessage({
'message': '請輸入wifi密碼',
'type': 'warning',
})
return;
}
const device = currDeviceID.split('[');
if (device.length <= 1) {
Taro.atMessage({
'message': '當前沒有藍牙設(shè)備在線',
'type': 'warning',
})
return;
}
const id = device[device.length - 1].replace("]", "");
this.createBLE(id);
}
onReset = () => {
this.initBLE();
}
initBLE = () => {
this.printLog("啟動藍牙適配器, 藍牙初始化")
Taro.openBluetoothAdapter({
success: () => {
// console.log(res);
this.findBLE();
},
fail: () => {
Taro.atMessage({
'message': '請先打開藍牙',
'type': 'warning',
})
}
})
}
findBLE = () => {
this.printLog("打開本機藍牙成功.");
Taro.startBluetoothDevicesDiscovery({
services: ['6E400001-B5A3-F393-E0A9-E50E24DCCA9E'], // 這里要注意焰络,填寫筒子們自己設(shè)備的uuid戴甩,如果不知道,可以注釋的掉這一行闪彼,注釋導(dǎo)致會搜索到很多藍牙設(shè)備
allowDuplicatesKey: false,
interval: 0,
success: () => {
Taro.showLoading({
title: '正在搜索設(shè)備',
})
this.delayTimer = setInterval(() => {
this.discoveryBLE() //3.0 //這里的discovery需要多次調(diào)用
}, 1000);
setTimeout(() => {
if (this.isFound) {
return;
} else {
Taro.hideLoading();
console.log("搜索設(shè)備超時");
Taro.stopBluetoothDevicesDiscovery({
success: () => {
console.log('連接藍牙成功之后關(guān)閉藍牙搜索');
}
})
clearInterval(this.delayTimer)
Taro.showModal({
title: '搜索設(shè)備超時',
content: '請檢查藍牙設(shè)備是否正常工作甜孤,Android手機請打開GPS定位.',
showCancel: false
})
Taro.atMessage({
'message': '搜索設(shè)備超時,請打開GPS定位畏腕,再搜索',
'type': 'warning',
})
return
}
}, 15000);
},
fail: res => {
this.printLog("藍牙設(shè)備服務(wù)發(fā)現(xiàn)失敗: " + res.errMsg);
}
})
}
discoveryBLE = () => {
Taro.getBluetoothDevices({
success: res => {
const list = res.devices;
console.log('藍牙列表', list);
if (isEmpty(list)) {
return;
}
const devices = [];
list.forEach(v => {
const name = v.name || v.localName;
if (!isEmpty(name) && v.RSSI != 0) {
// if (!isEmpty(name) && v.RSSI != 0) {
// const item = {
// RSSI: v.RSSI,
// name: v.name,
// deviceId: v.deviceId,
// mac: Array.prototype.map.call(new Uint8Array(v.advertisData.slice(4, 10)), x => ('00' + x.toString(16)).slice(-2)).join(':').toUpperCase()
// }
// devices.push(item);
devices.push(v);
}
})
console.log('devices -----', devices);
console.log('總共有' + devices.length + "個設(shè)備需要設(shè)置")
if (devices.length <= 0) {
return;
}
this.connectBLE(devices);
},
fail: function () {
Taro.atMessage({
'message': '搜索藍牙設(shè)備失敗',
'type': 'warning',
})
}
})
}
connectBLE = devices => {
this.printLog('總共有' + devices.length + "個設(shè)備需要設(shè)置")
Taro.hideLoading();
this.isFound = true;
clearInterval(this.delayTimer);
Taro.stopBluetoothDevicesDiscovery({
success: () => {
this.printLog('連接藍牙成功之后關(guān)閉藍牙搜索');
}
})
//兩個的時候需要選擇
const list = [];
devices.forEach(v => {
const name = v.localName || v.name;
list.push(name + "[" + v.deviceId + "]")
})
this.setState({
deviceArray: list
})
//默認選擇
this.setState({
currDeviceID: list[0]
})
}
createBLE = deviceId => {
this.printLog("連接: [" + deviceId + "]");
if (this.closeFlag) {
Taro.openBluetoothAdapter({
success: () => {
Taro.createBLEConnection({
deviceId: deviceId,
success: () => {
this.printLog("設(shè)備連接成功");
this.getBLEServiceId(deviceId);
},
fail: resf => {
this.printLog("設(shè)備連接失敗" + resf.errMsg);
}
})
},
fail: () => {
Taro.atMessage({
'message': '請先打開藍牙',
'type': 'warning',
})
}
})
} else {
Taro.createBLEConnection({
deviceId: deviceId,
success: () => {
this.printLog("設(shè)備連接成功");
this.getBLEServiceId(deviceId);
},
fail: resf => {
this.printLog("設(shè)備連接失敗" + resf.errMsg);
}
})
// this.closeBLE(deviceId, () => {
// console.log("預(yù)先關(guān)閉缴川,再打開");
// setTimeout(() => {
// Taro.createBLEConnection({
// deviceId: deviceId,
// success: () => {
// this.printLog("設(shè)備連接成功");
// this.getBLEServiceId(deviceId);
// },
// fail: resf => {
// this.printLog("設(shè)備連接失敗" + resf.errMsg);
// }
// })
// }, 2000)
// });
}
}
closeBLE = (deviceId, callback) => {
Taro.closeBLEConnection({
deviceId: deviceId,
success: () => {
this.printLog("斷開設(shè)備[" + deviceId + "]成功.");
// console.log(res)
},
fail: () => {
this.printLog("斷開設(shè)備[" + deviceId + "]失敗.");
},
complete: callback
})
}
// 往藍牙傳輸數(shù)據(jù)
getBLEServiceId = deviceId => {
this.printLog("獲取設(shè)備[" + deviceId + "]服務(wù)列表")
Taro.getBLEDeviceServices({
deviceId: deviceId,
success: res => {
const services = res.services;
if (isEmpty(services)) {
this.printLog("未找到主服務(wù)列表")
return;
}
this.printLog('找到設(shè)備服務(wù)列表個數(shù): ' + services.length);
console.log(services);
// if (services.length == 1){
const service = services[0];
this.printLog("服務(wù)UUID:[" + service.uuid + "] Primary:" + service.isPrimary);
this.getBLECharactedId(deviceId, service.uuid);
// }else{ //多個主服務(wù)
// //TODO
// }
},
fail: res => {
this.printLog("獲取設(shè)備服務(wù)列表失敗" + res.errMsg);
}
})
}
getBLECharactedId = (deviceId, serviceId) => {
this.printLog("獲取設(shè)備特征值")
Taro.getBLEDeviceCharacteristics({
deviceId: deviceId,
serviceId: serviceId,
success: res => {
// console.log(res);
//這里會獲取到兩個特征值,一個用來寫描馅,一個用來讀
const chars = res.characteristics;
if (isEmpty(chars)) {
this.printLog("未找到設(shè)備特征值")
return;
}
// console.log('char-------', chars);
this.printLog("找到設(shè)備特征值個數(shù):" + chars.length);
if (chars.length == 2) {
for (var i = 0; i < chars.length; i++) {
var char = chars[i];
// console.log(char);
this.printLog("特征值[" + char.uuid + "]")
const prop = char.properties;
console.log('-----特征值', char, prop, deviceId, serviceId, char.uuid)
if (prop.notify == true) {
this.printLog("該特征值屬性: Notify");
this.recvBLECharacterNotice(deviceId, serviceId, char.uuid);
} else if (prop.write == true) {
this.printLog("該特征值屬性: Write");
this.sendBLECharacterNotice(deviceId, serviceId, char.uuid);
} else {
this.printLog("該特征值屬性: 不支持寫操作");
}
}
} else {
//TODO
}
},
fail: () => {
this.printLog("獲取設(shè)備特征值失敗");
}
})
}
recvBLECharacterNotice = (deviceId, serviceId, charId) => {
//接收設(shè)置是否成功
this.printLog("注冊Notice 回調(diào)函數(shù)");
Taro.notifyBLECharacteristicValueChange({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: charId,
state: true, //啟用Notify功能
success: () => {
Taro.onBLECharacteristicValueChange(res => {
console.log(res);
this.printLog("收到Notify數(shù)據(jù): " + ab2hex(res.value));
//關(guān)閉藍牙
Taro.showModal({
title: '配網(wǎng)成功',
content: ab2hex(res.value),
showCancel: false
})
});
},
fail: res => {
console.log(res);
this.printLog("特征值Notice 接收數(shù)據(jù)失敗: " + res.errMsg);
}
})
}
sendBLECharacterNotice = (deviceId, serviceId, charId) => {
//發(fā)送ssid/pass
this.printLog("延時1秒后把夸,發(fā)送SSID/PASS");
const { wifiName, wifiPassWord } = this.state;
const buffer = string2buffer(JSON.stringify({
'ssid': wifiName,
'pass': wifiPassWord
}));
setTimeout(() => {
Taro.writeBLECharacteristicValue({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: charId,
value: buffer,
success: () => {
this.printLog("發(fā)送SSID/PASS成功");
},
fail: res => {
this.printLog("發(fā)送失敗." + res.errMsg);
},
complete: () => {
Taro.closeBluetoothAdapter({
success: () => {
this.printLog("發(fā)送完畢,關(guān)閉藍牙設(shè)備");
this.closeFlag = true;
}
})
}
})
}, 1000);
}
printLog = text => {
const { logs } = this.state;
logs.unshift(text);
this.setState({ logs })
}
handleChangeName = (e) => {
this.setState({
wifiName: e
})
}
handleChangePwd = (e) => {
this.setState({
wifiPassWord: e
})
}
onPickerChange = e => {
const { deviceArray } = this.state;
this.setState({
currDeviceID: deviceArray[e.detail.value]
})
}
render() {
const { wifiName, wifiPassWord, logs, deviceArray, currDeviceID } = this.state;
return (
<View className='blue-tooth-index'>
<AtMessage />
<View className='blue-title'>wifi信息(請先打開藍牙流昏,Android用戶需打開GPS定位)</View>
<AtForm className='contain-form'>
<AtInput
name='name'
title='wifi名稱'
type='text'
placeholder='請輸入wifi名稱'
value={wifiName}
onChange={e => { this.handleChangeName(e) }}
/>
<AtInput
name='password'
title='wifi密碼'
type='text'
placeholder='請輸入wifi密碼'
value={wifiPassWord}
onChange={e => { this.handleChangePwd(e) }}
/>
</AtForm>
<View className='blue-title'>選擇藍牙設(shè)備</View>
<Picker mode='selector' range={deviceArray} onChange={this.onPickerChange}>
<AtList>
<AtListItem
title='藍牙設(shè)備'
extraText={currDeviceID}
/>
</AtList>
</Picker>
<AtButton className='blue-btn-submit' type='primary' onClick={e => { this.onSubmit(e) }} >藍牙設(shè)備配網(wǎng)</AtButton>
<AtButton className='blue-btn-reset' onClick={e => { this.onReset(e) }} >搜索藍牙設(shè)備</AtButton>
<View className='blue-log'>
{
logs.map((v, i) => {
return (
<View className='at-article__p' key={i}>{v}</View>
)
})
}
</View>
</View>
)
}
}
export default BlueTooth;
utils.js
import Taro from "@tarojs/taro";
/**
* 將字符串轉(zhuǎn)換成ArrayBufer
*/
export function string2buffer(str) {
if (!str) return;
var val = "";
for (var i = 0; i < str.length; i++) {
val += str.charCodeAt(i).toString(16);
}
console.log(val);
str = val;
val = "";
let length = str.length;
let index = 0;
let array = []
while (index < length) {
array.push(str.substring(index, index + 2));
index = index + 2;
}
val = array.join(",");
// 將16進制轉(zhuǎn)化為ArrayBuffer
return new Uint8Array(val.match(/[\da-f]{2}/gi).map(function (h) {
return parseInt(h, 16)
})).buffer
}
/**
* 將ArrayBuffer轉(zhuǎn)換成字符串
*/
export function ab2hex(buffer) {
var hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function (bit) {
return ('00' + bit.toString(16)).slice(-2)
}
)
return hexArr.join(':');
}
/**
* 判斷傳值是否為空扎即、[]、{}
* @param {*} param
* @returns
*/
export const isEmpty = (param) => {
if (param == null) {
return true;
} else if (typeof param === 'string') {
return param == '';
} else if (typeof param === 'object') {
return JSON.stringify(param) == '[]' || JSON.stringify(param) == '{}';
}
return false;
}