From 88d006e9c4cfa89e86786acd44f23975bf2c5296 Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Tue, 3 Feb 2026 11:18:37 -0700 Subject: [PATCH] Add automated E2E testing documentation and test prompts - docs/automated-e2e-testing.md: Guide for running headless Claude CLI tests with both mcbluetooth and mcbluetooth-esp32 MCP servers - tests/prompts/test-prompt-v4.md: 71-test suite covering Classic BT, BLE GATT, HCI capture, device management - tests/prompts/test-prompt-v5.md: 76-test suite adding Battery Service (0x180F) and bt_ble_battery verification Test results from v4: 71/71 PASS with 143 HCI packets captured --- docs/automated-e2e-testing.md | 287 ++++++++++++++++++++++++++++++++ tests/prompts/README.md | 29 ++++ tests/prompts/test-prompt-v4.md | 153 +++++++++++++++++ tests/prompts/test-prompt-v5.md | 170 +++++++++++++++++++ 4 files changed, 639 insertions(+) create mode 100644 docs/automated-e2e-testing.md create mode 100644 tests/prompts/README.md create mode 100644 tests/prompts/test-prompt-v4.md create mode 100644 tests/prompts/test-prompt-v5.md diff --git a/docs/automated-e2e-testing.md b/docs/automated-e2e-testing.md new file mode 100644 index 0000000..7dd76f3 --- /dev/null +++ b/docs/automated-e2e-testing.md @@ -0,0 +1,287 @@ +# Automated E2E Testing with Claude CLI + +This document describes how to run fully automated end-to-end Bluetooth tests using the Claude CLI in headless mode. The tests exercise the complete Bluetooth stack across two devices: a Linux host running `mcbluetooth` (BlueZ) and an ESP32 running the `mcbluetooth-esp32` firmware. + +## Architecture + +``` +┌───────────────────────────────────────────────────────────────────┐ +│ Claude CLI (headless mode) │ +│ Orchestrates both MCP servers │ +└───────────────────────────┬───────────────────────────────────────┘ + │ + ┌───────────────┴───────────────┐ + │ │ + ┌───────┴───────┐ ┌───────┴───────┐ + │ mcbluetooth │ │mcbluetooth-esp32│ + │ MCP Server │ │ MCP Server │ + │ (bt_* tools)│ │ (esp32_* tools)│ + └───────┬───────┘ └───────┬────────┘ + │ │ + D-Bus/BlueZ Serial/UART + │ │ + ┌───────┴───────┐ ┌───────┴────────┐ + │ Linux Host │◄── Bluetooth ──►│ ESP32 │ + │ (hci1) │ (over air) │ (peripheral) │ + └───────────────┘ └────────────────┘ +``` + +## Prerequisites + +### Hardware +- ESP32 dev board connected via USB (typically `/dev/ttyUSB0` or `/dev/ttyUSB4`) +- Linux host with Bluetooth adapter (typically `hci0` or `hci1`) + +### Software +- ESP32 flashed with mcbluetooth-esp32 firmware +- Both MCP servers installed and accessible via `uvx` +- Claude CLI installed + +### Permissions +For HCI packet capture tests, grant btmon the required capability: + +```bash +sudo setcap cap_net_raw+ep /usr/bin/btmon +``` + +## Test Environment Setup + +### 1. Create a test directory + +```bash +mkdir -p /tmp/bt-e2e-test +cd /tmp/bt-e2e-test +``` + +### 2. Create MCP configuration + +Create `.mcp.json` with both MCP servers: + +```json +{ + "mcpServers": { + "esp32": { + "type": "stdio", + "command": "uvx", + "args": ["mcbluetooth-esp32"], + "env": { + "ESP32_SERIAL_PORT": "/dev/ttyUSB4" + } + }, + "bluez": { + "type": "stdio", + "command": "uvx", + "args": ["mcbluetooth"] + } + } +} +``` + +### 3. Initialize git (required for Claude CLI) + +```bash +git init +``` + +## Running Tests + +### Basic Command Structure + +```bash +claude -p "$(cat test-prompt.md)" \ + --mcp-config .mcp.json \ + --allowedTools "mcp__esp32__*,mcp__bluez__*" \ + --output-format json \ + 2>/dev/null | tee results.json | jq -r '.result' +``` + +**Key flags:** +- `-p`: Print/headless mode (non-interactive) +- `--mcp-config`: Path to MCP server configuration +- `--allowedTools`: Glob patterns for permitted tools (required in headless mode) +- `--output-format json`: Machine-parseable output + +### Full Test Suite (76 tests) + +The comprehensive test suite covers: +- ESP32 connection and system commands +- BlueZ adapter management +- Classic Bluetooth SSP pairing with auto-accept +- BLE GATT service creation (Environmental Sensing + Battery Service) +- HCI packet capture and analysis +- GATT read/write/notify operations +- Device management (trust, block, alias) + +```bash +claude -p "$(cat test-prompt-v5.md)" \ + --mcp-config .mcp.json \ + --allowedTools "mcp__esp32__*,mcp__bluez__*" \ + --output-format json 2>/dev/null | tee results-v5.json +``` + +### Analyzing Results + +Extract the summary: + +```bash +jq -r '.result' results-v5.json +``` + +Check pass/fail statistics: + +```bash +jq -r '.result' results-v5.json | grep -E "(PASS|FAIL|Total)" +``` + +View full metrics: + +```bash +jq '{ + duration_ms: .duration_ms, + num_turns: .num_turns, + total_cost_usd: .total_cost_usd, + success: .is_error == false +}' results-v5.json +``` + +## Test Phases + +The test suite is organized into phases that must run sequentially: + +| Phase | Tests | Coverage | +|-------|-------|----------| +| 1. ESP32 Connection | 1-4 | connect, ping, get_info, status | +| 2. BlueZ Adapter | 5-8 | list_adapters, adapter_info, pairable, discoverable | +| 3. Classic BT + SSP | 9-24 | enable, configure, SSP mode, scan, pair, device management | +| 4. Classic Cleanup | 25-29 | disable, events, clear_events | +| 5. BLE GATT Setup | 30-42 | Battery Service, Environmental Sensing, advertising | +| 6. HCI Capture + Discovery | 43-51 | capture_start, BLE scan, connect, services, characteristics | +| 7. Analyze Capture | 52-55 | capture_stop, parse, analyze, read_raw | +| 8. GATT Write + Notify | 56-63 | write, subscribe, notify, unsubscribe | +| 9. BLE Cleanup | 64-68 | stop advertising, clear GATT, disable BLE | +| 10. Adapter Management | 69-73 | set_alias, restore, disable discoverable | +| 11. Final Cleanup | 74-76 | ESP32 reset, disconnect, final check | + +## SSP Pairing: The auto_accept Flag + +Numeric Comparison SSP requires **both sides** to confirm the passkey. In headless mode, this creates a deadlock: + +1. Linux calls `bt_pair()` which blocks waiting for ESP32 confirmation +2. ESP32 can't receive the confirmation command because the LLM is blocked + +**Solution:** The ESP32 firmware supports `auto_accept` mode: + +``` +esp32_set_ssp_mode(mode="numeric_comparison", auto_accept=true) +``` + +This makes the ESP32 automatically confirm SSP pairings, breaking the deadlock. + +## Battery Service Test + +The test suite creates a standard Battery Service (UUID 0x180F) on the ESP32: + +1. Add Battery Service as primary GATT service +2. Add Battery Level characteristic (UUID 0x2A19) with read property +3. Set value to "4b" (75% in hex) +4. After BLE connection, call `bt_ble_battery` on Linux +5. Verify it returns 75 + +This tests the dedicated `bt_ble_battery` tool in mcbluetooth which reads from the standard Battery Level characteristic. + +## HCI Packet Capture + +Tests 43-55 exercise the btsnoop capture functionality: + +``` +bt_capture_start(adapter="hci1", output_file="/tmp/ble-gatt-capture.btsnoop") +# ... BLE operations ... +bt_capture_stop(capture_id="...") +bt_capture_parse(filepath="...", max_packets=50) +bt_capture_analyze(filepath="...") +bt_capture_read_raw(filepath="...", count=20) +``` + +Typical captures include 100-150 packets covering: +- HCI commands (LE scanning, connection) +- ACL data (GATT operations) +- HCI events (connection complete, encryption) + +## Test Prompt Format + +Test prompts follow a structured format: + +```markdown +# Test Suite Title + +## Phase N: Phase Name (Tests X-Y) + +N. **Test Name**: Call `tool_name` with params — expected result + +## Summary + +After all tests, print a DETAILED summary table: + +| # | Test | Result | Notes | +|---|------|--------|-------| +| 1 | Connect | PASS/FAIL | ... | +``` + +## Troubleshooting + +### Serial port busy + +``` +Error: could not open port /dev/ttyUSB4 +``` + +Check for other processes using the port: +```bash +lsof /dev/ttyUSB4 +``` + +### btmon permission denied + +``` +Error: Failed to open HCI raw socket +``` + +Grant capability: +```bash +sudo setcap cap_net_raw+ep /usr/bin/btmon +``` + +### ESP32 not responding + +Power cycle the ESP32 and check the firmware is flashed: +```bash +# Monitor serial output +screen /dev/ttyUSB4 115200 +``` + +Press reset button — should see boot event JSON. + +### Pairing timeout + +Ensure `auto_accept=true` is set for SSP numeric comparison mode before initiating pairing from Linux. + +## Example Results + +A successful v5 run produces: + +```json +{ + "type": "result", + "subtype": "success", + "is_error": false, + "duration_ms": 320000, + "num_turns": 88, + "result": "All 76 tests passed..." +} +``` + +Key metrics from successful runs: +- Duration: ~5-6 minutes +- API turns: 80-90 +- HCI packets captured: 100-150 +- Cost: ~$1.50-1.70 USD diff --git a/tests/prompts/README.md b/tests/prompts/README.md new file mode 100644 index 0000000..8b329a8 --- /dev/null +++ b/tests/prompts/README.md @@ -0,0 +1,29 @@ +# E2E Test Prompts + +These markdown files are test prompts for running automated E2E Bluetooth tests using Claude CLI in headless mode. + +## Usage + +```bash +cd /tmp/bt-e2e-test +claude -p "$(cat test-prompt-v5.md)" \ + --mcp-config .mcp.json \ + --allowedTools "mcp__esp32__*,mcp__bluez__*" \ + --output-format json +``` + +See [automated-e2e-testing.md](../../docs/automated-e2e-testing.md) for full setup instructions. + +## Test Prompts + +| File | Tests | Coverage | +|------|-------|----------| +| `test-prompt-v4.md` | 71 | Classic BT, BLE GATT, HCI capture | +| `test-prompt-v5.md` | 76 | v4 + Battery Service, bt_ble_battery | + +## Requirements + +- ESP32 with mcbluetooth-esp32 firmware +- Linux host with Bluetooth adapter +- Both MCP servers configured in `.mcp.json` +- btmon with CAP_NET_RAW for HCI capture tests diff --git a/tests/prompts/test-prompt-v4.md b/tests/prompts/test-prompt-v4.md new file mode 100644 index 0000000..57d52bf --- /dev/null +++ b/tests/prompts/test-prompt-v4.md @@ -0,0 +1,153 @@ +# ESP32 + BlueZ Full E2E Bluetooth Test Suite (v4 - Extended Coverage) + +You have two MCP servers available: +- **esp32** (prefix: `mcp__esp32__`) — controls an ESP32 dev board via serial/UART +- **bluez** (prefix: `mcp__bluez__`) — controls the Linux BlueZ Bluetooth stack + +Run ALL tests IN ORDER. For each test, report PASS or FAIL with actual response data. +If a test fails, note the error and continue with remaining tests. + +**Important**: The Linux adapter is `hci1` (not hci0). Use `hci1` for all BlueZ calls. + +--- + +## Phase 1: ESP32 Connection & System (Tests 1-4) + +1. **Connect**: Call `esp32_connect` — should return connected=true AND ready=true +2. **Ping**: Call `esp32_ping` — expect `{"pong": true}` +3. **Get Info**: Call `esp32_get_info` — note the chip model, FW version, and BT MAC address (you'll need the MAC later) +4. **Get Status**: Call `esp32_status` — confirm bt_enabled=false, ble_enabled=false + +## Phase 2: BlueZ Adapter Deep Dive (Tests 5-8) + +5. **List Adapters**: Call `bt_list_adapters` — find the powered adapter, note its name (probably hci1) +6. **Adapter Info**: Call `bt_adapter_info` with the adapter name — report full details including the adapter's MAC address +7. **Set Pairable**: Call `bt_adapter_pairable` with adapter, on=true — enable pairing acceptance +8. **Set Discoverable**: Call `bt_adapter_discoverable` with adapter, on=true, timeout=300 — make Linux visible + +## Phase 3: Classic BT + SSP Pairing (Tests 9-24) + +IMPORTANT: Enable Classic BT FIRST, then configure IO capabilities. + +9. **Enable Classic**: Call `esp32_classic_enable` +10. **Configure ESP32**: Call `esp32_configure` with name="SSP-Test-Device", io_cap="display_yesno" +11. **Set SSP Mode**: Call `esp32_set_ssp_mode` with mode="numeric_comparison", auto_accept=true +12. **Check Status**: Call `esp32_status` — confirm bt_enabled=true +13. **Set ESP32 Discoverable**: Call `esp32_classic_set_discoverable` with discoverable=true, timeout=120 +14. **Scan from Linux**: Call `bt_scan` with adapter="hci1", mode="classic", timeout=10 — find the ESP32 by its MAC + +Now initiate pairing: + +15. **Check Pairing Status Before**: Call `bt_pairing_status` — should show no pending requests +16. **Start Pairing**: Call `bt_pair` with adapter="hci1", address=, pairing_mode="auto" +17. **Check ESP32 Pair Events**: Call `esp32_get_events` — look for pair_request and pair_complete events +18. **Verify Paired on Linux**: Call `bt_device_info` with adapter="hci1", address= — check paired=true + +Post-pairing device management: + +19. **List Known Devices**: Call `bt_list_devices` with adapter="hci1", filter="paired" — ESP32 should be in the list +20. **Trust Device**: Call `bt_trust` with adapter="hci1", address=, trusted=true +21. **Set Alias**: Call `bt_device_set_alias` with adapter="hci1", address=, alias="My ESP32 Test Device" +22. **Block Device**: Call `bt_block` with adapter="hci1", address=, blocked=true — block the device +23. **Unblock Device**: Call `bt_block` with adapter="hci1", address=, blocked=false — unblock it +24. **Unpair**: Call `bt_unpair` with adapter="hci1", address= — clean up pairing + +## Phase 4: Classic Cleanup + Event System (Tests 25-29) + +25. **Disable Classic**: Call `esp32_classic_disable` +26. **Get All Events**: Call `esp32_get_events` — report ALL events captured so far +27. **Clear Events**: Call `esp32_clear_events` +28. **Verify Cleared**: Call `esp32_get_events` — should return empty list +29. **Check Status**: Call `esp32_status` — confirm bt_enabled=false + +## Phase 5: BLE GATT Setup (Tests 30-38) + +Set up ESP32 as a BLE peripheral with multiple characteristics: + +30. **Enable BLE**: Call `esp32_ble_enable` +31. **Add Service**: Call `esp32_gatt_add_service` with uuid="0000181a-0000-1000-8000-00805f9b34fb" (Environmental Sensing), primary=true — save the service_handle +32. **Add Read+Notify Char**: Call `esp32_gatt_add_characteristic` with the service_handle, uuid="00002a6e-0000-1000-8000-00805f9b34fb" (Temperature), properties=["read","notify"] — save as temp_handle +33. **Add Read+Write Char**: Call `esp32_gatt_add_characteristic` with the service_handle, uuid="00002a6f-0000-1000-8000-00805f9b34fb" (Humidity), properties=["read","write"] — save as humidity_handle +34. **Set Temperature**: Call `esp32_gatt_set_value` with char_handle=temp_handle, value="e803" (25.0°C) +35. **Set Humidity**: Call `esp32_gatt_set_value` with char_handle=humidity_handle, value="3200" (50%) +36. **Set Adv Data**: Call `esp32_ble_set_adv_data` with name="BLE-Sensor-Test", service_uuids=["0000181a-0000-1000-8000-00805f9b34fb"] +37. **Start Advertising**: Call `esp32_ble_advertise` with enable=true +38. **Clear Events**: Call `esp32_clear_events` — clear event history before BLE tests + +## Phase 6: HCI Capture + BLE Discovery (Tests 39-47) + +Start HCI packet capture BEFORE connecting, then analyze it afterwards: + +39. **Start HCI Capture**: Call `bt_capture_start` with adapter="hci1", output_file="/tmp/ble-gatt-capture.btsnoop" +40. **List Active Captures**: Call `bt_capture_list_active` — verify capture is running +41. **BLE Scan**: Call `bt_ble_scan` with adapter="hci1", timeout=10 — find the ESP32, verify name="BLE-Sensor-Test" +42. **Connect BLE**: Call `bt_connect` with adapter="hci1", address= + +Wait 3 seconds for service discovery to complete, then: + +43. **List Services**: Call `bt_ble_services` with adapter="hci1", address= — should see Environmental Sensing (181a) +44. **List Characteristics**: Call `bt_ble_characteristics` with adapter="hci1", address= — should see Temperature (2a6e) and Humidity (2a6f) +45. **Read Temperature**: Call `bt_ble_read` with adapter="hci1", address=, char_uuid="00002a6e-0000-1000-8000-00805f9b34fb" — should return hex "e803" +46. **Check Read Event on ESP32**: Call `esp32_get_events` — look for a gatt_read event +47. **Stop HCI Capture**: Call `bt_capture_stop` with capture_id from test 39 + +## Phase 7: Analyze HCI Capture (Tests 48-50) + +48. **Parse Capture**: Call `bt_capture_parse` with filepath="/tmp/ble-gatt-capture.btsnoop", max_packets=50 — report packet count and types +49. **Analyze Capture**: Call `bt_capture_analyze` with filepath="/tmp/ble-gatt-capture.btsnoop" — report statistics +50. **Read Raw Packets**: Call `bt_capture_read_raw` with filepath="/tmp/ble-gatt-capture.btsnoop", count=20 — show decoded packets + +## Phase 8: BLE GATT Write + Notifications (Tests 51-58) + +51. **Write Humidity**: Call `bt_ble_write` with adapter="hci1", address=, char_uuid="00002a6f-0000-1000-8000-00805f9b34fb", value="4b00", value_type="hex" +52. **Check Write Event**: Call `esp32_get_events` — look for a gatt_write event with the written value +53. **Subscribe Notifications**: Call `bt_ble_notify` with adapter="hci1", address=, char_uuid="00002a6e-0000-1000-8000-00805f9b34fb", enable=true +54. **Check Subscribe Event**: Call `esp32_get_events` — look for a gatt_subscribe event +55. **Push Notification**: First call `esp32_gatt_set_value` with char_handle=temp_handle, value="f401" (50.0°C), then call `esp32_gatt_notify` with char_handle=temp_handle +56. **Read Updated Value**: Call `bt_ble_read` with adapter="hci1", address=, char_uuid="00002a6e-0000-1000-8000-00805f9b34fb" — should now return "f401" +57. **Unsubscribe**: Call `bt_ble_notify` with adapter="hci1", address=, char_uuid="00002a6e-0000-1000-8000-00805f9b34fb", enable=false +58. **Disconnect BLE**: Call `bt_disconnect` with adapter="hci1", address= + +## Phase 9: BLE Cleanup (Tests 59-63) + +59. **Stop Advertising**: Call `esp32_ble_advertise` with enable=false +60. **Clear GATT**: Call `esp32_gatt_clear` +61. **Disable BLE**: Call `esp32_ble_disable` +62. **Final Status**: Call `esp32_status` — bt_enabled=false, ble_enabled=false +63. **Clear ESP32 Events**: Call `esp32_clear_events` + +## Phase 10: Adapter Self-Management (Tests 64-68) + +These tests verify adapter configuration tools. Done at the end to not interfere with earlier tests. + +64. **Get Original Alias**: Call `bt_adapter_info` with adapter="hci1" — note the current alias +65. **Set New Alias**: Call `bt_adapter_set_alias` with adapter="hci1", alias="E2E-Test-Adapter" — change the name +66. **Verify Alias Changed**: Call `bt_adapter_info` with adapter="hci1" — confirm alias is "E2E-Test-Adapter" +67. **Restore Original Alias**: Call `bt_adapter_set_alias` with adapter="hci1", alias= +68. **Disable Discoverable**: Call `bt_adapter_discoverable` with adapter="hci1", on=false — clean up + +## Phase 11: Final Cleanup (Tests 69-71) + +69. **Reset ESP32**: Call `esp32_reset` +70. **Disconnect Serial**: Call `esp32_disconnect` +71. **Final Adapter Check**: Call `bt_adapter_info` with adapter="hci1" — verify adapter is still powered and healthy + +--- + +## Summary + +After all tests, print a DETAILED summary table: + +| # | Test | Result | Notes | +|---|------|--------|-------| +| 1 | Connect | PASS/FAIL | ... | +| ... | ... | ... | ... | + +Report: +- Total PASS/FAIL counts +- Whether pairing succeeded (phase 3) +- What ESP32 events were captured (phases 4, 6, 8) +- Whether GATT read returned expected value (test 45) +- Whether GATT write succeeded (test 51) +- Whether the updated value was readable after notification (test 56) +- HCI capture statistics (tests 48-50): packet count, types captured, any errors diff --git a/tests/prompts/test-prompt-v5.md b/tests/prompts/test-prompt-v5.md new file mode 100644 index 0000000..80c2d8e --- /dev/null +++ b/tests/prompts/test-prompt-v5.md @@ -0,0 +1,170 @@ +# ESP32 + BlueZ Full E2E Bluetooth Test Suite (v5 - Complete Coverage) + +You have two MCP servers available: +- **esp32** (prefix: `mcp__esp32__`) — controls an ESP32 dev board via serial/UART +- **bluez** (prefix: `mcp__bluez__`) — controls the Linux BlueZ Bluetooth stack + +Run ALL tests IN ORDER. For each test, report PASS or FAIL with actual response data. +If a test fails, note the error and continue with remaining tests. + +**Important**: The Linux adapter is `hci1` (not hci0). Use `hci1` for all BlueZ calls. + +--- + +## Phase 1: ESP32 Connection & System (Tests 1-4) + +1. **Connect**: Call `esp32_connect` — should return connected=true AND ready=true +2. **Ping**: Call `esp32_ping` — expect `{"pong": true}` +3. **Get Info**: Call `esp32_get_info` — note the chip model, FW version, and BT MAC address (you'll need the MAC later) +4. **Get Status**: Call `esp32_status` — confirm bt_enabled=false, ble_enabled=false + +## Phase 2: BlueZ Adapter Deep Dive (Tests 5-8) + +5. **List Adapters**: Call `bt_list_adapters` — find the powered adapter, note its name (probably hci1) +6. **Adapter Info**: Call `bt_adapter_info` with the adapter name — report full details including the adapter's MAC address +7. **Set Pairable**: Call `bt_adapter_pairable` with adapter, on=true — enable pairing acceptance +8. **Set Discoverable**: Call `bt_adapter_discoverable` with adapter, on=true, timeout=300 — make Linux visible + +## Phase 3: Classic BT + SSP Pairing (Tests 9-24) + +IMPORTANT: Enable Classic BT FIRST, then configure IO capabilities. + +9. **Enable Classic**: Call `esp32_classic_enable` +10. **Configure ESP32**: Call `esp32_configure` with name="SSP-Test-Device", io_cap="display_yesno" +11. **Set SSP Mode**: Call `esp32_set_ssp_mode` with mode="numeric_comparison", auto_accept=true +12. **Check Status**: Call `esp32_status` — confirm bt_enabled=true +13. **Set ESP32 Discoverable**: Call `esp32_classic_set_discoverable` with discoverable=true, timeout=120 +14. **Scan from Linux**: Call `bt_scan` with adapter="hci1", mode="classic", timeout=10 — find the ESP32 by its MAC + +Now initiate pairing: + +15. **Check Pairing Status Before**: Call `bt_pairing_status` — should show no pending requests +16. **Start Pairing**: Call `bt_pair` with adapter="hci1", address=, pairing_mode="auto" +17. **Check ESP32 Pair Events**: Call `esp32_get_events` — look for pair_request and pair_complete events +18. **Verify Paired on Linux**: Call `bt_device_info` with adapter="hci1", address= — check paired=true + +Post-pairing device management: + +19. **List Known Devices**: Call `bt_list_devices` with adapter="hci1", filter="paired" — ESP32 should be in the list +20. **Trust Device**: Call `bt_trust` with adapter="hci1", address=, trusted=true +21. **Set Alias**: Call `bt_device_set_alias` with adapter="hci1", address=, alias="My ESP32 Test Device" +22. **Block Device**: Call `bt_block` with adapter="hci1", address=, blocked=true — block the device +23. **Unblock Device**: Call `bt_block` with adapter="hci1", address=, blocked=false — unblock it +24. **Unpair**: Call `bt_unpair` with adapter="hci1", address= — clean up pairing + +## Phase 4: Classic Cleanup + Event System (Tests 25-29) + +25. **Disable Classic**: Call `esp32_classic_disable` +26. **Get All Events**: Call `esp32_get_events` — report ALL events captured so far +27. **Clear Events**: Call `esp32_clear_events` +28. **Verify Cleared**: Call `esp32_get_events` — should return empty list +29. **Check Status**: Call `esp32_status` — confirm bt_enabled=false + +## Phase 5: BLE GATT Setup with Battery Service (Tests 30-42) + +Set up ESP32 as a BLE peripheral with Environmental Sensing AND Battery Service: + +30. **Enable BLE**: Call `esp32_ble_enable` + +First, add Battery Service (0x180F) for bt_ble_battery test: + +31. **Add Battery Service**: Call `esp32_gatt_add_service` with uuid="0000180f-0000-1000-8000-00805f9b34fb" (Battery Service), primary=true — save as battery_svc_handle +32. **Add Battery Level Char**: Call `esp32_gatt_add_characteristic` with service_handle=battery_svc_handle, uuid="00002a19-0000-1000-8000-00805f9b34fb" (Battery Level), properties=["read"] — save as battery_char_handle +33. **Set Battery Level**: Call `esp32_gatt_set_value` with char_handle=battery_char_handle, value="4b" (75%) + +Now add Environmental Sensing Service: + +34. **Add Env Service**: Call `esp32_gatt_add_service` with uuid="0000181a-0000-1000-8000-00805f9b34fb" (Environmental Sensing), primary=true — save as env_svc_handle +35. **Add Temperature Char**: Call `esp32_gatt_add_characteristic` with service_handle=env_svc_handle, uuid="00002a6e-0000-1000-8000-00805f9b34fb" (Temperature), properties=["read","notify"] — save as temp_handle +36. **Add Humidity Char**: Call `esp32_gatt_add_characteristic` with service_handle=env_svc_handle, uuid="00002a6f-0000-1000-8000-00805f9b34fb" (Humidity), properties=["read","write"] — save as humidity_handle +37. **Set Temperature**: Call `esp32_gatt_set_value` with char_handle=temp_handle, value="e803" (25.0°C) +38. **Set Humidity**: Call `esp32_gatt_set_value` with char_handle=humidity_handle, value="3200" (50%) + +Configure advertising: + +39. **Set Adv Data**: Call `esp32_ble_set_adv_data` with name="BLE-Sensor-Test", service_uuids=["0000180f-0000-1000-8000-00805f9b34fb", "0000181a-0000-1000-8000-00805f9b34fb"] +40. **Start Advertising**: Call `esp32_ble_advertise` with enable=true +41. **Clear Events**: Call `esp32_clear_events` — clear event history before BLE tests +42. **Check BLE Status**: Call `esp32_status` — confirm ble_enabled=true + +## Phase 6: HCI Capture + BLE Discovery (Tests 43-51) + +Start HCI packet capture BEFORE connecting: + +43. **Start HCI Capture**: Call `bt_capture_start` with adapter="hci1", output_file="/tmp/ble-gatt-capture.btsnoop" +44. **List Active Captures**: Call `bt_capture_list_active` — verify capture is running +45. **BLE Scan**: Call `bt_ble_scan` with adapter="hci1", timeout=10 — find the ESP32, verify name="BLE-Sensor-Test" +46. **Connect BLE**: Call `bt_connect` with adapter="hci1", address= + +Wait 3 seconds for service discovery to complete, then: + +47. **List Services**: Call `bt_ble_services` with adapter="hci1", address= — should see Battery (180f) and Environmental Sensing (181a) +48. **List Characteristics**: Call `bt_ble_characteristics` with adapter="hci1", address= — should see Battery Level (2a19), Temperature (2a6e), Humidity (2a6f) +49. **Read Battery Level**: Call `bt_ble_battery` with adapter="hci1", address= — should return 75 (the value we set as "4b") +50. **Read Temperature**: Call `bt_ble_read` with adapter="hci1", address=, char_uuid="00002a6e-0000-1000-8000-00805f9b34fb" — should return hex "e803" +51. **Check Read Events**: Call `esp32_get_events` — look for gatt_read events + +Stop capture for analysis: + +## Phase 7: Analyze HCI Capture (Tests 52-55) + +52. **Stop HCI Capture**: Call `bt_capture_stop` with capture_id from test 43 +53. **Parse Capture**: Call `bt_capture_parse` with filepath="/tmp/ble-gatt-capture.btsnoop", max_packets=50 — report packet count and types +54. **Analyze Capture**: Call `bt_capture_analyze` with filepath="/tmp/ble-gatt-capture.btsnoop" — report statistics +55. **Read Raw Packets**: Call `bt_capture_read_raw` with filepath="/tmp/ble-gatt-capture.btsnoop", count=20 — show decoded packets + +## Phase 8: BLE GATT Write + Notifications (Tests 56-63) + +56. **Write Humidity**: Call `bt_ble_write` with adapter="hci1", address=, char_uuid="00002a6f-0000-1000-8000-00805f9b34fb", value="4b00", value_type="hex" +57. **Check Write Event**: Call `esp32_get_events` — look for a gatt_write event with the written value +58. **Subscribe Notifications**: Call `bt_ble_notify` with adapter="hci1", address=, char_uuid="00002a6e-0000-1000-8000-00805f9b34fb", enable=true +59. **Check Subscribe Event**: Call `esp32_get_events` — look for a gatt_subscribe event +60. **Push Notification**: First call `esp32_gatt_set_value` with char_handle=temp_handle, value="f401" (50.0°C), then call `esp32_gatt_notify` with char_handle=temp_handle +61. **Read Updated Value**: Call `bt_ble_read` with adapter="hci1", address=, char_uuid="00002a6e-0000-1000-8000-00805f9b34fb" — should now return "f401" +62. **Unsubscribe**: Call `bt_ble_notify` with adapter="hci1", address=, char_uuid="00002a6e-0000-1000-8000-00805f9b34fb", enable=false +63. **Disconnect BLE**: Call `bt_disconnect` with adapter="hci1", address= + +## Phase 9: BLE Cleanup (Tests 64-68) + +64. **Stop Advertising**: Call `esp32_ble_advertise` with enable=false +65. **Clear GATT**: Call `esp32_gatt_clear` +66. **Disable BLE**: Call `esp32_ble_disable` +67. **Final Status**: Call `esp32_status` — bt_enabled=false, ble_enabled=false +68. **Clear ESP32 Events**: Call `esp32_clear_events` + +## Phase 10: Adapter Self-Management (Tests 69-73) + +These tests verify adapter configuration tools. Done at the end to not interfere with earlier tests. + +69. **Get Original Alias**: Call `bt_adapter_info` with adapter="hci1" — note the current alias +70. **Set New Alias**: Call `bt_adapter_set_alias` with adapter="hci1", alias="E2E-Test-Adapter" — change the name +71. **Verify Alias Changed**: Call `bt_adapter_info` with adapter="hci1" — confirm alias is "E2E-Test-Adapter" +72. **Restore Original Alias**: Call `bt_adapter_set_alias` with adapter="hci1", alias= +73. **Disable Discoverable**: Call `bt_adapter_discoverable` with adapter="hci1", on=false — clean up + +## Phase 11: Final Cleanup (Tests 74-76) + +74. **Reset ESP32**: Call `esp32_reset` +75. **Disconnect Serial**: Call `esp32_disconnect` +76. **Final Adapter Check**: Call `bt_adapter_info` with adapter="hci1" — verify adapter is still powered and healthy + +--- + +## Summary + +After all tests, print a DETAILED summary table: + +| # | Test | Result | Notes | +|---|------|--------|-------| +| 1 | Connect | PASS/FAIL | ... | +| ... | ... | ... | ... | + +Report: +- Total PASS/FAIL counts +- Whether pairing succeeded (phase 3) +- What ESP32 events were captured (phases 4, 6, 8) +- Whether bt_ble_battery returned the expected value 75 (test 49) +- Whether GATT read returned expected value (test 50) +- Whether GATT write succeeded (test 56) +- Whether the updated value was readable after notification (test 61) +- HCI capture statistics (tests 53-55): packet count, types captured, any errors