Refactor esp_radio::init() (#4482)

* Refactor esp_radio::init()

* Introduce BleInitError and WifiInitError enums and call RadioRefGuard new and drop in critical section

* reviews (wifi::new returns WifiError)

* Changelog and MG

* Redo tests

* Add coex qa-test that drops ble and test if wifi still works
This commit is contained in:
Juraj Sadel 2025-11-20 14:24:10 +01:00 committed by GitHub
parent 85a962b8bd
commit 1e508e2e1e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
31 changed files with 567 additions and 250 deletions

View File

@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `ble::mac` to get the MAC address of the device (#4485)
- `last_calibration_result` to get the result of the last calibration (#4479)
- `BleInitError` for BLE init failures and `Internal`, `WrongClockConfig`, `SchedulerNotInitialized` and `Adc2IsUsed` variants to `WifiError (#4482)
### Changed
@ -19,6 +20,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- The `InitializationError::General` is replaced by `InitializationError::Internal` (#4467)
- The `WifiError::InternalError` is removed and `InternalWifiError` was folded into `WifiError`, some variants renamed for clarity (#4467)
- Refactor: extract submodules from `esp_radio::wifi` module (#4460)
- `BleConnector::new` returns `BleInitError` instead of `InvalidConfigError` (#4482)
- `esp_radio::init()` is not public anymore (#4482)
- `esp_radio::wifi::new` and `BleConnector::new` no longer take a `Controller` (#4482)
### Fixed
@ -29,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Removed
- The `serde` feature has been removed (#4435)
- `Controller` struct and `InitializationError::InterruptsDisabled` enum variant have been removed (#4482)
## [v0.17.0] - 2025-10-30

View File

@ -44,6 +44,7 @@ cfg-if = "1"
esp-hal = { version = "1.0.0", path = "../esp-hal", default-features = false, features = ["requires-unstable"] }
portable-atomic = { version = "1.11", default-features = false }
enumset = { version = "1.1", default-features = false, optional = true }
critical-section = { version = "1", default-features = false, optional = true }
# ⚠️ Unstable dependencies
esp-radio-rtos-driver = { version = "0.2.0", path = "../esp-radio-rtos-driver" }
@ -137,6 +138,7 @@ esp32 = [
"dep:esp-wifi-sys-esp32",
"esp-metadata-generated/esp32",
"xtensa-lx-rt/float-save-restore",
"critical-section",
]
##
esp32c2 = [
@ -145,6 +147,7 @@ esp32c2 = [
"esp-phy/esp32c2",
"dep:esp-wifi-sys-esp32c2",
"esp-metadata-generated/esp32c2",
"critical-section",
]
##
esp32c3 = [
@ -153,6 +156,7 @@ esp32c3 = [
"esp-phy/esp32c3",
"dep:esp-wifi-sys-esp32c3",
"esp-metadata-generated/esp32c3",
"critical-section",
]
##
esp32c6 = [
@ -161,6 +165,7 @@ esp32c6 = [
"esp-phy/esp32c6",
"dep:esp-wifi-sys-esp32c6",
"esp-metadata-generated/esp32c6",
"critical-section",
]
##
esp32h2 = [
@ -169,6 +174,7 @@ esp32h2 = [
"esp-phy/esp32h2",
"dep:esp-wifi-sys-esp32h2",
"esp-metadata-generated/esp32h2",
"critical-section",
]
##
esp32s2 = [
@ -178,6 +184,7 @@ esp32s2 = [
"dep:esp-wifi-sys-esp32s2",
"esp-metadata-generated/esp32s2",
"xtensa-lx-rt/float-save-restore",
"critical-section",
]
##
esp32s3 = [
@ -187,6 +194,7 @@ esp32s3 = [
"dep:esp-wifi-sys-esp32s3",
"esp-metadata-generated/esp32s3",
"xtensa-lx-rt/float-save-restore",
"critical-section",
]
#! ### Wireless Feature Flags

View File

@ -36,3 +36,21 @@ impl From<ScanMethod> for esp_radio::wifi::ScanMethod {
- `EapFastConfig`, `TlsPhase2Method` and `EapClientConfig` are now located in `wifi::ap::eap`.
You will need to update any imports in your project accordingly.
## `esp_radio::init()` and `Controller` is no longer available
WiFi initialization:
```diff
- let esp_radio_ctrl = esp_radio::init().unwrap();
- let (mut controller, interfaces) =
- esp_radio::wifi::new(&esp_radio_ctrl, wifi, Default::default()).unwrap();
+ let (mut controller, interfaces) = esp_radio::wifi::new(wifi, Default::default()).unwrap();
```
BLE initialization:
```diff
- static RADIO: StaticCell<esp_radio::Controller<'static>> = StaticCell::new();
- let radio = RADIO.init(esp_radio::init().unwrap());
- let connector = BleConnector::new(radio, bluetooth, Default::default()).unwrap();
+ let connector = BleConnector::new(bluetooth, Default::default()).unwrap();
```

View File

@ -12,16 +12,37 @@ use esp_hal::asynch::AtomicWaker;
use esp_phy::PhyInitGuard;
use crate::{
Controller,
InitializationError,
RadioRefGuard,
ble::{Config, InvalidConfigError, have_hci_read_data, read_hci, read_next, send_hci},
};
#[derive(Debug)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
/// Error enum for BLE initialization failures.
pub enum BleInitError {
/// Failure during initial validation of the provided configuration.
Config(InvalidConfigError),
/// Failure during the acquisition or initialization of the global radio hardware.
RadioInit(InitializationError),
}
// Implement the From trait for cleaner error mapping
impl From<InvalidConfigError> for BleInitError {
fn from(err: InvalidConfigError) -> Self {
BleInitError::Config(err)
}
}
/// A blocking HCI connector
#[instability::unstable]
pub struct BleConnector<'d> {
_phy_init_guard: PhyInitGuard<'d>,
_device: crate::hal::peripherals::BT<'d>,
_guard: RadioRefGuard,
}
impl Drop for BleConnector<'_> {
fn drop(&mut self) {
crate::ble::ble_deinit();
@ -31,14 +52,17 @@ impl<'d> BleConnector<'d> {
/// Create and init a new BLE connector.
#[instability::unstable]
pub fn new(
_init: &'d Controller<'d>,
device: crate::hal::peripherals::BT<'d>,
config: Config,
) -> Result<BleConnector<'d>, InvalidConfigError> {
) -> Result<BleConnector<'d>, BleInitError> {
let _guard = RadioRefGuard::new().map_err(BleInitError::RadioInit)?;
config.validate()?;
Ok(Self {
_phy_init_guard: crate::ble::ble_init(&config),
_device: device,
_guard,
})
}

View File

@ -136,8 +136,6 @@ extern crate alloc;
// MUST be the first module
mod fmt;
use core::marker::PhantomData;
use esp_hal::{
self as hal,
@ -226,6 +224,9 @@ pub(crate) mod memory_fence;
pub(crate) static ESP_RADIO_LOCK: RawMutex = RawMutex::new();
static RADIO_REFCOUNT: critical_section::Mutex<core::cell::Cell<u32>> =
critical_section::Mutex::new(core::cell::Cell::new(0));
// this is just to verify that we use the correct defaults in `build.rs`
#[allow(clippy::assertions_on_constants)] // TODO: try assert_eq once it's usable in const context
const _: () = {
@ -243,30 +244,6 @@ const _: () = {
};
};
#[derive(Debug, PartialEq, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
/// Controller for the ESP Radio driver.
pub struct Controller<'d> {
_inner: PhantomData<&'d ()>,
}
impl Drop for Controller<'_> {
fn drop(&mut self) {
// Disable coexistence
#[cfg(coex)]
{
unsafe { crate::wifi::os_adapter::coex_disable() };
unsafe { crate::wifi::os_adapter::coex_deinit() };
}
shutdown_radio_isr();
#[cfg(esp32)]
// Allow using `ADC2` again
release_adc2(unsafe { esp_hal::Internal::conjure() });
}
}
#[procmacros::doc_replace]
/// Initialize for using Wi-Fi and or BLE.
///
@ -289,35 +266,12 @@ impl Drop for Controller<'_> {
)]
/// - The function may return an error if interrupts are disabled.
/// - The function may return an error if initializing the underlying driver fails.
///
/// ## Example
///
/// For examples of the necessary setup, see your RTOS's documentation. If you are
/// using the `esp-rtos` crate, you will need to initialize the scheduler before calling this
/// function:
///
/// ```rust, no_run
/// # {before_snippet}
/// use esp_hal::{interrupt::software::SoftwareInterruptControl, timer::timg::TimerGroup};
///
/// let timg0 = TimerGroup::new(peripherals.TIMG0);
/// let software_interrupt = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
/// esp_rtos::start(timg0.timer0, software_interrupt.software_interrupt0);
///
/// // You can now start esp-radio:
/// let esp_radio_controller = esp_radio::init().unwrap();
/// # {after_snippet}
/// ```
pub fn init<'d>() -> Result<Controller<'d>, InitializationError> {
pub(crate) fn init() -> Result<(), InitializationError> {
#[cfg(esp32)]
if try_claim_adc2(unsafe { hal::Internal::conjure() }).is_err() {
return Err(InitializationError::Adc2IsUsed);
}
if crate::is_interrupts_disabled() {
return Err(InitializationError::InterruptsDisabled);
}
if !preempt::initialized() {
return Err(InitializationError::SchedulerNotInitialized);
}
@ -339,12 +293,75 @@ pub fn init<'d>() -> Result<Controller<'d>, InitializationError> {
#[cfg(coex)]
match crate::wifi::coex_initialize() {
0 => {}
error => return Err(InitializationError::Internal),
error => panic!("Failed to initialize coexistence, error code: {}", error),
}
Ok(Controller {
_inner: PhantomData,
})
debug!("Radio initialized");
Ok(())
}
pub(crate) fn deinit() {
// Disable coexistence
#[cfg(coex)]
{
unsafe { crate::wifi::os_adapter::coex_disable() };
unsafe { crate::wifi::os_adapter::coex_deinit() };
}
shutdown_radio_isr();
#[cfg(esp32)]
// Allow using `ADC2` again
release_adc2(unsafe { esp_hal::Internal::conjure() });
debug!("Radio deinitialized");
}
/// Management of the global reference count
/// and conditional hardware initialization/deinitialization.
#[derive(Debug)]
pub(crate) struct RadioRefGuard;
impl RadioRefGuard {
/// Increments the refcount. If the old count was 0, it performs hardware init.
/// If hardware init fails, it rolls back the refcount only once.
fn new() -> Result<Self, InitializationError> {
critical_section::with(|cs| {
debug!("Creating RadioRefGuard");
let rc = RADIO_REFCOUNT.borrow(cs);
let prev = rc.get();
rc.set(prev + 1);
if prev == 0
&& let Err(e) = init()
{
rc.set(prev);
return Err(e);
}
Ok(RadioRefGuard)
})
}
}
impl Drop for RadioRefGuard {
/// Decrements the refcount. If the count drops to 0, it performs hardware de-init.
fn drop(&mut self) {
critical_section::with(|cs| {
debug!("Dropping RadioRefGuard");
let rc = RADIO_REFCOUNT.borrow(cs);
let prev = rc.get();
rc.set(prev - 1);
if prev == 1 {
// Last user dropped, run de-initialization
deinit();
}
});
}
}
/// Returns true if at least some interrupt levels are disabled.
@ -360,19 +377,14 @@ fn is_interrupts_disabled() -> bool {
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
/// Error which can be returned during [`init`].
/// Error which can be returned during radio initialization.
#[non_exhaustive]
pub enum InitializationError {
/// An internal error occurred.
Internal,
/// An error from the Wi-Fi driver.
#[cfg(feature = "wifi")]
WifiError(WifiError),
/// The current CPU clock frequency is too low.
WrongClockConfig,
/// Tried to initialize while interrupts are disabled.
/// This is not supported.
InterruptsDisabled,
/// The scheduler is not initialized.
SchedulerNotInitialized,
#[cfg(esp32)]
@ -385,7 +397,6 @@ impl core::error::Error for InitializationError {}
impl core::fmt::Display for InitializationError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
InitializationError::Internal => write!(f, "An internal error occurred"),
#[cfg(feature = "wifi")]
InitializationError::WifiError(e) => {
write!(f, "Wi-Fi driver related error occurred: {e}")
@ -393,10 +404,6 @@ impl core::fmt::Display for InitializationError {
InitializationError::WrongClockConfig => {
write!(f, "The current CPU clock frequency is too low")
}
InitializationError::InterruptsDisabled => write!(
f,
"Attempted to initialize while interrupts are disabled (Unsupported)"
),
InitializationError::SchedulerNotInitialized => {
write!(f, "The scheduler is not initialized")
}

View File

@ -40,7 +40,8 @@ use self::{
#[instability::unstable]
pub use crate::sys::include::wifi_csi_info_t; // FIXME
use crate::{
Controller,
InitializationError,
RadioRefGuard,
common_adapter::*,
esp_wifi_result,
hal::ram,
@ -730,6 +731,16 @@ pub enum WifiError {
// definitive and exhausting list of errors we should expect and panicking because of an
// unmapped error.
Unknown(i32),
/// The current CPU clock frequency is too low.
WrongClockConfig,
/// The scheduler is not initialized.
SchedulerNotInitialized,
#[cfg(esp32)]
/// ADC2 is required by esp-radio, but it is in use by esp-hal.
Adc2IsUsed,
}
impl WifiError {
@ -809,12 +820,35 @@ impl core::fmt::Display for WifiError {
WifiError::Unknown(_) => {
write!(f, "An unknown error was reported by the Wi-Fi driver.")
}
WifiError::WrongClockConfig => {
write!(f, "The current CPU clock frequency is too low")
}
WifiError::SchedulerNotInitialized => {
write!(f, "The scheduler is not initialized")
}
#[cfg(esp32)]
WifiError::Adc2IsUsed => write!(
f,
"ADC2 cannot be used with `radio` functionality on `esp32`"
),
}
}
}
impl core::error::Error for WifiError {}
impl From<InitializationError> for WifiError {
fn from(err: InitializationError) -> Self {
match err {
InitializationError::WifiError(e) => e,
InitializationError::SchedulerNotInitialized => WifiError::SchedulerNotInitialized,
InitializationError::WrongClockConfig => WifiError::WrongClockConfig,
#[cfg(esp32)]
InitializationError::Adc2IsUsed => WifiError::Adc2IsUsed,
}
}
}
/// Events generated by the Wi-Fi driver.
#[derive(Debug, FromPrimitive, EnumSetType)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@ -1014,7 +1048,7 @@ pub(crate) unsafe extern "C" fn coex_init() -> i32 {
0
}
fn wifi_deinit() -> Result<(), crate::InitializationError> {
fn wifi_deinit() -> Result<(), crate::WifiError> {
esp_wifi_result!(unsafe { esp_wifi_stop() })?;
esp_wifi_result!(unsafe { esp_wifi_deinit_internal() })?;
esp_wifi_result!(unsafe { esp_supplicant_deinit() })?;
@ -2142,10 +2176,12 @@ impl Config {
/// Make sure to **not** call this function while interrupts are disabled, or IEEE 802.15.4 is
/// currently in use.
pub fn new<'d>(
_inited: &'d Controller<'d>,
device: crate::hal::peripherals::WIFI<'d>,
config: Config,
) -> Result<(WifiController<'d>, Interfaces<'d>), WifiError> {
let _guard = RadioRefGuard::new()?;
// TODO: Re-check, if not having interrupts disabled pre-condition is still true
if crate::is_interrupts_disabled() {
return Err(WifiError::Unsupported);
}
@ -2203,6 +2239,7 @@ pub fn new<'d>(
// Only create WifiController after we've enabled TRNG - otherwise returning an error from this
// function will cause panic because WifiController::drop tries to disable the TRNG.
let mut controller = WifiController {
_guard,
_phantom: Default::default(),
beacon_timeout: 6,
ap_beacon_timeout: 100,
@ -2232,6 +2269,7 @@ pub fn new<'d>(
/// Wi-Fi controller.
#[non_exhaustive]
pub struct WifiController<'d> {
_guard: RadioRefGuard,
_phantom: PhantomData<&'d ()>,
// Things we have to remember due to how esp-wifi works:
beacon_timeout: u16,
@ -2283,10 +2321,8 @@ impl WifiController<'_> {
/// # use esp_radio::wifi::{ap::AccessPointConfig, ModeConfig};
/// use esp_radio::wifi::Protocol;
///
/// let esp_radio_controller = esp_radio::init().unwrap();
///
/// let (mut wifi_controller, _interfaces) =
/// esp_radio::wifi::new(&esp_radio_controller, peripherals.WIFI, Default::default())?;
/// esp_radio::wifi::new(peripherals.WIFI, Default::default())?;
///
/// wifi_controller.set_config(&ModeConfig::AccessPoint(
/// AccessPointConfig::default().with_ssid("esp-radio".into()),

View File

@ -15,7 +15,6 @@ use esp_hal::{
};
use esp_radio::ble::controller::BleConnector;
use log::{info, warn};
use static_cell::StaticCell;
use trouble_host::prelude::*;
esp_bootloader_esp_idf::esp_app_desc!();
@ -29,11 +28,8 @@ async fn main(_s: Spawner) {
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
static RADIO: StaticCell<esp_radio::Controller<'static>> = StaticCell::new();
let radio = RADIO.init(esp_radio::init().unwrap());
let bluetooth = peripherals.BT;
let connector = BleConnector::new(radio, bluetooth, Default::default()).unwrap();
let connector = BleConnector::new(bluetooth, Default::default()).unwrap();
let controller: ExternalController<_, 1> = ExternalController::new(connector);
ble_bas_peripheral_run(controller).await;

View File

@ -19,7 +19,6 @@ use esp_hal::{
use esp_radio::ble::controller::BleConnector;
use heapless::Deque;
use log::info;
use static_cell::StaticCell;
use trouble_host::prelude::*;
esp_bootloader_esp_idf::esp_app_desc!();
@ -35,11 +34,8 @@ async fn main(_s: Spawner) {
let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
static RADIO: StaticCell<esp_radio::Controller<'static>> = StaticCell::new();
let radio = RADIO.init(esp_radio::init().unwrap());
let bluetooth = peripherals.BT;
let connector = BleConnector::new(radio, bluetooth, Default::default()).unwrap();
let connector = BleConnector::new(bluetooth, Default::default()).unwrap();
let controller: ExternalController<_, 1> = ExternalController::new(connector);
ble_scanner_run(controller).await;

View File

@ -16,23 +16,10 @@ use esp_hal::{
timer::timg::TimerGroup,
};
use esp_println::println;
use esp_radio::{
Controller,
esp_now::{BROADCAST_ADDRESS, PeerInfo},
};
use esp_radio::esp_now::{BROADCAST_ADDRESS, PeerInfo};
esp_bootloader_esp_idf::esp_app_desc!();
// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html
macro_rules! mk_static {
($t:ty,$val:expr) => {{
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
#[deny(unused_attributes)]
let x = STATIC_CELL.uninit().write(($val));
x
}};
}
#[esp_rtos::main]
async fn main(_spawner: Spawner) -> ! {
esp_println::logger::init_logger_from_env();
@ -45,11 +32,8 @@ async fn main(_spawner: Spawner) -> ! {
let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
let wifi = peripherals.WIFI;
let (mut controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, wifi, Default::default()).unwrap();
let (mut controller, interfaces) = esp_radio::wifi::new(wifi, Default::default()).unwrap();
controller.set_mode(esp_radio::wifi::WifiMode::Sta).unwrap();
controller.start().unwrap();

View File

@ -17,9 +17,12 @@ use esp_hal::{
timer::timg::TimerGroup,
};
use esp_println::println;
use esp_radio::{
Controller,
esp_now::{BROADCAST_ADDRESS, EspNowManager, EspNowReceiver, EspNowSender, PeerInfo},
use esp_radio::esp_now::{
BROADCAST_ADDRESS,
EspNowManager,
EspNowReceiver,
EspNowSender,
PeerInfo,
};
esp_bootloader_esp_idf::esp_app_desc!();
@ -46,11 +49,8 @@ async fn main(spawner: Spawner) -> ! {
let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
let wifi = peripherals.WIFI;
let (mut controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, wifi, Default::default()).unwrap();
let (mut controller, interfaces) = esp_radio::wifi::new(wifi, Default::default()).unwrap();
controller.set_mode(esp_radio::wifi::WifiMode::Sta).unwrap();
controller.start().unwrap();

View File

@ -31,11 +31,8 @@ fn main() -> ! {
let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = esp_radio::init().unwrap();
let wifi = peripherals.WIFI;
let (mut controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, wifi, Default::default()).unwrap();
let (mut controller, interfaces) = esp_radio::wifi::new(wifi, Default::default()).unwrap();
controller.set_mode(esp_radio::wifi::WifiMode::Sta).unwrap();
controller.start().unwrap();

View File

@ -50,11 +50,9 @@ fn main() -> ! {
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = esp_radio::init().unwrap();
// We must initialize some kind of interface and start it.
let (mut controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
controller.set_mode(wifi::WifiMode::Sta).unwrap();
controller.start().unwrap();

View File

@ -67,10 +67,8 @@ fn main() -> ! {
);
});
let esp_radio_ctrl = esp_radio::init().unwrap();
let (mut controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
let mut device = interfaces.ap;
let iface = create_interface(&mut device);

View File

@ -53,10 +53,8 @@ fn main() -> ! {
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = esp_radio::init().unwrap();
let (mut controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
let mut ap_device = interfaces.ap;
let ap_interface = create_interface(&mut ap_device);

View File

@ -74,13 +74,11 @@ fn main() -> ! {
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = esp_radio::init().unwrap();
let now = || time::Instant::now().duration_since_epoch().as_millis();
// initializing Bluetooth first results in a more stable WiFi connection on
// ESP32
let connector = BleConnector::new(&esp_radio_ctrl, peripherals.BT, Default::default()).unwrap();
let connector = BleConnector::new(peripherals.BT, Default::default()).unwrap();
let hci = HciConnector::new(connector, now);
let mut ble = Ble::new(&hci);
@ -102,7 +100,7 @@ fn main() -> ! {
println!("started advertising");
let (mut controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
let mut device = interfaces.sta;
let iface = create_interface(&mut device);

View File

@ -50,10 +50,8 @@ fn main() -> ! {
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = esp_radio::init().unwrap();
let (mut controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
let mut device = interfaces.sta;
let iface = create_interface(&mut device);

View File

@ -34,9 +34,13 @@ use esp_hal::{
timer::timg::TimerGroup,
};
use esp_println::{print, println};
use esp_radio::{
Controller,
wifi::{ModeConfig, WifiApState, WifiController, WifiDevice, WifiEvent, ap::AccessPointConfig},
use esp_radio::wifi::{
ModeConfig,
WifiApState,
WifiController,
WifiDevice,
WifiEvent,
ap::AccessPointConfig,
};
esp_bootloader_esp_idf::esp_app_desc!();
@ -66,10 +70,8 @@ async fn main(spawner: Spawner) -> ! {
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
let (controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
let device = interfaces.ap;

View File

@ -42,17 +42,14 @@ use esp_hal::{
timer::timg::TimerGroup,
};
use esp_println::{print, println};
use esp_radio::{
Controller,
wifi::{
ModeConfig,
WifiApState,
WifiController,
WifiDevice,
WifiEvent,
ap::AccessPointConfig,
sta::ClientConfig,
},
use esp_radio::wifi::{
ModeConfig,
WifiApState,
WifiController,
WifiDevice,
WifiEvent,
ap::AccessPointConfig,
sta::ClientConfig,
};
esp_bootloader_esp_idf::esp_app_desc!();
@ -83,10 +80,8 @@ async fn main(spawner: Spawner) -> ! {
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
let (mut controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
let wifi_ap_device = interfaces.ap;
let wifi_sta_device = interfaces.sta;

View File

@ -24,17 +24,14 @@ use esp_hal::{
timer::timg::TimerGroup,
};
use esp_println::println;
use esp_radio::{
Controller,
wifi::{
ModeConfig,
ScanConfig,
WifiController,
WifiDevice,
WifiEvent,
WifiStaState,
sta::ClientConfig,
},
use esp_radio::wifi::{
ModeConfig,
ScanConfig,
WifiController,
WifiDevice,
WifiEvent,
WifiStaState,
sta::ClientConfig,
};
esp_bootloader_esp_idf::esp_app_desc!();
@ -65,10 +62,8 @@ async fn main(spawner: Spawner) -> ! {
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
let (controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
let wifi_interface = interfaces.sta;

View File

@ -30,17 +30,14 @@ use esp_hal::{
timer::timg::TimerGroup,
};
use esp_println::println;
use esp_radio::{
Controller,
wifi::{
ModeConfig,
ScanConfig,
WifiController,
WifiDevice,
WifiEvent,
WifiStaState,
sta::ClientConfig,
},
use esp_radio::wifi::{
ModeConfig,
ScanConfig,
WifiController,
WifiDevice,
WifiEvent,
WifiStaState,
sta::ClientConfig,
};
use log::{error, info};
use sntpc::{NtpContext, NtpTimestampGenerator, get_time};
@ -98,10 +95,8 @@ async fn main(spawner: Spawner) -> ! {
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
let (controller, interfaces) =
esp_radio::wifi::new(esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
let wifi_interface = interfaces.sta;

View File

@ -41,11 +41,9 @@ fn main() -> ! {
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = esp_radio::init().unwrap();
// We must initialize some kind of interface and start it.
let (mut controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
controller.set_mode(wifi::WifiMode::Sta).unwrap();
controller.start().unwrap();

View File

@ -45,10 +45,8 @@ fn main() -> ! {
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = esp_radio::init().unwrap();
let (mut controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
let mut device = interfaces.sta;
let iface = create_interface(&mut device);

View File

@ -28,18 +28,18 @@ mod tests {
// Compile-time test to check that esp-radio can be reinitialized.
fn _esp_radio_can_be_reinited() {
let p = esp_hal::init(esp_hal::Config::default());
let mut p = esp_hal::init(esp_hal::Config::default());
let timg0: TimerGroup<'_, _> = TimerGroup::new(p.TIMG0);
let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0);
{
let _init = esp_radio::init().unwrap();
let _connector = BleConnector::new(p.BT.reborrow(), Default::default()).unwrap();
}
{
let _init = esp_radio::init().unwrap();
let _connector = BleConnector::new(p.BT.reborrow(), Default::default()).unwrap();
}
}
const H4_TYPE_COMMAND: u8 = 1;
@ -60,9 +60,8 @@ mod tests {
let timg0: TimerGroup<'_, _> = TimerGroup::new(p.TIMG0);
let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0);
let init = esp_radio::init().unwrap();
let mut connector = BleConnector::new(&init, p.BT, Default::default()).unwrap();
let mut connector = BleConnector::new(p.BT, Default::default()).unwrap();
// send reset cmd
pub const fn opcode(ogf: u8, ocf: u16) -> [u8; 2] {
@ -111,9 +110,8 @@ mod tests {
let timg0: TimerGroup<'_, _> = TimerGroup::new(p.TIMG0);
let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0);
let init = esp_radio::init().unwrap();
let mut connector = BleConnector::new(&init, p.BT, Default::default()).unwrap();
let mut connector = BleConnector::new(p.BT, Default::default()).unwrap();
// send reset cmd
pub const fn opcode(ogf: u8, ocf: u16) -> [u8; 2] {

View File

@ -2,6 +2,8 @@
mod init_tests {
use embassy_sync::{blocking_mutex::raw::CriticalSectionRawMutex, signal::Signal};
#[cfg(soc_has_wifi)]
use esp_hal::peripherals::WIFI;
#[cfg(riscv)]
use esp_hal::riscv::interrupt::free as interrupt_free;
#[cfg(xtensa)]
@ -15,21 +17,26 @@ mod init_tests {
peripherals::{Peripherals, TIMG0},
timer::timg::TimerGroup,
};
use esp_radio::InitializationError;
#[cfg(soc_has_bt)]
use esp_radio::ble::controller::BleConnector;
#[cfg(soc_has_wifi)]
use esp_radio::wifi::WifiError;
use esp_rtos::embassy::InterruptExecutor;
use hil_test::mk_static;
use static_cell::StaticCell;
#[embassy_executor::task]
#[cfg(soc_has_wifi)]
async fn try_init(
signal: &'static Signal<CriticalSectionRawMutex, Option<InitializationError>>,
signal: &'static Signal<CriticalSectionRawMutex, Option<WifiError>>,
wifi_peripheral: WIFI<'static>,
timer: TIMG0<'static>,
sw_int0: SoftwareInterrupt<'static, 0>,
) {
let timg0 = TimerGroup::new(timer);
esp_rtos::start(timg0.timer0, sw_int0);
match esp_radio::init() {
match esp_radio::wifi::new(wifi_peripheral, Default::default()) {
Ok(_) => signal.signal(None),
Err(err) => signal.signal(Some(err)),
}
@ -44,39 +51,45 @@ mod init_tests {
}
#[test]
fn test_init_fails_without_scheduler(_peripherals: Peripherals) {
#[cfg(soc_has_wifi)]
fn test_init_fails_without_scheduler(p: Peripherals) {
// esp-rtos must be initialized before esp-radio.
let init = esp_radio::init();
let init = esp_radio::wifi::new(p.WIFI, Default::default());
assert!(matches!(
init,
Err(InitializationError::SchedulerNotInitialized),
));
assert!(matches!(init, Err(WifiError::SchedulerNotInitialized)));
}
#[test]
#[cfg(soc_has_wifi)]
fn test_init_fails_cs(p: Peripherals) {
let timg0 = TimerGroup::new(p.TIMG0);
let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0);
let init = critical_section::with(|_| esp_radio::init());
let init = critical_section::with(|_| esp_radio::wifi::new(p.WIFI, Default::default()));
assert!(matches!(init, Err(InitializationError::InterruptsDisabled)));
match init {
Ok(_) => defmt::info!("Initialized wifi in critical section"),
Err(ref e) => defmt::info!("Failed to initialize wifi in critical section: {:?}", e),
}
assert!(matches!(init, Err(WifiError::Unsupported)));
}
#[test]
#[cfg(soc_has_wifi)]
fn test_init_fails_interrupt_free(p: Peripherals) {
let timg0 = TimerGroup::new(p.TIMG0);
let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0);
let init = interrupt_free(|| esp_radio::init());
let init = interrupt_free(|| esp_radio::wifi::new(p.WIFI, Default::default()));
assert!(matches!(init, Err(InitializationError::InterruptsDisabled)));
assert!(matches!(init, Err(WifiError::Unsupported)));
}
#[test]
#[cfg(soc_has_wifi)]
async fn test_init_fails_in_interrupt_executor_task(p: Peripherals) {
let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT);
@ -86,17 +99,19 @@ mod init_tests {
let spawner = executor_core0.start(Priority::Priority1);
let signal =
mk_static!(Signal<CriticalSectionRawMutex, Option<InitializationError>>, Signal::new());
let signal = mk_static!(Signal<CriticalSectionRawMutex, Option<WifiError>>,
Signal::new());
spawner.must_spawn(try_init(signal, p.TIMG0, sw_ints.software_interrupt0));
spawner.must_spawn(try_init(
signal,
p.WIFI,
p.TIMG0,
sw_ints.software_interrupt0,
));
let res = signal.wait().await;
assert!(matches!(
res,
Some(esp_radio::InitializationError::InterruptsDisabled),
));
assert!(matches!(res, Some(WifiError::Unsupported)));
}
#[test]
@ -106,16 +121,52 @@ mod init_tests {
let timg0 = TimerGroup::new(p.TIMG0);
esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0);
let esp_radio_ctrl =
&*mk_static!(esp_radio::Controller<'static>, esp_radio::init().unwrap());
// Initialize, then de-initialize wifi
let wifi =
esp_radio::wifi::new(&esp_radio_ctrl, p.WIFI.reborrow(), Default::default()).unwrap();
let wifi = esp_radio::wifi::new(p.WIFI.reborrow(), Default::default()).unwrap();
drop(wifi);
// Now, can we do it again?
let _wifi =
esp_radio::wifi::new(&esp_radio_ctrl, p.WIFI.reborrow(), Default::default()).unwrap();
let _wifi = esp_radio::wifi::new(p.WIFI.reborrow(), Default::default()).unwrap();
}
#[test]
#[cfg(soc_has_wifi)]
#[cfg(soc_has_bt)]
fn test_init_and_drop(mut p: Peripherals) {
let timg0: TimerGroup<'_, _> = TimerGroup::new(p.TIMG0);
let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0);
// Initialize BLE and WiFi then drop BLE
let connector = BleConnector::new(p.BT.reborrow(), Default::default()).unwrap();
let wifi = esp_radio::wifi::new(p.WIFI.reborrow(), Default::default()).unwrap();
drop(connector);
// Re-initialize BLE and drop WiFi and BLE
let connector = BleConnector::new(p.BT.reborrow(), Default::default()).unwrap();
drop(wifi);
drop(connector);
}
#[test]
#[cfg(soc_has_wifi)]
#[cfg(soc_has_bt)]
fn test_create_ble_wifi_drop_ble_wifi_create_wifi_ble(mut p: Peripherals) {
let timg0: TimerGroup<'_, _> = TimerGroup::new(p.TIMG0);
let sw_ints = SoftwareInterruptControl::new(p.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_ints.software_interrupt0);
// Initialize WiFi and BLE then drop BLE and WiFi
let wifi = esp_radio::wifi::new(p.WIFI.reborrow(), Default::default()).unwrap();
let connector = BleConnector::new(p.BT.reborrow(), Default::default()).unwrap();
drop(connector);
drop(wifi);
// Re-initialize WiFi and BLE then drop WiFi
let wifi = esp_radio::wifi::new(p.WIFI.reborrow(), Default::default()).unwrap();
let _connector = BleConnector::new(p.BT.reborrow(), Default::default()).unwrap();
drop(wifi);
}
}

View File

@ -6,6 +6,10 @@ license = "MIT OR Apache-2.0"
publish = false
[dependencies]
bleps = { git = "https://github.com/bjoernQ/bleps", package = "bleps", rev = "a5148d8", features = [
"async",
"macros",
] }
blocking-network-stack = { git = "https://github.com/bjoernQ/blocking-network-stack.git", rev = "b3ecefc", optional = true }
cfg-if = "1"
embassy-executor = "0.9.0"

View File

@ -33,9 +33,13 @@ use esp_hal::{
timer::timg::TimerGroup,
};
use esp_println::println;
use esp_radio::{
Controller,
wifi::{ModeConfig, WifiController, WifiDevice, WifiEvent, WifiStaState, sta::ClientConfig},
use esp_radio::wifi::{
ModeConfig,
WifiController,
WifiDevice,
WifiEvent,
WifiStaState,
sta::ClientConfig,
};
esp_bootloader_esp_idf::esp_app_desc!();
@ -81,10 +85,8 @@ async fn main(spawner: Spawner) -> ! {
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
let (mut controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
let wifi_interface = interfaces.sta;

View File

@ -22,17 +22,14 @@ use esp_hal::{
timer::timg::TimerGroup,
};
use esp_println::println;
use esp_radio::{
Controller,
wifi::{
ModeConfig,
WifiController,
WifiDevice,
WifiEvent,
WifiStaState,
sta::ClientConfig,
sta_state,
},
use esp_radio::wifi::{
ModeConfig,
WifiController,
WifiDevice,
WifiEvent,
WifiStaState,
sta::ClientConfig,
sta_state,
};
use static_cell::StaticCell;
@ -245,9 +242,8 @@ async fn main(spawner: Spawner) {
.build(rx_descriptors);
// WiFi + network stack
let esp_radio_ctrl = &*mk_static!(Controller<'static>, esp_radio::init().unwrap());
let (controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
let wifi_interface = interfaces.sta;
let config = embassy_net::Config::dhcpv4(Default::default());

View File

@ -9,7 +9,7 @@
extern crate alloc;
use alloc::{boxed::Box, string::ToString};
use alloc::string::ToString;
use embassy_executor::Spawner;
use embassy_time::{Duration, Timer, WithTimeout};
@ -42,13 +42,8 @@ async fn main(_spawner: Spawner) {
let timg0 = TimerGroup::new(peripherals.TIMG0);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_wifi_ctrl = Box::leak(Box::new(esp_radio::init().unwrap()));
let (mut controller, _interfaces) = esp_radio::wifi::new(
esp_wifi_ctrl,
peripherals.WIFI.reborrow(),
Config::default(),
)
.unwrap();
let (mut controller, _interfaces) =
esp_radio::wifi::new(peripherals.WIFI.reborrow(), Config::default()).unwrap();
let mut i = 0;
loop {

View File

@ -69,10 +69,8 @@ fn main() -> ! {
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = esp_radio::init().unwrap();
let (mut controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
let mut device = interfaces.sta;
let iface = create_interface(&mut device);

View File

@ -46,10 +46,8 @@ fn main() -> ! {
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let esp_radio_ctrl = esp_radio::init().unwrap();
let (mut controller, interfaces) =
esp_radio::wifi::new(&esp_radio_ctrl, peripherals.WIFI, Default::default()).unwrap();
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
let mut device = interfaces.sta;
let iface = create_interface(&mut device);

View File

@ -0,0 +1,231 @@
//! This example creates BLE and WiFi and while waiting for WiFi to connect, BLE gets dropped - it
//! shows that WiFi still works afterwards.
//!
//! - set SSID and PASSWORD env variable
//! - gets an ip address via DHCP
//! - performs an HTTP get request to some "random" server
//!
//! Note: On ESP32-C2 and ESP32-C3 you need a wifi-heap size of 70000, on
//! ESP32-C6 you need 80000 and a tx_queue_size of 10
//% FEATURES: esp-radio esp-radio/wifi esp-radio/ble esp-radio/smoltcp
//% FEATURES: esp-radio/unstable esp-hal/unstable
//% CHIPS: esp32 esp32c2 esp32c3 esp32c6 esp32s3
#![no_std]
#![no_main]
use core::net::Ipv4Addr;
use bleps::{
Ble,
HciConnector,
ad_structure::{
AdStructure,
BR_EDR_NOT_SUPPORTED,
LE_GENERAL_DISCOVERABLE,
create_advertising_data,
},
att::Uuid,
};
use blocking_network_stack::Stack;
use embedded_io::*;
use esp_alloc as _;
use esp_backtrace as _;
use esp_hal::{
clock::CpuClock,
interrupt::software::SoftwareInterruptControl,
main,
ram,
rng::Rng,
time::{self, Duration},
timer::timg::TimerGroup,
};
use esp_println::{print, println};
use esp_radio::{
ble::controller::BleConnector,
wifi::{ModeConfig, sta::ClientConfig},
};
use smoltcp::{
iface::{SocketSet, SocketStorage},
wire::{DhcpOption, IpAddress},
};
esp_bootloader_esp_idf::esp_app_desc!();
const SSID: &str = env!("SSID");
const PASSWORD: &str = env!("PASSWORD");
#[main]
fn main() -> ! {
esp_println::logger::init_logger_from_env();
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
let peripherals = esp_hal::init(config);
// COEX needs more RAM - add some more
#[cfg(feature = "esp32")]
{
esp_alloc::heap_allocator!(#[ram(reclaimed)] size: 96 * 1024);
esp_alloc::heap_allocator!(size: 24 * 1024);
}
#[cfg(not(feature = "esp32"))]
{
esp_alloc::heap_allocator!(#[ram(reclaimed)] size: 64 * 1024);
esp_alloc::heap_allocator!(size: 64 * 1024);
}
let timg0 = TimerGroup::new(peripherals.TIMG0);
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
let now = || time::Instant::now().duration_since_epoch().as_millis();
// initializing Bluetooth first results in a more stable WiFi connection on
// ESP32
let connector = BleConnector::new(peripherals.BT, Default::default()).unwrap();
let hci = HciConnector::new(connector, now);
let mut ble = Ble::new(&hci);
println!("{:?}", ble.init());
println!("{:?}", ble.cmd_set_le_advertising_parameters());
println!(
"{:?}",
ble.cmd_set_le_advertising_data(
create_advertising_data(&[
AdStructure::Flags(LE_GENERAL_DISCOVERABLE | BR_EDR_NOT_SUPPORTED),
AdStructure::ServiceUuids16(&[Uuid::Uuid16(0x1809)]),
AdStructure::CompleteLocalName(esp_hal::chip!()),
])
.unwrap()
)
);
println!("{:?}", ble.cmd_set_le_advertise_enable(true));
println!("started advertising");
let (mut controller, interfaces) =
esp_radio::wifi::new(peripherals.WIFI, Default::default()).unwrap();
let mut device = interfaces.sta;
let iface = create_interface(&mut device);
controller
.set_power_saving(esp_radio::wifi::PowerSaveMode::None)
.unwrap();
let mut socket_set_entries: [SocketStorage; 3] = Default::default();
let mut socket_set = SocketSet::new(&mut socket_set_entries[..]);
let mut dhcp_socket = smoltcp::socket::dhcpv4::Socket::new();
// we can set a hostname here (or add other DHCP options)
dhcp_socket.set_outgoing_options(&[DhcpOption {
kind: 12,
data: b"esp-radio",
}]);
socket_set.add(dhcp_socket);
let rng = Rng::new();
let stack = Stack::new(iface, device, socket_set, now, rng.random());
let client_config = ModeConfig::Client(
ClientConfig::default()
.with_ssid(SSID.into())
.with_password(PASSWORD.into()),
);
let res = controller.set_config(&client_config);
println!("wifi_set_configuration returned {:?}", res);
controller.start().unwrap();
println!("is wifi started: {:?}", controller.is_started());
println!("{:?}", controller.capabilities());
println!("wifi_connect {:?}", controller.connect());
drop(hci);
println!("Dropped BLE HCI");
// wait to get connected
println!("Wait to get connected");
loop {
match controller.is_connected() {
Ok(true) => break,
Ok(false) => {}
Err(err) => {
println!("{:?}", err);
loop {}
}
}
}
println!("{:?}", controller.is_connected());
// wait for getting an ip address
println!("Wait to get an ip address");
loop {
stack.work();
if stack.is_iface_up() {
println!("got ip {:?}", stack.get_ip_info());
break;
}
}
println!("Start busy loop on main");
let mut rx_buffer = [0u8; 128];
let mut tx_buffer = [0u8; 128];
let mut socket = stack.get_socket(&mut rx_buffer, &mut tx_buffer);
loop {
println!("Making HTTP request");
socket.work();
socket
.open(IpAddress::Ipv4(Ipv4Addr::new(142, 250, 185, 115)), 80)
.unwrap();
socket
.write(b"GET / HTTP/1.0\r\nHost: www.mobile-j.de\r\n\r\n")
.unwrap();
socket.flush().unwrap();
let deadline = time::Instant::now() + Duration::from_secs(20);
let mut buffer = [0u8; 128];
while let Ok(len) = socket.read(&mut buffer) {
let to_print = unsafe { core::str::from_utf8_unchecked(&buffer[..len]) };
print!("{}", to_print);
if time::Instant::now() > deadline {
println!("Timeout");
break;
}
}
println!();
socket.disconnect();
let deadline = time::Instant::now() + Duration::from_secs(5);
while time::Instant::now() < deadline {
socket.work();
}
}
}
// some smoltcp boilerplate
fn timestamp() -> smoltcp::time::Instant {
smoltcp::time::Instant::from_micros(
esp_hal::time::Instant::now()
.duration_since_epoch()
.as_micros() as i64,
)
}
pub fn create_interface(device: &mut esp_radio::wifi::WifiDevice) -> smoltcp::iface::Interface {
// users could create multiple instances but since they only have one WifiDevice
// they probably can't do anything bad with that
smoltcp::iface::Interface::new(
smoltcp::iface::Config::new(smoltcp::wire::HardwareAddress::Ethernet(
smoltcp::wire::EthernetAddress::from_bytes(&device.mac_address()),
)),
device,
timestamp(),
)
}