mirror of
https://github.com/esp-rs/esp-hal.git
synced 2025-12-29 20:51:10 +00:00
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:
parent
85a962b8bd
commit
1e508e2e1e
@ -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
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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();
|
||||
```
|
||||
|
||||
@ -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,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -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")
|
||||
}
|
||||
|
||||
@ -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()),
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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();
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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] {
|
||||
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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());
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
231
qa-test/src/bin/wifi_survives_ble_drop.rs
Normal file
231
qa-test/src/bin/wifi_survives_ble_drop.rs
Normal 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(),
|
||||
)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user