在物联网快速发展的今天,微信小程序与蓝牙设备的交互已成为智能硬件开发的重要场景。本文将基于官方蓝牙 API,详细讲解小程序连接蓝牙设备的完整流程,涵盖从适配器初始化到数据收发的全链路操作。
一、蓝牙开发基础认知
蓝牙连接是小程序与硬件设备通信的重要方式,主要应用于:
- 智能家居设备控制(智能门锁、灯光)
- 穿戴设备数据同步(手环、健康监测设备)
- 物联网传感器数据采集
核心优势:
- 无需云开发即可实现设备直连
- 低功耗设计适合移动设备
- 微信生态内用户触达便捷
二、蓝牙连接全流程实现
1. 初始化蓝牙适配器
首先需要获取设备的蓝牙权限并初始化适配器:
wx.openBluetoothAdapter({
  // 打开蓝牙适配器成功回调
  success(res) {
    console.log(res)
    console.log("初始化成功!")
  },
  // 打开蓝牙适配器失败回调
  fail(res) {
    console.log(res)
    console.log("初始化失败!")
  },
  // 无论成功失败都会执行的回调
  complete(res) {
    console.log(res)
    console.log("初始化完成!")
  },
})
关键说明:
- 首次使用时会弹出权限申请弹窗
- 安卓设备需确保系统蓝牙已开启
- 失败时可能需要引导用户检查蓝牙状态
2. 获取蓝牙适配器状态
初始化后需确认适配器当前状态:
wx.getBluetoothAdapterState({
  // 成功获取状态回调
  success(res) {
    console.log(res)
    console.log("得到蓝牙适配器状态成功!")
  },
  // 失败获取状态回调
  fail(res) {
    console.log(res)
    console.log("得到蓝牙适配器状态失败!")
  },
  // 状态获取完成回调
  complete(res) {
    console.log(res)
    console.log("得到蓝牙适配器状态完成!")
  },
})
关键字段:
- res.available:蓝牙是否可用
- res.discovering:是否正在搜索设备
- res.platform:设备平台(iOS/Android)
3. 搜索附近蓝牙设备
搜索设备是连接的前提,需注意资源消耗问题:
javascript
wx.startBluetoothDevicesDiscovery({
  // services参数可选,指定UUID可过滤设备
  // services: ['FEE7'], 
  success(res) {
    console.log(res, '搜索成功')
  },
  fail(res) {
    console.log(res, '搜索失败')
  },
  complete(res) {
    console.log(res, '搜索操作完成')
  }
})
重要注意事项:
- 安卓 6.0 + 设备需开启定位权限才能搜索
- 搜索会消耗较多电量,找到设备后需及时停止
- 8.0.16 以上版本安卓设备无定位权限会直接报错
4. 获取搜索到的设备列表
实时获取搜索到的设备信息:
javascript
// 工具函数:ArrayBuffer转16进制字符串
function ab2hex(buffer) {
  var hexArr = Array.prototype.map.call(
    new Uint8Array(buffer),
    function(bit) {
      return ('00' + bit.toString(16)).slice(-2)
    }
  )
  return hexArr.join('');
}
// 获取蓝牙设备列表
getBluetoothDevices: function() {
  wx.getBluetoothDevices({
    success: function(res) {
      console.log(res) // 打印所有设备信息
      if (res.devices[0]) {
        console.log(ab2hex(res.devices[0].advertisData)) // 打印第一个设备的广播数据
      }
    }
  })
}
设备数据解析:
- name:设备名称(可能为空)
- deviceId:设备唯一标识
- RSSI:信号强度(-120~0dBm,值越大信号越好)
- advertisData:广播数据(可解析设备信息)
5. 连接指定蓝牙设备
通过 deviceId 建立与目标设备的连接:
javascript
wx.createBLEConnection({
  deviceId: deviceId, // 从搜索结果中获取的设备ID
  success(res) {
    console.log(res, '连接成功')
    // 连接成功后立即停止搜索以节省资源
    wx.stopBluetoothDevicesDiscovery({
      success(res) {
        console.log(res)
      }
    })
  },
  fail(res) {
    console.log(res, '连接失败')
  },
  complete(res) {
    console.log(res, '连接操作完成')
  }
})
连接管理要点:
- 连接成功后必须调用 stopBluetoothDevicesDiscovery
- 连接失败可能原因:设备距离过远、设备未开启可连接模式
- 每个设备同时只能有一个连接
6. 停止设备搜索
当找到目标设备后务必停止搜索:
wx.stopBluetoothDevicesDiscovery({
  success(res) {
    console.log(res)
  },
  fail(res) {
    console.log(res, '停止搜索失败')
  },
  complete(res) {
    console.log(res, '停止搜索完成')
  }
})
7. 获取设备服务列表
连接后需要获取设备支持的服务:
wx.getBLEDeviceServices({
  deviceId, // 已连接的设备ID
  success(res) {
    console.log(res)
    // 从res.services中获取serviceId
  },
  fail(res) {
    console.log(res, '获取服务失败')
  },
  complete(res) {
    console.log(res, '获取服务完成')
  }
})
服务数据结构:
- uuid:服务唯一标识
- isPrimary:是否为主服务(主服务是设备核心功能)
8. 获取服务特征值
每个服务包含多个特征值,这是数据交互的关键:
wx.getBLEDeviceCharacteristics({
  deviceId, // 已连接设备ID
  serviceId, // 从服务列表中获取的serviceId
  success(res) {
    console.log('设备特征值列表:', res.characteristics)
    if (res.characteristics.properties) {
      // 从res.characteristics中获取characteristicId
      return
    }
  }
})
特征值属性解析:
- read:是否支持读取
- write:是否支持写入
- notify:是否支持通知(设备主动推送数据)
- indicate:是否支持指示(比通知更可靠的推送)
9. 订阅特征值变化通知
开启设备主动推送数据的功能:
wx.notifyBLECharacteristicValueChange({
  state: true, // true为启用notify功能
  deviceId,
  serviceId,
  characteristicId, // 从特征值列表中获取的characteristicId
  success(res) {
    console.log('订阅成功', res.errMsg)
  }
})
订阅注意事项:
- 必须确保特征值支持 notify/indicate
- 订阅后设备主动更新数据才会触发回调
- 安卓部分机型订阅后立即写入可能报错(10008 错误)
10. 监听特征值变化事件
接收设备主动推送的数据:
// 工具函数:ArrayBuffer转16进制字符串
function ab2hex(buffer) {
  let hexArr = Array.prototype.map.call(
    new Uint8Array(buffer),
    function(bit) {
      return ('00' + bit.toString(16)).slice(-2)
    }
  )
  return hexArr.join('');
}
// 注册监听事件
wx.onBLECharacteristicValueChange(function(res) {
  console.log(`特征值 ${res.characteristicId} 已更新,当前值:${res.value}`)
  console.log(ab2hex(res.value)) // 打印16进制数据
})
数据处理要点:
- 数据以 ArrayBuffer 形式传输
- 需根据设备协议解析数据(如温度、湿度等)
- 建议在页面卸载时取消监听(wx.offBLECharacteristicValueChange)
11. 向设备写入数据
与设备交互的最后一步,发送控制指令:
senddata: function() {
  // 准备写入数据(示例:发送0x00)
  let buffer = new ArrayBuffer(1)
  let dataView = new DataView(buffer)
  dataView.setUint8(0, 0) // 设置第一个字节为0
  
  wx.writeBLECharacteristicValue({
    deviceId,
    serviceId,
    characteristicId,
    value: buffer, // 必须为ArrayBuffer类型
    success(res) {
      console.log('写入成功', res.errMsg)
    }
  })
}
写入操作限制:
- 单次写入建议不超过 20 字节
- 并行多次写入可能失败
- iOS 设备写入过长数据可能无回调
- 安卓订阅后立即写入可能报错
三、实战开发最佳实践
- 
	设备状态管理: - 维护全局设备连接状态变量
