跳到主要内容
浏览器 Web Bluetooth API 使用方法 | 极客日志
JavaScript 大前端
浏览器 Web Bluetooth API 使用方法 综述由AI生成 浏览器 Web Bluetooth API 的使用方法,涵盖核心概念如设备、服务、特征及 UUID,以及完整的通信流程。内容包括如何扫描并选择设备、连接 GATT 服务器、获取服务与特征、读写数据(包括通知模式)、数据转换方法(DataView 处理)、标准 UUID 参考、错误处理机制及重连策略。文章还提供了 JavaScript 类封装示例和 Vue 组件集成方案,帮助开发者实现网页与蓝牙设备的交互。
林间仙子 发布于 2026/4/6 更新于 2026/5/23 35 浏览浏览器 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.bluetooth .requestDevice ({
: [{ : [ ] }],
: [ ]
});
. ( , device. );
filters
services
'battery_service'
optionalServices
'device_information'
console
log
'选中设备:'
name
requestDevice() 会弹出浏览器对话框,让用户选择
只有提供了指定服务的设备才会显示
返回一个 Device 对象
3.2 第二步:连接到设备
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' );
console .log ('✅ 获取服务成功' );
一个设备可能有多个服务
需要知道你要的服务的 UUID
返回一个 Service 对象
3.4 第四步:获取特征
const characteristic = await service.getCharacteristic ('battery_level' );
console .log ('✅ 获取特征成功' );
一个服务可能有多个特征
特征是真正的数据承载者
返回一个 Characteristic 对象
3.5 第五步:读取数据
const value = await characteristic.readValue ();
const battery = value.getUint8 (0 );
console .log ('电池电量:' , battery, '%' );
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 = new Uint8Array ([
0x01 ,
0x02 ,
0x03
]);
await characteristic.writeValue (command);
console .log ('✅ 命令已发送' );
必须用 Uint8Array 格式
每个数字是 0-255 之间的十六进制值
设备收到命令后会执行相应的操作
3.8 第八步:禁用通知和断开连接
await characteristic.stopNotifications ();
device.gatt .disconnect ();
console .log ('✅ 已清理资源' );
四、完整的实战代码示例
4.1 简单的设备连接管理器 class SimpleBluetoothClient {
constructor ( ) {
this .device = null ;
this .gattServer = null ;
this .service = null ;
this .characteristic = null ;
}
async connect (serviceName, characteristicName ) {
try {
this .device = await navigator.bluetooth .requestDevice ({
filters : [{ services : [serviceName] }],
optionalServices : []
});
console .log ('✅ 已选择设备:' , this .device .name );
this .gattServer = await this .device .gatt .connect ();
console .log ('✅ GATT 连接成功' );
this .service = await this .gattServer .getPrimaryService (serviceName);
console .log ('✅ 获取服务成功' );
this .characteristic = await this .service .getCharacteristic (characteristicName);
console .log ('✅ 获取特征成功' );
this .device .addEventListener ('gattserverdisconnected' , () => {
console .log ('⚠️ 设备已断开' );
this .device = null ;
});
} catch (error) {
console .error ('❌ 连接失败:' , error);
throw error;
}
}
async startListening (callback ) {
try {
await this .characteristic .startNotifications ();
console .log ('✅ 已启用通知' );
this .characteristic .addEventListener ('characteristicvaluechanged' , event => {
const value = event.target .value ;
callback (value);
});
} catch (error) {
console .error ('❌ 启用通知失败:' , error);
throw error;
}
}
async sendCommand (data ) {
try {
await this .characteristic .writeValue (data);
console .log ('✅ 命令已发送' );
} catch (error) {
console .error ('❌ 发送失败:' , error);
throw error;
}
}
async readOnce ( ) {
try {
const value = await this .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();
// 连接到设备
// 注:这里用的是标准的蓝牙服务 UUID
await this.bluetooth.connect(
'180a', // 设备信息服务
'2a29' // 制造商名称
);
this.isConnected = true;
// 启用通知
await this.bluetooth.startListening(value => {
this.lastData = this.formatData(value);
});
} catch (error) {
alert('连接失败:' + error.message);
}
},
async readData() {
try {
const value = await this.bluetooth.readOnce();
this.lastData = this.formatData(value);
} catch (error) {
alert('读取失败:' + error.message);
}
},
disconnect() {
this.bluetooth.disconnect();
this.isConnected = false;
this.lastData = null;
},
formatData(dataView) {
// 将 DataView 转换为十六进制字符串
const bytes = new Uint8Array(dataView.buffer);
return Array.from(bytes)
.map(b => b.toString(16).padStart(2, '0'))
.join(' ');
}
}
};
</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;
const byte0 = value.getUint8 (0 );
const byte1 = value.getUint8 (1 );
const signedByte = value.getInt8 (0 );
const int16 = value.getUint16 (0 , true );
const int32 = value.getUint32 (0 , true );
const float = value.getFloat32 (0 , true );
const double = value.getFloat64 (0 , true );
5.2 构造要发送的数据
const command = new Uint8Array ([
0x01 ,
0x02 ,
0x03 ,
0x04
]);
await characteristic.writeValue (command);
5.3 十六进制和数组互转
function arrayToHex (dataView ) {
const bytes = new Uint8Array (dataView.buffer || dataView);
return Array .from (bytes).map (b => '0x' + b.toString (16 ).padStart (2 , '0' )).join (', ' );
}
console .log (arrayToHex (value));
function hexToArray (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 ));
}
return new Uint8Array (array);
}
const arr = hexToArray ('01 02 03 04' );
await characteristic.writeValue (arr);
六、常见蓝牙服务和特征
6.1 标准服务 UUID 这些是 Bluetooth SIG 定义的标准服务:
const STANDARD_SERVICES = {
'180a' : '设备信息服务' ,
'180d' : '心率服务' ,
'180b' : '血压服务' ,
'180f' : '电池服务' ,
'1800' : '通用访问' ,
'1801' : '通用属性'
};
6.2 标准特征 UUID const STANDARD_CHARACTERISTICS = {
'2a19' : '电池电量' ,
'2a29' : '制造商名称' ,
'2a24' : '型号号码' ,
'2a25' : '序列号码' ,
'2a37' : '心率测量' ,
'2a35' : '血压测量'
};
七、错误处理
7.1 常见错误类型 try {
await navigator.bluetooth .requestDevice (...);
} catch (error) {
if (error.name === 'NotFoundError' ) {
console .log ('用户取消了设备选择' );
} else if (error.name === 'NotSupportedError' ) {
console .log ('浏览器不支持 Web Bluetooth API' );
} else if (error.name === 'SecurityError' ) {
console .log ('需要在 HTTPS 或 localhost 上运行' );
} else if (error.name === 'NetworkError' ) {
console .log ('蓝牙通信失败,设备可能已离线' );
} else {
console .error ('未知错误:' , error.name );
}
}
7.2 重连机制 async function connectWithRetry (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 ) {
await new Promise (resolve => setTimeout (resolve, 1000 ));
}
}
}
throw new Error ('连接失败,已达到最大重试次数' );
}
八、调试技巧
8.1 打印数据便于调试
function debugData (dataView ) {
const bytes = new Uint8Array (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 在浏览器中查看已连接的设备
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()
移除事件监听器
十、快速参考
基本流程
const device = await navigator.bluetooth .requestDevice ({
filters : [{ services : ['SERVICE_UUID' ] }]
});
const gattServer = await device.gatt .connect ();
const service = await gattServer.getPrimaryService ('SERVICE_UUID' );
const char = await service.getCharacteristic ('CHARACTERISTIC_UUID' );
await char.startNotifications ();
char.addEventListener ('characteristicvaluechanged' , event => {
const data = event.target .value ;
});
const data = await char.readValue ();
await char.writeValue (new Uint8Array ([0x01 , 0x02 ]));
await char.stopNotifications ();
device.gatt .disconnect ();
数据提取
value.getUint8 (index);
value.getInt8 (index);
value.getUint16 (index, true );
value.getInt16 (index, true );
value.getUint32 (index, true );
value.getInt32 (index, true );
value.getFloat32 (index, true );
value.getFloat64 (index, true );
总结 Web Bluetooth API 的核心就是这 7 个步骤:
扫描 → requestDevice()
连接 → gatt.connect()
获取服务 → getPrimaryService()
获取特征 → getCharacteristic()
启用通知或读取 → startNotifications() 或 readValue()
发送或接收数据 → writeValue() 或监听事件
清理 → disconnect()
掌握这 7 个 API,你就可以与任何蓝牙设备通信!
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online