跳到主要内容
浏览器 Web Bluetooth API使用方法 | 极客日志
Python
浏览器 Web Bluetooth API使用方法 浏览器 Web Bluetooth API 完整指南 一、简介 什么是 Web Bluetooth API? Web Bluetooth API 让网页应用可以与蓝牙设备通信。通过这个 API,你可以: 👂 **扫描并连接**蓝牙设备 📤 **发送命令**到设备 📥 **接收数据**从设备返回 ⚙️ **控制设备**的各种操作 适用场景 浏览器支持 | 浏览器 | 支持 | 最低版本 | |…
观心 发布于 2026/4/6 更新于 2026/5/22 23K 浏览浏览器 Web Bluetooth API 完整指南
一、简介
什么是 Web Bluetooth API?
Web Bluetooth API 让网页应用可以与蓝牙设备通信。通过这个 API,你可以:
👂 扫描并连接 蓝牙设备
📤 发送命令 到设备
📥 接收数据 从设备返回
⚙️ 控制设备 的各种操作
适用场景
医疗设备、手环、手表、传感器、遥控器、音箱、灯等 ↓ 所有支持蓝牙的设备都可以通过这个 API 与网页应用通信
浏览器支持
浏览器 支持 最低版本 Chrome/Edge ✅ 56+ / 79+ Firefox ⚠️ 需启用 98+ Safari ❌ -
二、核心概念(5 分钟快速理解)
2.1 蓝牙通信的三层结构
物理设备(血压计、手环等) ↓ GATT 服务器(设备内的数据服务) ├─ Service(服务,定义功能) │ ├─ Characteristic(特征,具体的数据) │ └─ Characteristic └─ Service └─ Characteristic
2.2 最重要的 4 个概念
名称 说明 类比 Device 蓝牙设备 一台手机 Service 功能模块 手机的相机模块 Characteristic 具体数据 相机拍摄的照片 UUID 全局唯一标识 身份证号
2.3 通信方式
发送数据:Web App → 设备 ↓ writeValue () 向设备发送命令 接收数据:设备 → Web App ↓ 两种方式: 1 . startNotifications () 设备主动推送(推荐) 2 . readValue () 手动读取(备用)
三、完整的 API 用法
3.1 第一步:扫描并选择设备
// 让用户选择要连接的设备const device =await navigator ({// 按服务过滤(设备必须提供这些服务才会出现) filters:[{ services:[ ]// 电池服务}],// 可选:列出可能需要的其他服务 optionalServices:[ ]}); console ('选中设备:
.bluetooth
.requestDevice
'battery_service'
'device_information'
.log
', device.name);
requestDevice() 会弹出浏览器对话框,让用户选择
只有提供了指定服务的设备才会显示
返回一个 Device 对象
3.2 第二步:连接到设备 // 连接到 GATT 服务器const gattServer =await device.gatt.connect(); console.log ('✅ 已连接' );// 监听断开连接事件 device.addEventListener('gattserverdisconnected' ,()=>{ console.log ('❌ 设备已断开' );});
gatt.connect() 是异步的,需要等待
连接后会返回 gattServer 对象
设备可能会主动断开,要监听事件
3.3 第三步:获取服务 // 从设备获取指定的服务const service =await gattServer.getPrimaryService('battery_service' )
一个设备可能有多个服务
需要知道你要的服务的 UUID
返回一个 Service 对象
3.4 第四步:获取特征 // 从服务中获取特征const characteristic =await service.getCharacteristic('battery_level' )
一个服务可能有多个特征
特征是真正的数据承载者
返回一个 Characteristic 对象
3.5 第五步:读取数据 // 方式 1 :手动读取一次const value =await characteristic.readValue();// 转换为可读的格式const battery = value.getUint8(0 ); console.log(
readValue() 返回 DataView 对象
需要用 getUint8()、getInt16() 等方法提取数据
每次调用都重新读取最新值
3.6 第六步:启用通知(推荐) // 启用通知,让设备主动推送数据await characteristic.startNotifications(); console.log ('✅ 通知已启用,等待数据...' );// 监听数据变化 characteristic.addEventListener('characteristicvaluechanged' ,event=>{const value = event.target.value;// 提取数据const battery = value.getUint8(0 ); console.log ('电池电量变化:' , battery,'%' );});
startNotifications() 让设备主动推送数据
每次数据变化时触发 characteristicvaluechanged 事件
比手动读取效率高
3.7 第七步:写入数据(发送命令) // 构造要发送的数据const command =newUint8Array([0x01 ,// 命令代码0x02 ,// 参数 10x03// 参数 2 ]);// 发送数据到设备await characteristic.writeValue(command); console.log('✅ 命令已发送' );
必须用 Uint8Array 格式
每个数字是 0-255 之间的十六进制值
设备收到命令后会执行相应的操作
3.8 第八步:禁用通知和断开连接 // 停止通知await characteristic.stopNotifications();// 断开连接 device.gatt.disconnect(); console.log ('✅ 已清理资源' );
四、完整的实战代码示例
4.1 简单的设备连接管理器 classSimpleBluetoothClient{constructor(){this.device =null;this.gattServer =null;this.service =null;this.characteristic =null;}/** * 连接设备 * @param {string } serviceName - 服务名称(如 'battery_service' ) * @param {string } characteristicName - 特征名称(如 'battery_level' ) */asyncconnect(serviceName, characteristicName){try{// 1. 让用户选择设备this.device =await navigator.bluetooth.requestDevice({ filters:[{ services:[serviceName]}], optionalServices:[]}); console.log ('✅ 已选择设备:' ,this.device.name);// 2. 连接到设备this.gattServer =awaitthis.device.gatt.connect(); console.log ('✅ GATT 连接成功' );// 3. 获取服务this.service =awaitthis.gattServer.getPrimaryService(serviceName); console.log ('✅ 获取服务成功' );// 4. 获取特征this.characteristic =awaitthis.service.getCharacteristic(characteristicName); console.log ('✅ 获取特征成功' );// 5. 监听断开事件this.device.addEventListener('gattserverdisconnected' ,()=>{ console.log ('⚠️ 设备已断开' );this.device =null;});}catch(error ){ console.error ('❌ 连接失败:' , error );throw error ;}}/** * 启用通知 * @param {Function} callback - 收到数据时的回调 */asyncstartListening(callback){try{awaitthis.characteristic.startNotifications(); console.log ('✅ 已启用通知' );this.characteristic.addEventListener('characteristicvaluechanged' ,event=>{const value = event.target.value;callback(value);});}catch(error ){ console.error ('❌ 启用通知失败:' , error );throw error ;}}/** * 发送命令 * @param {Uint8Array} data - 要发送的数据 */asyncsendCommand(data){try{awaitthis.characteristic.writeValue(data); console.log ('✅ 命令已发送' );}catch(error ){ console.error ('❌ 发送失败:' , error );throw error ;}}/** * 读取一次数据 */asyncreadOnce(){try{const value =awaitthis.characteristic.readValue(); console.log ('✅ 数据已读取' );return value;}catch(error ){ console.error ('❌ 读取失败:' , error );throw error ;}}/** * 断开连接 */disconnect(){if (this.device &&this.device.gatt.connected){this.device.gatt.disconnect(); console.log ('✅ 已断开连接' );}}}
4.2 在 Vue 中使用 <template > <div > <div :class ="{ connected: isConnected }" > {{ isConnected ? '已连接' : '未连接' }} </div > <button @click ="connectDevice" :disabled ="isConnected" > 连接设备 </button > <button @click ="readData" :disabled ="!isConnected" > 读取数据 </button > <button @click ="disconnect" :disabled ="!isConnected" > 断开连接 </button > <div v-if ="lastData" > <p > 最后收到的数据:</p > <code > {{ lastData }}</code > </div > </div > </template > <script > import SimpleBluetoothClient from '@/utils/BluetoothClient' ; export default { data ( ) { return { bluetooth : null , isConnected : false , lastData : null }; }, methods : { async connectDevice ( ) { try { this .bluetooth = new SimpleBluetoothClient (); </script > <style scoped > .bluetooth-demo { padding : 20px ; } .status { padding : 10px ; margin-bottom : 20px ; border-radius : 4px ; font-weight : bold; color : white; background : red; } .status .connected { background : green; } button { padding : 10px 20px ; margin-right : 10px ; cursor : pointer; } button :disabled { opacity : 0.5 ; cursor : not-allowed; } .data { margin-top : 20px ; padding : 10px ; background : #f0f0f0 ; border-radius : 4px ; } code { font-family : monospace; font-size : 12px ; } </style >
五、数据转换方法
5.1 从 DataView 提取数据 // 假设接收到的数据const value = dataView;// 类型为 DataView// 提取单个字节(无符号)const byte0 = value.getUint8(0 );// 0 -255 const byte1 = value.getUint8(1 );// 提取有符号字节const signedByte = value.getInt8(0 );// -128 到 127 // 提取 16 位整数const int16 = value.getUint16(0 ,true );// true = 小端序// 提取 32 位整数const int32 = value.getUint32(0 ,true );// 提取浮点数const float = value.getFloat32(0 ,true );const double = value.getFloat64(0 ,true );
5.2 构造要发送的数据 // 创建一个 4 字节的命令const command =newUint8Array([0x01,/ / 第 1 字节:命令 ID0x02,/ / 第 2 字节:参数 10x03,/ / 第 3 字节:参数 20x04/ / 第 4 字节:参数 3]);/ / 发送await characteristic.writeValue(command);
5.3 十六进制和数组互转 // 数组转十六进制字符串functionarrayToHex(dataView){const bytes =newUint8Array(dataView.buffer || dataView);return Array.from (bytes ).map (b=>'0x' + b.toString(16 ).padStart(2 ,'0' )).join(', ' );} console.log(arrayToHex(value));// 0x01 , 0x02 , 0x03 , ...// 十六进制字符串转数组functionhexToArray(hexString){const hex = hexString.replace(/\s+|0x/g,'' );const array =[];for (let i =0 ; i < hex .length; i +=2 ){ array.push(parseInt(hex .substr(i,2 ),16 ));}returnnewUint8Array(array);}const arr =hexToArray('01 02 03 04' );await characteristic.writeValue(arr);
六、常见蓝牙服务和特征
6.1 标准服务 UUID 这些是 Bluetooth SIG 定义的标准服务:
constSTANDARD_SERVICES ={'180a' :'设备信息服务' ,'180d' :'心率服务' ,'180b' :'血压服务' ,'180f' :'电池服务' ,'1800' :'通用访问' ,'1801' :'通用属性' ,}
6.2 标准特征 UUID constSTANDARD_CHARACTERISTICS ={'2a19' :'电池电量' ,'2a29' :'制造商名称' ,'2a24' :'型号号码' ,'2a25' :'序列号码' ,'2a37' :'心率测量' ,'2a35' :'血压测量' ,}
七、错误处理
7.1 常见错误类型 try{await navigator.bluetooth.requestDevice(...)}catch(error ){if (error .name ==='NotFoundError' ){// 用户取消了选择 console.log ('用户取消了设备选择' );}elseif (error .name ==='NotSupportedError' ){// 浏览器不支持 Web Bluetooth console.log ('浏览器不支持 Web Bluetooth API' );}elseif (error .name ==='SecurityError' ){// 需要 HTTPS 或 localhost console.log ('需要在 HTTPS 或 localhost 上运行' );}elseif (error .name ==='NetworkError' ){// 通信失败 console.log ('蓝牙通信失败,设备可能已离线' );}else { console.error ('未知错误:' , error .name);}}
7.2 重连机制 asyncfunctionconnectWithRetry (maxRetries =3 ){for (let i =0 ; i < maxRetries; i++){try { console .log (`第 ${i +1 } /${maxRetries} 次连接尝试...` );const device =await navigator.bluetooth .requestDevice ({ filters :[{ services :['battery_service' ]}]});const gattServer =await device.gatt .connect (); console .log ('✅ 连接成功' );return gattServer;}catch (error){ console .warn (`第 ${i +1 } 次尝试失败:` , error.message );if (i < maxRetries -1 ){
八、调试技巧
8.1 打印数据便于调试 // 将 DataView 转为可读的格式functiondebugData(dataView){const bytes =newUint8Array(dataView.buffer || dataView);const hexArray = Array.from (bytes ).map (b=> b.toString(16 ).padStart(2 ,'0' ).toUpperCase()); console.log('Raw hex:' , hexArray.join(' ' )); console.log('Decimal:' , Array.from (bytes ).join(', ' )); console.log('Binary:' , Array.from (bytes ).map (b=> b.toString(2 ).padStart(8 ,'0' )).join(' ' ));}// 使用 characteristic.addEventListener('characteristicvaluechanged' ,event=>{debugData(event.target.value);});
8.2 在浏览器中查看已连接的设备 // 在 Chrome 控制台运行: navigator.bluetooth.getDevices().then(devices =>{ console.table(devices.map(d=>({ name: d.name, id: d.id, connected: d.gatt.connected })))
8.3 监听所有事件 // 设备事件 device.addEventListener('gattserverdisconnected' ,()=>{ console.log ('❌ 设备已断开' );});// 特征值变化 characteristic.addEventListener('characteristicvaluechanged' ,event=>{ console.log ('📥 数据变化:' , event.target.value);});
九、使用前的检查清单
✅ 环境要求
使用 Chrome、Edge 等支持的浏览器
使用 HTTPS 或 localhost
确保用户已授予蓝牙权限
蓝牙设备已打开且可发现
✅ 连接步骤
调用 requestDevice() 让用户选择
调用 gatt.connect()
调用 getPrimaryService()
调用 getCharacteristic()
使用 startNotifications() 或 readValue()
✅ 数据处理
用 getUint8()、getInt16() 等正确提取数据
用 Uint8Array 构造发送的数据
处理可能的数据不完整情况
✅ 资源清理
用完通知后调用 stopNotifications()
断开连接时调用 disconnect()
移除事件监听器
十、快速参考
基本流程 // 1 . 扫描选择const device =await navigator.bluetooth.requestDevice({ filters:[{ services:['SERVICE_UUID' ]}]});// 2 . 连接const gattServer =await device.gatt.connect();// 3 . 获取服务const service =await gattServer.getPrimaryService('SERVICE_UUID' );// 4 . 获取特征const char =await service.getCharacteristic('CHARACTERISTIC_UUID' );// 5 . 读取或监听// 方式 A:启用通知await char.startNotifications(); char.addEventListener('characteristicvaluechanged' ,event=> {const data = event.target.value;});// 方式 B:手动读取const data =await char.readValue();// 6 . 写入await char.writeValue(newUint8Array([0x01 ,0x02 ]));// 7 . 清理await char.stopNotifications(); device.gatt.disconnect();
数据提取 // DataView 方法 value.getUint8(index );// 无符号字节 value.getInt8(index );// 有符号字节 value.getUint16(index ,true);// 无符号 16 位 value.getInt16(index ,true);// 有符号 16 位 value.getUint32(index ,true);// 无符号 32 位 value.getInt32(index ,true);// 有符号 32 位 value.getFloat32(index ,true);// 浮点数 32 位 value.getFloat64(index ,true);// 浮点数 64 位
总结 Web Bluetooth API 的核心就是这 7 个步骤:
扫描 → requestDevice()
连接 → gatt.connect()
获取服务 → getPrimaryService()
获取特征 → getCharacteristic()
启用通知或读取 → startNotifications() 或 readValue()
发送或接收数据 → writeValue() 或监听事件
清理 → disconnect()
掌握这 7 个 API,你就可以与任何蓝牙设备通信!
相关免费在线工具 curl 转代码 解析常见 curl 参数并生成 fetch、axios、PHP curl 或 Python requests 示例代码。 在线工具,curl 转代码在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online