UART-controlled ESP32 peripheral for automated E2E Bluetooth testing. Dual-mode (Classic BT + BLE) via Bluedroid on original ESP32. Firmware (ESP-IDF v5.x, 2511 lines C): - NDJSON protocol over UART1 (115200 baud) - System commands: ping, reset, get_info, get_status - Classic BT: GAP, SPP, all 4 SSP pairing modes - BLE: GATTS, advertising, GATT service/characteristic management - 6 device personas: headset, speaker, keyboard, sensor, phone, bare - Event reporter: thread-safe async event queue to host Python MCP server (FastMCP, 1626 lines): - Async serial client with command/response correlation - Event queue with wait_for pattern matching - Tools: connection, configure, classic, ble, persona, events - MCP resources: esp32://status, esp32://events, esp32://personas Tests: 74 unit tests passing, 5 integration test stubs (skip without hardware)
250 lines
7.2 KiB
Markdown
250 lines
7.2 KiB
Markdown
# Hardware Setup Guide
|
|
|
|
How to set up the ESP32 hardware and build environment for the mcbluetooth-esp32 test harness.
|
|
|
|
## Requirements
|
|
|
|
- **ESP32 dev board** -- must be an original ESP32 (ESP32-D0WD or similar) with Classic Bluetooth support. ESP32-S3, ESP32-C3, ESP32-H2, and ESP32-S2 lack the BR/EDR radio and will not work for Classic BT pairing tests.
|
|
- **USB cable** (USB-A to micro-USB or USB-C depending on your board)
|
|
- **Linux host** with BlueZ installed (for the `mcbluetooth` MCP server on the other side of E2E tests)
|
|
- **ESP-IDF v5.x** toolchain
|
|
|
|
## Verified Hardware
|
|
|
|
| Property | Value |
|
|
|----------|-------|
|
|
| Chip | ESP32-D0WD-V3 (rev 3.1) |
|
|
| Flash | 4MB |
|
|
| Crystal | 40MHz |
|
|
| Features | Wi-Fi, Bluetooth (dual-mode), Dual Core, 240MHz |
|
|
|
|
Any ESP32 board based on the original ESP32 chip should work. Commonly available boards include ESP32-DevKitC, ESP32-WROOM-32, and NodeMCU-32S.
|
|
|
|
## Wiring
|
|
|
|
### Default: USB only
|
|
|
|
For most development, a single USB cable handles both flashing and protocol communication. The ESP32's built-in USB-to-UART bridge (typically CP2102 or CH340) provides the serial link.
|
|
|
|
The firmware uses **UART1** (GPIO4/GPIO5) for the NDJSON protocol and keeps **UART0** for ESP-IDF console logging. When using USB only, the USB bridge connects to UART0 by default -- so you will need either:
|
|
|
|
1. A board that routes UART1 through the USB bridge (uncommon), or
|
|
2. A separate USB-UART adapter connected to GPIO4/GPIO5 (described below)
|
|
|
|
For quick testing with the firmware's default pin assignment, connect a USB-UART adapter.
|
|
|
|
### Dedicated UART (GPIO4/GPIO5)
|
|
|
|
Connect a USB-UART adapter (e.g., FTDI FT232R, CP2102, CH340) to the ESP32:
|
|
|
|
```
|
|
ESP32 GPIO4 (TX) ----> USB-UART adapter RX
|
|
ESP32 GPIO5 (RX) <---- USB-UART adapter TX
|
|
ESP32 GND ----> USB-UART adapter GND
|
|
```
|
|
|
|
The adapter appears as a second `/dev/ttyUSB*` device on the host. Use this device path for `ESP32_SERIAL_PORT`.
|
|
|
|
Do not connect voltage lines (VCC/3V3) between the adapter and the ESP32 if the board is already powered via its own USB port.
|
|
|
|
### Pin reassignment
|
|
|
|
If GPIO4/GPIO5 conflict with other peripherals on your board, change the pin definitions in `firmware/main/uart_handler.c`:
|
|
|
|
```c
|
|
#define UART_TX_PIN GPIO_NUM_4
|
|
#define UART_RX_PIN GPIO_NUM_5
|
|
```
|
|
|
|
Rebuild and reflash after changing pins.
|
|
|
|
## ESP-IDF Setup
|
|
|
|
### 1. Install ESP-IDF v5.x
|
|
|
|
Follow the official installation guide: https://docs.espressif.com/projects/esp-idf/en/stable/esp32/get-started/
|
|
|
|
On Arch Linux:
|
|
|
|
```bash
|
|
# Install dependencies
|
|
sudo pacman -S cmake ninja python
|
|
|
|
# Clone ESP-IDF
|
|
mkdir -p ~/esp && cd ~/esp
|
|
git clone --recursive https://github.com/espressif/esp-idf.git
|
|
cd esp-idf
|
|
./install.sh esp32
|
|
|
|
# Activate the environment (add to .bashrc or run each session)
|
|
. ~/esp/esp-idf/export.sh
|
|
```
|
|
|
|
### 2. Set the target
|
|
|
|
```bash
|
|
cd firmware
|
|
idf.py set-target esp32
|
|
```
|
|
|
|
This only needs to be done once per checkout. It configures the build system for the ESP32 chip.
|
|
|
|
### 3. Build
|
|
|
|
```bash
|
|
idf.py build
|
|
```
|
|
|
|
Or using the project Makefile from the repository root:
|
|
|
|
```bash
|
|
make build
|
|
```
|
|
|
|
### 4. Flash
|
|
|
|
```bash
|
|
idf.py -p /dev/ttyUSB4 flash
|
|
```
|
|
|
|
Or:
|
|
|
|
```bash
|
|
make flash SERIAL_PORT=/dev/ttyUSB4
|
|
```
|
|
|
|
### 5. Monitor (optional)
|
|
|
|
Open the ESP-IDF serial monitor to see console logs from UART0:
|
|
|
|
```bash
|
|
idf.py -p /dev/ttyUSB4 monitor
|
|
```
|
|
|
|
Press `Ctrl+]` to exit the monitor.
|
|
|
|
### 6. Flash and monitor in one step
|
|
|
|
```bash
|
|
make flash-monitor SERIAL_PORT=/dev/ttyUSB4
|
|
```
|
|
|
|
## Quick Verification
|
|
|
|
### Using the MCP server
|
|
|
|
```bash
|
|
# Set the serial port and start the server
|
|
ESP32_SERIAL_PORT=/dev/ttyUSB4 uvx mcbluetooth-esp32
|
|
```
|
|
|
|
Then from a Claude Code session with the MCP server configured, call:
|
|
|
|
1. `esp32_connect` -- opens the serial link
|
|
2. `esp32_ping` -- should return `{"pong": true}`
|
|
3. `esp32_get_info` -- should return chip model, firmware version, MAC address
|
|
|
|
### Using the Makefile ping target
|
|
|
|
```bash
|
|
make ping SERIAL_PORT=/dev/ttyUSB4
|
|
```
|
|
|
|
This runs a standalone Python script that connects, sends a `ping` command, prints the response, and disconnects.
|
|
|
|
### Raw serial check
|
|
|
|
If everything else fails, use `screen` or `minicom` to send raw JSON:
|
|
|
|
```bash
|
|
screen /dev/ttyUSB4 115200
|
|
```
|
|
|
|
Type (all on one line, then press Enter):
|
|
|
|
```json
|
|
{"type":"cmd","id":"1","cmd":"ping"}
|
|
```
|
|
|
|
You should see:
|
|
|
|
```json
|
|
{"type":"resp","id":"1","status":"ok","data":{"pong":true}}
|
|
```
|
|
|
|
Press `Ctrl+A` then `K` then `Y` to exit screen.
|
|
|
|
## sdkconfig
|
|
|
|
The project ships `firmware/sdkconfig.defaults` with the required Bluetooth configuration pre-set:
|
|
|
|
| Setting | Value | Purpose |
|
|
|---------|-------|---------|
|
|
| `CONFIG_BT_ENABLED` | y | Enable Bluetooth controller |
|
|
| `CONFIG_BT_BLUEDROID_ENABLED` | y | Use Bluedroid host stack |
|
|
| `CONFIG_BT_CLASSIC_ENABLED` | y | Enable BR/EDR (Classic BT) |
|
|
| `CONFIG_BT_BLE_ENABLED` | y | Enable BLE |
|
|
| `CONFIG_BT_SSP_ENABLED` | y | Enable Secure Simple Pairing |
|
|
| `CONFIG_BT_SPP_ENABLED` | y | Enable Serial Port Profile |
|
|
| `CONFIG_BT_GATTS_ENABLE` | y | Enable GATT Server |
|
|
| `CONFIG_BTDM_CTRL_MODE_BTDM` | y | Dual-mode controller (Classic + BLE simultaneously) |
|
|
| `CONFIG_NVS_ENABLED` | y | Non-volatile storage for bonding data |
|
|
|
|
Do not modify these unless you understand the implications. Disabling `CONFIG_BT_CLASSIC_ENABLED` breaks all Classic BT pairing tests. Disabling `CONFIG_BT_SSP_ENABLED` forces legacy PIN-only pairing.
|
|
|
|
## Troubleshooting
|
|
|
|
### Permission denied on `/dev/ttyUSB*`
|
|
|
|
Add your user to the `dialout` group (or `uucp` on Arch Linux):
|
|
|
|
```bash
|
|
# Debian/Ubuntu
|
|
sudo usermod -aG dialout $USER
|
|
|
|
# Arch Linux
|
|
sudo usermod -aG uucp $USER
|
|
```
|
|
|
|
Log out and back in for the group change to take effect.
|
|
|
|
### Device not found
|
|
|
|
Check what serial devices are present:
|
|
|
|
```bash
|
|
ls -la /dev/ttyUSB* /dev/ttyACM*
|
|
```
|
|
|
|
If nothing appears, verify the USB cable is a data cable (not charge-only) and that the board's USB-UART chip driver is loaded:
|
|
|
|
```bash
|
|
dmesg | tail -20
|
|
```
|
|
|
|
Look for lines mentioning `cp210x`, `ch341`, or `ftdi_sio`.
|
|
|
|
### Flash fails or hangs
|
|
|
|
Some ESP32 boards require holding the **BOOT** button during the initial flash sequence. Hold BOOT, press and release EN (reset), then release BOOT. The flash should proceed.
|
|
|
|
If the board has auto-download circuitry (most DevKitC boards do), this should not be necessary.
|
|
|
|
### No response over UART
|
|
|
|
1. **Verify TX/RX pin assignment.** The firmware uses UART1 on GPIO4 (TX) and GPIO5 (RX). If your adapter is connected to different pins, update `uart_handler.c`.
|
|
|
|
2. **Check baud rate.** Both sides must use 115200. Verify in `screen` or your terminal emulator.
|
|
|
|
3. **Check the correct serial device.** If the board has two USB-UART interfaces (one for UART0 console, one for UART1 protocol), make sure you are talking to the right one.
|
|
|
|
4. **Look at UART0 console output.** Connect the ESP-IDF monitor to the console port. Boot messages and error logs appear there. If you see `UART1 ready (TX=4 RX=5 @ 115200 baud)` in the log, the firmware started correctly.
|
|
|
|
### Build errors about missing Bluetooth headers
|
|
|
|
Make sure `idf.py set-target esp32` was run. The ESP32-S3 and ESP32-C3 targets do not expose Classic BT APIs, which causes build failures.
|
|
|
|
### NVS errors on first boot
|
|
|
|
If the console shows `NVS partition issue, erasing and re-initializing`, this is expected on first flash or after a partition table change. The firmware handles it automatically by erasing and reinitializing NVS.
|