kitchendDevice/components/bluetooth_food.vue

744 lines
18 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="weightPages">
<view class="table">
<view class="text">
<image src="/static/zhong.png"></image>
<text v-if="isConnection == 0">{{bleTipsText}}</text>
<text v-if="isConnection == 1" @click="openBluetoothAdapter">连接失败,点击重新连接</text>
</view>
<view class="duan" @click="handleBack" v-if="isShow&&isConnection == 0">
断开连接
</view>
</view>
<!-- -->
<view class="weight-wrap">
<!-- 蓝牙称重 -->
<view class="weight" @click="inputDialogToggle">
<text class="val">{{weight == '' ? '--':weight}}</text>
<text class="unit">{{dw}}</text>
</view>
<!-- <view class="weight" v-else>
<input type="digit" v-model="activeType.weight" placeholder="请输入" @input="replaceInput" />
<text class="unit">克</text>
</view>
<view class="more" @click="handleToggleBle">
<image class="keybordIcon" :src="isBle?'/static/chengIcon.png':'/static/keybordIcon.png'"
mode="widthFix">
</image>
</view> -->
</view>
<!-- -->
<view class="groupbtn">
<view class="btn danwei">
<view class="lan border-bottom">
<view class="right">
<picker mode="selector" :range="unitList" range-key="name" @change="changleUnits"
:value="unitListIndex">
<view class="uni-input">
单位
</view>
</picker>
</view>
</view>
</view>
<view class="btn addbtn size14" @click="handlesub" v-if="weightType==2&&btnType==2">保存</view>
<view class="btn addbtn" @click="handleAddFood" v-if="weightType==2&&btnType==1">+</view>
<view class="btn qingling" @click="handleqingling">清零</view>
</view>
<!-- -->
<view>
<uni-popup ref="popup" type="dialog">
<uni-popup-dialog mode="input" title="重量" placeholder="请输入食物重量" @close="close"
@confirm="confirm"></uni-popup-dialog>
</uni-popup>
</view>
</view>
</template>
<script>
import {
mapState
} from "vuex";
let myTime
let nextCnt = 0
export default {
data() {
return {
dw: "克",
kcal: 0,
unit: '',
isShow: false,
bleTipsText: "",
inputDialog: false,
isConnection: 0, //是否连接成功
unitList: [{
name: "克",
id: '00',
unit: "g"
}, {
name: "盎司",
id: "08",
unit: "oz"
}],
unitListIndex: 0,
units: ['kg', '斤', 'st:lb', 'lb', 'g', 'ml', 'Waterml',
'milkml', 'oz', 'floz', 'lboz'
]
}
},
props: {
weightKcal: {
type: Number,
default: 0 //当前测量食物每100g含的kcal
},
weightType: {
type: Number,
default: -1 //0分类测量,1累计测量,2购物车测量
},
btnType: {
type: Number,
default: 1 //1添加食材2保存测量
},
},
computed: {
...mapState(["bleValue", "isBluetoothTyle", "countFoodInfo"]),
weight() {
return this.bleValue.countWeight
}
},
mounted() {
let that = this
if (that.bleValue.serviceId != '') {
that.getBLECharacteristicValueChange(that.bleValue.deviceId, that.bleValue.serviceId, that.bleValue.notify)
} else {
that.openBluetoothAdapter()
}
that.onBLEConnectionStateChange()
uni.onBluetoothAdapterStateChange(function(res) {
that.$store.commit("changeBluetooth", res.available);
})
},
destroyed() {
// this.isConnection = 1
// this.closeBLEConnection()
// this.closeBluetoothAdapter()
},
watch: {
isBluetoothTyle: function() {
let that = this
if (!that.isBluetoothTyle) {
that.handleBack()
}
},
},
methods: {
// 初始化蓝牙
openBluetoothAdapter() {
let that = this
that.kcal = ""
that.isShow = false
that.bleTipsText = "蓝牙搜索中"
uni.openBluetoothAdapter({
success: e => {
that.isConnection = 0
that.startBluetoothDeviceDiscovery()
},
fail: e => {
that.isConnection = 0
that.bleTipsText = "请确定设备和手机蓝牙已打开!"
console.log('openBluetoothAdapter', e)
}
});
},
// 开始搜寻附近的蓝牙外围设备
startBluetoothDeviceDiscovery() {
let that = this
uni.startBluetoothDevicesDiscovery({
allowDuplicatesKey: true,
services: [
// "F0A0",
// "A5FE"
],
success: res => {
that.isConnection = 0
that.onBluetoothDeviceFound();
},
fail: res => {
that.isConnection = 0
that.bleTipsText = "请确定设备和手机蓝牙已打开!"
console.log('startBluetoothDeviceDiscovery', res)
}
});
},
// 监听蓝牙连接状态
onBLEConnectionStateChange() {
let that = this
uni.onBLEConnectionStateChange(function(res) {
console.log("监听蓝牙连接状态", res.connected)
if (!res.connected) {
that.isConnection = 1
that.closeBLEConnection()
that.closeBluetoothAdapter()
}
that.$store.commit("changeBluetooth", res.connected);
})
},
/**
* 停止搜索蓝牙设备
*/
stopBluetoothDevicesDiscovery() {
uni.stopBluetoothDevicesDiscovery({
success: e => {
console.log("停止搜索蓝牙设备", e)
},
});
},
/**
* 发现外围设备
*/
onBluetoothDeviceFound() {
var that = this;
that.isConnection = 0
uni.onBluetoothDeviceFound(res => {
res.devices.forEach(device => {
device.advertisData = device.advertisData ? device.advertisData : ''
device.advertisServiceUUIDs = device.advertisServiceUUIDs ? device
.advertisServiceUUIDs : ""
let value = that.$tools.ab2hex(device.advertisData, "")
let id = value.substring(0, 4)
if (!device.name && !device.localName) {
return
}
if (device.name.indexOf("EL") !== -1 && device.advertisServiceUUIDs != '') {
that.isConnection = 0
that.bleTipsText = "测量中,请将食物放到秤上"
let value = that.$tools.ab2hex(device.advertisData)
let parseDataRes = plugin.parseBroadcastData(device.advertisData)
let analyzeData = plugin.analyzeBroadcastScaleData(parseDataRes)
let analyzeDataText = analyzeData.text
let data = analyzeData.data
if (parseDataRes.status == 1) {
let data0 = parseDataRes.payload
let data = parseInt(data0[3]).toString(16)
console.log('data' + data)
let data1 = parseInt(data0[4]).toString(16)
let data2 = parseInt((data + data1), 16) //重量
//
let unit0 = parseInt(data0[5]).toString(16) //单位小数点
let unit = unit0.length > 1 ? unit0.substring(1, 2) : unit0 //单位
let num = parseInt(unit0.substring(0, 1), 16).toString(8)
let dot = num.toString().substring(0, 1) //小数点
let zfz = 0 //正负值
if (num.toString().length > 1) {
dot = num.toString().substring(1, 2)
zfz = num.toString().substring(0, 1)
}
if (unit == '0') {
that.dw = 'g'
}
if (unit == "7") {
that.dw = "ml"
}
if (unit == "3") {
that.dw = "oz"
}
if (unit == "2") {
that.dw = "lb'oz"
}
if (dot == "1") {
data2 = data2 / 10
}
if (dot == "2") {
data2 = data2 / 100
}
if (zfz == "0") {
data2 = data2
}
if (zfz == "1") {
data2 = "-" + data2
}
that.weight = data2
that.kcal = (Number(that.weightKcal) / 100 * data2).toFixed(2)
that.$emit('realTimeWeight', data2, that.dw)
}
return
}
if (device.name.indexOf('Chipsea-BLE') != -1 || (device.localName && device
.localName.indexOf('Chipsea-BLE') != -1) || id == 'a5fe') {
that.stopBluetoothDevicesDiscovery()
that.connectDevice(device.deviceId)
return
}
})
});
},
//连接设备
async connectDevice(device_id) {
let that = this;
uni.createBLEConnection({
deviceId: device_id,
success: res => {
setTimeout(function() {
that.getBLEDeviceServices(device_id)
}, 200)
},
fail: res => {
console.log("设备连接失败,请重新连接", res);
}
});
},
/**
* 获取设备的UUID
*/
getBLEDeviceServices(device_id) {
let serviceList = [];
let that = this;
uni.getBLEDeviceServices({
deviceId: device_id,
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("FFF0") != -1) {
that.getBLEDeviceCharacteristics(device_id, service.uuid);
break;
}
}
},
fail: res => {
console.log('获取设备的UUID失败:', res)
}
});
},
/**
* 获取指定服务的特征值
*/
getBLEDeviceCharacteristics(deviceId, serviceId) {
let characteristicsList = [];
let that = this;
uni.getBLEDeviceCharacteristics({
deviceId: deviceId,
serviceId: serviceId,
success: res => {
console.log("服务的特征值成功", res)
let write, notify
for (let i = 0; i < res.characteristics.length; i++) {
let item = res.characteristics[i];
if (item.uuid.indexOf('0000FFF2') != -1) {
write = item.uuid
} else if (item.uuid.indexOf('0000FFF1') != -1) {
notify = item.uuid
}
}
let info = {
deviceId: deviceId,
serviceId: serviceId,
notify: notify,
write: write,
}
that.handletoggleUnit(0x04)
that.$store.commit('changeBluetoothValue', info)
setTimeout(function() {
that.getBLECharacteristicValueChange(deviceId, serviceId, notify)
}, 300)
},
fail: res => {
console.log('获取特征值失败:', JSON.stringify(res))
}
})
},
getBLECharacteristicValueChange(deviceId, serviceId, notify) {
let that = this
uni.notifyBLECharacteristicValueChange({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: notify,
state: true,
success: () => {
that.isShow = true
that.isConnection = 0
that.bleTipsText = "测量中,请将食物放到秤上"
uni.onBLECharacteristicValueChange(function(res) {
const value = res.value
const dataView = new DataView(value)
const header = dataView.getUint8(0)
console.log("value", that.$tools.ab2hex(res.value, ""))
// MCU主动上报数据
if (header === 0xC7) {
const cmd = dataView.getUint8(2)
switch (cmd) {
case 0x02:
that.parseWeightData(dataView)
break
case 0x03:
break
}
}
})
},
fail: res => {
console.log('获取特征值失败:', JSON.stringify(res))
}
})
},
changleUnits(e) {
let that = this
let name = that.unitList[e.detail.value].name
if (that.isShow && that.dw != name) {
that.handletoggleUnit(name == '盎司' ? 0x08 : 0x04)
that.$store.commit("changeBluetoothValue", {
countWeight: finalWeight,
unit: that.unitList[e.detail.value].unit
})
}
that.unitListIndex = [e.detail.value]
that.dw = that.unitList[e.detail.value].name
},
handletoggleUnit(unit) {
let that = this
let checksum = 0;
const bytes = [0xC5, 0x03, 0x05, 0x11]
bytes[4] = unit
for (let i = 0; i < bytes.length; i++) {
checksum ^= bytes[i];
}
bytes[5] = checksum
that.sendData(new Uint8Array(bytes).buffer)
},
handleqingling() {
let that = this
let str = "C503071100D0"
let buf = new Uint8Array(str.match(/[\da-f]{2}/gi).map(function(h) {
return parseInt(h, 16)
}))
that.sendData(buf.buffer)
},
sendData(buffer) {
let that = this
uni.writeBLECharacteristicValue({
deviceId: that.bleValue.deviceId,
serviceId: that.bleValue.serviceId,
characteristicId: that.bleValue.write,
value: buffer,
success: res => {
console.log('下发指令成功', res.errMsg)
},
fail: res => {
console.log("下发指令失败", res);
},
})
},
parseWeightData(dataView) {
const statusByte = dataView.getUint8(4)
const isNegative = !!(statusByte & 0x80) // 最高位表示正负
const statusType = statusByte & 0x0F // 状态类型
// 组合24位重量值 (大端序)
const weightValue =
(dataView.getUint8(5) << 16) |
(dataView.getUint8(6) << 8) |
dataView.getUint8(7)
// 精度和单位
const unitByte = dataView.getUint8(8)
const precision = (unitByte & 0xF0) >> 4 // 高4位精度
const unitIndex = unitByte & 0x0F // 低4位单位
// 计算实际重量
let finalWeight = weightValue / Math.pow(10, precision)
if (isNegative) finalWeight = -finalWeight
if (this.units[unitIndex] == 'kcal') {
this.dw = '千卡'
} else if (this.units[unitIndex] == 'g') {
this.dw = '克'
} else if (this.units[unitIndex] == 'lb') {
this.dw = '磅'
} else if (this.units[unitIndex] == 'oz') {
this.dw = '盎司'
}
this.$store.commit("changeBluetoothValue", {
countWeight: finalWeight,
unit: this.units[unitIndex]
})
// this.$emit('realTimeWeight', finalWeight, this.dw)
// 状态处理
// if (statusType === 0x02) {
// this.$emit('handleBle', finalWeight,this.unit,0)
// }
},
// 保存测量结果
handlesub() {
let that = this
console.log("weight", that.weight)
if (Number(that.weight) > 0) {
that.$emit("handleBle", that.weight, that.dw, that.kcal)
} else {
that.$tools.msg("数据异常,请清零后重新测量!")
}
},
handleBack() {
let that = this
that.isConnection = 1
that.stopBluetoothDevicesDiscovery() //取消蓝牙搜索
that.closeBLEConnection()
that.closeBluetoothAdapter()
},
/**
* 断开蓝牙模块
*/
closeBluetoothAdapter() {
let that = this;
uni.closeBluetoothAdapter({
success: res => {
that.isConnection = 1
that.$store.commit("changeBluetoothValue", {
deviceId: "",
serviceId: "",
notify: "",
write: "",
unit: "",
countWeight: 0,
})
console.log('蓝牙模块关闭成功');
}
})
},
/**
* 断开蓝牙连接
*/
closeBLEConnection() {
var that = this;
uni.closeBLEConnection({
deviceId: that.bleValue.deviceId,
success: res => {
that.isConnection = 1
console.log('断开蓝牙连接成功');
}
});
},
unitConversion(unit) {
if (unit == 'kcal') {
return '千卡'
} else if (unit == 'g') {
return '克'
} else if (unit == 'lb') {
return '磅'
} else if (unit == 'oz') {
return '盎司'
}
return unit
},
// 添加食物
handleAddFood() {
uni.navigateTo({
url: "/pageTwo/count/search?name=早餐&time=" + this.countFoodInfo.date
})
},
inputDialogToggle() {
this.$refs.popup.open()
},
confirm(value) {
console.log(value)
this.$store.commit("changeBluetoothValue", {
countWeight: value,
unit: this.unitList[this.unitListIndex].unit
})
this.$refs.popup.close()
},
close() {
this.$refs.popup.close()
},
// isNutritionScale(advertisData) {
// const buffer = this.base64ToArrayBuffer(advertisData)
// const dataView = new DataView(buffer)
// // 检查厂商自定义数据头
// if (dataView.getUint16(0) !== 0xA5FE) return false
// // 检查产品类型 (营养秤:0x0001)
// const typeId = dataView.getUint16(2)
// if (typeId !== 0x0001) return false
// // 检查厂商ID (通用方案:0x0001)
// const vendorId = dataView.getUint16(4)
// return vendorId === 0x0001
// },
// base64ToArrayBuffer(base64) {
// const str = atob(base64)
// const buffer = new ArrayBuffer(str.length)
// const view = new Uint8Array(buffer)
// for (let i = 0; i < str.length; i++) {
// view[i] = str.charCodeAt(i)
// }
// return buffer
// }
},
}
</script>
<style scoped lang="scss">
image {
width: 22px;
height: 22px;
}
.more {
padding: 6rpx 10rpx;
border-radius: 12rpx;
color: #fff;
background-color: #f0ae43;
}
.weightPages {
display: flex;
flex-wrap: wrap;
flex-direction: column;
position: relative;
background: #fff;
border-radius: 20rpx;
justify-content: space-around;
.weight-wrap {
display: flex;
justify-content: center;
align-items: center;
background: #fff;
color: #666;
font-size: 16px;
text-align: center;
height: 60px;
margin: 10px 0;
.weight,
.kcal {
display: flex;
justify-content: center;
align-items: center;
width: 70%;
padding: 30rpx 0;
border-radius: 20rpx;
background-color: #F8F8F8;
}
.weight {
.val {
font-size: 40rpx;
color: #F0AE43;
margin: 0 !important;
}
.unit {
padding: 10rpx;
margin-left: 30rpx;
font-size: 28rpx;
color: #fff;
border-radius: 8rpx;
background-color: #F0AE43;
}
}
.kcal {
font-size: 32rpx;
.val {
font-size: 40rpx;
color: #F0AE43;
margin: 0 !important;
}
.unit {
margin-left: 20rpx;
}
}
}
.tips {
font-size: 24rpx;
text-align: center;
}
.groupbtn {
margin-top: 15px;
display: flex;
align-items: center;
justify-content: space-between;
.btn {
width: 30%;
color: $maincolor;
text-align: center;
height: 40px;
line-height: 40px;
border-radius: 10px;
border: 1px solid $maincolor;
background: #fff;
margin: 0;
}
.addbtn {
width: 30%;
color: #fff;
font-size: 45px;
line-height: 38px;
background: $maincolor;
}
}
.table {
width: 100%;
font-size: 14px;
align-items: center;
padding: 5px 0;
border-radius: 5px;
display: flex;
justify-content: space-between;
.text {
color: #8284f0;
display: flex;
}
image {
width: 22px;
height: 22px;
margin-right: 5px;
}
}
.image {
width: 1120rpx;
height: 1120rpx;
margin: auto;
image {
width: 100%;
height: 100%;
}
}
.duan {
width: fit-content;
background: linear-gradient(-90deg, #d4f5c4, #a7d5e4 80%, );
border-radius: 5px;
text-align: center;
padding: 3px 10px;
font-size: 12px;
}
.tips {
margin-top: 30rpx;
margin-left: 30rpx;
display: flex;
color: #999;
}
}
</style>