perf(ble): optimize reconnect speed to ~3s, fix ATVV voice pipeline and UAC control flow

This commit is contained in:
cuicuiV5 committed 2026-08-31 14:37:59 +08:00
1 parent 59d378ed42
commit c24c31bca3
3 files changed
+53 -31

No files matched your search

+2 -2
View File
@@ -30,8 +30,8 @@ extern "C" {
// ==========================================
#define BLE_REMOTE_NAME_PREFIX "MI RC"
#define BLE_REMOTE_MAC_PREFIX "c0:5d:39"
#define BLE_SCAN_INTERVAL_MS 100
#define BLE_SCAN_WINDOW_MS 80
#define BLE_SCAN_INTERVAL_MS 30
#define BLE_SCAN_WINDOW_MS 30
#define BLE_SILENCE_WATCHDOG_MS 450 // Voice key release watchdog timeout (ms)
#define BLE_KEEP_ALIVE_INTERVAL 2000 // MIC_EXTEND interval during active voice (ms)
+47 -28
View File
@@ -99,9 +99,12 @@ static uint8_t s_last_hogp_key = 0;
static void on_hogp_report_notify(NimBLERemoteCharacteristic* pChar, uint8_t* pData, size_t length, bool isNotify) {
if (length < 1) return;
// 1. Non-keyboard packets (len > 8, e.g. vendor audio/debug reports)
// 1. Audio / Voice Packets (len > 8, e.g. 20, 120, 240 bytes ADPCM)
if (length > 8) {
return; // NEVER process non-keyboard frames as keys!
app_log("HOGP_AUD", "Voice frame len=%d from Char %s", (int)length, pChar->getUUID().toString().c_str());
s_last_audio_ms = millis();
audio_pipeline_feed_adpcm(&g_audio_pipeline, pData, length);
return;
}
// Dump raw bytes for diagnostics (only for genuine key reports len <= 8)
@@ -173,6 +176,14 @@ static void on_hogp_report_notify(NimBLERemoteCharacteristic* pChar, uint8_t* pD
if (raw_key != 0) {
app_log("HOGP", "Key event: 0x%02X (%s)", raw_key, is_pressed ? "DOWN" : "UP");
if (raw_key == MI_KEY_VOICE || raw_key == MI_KEY_VOICE_ALT) {
app_log("VOICE", "Voice button event: 0x%02X (%s)", raw_key, is_pressed ? "DOWN" : "UP");
if (s_char_cmd != nullptr && is_pressed) {
uint8_t cmd_open[] = { 0x0C, 0x00 };
s_char_cmd->writeValue(cmd_open, sizeof(cmd_open), false);
app_log("ATVV", "Triggered MIC_OPEN on Voice key press");
}
}
key_engine_feed_key(&g_key_engine, raw_key, is_pressed, millis());
}
}
@@ -297,7 +308,11 @@ static bool setup_services_and_handshake() {
// 1. Security & Bonding
s_client->secureConnection();
// 2. Discover all services and characteristics
// 2. Fast connection parameters (15ms interval)
s_client->setDataLen(251);
s_client->updateConnParams(12, 12, 0, 400);
// 3. Discover services
std::vector<NimBLERemoteService*>* pServices = s_client->getServices(true);
if (!pServices) {
app_log("BLE", "No GATT services found");
@@ -309,40 +324,51 @@ static bool setup_services_and_handshake() {
int sub_count = 0;
for (auto* pSvc : *pServices) {
String svc_uuid = pSvc->getUUID().toString().c_str();
app_log("BLE", "Service: %s", svc_uuid.c_str());
svc_uuid.toLowerCase();
app_log("GATT_SVC", "Service: %s", svc_uuid.c_str());
// Skip known standard BLE metadata services (0x1800 GAP, 0x1801 GATT, 0x180A DIS, 0x180F Battery)
if (svc_uuid.indexOf("1800") >= 0 || svc_uuid.indexOf("1801") >= 0 ||
svc_uuid.indexOf("180a") >= 0 || svc_uuid.indexOf("180f") >= 0) {
continue;
}
std::vector<NimBLERemoteCharacteristic*>* pChars = pSvc->getCharacteristics(true);
if (!pChars) continue;
for (auto* pChar : *pChars) {
String char_uuid = pChar->getUUID().toString().c_str();
char_uuid.toLowerCase();
bool can_notif = pChar->canNotify();
bool can_ind = pChar->canIndicate();
bool can_wr = pChar->canWrite() || pChar->canWriteNoResponse();
app_log("BLE", " Char: %s (Notify:%d, Indicate:%d, Write:%d)", char_uuid.c_str(), can_notif ? 1 : 0, can_ind ? 1 : 0, can_wr ? 1 : 0);
app_log("GATT_CHAR", " Char: %s (N:%d, I:%d, W:%d)", char_uuid.c_str(), can_notif ? 1 : 0, can_ind ? 1 : 0, can_wr ? 1 : 0);
// Match ATVV CMD (0xab5e0002)
if (pChar->getUUID().equals(NimBLEUUID(ATVV_CHAR_CMD_UUID))) {
// Match ATVV CMD (ab5e0002)
if (char_uuid.indexOf("ab5e0002") >= 0) {
s_char_cmd = pChar;
app_log("ATVV", "Matched ATVV CMD Char: %s", char_uuid.c_str());
}
// Match ATVV AUD (0xab5e0003)
else if (pChar->getUUID().equals(NimBLEUUID(ATVV_CHAR_AUD_UUID))) {
// Match ATVV AUD (ab5e0003)
else if (char_uuid.indexOf("ab5e0003") >= 0) {
s_char_aud = pChar;
if (can_notif) {
pChar->subscribe(true, on_audio_notify);
pChar->subscribe(true, on_audio_notify, false);
sub_count++;
app_log("ATVV", "Subscribed to ATVV AUD Char: %s", char_uuid.c_str());
}
}
// Match ATVV CTL (0xab5e0004)
else if (pChar->getUUID().equals(NimBLEUUID(ATVV_CHAR_CTL_UUID))) {
// Match ATVV CTL (ab5e0004)
else if (char_uuid.indexOf("ab5e0004") >= 0) {
s_char_ctl = pChar;
if (can_notif) {
pChar->subscribe(true, on_ctl_notify);
pChar->subscribe(true, on_ctl_notify, false);
sub_count++;
app_log("ATVV", "Subscribed to ATVV CTL Char: %s", char_uuid.c_str());
}
}
// Match Protocol Mode (0x2A4E) -> write Report Mode (0x01)
else if (pChar->getUUID().equals(NimBLEUUID((uint16_t)0x2A4E))) {
else if (char_uuid.indexOf("2a4e") >= 0) {
if (can_wr) {
uint8_t mode = 0x01;
pChar->writeValue(&mode, 1, false);
@@ -350,17 +376,16 @@ static bool setup_services_and_handshake() {
}
}
// Match HID Control Point (0x2A4C) -> write Exit Suspend (0x00)
else if (pChar->getUUID().equals(NimBLEUUID((uint16_t)0x2A4C))) {
else if (char_uuid.indexOf("2a4c") >= 0) {
if (can_wr) {
uint8_t cp = 0x00;
pChar->writeValue(&cp, 1, false);
}
}
// Match HOGP Report (0x2A4D) or any notify char in 0x1812 service
else if (pChar->getUUID().equals(NimBLEUUID((uint16_t)0x2A4D)) ||
pSvc->getUUID().equals(NimBLEUUID((uint16_t)0x1812))) {
else if (char_uuid.indexOf("2a4d") >= 0 || svc_uuid.indexOf("1812") >= 0) {
if (can_notif || can_ind) {
pChar->subscribe(true, on_hogp_report_notify);
pChar->subscribe(true, on_hogp_report_notify, false);
sub_count++;
app_log("HOGP", "Subscribed to Report Char: %s", char_uuid.c_str());
}
@@ -370,11 +395,11 @@ static bool setup_services_and_handshake() {
app_log("BLE", "Total Subscribed Characteristic(s): %d", sub_count);
// 3. ATVV Handshake
// 4. ATVV Handshake
if (s_char_cmd) {
uint8_t cmd_caps[] = { 0x0A, 0x01, 0x00, 0x00, 0x03, 0x03 };
s_char_cmd->writeValue(cmd_caps, sizeof(cmd_caps), false);
delay(150);
vTaskDelay(pdMS_TO_TICKS(20));
uint8_t cmd_open[] = { 0x0C, 0x00 };
s_char_cmd->writeValue(cmd_open, sizeof(cmd_open), false);
app_log("ATVV", "Handshake completed (MIC_OPEN active)");
@@ -489,14 +514,8 @@ void ble_remote_init(void) {
NimBLEScan* pScan = NimBLEDevice::getScan();
pScan->setAdvertisedDeviceCallbacks(new AdvertisedDeviceCallbacks());
// If we have a saved bound remote, attempt proactive direct connect, otherwise start scan
if (s_bound_mac.length() > 0) {
s_pending_mac = s_bound_mac;
s_pending_addr_type = s_bound_addr_type;
s_do_connect = true;
} else {
start_scan();
}
// Start continuous fast scan immediately (instant catch when remote advertises)
start_scan();
}
void ble_remote_task(void) {
+4 -1
View File
@@ -203,7 +203,10 @@ static bool uac_driver_control_xfer_cb(uint8_t rhport, uint8_t stage,
if (cs == 0x01) return tud_control_xfer(rhport, req, &s_mic_mute, 1);
else return tud_control_xfer(rhport, req, &s_mic_volume, 2);
}
if (r == 0x01) return tud_control_status(rhport, req); // SET_CUR
if (r == 0x01) { // SET_CUR
if (cs == 0x01) return tud_control_xfer(rhport, req, &s_mic_mute, 1);
else return tud_control_xfer(rhport, req, &s_mic_volume, 2);
}
static int16_t vol_min = -32768, vol_max = 0, vol_res = 256;
if (r == 0x82) return tud_control_xfer(rhport, req, &vol_min, 2);
if (r == 0x83) return tud_control_xfer(rhport, req, &vol_max, 2);