# BLEDebug
**Repository Path**: wflwang/bledebug
## Basic Information
- **Project Name**: BLEDebug
- **Description**: 蓝牙调试APP
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 1
- **Forks**: 0
- **Created**: 2026-06-10
- **Last Updated**: 2026-09-10
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# wFOC Tool — BLE广播调试工具
原生Android应用,用于发送BLE(蓝牙低功耗)广播信号,与 **FOC 控制器** 或 **陀螺仪比例车(RX)** 两类固件进行通讯。
两类设备通过不同的 **Company ID** 区分,APP 扫描时同时匹配两者,做到"**连上谁就显示谁、调试谁**"。
## 功能概述
### 通用
- 发送**非定向不可连接**BLE广播,发送后自动切换扫描接收固件回执
- 蓝牙状态实时检测,未开启时引导用户开启;Android 12+ 自动申请广播/连接/扫描权限
- **发现设备**:一键扫描,自动识别当前在线的是 FOC 还是陀螺车并切换面板
- **扫描超时机制**:单次3s / 持续模式每轮1.5s / 结束后5s
- **OTA远程升级**:基于Gitee Releases自动检测新版本并升级
### FOC 控制器(Company ID `0x0805`,旧式 5 字节帧)
- 数据格式:5字节 `[P, I, D, 方向, 计数器]`
- PID参数调节(滑块+数字输入,范围0-255)
- 方向控制(正向/反向开关)
- 单次发送 / 持续发送5秒
- 计数器每次发送自动+1(0-255循环),**相同数据自动跳过发送**
### 陀螺仪比例车(Company ID `0x0806`,标准 BLE 命令帧)
- 按 `params.h` 参数表**动态生成**全部参数行(名称/范围/分组/只读),无需硬编码 UI
- **大类分页**:连接 / 舵机 / 电机 / 陀螺 / 模式 五个 Tab,参数越多越好找(分组由参数表自动生成)
- 分组面板:系统能力 / 舵机位置环 / 速度环 / 前馈摩擦 / 限幅死区 / 电机 / **过流保护** / **前进陀螺PID** / **后退陀螺PID** / 陀螺行为 / 手感模式
- **前后双陀螺 PID**:前进(`GYRO_FWD_*`)与后退(`GYRO_REV_*`)各自独立整定,因车头/车尾向前时陀螺动作方向相反,参数可完全不同
- **转向旁路**(`GYRO_STEER_BYPASS`):有转向命令时暂时旁路陀螺、纯靠舵机转向,避免甩头
- **换向防抖**(`GYRO_SWITCH_ANTJIT`):正/反切换瞬间冻结陀螺输出再低通平滑切入,防止前后切换舵机抖动
- 逐参数"读/写","读取全部"自动顺序拉取;**PING** 读取固件版本与**能力位图**并在界面展示
- 保存到Flash / 恢复出厂 / **舵机自整定**(含进度查询)/ **固件升级(在线,不落盘)** / 退出调参
## 版本历史
### v1.4.1(当前版本)
- **OTA 协议 V2**(规范见 [`OTA_PROTOCOL_V2.md`](./OTA_PROTOCOL_V2.md)):
数据段 **17 B → 21 B**;帧头 `55 AA`/`SRC`/`CMD`/`SEQ` 全部并入 2 B 的 `CTRL`
(bit15=1 数据包带 PID,bit15=0 命令包);**CRC8 改为覆盖 CTRL(含 PID)** —— 以前 PID 传错会静默写到错误偏移
- **传输改用持续广播 + 持续扫描**(手机广播全程不 stop,换内容走 `setAdvertisingData()`):
没收到 ACK 就继续发同一帧 ⇒ 天然自动重传;设备收到重复帧只回 ACK 不重复写 ⇒ 天然去重。
三级自动降级:**L2** 多 advertising set 并行 + 块 ACK / **L1** 单 set / **L0** 兼容 API 21~25 老手机
- **握手 + 4 B 滚码认领**:`ENTER_OTA → READY(版本+滚码R) → BEGIN(回显R) → GO`,
多台车同时上电时 R 不匹配的设备静默退出不写 flash,防误刷
- **修三个老 bug**:① 收尾的 COMPLETE 帧以前只发一次不重试,传几百帧后最后一帧丢了全白干;
② 解密失败/密钥不匹配属永久错误,以前照样重发 6 次白等约 25 s,现在立即中止;
③ 新增 10 s 全局看门狗
- **修 P0 卡死**:OTA 期间收到**任何一个**同 CID 的无关包(旁边另一台车的调参回执就够),
会让该帧挂满 9 s × 6 次重试 = 54 秒。现在不匹配的包直接丢弃并继续扫描
- 构建环境修复:Gradle 由 9.4.1 回退到 **8.9**(AGP 8.3.2 与 Gradle 9.x 不兼容),已预置到本机缓存,构建不联网
- ⚠️ **不兼容提示**:V1 协议代码**已删除**,**老 V1 bootloader 的设备刷不了**。
新协议需固件同步支持(改法见 `OTA_PROTOCOL_V2.md` 第 8 章 checklist),
在固件配合前,本版 OTA 功能**无法实机验证**(握手就会超时),其余调参功能不受影响
### v1.4.0
- **AI 交接手册**:新增 [`AI_HANDOVER.md`](./AI_HANDOVER.md),换 AI 接手时先读它即可接着改
- **过流保护(OCP)**:新增参数组 —— 保护使能 / **最大保护电流** / 曲线起点 / **曲线形状**(线性·平方·开方) / 连续超限次数 / 起步屏蔽 / 恢复方式;"电机"页带**实时曲线预览图**
- **参数表同步**:补上固件 v1.3.2 已有的「陀螺零偏」(id42),APP 之前漏了
- **调试功能分类分页**:陀螺车面板改为 **连接 / 舵机 / 电机 / 陀螺 / 模式** 五个大类 Tab,分组与参数行全部按 `GyroParams.TABS` 动态生成,后续加参数不用再改界面
- **连接先读版本**:扫描到陀螺车后自动 PING 读取固件版本号并显示在「调试目标」卡片;**按版本号自动显示/隐藏参数**,老固件不会看到它不支持的新参数(版本兼容)
- **时序核对**:对齐固件回执重发窗(7×100ms),「读取全部」每条之间等待完整回执窗,并加超时重发/跳过 + 进度显示,不再卡死
- **界面改善**:参数行增加**数字输入框**(可直接键入数值,失焦/回车生效并自动夹取到范围);「写/读」→ 完整的「写入/读取」并修正截断;批量按钮统一防截断样式;Tab 中文不再被强制大写
- **在线固件升级(不落盘)**:「固件升级(在线)」点一下即联网检测仓库 `firmware.json` 最新版本 → 确认 → HTTPS 下载到**内存**(全程不写本地文件)→ 可选 sha256 完整性校验 → 本程序直接烧录;不再需要手动找固件文件,本地导入仅保留为网络异常时的备用
- 配套固件需按 `AI_HANDOVER.md` §8 追加 `P_MOTOR_OCP_*` 参数并把版本提到 v1.4.0
### v1.3.3
- **OTA 帧格式 24B 全兼容**:Android 传统广播 Manufacturer Data 上限 26B(含 CID),固件协议改为 24B 帧(6B 头+17B 数据+CRC8),任何手机均可收发,不再报 data too large
- **本地固件选择**:OTA 时先选文件再进 Bootloader,支持 `.bin`(裸固件)/ `.hex`(Intel HEX 自动解析)/ `.w`(加密容器),其它文件拒绝
- **Intel HEX 解析器**:校验和逐行校验、缺口填 0xFF、自动跳过 0x1800 前 boot 区、尾部 0xFF 截断,杜绝把文本刷进 Flash
- **OTA 触发顺序优化**:先选固件 → 设备重启进 Bootloader(5 秒窗口内)→ 立即 TRIGGER,不再受选文件超时影响
- 配套固件 bootloader:`OTA_FRAME_PHONE 27→24`(见 wBrushDrive 仓库 boot_config.h)
> 完整发版流程见 **[RELEASE_GUIDE.md](./RELEASE_GUIDE.md)**(AI 必读)。
### v1.3.2
- APP 版本进化(versionCode 6),承接 OTA 调优
### v1.3.1
- **APP 支持下发 `.w` 加密固件**:与 PC 端 `ota_encrypt.py`(XTEA-CTR + CRC32)配套,设备端 bootloader 解密写入
### v1.2.0
- **双设备支持**:同一 APP 既给 FOC 控制器(CID `0x0805`)调参,也给陀螺仪比例车(CID `0x0806`)调参
- **广播配对式发现**:扫描同时匹配两类 CID,"连上谁就显示谁",自动切换面板
- **陀螺车标准 BLE 调参**:新增 `GyroParams.kt` 命令帧(`[CMD][id:2 LE][val:2 LE][CRC8]`,CRC8 poly 0x07)与 `ParamRow` 动态参数行
- **前后双陀螺 PID**:前进 / 后退各一套独立陀螺 PID 参数,分别整定(动作相反,参数可完全不同)
- **转向旁路**:有转向命令时暂时旁路陀螺、纯靠舵机转向
- **换向防抖**:正/反切换冻结陀螺输出再低通平滑,避免前后切换舵机抖动
- **能力位图展示**:PING 后显示固件版本与能力(双环/自整定/双陀螺/转向旁路/换向防抖)
- 新增"读取全部 / 保存 / 恢复出厂 / 舵机自整定 / 查询自整定 / 进入OTA / 退出调参"批量操作
### v1.1.0
- 新增OTA远程升级功能,基于Gitee Releases API自动检测更新
- APP启动时自动检查新版本,支持"立刻升级"/"7天内不提醒"/"永不提醒"
- 标题栏显示当前版本号,新增"检查更新"按钮手动同步
- 新增BLE扫描模式:发送广播后自动切换到扫描,接收固件反馈
- 新增扫描超时机制(单次3s / 持续每轮1.5s / 结束后5s)
- 新增相同PID数据自动跳过发送功能
- 界面新增"固件反馈"卡片,显示扫描状态和收到的反馈数据
- 修复蓝牙开启后仍提示"设备不支持"的问题(advertiser改为惰性获取)
- 修复APP图标未显示自定义图片的问题(移除adaptive-icon覆盖)
### v1.0.0
- 初始版本
- BLE广播发送(Company ID: 0x0805,5字节数据包)
- PID参数调节(P/I/D滑块+数字输入,0-255)
- 方向控制开关
- 单次发送 / 持续发送5秒
- 蓝牙状态实时检测
- 计数器自增(0-255循环)
## 项目结构
```
BLEdebugAPP/
├── AI_HANDOVER.md # ★AI交接手册(换AI/接手必读,含协议/时序/参数表铁律)
├── README.md # 本文件
├── RELEASE_GUIDE.md # 发版与OTA操作手册(AI发版必读)
└── BleDebugApp/
├── app/
│ ├── src/main/
│ │ ├── java/com/ble/debugapp/
│ │ │ ├── BleAdvertiser.kt # BLE广播+扫描服务(协议无关,双 CID + 时序常量)
│ │ │ ├── MainActivity.kt # 主界面Activity(双设备编排 / Tab分页 / 版本兼容)
│ │ │ ├── GyroParams.kt # ★参数表真源 + 命令帧 + CRC8 + 版本编解码 + Tab定义
│ │ │ ├── ParamRow.kt # 动态参数行控件(滑块+数字输入框/开关/写入/读取)
│ │ │ ├── OcpCurveView.kt # 过流保护曲线预览(自定义View)
│ │ │ ├── FirmwareUpdater.kt # 固件OTA上传器(24B帧,逐帧ACK)
│ │ │ └── UpdateService.kt # OTA远程升级服务 + firmware.json 固件发现
│ │ ├── res/
│ │ │ ├── layout/
│ │ │ │ ├── activity_main.xml # 外壳(设备选择+固件版本+蓝牙状态+反馈卡片)
│ │ │ │ ├── panel_foc.xml # FOC PID 调参面板
│ │ │ │ ├── panel_gyro.xml # 陀螺车面板(TabLayout + 5个页容器,分组动态生成)
│ │ │ │ └── param_row.xml # 单参数行(含数字输入框)
│ │ │ ├── xml/
│ │ │ │ └── file_paths.xml # FileProvider路径配置
│ │ │ ├── mipmap-*/ # 各分辨率APP图标
│ │ │ └── values/
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── themes.xml # 含 Tab/批量按钮样式(防中文截断)
│ │ └── AndroidManifest.xml # 权限与组件声明
│ ├── build.gradle # 模块级构建配置(版本号在此改)
│ └── proguard-rules.pro
├── gradle/wrapper/
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties # Gradle版本配置
├── build.gradle # 项目级构建配置
├── settings.gradle
├── gradle.properties
├── local.properties # SDK路径(不入版本库)
├── gradlew / gradlew.bat # Gradle Wrapper脚本
└── 编译并复制到剪贴板.bat # 一键编译+压缩+复制脚本
```
## 编译方法
### 环境要求
- Android Studio 2024+
- JDK 17+(Android Studio自带)
- Android SDK(compileSdk 33,Build-Tools 34.0.0)
- Gradle 8.5 或 9.4.1(项目自带 Wrapper,当前指向 9.4.1;AGP 8.3.2 兼容两者)
### 命令行编译
```powershell
cd E:\project\X86X64\BLEdebugAPP\BleDebugApp
.\gradlew.bat assembleDebug # 编译Debug APK
.\gradlew.bat assembleRelease # 编译Release APK
```
如需指定SDK路径,设置环境变量:
```powershell
$env:ANDROID_HOME = "D:\Tool\androidSDK"
$env:GRADLE_USER_HOME = "C:\Users\administrator\.gradle"
.\gradlew.bat assembleDebug
```
### 一键编译打包脚本
双击运行 `编译并复制到剪贴板.bat`,脚本会自动:
1. 编译Debug APK
2. 复制并重命名为 `wFOC_Tool.apk`
3. 压缩为 `wFOC_Tool.zip`
4. 复制ZIP到系统剪贴板(可直接在微信Ctrl+V粘贴发送)
### Android Studio编译
1. 打开 Android Studio
2. File → Open → 选择 `BleDebugApp` 目录
3. 等待Gradle同步完成
4. Build → Build Bundle(s) / APK(s) → Build APK(s)
### APK输出路径
| 类型 | 路径 |
|------|------|
| Debug | `app/build/outputs/apk/debug/app-debug.apk` |
| Release | `app/build/outputs/apk/release/app-release-unsigned.apk` |
| 打包输出 | `_release/wFOC_Tool.apk` / `_release/wFOC_Tool.zip` |
## 使用方法
1. 将 `wFOC_Tool.apk` 传到手机安装
2. 打开APP,系统会请求蓝牙权限,点击允许
3. 如果蓝牙未开启,APP会弹窗提示,点击"开启蓝牙"
4. 调节P/I/D参数(拖动滑块或点击数字直接输入,范围0-255)
5. 选择方向(正向/反向)
6. 点击"发送广播"发送一次,或点击"持续发送5秒"连续发送
7. 界面显示当前发送的数据包(十六进制格式)和计数器值
8. 发送后APP自动切换到扫描模式,等待固件反馈
9. 反馈区域显示扫描状态(待机/扫描中/已收到/超时)和收到的数据
## 数据包格式
```
广播类型: 非定向不可连接广播 (Non-connectable Undirected)
Company ID: 0x0805
Manufacturer Data (5字节):
┌─────────┬─────────┬─────────┬──────────┬───────────┐
│ Byte 0 │ Byte 1 │ Byte 2 │ Byte 3 │ Byte 4 │
│ P │ I │ D │ 方向 │ 计数器 │
│ 0-255 │ 0-255 │ 0-255 │ 0x00/0x01 │ 0-255 │
└─────────┴─────────┴─────────┴──────────┴───────────┘
方向: 0x00 = 正向, 0x01 = 反向
计数器: 每次发送自动+1,溢出后从0重新开始
```
示例:P=50(0x32) I=20(0x14) D=10(0x0A) 正向 计数器=0
→ 广播数据: `32 14 0A 00 00`
## 陀螺仪比例车调参(Company ID `0x0806`)
### 数据包格式(标准 BLE Manufacturer Data)
命令 / 回执均放入 BLE 广播的 Manufacturer Specific Data(CID=`0x0806`)中,负载 6 字节:
```
0 1 2 3 4 5
+------+------+------+------+------+------+
| CMD | id_lo| id_hi| val_lo|val_hi| CRC8 |
+------+------+------+------+------+------+
```
- `CMD`:命令字(见下)
- `id`:参数 ID(uint16 小端),与固件 `params.h` 的 `ParamId` 顺序一致
- `val`:参数值(uint16 小端;有符号量以补码表示,如方向 `-1`=`0xFFFF`)
- `CRC8`:`poly=0x07, init=0`,覆盖字节 0..4
命令字:`0x10` WRITE / `0x11` READ / `0x12` SAVE / `0x13` FACTORY / `0x14` ENTER_OTA /
`0x15` EXIT / `0x16` PING / `0x17` AUTOTUNE / `0x18` AUTOTUNE_Q;
设备回执 `CMD=0x20 STATUS`,特殊 id:`0xFFFE`(ACK/版本) / `0xFFFD`(自整定进度=`(stage<<8)|progress`)。
> 完整协议见固件侧文档 `docs/ble_tuning_ota_protocol.md`。
### 使用流程
1. 小车**长按开关 5 秒**进入 BLE 调参模式(LED 爆闪),停在平稳处。
2. APP 点「**扫描并连接设备**」:识别到陀螺车后**自动 PING 读取固件版本号**并显示;
随后**按版本号自动显示该固件支持的参数**(版本兼容)。也可在「连接」页手动点「读取版本」。
界面同时展示**能力位图**(双环/自整定/双陀螺/转向旁路/换向防抖/过流保护是否支持)。
3. 点 **读取全部**:自动顺序拉取所有参数并显示在对应分组(带进度,单条超时自动重试/跳过)。
4. **首次建议先点「舵机自整定」**(自动辨识方向/最大转速/摩擦/惯性,写入 PID 与前馈初值并保存),再「读取全部」看辨识结果。
5. **前后双陀螺 PID 调法**:
- 车头向前行驶时整定 **前进陀螺 PID**(`GYRO_FWD_*`);车尾向前(倒车)时整定 **后退陀螺 PID**(`GYRO_REV_*`)。两者动作相反,参数可完全不同,分别微调。
- 调某组时把另一组先放着,互不干扰。
6. **转向旁路**(`GYRO_STEER_BYPASS=1`,默认开):只要拨杆有转向命令,陀螺即被旁路、纯舵机转向,避免转向叠加甩头。
7. **换向防抖**(`GYRO_SWITCH_ANTJIT`,默认 30):正/反切换瞬间固件冻结陀螺输出再低通平滑切入;若切换仍有舵机抖动,调大此值(0=关,建议 20~40)。
8. 改完点 **保存到Flash** 固化;想重来点 **恢复出厂**。
9. 需要刷固件时点「连接」页的 **固件升级(在线)**:自动联网检测仓库 `firmware.json`
里的最新固件版本 → 弹窗核对版本/加密/校验信息 → 确认后 HTTPS **下载到内存**(不落盘,
全程本地不出固件文件)→ 设备重启进 Bootloader → 本程序自动逐页烧录。
网络不可用时会提示改用本地固件文件(.w/.bin/.hex)作为备用。
> **发布方请把固件传到仓库并更新 `firmware.json`**,格式见 `AI_HANDOVER.md` §3.5。
> 建议发布 **`.w` 加密容器**(XTEA-CTR)+ `sha256`,明文固件只在设备端解密,保密性最好。
### 过流保护(OCP,固件 v1.4.0 起)
保护电流随油门占空比线性变化,"电机"页可调两头 + 曲线形状,并实时预览整条曲线:
```
I_lim(duty%) = 曲线起点 + (最大保护电流 − 曲线起点) × f(duty/100)
f = 线性 x | 缓起(平方) x² | 急起(开方) √x
```
| 参数 | 含义 |
|------|------|
| 过流保护使能 | 0=关 1=开(建议开) |
| 最大保护电流 | 满占空比时的保护值,0.1A 编码,硬顶 40.0A(默认 20.0A) |
| 保护曲线起点 | 小占空比时的保护值,必须小于最大值(默认 6.0A) |
| 保护曲线形状 | 线性 / 缓起(平方) / 急起(开方) |
| 连续超限次数 | 连续 N 个采样窗超限才动作,躲起步浪涌、防误保护(默认 3) |
| 起步屏蔽时间 | 起步/换向后的屏蔽窗,窗内只告警不动作(默认 150ms) |
| 保护恢复方式 | 锁存(松油门恢复) / 自动恢复 |
> 调参口诀:**先给"最大保护电流"留出电机峰值余量,再用"曲线起点"压小油门堵转**;
> 出现"负载稍大就停"就把「连续超限次数」调大或拉高「曲线起点」;出现烧 MOS 风险则反向收紧。
## 发版与OTA升级
### 发版流程
每次程序有新功能更新后,按以下步骤发布新版本:
#### 第1步:更新版本号
修改 `app/build.gradle` 中的版本号:
```groovy
defaultConfig {
versionCode 3 // 每次发版+1(整数递增)
versionName "1.2.0" // 语义化版本号
}
```
#### 第2步:更新README版本历史
在本文档「版本历史」章节顶部添加新版本记录,格式:
```markdown
### vX.Y.Z
- 新增功能A
- 修复问题B
- 优化性能C
```
#### 第3步:编译APK
```powershell
cd E:\project\X86X64\BLEdebugAPP\BleDebugApp
.\gradlew.bat clean assembleDebug
```
或双击 `编译并复制到剪贴板.bat` 一键编译打包。
#### 第4步:在Gitee创建Release
1. 登录 [Gitee](https://gitee.com) → 进入 `wflwang/bledebug` 仓库
2. 点击「管理」→「Releases」→「创建新 Release」
3. 填写以下信息:
| 字段 | 填写内容 | 示例 |
|------|----------|------|
| Tag name | 版本号(加v前缀) | `v1.2.0` |
| Release title | 版本标题 | `wFOC Tool v1.2.0` |
| Release notes | 更新内容说明 | 见下方示例 |
| 附件 | 上传编译好的APK | `wFOC_Tool.apk` |
4. Release notes 示例:
```
## 新功能
- 新增XXX功能
- 新增YYY选项
## 修复
- 修复ZZZ问题
## 优化
- 优化AAA性能
```
5. 点击「发布」完成
#### 第5步:验证OTA
在已安装旧版本的手机上打开APP:
- APP启动时会自动检测到新版本
- 弹出对话框显示当前版本和最新版本
- 点击"立刻升级"即可自动下载安装
### OTA工作原理
```
APP启动 / 用户点击"检查更新"
→ 调用 Gitee API: https://gitee.com/api/v5/repos/wflwang/bledebug/releases/latest
→ 获取最新 Release 的 tag_name(版本号)和 assets(APK下载地址)
→ 比较版本号:latest > current ?
→ 是: 弹出升级对话框
→ 用户选择"立刻升级"
→ 下载APK(显示进度条)
→ 下载完成 → 触发系统安装界面
→ 用户选择"7天内不提醒" → 记录跳过时间
→ 用户选择"永不提醒" → 记录永久跳过
→ 否: 提示"当前已是最新版本"
```
### 注意事项
- Tag name 必须比当前版本号大(如当前 v1.1.0,新版本至少 v1.1.1 或 v1.2.0)
- Release 附件必须是 `.apk` 文件
- 仓库地址:`https://gitee.com/wflwang/bledebug`
- 首次通过OTA安装时,手机会提示"安装未知应用"权限,需允许
- 用户选择"永不提醒"后,仍可点击右上角"检查更新"手动检测
## 代码解读
### BleAdvertiser.kt — BLE广播+扫描服务
核心类,封装了Android `BluetoothLeAdvertiser` 和 `BluetoothLeScanner` API。
**广播发送流程(协议无关,双 CID)**:
```kotlin
// FOC:5字节数据包,Company ID = 0x0805
bleAdvertiser.sendOnce(BleAdvertiser.FOC_CID, buildFocPacket())
// 陀螺车:标准 BLE 命令帧,Company ID = 0x0806
bleAdvertiser.sendOnce(BleAdvertiser.GYRO_CID,
GyroParams.buildFrame(GyroParams.CMD_WRITE, id, value))
// 内部配置广播参数:低延迟、不可连接、高功率
val settings = AdvertiseSettings.Builder()
.setConnectable(false)
.setAdvertiseMode(ADVERTISE_MODE_LOW_LATENCY)
.setTxPowerLevel(ADVERTISE_TX_POWER_HIGH)
.build()
// Manufacturer Data 携带 companyId + 负载(FOC=0x0805 / 陀螺=0x0806)
val data = AdvertiseData.Builder()
.addManufacturerData(companyId, payload)
.build()
advertiser.startAdvertising(settings, data, advertiseCallback)
// 500ms后停止广播 → 切换到扫描模式
handler.postDelayed({ stopAdvertisingInternal(); startScan(timeoutMs) }, 500)
```
**关键设计**:
- `sendOnce(companyId, payload)`:发送单包 → 500ms后停止 → 扫描反馈3s
- `sendContinuous(companyId, payload, durationMs)`:循环 [广播500ms → 扫描1.5s] → 结束后最终扫描5s
- `discoverDevices(timeoutMs, cb)`:纯扫描收集出现过的 CID 集合,实现"连上谁调试谁"
- 扫描回调同时匹配 `0x0805` / `0x0806`,并在 `onFeedbackReceived(companyId, data)` 中回传实际命中的 CID
- `clearAllCallbacks()`:统一清理所有延迟任务,防止泄漏
### UpdateService.kt — OTA远程升级服务
通过Gitee Releases API检查和下载更新。
**核心方法**:
- `checkUpdate()`:调用Gitee API获取最新Release,比较版本号
- `downloadAndInstall()`:下载APK文件,通过FileProvider触发系统安装
- `shouldCheckUpdate()`:检查是否应该提示更新(尊重"7天跳过"和"永不提醒")
- `setSkip7Days()` / `setNeverRemind()`:记录用户选择到SharedPreferences
- `isNewerVersion()`:语义化版本号比较(如 1.2.0 > 1.1.0)
### MainActivity.kt — 主界面
管理UI交互和状态,主要职责:
1. **权限处理**:Android 12+需请求 `BLUETOOTH_ADVERTISE`、`BLUETOOTH_CONNECT`、`BLUETOOTH_SCAN` 权限;低版本需 `ACCESS_FINE_LOCATION`
2. **蓝牙状态检测**:`onResume()` 时检查蓝牙是否开启,未开启显示警告卡片
3. **PID参数联动**:滑块和输入框双向绑定,值变化时实时更新数据包预览
4. **发送控制**:点击发送 → 检查权限 → 检查蓝牙 → 检查数据变化 → 发送广播 → 扫描反馈
5. **OTA更新**:启动时自动检查,手动按钮强制检查,下载进度显示
6. **状态回调**:通过回调更新UI(广播状态/扫描状态/反馈数据/超时)
### activity_main.xml — 界面布局
使用 Material Design 3 卡片式布局:
- 标题栏(APP名称 + 版本号 + 检查更新按钮)
- 蓝牙状态卡片(含开启蓝牙按钮)
- P/I/D 参数卡片(滑块+输入框)
- 方向控制卡片(MaterialSwitch开关)
- 发送数据包卡片(等宽字体显示十六进制)
- 固件反馈卡片(扫描状态 + 反馈数据)
- 操作按钮区(发送/持续发送5秒/停止)
### AndroidManifest.xml — 权限配置
```xml
```
FileProvider用于安全分享下载的APK文件给系统安装器:
```xml
```
## 技术栈
| 组件 | 版本 |
|------|------|
| 语言 | Kotlin |
| Android Gradle Plugin | 8.3.2 |
| Gradle | 8.5(如分发包不可用,已验证可改用 9.4.1;AGP 8.3.2 兼容 8.4+ 与 9.x) |
| compileSdk / targetSdk | 33 |
| minSdk | 21 (Android 5.0) |
| Material Components | 1.9.0 |
| AndroidX AppCompat | 1.6.1 |
| AndroidX Core KTX | 1.10.1 |