保姆级教程:用微信小程序模拟蓝牙钥匙,5分钟搞定充电桩自动充电(附完整代码)

张开发
2026/4/25 1:13:10 15 分钟阅读

分享文章

保姆级教程:用微信小程序模拟蓝牙钥匙,5分钟搞定充电桩自动充电(附完整代码)
微信小程序蓝牙钥匙开发实战5分钟实现充电桩自动触发想象一下这样的场景当你把电动车停到充电桩前只需打开手机上的小程序充电桩就会自动识别并开始充电——整个过程无需扫码、无需手动操作就像用蓝牙钥匙解锁车门一样自然。这种丝滑的体验背后正是微信小程序蓝牙外围设备技术的巧妙应用。本文将带你从零实现一个蓝牙钥匙小程序核心功能是模拟硬件钥匙向充电桩广播特定指令如047F0E770416。不同于常见的蓝牙中心设备连接模式这里我们要让手机成为广播端Peripheral模式这对智能家居、共享设备等物联网场景具有重要实践价值。下面将从环境配置到代码实现完整走通这个流程。1. 开发环境与基础配置1.1 项目初始化与权限设置首先创建一个新的微信小程序项目在app.json中声明蓝牙权限{ permission: { scope.bluetooth: { desc: 用于模拟蓝牙钥匙功能 } } }然后在项目设置中勾选蓝牙能力并注意以下基础配置要点使用wx.openBluetoothAdapter时必须指定mode: peripheraliOS系统对广播数据有特殊限制需通过serviceUuids传递指令安卓6.0需要动态申请位置权限蓝牙扫描需要提示真机调试时务必在手机设置中开启定位服务即使小程序不直接使用定位1.2 蓝牙外围设备模式解析与传统中心设备模式不同外围设备模式的工作流程如下初始化蓝牙适配器Peripheral模式创建BLE外围服务器添加服务(Service)和特征值(Characteristic)开始广播指定数据处理中心设备的连接请求可选关键区别在于数据流向模式类型数据方向典型应用场景中心设备手机主动扫描连接连接智能手环读取数据外围设备手机被动广播数据模拟门禁卡、支付终端2. 核心代码实现2.1 蓝牙服务初始化创建bleManager.js作为蓝牙模块核心文件let bluetoothServer null function initBluetooth() { return new Promise((resolve, reject) { wx.openBluetoothAdapter({ mode: peripheral, success(res) { createServer().then(resolve).catch(reject) }, fail(err) { console.error(蓝牙初始化失败, err) reject(err) } }) }) } function createServer() { return new Promise((resolve, reject) { wx.createBLEPeripheralServer({ success(res) { bluetoothServer res.server resolve(bluetoothServer) }, fail(err) { reject(err) } }) }) }2.2 服务与特征值配置充电桩通常需要特定的服务UUID来识别合法设备。这里我们添加一个自定义服务function addService(server) { return new Promise((resolve, reject) { server.addService({ service: { uuid: 0000FFF0-0000-1000-8000-00205F9B34EB, characteristics: [ { uuid: 0000FFF1-0000-1000-8000-00205F9B34EB, properties: { write: true }, permission: { writeable: true } } ] }, success: resolve, fail: reject }) }) }2.3 数据广播关键实现这是最核心的部分——如何让充电桩识别我们的钥匙function startAdvertising(server) { const chargeCommand 047F0E770416 // 充电桩识别指令 return new Promise((resolve, reject) { server.startAdvertising({ advertiseRequest: { connectable: true, deviceName: EV_Key, serviceUuids: [FFFF, ...parseCommand(chargeCommand)], manufacturerData: parseCommand(chargeCommand) }, success: resolve, fail: reject }) }) } // 将指令字符串转换为UUID兼容格式 function parseCommand(cmd) { const chunks [] for (let i 0; i cmd.length; i 4) { chunks.push(cmd.substr(i, 4)) } return chunks }注意iOS系统会忽略manufacturerData字段所以必须同时通过serviceUuids传递指令3. 平台兼容性处理方案3.1 iOS特殊处理由于iOS系统的限制我们需要做额外兼容处理function getAdvertisingParams(command) { const baseParams { connectable: true, deviceName: EV_Key } // 安卓可以使用manufacturerData if (wx.getSystemInfoSync().platform android) { return { ...baseParams, manufacturerData: parseCommand(command) } } // iOS只能通过serviceUuids return { ...baseParams, serviceUuids: [FFFF, ...parseCommand(command)] } }3.2 错误处理与重试机制蓝牙操作容易受环境影响需要完善的错误处理async function safeStartAdvertising(retry 3) { try { const server await initBluetooth() await addService(server) await startAdvertising(server) return true } catch (err) { if (retry 0) { await delay(1000) return safeStartAdvertising(retry - 1) } throw err } } function delay(ms) { return new Promise(resolve setTimeout(resolve, ms)) }4. 完整业务流程实现4.1 页面逻辑整合在页面中调用我们封装的蓝牙模块Page({ data: { isAdvertising: false }, async startCharging() { wx.showLoading({ title: 启动中... }) try { await safeStartAdvertising() this.setData({ isAdvertising: true }) wx.showToast({ title: 充电已启动, icon: success }) } catch (err) { wx.showToast({ title: 启动失败, icon: error }) } finally { wx.hideLoading() } }, stopCharging() { if (bluetoothServer) { bluetoothServer.stopAdvertising() this.setData({ isAdvertising: false }) } } })4.2 前端界面示例配套的WXML界面简单实用view classcontainer image src/images/charging_pile.png modeaspectFit/ text classstatus{{isAdvertising ? 充电中... : 等待连接}}/text button typeprimary bindtap{{isAdvertising ? stopCharging : startCharging}} {{isAdvertising ? 停止充电 : 开始充电}} /button /view5. 进阶优化与实践技巧5.1 广播数据优化实际项目中广播数据需要与硬件设备严格匹配。常见优化点包括数据分片策略长指令分段广播校验机制添加CRC校验位动态密钥每次广播变化的数据部分示例改进后的广播数据生成function generateAdvertisement(command) { const crc calculateCRC(command) return ${command}${crc.toString(16).padStart(4, 0)} } function calculateCRC(data) { // 简化的CRC16计算示例 let crc 0xFFFF for (let i 0; i data.length; i) { crc ^ data.charCodeAt(i) for (let j 0; j 8; j) { if (crc 0x0001) { crc (crc 1) ^ 0xA001 } else { crc crc 1 } } } return crc }5.2 性能与稳定性提升长时间广播时需要注意增加心跳机制防止断开处理蓝牙适配器状态变化低功耗优化策略wx.onBluetoothAdapterStateChange((res) { if (!res.available) { // 蓝牙不可用时自动重连 setTimeout(initBluetooth, 2000) } })在实际电动车充电场景中我们还应该考虑用户操作习惯——比如离开自动停止充电的功能实现。这可以通过结合小程序生命周期和蓝牙状态管理来实现// app.js App({ onHide() { // 应用进入后台时停止广播 if (bluetoothServer) { bluetoothServer.stopAdvertising() } } })

更多文章