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

发布于:2025-09-07 ⋅ 阅读:(16) ⋅ 点赞:(0)

微信小程序(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蓝牙开发技术,开发出功能完善的蓝牙应用。


网站公告

今日签到

点亮在社区的每一天
去签到