微信小软件(uniapp)实现连接蓝牙

微信小程序(uniapp)实现连接蓝牙

一、蓝牙连接的基本流程

在uni-app中实现蓝牙连接,主要包含以下步骤:

  1. 初始化蓝牙模块:开启蓝牙适配器,为后续操作做准备
  2. 搜索蓝牙设备:扫描周围可用的蓝牙设备
  3. 连接目标设备:建立与指定蓝牙设备的连接
  4. 获取服务与特征值:获取设备提供的服务和特征值信息
  5. 数据交互:通过特征值进行数据的读写操作

二、具体实现步骤

1. 初始化蓝牙模块

使用uni.openBluetoothAdapter()方法初始化蓝牙适配器。这是所有蓝牙操作的基础,必须在其他API调用前执行。

// 初始化蓝牙适配器
function initBluetooth() {
uni.openBluetoothAdapter({
success: (res) =>
{
console.log('蓝牙适配器初始化成功', res);
// 初始化成功后可以开始搜索设备
searchBluetoothDevices();
},
fail: (err) =>
{
console.error('蓝牙适配器初始化失败', err);
if (err.errCode === 10001) {
uni.showModal({
title: '提示',
content: '请开启手机蓝牙',
showCancel: false
});
}
}
});
}

2. 搜索蓝牙设备

使用uni.startBluetoothDevicesDiscovery()方法开始搜索附近的蓝牙设备。由于搜索操作会消耗系统资源,建议在连接成功后及时停止搜索。

// 搜索蓝牙设备
function searchBluetoothDevices() {
uni.startBluetoothDevicesDiscovery({
allowDuplicatesKey: false, // 不允许重复上报同一设备
success: (res) =>
{
console.log('开始搜索蓝牙设备', res);
uni.showLoading({ title: '正在搜索设备...'
});
// 设置定时器,15秒后超时停止搜索
const timer = setTimeout(() =>
{
stopBluetoothSearch();
uni.showModal({
title: '提示',
content: '搜索设备超时,请检查设备是否开启',
showCancel: false
});
}, 15000);
// 监听找到新设备的事件
uni.onBluetoothDeviceFound((devices) =>
{
handleFoundDevices(devices);
clearTimeout(timer);
// 找到设备后清除超时定时器
});
},
fail: (err) =>
{
console.error('开始搜索设备失败', err);
uni.hideLoading();
}
});
}
// 处理找到的设备
function handleFoundDevices(devices) {
const foundDevices = [];
devices.devices.forEach(device =>
{
if (device.name && device.name.includes('目标设备关键字')) {
foundDevices.push(device);
}
});
if (foundDevices.length >
0) {
stopBluetoothSearch();
showDeviceList(foundDevices);
}
}
// 停止搜索蓝牙设备
function stopBluetoothSearch() {
uni.stopBluetoothDevicesDiscovery({
success: () =>
{
uni.hideLoading();
uni.offBluetoothDeviceFound();
// 停止监听新设备
}
});
}

3. 显示设备列表并连接

将搜索到的设备显示在页面上,用户可以选择要连接的设备。

// 在Vue组件的data中定义
data() {
return {
deviceList: [], // 设备列表
currentDevice: null // 当前选中的设备
}
}
// 显示设备列表
function showDeviceList(devices) {
this.deviceList = devices;
// 可以在这里更新页面显示,或者使用uni-app的列表渲染
}
// 连接选中的设备
function connectDevice(deviceId) {
uni.createBLEConnection({
deviceId: deviceId,
success: (res) =>
{
console.log('连接设备成功', res);
this.currentDevice = deviceId;
// 连接成功后获取设备服务
getDeviceServices(deviceId);
},
fail: (err) =>
{
console.error('连接设备失败', err);
uni.showToast({
title: '连接失败',
icon: 'none'
});
}
});
}

4. 获取设备服务和特征值

连接成功后,需要获取设备提供的服务和特征值,才能进行数据交互。

