Skip to content

Commit

Permalink
Async WiFi: connect/disconnect/scan/wait_for_event (esp-rs#129)
Browse files Browse the repository at this point in the history
* Add async scan_n()

* WifiFuture and async connect

- Don't compile the blocking `WifiStack` when using the embassy-net
  feature
- Improve WifiEventFuture to have a waker for each WifiEvent
- Stub out async versions of embedded-svc trait

* Add async start/stop/connect

- All works first time
  - Fails on reconnect, but this is a bug in the svc implementation
- Implements `IntoFuture` for `WifiEvent`
  - Is it possible to await the event in two seperate futures? or will
    one overwrite the other?

* Async connect/disconnect

- Now working in fully async fashion
- Improved embedded-svc `Wifi` trait impl
  - is_started etc still needs work
- spotted memory leak when not transmitting anything

* Fix async start

Can now succesfully reconnect to a network

* Fix `is_started`

Closes esp-rs/esp-wifi-sys#126

* link state waker

* Only wake the transmit waker if we have something to send

* Correct WifiEventFuture

Instead of relying on the current state, it now tracks events and clears
the event before trying to listen.

* wifi: split into device and controller

- Split the wifi into two parts, the device part which is used within the
network stack and the controller, which handles the wifi connection
parts

- Remove the uneeded `Wifi` impl now that the two parts are separate

* undo cfg of blocking stack

* small fixups

* controller: add `wait_for_event`

- This solves the waker overwrite issue, as the function takes `&mut
  self`, meaning its only possible to await for the same event once.
- Sadly this means we have to remove the really clean `into_future` impl
  :(, but atleast we don't have to bump MSRV.

* Fix examples

* fmt

* make globals pub(crate)

* make all events awaitable

- Add wakers for all events
- Add some docs
- Make WifiEventFuture pub(crate)

* fix esp-now
  • Loading branch information
MabezDev authored and bjoernQ committed May 23, 2024
1 parent 1ae9ec9 commit dcc9223
Show file tree
Hide file tree
Showing 11 changed files with 601 additions and 396 deletions.
3 changes: 2 additions & 1 deletion esp-wifi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ num-derive = { version = "0.3", features = ["full-syntax"] }
num-traits = { version = "0.2", default-features = false }
esp-wifi-sys = { version = "0.1.0", path = "../esp-wifi-sys" }
embassy-sync = { version = "0.1.0", optional = true }
embassy-futures = { version = "0.1.0", optional = true }
embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "26474ce6eb759e5add1c137f3417845e0797df3a", features = ["nightly", "tcp", "udp", "dhcpv4", "medium-ethernet"], optional = true }
embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "26474ce6eb759e5add1c137f3417845e0797df3a", optional = true }

