Page({ data: { devices: [], isSearching: false, status: '未连接', connected: false, skipCount: 0, connectedDeviceId: null, deviceServices: [], deviceCharacteristics: [], blueIsOk: false, battery: null, batteryLoading: false, deviceName: '', nameInput: '', nameLoading: false }, // 1. 初始化蓝牙模块 startBluetooth() { const that = this wx.closeBluetoothAdapter({ success:()=>{ wx.openBluetoothAdapter({ success: () => { // console.log('蓝牙初始化成功'); this.setData({ blueIsOk: true }) that.searchDevices() //开始搜索设备 // wx.showToast({ title: '蓝牙已开启', icon: 'success' }); }, fail: (res) => { console.error('蓝牙初始化失败', res); wx.showModal({ title: '提示', content: '请打开手机蓝牙并授权小程序使用', showCancel: false, }); } }); } }) }, onLoad(options) { this.startBluetooth() console.log(options.params) if (options.params) { this.setData({ params: options.params.replaceAll('*', '=').replaceAll('@', '&') }) console.log(this.data.params) } }, // 2. 搜索设备 searchDevices() { const that = this if (!this.data.blueIsOk) return wx.showModal({ title: '提示', content: '请打开手机蓝牙并授权小程序使用', showCancel: false, }); this.setData({ isSearching: true, devices: [] }); const last_device_id = wx.getStorageSync('device_id') const uuid = wx.getStorageSync('uuid') // if (uuid != '' && device_id != '') { // wx.getConnectedBluetoothDevices({ // services: [uuid], // success: ress => { // console.log('aa', ress) // console.log('看看有没有', ress) // console.log('看看有没有', ress) // console.log('看看有没有', ress) // console.log('看看有没有', ress) // if (ress.devices.length > 0) { // for (let item of ress.devices) { // if (item.deviceId == device_id) { // that.setData({ // connectedDeviceId: device_id, // devices: [item, ...that.data.devices] // }, () => { // console.log('nowDevice===========>', that.data.devices) // console.log('nowDevice===========>', that.data.devices) // console.log('nowDevice===========>', that.data.devices) // console.log('nowDevice===========>', that.data.devices) // }) // } // } // } else { // //尝试链接原来的蓝牙 // wx.closeBLEConnection() // return // wx.showModal({ // title: '是否连接上次的设备?', // content: '', // complete: (res) => { // if (res.cancel) { // } // if (res.confirm) { // that.connectDevice({ // currentTarget: { // dataset: { // device: { // deviceId: device_id // } // } // } // }) // } // } // }) // // that.connectDevice({ // // currentTarget:{ // // dataset:{ // // device:{deviceId:device_id} // // } // // } // // }) // } // } // }) // } // 启动搜索 wx.startBluetoothDevicesDiscovery({ allowDuplicatesKey: false, success: () => { console.log('开始搜索设备'); // 监听发现设备事件 wx.onBluetoothDeviceFound((res) => { console.log('发现新设备', res.devices); res.devices.forEach(device => { if (device.name == 'YD-Jump' || device.name == 'YD_Jump' || device.name == 'YD-O1' || device.localName == 'YD-O1' || device.localName == 'YD-Jump' || device.localName == 'YD_Jump') { const exists = this.data.devices.find(d => d.deviceId === device.deviceId); if (!exists) { if (device.deviceId == last_device_id) { that.connectDevice({ currentTarget: { dataset: { device: device } } }) } this.setData({ devices: [...this.data.devices, device] }); } } }); }); }, fail: (err) => { console.error('搜索设备失败', err); wx.showToast({ title: '搜索失败', icon: 'error' }); this.setData({ isSearching: false }); } }); // 设置超时自动停止 setTimeout(() => { wx.stopBluetoothDevicesDiscovery(); this.setData({ isSearching: false }); console.log('停止搜索'); }, 9000); // 10秒后停止 }, // 3. 连接设备 connectDevice(e) { if (this.data.connectedDeviceId) { //如果已经链接先断开连接 this.disconnectDevice() } const device = e.currentTarget.dataset.device; console.log('尝试连接设备', device); wx.showLoading({ title: '连接中...', }) wx.createBLEConnection({ deviceId: device.deviceId, success: () => { console.log('连接成功', device.deviceId); console.log('连接成功的设备信息:', JSON.stringify(device)); wx.showToast({ title: '连接成功', icon: 'success' }); this.setData({ status: '已连接', connected: true, connectedDeviceId: device.deviceId }); wx.setStorageSync('device_id', device.deviceId) wx.setStorageSync('device_name', device.name) if (device.name == 'YD-O1') { const serviceId = device.advertisServiceUUIDs[0]; wx.setStorageSync('uuid', serviceId) this.setupWriteAndNotify(device.deviceId, serviceId); } else { // 获取设备的服务 wx.getBLEDeviceServices({ deviceId: device.deviceId, success: (res) => { console.log('设备服务', res.services); // YD_Jump(下划线)使用 services[1],YD-Jump(短横线)使用 services[0] const serviceIndex = device.name === 'YD_Jump' ? 1 : 0; const serviceId = res.services[serviceIndex].uuid; wx.setStorageSync('uuid', serviceId) this.setupWriteAndNotify(device.deviceId, serviceId); }, fail: (err) => { console.error('获取服务失败', err); } }); } if (this.data.params) { wx.redirectTo({ url: '/subpackage/train/bluetooth/train?' + this.data.params, }) } }, fail: (err) => { console.error('连接失败', err); wx.showToast({ title: '连接失败', icon: 'error' }); }, complete: () => { wx.hideLoading() } }); }, // 获取设备的服务 getDeviceServices(deviceId) { wx.getBLEDeviceServices({ deviceId: deviceId, success: (res) => { console.log('设备服务', res.services); this.setData({ deviceServices: res.services }); wx.getConnectedBluetoothDevices({ services: [res.services[0].uuid], success: res => { console.log('aaaaa333333===>', res) } }) wx.getConnectedBluetoothDevices({ services: [res.services[1].uuid], success: res => { console.log('aaaaa444444===>', res) } }) // 获取第一个服务的特征值 // if (res.services.length > 0) { // this.getDeviceCharacteristics(deviceId, res.services[0].uuid); // } }, fail: (err) => { console.error('获取服务失败', err); } }); }, // 获取第一个服务的特征值 getDeviceCharacteristics(deviceId, serviceId) { wx.getBLEDeviceCharacteristics({ deviceId: deviceId, serviceId: serviceId, success: (res) => { console.log('服务特征值', res.characteristics); this.setData({ deviceCharacteristics: res.characteristics }); // 启动接收数据通知 this.startNotify(deviceId, serviceId); // 发送启动自由跳绳的命令 this.startFreeJumpRope(deviceId, serviceId); }, fail: (err) => { console.error('获取特征值失败', err); } }); }, // 启动接收数据通知 startNotify(deviceId, serviceId) { const characteristics = this.data.deviceCharacteristics; characteristics.forEach(characteristic => { if (characteristic.properties.notify) { wx.notifyBLECharacteristicValueChange({ deviceId: deviceId, serviceId: serviceId, characteristicId: characteristic.uuid, state: true, success: () => { console.log('启动数据通知'); }, fail: (err) => { console.error('启动通知失败', err); } }); // 接收数据 wx.onBLECharacteristicValueChange((res) => { console.log('接收到数据', res.value); if (res.deviceId === deviceId && res.characteristicId === characteristic.uuid) { console.log('接收到数据', res.value); this.handleReceivedData(res.value); } }); } }); }, // 发送启动自由跳绳命令 startFreeJumpRope(deviceId, serviceId) { const command = 3; // 启动自由跳绳的命令 const code = 1; const parameter = 0; // 启动自由跳绳,参数为0 // 获取蓝牙设备的特征值(特征值应是你已经连接的蓝牙设备的写入特征值) const characteristic = this.data.deviceCharacteristics.find(c => c.properties.write); if (!characteristic) { console.error('没有可写的特征值'); return; } // 构建命令数据 const commandData = new ArrayBuffer(4); const dataView = new DataView(commandData); dataView.setUint8(0, 0x03); dataView.setUint8(1, 0x01); dataView.setUint8(2, 0x00); dataView.setUint8(3, 0x00); // 发送命令到设备 wx.writeBLECharacteristicValue({ deviceId: deviceId, serviceId: serviceId, characteristicId: characteristic.uuid, value: commandData, success: () => { console.log("自由跳绳已启动!"); }, fail: (error) => { console.error("启动自由跳绳失败", error); } }); }, // 处理接收到的数据 handleReceivedData(arrayBuffer) { // 1. 创建 Int16Array 视图(从 ArrayBuffer 读取) const int16Array = new Int16Array(arrayBuffer); // 2. 检查长度是否足够(至少 3 个元素) if (int16Array.length < 3) { console.error("Int16Array length is too short!"); return; } // 3. 读取索引 2 的值 const value = int16Array[2]; // 4. 更新数据 this.setData({ skipCount: value }); }, // 连接成功后获取特征值、存储写特征并建立通知监听 setupWriteAndNotify(deviceId, serviceId) { wx.getBLEDeviceCharacteristics({ deviceId, serviceId, success: (res) => { const writeChar = res.characteristics.find(c => c.properties.write || c.properties.writeNoResponse); if (writeChar) { wx.setStorageSync('writeCharacteristic', writeChar.uuid); } const notifyChar = res.characteristics.find(c => c.properties.notify); if (notifyChar) { wx.notifyBLECharacteristicValueChange({ deviceId, serviceId, characteristicId: notifyChar.uuid, state: true, }); wx.onBLECharacteristicValueChange((result) => { const deviceName = wx.getStorageSync('device_name'); if (deviceName === 'YD-O1') { // YD-O1: 二进制协议解析电量响应 const int16Array = new Int16Array(result.value); if (int16Array.length > 0) { const battery = int16Array[0]; // 根据实际协议调整字段索引 if (battery >= 0 && battery <= 100) { this.setData({ battery, batteryLoading: false }); wx.hideLoading(); } } } else { // YD_Jump / YD-Jump: 字符串协议解析 $Batt,xx! const bytes = new Uint8Array(result.value); let str = ''; for (let i = 0; i < bytes.length; i++) { str += String.fromCharCode(bytes[i]); } console.log('收到蓝牙数据:', str); // 解析电量响应 $Batt,xx! const battMatch = str.match(/\$Batt,(\d+)!/); if (battMatch) { this.setData({ battery: parseInt(battMatch[1], 10), batteryLoading: false }); wx.hideLoading(); } // 解析获取名称响应 $My Name:xxxxxxxx! const nameMatch = str.match(/\$My Name:(.+)!/); if (nameMatch) { this.setData({ deviceName: nameMatch[1].trim(), nameLoading: false }); wx.hideLoading(); } // 解析修改名称响应 $Set Name OK! if (str.includes('$Set Name OK!')) { wx.hideLoading(); wx.showToast({ title: '名称修改成功', icon: 'success' }); } } }); } }, fail: (err) => console.error('获取特征值失败', err) }); }, // 4. 断开连接 disconnectDevice(id) { wx.closeBLEConnection({ deviceId: id || this.data.connectedDeviceId, success: () => { console.log('断开连接'); this.setData({ status: '未连接', connected: false, battery: null, batteryLoading: false, deviceName: '', nameInput: '', nameLoading: false }); } }); }, // 5. 获取设备电量(按设备类型发送不同协议命令) getBattery() { if (!this.data.connected || !this.data.connectedDeviceId) { wx.showToast({ title: '请先连接设备', icon: 'none' }); return; } const characteristic = wx.getStorageSync('writeCharacteristic'); if (!characteristic) { wx.showToast({ title: '特征值未就绪,请重新连接', icon: 'none' }); return; } const deviceId = this.data.connectedDeviceId; const serviceId = wx.getStorageSync('uuid'); const deviceName = wx.getStorageSync('device_name'); wx.showLoading({ title: '获取电量中...' }); this.setData({ batteryLoading: true }); let commandData; if (deviceName === 'YD-O1') { // YD-O1: 二进制协议获取电量 commandData = new ArrayBuffer(4); const dataView = new DataView(commandData); dataView.setUint8(0, 0x07); // 获取电量命令字节 dataView.setUint8(1, 0x00); dataView.setUint8(2, 0x00); dataView.setUint8(3, 0x00); } else { // YD_Jump / YD-Jump: 字符串协议获取电量 commandData = this.stringToArrayBuffer('$Get Batt!'); } wx.writeBLECharacteristicValue({ deviceId, serviceId, characteristicId: characteristic, value: commandData, success: () => { console.log(`${deviceName} 发送获取电量命令成功`); }, fail: (err) => { console.error('发送命令失败', err); wx.hideLoading(); this.setData({ batteryLoading: false }); wx.showToast({ title: '发送命令失败', icon: 'none' }); } }); }, // 获取设备名称 getDeviceName() { if (!this.data.connected || !this.data.connectedDeviceId) { wx.showToast({ title: '请先连接设备', icon: 'none' }); return; } const characteristic = wx.getStorageSync('writeCharacteristic'); if (!characteristic) { wx.showToast({ title: '特征值未就绪,请重新连接', icon: 'none' }); return; } const deviceId = this.data.connectedDeviceId; const serviceId = wx.getStorageSync('uuid'); wx.showLoading({ title: '获取名称中...' }); this.setData({ nameLoading: true }); wx.writeBLECharacteristicValue({ deviceId, serviceId, characteristicId: characteristic, value: this.stringToArrayBuffer('$Get Name!'), success: () => console.log('发送获取名称命令成功'), fail: (err) => { console.error('发送命令失败', err); wx.hideLoading(); this.setData({ nameLoading: false }); wx.showToast({ title: '发送命令失败', icon: 'none' }); } }); }, // 修改设备名称(名称不足8位用空格填充,超过8位截断) setDeviceName() { if (!this.data.connected || !this.data.connectedDeviceId) { wx.showToast({ title: '请先连接设备', icon: 'none' }); return; } const nameInput = this.data.nameInput.trim(); if (!nameInput) { wx.showToast({ title: '请输入新名称', icon: 'none' }); return; } const characteristic = wx.getStorageSync('writeCharacteristic'); if (!characteristic) { wx.showToast({ title: '特征值未就绪,请重新连接', icon: 'none' }); return; } const deviceId = this.data.connectedDeviceId; const serviceId = wx.getStorageSync('uuid'); // 名称固定8字节:不足补空格,超过截断 const paddedName = nameInput.padEnd(8, ' ').substring(0, 8); const cmd = `$Set Name:${paddedName}!`; console.log('发送修改名称命令:', cmd); wx.showLoading({ title: '修改名称中...' }); wx.writeBLECharacteristicValue({ deviceId, serviceId, characteristicId: characteristic, value: this.stringToArrayBuffer(cmd), success: () => console.log('修改名称命令已发送:', cmd), fail: (err) => { console.error('发送命令失败', err); wx.hideLoading(); wx.showToast({ title: '发送命令失败', icon: 'none' }); } }); }, // 名称输入框变化 onNameInput(e) { this.setData({ nameInput: e.detail.value }); }, // 字符串转 ArrayBuffer(与 bluetoothServe.js 保持一致) stringToArrayBuffer(str) { const buffer = new ArrayBuffer(str.length); const view = new Uint8Array(buffer); for (let i = 0; i < str.length; i++) { view[i] = str.charCodeAt(i) & 0xFF; } return buffer; } });