Skip to content

Latest commit

 

History

History
570 lines (442 loc) · 24.2 KB

File metadata and controls

570 lines (442 loc) · 24.2 KB

⬡ BLE 调试助手

面向硬件工程师的专业蓝牙低功耗调试工具

UniApp TypeScript Pinia 平台 版本 协议

English · 功能特性 · 快速开始 · 架构设计 · 界面截图


项目简介

BLE 调试助手是一款基于 UniApp + Vue3 开发的跨平台(Android / iOS)蓝牙低功耗调试工具。专为嵌入式开发者和硬件工程师设计,提供类似串口调试助手的无线调试体验——涵盖实时 HEX/ASCII 双向通信、服务特征值树状解析、Notify 订阅、快捷命令管理、RSSI 信号历史图表、MTU 协商控制、特征值历史 Diff 对比、自定义协议插件执行、多格式日志导出,以及多设备同时调试

这个项目的下一阶段产品方向,是让 BLE 调试更接近 Postman / Apifox 调试 API 接口:服务和特征值不再只是 UUID,而是可描述、可归档、可导出的接口文档,包含请求/响应样例、字段含义、有效服务说明、调试记录、AI 友好的报告,以及可用于脱离硬件测试的 Mock 数据包。

应用内置暗色/亮色双主题中/英双语界面,无需重启即可随时切换。


功能特性

BLE 核心能力

  • 设备扫描 — 实时发现周边 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 — 记录每个特征值的历史接收值,逐字节高亮标注变化字节
  • 最近设备 — 持久化最近连接记录,支持快速重连

UI / UX

  • 暗色主题 — 科技感终端风 (#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_SCANBLUETOOTH_CONNECT 权限会在运行时动态申请。


快速开始

方式 A — HBuilderX(推荐)

# 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 直接编译部署。

方式 B — CLI

npm install

# 开发构建(App-Plus)
npm run dev:app

# 生产构建
npm run build:app

# 预览内置 BLE 协议 Markdown 模板的解析结果
npm run docs:protocol

项目结构

uniapp-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 响应式状态(自动重渲染)

BLE 适配器状态机

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

活跃会话代理 — 调试页所有计算属性(isConnectedlogsservicesactiveCharacteristic 等)均从 sessions.get(activeSessionId) 读取。切换 Tab 只需更改 activeSessionId,无需任何重新初始化,即时反映另一设备的完整状态。

API 化 BLE 文档流

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,用于设备页增强展示和导出。

AI 友好报告与 Mock 文档

当前导出层会把 BLE 通信按“接口契约”处理:

  • AI Debug Report Markdown 汇总设备信息、匹配到的内置协议、服务/特征语义、保存的协议样例、近期 TX/RX 日志。
  • Protocol Spec JSON 保留机器可读协议模型,包括服务、特征、接口、字段表、示例和保存样例。
  • Mock Pack JSON 按服务/特征值整理 mock seed,包含请求/响应样例和用户保存的 TX/RX 样例,方便 AI agent 或 App 侧测试工具生成无硬件模拟数据。

TX/RX 日志现在会携带 serviceUUIDcharacteristicUUID,导出时能按 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() },
  ]
};

同一时刻只允许启用一个插件。管理入口:调试页 → 协议解析 → 自定义 → 管理插件

内置协议 Markdown 格式

内置协议模板位于 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 结构。


核心 API 参考

services/bleManager.ts

方法 说明
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) 订阅特征值数据变化

utils/hex.ts

函数 说明
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 值 → 对应颜色字符串

utils/buffer.ts

函数 说明
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 持久化,应用重启后自动恢复。


权限说明

Android

权限 用途
BLUETOOTH / BLUETOOTH_ADMIN 基础蓝牙控制(API < 31)
BLUETOOTH_SCAN BLE 设备扫描(API 31+)
BLUETOOTH_CONNECT BLE 设备连接(API 31+)
ACCESS_FINE_LOCATION BLE 扫描需要位置权限
WRITE_EXTERNAL_STORAGE 日志文件导出

iOS

权限 用途
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: 如何保存常用发送命令?

在调试页输入数据后,点击「+ 保存为快捷命令」,填写名称、类型、描述和命令内容即可;下次发送只需点击命令按钮。


参与贡献

  1. Fork 本仓库
  2. 创建功能分支:git checkout -b feature/my-feature
  3. 提交变更:git commit -m 'feat: 添加新功能'
  4. 推送并创建 Pull Request

请遵循现有的 TypeScript + Vue3 Composition API 代码风格,所有 UI 文字须同步更新 locales/zh.tslocales/en.ts


开源协议

MIT © 2024 BLE 调试助手贡献者


基于 UniApp · Vue 3 · TypeScript · Pinia 构建
专为对工具有极致要求的硬件工程师设计