Expand Down Expand Up @@ -81,7 +82,7 @@ esp32c2-async = [ "esp32c2-hal/embassy", "esp32c2-hal/embassy-time-timg0", "asyn
esp32-async = [ "esp32-hal/embassy", "esp32-hal/embassy-time-timg0", "async" ]
esp32s2-async = [ "esp32s2-hal/embassy", "esp32s2-hal/embassy-time-timg0", "async" ]
esp32s3-async = [ "esp32s3-hal/embassy", "esp32s3-hal/embassy-time-timg0", "async" ]
async = [ "dep:embassy-sync", "embedded-io/async"]
async = [ "dep:embassy-sync", "dep:embassy-futures", "embedded-io/async"]
embassy-net = ["dep:embassy-net", "dep:embassy-net-driver", "async"]

# misc features
Expand Down
35 changes: 18 additions & 17 deletions esp-wifi/examples/coex.rs
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,9 @@ fn main() -> ! {
rtc.rwdt.disable();

let mut socket_set_entries: [SocketStorage; 3] = Default::default();
let (iface, device, sockets) = create_network_interface(&mut socket_set_entries);
let mut wifi_stack = WifiStack::new(iface, device, sockets, current_millis);
let (iface, device, mut controller, sockets) =
create_network_interface(&mut socket_set_entries);
let wifi_stack = WifiStack::new(iface, device, sockets, current_millis);

#[cfg(feature = "esp32c3")]
{
Expand All @@ -88,32 +89,32 @@ fn main() -> ! {
initialize(timg1.timer0, Rng::new(peripherals.RNG), &clocks).unwrap();
}

println!("is wifi started: {:?}", wifi_stack.is_started());
let client_config = Configuration::Client(ClientConfiguration {
ssid: SSID.into(),
password: PASSWORD.into(),
..Default::default()
});
let res = controller.set_configuration(&client_config);
println!("wifi_set_configuration returned {:?}", res);

controller.start().unwrap();
println!("is wifi started: {:?}", controller.is_started());

println!("Start Wifi Scan");
let res: Result<(heapless::Vec<AccessPointInfo, 10>, usize), WifiError> = wifi_stack.scan_n();
let res: Result<(heapless::Vec<AccessPointInfo, 10>, usize), WifiError> = controller.scan_n();
if let Ok((res, _count)) = res {
for ap in res {
println!("{:?}", ap);
}
}

println!("Call wifi_connect");
let client_config = Configuration::Client(ClientConfiguration {
ssid: SSID.into(),
password: PASSWORD.into(),
..Default::default()
});
let res = wifi_stack.set_configuration(&client_config);
println!("wifi_connect returned {:?}", res);

println!("{:?}", wifi_stack.get_capabilities());
println!("wifi_connect {:?}", wifi_stack.connect());
println!("{:?}", controller.get_capabilities());
println!("wifi_connect {:?}", controller.connect());

// wait to get connected
println!("Wait to get connected");
loop {
let res = wifi_stack.is_connected();
let res = controller.is_connected();
match res {
Ok(connected) => {
if connected {
Expand All @@ -126,7 +127,7 @@ fn main() -> ! {
}
}
}
println!("{:?}", wifi_stack.is_connected());
println!("{:?}", controller.is_connected());

// wait for getting an ip address
println!("Wait to get an ip address");
Expand Down
35 changes: 18 additions & 17 deletions esp-wifi/examples/dhcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,9 @@ fn main() -> ! {
rtc.rwdt.disable();

let mut socket_set_entries: [SocketStorage; 3] = Default::default();
let (iface, device, sockets) = create_network_interface(&mut socket_set_entries);
let mut wifi_stack = WifiStack::new(iface, device, sockets, current_millis);
let (iface, device, mut controller, sockets) =
create_network_interface(&mut socket_set_entries);
let wifi_stack = WifiStack::new(iface, device, sockets, current_millis);

#[cfg(any(feature = "esp32c3", feature = "esp32c2"))]
{
Expand All @@ -86,32 +87,32 @@ fn main() -> ! {
initialize(timg1.timer0, Rng::new(peripherals.RNG), &clocks).unwrap();
}

println!("is wifi started: {:?}", wifi_stack.is_started());
let client_config = Configuration::Client(ClientConfiguration {
ssid: SSID.into(),
password: PASSWORD.into(),
..Default::default()
});
let res = controller.set_configuration(&client_config);
println!("wifi_set_configuration returned {:?}", res);

controller.start().unwrap();
println!("is wifi started: {:?}", controller.is_started());

println!("Start Wifi Scan");
let res: Result<(heapless::Vec<AccessPointInfo, 10>, usize), WifiError> = wifi_stack.scan_n();
let res: Result<(heapless::Vec<AccessPointInfo, 10>, usize), WifiError> = controller.scan_n();
if let Ok((res, _count)) = res {
for ap in res {
println!("{:?}", ap);
}
}

println!("Call wifi_connect");
let client_config = Configuration::Client(ClientConfiguration {
ssid: SSID.into(),
password: PASSWORD.into(),
..Default::default()
});
let res = wifi_stack.set_configuration(&client_config);
println!("wifi_set_configuration returned {:?}", res);

println!("{:?}", wifi_stack.get_capabilities());
println!("wifi_connect {:?}", wifi_stack.connect());
println!("{:?}", controller.get_capabilities());
println!("wifi_connect {:?}", controller.connect());

// wait to get connected
println!("Wait to get connected");
loop {
let res = wifi_stack.is_connected();
let res = controller.is_connected();
match res {
Ok(connected) => {
if connected {
Expand All @@ -124,7 +125,7 @@ fn main() -> ! {
}
}
}
println!("{:?}", wifi_stack.is_connected());
println!("{:?}", controller.is_connected());

// wait for getting an ip address
println!("Wait to get an ip address");
Expand Down
93 changes: 48 additions & 45 deletions esp-wifi/examples/embassy_dhcp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ use esp32s3_hal as hal;

use embassy_executor::Executor;
use embassy_time::{Duration, Timer};
use embedded_svc::wifi::{AccessPointInfo, ClientConfiguration, Configuration, Wifi};
use embedded_svc::wifi::{ClientConfiguration, Configuration, Wifi};
use esp_backtrace as _;
use esp_println::logger::init_logger;
use esp_println::println;
use esp_wifi::initialize;
use esp_wifi::wifi::{WifiDevice, WifiError};
use esp_wifi::wifi::{WifiController, WifiDevice, WifiEvent, WifiState};
use hal::clock::{ClockControl, CpuClock};
use hal::Rng;
use hal::{embassy, peripherals::Peripherals, prelude::*, timer::TimerGroup, Rtc};
Expand Down Expand Up @@ -92,48 +92,7 @@ fn main() -> ! {
initialize(timg1.timer0, Rng::new(peripherals.RNG), &clocks).unwrap();
}

let mut wifi_interface = WifiDevice::new();

println!("is wifi started: {:?}", wifi_interface.is_started());

println!("Start Wifi Scan");
let res: Result<(heapless::Vec<AccessPointInfo, 10>, usize), WifiError> =
wifi_interface.scan_n();
if let Ok((res, _count)) = res {
for ap in res {
println!("{:?}", ap);
}
}

println!("Call wifi_connect");
let client_config = Configuration::Client(ClientConfiguration {
ssid: SSID.into(),
password: PASSWORD.into(),
..Default::default()
});
let res = wifi_interface.set_configuration(&client_config);
println!("wifi_set_configuration returned {:?}", res);

println!("{:?}", wifi_interface.get_capabilities());
println!("wifi_connect {:?}", wifi_interface.connect());

// wait to get connected
println!("Wait to get connected");
loop {
let res = wifi_interface.is_connected();
match res {
Ok(connected) => {
if connected {
break;
}
}
Err(err) => {
println!("{:?}", err);
loop {}
}
}
}
println!("{:?}", wifi_interface.is_connected());
let (wifi_interface, controller) = esp_wifi::wifi::new();

let timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
embassy::init(&clocks, timer_group0.timer0);
Expand All @@ -152,11 +111,48 @@ fn main() -> ! {

let executor = EXECUTOR.init(Executor::new());
executor.run(|spawner| {
spawner.spawn(connection(controller)).ok();
spawner.spawn(net_task(&stack)).ok();
spawner.spawn(task(&stack)).ok();
});
}

#[embassy_executor::task]
async fn connection(mut controller: WifiController) {
println!("start connection task");
println!("Device capabilities: {:?}", controller.get_capabilities());
loop {
match esp_wifi::wifi::get_wifi_state() {
WifiState::StaConnected => {
// wait until we're no longer connected
controller.wait_for_event(WifiEvent::StaDisconnected).await;
Timer::after(Duration::from_millis(5000)).await
}
_ => {}
}
if !matches!(controller.is_started(), Ok(true)) {
let client_config = Configuration::Client(ClientConfiguration {
ssid: SSID.into(),
password: PASSWORD.into(),
..Default::default()
});
controller.set_configuration(&client_config).unwrap();
println!("Starting wifi");
controller.start().await.unwrap();
println!("Wifi started!");
}
println!("About to connect...");

match controller.connect().await {
Ok(_) => println!("Wifi connected!"),
Err(e) => {
println!("Failed to connect to wifi: {e:?}");
Timer::after(Duration::from_millis(5000)).await
}
}
}
}

#[embassy_executor::task]
async fn net_task(stack: &'static Stack<WifiDevice>) {
stack.run().await
Expand All @@ -167,6 +163,13 @@ async fn task(stack: &'static Stack<WifiDevice>) {
let mut rx_buffer = [0; 4096];
let mut tx_buffer = [0; 4096];

loop {
if stack.is_link_up() {
break;
}
Timer::after(Duration::from_millis(500)).await;
}

println!("Waiting to get IP address...");
loop {
if let Some(config) = stack.config() {
Expand Down Expand Up @@ -214,6 +217,6 @@ async fn task(stack: &'static Stack<WifiDevice>) {
};
println!("{}", core::str::from_utf8(&buf[..n]).unwrap());
}
Timer::after(Duration::from_millis(1000)).await;
Timer::after(Duration::from_millis(3000)).await;
}
}
33 changes: 17 additions & 16 deletions esp-wifi/examples/static_ip.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,8 @@ fn main() -> ! {
rtc.rwdt.disable();

let mut socket_set_entries: [SocketStorage; 3] = Default::default();
let (iface, device, sockets) = create_network_interface(&mut socket_set_entries);
let (iface, device, mut controller, sockets) =
create_network_interface(&mut socket_set_entries);
let mut wifi_stack = WifiStack::new(iface, device, sockets, current_millis);

#[cfg(any(feature = "esp32c3", feature = "esp32c2"))]
Expand All @@ -87,32 +88,32 @@ fn main() -> ! {
initialize(timg1.timer0, Rng::new(peripherals.RNG), &clocks).unwrap();
}

println!("is wifi started: {:?}", wifi_stack.is_started());
let client_config = Configuration::Client(ClientConfiguration {
ssid: SSID.into(),
password: PASSWORD.into(),
..Default::default()
});
let res = controller.set_configuration(&client_config);
println!("wifi_set_configuration returned {:?}", res);

controller.start().unwrap();
println!("is wifi started: {:?}", controller.is_started());

println!("Start Wifi Scan");
let res: Result<(heapless::Vec<AccessPointInfo, 10>, usize), WifiError> = wifi_stack.scan_n();
let res: Result<(heapless::Vec<AccessPointInfo, 10>, usize), WifiError> = controller.scan_n();
if let Ok((res, _count)) = res {
for ap in res {
println!("{:?}", ap);
}
}

println!("Call wifi_connect");
let client_config = Configuration::Client(ClientConfiguration {
ssid: SSID.into(),
password: PASSWORD.into(),
..Default::default()
});
let res = wifi_stack.set_configuration(&client_config);
println!("wifi_set_configuration returned {:?}", res);

println!("{:?}", wifi_stack.get_capabilities());
println!("wifi_connect {:?}", wifi_stack.connect());
println!("{:?}", controller.get_capabilities());
println!("wifi_connect {:?}", controller.connect());

// wait to get connected
println!("Wait to get connected");
loop {
let res = wifi_stack.is_connected();
let res = controller.is_connected();
match res {
Ok(connected) => {
if connected {
Expand All @@ -125,7 +126,7 @@ fn main() -> ! {
}
}
}
println!("{:?}", wifi_stack.is_connected());
println!("{:?}", controller.is_connected());

println!("Setting static IP {}", STATIC_IP);

Expand Down
2 changes: 2 additions & 0 deletions esp-wifi/src/esp_now/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,8 @@ impl EspNowCreator {
/// After this the broadcast address is already added as a peer.
pub fn initialize(self) -> Result<EspNow, EspNowError> {
let mut esp_now = EspNow { _private: () };
check_error!({ esp_wifi_set_mode(wifi_mode_t_WIFI_MODE_STA) });
check_error!({ esp_wifi_start() });
check_error!({ esp_now_init() });
check_error!({ esp_now_register_recv_cb(Some(rcv_cb)) });

Expand Down
4 changes: 1 addition & 3 deletions esp-wifi/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ pub fn current_millis() -> u64 {
}

#[cfg(not(coex))]
const HEAP_SIZE: usize = 42 * 1024;
const HEAP_SIZE: usize = 64 * 1024;

#[cfg(coex)]
const HEAP_SIZE: usize = 64 * 1024;
Expand Down Expand Up @@ -143,7 +143,6 @@ pub fn initialize(
log::debug!("wifi init");
// wifi init
crate::wifi::wifi_init()?;
crate::wifi::wifi_start()?;
}

#[cfg(feature = "ble")]
Expand Down Expand Up @@ -217,7 +216,6 @@ pub fn initialize(
{
log::debug!("wifi init");
crate::wifi::wifi_init()?;
crate::wifi::wifi_start()?;
}

#[cfg(feature = "ble")]
Expand Down
Loading

0 comments on commit dcc9223

Please sign in to comment.