From 5010b1bdbb251b92184a786ec4ef8576b991ad5e Mon Sep 17 00:00:00 2001 From: chenzhihao <1798906853@qq.com> Date: Wed, 4 Mar 2026 11:23:05 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0W13=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E8=AE=BE=E7=BD=AE=E8=93=9D=E7=89=99=E5=90=8D=E7=A7=B0=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Document/W13无卡取电设备 - 蓝牙通讯协议.md | 10 +++ app.json | 6 +- .../BluetoothDebugging/B13page/B13page.js | 82 ++++++++++++++++++- .../BluetoothDebugging/B13page/B13page.wxml | 18 ++++ .../BluetoothDebugging/B13page/B13page.wxss | 3 + utils/w13Packet.js | 33 ++++++++ 6 files changed, 148 insertions(+), 4 deletions(-) diff --git a/Document/W13无卡取电设备 - 蓝牙通讯协议.md b/Document/W13无卡取电设备 - 蓝牙通讯协议.md index b1fe2e0..90b7f28 100644 --- a/Document/W13无卡取电设备 - 蓝牙通讯协议.md +++ b/Document/W13无卡取电设备 - 蓝牙通讯协议.md @@ -202,6 +202,16 @@ MCU -> PC - 请求示例(PC→MCU):Frame_Type=0x17,P0=0x01。 - 响应示例(MCU→PC):Frame_Type=0x17,参数区域例如 P0~P7 = [0x0A,0x00, 0x14,0x00, 0x05,0x00, 0x08,0x00] 表示:门磁触发延时 10s,门磁释放延时 20s,卫浴触发 5s,卫浴释放 8s(均为小端)。 +### 3.10 设置蓝牙名 +| 方向 | 命令字 | 参数 | 备注 | +|------|-------|------|------| +| PC→MCU | 0x20 | P0: 设置名称的有效长度
P1~P4: 当前只支持设置房号后面四个字符 | 当前蓝牙名称:BLW_W13_XXXX;注意:后四位为字符类型 | +| MCU→PC | 0x20 | P0:
  0x01: 参数正确
  0x02: 参数错误 | 返回设置结果 | + +说明: +- 请求示例(PC→MCU):Frame_Type=0x20,P0=0x04,P1~P4 对应后四位字符(ASCII 小端/逐字节)。 +- 响应示例(MCU→PC):Frame_Type=0x20,P0=0x01 表示设置成功,P0=0x02 表示参数错误。 + ## 4. 命令交互流程图 ### 4.1 读版本号流程 ```mermaid diff --git a/app.json b/app.json index 2458a29..b386b81 100644 --- a/app.json +++ b/app.json @@ -1,8 +1,8 @@ { "pages": [ - - "pages/autho/index", - "pages/basics/BluetoothDebugging/B13page/B13page", + + "pages/autho/index", + "pages/basics/BluetoothDebugging/B13page/B13page", "pages/login/login", "pages/index/index", "pages/mycenter/index", diff --git a/pages/basics/BluetoothDebugging/B13page/B13page.js b/pages/basics/BluetoothDebugging/B13page/B13page.js index 75d783f..db35a2d 100644 --- a/pages/basics/BluetoothDebugging/B13page/B13page.js +++ b/pages/basics/BluetoothDebugging/B13page/B13page.js @@ -1,5 +1,5 @@ -const { buildCommand, buildReadVersion, buildSetDoorBathEvent, buildReadDoorBathEvent, COMMANDS, verifyHexPacket } = require('../../../../utils/w13Packet.js') +const { buildCommand, buildReadVersion, buildSetDoorBathEvent, buildReadDoorBathEvent, buildSetBluetoothName, COMMANDS, verifyHexPacket } = require('../../../../utils/w13Packet.js') const FIXED_CONNECT_CMD = new Uint8Array([0xCC, 0xC0, 0x0C, 0x00, 0x4F, 0xC0, 0x01, 0x00, 0x02, 0x00, 0x0C, 0x08]) // Optional encoding library for robust GBK/GB18030 decoding let EncodingLib = null @@ -19,6 +19,8 @@ Page({ bleMac: '00:00:00:00:00:00', bleAMC: '-', bleVersion: '-', + // 设置蓝牙名后四位输入 + bleNameSuffix: '', // 设备信号/RSSI/连接状态显示 bleSignal: '-', bleRSSI: '-', @@ -395,6 +397,53 @@ Page({ try { setTimeout(() => { try { this.updateLogListHeight && this.updateLogListHeight() } catch (e) {} }, 250) } catch (e) {} }, + onBleNameInput(e) { + const v = (e && e.detail && e.detail.value) ? String(e.detail.value) : '' + this.setData({ bleNameSuffix: v }) + }, + + onSendBleName() { + if (!this.data.isConnected) { wx.showToast({ title: '未连接设备', icon: 'none' }); return } + const suffix = (this.data.bleNameSuffix || '').toString() + if (suffix.length === 0 || suffix.length > 4) { + wx.showToast({ title: '请输入 1 到 4 个字符', icon: 'none' }) + return + } + // 简单校验:仅允许可打印 ASCII(32..126) + for (let i = 0; i < suffix.length; i++) { + const c = suffix.charCodeAt(i) + if (c < 32 || c > 126) { wx.showToast({ title: '仅支持可打印字符', icon: 'none' }); return } + } + // 二次确认:提示设备将重启 + wx.showModal({ + title: '确认设置', + content: '设置蓝牙名称后设备将重启,是否继续?', + confirmText: '继续', + cancelText: '取消', + success: (res) => { + if (!res.confirm) return + try { + const pkt = buildSetBluetoothName(suffix) + wx.showLoading({ title: '正在设置...' }) + this.transmitPacket(pkt, '设置蓝牙名') + // 乐观更新显示名称(可见性增强),但实际以设备返回为准 + try { + const cur = this.data.bleName || '' + if (/BLW_W13_/.test(cur)) { + const base = cur.slice(0, Math.max(0, cur.length - 4)) + const padded = suffix.padEnd(4, ' ') + this.setData({ bleName: base + padded }) + } else { + this.setData({ bleName: (cur || '') + suffix }) + } + } catch (e) { /* ignore */ } + } catch (err) { + wx.showToast({ title: '构包失败', icon: 'none' }) + } + } + }) + }, + onHide() { // 页面隐藏时不停止设备信息轮询,保留以便断开后继续刷新(如需可在此处暂停) }, @@ -1673,6 +1722,37 @@ Page({ this.appendLog('PARSE', `雷达状态: 有效端口${parsed.portCount} 有人标记=${parsed.human === 0x01 ? '有人' : '无人'} 位=0b${parsed.bits.toString(2).padStart(8, '0')}`) } } + // 设置蓝牙名响应 (Frame_Type = 0x20) + if (frameType === (COMMANDS.SET_BLE_NAME & 0xFF)) { + try { + const params = u8.slice(11) || [] + if (params.length >= 1) { + if (params[0] === 0x01) { + this.appendLog('PARSE', '设置蓝牙名:设备返回成功') + wx.hideLoading() + wx.showToast({ title: '设置蓝牙名成功,设备重启中...', icon: 'success' }) + // 等待短暂时间后返回蓝牙搜索页(上一页) + setTimeout(() => { + try { wx.navigateBack() } catch (e) { /* ignore */ } + }, 900) + } else { + this.appendLog('PARSE', '设置蓝牙名:设备返回参数错误') + wx.hideLoading() + wx.showToast({ title: '设备返回参数错误', icon: 'none' }) + } + } + try { + if (this._pendingResponse && this._pendingResponse.expectedType === (COMMANDS.SET_BLE_NAME & 0xFF)) { + const params = u8.slice(11) || [] + try { this._pendingResponse.resolve(params) } catch (e) { /* ignore */ } + try { if (this._pendingResponse._timeout) clearTimeout(this._pendingResponse._timeout) } catch (e) {} + this._pendingResponse = null + } + } catch (e) { /* ignore */ } + } catch (e) { + this.appendLog('WARN', '解析设置蓝牙名响应异常') + } + } // 读版本号响应 if (frameType === (COMMANDS.READ_VERSION & 0xFF)) { try { diff --git a/pages/basics/BluetoothDebugging/B13page/B13page.wxml b/pages/basics/BluetoothDebugging/B13page/B13page.wxml index f67d764..5ec30e7 100644 --- a/pages/basics/BluetoothDebugging/B13page/B13page.wxml +++ b/pages/basics/BluetoothDebugging/B13page/B13page.wxml @@ -65,6 +65,24 @@ --> + + + + 设置蓝牙名 + + + + 后四位 + + 当前: + {{bleName || '-'}} + + + + + + + diff --git a/pages/basics/BluetoothDebugging/B13page/B13page.wxss b/pages/basics/BluetoothDebugging/B13page/B13page.wxss index 628599d..fa4540c 100644 --- a/pages/basics/BluetoothDebugging/B13page/B13page.wxss +++ b/pages/basics/BluetoothDebugging/B13page/B13page.wxss @@ -286,6 +286,9 @@ .inline-input { width: 110rpx; height: 44rpx; padding: 0 8rpx; border: 1rpx solid #e5e9f2; border-radius: 8rpx; font-size: 24rpx; box-sizing: border-box; } .inline-picker .picker-text { width: 90rpx; height: 44rpx; padding: 0 8rpx; border-radius: 8rpx; font-size: 22rpx; } +/* 专用:设置蓝牙名后四位输入,宽度减半显示 */ +.ble-suffix { width: 30%; box-sizing: border-box; } + /* If screen too narrow, allow wrapping but keep label + control groups together */ @media (max-width: 360px) { .form-inline { flex-wrap: wrap; } diff --git a/utils/w13Packet.js b/utils/w13Packet.js index 3a5d8b7..f3854b5 100644 --- a/utils/w13Packet.js +++ b/utils/w13Packet.js @@ -15,6 +15,8 @@ export const COMMANDS = { // 读取门磁/卫浴事件触发/释放参数 READ_DOOR_BATH_EVENT: 0x17, RADAR_STATUS: 0x11, + // 设置蓝牙名称(仅后四位) + SET_BLE_NAME: 0x20, TEST_KEYS: 0x13, }; @@ -238,6 +240,37 @@ export function buildReadDoorBathEvent(options = {}) { return buildCommand(COMMANDS.READ_DOOR_BATH_EVENT, [0x01], options); } +/** + * 构造:设置蓝牙名(Frame_Type=0x20) + * 协议要求:P0 = 有效长度(1..4) + * P1~P4: 后四位字符(ASCII 字节),不足用 0x00 填充;若输入长度>4,取最后 4 个字符 + * @param {string|number[]|Uint8Array} name 后四位字符串或字节数组 + * @param {{frame?:number, framNum?:number, crcType?:('CCITT'|'MODBUS'), head?:number[]}} [options] + */ +export function buildSetBluetoothName(name, options = {}) { + // 接受字符串或字节数组 + let arr = []; + if (typeof name === 'string') { + const s = name.slice(-4); + for (let i = 0; i < s.length; i++) arr.push(s.charCodeAt(i) & 0xFF); + } else if (name instanceof Uint8Array) { + for (let i = 0; i < Math.min(4, name.length); i++) arr.push(name[i] & 0xFF); + } else if (Array.isArray(name)) { + for (let i = 0; i < Math.min(4, name.length); i++) arr.push(name[i] & 0xFF); + } else if (name == null) { + arr = []; + } else { + // 尝试转换 + const u = ensureUint8Array(name); + for (let i = 0; i < Math.min(4, u.length); i++) arr.push(u[i] & 0xFF); + } + const len = Math.min(4, Math.max(0, arr.length)); + // P1~P4 必须有 4 字节位置,短则补 0 + const p = [len]; + for (let i = 0; i < 4; i++) p.push(i < arr.length ? (arr[i] & 0xFF) : 0x00); + return buildCommand(COMMANDS.SET_BLE_NAME, p, options); +} + /** * 验证十六进制字符串包并计算/写入 CRC(默认 MODBUS),返回完整包与CRC值 * @param {string} hexStr 如 'CC C0 0C 00 00 00 01 00 02 00 0C 08' 或连续hex