Expose esp_wifi_set_mode functionality (to disable access point after initialization)
Opened this issue · 4 comments
I would like to initialize the Wifi interface in ApSta mode and later disable the access point (and switch to Sta mode). This is so that the device can be accessed via the access point to configure it. Afterwards, the access point should disappear and be inaccessible (this is especially relevant because the "obvious hack" of making the AP hidden with a password does not work in esp-wifi, as it does not support password protected APs right now).
As far as I can tell, this is possible in esp-idf through esp_wifi_set_mode
. I propose to change WifiController::set_confguration
so that it just infers the mode from the configuration that was passed in, i.e. something like this
pub fn set_configuration(&mut self, conf: Configuration) -> Result<(), WifiError> {
let wifi_mode = WifiMode::try_from(&conf)?;
esp_wifi_result!(unsafe { esp_wifi_set_mode(wifi_mode.into()) })?;
self.config = conf;
match &self.config {
Configuration::None => {
return Err(WifiError::InternalError(
InternalWifiError::EspErrInvalidArg,
));
}
Configuration::Client(config) => apply_sta_config(config)?,
Configuration::AccessPoint(config) => apply_ap_config(config)?,
Configuration::Mixed(sta_config, ap_config) => {
apply_ap_config(ap_config)?;
apply_sta_config(sta_config)?;
}
Configuration::EapClient(config) => apply_sta_eap_config(config)?,
};
Ok(())
}
I think this approach also somewhat simplifies the amount of validation (see here) that has to be done in set_configuration
.
I did try out this change locally and it seems to work and do what it should (though I am not deeply familiar with esp-wifi or esp-idf, so this might be subtly broken).
@Karuso33 Are you trying to make the ApSta and then use the raw interface for TX/RX of raw frames? I am trying to do the same here.
Thanks for the suggestion!
I think we could allow to degrade from ApSta to either Ap or Sta but we cannot allow going from Ap to Sta, Sta to Ap etc. since the user doesn't have access to the device/interface
But we should do that by either consuming the ap-device/ap-interface or the sta-device/sta-interface since otherwise the user could try to continue using them.
Maybe something like shutdown_access_point(interface: Interface, device: WifiDevice<'d, WifiApDevice>)
As I said, I am not deeply familiar with the code base, so take everything I say with a grain of salt. But I think it might actually be kind of fine to let the user keep control of the WifiApDevice
, even when the Ap is shut down. When the user tries to send data over that device esp_wifi_send_data
is eventually called, and that just calls into the C library (esp_wifi_internal_tx
). And I think that call would just fail more or less silently (the failure is logged, but not returned to the caller; the error is 12294 - ESP_ERR_WIFI_STATE).
While failing silently is not ideal, it is pretty much the same situation that the user currently faces when trying to send data in Sta mode, while not connected to an access point (that also returns 12294 - ESP_ERR_WIFI_STATE). And I feel like if that behavior is okay in Sta mode, then it would also not be the worst thing to have this behavior for Ap mode as well.
I think that this is realistically the best solution there is here. Especially since it is not possible to "get back" the WifiApDevice once an embassy-net Stack
has been constructed (there is no .into_inner()
or similar, maybe there should be (?)).
I think the current esp-wifi
design, where the mode of operation (AP, STA or AP-STA) is encoded as a type-state
and cannot be changed at runtime is the root cause to begin with.
The above makes it very difficult to switch - at runtime - from AP to STA mode and back, as one has to tear down the whole network stack, along with all the apps (HTTPD, etc.) that sit on top of it.
This does not play well with use cases where - say - the HTTPD server, or other network-layer futures are modeled with embassy-executor-tasks each, as in that case, the network stack - by necessity - needs to be passed in a &' static
context and thus needs to live forever.
What the above entails, is that either:
- The whole networking (embassy net driver, wifi controller, all embassy-net apps on top) then needs to either live in a single, giant future and be confined to a single "embassy-net-and-everything-that-lives-on-top" task in the executor
- ... or people have to invent themselves a variant of async cancellation, and pass around the stack either unsafely, or with
Rc
s viaesp-alloc
, so that the stack can be disposed of at runtime. And there is stillunsafe
in all that trickery
In the STD hal we are able to switch between AP and STA at runtime without any issues so this "AP-and-STA-are-typestates" design in esp-wifi
might actually be accidentally self-inflicted rather than following some design constraint in the ESP IDF Wifi blobs themselves.
Also, I just looked at the cyw SPI wifi, and it does seem to be able to switch between AP and STA at runtime, so the issue seems to be unique to esp-hal
.