- 页面卸载时断开连接(onUnload中调用wx.closeBLEConnection)
 
- 
	错误处理优化: - 封装蓝牙操作函数并统一处理错误
- 对常见错误(如 10008)提供用户友好提示
 
- 
	性能优化: - 严格控制搜索时间(建议不超过 10 秒)
- 批量处理特征值读写操作
 
- 
	跨平台适配: - 针对 iOS/Android 做差异化处理
- 安卓设备优先检查定位权限
 
四、常见问题与解决方案
- 
	安卓设备无法搜索到设备: - 检查是否开启定位权限(安卓 6.0 + 强制要求)
- 确认蓝牙适配器状态是否可用
 
- 
	连接成功但无法通信: - 检查 serviceId/characteristicId 是否正确
- 确认特征值是否支持对应操作(read/write/notify)
 
- 
	数据传输异常: - 确保数据格式为 ArrayBuffer
- 按设备协议规范解析数据(如大端 / 小端模式)
 
通过以上步骤,我们可以完成微信小程序与蓝牙设备的全流程交互。实际开发中需结合具体硬件协议调整数据处理逻辑,并做好异常情况的处理,以提供稳定的用户体验。
下面是写的一些简易的代码,有错误的地方希望大家指出
WXML
<view class="page-body">
    <view class="btn-area" id="buttonContainer">
        <wux-toptips class="wux-light--bg" id="wux-toptips" />
        <button type="primary" bindtap='openbluetooth'>打开蓝牙适配器</button>
        <button type="primary" bindtap='getbluetoothstatus'>获取蓝牙适配器状态</button>
        <button type="primary" bindtap='startsearchbluetooth'>开始搜索附近的蓝牙设备</button>
        <button type="primary" bindtap='getbluetooths'>获取搜到的蓝牙设备</button>
        <view wx:for="{{getbluetoothlist}}" wx:key="index" class="device-card">
            <view class="device-row">
                <text class="device-label">设备名:</text>
                <text class="device-value">{{item.name || '未知设备'}}</text>
            </view>
            <view class="device-row">
                <text class="device-label">设备ID:</text>
                <text class="device-value">{{item.deviceId}}</text>
            </view>
            <view class="device-row">
                <text class="device-label">信号强度:</text>
                <text class="device-value">{{item.RSSI}}</text>
            </view>
            <view class="device-row">
                <text class="device-label">支持连接:</text>
                <text class="device-value">{{item.connectable ? '是' : '否'}}</text>
            </view>
            <button class="mini-btn" type="primary" size="mini" bindtap='contentdev' data-devid="{{item.deviceId}}">连接</button>
        </view>
        <button type="primary" bindtap='getservice'>获取已连接蓝牙的服务</button>
        <button type="primary" bindtap='getserviceeigenvalues'>获取蓝牙设备某服务的所有特征值</button>
        <view wx:for="{{genvalueslist}}" wx:key="index" class="device-card">
            <view class="device-row">
                <text class="device-label">特征值:</text>
                <text class="device-value">{{item.uuid}}</text>
            </view>
            <view class="device-row">
                <text class="device-label">是否可读:</text>
                <text class="device-value">{{item.properties.read?'是':'否'}}</text>
            </view>
            <view class="device-row">
                <text class="device-label">是否可写:</text>
                <text class="device-value">{{item.properties.write?'是':'否'}}</text>
            </view>
            <view class="device-row">
                <text class="device-label">是否支持notify操作:</text>
                <text class="device-value">{{item.properties.notify?'是':'否'}}</text>
            </view>
            <view class="device-row">
                <text class="device-label">是否支持indicate操作:</text>
                <text class="device-value">{{item.properties.indicate?'是':'否'}}</text>
            </view>
            <view class="device-row">
                <text class="device-label">是否支持无回复写操作:</text>
                <text class="device-value">{{item.properties.writeNoResponse?'是':'否'}}</text>
            </view>
            <view class="device-row">
                <text class="device-label">是否支持有回复写操作:</text>
                <text class="device-value">{{item.properties.writeDefault?'是':'否'}}</text>
            </view>
        </view>
        <button type="primary" bindtap='startmonitornotify'>启用特征值变化时的 notify 功能</button>
        <button type="primary" bindtao='monitoreventchanges'>监听低功耗蓝牙设备的特征值变化事件</button>
        <button type="primary" bindtap='writecontent'>向低功耗蓝牙设备特征值中写入二进制数据</button>
    </view>
