esp32-bt-classic 0.1.0

Bluetooth Classic support for ESP32 in Rust via ESP-IDF Bluedroid.
docs.rs failed to build esp32-bt-classic-0.1.0
Please check the build logs for more information.
See Builds for ideas on how to fix a failed build, or Metadata for how to configure docs.rs builds.
If you believe this is docs.rs' fault, open an issue.

esp32-bt-classic

Bluetooth Classic (BR/EDR) support for the ESP32 in Rust, built on esp-idf-sys and ESP-IDF's Bluedroid stack.

Status: early / work in progress. GAP (device discovery, discoverable mode, pairing configuration) works and has been tested on real hardware. SPP, A2DP, and HFP are not implemented yet. See Status below before depending on this for anything beyond discovery.

Hardware requirement

Bluetooth Classic is only available on the original ESP32. The S2 has no Bluetooth at all, and the S3/C3/C6/H2 family only support BLE, not Classic/BR-EDR. If your board isn't an original ESP32, this crate isn't for it.

Status

Profile State
GAP — discovery/scan, discoverable mode, remote service lookup, configurable IO capability for pairing Working, tested on hardware
Pairing replies (responding to ConfirmRequest/PinRequest) Not implemented — a real device attempting to pair will currently time out
SPP (serial port / generic byte stream) In progress — Kconfig enabled, not functional yet
A2DP, HFP Not started

Requirements

  • An original ESP32 dev board
  • The esp Rust toolchain, via espup
  • ldproxy and espflash
  • Linux only: your user needs to be in the dialout group to access the board's serial port
cargo install espup
espup install
source ~/export-esp.sh   # re-run per shell, or add to your shell profile

cargo install ldproxy espflash

# Linux only, one-time:
sudo usermod -aG dialout $USER   # then log out/in (or reboot) for it to apply

This crate targets xtensa-esp32-espidf and requires the full ESP-IDF build toolchain — it will not compile for a normal desktop target, and docs.rs will not be able to auto-build its documentation for the same reason. This is normal for esp-rs ecosystem crates.

Example

use esp_idf_hal::peripherals::Peripherals;
use esp_idf_sys::nvs_flash_init;
use esp32_bt_classic::BtClassic;

fn main() -> anyhow::Result<()> {
    esp_idf_svc::sys::link_patches();
    esp_idf_svc::log::EspLogger::initialize_default();
    unsafe { nvs_flash_init() };

    let peripherals = Peripherals::take()?;
    let (_wifi_modem, modem) = peripherals.modem.split();

    let bt = BtClassic::new(modem)?;
    bt.gap.set_name("esp32-scanner")?;
    bt.gap.set_discoverable(true)?;

    log::info!("Staying visible for 60 seconds");
    std::thread::sleep(std::time::Duration::from_secs(60));

    Ok(())
}

Runnable versions of this live in examples/:

cargo run --example scan --release              # scans for nearby Classic BT devices
cargo run --example enable_discovery --release   # makes the device discoverable/connectable

Architecture

Each Bluetooth profile lives under src/profiles/<name>/ and follows the same shape: an unsafe extern "C" callback matching ESP-IDF's callback signature for that profile, an event.rs that converts the raw C event into an owned Rust enum, and a bounded async channel connecting the callback (invoked from Bluedroid's own FreeRTOS task) to consumer code.

License

Licensed under either of MIT or Apache-2.0 at your option.