Fix event system init and add SSP auto-accept for E2E testing
Two fixes for the E2E test failures: 1. event_reporter_init() was never called in app_main(), so the FreeRTOS queue and reporter task were never created. Every BT event (pair_request, gatt_read, gatt_write, gatt_subscribe) was silently dropped at the NULL-queue guard. 2. SSP Numeric Comparison requires both sides to confirm, but bt_pair blocks until completion — creating a deadlock since the LLM can't send classic_pair_respond while waiting. Added auto_accept flag to set_ssp_mode that auto-confirms numeric comparison requests in the GAP callback.
This commit is contained in:
parent
397b164eee
commit
5a853c15fc
@ -46,6 +46,7 @@ static struct {
|
|||||||
esp_bt_io_cap_t io_cap;
|
esp_bt_io_cap_t io_cap;
|
||||||
char pin_code[17];
|
char pin_code[17];
|
||||||
bool ssp_enabled;
|
bool ssp_enabled;
|
||||||
|
bool auto_accept; /* Auto-confirm SSP pairing (for testing) */
|
||||||
/* Pending pairing state */
|
/* Pending pairing state */
|
||||||
char pending_pair_address[18];
|
char pending_pair_address[18];
|
||||||
char pending_pair_cmd_id[32];
|
char pending_pair_cmd_id[32];
|
||||||
@ -132,10 +133,16 @@ static void gap_cb(esp_bt_gap_cb_event_t event, esp_bt_gap_cb_param_t *param)
|
|||||||
|
|
||||||
event_report_pair_request(addr_str, "numeric_comparison", (int)passkey);
|
event_report_pair_request(addr_str, "numeric_comparison", (int)passkey);
|
||||||
|
|
||||||
|
if (classic_state.auto_accept) {
|
||||||
|
/* Auto-confirm for automated E2E testing */
|
||||||
|
esp_bt_gap_ssp_confirm_reply(param->cfm_req.bda, true);
|
||||||
|
ESP_LOGI(TAG, "cfm_req: auto-accepted (auto_accept=true)");
|
||||||
|
} else {
|
||||||
/* Stash address so pair_respond can reply */
|
/* Stash address so pair_respond can reply */
|
||||||
strncpy(classic_state.pending_pair_address, addr_str,
|
strncpy(classic_state.pending_pair_address, addr_str,
|
||||||
sizeof(classic_state.pending_pair_address) - 1);
|
sizeof(classic_state.pending_pair_address) - 1);
|
||||||
classic_state.pair_type = PAIR_TYPE_NUMERIC_COMPARISON;
|
classic_state.pair_type = PAIR_TYPE_NUMERIC_COMPARISON;
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -575,11 +582,19 @@ void cmd_classic_set_ssp_mode(const char *id, cJSON *params)
|
|||||||
esp_bt_gap_set_security_param(ESP_BT_SP_IOCAP_MODE, &iocap,
|
esp_bt_gap_set_security_param(ESP_BT_SP_IOCAP_MODE, &iocap,
|
||||||
sizeof(iocap));
|
sizeof(iocap));
|
||||||
|
|
||||||
ESP_LOGI(TAG, "SSP mode set to '%s' (io_cap=%d)", mode, new_cap);
|
/* Optional auto_accept flag for automated testing */
|
||||||
|
const cJSON *j_auto = cJSON_GetObjectItem(params, "auto_accept");
|
||||||
|
if (cJSON_IsBool(j_auto)) {
|
||||||
|
classic_state.auto_accept = cJSON_IsTrue(j_auto);
|
||||||
|
}
|
||||||
|
|
||||||
|
ESP_LOGI(TAG, "SSP mode set to '%s' (io_cap=%d, auto_accept=%d)",
|
||||||
|
mode, new_cap, classic_state.auto_accept);
|
||||||
|
|
||||||
cJSON *data = cJSON_CreateObject();
|
cJSON *data = cJSON_CreateObject();
|
||||||
cJSON_AddStringToObject(data, "mode", mode);
|
cJSON_AddStringToObject(data, "mode", mode);
|
||||||
cJSON_AddNumberToObject(data, "io_cap", new_cap);
|
cJSON_AddNumberToObject(data, "io_cap", new_cap);
|
||||||
|
cJSON_AddBoolToObject(data, "auto_accept", classic_state.auto_accept);
|
||||||
uart_send_response(id, STATUS_OK, data);
|
uart_send_response(id, STATUS_OK, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#include "protocol.h"
|
#include "protocol.h"
|
||||||
#include "uart_handler.h"
|
#include "uart_handler.h"
|
||||||
|
#include "event_reporter.h"
|
||||||
#include "cmd_dispatcher.h"
|
#include "cmd_dispatcher.h"
|
||||||
|
|
||||||
#include "nvs_flash.h"
|
#include "nvs_flash.h"
|
||||||
@ -57,6 +58,7 @@ void app_main(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
uart_handler_init();
|
uart_handler_init();
|
||||||
|
event_reporter_init();
|
||||||
send_boot_event();
|
send_boot_event();
|
||||||
cmd_dispatcher_init();
|
cmd_dispatcher_init();
|
||||||
|
|
||||||
|
|||||||
@ -74,7 +74,10 @@ def register_tools(mcp: FastMCP) -> None:
|
|||||||
return {"error": str(e)}
|
return {"error": str(e)}
|
||||||
|
|
||||||
@mcp.tool()
|
@mcp.tool()
|
||||||
async def esp32_set_ssp_mode(mode: str) -> dict[str, Any]:
|
async def esp32_set_ssp_mode(
|
||||||
|
mode: str,
|
||||||
|
auto_accept: bool = False,
|
||||||
|
) -> dict[str, Any]:
|
||||||
"""Set the Secure Simple Pairing (SSP) mode on the ESP32.
|
"""Set the Secure Simple Pairing (SSP) mode on the ESP32.
|
||||||
|
|
||||||
SSP mode determines which pairing association model the ESP32
|
SSP mode determines which pairing association model the ESP32
|
||||||
@ -90,6 +93,11 @@ def register_tools(mcp: FastMCP) -> None:
|
|||||||
io_cap).
|
io_cap).
|
||||||
"passkey_display" — ESP32 displays a passkey for the
|
"passkey_display" — ESP32 displays a passkey for the
|
||||||
remote device to enter.
|
remote device to enter.
|
||||||
|
auto_accept: When true, the ESP32 automatically confirms
|
||||||
|
numeric comparison pairing requests without waiting for
|
||||||
|
a classic_pair_respond command. Useful for automated
|
||||||
|
E2E testing where both sides need to confirm but the
|
||||||
|
MCP tool calls are sequential.
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
Response data confirming the new SSP mode, or an error dict
|
Response data confirming the new SSP mode, or an error dict
|
||||||
@ -99,9 +107,13 @@ def register_tools(mcp: FastMCP) -> None:
|
|||||||
if mode not in valid_modes:
|
if mode not in valid_modes:
|
||||||
return {"error": f"Invalid SSP mode '{mode}'. Must be one of: {sorted(valid_modes)}"}
|
return {"error": f"Invalid SSP mode '{mode}'. Must be one of: {sorted(valid_modes)}"}
|
||||||
|
|
||||||
|
params: dict[str, Any] = {"mode": mode}
|
||||||
|
if auto_accept:
|
||||||
|
params["auto_accept"] = True
|
||||||
|
|
||||||
try:
|
try:
|
||||||
client = get_client()
|
client = get_client()
|
||||||
resp = await client.send_command(CMD_CLASSIC_SET_SSP_MODE, {"mode": mode})
|
resp = await client.send_command(CMD_CLASSIC_SET_SSP_MODE, params)
|
||||||
if resp.status == Status.OK:
|
if resp.status == Status.OK:
|
||||||
return resp.data
|
return resp.data
|
||||||
return {"error": resp.data.get("error", "Failed to set SSP mode")}
|
return {"error": resp.data.get("error", "Failed to set SSP mode")}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user