|
|
|
@ -27,7 +27,7 @@ import java.util.concurrent.RejectedExecutionException |
|
|
|
* Echo-one 面对面的 **BLE 双耳链路**(协议见 docs/face_to_face_ble_protocol.md v0.4)。 |
|
|
|
* |
|
|
|
* 时序(App → 耳机全是 AA 帧头): |
|
|
|
* ① SPP 发一帧 `AA 19 06 02 01 01` 切 BLE 链路 → 左右耳各自发 BLE 广播(**`Echo-one-L` / `Echo-one-R`**); |
|
|
|
* ① SPP 发一帧 `AA 19 06 02 01 01` 切 BLE 链路 → 左右耳各自发 BLE 广播(`Echo-one-L/R` 或 `Echo-translation-L/R`); |
|
|
|
* ② App 扫到两只、各建一条 GATT(MTU + 开通知); |
|
|
|
* ③ 两耳都就绪后经 BLE 给两耳发 `AA 66` 启动面对面; |
|
|
|
* ④ 左耳收音 → 译文播右耳,右耳收音 → 译文播左耳; |
|
|
|
@ -35,7 +35,7 @@ import java.util.concurrent.RejectedExecutionException |
|
|
|
* Dart 仍只发 `AA 66` / `AA 67`,由 BluetoothManager.writeBytes 转给 [start] / [requestStop] 编排。 |
|
|
|
* |
|
|
|
* 细节: |
|
|
|
* - **集齐一左一右再连**:扫描 [SCAN_TIMEOUT_MS] 内要扫到 [LEFT_NAME]、[RIGHT_NAME] 各一个广播,只扫到一侧/一个都没有 |
|
|
|
* - **集齐一左一右再连**:扫描 [SCAN_TIMEOUT_MS] 内要扫到左耳名、右耳名各一个广播,只扫到一侧/一个都没有 |
|
|
|
* 判失败回滚;同侧的第二个广播(附近别的 Echo-one)忽略;名字写死,`-L` = 左耳 = leg A,其它名字一律不认; |
|
|
|
* - 两条 GATT 同时连,[CONNECT_TIMEOUT_MS] 内两耳都要就绪(MTU + 开通知),否则失败回滚; |
|
|
|
* - 服务 00001100-d102-…,可写特征 / 通知特征按 properties 挑(真机 1101 可写、1102 notify); |
|
|
|
@ -79,9 +79,9 @@ object F2fBleLink { |
|
|
|
private const val UPLINK_SILENT_MS = 1_500L |
|
|
|
private const val SILENCE_CHECK_MS = 500L |
|
|
|
|
|
|
|
/** 固件在面对面里的左右耳广播名(写死,只认这两个) */ |
|
|
|
private const val LEFT_NAME = "Echo-one-L" |
|
|
|
private const val RIGHT_NAME = "Echo-one-R" |
|
|
|
/** 面对面左右耳广播名。Echo-one 与同协议的 Echo-translation 都认,`-L` 左耳 / `-R` 右耳。 */ |
|
|
|
private val LEFT_NAMES = listOf("Echo-one-L", "Echo-translation-L") |
|
|
|
private val RIGHT_NAMES = listOf("Echo-one-R", "Echo-translation-R") |
|
|
|
private const val REQUEST_MTU = 247 |
|
|
|
/** 建链阶段单耳 GATT 断开的重试次数 */ |
|
|
|
private const val GATT_RETRIES = 3 |
|
|
|
@ -655,7 +655,7 @@ object F2fBleLink { |
|
|
|
if (candidates.containsKey(addr)) return |
|
|
|
val side = sideOf(name) |
|
|
|
if (side == null) { |
|
|
|
if (firstSeen) Log.w(TAG, "广播 $addr '$name' 不是 $LEFT_NAME / $RIGHT_NAME,忽略") |
|
|
|
if (firstSeen) Log.w(TAG, "广播 $addr '$name' 不是 Echo-one / Echo-translation 的左右耳名,忽略") |
|
|
|
return |
|
|
|
} |
|
|
|
// 已经有同侧的了:多半是旁边另一副 Echo-one,别凑成两只左耳 |
|
|
|
@ -708,12 +708,12 @@ object F2fBleLink { |
|
|
|
handler.postDelayed(connectDeadline, CONNECT_TIMEOUT_MS) |
|
|
|
} |
|
|
|
|
|
|
|
/** 广播名 → 左右:只认 [LEFT_NAME] / [RIGHT_NAME](忽略大小写和首尾空白),其它返回 null */ |
|
|
|
/** 广播名 → 左右:只认 Echo-one / Echo-translation 的 `-L` / `-R`(忽略大小写和首尾空白)。 */ |
|
|
|
private fun sideOf(name: String): String? { |
|
|
|
val n = name.trim { it.isWhitespace() || it == '\u0000' } |
|
|
|
return when { |
|
|
|
n.equals(LEFT_NAME, ignoreCase = true) -> "L" |
|
|
|
n.equals(RIGHT_NAME, ignoreCase = true) -> "R" |
|
|
|
LEFT_NAMES.any { n.equals(it, ignoreCase = true) } -> "L" |
|
|
|
RIGHT_NAMES.any { n.equals(it, ignoreCase = true) } -> "R" |
|
|
|
else -> null |
|
|
|
} |
|
|
|
} |
|
|
|
|