BLE 调试助手是一款基于 UniApp + Vue3 开发的跨平台(Android / iOS)蓝牙低功耗调试工具。专为嵌入式开发者和硬件工程师设计,提供类似串口调试助手的无线调试体验——涵盖实时 HEX/ASCII 双向通信、服务特征值树状解析、Notify 订阅、快捷命令管理、RSSI 信号历史图表、MTU 协商控制、特征值历史 Diff 对比、自定义协议插件执行、多格式日志导出,以及多设备同时调试。
这个项目的下一阶段产品方向,是让 BLE 调试更接近 Postman / Apifox 调试 API 接口:服务和特征值不再只是 UUID,而是可描述、可归档、可导出的接口文档,包含请求/响应样例、字段含义、有效服务说明、调试记录、AI 友好的报告,以及可用于脱离硬件测试的 Mock 数据包。
应用内置暗色/亮色双主题和中/英双语界面,无需重启即可随时切换。
- 设备扫描 — 实时发现周边 BLE 广播包,配合雷达动效可视化;已连接设备时扫描仍可继续
- 智能过滤 — 支持按设备名称或 RSSI 信号阈值筛选设备
- 服务特征值树 — 多设备总览树,所有已连接设备的服务与特征值并列展示,属性标签一目了然(READ / WRITE / WRITE NR / NOTIFY / INDICATE)
- 内置协议提示 — 以内置 Markdown 协议文档为源,为已知服务/特征补充语义名称、有效性说明、值格式和接口样例
- HEX / ASCII 双向通信 — 支持两种格式的数据收发,随时切换
- Notify 订阅 — 逐特征值开启/关闭 BLE 通知监听
- 主动读取 — 按需触发特征值 Read 操作
- 自动重连 — 支持断线自动重连和心跳保活机制
- MTU 协商 — 每设备独立协商 MTU 大小(23–512 字节),实时显示协商结果
- 多设备同时调试 — 同时连接并调试多个 BLE 设备,每个设备拥有完全独立的日志缓冲区、服务树和通信状态
- 快捷命令 — 像 Postman 保存请求一样保存常用指令,包含名称、类型、描述、命令内容和内容格式;长按删除,一键复用
- 通信日志 — 带时间戳的 TX/RX/SYS 彩色日志,环形缓冲区最多保留 2000 条;每设备完全独立
- 三种显示模式 — HEX 模式、ASCII 模式、DUAL 双显模式自由切换
- 日志导出(TXT / CSV) — 导出时选择纯文本格式或电子表格 CSV 格式,一键保存至本地
- AI / Mock 导出 — 支持导出 AI 调试报告、协议 JSON、Mock JSON,方便 AI agent 阅读和生成模拟测试数据
- 协议样例保存 — 长按 TX/RX 日志即可保存为协议样例,按设备/服务/特征值归类进入后续报告和 Mock 文档
- 协议解析 — 内置 RAW / UART 解析器;支持自定义 JavaScript 协议插件,自定义帧格式解析逻辑
- RSSI 信号历史图表 — 连接后每 2 秒采集一次信号强度,以柱状图实时展示趋势
- 特征值历史 Diff — 记录每个特征值的历史接收值,逐字节高亮标注变化字节
- 最近设备 — 持久化最近连接记录,支持快速重连
- 暗色主题 — 科技感终端风 (
#0A0F1C背景 +#00F5FF霓虹蓝 +#39FF14荧光绿) - 亮色主题 — 专业日间模式 (
#EDF2F7背景 +#0369A1深蓝 +#059669翠绿) - 主题切换 — 顶部按钮或设置面板即时切换,偏好持久化
- 双语界面 — 完整中/英文界面,无缝切换,导航栏标题同步更新
- 响应式布局 — 手机端上下堆叠;平板/横屏(≥768 px)切换为左右分栏布局,固定 60 px 左侧导航栏替代底部 TabBar
左:暗色主题 · 右:亮色主题
| 扫描页 | 设备总览树 | 调试控制台 |
|---|---|---|
| 雷达动效、RSSI 信号格、已连接标记 | 多设备树 · MTU 协商面板 · RSSI 图表 | 设备 Tab 切换 · HEX/ASCII 收发 · 日志面板 · 协议插件 |
| 层级 | 技术选型 |
|---|---|
| 框架 | UniApp(Vue 3 + <script setup>) |
| 语言 | TypeScript 5 |
| 状态管理 | Pinia 2 — bleStore(会话 + 适配器)· appStore(主题/语言)· protocolStore(插件) |
| BLE API | UniApp 原生 BLE API — Promise 化封装,每设备独立状态机 |
| 样式 | Scoped SCSS + CSS 自定义属性(.theme-dark / .theme-light 双主题类) |
| 响应式布局 | useResponsive composable — ≥768 px 使用 LeftTabBar 组件;窄屏保留原生底部 TabBar |
| 国际化 | 自研 useI18n composable(dot-notation key,响应式语言切换) |
| 持久化 | uni.setStorageSync(设置、快捷命令、协议插件、设备 PIN) |
| 平台 | 最低版本 |
|---|---|
| Android | API 21(Android 5.0) |
| iOS | iOS 13.0 |
| HBuilderX | 3.x+ |
| Node.js | 16+(CLI 模式) |
蓝牙权限已在
manifest.json中声明。 Android 12+ 设备上,BLUETOOTH_SCAN和BLUETOOTH_CONNECT权限会在运行时动态申请。
# 1. 克隆仓库
git clone https://github.com/your-org/uniapp-ble-debugging-assistant.git
cd uniapp-ble-debugging-assistant
# 2. 安装依赖
npm install
# 3. 用 HBuilderX 打开 → 运行 → 运行到手机或模拟器在 HBuilderX 中选择 运行 → 运行到手机或模拟器 → 运行到 Android/iOS 直接编译部署。
npm install
# 开发构建(App-Plus)
npm run dev:app
# 生产构建
npm run build:app
# 预览内置 BLE 协议 Markdown 模板的解析结果
npm run docs:protocoluniapp-ble-debugging-assistant/
│
├── pages/
│ ├── scan/index.vue # 设备扫描页——发现设备,已连接设备带标记
│ ├── device/index.vue # 多设备总览树——所有会话的服务与特征值
│ ├── debug/index.vue # BLE 调试控制台——DeviceTabBar + 每会话独立面板
│ └── protocol/index.vue # 协议插件管理页(添加 / 编辑 / 启用)
│
├── components/
│ ├── DeviceTabBar.vue # 已连接设备的横向 Tab 切换栏
│ ├── DeviceItem.vue # 扫描列表卡片(RSSI 信号格、已连接标记)
│ ├── BleLogPanel.vue # 通信日志查看器(每会话独立)
│ ├── HexInput.vue # HEX/ASCII 输入 + 快捷命令 + 发送
│ ├── RadarScanAnimation.vue # 雷达扫描动效(含设备点位)
│ ├── RssiChart.vue # RSSI 信号历史柱状图
│ ├── DiffModal.vue # 特征值历史记录弹窗(逐字节 Diff 高亮)
│ ├── LeftTabBar.vue # ≥768 px 宽屏固定 60 px 左侧导航栏
│ └── SettingsPanel.vue # 底部弹出设置面板(主题 & 语言切换)
│
├── services/
│ ├── bleManager.ts # BLE 封装层
│ # 适配器状态机:UNINITIALIZED → IDLE ↔ SCANNING
│ # 每设备状态:Map<deviceId, CONNECTING|CONNECTED|DISCONNECTED>
│ # + getRSSI(deviceId) + negotiateMTU(deviceId, mtu)
│ └── builtinProtocolDocs.ts # 加载内置 Markdown 协议模板,并提供 UUID 匹配能力
│
├── store/
│ ├── bleStore.ts # BLE 运行时状态
│ │ # sessions: Map<deviceId, DeviceSession> ← 每设备独立数据
│ │ # activeSessionId: string ← 驱动调试页渲染
│ │ # + 适配器层(扫描设备、过滤、快捷命令、最近设备)
│ ├── appStore.ts # 应用设置状态(主题、语言、CSS 变量)
│ └── protocolStore.ts # 协议插件注册表(添加 / 执行 / 持久化)
│
├── composables/
│ ├── useI18n.ts # i18n composable — t('dot.notation.key')
│ └── useResponsive.ts # isWideScreen 响应式标志(window.width ≥ 768 px)
│
├── locales/
│ ├── zh.ts # 简体中文语言包
│ └── en.ts # 英文语言包
│
├── utils/
│ ├── hex.ts # HEX↔ArrayBuffer、ASCII、UUID、RSSI 工具函数
│ ├── buffer.ts # 日志条目、环形缓冲区、导出、持久化、协议样例保存
│ └── protocolDocs.ts # Markdown 协议解析 + AI 报告 / 协议 JSON / Mock JSON 生成
│
├── docs/
│ └── protocols/ # 可本地预览、也会被 App 解析加载的 Markdown 协议模板
│
├── scripts/
│ └── preview-protocol-docs.mjs # 本地协议模板预览命令(npm run docs:protocol)
│
├── App.vue # 全局 CSS 自定义属性定义(双主题变量)
├── env.d.ts # raw Markdown import 类型声明
├── pages.json # 路由配置
└── manifest.json # 应用权限 & 平台配置
硬件 BLE 无线电
│
▼
uni BLE API 回调(onBLECharacteristicValueChange、onBLEConnectionStateChange …)
│
▼
bleManager ── Promise API + 事件发射 ──▶ bleStore
│ │
│ onDataReceived(deviceId, ...) ├── sessions.get(deviceId).logBuffer
│ onConnectionChange(deviceId, connected) ├── sessions.get(deviceId).charValueHistory
│ onAdapterStateChange(adapterState) └── adapterState(SCANNING / IDLE / …)
│
▼
页面 & 组件 ← Pinia 响应式状态(自动重渲染)
UNINITIALIZED ──openAdapter()──▶ IDLE ◀──▶ SCANNING
│
(与扫描完全独立,并行运行)
▼
每设备:Map<deviceId, DeviceState>
CONNECTING → CONNECTED → DISCONNECTED
↑ │
└──(自动重连)─┘
bleManager.ts 同时管理两个层次。连接新设备不会停止正在进行的扫描——扫描与连接完全独立运行。
bleStore
├── sessions: Map<deviceId, DeviceSession>
│ DeviceSession {
│ device BleDevice
│ deviceState BleDeviceState
│ services BleService[]
│ characteristics Map<serviceId, BleCharacteristic[]>
│ logBuffer RingBuffer<LogEntry> ← 独立缓冲区,2000 条
│ logs LogEntry[]
│ rssiHistory { time, rssi }[] ← 独立,最多 60 个点
│ charValueHistory Record<charId, { time, hex }[]>
│ savedSamples BleProtocolSample[] ← 长按 TX/RX 保存的协议样例
│ currentMtu number
│ txBytes / rxBytes number
│ activeServiceId / activeCharacteristicId / notifyEnabled
│ }
│
├── activeSessionId: string ← 决定调试页显示哪个设备
│
└── 适配器层(共享)
adapterState、scannedDevices、filterName、filterMinRssi、
quickCommands、recentDevices、isConnecting、errorMessage
活跃会话代理 — 调试页所有计算属性(isConnected、logs、services、activeCharacteristic 等)均从 sessions.get(activeSessionId) 读取。切换 Tab 只需更改 activeSessionId,无需任何重新初始化,即时反映另一设备的完整状态。
docs/protocols/*.md
│
▼
services/builtinProtocolDocs.ts
│ raw Markdown import + parseProtocolMarkdown()
▼
utils/protocolDocs.ts
│ 按 Service UUID 匹配
▼
设备页服务树
│ 显示服务/特征语义、方向、值格式
▼
导出管线
├── AI Debug Report Markdown
├── Protocol Spec JSON
└── Mock Pack JSON
内置协议文档优先以 Markdown 编写。这样既方便本地预览、评审和持续补充,又能被 App 解析成结构化 ProtocolProfileDoc,用于设备页增强展示和导出。
当前导出层会把 BLE 通信按“接口契约”处理:
- AI Debug Report Markdown 汇总设备信息、匹配到的内置协议、服务/特征语义、保存的协议样例、近期 TX/RX 日志。
- Protocol Spec JSON 保留机器可读协议模型,包括服务、特征、接口、字段表、示例和保存样例。
- Mock Pack JSON 按服务/特征值整理 mock seed,包含请求/响应样例和用户保存的 TX/RX 样例,方便 AI agent 或 App 侧测试工具生成无硬件模拟数据。
TX/RX 日志现在会携带 serviceUUID 与 characteristicUUID,导出时能按 BLE endpoint 归类,而不是只有原始字节列表。
CSS 自定义属性定义在 App.vue 的 .theme-dark 和 .theme-light 类中。每个页面根元素应用 :class="appStore.themeClass",所有子组件的 Scoped CSS 通过 var(--xxx) 自动继承当前主题变量。
App.vue(定义 .theme-dark / .theme-light 变量)
└── 页面根 <view :class="appStore.themeClass">
└── 子组件 → var(--bg-base), var(--color-primary), ...
appStore 同时导出 cssVarsStyle(内联 style 字符串),用于需要直接注入变量的特殊场景。
useResponsive() composable
└── isWideScreen: boolean (window.width ≥ 768 px,响应式监听)
窄屏(<768 px) 宽屏(≥768 px)
─────────────────── ─────────────────────────────────────
原生底部 TabBar LeftTabBar.vue(60 px 固定左侧栏)
单列堆叠布局 双列 flex 布局(各页面独立比例)
各页面分栏比例(宽屏):
扫描页: 40% 左栏(雷达 + 控制) / 60% 右栏(设备列表)
设备页: 35% 左栏(信息 + MTU + RSSI) / 65% 右栏(服务树)
调试页: 55% 左栏(日志面板) / 45% 右栏(发送面板)
每个 Tab 页面条件渲染 <LeftTabBar v-if="isWideScreen" />,并在宽屏下加 padding-left: 60px 为侧边栏留位。
// composables/useI18n.ts
const { t } = useI18n()
t('scan.startScan') // → '开始扫描' | 'Start Scan'
t('debug.bytes') // → '字节' | 'bytes'切换 appStore.locale('zh' / 'en')是响应式的,所有 t() 调用即时生效,导航栏标题通过 watch 同步更新。
插件以 JavaScript 函数体字符串形式存储,由 protocolStore 通过 new Function() 动态执行。插件接收 hexStr(如 "01 02 FF")和 asciiStr 两个参数,返回 { fields: [{ name, value }] }。
// 示例插件 — 解析 4 字节自定义帧
const b = hexStr.split(' ').map(h => parseInt(h, 16));
return {
fields: [
{ name: 'CMD', value: '0x' + b[0].toString(16).toUpperCase() },
{ name: '长度', value: b[1] + ' 字节' },
{ name: '载荷', value: hexStr.slice(6) },
{ name: 'CRC', value: '0x' + b[b.length - 1].toString(16).toUpperCase() },
]
};同一时刻只允许启用一个插件。管理入口:调试页 → 协议解析 → 自定义 → 管理插件。
内置协议模板位于 docs/protocols/。每份模板既是人可读文档,也会被 App 解析。
---
id: generic-command-profile
name: Generic Command BLE Profile
version: 0.1.0
summary: Command/response BLE template.
---
## Service: Generic Command Service
- uuid: 0000FFE0-0000-1000-8000-00805F9B34FB
- summary: Vendor command transport service.
- validWhen: Use for FFE0/FFE1-style command transports.
- role: request-response transport
### Characteristic: Command TX
- uuid: 0000FFE1-0000-1000-8000-00805F9B34FB
- properties: WRITE, WRITE_NR
- direction: app-to-device
- valueFormat: binary-frame
#### Interface: Get Device Info
- operationId: device.getInfo
- requestExample: AA 01 00 AB
- responseExample: AA 81 03 01 00 10 39
- mock: Return responseExample when request starts with AA 01.可执行 npm run docs:protocol 预览解析后的 JSON 结构。
| 方法 | 说明 |
|---|---|
openAdapter() |
打开蓝牙适配器,设置状态与设备监听器 |
startScan(options?) |
开始 BLE 扫描,可设超时时间;已连接设备时可安全调用 |
stopScan() |
停止 BLE 扫描 |
connect(deviceId) |
建立 BLE 连接(不会停止进行中的扫描) |
disconnect(deviceId) |
断开指定设备的 BLE 连接 |
getServices(deviceId) |
获取全部服务 |
getCharacteristics(deviceId, serviceId) |
获取特征值列表 |
write(deviceId, serviceId, charId, buffer) |
向特征值写入数据 |
readCharacteristic(deviceId, serviceId, charId) |
读取特征值当前值 |
setNotify(deviceId, serviceId, charId, enable) |
开启/关闭 BLE 通知 |
getRSSI(deviceId) |
查询已连接设备的当前 RSSI 信号强度 |
negotiateMTU(deviceId, mtu) |
发起 MTU 协商请求(23–512),返回实际协商结果 |
getConnectedDeviceIds() |
返回所有已连接设备 ID 的 Set<string> |
getDeviceState(deviceId) |
返回指定设备的连接状态 |
onAdapterStateChange(fn) |
订阅适配器状态变化(UNINITIALIZED / IDLE / SCANNING) |
onDeviceStateChange(fn) |
订阅每设备连接状态变化 |
onDataReceived(fn) |
订阅特征值数据变化 |
| 函数 | 说明 |
|---|---|
bufToHex(buf) |
ArrayBuffer → "01 AB FF" |
hexToBuf(str) |
"01ABFF" → ArrayBuffer |
bufToAscii(buf) |
二进制 → 可打印 ASCII(不可打印字符显示为 .) |
isValidHex(str) |
验证 HEX 字符串格式 |
normalizeHex(str) |
格式化 HEX,补全空格分隔 |
shortUUID(uuid) |
将完整 UUID 缩写为 0xXXXX 格式 |
rssiToLevel(rssi) |
RSSI 值 → 信号格等级(1–5) |
rssiToColor(rssi) |
RSSI 值 → 对应颜色字符串 |
| 函数 | 说明 |
|---|---|
exportLogsToText(logs, device) |
将日志数组序列化为格式化纯文本 |
exportLogsToCSV(logs, device) |
将日志数组序列化为 RFC-4180 标准 CSV |
saveLogsToFile(content, filename, mimeType) |
App 端写入本地文件;H5 端触发浏览器下载 |
| 变量名 | 色值 | 用途 |
|---|---|---|
--bg-base |
#0A0F1C |
页面背景 |
--bg-panel |
#111827 |
卡片、头部区域 |
--bg-card |
#1A2235 |
列表项卡片 |
--color-primary |
#00F5FF |
霓虹蓝 — 主要操作、边框 |
--color-accent |
#39FF14 |
荧光绿 — RX 数据、成功状态 |
--color-danger |
#FF3B3B |
错误、断开连接 |
--color-warning |
#F59E0B |
警告、系统日志 |
--text-primary |
#E2E8F0 |
主体文字 |
--text-mono |
#A8D8A8 |
等宽数据显示区 |
| 变量名 | 色值 | 用途 |
|---|---|---|
--bg-base |
#EDF2F7 |
页面背景 |
--bg-panel |
#FFFFFF |
卡片、头部区域 |
--bg-card |
#F7FAFC |
列表项卡片 |
--color-primary |
#0369A1 |
深蓝 — 主要操作、边框 |
--color-accent |
#059669 |
翠绿 — RX 数据、成功状态 |
--color-danger |
#DC2626 |
错误、断开连接 |
--color-warning |
#D97706 |
警告、系统日志 |
--text-primary |
#1A202C |
主体文字 |
--text-mono |
#1A5F2E |
等宽数据显示区 |
| 设置项 | Storage Key | 默认值 |
|---|---|---|
| 显示主题 | ble_app_theme |
dark |
| 界面语言 | ble_app_locale |
zh |
| 快捷命令 | ble_quick_commands |
[] |
| 最近设备 | ble_recent_devices |
[] |
| 协议插件 | ble_protocol_plugins |
[] |
| 协议样例 | ble_protocol_samples |
{} |
全部设置通过 uni.setStorageSync 持久化,应用重启后自动恢复。
| 权限 | 用途 |
|---|---|
BLUETOOTH / BLUETOOTH_ADMIN |
基础蓝牙控制(API < 31) |
BLUETOOTH_SCAN |
BLE 设备扫描(API 31+) |
BLUETOOTH_CONNECT |
BLE 设备连接(API 31+) |
ACCESS_FINE_LOCATION |
BLE 扫描需要位置权限 |
WRITE_EXTERNAL_STORAGE |
日志文件导出 |
| 权限 | 用途 |
|---|---|
NSBluetoothAlwaysUsageDescription |
BLE 设备扫描与连接 |
NSBluetoothPeripheralUsageDescription |
外设数据通信 |
NSLocationWhenInUseUsageDescription |
BLE 扫描位置需求 |
Q: Android 设备扫描不到蓝牙设备?
检查是否已开启手机蓝牙和位置服务;Android 12+ 需授予
BLUETOOTH_SCAN和位置权限。
Q: iOS 无法连接设备?
确认已在系统设置中授予蓝牙权限;部分 iOS 设备需先在系统蓝牙中配对。
Q: 发送数据没有响应?
确认已选择支持 WRITE 或 WRITE WITHOUT RESPONSE 属性的特征值,在设备页可查看属性标签。
Q: MTU 协商失败?
部分设备或固件不支持 MTU 协商,此时 API 会返回错误,保持默认 23 字节即可。iOS 会自动协商 MTU,无需手动操作。
Q: 协议插件运行出错?
插件代码在沙盒
new Function()中执行。出错时字段列表会显示Error: <错误信息>,便于定位问题。注意函数体最后需要有return语句。
Q: 同时连接多少个设备合适?
没有硬性限制,但手机蓝牙栈通常同时稳定维持 3–5 个连接。连接 3 个及以上时应用会给出提示,建议在实际设备上测试稳定性。
Q: 日志显示乱码?
切换到 HEX 模式查看原始字节;ASCII 模式下不可打印字符会显示为
.。
Q: 如何保存常用发送命令?
在调试页输入数据后,点击「+ 保存为快捷命令」,填写名称、类型、描述和命令内容即可;下次发送只需点击命令按钮。
- Fork 本仓库
- 创建功能分支:
git checkout -b feature/my-feature - 提交变更:
git commit -m 'feat: 添加新功能' - 推送并创建 Pull Request
请遵循现有的 TypeScript + Vue3 Composition API 代码风格,所有 UI 文字须同步更新 locales/zh.ts 和 locales/en.ts。
MIT © 2024 BLE 调试助手贡献者
专为对工具有极致要求的硬件工程师设计



