General esp-radio cleanup (#4017)

* General esp-radio cleanup

* changelog

* MG

* fix build error

---------

Co-authored-by: Dániel Buga <bugadani@gmail.com>
This commit is contained in:
Juraj Sadel 2025-09-02 18:48:17 +02:00 committed by GitHub
parent f2bae5a923
commit fe0a04517f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
17 changed files with 177 additions and 146 deletions

View File

@ -27,8 +27,14 @@ const KNOWN_HELPERS: &[&str] = &[
"skip_getter",
// Feature gate the generated setters and getters by the "unstable" feature
"unstable",
// Generate a by-reference getter instead of a by-value getter for non-`Copy` types
"reference",
];
/// A lightweight version of the `Builder` derive macro that only generates
/// setters and getters for each field of a struct.
///
/// <https://matklad.github.io/2022/05/29/builder-lite.html>
pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
let input = syn::parse_macro_input!(item as syn::DeriveInput);
@ -131,13 +137,28 @@ pub fn builder_lite_derive(item: TokenStream) -> TokenStream {
None
}
});
fns.push(quote! {
#(#docs)*
#unstable
pub fn #field_ident(&self) -> #field_type {
self.#field_ident
}
});
let is_non_copy = helper_attributes.iter().any(|h| h == "reference");
if is_non_copy {
// Generate a by-reference getter for non-`Copy` types
fns.push(quote! {
#(#docs)*
#unstable
pub fn #field_ident(&self) -> &#field_type {
&self.#field_ident
}
});
} else {
// Generate a by-value getter for `Copy` types (the default)
fns.push(quote! {
#(#docs)*
#unstable
pub fn #field_ident(&self) -> #field_type {
self.#field_ident
}
});
}
}
}

View File

