adultDeviceApp/BLEPages/child/B68T.vue

604 lines
17 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="content skipping">
<view class="title" v-if="isConnection == 0">连接中,请稍后...</view>
<view class="title" v-else-if="isConnection == 1">连接成功,请开始测量</view>
<view class="title" style="color: brown;" v-else-if="isConnection == 2" @click="openBluetoothAdapter">连接失败,点击重新连接
</view>
<div class="result-wrap">
<view class="result-item">
<view class="result-title">身高</view>
<view class="result-value">{{info.height}}cm</view>
</view>
<view class="result-item">
<view class="result-title">体重</view>
<view class="result-value">{{info.weight}}kg</view>
</view>
<view class="result-item">
<view class="result-title">骨量</view>
<view class="result-value">{{info.boneMass}}kg</view>
</view>
<view class="result-item">
<view class="result-title">阻抗</view>
<view class="result-value">{{info.impedance}}Ω</view>
</view>
<view class="result-item">
<view class="result-title">BMI</view>
<view class="result-value">{{info.bmi}}</view>
</view>
<view class="result-item">
<view class="result-title">内脏脂肪</view>
<view class="result-value">{{info.visceralFat}}%</view>
</view>
<view class="result-item">
<view class="result-title">脂肪率</view>
<view class="result-value">{{info.fatRate}}%</view>
</view>
<view class="result-item">
<view class="result-title">水分率</view>
<view class="result-value">{{info.waterRate}}%</view>
</view>
<view class="result-item">
<view class="result-title">肌肉率</view>
<view class="result-value">{{info.muscleRate}}%</view>
</view>
</div>
<view class="image">
<image mode="aspectFit" src="/BLEPages/static/H08B2.gif"></image>
</view>
<view class="tips">
<view>提示:</view>
<view>1.请确定设备是开机状态</view>
<view>2.请确定手机蓝牙、位置信息已打开</view>
<view>3.ios系统需打开设置—>应用—>微信里的蓝牙权限</view>
</view>
<div class="info-dlg" v-if="show_info">
<view class="info-inner">
<view class="info-item">
<text>性别:</text>
<text class="man" :class="{'active':cur_sex==1}" @click="cur_sex=1">男</text>
<text class="woman" :class="{'active':cur_sex==2}" @click="cur_sex=2">女</text>
</view>
<view class="info-item">
<text>年龄:</text>
<!-- <input type="text" placeholder="请输入年龄" v-model="cur_age"/>
-->
<picker class="age-select" mode="selector" :value="cur_age" :range="ageRange" @change="onAgeChange">
{{cur_age+1}}岁
</picker>
</view>
<view class="confirm" @click="setUserInfo">确定</view>
</view>
</div>
</view>
</template>
<script>
import {
mapState
} from "vuex";
export default {
data() {
return {
deviceId: "",
serviceId: "",
write: "",
notify: "",
cur_sex: 1,
cur_age: 27,
show_info: false,
ageRange: [],
ageIndex: 30,
send_err_count: 0,
BLEResult: {
bmi: 0,
weight: 0,
fatRate: 0, //脂肪率
waterRate: 0, //水分率
muscleRate: 0, //肌肉率,
boneMass: 0, //骨量
impedance: 0, //阻抗
height: 0, //身高
value: 0
},
isConnection: 0,
}
},
computed: {
...mapState(["user", "isConnected", "isBluetoothTyle", "appTheme"]),
info() {
return this.user
}
},
mounted() {
let that = this
for (let i = 1; i <= 100; i++) {
that.ageRange.push(i);
}
let sex = uni.getStorageSync('sex')
let age = uni.getStorageSync('age')
that.cur_sex = sex ? sex : 1
that.cur_age = age ? age : 27
},
onLoad(options) {
let that = this
if (options && options.deviceId) {
that.deviceId = options.deviceId
that.stopBluetoothDevicesDiscovery()
that.createBLEConnection()
that.onBLEConnectionStateChange()
uni.onBluetoothAdapterStateChange(function(res) {
that.$store.commit("changeBluetooth", res.available);
})
}
},
onUnload: function() {
let that = this
if (!that.Unload) {
that.handleBack()
uni.switchTab({
url: "/pages/index/index"
})
console.log("返回上一个页面")
}
},
watch: {
isConnected: function() {
let that = this
if (!that.isConnected) {
that.handleBack()
that.isConnection = 2
}
},
isBluetoothTyle: function() {
let that = this
if (!that.isBluetoothTyle) {
that.handleBack()
that.isConnection = 2
}
},
},
methods: {
setUserInfo() {
let that = this
uni.setStorageSync('sex', that.cur_sex)
uni.setStorageSync('age', that.cur_age)
that.show_info = false
that.sendCommand(that.cur_sex, that.cur_age + 1);
},
onAgeChange(e) {
this.cur_age = parseInt(e.detail.value)
},
openBluetoothAdapter() {
let that = this
that.isConnection = 0
uni.openBluetoothAdapter({
success: e => {
console.log("初始化设备")
that.startBluetoothDeviceDiscovery()
},
fail: e => {
that.$tools.msg("请确定设备是开机状态、手机蓝牙权限已打开!")
}
});
},
// 开始搜寻附近的蓝牙外围设备
startBluetoothDeviceDiscovery() {
let that = this
uni.startBluetoothDevicesDiscovery({
allowDuplicatesKey: true,
success: res => {
console.log("开始搜索")
that.onBluetoothDeviceFound();
},
fail: res => {
that.$tools.msg("请确定设备是开机状态、手机蓝牙权限已打开!")
}
});
},
/**
* 发现外围设备
*/
onBluetoothDeviceFound() {
var that = this;
uni.onBluetoothDeviceFound(res => {
res.devices.forEach(device => {
if (!device.name && !device.localName) {
return
}
if (device.name.indexOf('JS-B68T') != -1 || (device.localName && device.localName.toLowerCase()
.indexOf('JS-B68T') != -1)) {
this.deviceId = device.deviceId
that.stopBluetoothDevicesDiscovery()
that.createBLEConnection()
}
})
});
},
// 连接蓝牙
createBLEConnection() {
let that = this;
that.isConnection = 0
uni.createBLEConnection({
deviceId: that.deviceId,
success: res => {
that.getBLEDeviceServices()
},
fail: res => {
console.log("设备连接失败,请重新连接", res);
}
});
},
/**
* 获取设备的UUID
*/
getBLEDeviceServices() {
let serviceList = [];
let that = this;
uni.getBLEDeviceServices({
deviceId: that.deviceId,
success: res => {
console.log("获取设备的UUID成功", res)
serviceList = res.services;
for (let i = 0; i < serviceList.length; i++) {
let service = serviceList[i];
if (service.uuid.indexOf("00001910") != -1) {
that.serviceId = service.uuid;
that.getBLEDeviceCharacteristics();
break;
}
}
},
fail: res => {
console.log('获取设备的UUID失败:', res)
}
});
},
/**
* 获取指定服务的特征值
*/
getBLEDeviceCharacteristics() {
let characteristicsList = [];
let that = this;
uni.getBLEDeviceCharacteristics({
deviceId: that.deviceId,
serviceId: that.serviceId,
success: res => {
console.log("服务的特征值成功", res)
for (let i = 0; i < res.characteristics.length; i++) {
let item = res.characteristics[i];
if (item.uuid.indexOf('00002B10') != -1) {
that.write = item.uuid
} else if (item.uuid.indexOf('00002B11') != -1) {
that.notify = item.uuid
}
}
uni.notifyBLECharacteristicValueChange({
deviceId: that.deviceId,
serviceId: that.serviceId,
characteristicId: that.notify,
state: true,
success: () => {
setTimeout(() => {
that.show_info = true
}, 1000);
uni.onBLECharacteristicValueChange(function(res) {
console.log(res.value)
const raw = new Uint8Array(res.value);
if (raw[0] == 0x55 && raw[1] == 0xAA) {
that.isConnection = 1
that.send_err_count = 0
uni.showToast({
title: '年龄信息发送成功,请开始测量'
})
return
}
if(raw[0] == 0x55 && raw[1] == 0xBB) {
if(that.send_err_count > 2) {
that.isConnection = 2
uni.showToast({
title: '年龄信息发送失败,请重新连接'
})
return
}
that.send_err_count++
that.setUserInfo()
return
}
if(raw[0] == 0x5A) {
// 体重(高低位组合, 实际值的100倍)
that.BLEResult.weight = ((raw[2] << 8) | raw[3]) / 100.0
if (that.BLEResult.weight < 1) {
that.BLEResult.weight = ((raw[2] << 8) | raw[3])
}
// 脂肪率(高低位组合, 实际值的10倍)
that.BLEResult.fatRate = ((raw[4] << 8) | raw[5]) / 10.0
// 水分率(高低位组合, 实际值的10倍)
that.BLEResult.waterRate = ((raw[6] << 8) | raw[7]) / 10.0
// 肌肉率(高低位组合, 实际值的10倍)
that.BLEResult.muscleRate = ((raw[8] << 8) | raw[9]) / 10.0
// 骨量(高低位组合, 实际值的10倍)
that.BLEResult.boneMass = ((raw[10] << 8) | raw[11]) / 10.0
// 阻抗(高位和低位组合)
that.BLEResult.impedance = (raw[12] << 8) | raw[13]
//BMI
that.BLEResult.bmi = ((raw[14] << 8) | raw[16]) / 10.0;
// 身高(高低位组合, 厘米)
that.BLEResult.height = raw[15]
if (raw.length == 18 && raw[17] == 0xA5) {
that.BLEResult.familyid = that.info.id
that.handleGetMeasure()
}
}
})
},
fail: res => {
console.log('获取特征值失败:', JSON.stringify(res))
}
})
},
fail: res => {
console.log('获取特征值失败:', JSON.stringify(res))
}
})
},
// 发送指令到设备
sendCommand(gender, age) {
let that = this;
// 创建11字节Buffer (协议要求长度)
const buffer = new ArrayBuffer(10);
const bytes = new Uint8Array(buffer);
// 填充固定数据
bytes[0] = 0x02; // 开始字节
bytes[1] = 0x53; // 备用1
// 设置性别 (根据参数动态设置)
bytes[2] = gender === 2 ? 0x31 : 0x30; // 女0x31, 男0x30
// 填充4字节固定数据 (0x30)
bytes[3] = 0x30;
bytes[4] = 0x30;
bytes[5] = 0x30;
// 处理年龄转换为两位ASCII字符串
const ageStr = age.toString().padStart(2, '0');
bytes[6] = ageStr.charCodeAt(0); // 十位ASCII
bytes[7] = ageStr.charCodeAt(1); // 个位ASCII
// 动态计算校验码 (异或0x02到年龄个位)
let checksum = bytes[0];
for (let i = 1; i <= 7; i++) {
checksum ^= bytes[i];
}
bytes[8] = checksum;
// 结束字节
bytes[9] = 0x03;
// 发送数据
uni.writeBLECharacteristicValue({
deviceId: that.deviceId,
serviceId: that.serviceId,
characteristicId: that.write,
value: buffer,
success: () => {
console.log("发送成功")
},
fail: (err) => {
uni.showToast({
title: '发送失败'
})
}
});
},
// 保存测量结果
handleGetMeasure() {
},
onBLEConnectionStateChange() {
let that = this
uni.onBLEConnectionStateChange(function(res) {
console.log("监听蓝牙连接状态", res.connected)
if (!res.connected) {
that.stopBluetoothDevicesDiscovery()
that.closeBLEConnection()
that.closeBluetoothAdapter()
that.isConnection = 2
}
that.$store.commit("changeConnected", res.connected);
})
},
handleBack(ind) {
let that = this
that.stopBluetoothDevicesDiscovery() //取消蓝牙搜索
that.closeBLEConnection()
that.closeBluetoothAdapter()
},
/**
* 停止搜索蓝牙设备
*/
stopBluetoothDevicesDiscovery() {
uni.stopBluetoothDevicesDiscovery({
success: e => {
console.log("停止搜索蓝牙设备", e)
},
});
},
/**
* 断开蓝牙模块
*/
closeBluetoothAdapter() {
let that = this;
uni.closeBluetoothAdapter({
success: res => {
console.log('蓝牙模块关闭成功');
}
})
},
/**
* 断开蓝牙连接
*/
closeBLEConnection() {
var that = this;
uni.closeBLEConnection({
deviceId: that.deviceId,
success: res => {
console.log('断开蓝牙连接成功');
}
});
},
}
}
</script>
<style lang="scss" scoped>
.title {
width: 100%;
text-align: center;
font-size: 36rpx;
font-weight: 700;
margin: 20rpx 0;
}
.result-wrap {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 30rpx;
box-sizing: border-box;
.result-item {
display: flex;
justify-content: flex-start;
align-items: center;
width: 48%;
padding: 20rpx;
margin-top: 16rpx;
box-sizing: border-box;
border-radius: 12rpx;
background-color: #fff;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.08);
.result-title {
flex: 150rpx 0 0;
font-size: 32rpx;
font-weight: 600;
color: #4b6cb7;
}
.result-value {
flex: 1;
font-size: 32rpx;
font-weight: 700;
color: #182848;
margin-top: 5rpx;
text-align: right;
}
}
}
.tips {
font-weight: 700;
padding: 0 20rpx;
box-sizing: border-box;
}
.image {
display: flex;
justify-content: center;
margin-top: 20rpx;
}
.info-dlg {
display: flex;
justify-content: center;
align-items: center;
position: fixed;
left: 0;
top: 0;
width: 100%;
height: 100%;
z-index: 99;
background-color: rgba(0, 0, 0, 0.8);
.info-inner {
width: 65%;
height: 350rpx;
border-radius: 20rpx;
background-color: #fff;
padding: 80rpx 50rpx;
box-sizing: border-box;
.info-item {
display: flex;
margin-bottom: 30rpx;
text {
font-size: 36rpx;
font-weight: 700;
}
.man,
.woman {
width: 120rpx;
height: 50rpx;
font-size: 28rpx;
line-height: 50rpx;
text-align: center;
border-radius: 8rpx;
border: 1px solid #e1e1e1;
margin-right: 10rpx;
&.active {
color: #fff;
}
}
.man.active {
background-color: #74b9ff;
}
.woman.active {
background-color: #fd79a8;
}
.age-select {
width: 210rpx;
height: 50rpx;
line-height: 50rpx;
border-radius: 8rpx;
padding: 0 20rpx;
border: 1px solid #e1e1e1;
}
}
.confirm {
width: 200rpx;
height: 50rpx;
color: #fff;
font-size: 32rpx;
line-height: 50rpx;
text-align: center;
margin: 0 auto;
background-color: #2ecc71;
}
.info-item:last-child {
margin-top: 50rpx;
}
}
}
</style>