</view>
js
Page({
    /**
     * 页面的初始数据
     */
    data: {
        getbluetoothlist: [], //获取到的蓝牙设备列表,
        genvalueslist: [], //设备特征值列表
        deviceId: '', //蓝牙设备ID
        serviceId: '', //蓝牙设备服务ID
        genvalueId: '', //特征值ID
    },
    // 打开蓝牙适配器
    openbluetooth() {
        const that = this;
        console.log(1111)
        wx.showLoading({
            title: '蓝牙初始化...',
        })
        wx.openBluetoothAdapter({
            //打开蓝牙适配器成功
            success(res) {
                wx.hideLoading()
                wx.showToast({
                    title: '初始化成功',
                    icon: 'success',
                    duration: 2000
                })
            },
            //打开蓝牙适配器失败
            fail(res) {
                console.log(res)
                console.log("初始化失败!")
                wx.hideLoading()
                wx.showToast({
                    title: '初始化失败',
                    icon: 'error',
                    duration: 2000
                })
                const toptips = that.selectComponent('#wux-toptips')
                toptips && toptips.warn({
                    icon: 'cancel',
                    hidden: false,
                    text: '请检查蓝牙是否开启',
                    duration: 3000,
                })
            },
        })
    },
    // 获取蓝牙适配器状态
    getbluetoothstatus() {
        wx.showLoading({
            title: '获取蓝牙适配器状态...',
        })
        wx.getBluetoothAdapterState({
            success(res) {
                console.log("获取蓝牙适配器状态成功!")
                wx.hideLoading()
                wx.showToast({
                    title: '获取适配器状态成功',
                    icon: 'success',
                    duration: 2000
                })
            },
            fail(res) {
                console.log("得到蓝牙适配器状态失败!")
                wx.hideLoading()
                wx.showToast({
                    title: '获取状态失败',
                    icon: 'error',
                    duration: 2000
                })
            }
        })
    },
    //开始搜索蓝牙
    startsearchbluetooth() {
        wx.showLoading({
            title: '搜索蓝牙中...',
        })
        wx.startBluetoothDevicesDiscovery({
            // services: ['FEE7'],  services额可以不填,则搜索的是所有的蓝颜设备,如果填写则搜索广播包有对应 UUID 的主服务的蓝牙设备
            success(res) {
                console.log(res, '成功')
                wx.hideLoading()
            },
            fail(res) {
                console.log(res, '失败')
            }
        })
    },
    //获取搜索到的蓝牙
    getbluetooths() {
        const that = this;
        // ArrayBuffer转16进制字符串示例
        function ab2hex(buffer) {
            var hexArr = Array.prototype.map.call(
                new Uint8Array(buffer),
                function (bit) {
                    return ('00' + bit.toString(16)).slice(-2)
                }
            )
            return hexArr.join('');
        }
      
        wx.getBluetoothDevices({
            success: function (res) {
                console.log(res) //打印蓝牙设备信息日志
                that.setData({
                    getbluetoothlist: [...res.devices]
                })
                if (that.data.getbluetoothlist) {
                    console.log(22222, that.data.getbluetoothlist)
                }
            }
        })
    },
    //连接蓝牙设备
    contentdev(event) {
        const that = this
        that.setData({
            deviceId: event.currentTarget.dataset.devid
        })
        wx.createBLEConnection({
            deviceId: that.data.deviceId, //(deviceId)
            success(res) {
                console.log(res, '连接成功')
                wx.showToast({
                    title: '连接成功',
                    icon: 'success',
                    duration: 2000
                })
                //连接成功后最好要释放资源,不然会消耗手机资源
                wx.stopBluetoothDevicesDiscovery({
                    success(res) {
                        console.log(res)
                    }
                })
            },
            fail(res) {
                console.log(res, '失败')
            }
        })
    },
    //获取蓝牙设备的服务
    getservice() {
        const that = this
        wx.getBLEDeviceServices({
            deviceId: that.data.deviceId, //deviceId 需要已经通过createBLEConnection与对应设备建立连接
            success(res) {
                console.log(res)
                //res.services.uuid服务id的获取
                res.services.forEach((item) => {
                    if (item.isPrimary) {
                        that.setData({
                            serviceId: item.uuid
                        })
                        return
                    }
                })
            },
            fail(res) {
                console.log(res, '失败')
            }
        })
    },
    //获取蓝牙设备某服务所有特征值
    getserviceeigenvalues() {
        const that = this
        wx.getBLEDeviceCharacteristics({
            deviceId: that.data.deviceId,
            serviceId: that.data.serviceId,
            success(res) {
                console.log('device getBLEDeviceCharacteristics:', res.characteristics)
                that.setData({
                    genvalueslist: [...res.characteristics]
                })
                res.characteristics.forEach((item) => {
                    if (item.properties.notify) {
                        that.setData({
                            genvalueId: item.uuid
                        })
                        return
                    }
                })
            }
        })
    },
    //启用特征值变化的motify功能
    startmonitornotify() {
        const that = this
        wx.notifyBLECharacteristicValueChange({
            state: true, // 启用 notify 功能
            deviceId: that.data.deviceId,
            serviceId: that.data.serviceId,
            characteristicId: that.data.genvalueId,
            success(res) {
                console.log('notifyBLECharacteristicValueChange success', res.errMsg)
            }
        })
    },
    //监听变化事件
    monitoreventchanges() {
        function ab2hex(buffer) {
            let hexArr = Array.prototype.map.call(
                new Uint8Array(buffer),
                function (bit) {
                    return ('00' + bit.toString(16)).slice(-2)
                }
            )
            return hexArr.join('');
        }
        wx.onBLECharacteristicValueChange(function (res) {
            console.log(`characteristic ${res.characteristicId} has changed, now is ${res.value}`)
            console.log(ab2hex(res.value))
        })
    },
    //向蓝牙设备写入二进制数据
    writecontent() {
        const that=this
        // 向蓝牙设备发送一个0x00的16进制数据
        let buffer = new ArrayBuffer(1)
        // let buffer = new ArrayBuffer(写入内容)
        let dataView = new DataView(buffer)
        dataView.setUint8(0, 0)
        // dataView.setUint8(数组下标, 值)
        wx.writeBLECharacteristicValue({
            deviceId:that.data.deviceId,
            serviceId:that.data.serviceId,
            characteristicId:that.data.genvalueId,
            value: buffer,
            success(res) {
                console.log('writeBLECharacteristicValue success', res.errMsg)
            }
        })
    },
    
})
wxss
/* pages/bluetooth/bluetooth.wxss */
button {
    margin-top: 30rpx;
    margin-bottom: 30rpx;
    width: 90% !important;
}
.button-sp-area {
    margin: 0 auto;
    width: 100%;
}
.mini-btn {
    margin-right: 10rpx;
}
.device-card {
    background: #fff;
    border-radius: 12rpx;
    box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
    margin: 24rpx 16rpx;
    padding: 20rpx 24rpx;
    border: 1rpx solid #f0f0f0;
}
.device-row {
    display: flex;
    align-items: center;
    margin-bottom: 12rpx;
}
.device-label {
    color: #888;
    width: 160rpx;
    font-size: 28rpx;
}
.device-value {
    color: #222;
    font-size: 28rpx;
    word-break: break-all;
}
.device-row:last-child {
    margin-bottom: 0;
}
                 
                   
                   
                   
                   
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
              
             
                   1万+
					1万+
					
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
            


 
            