From 4f7ac1946a43379306aa432961fb97bba1139a6e Mon Sep 17 00:00:00 2001 From: Dario Nieuwenhuis Date: Mon, 5 Aug 2024 20:58:04 +0200 Subject: cyw43: add Bluetooth support. Co-Authored-By: Brandon Ros --- examples/rp/Cargo.toml | 17 +++- examples/rp/src/bin/bluetooth.rs | 148 ++++++++++++++++++++++++++++++ examples/rp/src/bin/wifi_ap_tcp_server.rs | 4 +- examples/rp/src/bin/wifi_blinky.rs | 4 +- examples/rp/src/bin/wifi_scan.rs | 4 +- examples/rp/src/bin/wifi_tcp_server.rs | 8 +- examples/rp/src/bin/wifi_webrequest.rs | 4 +- 7 files changed, 175 insertions(+), 14 deletions(-) create mode 100644 examples/rp/src/bin/bluetooth.rs (limited to 'examples') diff --git a/examples/rp/Cargo.toml b/examples/rp/Cargo.toml index 4945f4bd2..2884ca85a 100644 --- a/examples/rp/Cargo.toml +++ b/examples/rp/Cargo.toml @@ -16,8 +16,8 @@ embassy-net = { version = "0.4.0", path = "../../embassy-net", features = ["defm embassy-net-wiznet = { version = "0.1.0", path = "../../embassy-net-wiznet", features = ["defmt"] } embassy-futures = { version = "0.1.0", path = "../../embassy-futures" } embassy-usb-logger = { version = "0.2.0", path = "../../embassy-usb-logger" } -cyw43 = { version = "0.2.0", path = "../../cyw43", features = ["defmt", "firmware-logs"] } -cyw43-pio = { version = "0.2.0", path = "../../cyw43-pio", features = ["defmt", "overclock"] } +cyw43 = { version = "0.2.0", path = "../../cyw43", features = ["defmt", "firmware-logs", "bluetooth"] } +cyw43-pio = { version = "0.2.0", path = "../../cyw43-pio", features = ["defmt"] } defmt = "0.3" defmt-rtt = "0.4" @@ -59,9 +59,22 @@ pio = "0.2.1" rand = { version = "0.8.5", default-features = false } embedded-sdmmc = "0.7.0" +bt-hci = { version = "0.1.0", default-features = false, features = ["defmt"] } +trouble-host = { version = "0.1.0", features = ["defmt", "gatt"] } + [profile.release] debug = 2 [profile.dev] lto = true opt-level = "z" + +[patch.crates-io] +trouble-host = { git = "https://github.com/embassy-rs/trouble.git", rev = "4b8c0f499b34e46ca23a56e2d1640ede371722cf" } +bt-hci = { git = "https://github.com/alexmoon/bt-hci.git", rev = "b9cd5954f6bd89b535cad9c418e9fdf12812d7c3" } +embassy-executor = { path = "../../embassy-executor" } +embassy-sync = { path = "../../embassy-sync" } +embassy-futures = { path = "../../embassy-futures" } +embassy-time = { path = "../../embassy-time" } +embassy-time-driver = { path = "../../embassy-time-driver" } +embassy-embedded-hal = { path = "../../embassy-embedded-hal" } diff --git a/examples/rp/src/bin/bluetooth.rs b/examples/rp/src/bin/bluetooth.rs new file mode 100644 index 000000000..901521b60 --- /dev/null +++ b/examples/rp/src/bin/bluetooth.rs @@ -0,0 +1,148 @@ +//! This example test the RP Pico W on board LED. +//! +//! It does not work with the RP Pico board. See blinky.rs. + +#![no_std] +#![no_main] + +use bt_hci::controller::ExternalController; +use cyw43_pio::PioSpi; +use defmt::*; +use embassy_executor::Spawner; +use embassy_futures::join::join3; +use embassy_rp::bind_interrupts; +use embassy_rp::gpio::{Level, Output}; +use embassy_rp::peripherals::{DMA_CH0, PIO0}; +use embassy_rp::pio::{InterruptHandler, Pio}; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_time::{Duration, Timer}; +use static_cell::StaticCell; +use trouble_host::advertise::{AdStructure, Advertisement, BR_EDR_NOT_SUPPORTED, LE_GENERAL_DISCOVERABLE}; +use trouble_host::attribute::{AttributeTable, CharacteristicProp, Service, Uuid}; +use trouble_host::gatt::GattEvent; +use trouble_host::{Address, BleHost, BleHostResources, PacketQos}; +use {defmt_rtt as _, embassy_time as _, panic_probe as _}; + +bind_interrupts!(struct Irqs { + PIO0_IRQ_0 => InterruptHandler; +}); + +#[embassy_executor::task] +async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { + runner.run().await +} + +#[embassy_executor::main] +async fn main(spawner: Spawner) { + let p = embassy_rp::init(Default::default()); + let fw = include_bytes!("../../../../cyw43-firmware/43439A0.bin"); + let clm = include_bytes!("../../../../cyw43-firmware/43439A0_clm.bin"); + let btfw = include_bytes!("../../../../cyw43-firmware/43439A0_btfw.bin"); + + // To make flashing faster for development, you may want to flash the firmwares independently + // at hardcoded addresses, instead of baking them into the program with `include_bytes!`: + // probe-rs download 43439A0.bin --format bin --chip RP2040 --base-address 0x10100000 + // probe-rs download 43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000 + //let fw = unsafe { core::slice::from_raw_parts(0x10100000 as *const u8, 224190) }; + //let clm = unsafe { core::slice::from_raw_parts(0x10140000 as *const u8, 4752) }; + + let pwr = Output::new(p.PIN_23, Level::Low); + let cs = Output::new(p.PIN_25, Level::High); + let mut pio = Pio::new(p.PIO0, Irqs); + let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p.PIN_24, p.PIN_29, p.DMA_CH0); + + static STATE: StaticCell = StaticCell::new(); + let state = STATE.init(cyw43::State::new()); + let (_net_device, bt_device, mut control, runner) = cyw43::new_with_bluetooth(state, pwr, spi, fw, btfw).await; + unwrap!(spawner.spawn(cyw43_task(runner))); + control.init(clm).await; + + let controller: ExternalController<_, 10> = ExternalController::new(bt_device); + static HOST_RESOURCES: StaticCell> = StaticCell::new(); + let host_resources = HOST_RESOURCES.init(BleHostResources::new(PacketQos::None)); + + let mut ble: BleHost<'_, _> = BleHost::new(controller, host_resources); + + ble.set_random_address(Address::random([0xff, 0x9f, 0x1a, 0x05, 0xe4, 0xff])); + let mut table: AttributeTable<'_, NoopRawMutex, 10> = AttributeTable::new(); + + // Generic Access Service (mandatory) + let id = b"Pico W Bluetooth"; + let appearance = [0x80, 0x07]; + let mut bat_level = [0; 1]; + let handle = { + let mut svc = table.add_service(Service::new(0x1800)); + let _ = svc.add_characteristic_ro(0x2a00, id); + let _ = svc.add_characteristic_ro(0x2a01, &appearance[..]); + svc.build(); + + // Generic attribute service (mandatory) + table.add_service(Service::new(0x1801)); + + // Battery service + let mut svc = table.add_service(Service::new(0x180f)); + + svc.add_characteristic( + 0x2a19, + &[CharacteristicProp::Read, CharacteristicProp::Notify], + &mut bat_level, + ) + .build() + }; + + let mut adv_data = [0; 31]; + AdStructure::encode_slice( + &[ + AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED), + AdStructure::ServiceUuids16(&[Uuid::Uuid16([0x0f, 0x18])]), + AdStructure::CompleteLocalName(b"Pico W Bluetooth"), + ], + &mut adv_data[..], + ) + .unwrap(); + + let server = ble.gatt_server(&table); + + info!("Starting advertising and GATT service"); + let _ = join3( + ble.run(), + async { + loop { + match server.next().await { + Ok(GattEvent::Write { handle, connection: _ }) => { + let _ = table.get(handle, |value| { + info!("Write event. Value written: {:?}", value); + }); + } + Ok(GattEvent::Read { .. }) => { + info!("Read event"); + } + Err(e) => { + error!("Error processing GATT events: {:?}", e); + } + } + } + }, + async { + let mut advertiser = ble + .advertise( + &Default::default(), + Advertisement::ConnectableScannableUndirected { + adv_data: &adv_data[..], + scan_data: &[], + }, + ) + .await + .unwrap(); + let conn = advertiser.accept().await.unwrap(); + // Keep connection alive + let mut tick: u8 = 0; + loop { + Timer::after(Duration::from_secs(10)).await; + tick += 1; + server.notify(handle, &conn, &[tick]).await.unwrap(); + } + }, + ) + .await; +} diff --git a/examples/rp/src/bin/wifi_ap_tcp_server.rs b/examples/rp/src/bin/wifi_ap_tcp_server.rs index 4fc2690e3..b5fbd8e36 100644 --- a/examples/rp/src/bin/wifi_ap_tcp_server.rs +++ b/examples/rp/src/bin/wifi_ap_tcp_server.rs @@ -28,7 +28,7 @@ bind_interrupts!(struct Irqs { }); #[embassy_executor::task] -async fn wifi_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { +async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { runner.run().await } @@ -62,7 +62,7 @@ async fn main(spawner: Spawner) { static STATE: StaticCell = StaticCell::new(); let state = STATE.init(cyw43::State::new()); let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; - unwrap!(spawner.spawn(wifi_task(runner))); + unwrap!(spawner.spawn(cyw43_task(runner))); control.init(clm).await; control diff --git a/examples/rp/src/bin/wifi_blinky.rs b/examples/rp/src/bin/wifi_blinky.rs index 471349639..04a61bbd5 100644 --- a/examples/rp/src/bin/wifi_blinky.rs +++ b/examples/rp/src/bin/wifi_blinky.rs @@ -21,7 +21,7 @@ bind_interrupts!(struct Irqs { }); #[embassy_executor::task] -async fn wifi_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { +async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { runner.run().await } @@ -46,7 +46,7 @@ async fn main(spawner: Spawner) { static STATE: StaticCell = StaticCell::new(); let state = STATE.init(cyw43::State::new()); let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; - unwrap!(spawner.spawn(wifi_task(runner))); + unwrap!(spawner.spawn(cyw43_task(runner))); control.init(clm).await; control diff --git a/examples/rp/src/bin/wifi_scan.rs b/examples/rp/src/bin/wifi_scan.rs index 5f4c848a2..ab3529112 100644 --- a/examples/rp/src/bin/wifi_scan.rs +++ b/examples/rp/src/bin/wifi_scan.rs @@ -23,7 +23,7 @@ bind_interrupts!(struct Irqs { }); #[embassy_executor::task] -async fn wifi_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { +async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { runner.run().await } @@ -56,7 +56,7 @@ async fn main(spawner: Spawner) { static STATE: StaticCell = StaticCell::new(); let state = STATE.init(cyw43::State::new()); let (_net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; - unwrap!(spawner.spawn(wifi_task(runner))); + unwrap!(spawner.spawn(cyw43_task(runner))); control.init(clm).await; control diff --git a/examples/rp/src/bin/wifi_tcp_server.rs b/examples/rp/src/bin/wifi_tcp_server.rs index 5575df677..87487cbe8 100644 --- a/examples/rp/src/bin/wifi_tcp_server.rs +++ b/examples/rp/src/bin/wifi_tcp_server.rs @@ -27,11 +27,11 @@ bind_interrupts!(struct Irqs { PIO0_IRQ_0 => InterruptHandler; }); -const WIFI_NETWORK: &str = "EmbassyTest"; -const WIFI_PASSWORD: &str = "V8YxhKt5CdIAJFud"; +const WIFI_NETWORK: &str = "LadronDeWifi"; +const WIFI_PASSWORD: &str = "MBfcaedHmyRFE4kaQ1O5SsY8"; #[embassy_executor::task] -async fn wifi_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { +async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { runner.run().await } @@ -65,7 +65,7 @@ async fn main(spawner: Spawner) { static STATE: StaticCell = StaticCell::new(); let state = STATE.init(cyw43::State::new()); let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; - unwrap!(spawner.spawn(wifi_task(runner))); + unwrap!(spawner.spawn(cyw43_task(runner))); control.init(clm).await; control diff --git a/examples/rp/src/bin/wifi_webrequest.rs b/examples/rp/src/bin/wifi_webrequest.rs index 70b6f0949..e32be6e45 100644 --- a/examples/rp/src/bin/wifi_webrequest.rs +++ b/examples/rp/src/bin/wifi_webrequest.rs @@ -34,7 +34,7 @@ const WIFI_NETWORK: &str = "ssid"; // change to your network SSID const WIFI_PASSWORD: &str = "pwd"; // change to your network password #[embassy_executor::task] -async fn wifi_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { +async fn cyw43_task(runner: cyw43::Runner<'static, Output<'static>, PioSpi<'static, PIO0, 0, DMA_CH0>>) -> ! { runner.run().await } @@ -67,7 +67,7 @@ async fn main(spawner: Spawner) { static STATE: StaticCell = StaticCell::new(); let state = STATE.init(cyw43::State::new()); let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await; - unwrap!(spawner.spawn(wifi_task(runner))); + unwrap!(spawner.spawn(cyw43_task(runner))); control.init(clm).await; control -- cgit