-
Notifications
You must be signed in to change notification settings - Fork 246
Add Trouble BLE Support over ESP-IDF VHCI #677
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We鈥檒l occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # Trouble uses ESP-IDF's Bluetooth controller without Bluedroid or NimBLE. | ||
| CONFIG_BT_ENABLED=y | ||
| CONFIG_BT_CONTROLLER_ONLY=y | ||
| CONFIG_BT_CONTROLLER_ENABLED=y | ||
|
|
||
| # Required on chips that let the application select the controller HCI | ||
| # interface. Other chips use VHCI for controller-only mode automatically. | ||
| CONFIG_BT_LE_HCI_INTERFACE_USE_RAM=y | ||
|
|
||
| # Trouble reads into a 259-byte HCI buffer. An ACL payload of 251 bytes occupies | ||
| # 256 bytes with the ACL header and H4 packet indicator. | ||
| CONFIG_BT_LE_ACL_BUF_SIZE=251 | ||
|
|
||
| # The Trouble runner and host resources exceed ESP-IDF's default main-task stack. | ||
| CONFIG_ESP_MAIN_TASK_STACK_SIZE=32768 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,9 @@ name: PublishDryRun | |
| on: workflow_dispatch | ||
|
|
||
| env: | ||
| rust_toolchain: nightly | ||
| # Temporary workaround for rust-lang/rust#158168. Remove after the fix reaches nightly: | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Revert, this script does not run every day. |
||
| # https://github.com/rust-lang/rust/pull/160170 | ||
| rust_toolchain: nightly-2026-07-29 | ||
|
|
||
| jobs: | ||
| publishdryrun: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -3,7 +3,9 @@ name: Publish | |
| on: workflow_dispatch | ||
|
|
||
| env: | ||
| rust_toolchain: nightly | ||
| # Temporary workaround for rust-lang/rust#158168. Remove after the fix reaches nightly: | ||
| # https://github.com/rust-lang/rust/pull/160170 | ||
| rust_toolchain: nightly-2026-07-29 | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ditto. |
||
| CRATE_NAME: esp-idf-svc | ||
|
|
||
| jobs: | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -26,6 +26,14 @@ embassy-time-driver = ["dep:embassy-time-driver", "embassy-time-queue-utils"] | |
| alloc = ["esp-idf-hal/alloc", "embedded-svc/alloc", "uncased/alloc"] | ||
| nightly = ["embedded-svc/nightly", "esp-idf-hal/nightly"] | ||
| experimental = ["embedded-svc/experimental", "esp-idf-hal/experimental"] | ||
| trouble = [ | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Remove |
||
| "alloc", | ||
| "dep:bt-hci", | ||
| "dep:embassy-sync", | ||
| "dep:embedded-io", | ||
| "critical-section", | ||
| "embassy-sync", | ||
| ] | ||
|
|
||
| # Propagated esp-idf-hal features | ||
| critical-section = ["esp-idf-hal/critical-section"] | ||
|
|
@@ -57,17 +65,22 @@ embassy-time-queue-utils = { version = "0.3", optional = true } | |
| embassy-futures = "0.1.2" | ||
| embedded-storage = { version = "0.3", optional = true } | ||
| futures-io = { version = "0.3", optional = true } | ||
| bt-hci = { version = "0.9", optional = true, default-features = false } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removing the |
||
| embassy-sync = { version = "0.7", optional = true } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As per my earlier comment, let's try not to introduce this. |
||
| embedded-io = { version = "0.7", optional = true } | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not necessary to list explicitly. comes via |
||
|
|
||
| [build-dependencies] | ||
| embuild = "0.33.3" | ||
|
|
||
| [dev-dependencies] | ||
| anyhow = "1" | ||
| embassy-time = { version = "0.5", features = ["generic-queue-16"] } | ||
| esp-idf-sys = { version = "0.37.1", features = ["binstart"] } | ||
| futures = "0.3" | ||
| serde = { version = "1", default-features = false, features = ["derive"] } | ||
| serde_json = { version = "1", default-features = false, features = ["alloc"] } | ||
| postcard = "1" | ||
| trouble-host = { version = "0.7", features = ["log"] } | ||
| # `async-io-mini` is a fork of `async-io` optimized for ESP-IDF: much smaller | ||
| # RAM footprint (no large reactor thread stack, no per-fd heap bookkeeping) | ||
| async-io = { version = "0.4", package = "async-io-mini", default-features = false, features = ["futures-io"] } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,112 @@ | ||
| //! Non-connectable BLE beacon using the Trouble host with ESP-IDF's controller. | ||
| //! | ||
| //! Enable the `trouble` and `embassy-time-driver` Cargo features and use a | ||
| //! controller-only ESP-IDF configuration such as | ||
| //! `.github/configs/sdkconfig.defaults.trouble`. | ||
|
|
||
| #![allow(unknown_lints)] | ||
| #![allow(unexpected_cfgs)] | ||
|
|
||
| #[cfg(all( | ||
| not(any(esp32s2, esp32p4)), | ||
| feature = "trouble", | ||
| esp_idf_bt_enabled, | ||
| esp_idf_bt_controller_only, | ||
| ))] | ||
| fn main() -> anyhow::Result<()> { | ||
| example::main() | ||
| } | ||
|
|
||
| #[cfg(not(all( | ||
| not(any(esp32s2, esp32p4)), | ||
| feature = "trouble", | ||
| esp_idf_bt_enabled, | ||
| esp_idf_bt_controller_only, | ||
| )))] | ||
| fn main() -> anyhow::Result<()> { | ||
| panic!( | ||
| "This example requires the `trouble` feature and a controller-only configuration on a chip with a BLE radio" | ||
| ); | ||
| } | ||
|
|
||
| #[cfg(all( | ||
| not(any(esp32s2, esp32p4)), | ||
| feature = "trouble", | ||
| esp_idf_bt_enabled, | ||
| esp_idf_bt_controller_only, | ||
| ))] | ||
| mod example { | ||
| use anyhow::Result; | ||
| use bt_hci::controller::ExternalController; | ||
| use embassy_futures::select::{select, Either}; | ||
| use trouble_host::prelude::*; | ||
|
|
||
| use esp_idf_svc::bt_controller::{Ble, EspBtController, EspVhciTransport}; | ||
| use esp_idf_svc::hal::peripherals::Peripherals; | ||
| use esp_idf_svc::hal::task::block_on; | ||
| use esp_idf_svc::nvs::EspDefaultNvsPartition; | ||
|
|
||
| type Controller = ExternalController<EspVhciTransport<'static>, 10>; | ||
|
|
||
| pub fn main() -> Result<()> { | ||
| esp_idf_svc::sys::link_patches(); | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is no longer necessary, as it is called automatically by the startup code in |
||
| let _ = esp_idf_svc::hal::task::critical_section::link(); | ||
| let _ = esp_idf_svc::timer::embassy_time_driver::link(); | ||
| esp_idf_svc::log::EspLogger::initialize_default(); | ||
|
|
||
| let peripherals = Peripherals::take()?; | ||
|
|
||
| // Keep NVS alive for ESP-IDF's PHY calibration data. | ||
| let _nvs = EspDefaultNvsPartition::take()?; | ||
|
|
||
| let controller = EspBtController::<Ble>::new(peripherals.modem)?; | ||
| let transport = EspVhciTransport::new(controller)?; | ||
| let controller = ExternalController::<_, 10>::new(transport); | ||
|
|
||
| block_on(run(controller)); | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| async fn run(controller: Controller) { | ||
| let address = Address::random([0xff, 0x8f, 0x1a, 0x05, 0xe4, 0xff]); | ||
| let mut resources: HostResources<Controller, DefaultPacketPool, 0, 0, 1> = | ||
| HostResources::new(); | ||
| let stack = trouble_host::new(controller, &mut resources) | ||
| .set_random_address(address) | ||
| .build(); | ||
| let mut peripheral = stack.peripheral(); | ||
| let mut runner = stack.runner(); | ||
|
|
||
| let mut adv_data = [0; 31]; | ||
| let len = AdStructure::encode_slice( | ||
| &[ | ||
| AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), | ||
| AdStructure::CompleteLocalName(b"ESP-IDF Trouble"), | ||
| ], | ||
| &mut adv_data, | ||
| ) | ||
| .unwrap(); | ||
|
|
||
| log::info!("Starting Trouble beacon as {address:?}"); | ||
|
|
||
| match select(runner.run(), async { | ||
| let _advertiser = peripheral | ||
| .advertise( | ||
| &AdvertisementParameters::default(), | ||
| Advertisement::NonconnectableNonscannableUndirected { | ||
| adv_data: &adv_data[..len], | ||
| }, | ||
| ) | ||
| .await | ||
| .unwrap(); | ||
|
|
||
| core::future::pending::<()>().await; | ||
| }) | ||
| .await | ||
| { | ||
| Either::First(result) => result.unwrap(), | ||
| Either::Second(()) => unreachable!("the advertising task never completes"), | ||
| } | ||
| } | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HostResourcesshould not be allocated on-stack, but staticlaly in bss or on heap. It is another topic that they regressed it recently, where such an allocation was not possible anymore due to theControllerre-generification. Fortunately, fixed inmain: embassy-rs/trouble#645