@ -14,6 +14,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Added unstable `wifi-eap` feature (#3924)
- Optional `max` field in `ScanConfig` to allow limiting the number of returned results (#3963)
- `set_phy_calibration_data` and `phy_calibration_data` (#4001)
- common traits for `Protocol`, `Country`, (#4017)
- `BuilderLite` pattern to `AccessPointConfig`, `ClientConfig` (#4017)
- lifetime to `Sniffer` (#4017)
### Changed
@ -25,10 +28,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `esp-ieee802154` package has been folded into `esp-radio`, it's now alloc. (#3861, #3890)
- `ble`, `esp-now`, `csi`, `sniffer`, `esp-ieee802154` and `smoltcp` features and APIs marked as unstable (#3865)
- Update bt-hci version to add additional HCI commands (#3920)
- A number of enums/structs have been marked as `#[non_exhaustive]` (#3981)
- `AuthMethod`, `Protocol`, `AccessPointInfo`, `AccessPointConfiguration`, `ClientConfiguration`, `Capability`, `Configuration`, `WifiEvent`, `InternalWifiError`, `ScanTypeConfig`, and `WifiState`
- A number of enums/structs have been marked as `#[non_exhaustive]` (#3981, #4017)
- `AuthMethod`, `Protocol`, `AccessPointInfo`, `AccessPointConfiguration`, `ClientConfiguration`, `Capability`, `Configuration`, `WifiEvent`, `InternalWifiError`, `ScanTypeConfig`, `WifiState`, and `WifiMode`
- The `Configuration`, `ClientConfiguration`, `AccessPointConfiguration`, and `EapClientConfiguration` enums have been renamed to `Config`, `ClientConfig`, `AccessPointConfig`, and `EapClientConfig` (#3994)
- Error types implements `core::error:Error`
- `ap_state()` and `sta_state()` marked as stable (#4017)
- `wifi_state()` marked as unstable (#4017)
- `ap_mac` and `sta_mac` returns `[u8; 6]` instead of taking an `[u8; 6]` argument (#4017)
- `RxControlInfo` hidden behind `esp-now` feature (#4017)
- `set_configuration()` to `set_config() (#4017)
### Fixed
@ -39,6 +47,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Removed
- `scan_with_config_sync_max`, `scan_with_config_sync_max`, `scan_n`, and `scan_n_async` functions (#3963)
- `EnumSetType` from `Protocol`, `Country` enums (#4017)
- `AtomicWifiState` and `WifiDeviceMode` are not available anymore (#4029)
## [v0.15.0] - 2025-07-16

View File

@ -69,4 +69,22 @@ use esp_radio::wifi::{
+ Config,
+ EapClientConfig
}
```
Same for `set_configuration()` to `set_config()`:
```diff
- let res = controller.set_configuration(&ap_config);
+ let res = controller.set_config(&ap_config);
```
## BuilderLite pattern `AccessPointConfig` and `ClientConfig`
```diff
- let ap_config = Config::AccessPoint({
- let mut config = AccessPointConfig::default();
- config.ssid = "esp-radio".into();
- config
- });
+ let ap_config = Config::AccessPoint(AccessPointConfig::default().with_ssid("esp-radio".into()));
```

View File

@ -19,13 +19,14 @@ use core::{
use enumset::{EnumSet, EnumSetType};
use esp_hal::{asynch::AtomicWaker, sync::Locked};
#[cfg(all(any(feature = "sniffer", feature = "esp-now"), feature = "unstable"))]
use esp_wifi_sys::include::wifi_pkt_rx_ctrl_t;
use esp_wifi_sys::include::{
WIFI_PROTOCOL_11AX,
WIFI_PROTOCOL_11B,
WIFI_PROTOCOL_11G,
WIFI_PROTOCOL_11N,
WIFI_PROTOCOL_LR,
wifi_pkt_rx_ctrl_t,
wifi_scan_channel_bitmap_t,
};
#[cfg(feature = "wifi-eap")]
@ -149,7 +150,7 @@ use crate::binary::{
};
/// Supported Wi-Fi authentication methods.
#[derive(Debug, Default, PartialOrd, EnumSetType)]
#[derive(Copy, Clone, Debug, Default, Eq, PartialEq, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
@ -210,7 +211,7 @@ pub enum Protocol {
}
/// Secondary Wi-Fi channels.
#[derive(EnumSetType, Debug, PartialOrd)]
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[derive(Default)]
@ -271,7 +272,7 @@ impl defmt::Format for Country {
}
/// Information about a detected Wi-Fi access point.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
#[derive(Clone, Debug, Default, Eq, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
@ -301,33 +302,35 @@ pub struct AccessPointInfo {
}
/// Configuration for a Wi-Fi access point.
#[derive(Clone, PartialEq, Eq)]
#[derive(BuilderLite, Clone, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub struct AccessPointConfig {
/// The SSID of the access point.
pub ssid: String,
#[builder_lite(reference)]
ssid: String,
/// Whether the SSID is hidden or visible.
pub ssid_hidden: bool,
ssid_hidden: bool,
/// The channel the access point will operate on.
pub channel: u8,
channel: u8,
/// The secondary channel configuration.
pub secondary_channel: Option<u8>,
secondary_channel: Option<u8>,
/// The set of protocols supported by the access point.
pub protocols: EnumSet<Protocol>,
protocols: EnumSet<Protocol>,
/// The authentication method to be used by the access point.
pub auth_method: AuthMethod,
auth_method: AuthMethod,
/// The password for securing the access point (if applicable).
pub password: String,
#[builder_lite(reference)]
password: String,
/// The maximum number of connections allowed on the access point.
pub max_connections: u16,
max_connections: u16,
}
impl AccessPointConfig {
@ -419,25 +422,27 @@ impl defmt::Format for AccessPointConfig {
}
/// Client configuration for a Wi-Fi connection.
#[derive(Clone, PartialEq, Eq, Default)]
#[derive(BuilderLite, Clone, Default, Eq, PartialEq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub struct ClientConfig {
/// The SSID of the Wi-Fi network.
pub ssid: String,
#[builder_lite(reference)]
ssid: String,
/// The BSSID (MAC address) of the client.
pub bssid: Option<[u8; 6]>,
bssid: Option<[u8; 6]>,
// pub protocol: Protocol,
/// The authentication method for the Wi-Fi connection.
pub auth_method: AuthMethod,
auth_method: AuthMethod,
/// The password for the Wi-Fi connection.
pub password: String,
#[builder_lite(reference)]
password: String,
/// The Wi-Fi channel to connect to.
pub channel: Option<u8>,
channel: Option<u8>,
}
impl ClientConfig {
@ -908,6 +913,7 @@ impl AuthMethodExt for AuthMethod {
#[derive(Debug, Clone, Copy, PartialEq)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
#[non_exhaustive]
pub enum WifiMode {
/// Station mode.
Sta,
@ -1401,18 +1407,22 @@ pub enum InternalWifiError {
TxDisallowed = 0x3016,
}
/// Get the STA MAC address
pub fn sta_mac(mac: &mut [u8; 6]) {
/// Get the AP MAC address of the device.
pub fn ap_mac() -> [u8; 6] {
let mut mac = [0u8; 6];
unsafe {
read_mac(mac as *mut u8, 0);
read_mac(mac.as_mut_ptr(), 1);
}
mac
}
/// Get the AP MAC address
pub fn ap_mac(mac: &mut [u8; 6]) {
/// Get the STA MAC address of the device.
pub fn sta_mac() -> [u8; 6] {
let mut mac = [0u8; 6];
unsafe {
read_mac(mac as *mut u8, 1);
read_mac(mac.as_mut_ptr(), 0);
}
mac
}
pub(crate) fn wifi_init() -> Result<(), WifiError> {
@ -1600,7 +1610,7 @@ pub(crate) fn wifi_start() -> Result<(), WifiError> {
Ok(())
}
/// Configuration for active or passive scan. For details see the [Wi-Fi Alliance FAQ](https://www.wi-fi.org/knowledge-center/faq/what-are-passive-and-active-scanning).
/// Configuration for active or passive scan.
///
/// # Comparison of active and passive scan
///
@ -1796,16 +1806,8 @@ enum WifiDeviceMode {
impl WifiDeviceMode {
fn mac_address(&self) -> [u8; 6] {
match self {
WifiDeviceMode::Sta => {
let mut mac = [0; 6];
sta_mac(&mut mac);
mac
}
WifiDeviceMode::Ap => {
let mut mac = [0; 6];
ap_mac(&mut mac);
mac
}
WifiDeviceMode::Sta => sta_mac(),
WifiDeviceMode::Ap => ap_mac(),
}
}
@ -1952,9 +1954,10 @@ fn convert_ap_info(record: &include::wifi_ap_record_t) -> AccessPointInfo {
/// The radio metadata header of the received packet, which is the common header
/// at the beginning of all RX callback buffers in promiscuous mode.
#[cfg(not(any(esp32c6)))]
#[cfg(not(esp32c6))]
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
pub struct RxControlInfo {
/// Received Signal Strength Indicator (RSSI) of the packet, in dBm.
pub rssi: i32,
@ -2012,6 +2015,7 @@ pub struct RxControlInfo {
#[cfg(esp32c6)]
#[derive(Debug, Clone, Copy)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
pub struct RxControlInfo {
/// Received Signal Strength Indicator (RSSI) of the packet, in dBm.
pub rssi: i32,
@ -2054,6 +2058,8 @@ pub struct RxControlInfo {
/// Indicate whether the reception frame is from interface 0.
pub rxmatch0: u32,
}
#[cfg(all(any(feature = "esp-now", feature = "sniffer"), feature = "unstable"))]
impl RxControlInfo {
/// Create an instance from a raw pointer to [wifi_pkt_rx_ctrl_t].
///
@ -2168,17 +2174,21 @@ unsafe extern "C" fn promiscuous_rx_cb(buf: *mut core::ffi::c_void, frame_type:
#[instability::unstable]
/// A Wi-Fi sniffer.
#[non_exhaustive]
pub struct Sniffer {}
pub struct Sniffer<'d> {
_phantom: PhantomData<&'d ()>,
}
#[cfg(all(feature = "sniffer", feature = "unstable"))]
impl Sniffer {
impl Sniffer<'_> {
pub(crate) fn new() -> Self {
// This shouldn't fail, since the way this is created, means that wifi will
// always be initialized.
unwrap!(esp_wifi_result!(unsafe {
esp_wifi_set_promiscuous_rx_cb(Some(promiscuous_rx_cb))
}));
Self {}
Self {
_phantom: PhantomData,
}
}
/// Set promiscuous mode enabled or disabled.
#[instability::unstable]
@ -2710,7 +2720,7 @@ pub struct Interfaces<'d> {
/// Wi-Fi sniffer interface.
#[cfg(all(feature = "sniffer", feature = "unstable"))]
#[cfg_attr(docsrs, doc(cfg(feature = "unstable")))]
pub sniffer: Sniffer,
pub sniffer: Sniffer<'d>,
}
/// Create a Wi-Fi controller and it's associated interfaces.
@ -2821,6 +2831,8 @@ impl WifiController<'_> {
/// ```
/// wifi_controller.set_protocol(Protocol::P802D11BGNLR.into());
/// ```
/// # Note
/// Calling this function before `set_config` will return an error.
pub fn set_protocol(&mut self, protocols: EnumSet<Protocol>) -> Result<(), WifiError> {
let protocol = protocols
.into_iter()
@ -2956,7 +2968,7 @@ impl WifiController<'_> {
///
/// If you don't intend to use Wi-Fi anymore at all consider tearing down
/// Wi-Fi completely.
pub fn set_configuration(&mut self, conf: &Config) -> Result<(), WifiError> {
pub fn set_config(&mut self, conf: &Config) -> Result<(), WifiError> {
conf.validate()?;
let mode = match conf {
@ -2992,7 +3004,7 @@ impl WifiController<'_> {
/// Set the Wi-Fi mode.
///
/// This will override the mode inferred by [Self::set_configuration].
/// This will override the mode inferred by [Self::set_config].
pub fn set_mode(&mut self, mode: WifiMode) -> Result<(), WifiError> {
esp_wifi_result!(unsafe { esp_wifi_set_mode(mode.into()) })?;
Ok(())

View File

@ -51,13 +51,11 @@ pub(crate) static STA_STATE: AtomicWifiState = AtomicWifiState::new(WifiState::I
pub(crate) static AP_STATE: AtomicWifiState = AtomicWifiState::new(WifiState::Invalid);
/// Get the current state of the AP.
#[instability::unstable]
pub fn ap_state() -> WifiState {
AP_STATE.load(Ordering::Relaxed)
}
/// Get the current state of the STA.
#[instability::unstable]
pub fn sta_state() -> WifiState {
STA_STATE.load(Ordering::Relaxed)
}
@ -93,6 +91,7 @@ pub(crate) fn reset_sta_state() {
///
/// This does not support AP-STA mode. Use one of `sta_state` or
/// `ap_state` instead.
#[instability::unstable]
pub fn wifi_state() -> WifiState {
use super::WifiMode;
match WifiMode::current() {

View File

@ -78,12 +78,8 @@ fn main() -> ! {
let socket_set = SocketSet::new(&mut socket_set_entries[..]);
let mut stack = Stack::new(iface, device, socket_set, now, rng.random());
let client_config = Config::AccessPoint({
let mut config = AccessPointConfig::default();
config.ssid = "esp-radio".into();
config
});
let res = controller.set_configuration(&client_config);
let ap_config = Config::AccessPoint(AccessPointConfig::default().with_ssid("esp-radio".into()));
let res = controller.set_config(&ap_config);
println!("wifi_set_configuration returned {:?}", res);
controller.start().unwrap();

View File

@ -72,19 +72,12 @@ fn main() -> ! {
let sta_stack = Stack::new(sta_interface, sta_device, sta_socket_set, now, rng.random());
let client_config = Config::Mixed(
{
let mut client_config = ClientConfig::default();
client_config.ssid = SSID.into();
client_config.password = PASSWORD.into();
client_config
},
{
let mut ap_config = AccessPointConfig::default();
ap_config.ssid = "esp-radio".into();
ap_config
},
ClientConfig::default()
.with_ssid(SSID.into())
.with_password(PASSWORD.into()),
AccessPointConfig::default().with_ssid("esp-radio".into()),
);
let res = controller.set_configuration(&client_config);
let res = controller.set_config(&client_config);
println!("wifi_set_configuration returned {:?}", res);
controller.start().unwrap();

View File

@ -121,13 +121,13 @@ fn main() -> ! {
let rng = Rng::new();
let stack = Stack::new(iface, device, socket_set, now, rng.random());
let client_config = Config::Client({
let mut config = ClientConfig::default();
config.ssid = SSID.into();
config.password = PASSWORD.into();
config
});
let res = controller.set_configuration(&client_config);
let client_config = Config::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();

View File

@ -72,13 +72,12 @@ fn main() -> ! {
.set_power_saving(esp_radio::config::PowerSaveMode::None)
.unwrap();
let client_config = Config::Client({
let mut config = ClientConfig::default();
config.ssid = SSID.into();
config.password = PASSWORD.into();
config
});
let res = controller.set_configuration(&client_config);
let client_config = Config::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();

View File

@ -249,12 +249,9 @@ async fn connection(mut controller: WifiController<'static>) {
_ => {}
}
if !matches!(controller.is_started(), Ok(true)) {
let client_config = Config::AccessPoint({
let mut config = AccessPointConfig::default();
config.ssid = "esp-radio".into();
config
});
controller.set_configuration(&client_config).unwrap();
let client_config =
Config::AccessPoint(AccessPointConfig::default().with_ssid("esp-radio".into()));
controller.set_config(&client_config).unwrap();
println!("Starting wifi");
controller.start_async().await.unwrap();
println!("Wifi started!");

View File

@ -119,19 +119,12 @@ async fn main(spawner: Spawner) -> ! {
);
let client_config = Config::Mixed(
{
let mut client_config = ClientConfig::default();
client_config.ssid = SSID.into();
client_config.password = PASSWORD.into();
client_config
},
{
let mut ap_config = AccessPointConfig::default();
ap_config.ssid = "esp-radio".into();
ap_config
},
ClientConfig::default()
.with_ssid(SSID.into())
.with_password(PASSWORD.into()),
AccessPointConfig::default().with_ssid("esp-radio".into()),
);
controller.set_configuration(&client_config).unwrap();
controller.set_config(&client_config).unwrap();
spawner.spawn(connection(controller)).ok();
spawner.spawn(net_task(ap_runner)).ok();

View File

@ -159,13 +159,12 @@ async fn connection(mut controller: WifiController<'static>) {
_ => {}
}
if !matches!(controller.is_started(), Ok(true)) {
let client_config = Config::Client({
let mut config = ClientConfig::default();
config.ssid = SSID.into();
config.password = PASSWORD.into();
config
});
controller.set_configuration(&client_config).unwrap();
let client_config = Config::Client(
ClientConfig::default()
.with_ssid(SSID.into())
.with_password(PASSWORD.into()),
);
controller.set_config(&client_config).unwrap();
println!("Starting wifi");
controller.start_async().await.unwrap();
println!("Wifi started!");

View File

@ -215,13 +215,12 @@ async fn connection(mut controller: WifiController<'static>) {
Timer::after(Duration::from_millis(5000)).await
}
if !matches!(controller.is_started(), Ok(true)) {
let client_config = Config::Client({
let mut config = ClientConfig::default();
config.ssid = SSID.into();
config.password = PASSWORD.into();
config
});
controller.set_configuration(&client_config).unwrap();
let client_config = Config::Client(
ClientConfig::default()
.with_ssid(SSID.into())
.with_password(PASSWORD.into()),
);
controller.set_config(&client_config).unwrap();
println!("Starting wifi");
controller.start_async().await.unwrap();
println!("Wifi started!");

View File

@ -61,13 +61,12 @@ fn main() -> ! {
let now = || time::Instant::now().duration_since_epoch().as_millis();
let mut stack = Stack::new(iface, device, socket_set, now, rng.random());
let client_config = Config::Client({
let mut config = ClientConfig::default();
config.ssid = SSID.into();
config.password = PASSWORD.into();
config
});
let res = controller.set_configuration(&client_config);
let client_config = Config::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();

View File

@ -161,13 +161,12 @@ async fn connection(mut controller: WifiController<'static>) {
_ => {}
}
if !matches!(controller.is_started(), Ok(true)) {
let client_config = Config::Client({
let mut config = ClientConfig::default();
config.ssid = SSID.into();
config.password = PASSWORD.into();
config
});
controller.set_configuration(&client_config).unwrap();
let client_config = Config::Client(
ClientConfig::default()
.with_ssid(SSID.into())
.with_password(PASSWORD.into()),
);
controller.set_config(&client_config).unwrap();
println!("Starting wifi");
controller.start_async().await.unwrap();
println!("Wifi started!");

View File

@ -90,13 +90,12 @@ fn main() -> ! {
let now = || time::Instant::now().duration_since_epoch().as_millis();
let stack = Stack::new(iface, device, socket_set, now, rng.random());
let client_config = Config::Client({
let mut config = ClientConfig::default();
config.ssid = SSID.into();
config.password = PASSWORD.into();
config
});
let res = controller.set_configuration(&client_config);
let client_config = Config::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();

View File

@ -60,13 +60,12 @@ fn main() -> ! {
let now = || time::Instant::now().duration_since_epoch().as_millis();
let stack = Stack::new(iface, device, socket_set, now, rng.random());
let client_config = Config::Client({
let mut config = ClientConfig::default();
config.ssid = SSID.into();
config.password = PASSWORD.into();
config
});
let res = controller.set_configuration(&client_config);
let client_config = Config::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();