ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

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

2026/8/6 10:31:31 拓冰建站 浏览量
保姆级教程:用微信小程序模拟蓝牙钥匙,5分钟搞定充电桩自动充电(附完整代码)

微信小程序蓝牙钥匙开发实战:5分钟实现充电桩自动触发

想象一下这样的场景:当你把电动车停到充电桩前,只需打开手机上的小程序,充电桩就会自动识别并开始充电——整个过程无需扫码、无需手动操作,就像用蓝牙钥匙解锁车门一样自然。这种丝滑的体验背后,正是微信小程序蓝牙外围设备技术的巧妙应用。

本文将带你从零实现一个"蓝牙钥匙"小程序,核心功能是模拟硬件钥匙,向充电桩广播特定指令(如047F0E770416)。不同于常见的蓝牙中心设备连接模式,这里我们要让手机成为广播端(Peripheral模式),这对智能家居、共享设备等物联网场景具有重要实践价值。下面将从环境配置到代码实现完整走通这个流程。

1. 开发环境与基础配置

1.1 项目初始化与权限设置

首先创建一个新的微信小程序项目,在app.json中声明蓝牙权限:

{ "permission": { "scope.bluetooth": { "desc": "用于模拟蓝牙钥匙功能" } } }

然后在项目设置中勾选"蓝牙"能力,并注意以下基础配置要点:

  • 使用wx.openBluetoothAdapter时必须指定mode: 'peripheral'
  • iOS系统对广播数据有特殊限制,需通过serviceUuids传递指令
  • 安卓6.0+需要动态申请位置权限(蓝牙扫描需要)

提示:真机调试时,务必在手机设置中开启定位服务(即使小程序不直接使用定位)

1.2 蓝牙外围设备模式解析

与传统中心设备模式不同,外围设备模式的工作流程如下:

  1. 初始化蓝牙适配器(Peripheral模式)
  2. 创建BLE外围服务器
  3. 添加服务(Service)和特征值(Characteristic)
  4. 开始广播指定数据
  5. 处理中心设备的连接请求(可选)

关键区别在于数据流向:

模式类型数据方向典型应用场景
中心设备手机主动扫描连接连接智能手环读取数据
外围设备手机被动广播数据模拟门禁卡、支付终端

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 class="container"> <image src="/images/charging_pile.png" mode="aspectFit"/> <text class="status">{{isAdvertising ? '充电中...' : '等待连接'}}</text> <button type="primary" bindtap="{{isAdvertising ? 'stopCharging' : 'startCharging'}}" > {{isAdvertising ? '停止充电' : '开始充电'}} </button> </view>

5. 进阶优化与实践技巧

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() } } })