1. 导言:当桌面应用遇见蓝牙

当我们需要在办公场景中连接智能打印机、在健身应用中同步手环数据,甚至在工业场景中操控BLE设备时,桌面应用的蓝牙通信能力就成为刚需。Electron作为跨平台桌面应用的开发利器,通过集成Web蓝牙API为开发者提供了标准化的解决方案。本文将带您从零开始构建具备完整蓝牙通信能力的Electron应用(技术栈:Electron 28 + Node.js 20 + Web Bluetooth API)。

2. 开发环境搭建

2.1 创建基础项目

mkdir electron-bluetooth-demo && cd electron-bluetooth-demo
npm init -y
npm install electron@28 --save-dev

2.2 基础窗口配置

// main.js
const { app, BrowserWindow } = require('electron')

app.whenReady().then(() => {
  const mainWindow = new BrowserWindow({
    width: 1200,
    height: 800,
    webPreferences: {
      nodeIntegration: true,
      contextIsolation: false
    }
  })

  mainWindow.loadFile('index.html')
})

3. 核心API详解

3.1 设备发现流程

// renderer.js
const scanButton = document.getElementById('scan-btn');

scanButton.addEventListener('click', async () => {
  try {
    const device = await navigator.bluetooth.requestDevice({
      acceptAllDevices: true,
      optionalServices: ['generic_access'] // 关键:需要声明的服务UUID
    });
    
    console.log('发现设备:', device.name);
    device.addEventListener('gattserverdisconnected', onDisconnect);
  } catch (error) {
    console.error('设备选择错误:', error);
  }
});

function onDisconnect(event) {
  console.log('设备断开:', event.target.name);
}

3.2 数据交互实战

async function connectAndReadData(device) {
  const server = await device.gatt.connect();
  const service = await server.getPrimaryService('heart_rate');
  const characteristic = await service.getCharacteristic('heart_rate_measurement');

  // 启用通知功能
  await characteristic.startNotifications();
  
  characteristic.addEventListener('characteristicvaluechanged', event => {
    const value = event.target.value;
    const heartRate = value.getUint8(1);
    updateHeartRateDisplay(heartRate);
  });
}

function updateHeartRateDisplay(rate) {
  const display = document.getElementById('heart-rate');
  display.textContent = `${rate} BPM`;
  display.classList.add('pulse-effect'); // 添加可视化效果
}

4. 进阶功能实现

4.1 多设备管理池

class DeviceManager {
  constructor() {
    this.connectedDevices = new Map();
  }

  async addDevice(device) {
    const server = await device.gatt.connect();
    const deviceObj = {
      server,
      characteristics: new Map()
    };
    this.connectedDevices.set(device.id, deviceObj);
  }

  async writeData(deviceId, serviceUuid, charUuid, data) {
    const device = this.connectedDevices.get(deviceId);
    if (!device) throw new Error('设备未连接');
    
    let characteristic = device.characteristics.get(charUuid);
    if (!characteristic) {
      const service = await device.server.getPrimaryService(serviceUuid);
      characteristic = await service.getCharacteristic(charUuid);
      device.characteristics.set(charUuid, characteristic);
    }
    
    await characteristic.writeValue(data);
  }
}

5. 应用场景解析

  • 医疗健康领域:与心率带、血糖仪等医疗设备的数据同步
  • 物联网中控:同时管理多个智能灯泡、温控器的组网控制
  • 工业巡检:通过蓝牙标签获取设备状态信息
  • 教育实验:物理实验装置的实时数据采集教学系统

6. 技术优缺点分析

优点:

  • 跨平台支持(Windows/macOS/Linux)
  • 基于标准的Web Bluetooth API
  • 无需处理底层驱动
  • 完整的前后端通信支持

挑战:

  • Windows系统需要启用支持标志
  • 蓝牙4.0+设备兼容性要求
  • 大数据量传输的稳定性优化

7. 开发注意事项

  1. 权限陷阱:在package.json中必须包含蓝牙权限声明:
{
  "name": "bluetooth-app",
  "version": "1.0.0",
  "bluetooth": {
    "lowEnergy": true,
    "classic": false
  }
}
  1. 数据缓存策略:实现数据分包处理逻辑
let receiveBuffer = new Uint8Array(0);

function handlePartialData(newData) {
  receiveBuffer = Uint8Array.from([...receiveBuffer, ...newData]);
  
  if (receiveBuffer.length >= 4) { // 假设头部4字节是长度标识
    const dataView = new DataView(receiveBuffer.buffer);
    const packetLength = dataView.getUint32(0, true);
    
    if (receiveBuffer.length >= 4 + packetLength) {
      processCompletePacket(receiveBuffer.subarray(4, 4 + packetLength));
      receiveBuffer = receiveBuffer.subarray(4 + packetLength);
    }
  }
}

8. 总结与展望

通过Electron实现蓝牙通信将Web技术的便捷性与本地设备控制能力完美结合。开发者在享受快速开发红利的同时,也需要注意不同平台的特性差异。随着Web Bluetooth标准的持续演进,未来在蓝牙mesh网络、批量数据传输等方面还有更大的探索空间。