// 获取设备服务
function getDeviceServices(deviceId) {
uni.getBLEDeviceServices({
deviceId: deviceId,
success: (res) =>
{
console.log('获取服务成功', res.services);
// 通常我们只需要特定的服务,可以根据UUID过滤
const targetService = res.services.find(
service => service.uuid === '目标服务UUID'
);
if (targetService) {
getDeviceCharacteristics(deviceId, targetService.uuid);
}
},
fail: (err) =>
{
console.error('获取服务失败', err);
}
});
}
// 获取服务特征值
function getDeviceCharacteristics(deviceId, serviceId) {
uni.getBLEDeviceCharacteristics({
deviceId: deviceId,
serviceId: serviceId,
success: (res) =>
{
console.log('获取特征值成功', res.characteristics);
// 存储特征值信息,用于后续读写操作
this.characteristics = res.characteristics;
// 启用特征值变化的通知
enableCharacteristicNotification(deviceId, serviceId, res.characteristics);
},
fail: (err) =>
{
console.error('获取特征值失败', err);
}
});
}
// 启用特征值变化的通知
function enableCharacteristicNotification(deviceId, serviceId, characteristics) {
const notifyChar = characteristics.find(
char => char.properties.notify
);
if (notifyChar) {
uni.notifyBLECharacteristicValueChange({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: notifyChar.uuid,
state: true,
success: () =>
{
console.log('启用通知成功');
// 监听特征值变化
uni.onBLECharacteristicValueChange(res =>
{
console.log('特征值变化', res);
// 处理接收到的数据
const data = ab2hex(res.value);
console.log('收到数据:', data);
});
},
fail: (err) =>
{
console.error('启用通知失败', err);
}
});
}
}

5. 数据读写操作

通过特征值进行数据的写入和读取操作。

// 向设备写入数据
function writeDataToDevice(deviceId, serviceId, characteristicId, data) {
// 将数据转换为ArrayBuffer
const buffer = string2buffer(data);
uni.writeBLECharacteristicValue({
deviceId: deviceId,
serviceId: serviceId,
characteristicId: characteristicId,
value: buffer,
success: () =>
{
console.log('写入数据成功');
},
fail: (err) =>
{
console.error('写入数据失败', err);
}
});
}
// 数据转换工具函数
function string2buffer(str) {
let buf = new ArrayBuffer(str.length);
let bufView = new Uint8Array(buf);
for (let i = 0; i < str.length; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
function ab2hex(buffer) {
const hexArr = Array.prototype.map.call(
new Uint8Array(buffer),
function(bit) {
return ('00' + bit.toString(16)).slice(-2);
}
);
return hexArr.join('');
}

三、完整实现示例

1. 页面结构 (index.vue)

<template>
    <view class="container">
  <button @click="initBluetooth">初始化蓝牙</button>
  <button @click="searchBluetoothDevices">搜索设备</button>
      <view v-if="deviceList.length > 0">
        <view v-for="(device, index) in deviceList" :key="index" @click="connectDevice(device.deviceId)">
        {{ device.name || device.localName || '未知设备' }} - {{ device.deviceId }}
      </view>
    </view>
  <button @click="sendData" :disabled="!currentDevice">发送测试数据</button>
  </view>
</template>

2. 脚本部分 (index.vue)

<script>
  export default {
  data() {
  return {
  deviceList: [],
  currentDevice: null,
  currentServiceId: '',
  currentCharacteristicId: ''
  }
  },
  methods: {
  // 初始化蓝牙适配器
  initBluetooth() {
  uni.openBluetoothAdapter({
  success: (res) =>
  {
  console.log('蓝牙适配器初始化成功', res);
  uni.showToast({ title: '蓝牙初始化成功', icon: 'success'
  });
  },
  fail: (err) =>
  {
  console.error('蓝牙适配器初始化失败', err);
  uni.showToast({ title: '蓝牙初始化失败', icon: 'none'
  });
  }
  });
  },
  // 搜索蓝牙设备
  searchBluetoothDevices() {
  uni.startBluetoothDevicesDiscovery({
  allowDuplicatesKey: false,
  success: (res) =>
  {
  console.log('开始搜索蓝牙设备', res);
  uni.showLoading({ title: '正在搜索设备...'
  });
  // 监听找到新设备的事件
  this.deviceFoundListener = uni.onBluetoothDeviceFound((devices) =>
  {
  this.handleFoundDevices(devices);
  });
  // 15秒后超时停止搜索
  setTimeout(() =>
  {
  this.stopBluetoothSearch();
  if (this.deviceList.length === 0) {
  uni.showModal({
  title: '提示',
  content: '未找到蓝牙设备',
  showCancel: false
  });
  }
  }, 15000);
  },
  fail: (err) =>
  {
  console.error('开始搜索设备失败', err);
  uni.hideLoading();
  }
  });
  },
  // 处理找到的设备
  handleFoundDevices(devices) {
  const foundDevices = [];
  devices.devices.forEach(device =>
  {
  if (device.name && device.name.includes('目标设备关键字')) {
  foundDevices.push(device);
  }
  });
  if (foundDevices.length >
  0) {
  // 去重处理
  const uniqueDevices = [...new Map(
  foundDevices.map(item =>
  [item.deviceId, item])
  ).values()];
  this.deviceList = [...this.deviceList, ...uniqueDevices].filter(
  (item, index, self) =>
  index === self.findIndex(t => t.deviceId === item.deviceId)
  );
  }
  },
  // 停止搜索蓝牙设备
  stopBluetoothSearch() {
  uni.stopBluetoothDevicesDiscovery({
  success: () =>
  {
  uni.hideLoading();
  if (this.deviceFoundListener) {
  uni.offBluetoothDeviceFound(this.deviceFoundListener);
  }
  }
  });
  },
  // 连接设备
  connectDevice(deviceId) {
  uni.createBLEConnection({
  deviceId: deviceId,
  success: (res) =>
  {
  console.log('连接设备成功', res);
  this.currentDevice = deviceId;
  uni.showToast({ title: '连接成功', icon: 'success'
  });
  // 连接成功后获取设备服务
  this.getDeviceServices(deviceId);
  },
  fail: (err) =>
  {
  console.error('连接设备失败', err);
  uni.showToast({ title: '连接失败', icon: 'none'
  });
  }
  });
  },
  // 获取设备服务
  getDeviceServices(deviceId) {
  uni.getBLEDeviceServices({
  deviceId: deviceId,
  success: (res) =>
  {
  console.log('获取服务成功', res.services);
  const targetService = res.services.find(
  service => service.uuid === '目标服务UUID'
  );
  if (targetService) {
  this.currentServiceId = targetService.uuid;
  this.getDeviceCharacteristics(deviceId, targetService.uuid);
  }
  },
  fail: (err) =>
  {
  console.error('获取服务失败', err);
  }
  });
  },
  // 获取服务特征值
  getDeviceCharacteristics(deviceId, serviceId) {
  uni.getBLEDeviceCharacteristics({
  deviceId: deviceId,
  serviceId: serviceId,
  success: (res) =>
  {
  console.log('获取特征值成功', res.characteristics);
  const notifyChar = res.characteristics.find(
  char => char.properties.notify
  );
  const writeChar = res.characteristics.find(
  char => char.properties.write
  );
  if (notifyChar) {
  this.enableCharacteristicNotification(deviceId, serviceId, notifyChar.uuid);
  }
  if (writeChar) {
  this.currentCharacteristicId = writeChar.uuid;
  }
  },
  fail: (err) =>
  {
  console.error('获取特征值失败', err);
  }
  });
  },
  // 启用特征值变化的通知
  enableCharacteristicNotification(deviceId, serviceId, characteristicId) {
  uni.notifyBLECharacteristicValueChange({
  deviceId: deviceId,
  serviceId: serviceId,
  characteristicId: characteristicId,
  state: true,
  success: () =>
  {
  console.log('启用通知成功');
  this.characteristicChangeListener = uni.onBLECharacteristicValueChange(res =>
  {
  console.log('特征值变化', res);
  const data = this.ab2hex(res.value);
  console.log('收到数据:', data);
  });
  },
  fail: (err) =>
  {
  console.error('启用通知失败', err);
  }
  });
  },
  // 发送测试数据
  sendData() {
  if (!this.currentDevice || !this.currentServiceId || !this.currentCharacteristicId) {
  uni.showToast({ title: '请先连接设备', icon: 'none'
  });
  return;
  }
  const testData = 'Hello Bluetooth!';
  const buffer = this.string2buffer(testData);
  uni.writeBLECharacteristicValue({
  deviceId: this.currentDevice,
  serviceId: this.currentServiceId,
  characteristicId: this.currentCharacteristicId,
  value: buffer,
  success: () =>
  {
  console.log('写入数据成功');
  uni.showToast({ title: '发送成功', icon: 'success'
  });
  },
  fail: (err) =>
  {
  console.error('写入数据失败', err);
  uni.showToast({ title: '发送失败', icon: 'none'
  });
  }
  });
  },
  // 数据转换工具函数
  string2buffer(str) {
  let buf = new ArrayBuffer(str.length);
  let bufView = new Uint8Array(buf);
  for (let i = 0; i < str.length; i++) {
  bufView[i] = str.charCodeAt(i);
  }
  return buf;
  },
  ab2hex(buffer) {
  const hexArr = Array.prototype.map.call(
  new Uint8Array(buffer),
  function(bit) {
  return ('00' + bit.toString(16)).slice(-2);
  }
  );
  return hexArr.join('');
  }
  },
  beforeDestroy() {
  // 组件销毁前清理蓝牙资源
  if (this.currentDevice) {
  uni.closeBLEConnection({
  deviceId: this.currentDevice
  });
  }
  if (this.deviceFoundListener) {
  uni.offBluetoothDeviceFound(this.deviceFoundListener);
  }
  if (this.characteristicChangeListener) {
  uni.offBLECharacteristicValueChange(this.characteristicChangeListener);
  }
  }
  }
  <
  /script>

3. 样式部分 (index.vue)

<style>
  .container {
  padding: 20px;
  }
  button {
  margin: 10px 0;
  background-color: #07C160;
  color: white;
  border: none;
  padding: 10px 15px;
  border-radius: 5px;
  }
  view {
  margin: 10px 0;
  padding: 10px;
  border: 1px solid #eee;
  border-radius: 5px;
  }
</style>

四、注意事项

  1. 权限配置:在微信小程序中,需要在app.json中声明蓝牙权限:

    {
    "permission": {
    "scope.bluetooth": {
    "desc": "需要蓝牙权限以连接设备"
    }
    }
    }
  2. iOS与Android差异

    • iOS设备不支持获取MAC地址,如需MAC地址,需要设备厂商将MAC地址放在广播数据中[9]
    • Android设备需要定位权限才能搜索蓝牙设备[9]
  3. 连接稳定性

    • 蓝牙连接可能随时断开,建议监听onBLEConnectionStateChange事件进行重连处理[10]
    • 避免多次调用createBLEConnection创建重复连接[10]
  4. 数据传输限制

    • 单次写入数据建议不超过20字节[4]
    • 蓝牙4.0设备对单次传输数据大小有限制,超过可能导致写入错误[4]
  5. 资源释放

    • 页面销毁或不再需要蓝牙功能时,应调用closeBLEConnectioncloseBluetoothAdapter释放资源[10]

五、总结

通过本文的介绍,我们了解了在uni-app框架下实现微信小程序蓝牙连接的全过程。从初始化蓝牙模块、搜索设备、连接设备到数据交互,每个步骤都有详细的代码示例和说明。

主要实现步骤包括:

  1. 使用uni.openBluetoothAdapter()初始化蓝牙适配器
  2. 使用uni.startBluetoothDevicesDiscovery()搜索设备
  3. 使用uni.createBLEConnection()连接设备
  4. 获取设备服务和特征值
  5. 通过特征值进行数据读写操作

在实际开发中,还需要注意权限配置、平台差异、连接稳定性和资源释放等问题。希望本文的内容能帮助开发者快速掌握uni-app蓝牙开发技术,开发出功能完善的蓝牙应用。

posted @ 2025-09-07 17:22  yjbjingcha  阅读(140)  评论(0)    收藏  举报