Skip to content

Commit 5816723

Browse files
ivmarkovpilotniq
andauthored
Async & blocking adaptors for the Wifi driver (supersedes esp-rs#240) (esp-rs#243)
* Added async Wifi implementation with stubs The stubs just block on the sync variants of the functions, and return a 'ready' future. In a later iteration, the async functions that block can be replaced with proper futures. Also added an example of using the async Wifi. Feedback and comments welcome! * Add the new wifi_async example to list of examples in Cargo.toml * Revert mistaken reformatting of Cargo.toml * Attempt to address review comments * Refactored to address my misunderstandings from previous review comments. The implementation of asynch::Wifi is still dependent on "experimental" feature, because embedded-svc only exports the trait with "experimental". `get_configuration`, `set_configuration`, `get_capabilities`, `is_started`, and `is_connected` methds are not async in AsyncWifiDriver, because I don't think it make sense since they return immediately on the ESP32 platform. However, they still have async wrappers in the `asynch::Wifi` implementation to fulfill that trait. * Cleanups in async methods. Removal of some nightly feature requirements. * Fixup * Added initial async implementation of Wifi start and stop. Not tested. * Added true async implementations for stop, connect and disconnect * Make examples buildable (incl with CI) * Fix the HTTP request example * Blocking and async wrappers for the Wifi driver * Wait for connect with a timeout * Use debug! as we are much more noiser now * Generalize Wait / AsyncWait in eventloop; NetifStatus trait --------- Co-authored-by: Erland Lewin <[email protected]>
1 parent cd26f84 commit 5816723

File tree

16 files changed

+1389
-220
lines changed

16 files changed

+1389
-220
lines changed

.cargo/config.toml

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
[target.xtensa-esp32-espidf]
2+
linker = "ldproxy"
3+
4+
[target.xtensa-esp32s2-espidf]
5+
linker = "ldproxy"
6+
7+
[target.xtensa-esp32s3-espidf]
8+
linker = "ldproxy"
9+
10+
[target.riscv32imc-esp-espidf]
11+
linker = "ldproxy"
12+
13+
# Future - necessary for the experimental "native build" of esp-idf-sys with ESP32C3
14+
# See also https://github.com/ivmarkov/embuild/issues/16
15+
rustflags = ["-C", "default-linker-libraries"]
16+
17+
[env]
18+
ESP_IDF_SDKCONFIG_DEFAULTS = ".github/configs/sdkconfig.defaults"
19+
20+
[unstable]
21+
build-std = ["std", "panic_abort"]
22+
build-std-features = ["panic_immediate_abort"]

.github/configs/sdkconfig.defaults

+3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
# Workaround for https://github.com/espressif/esp-idf/issues/7631
22
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE=n
33
CONFIG_MBEDTLS_CERTIFICATE_BUNDLE_DEFAULT_FULL=n
4+
5+
# Examples often require a larger than the default stack size for the main thread.
6+
CONFIG_ESP_MAIN_TASK_STACK_SIZE=10000

.github/workflows/ci.yml

+7
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,10 @@ jobs:
7474
ESP_IDF_SDKCONFIG_DEFAULTS: $(pwd)/.github/configs/sdkconfig.defaults
7575
RUSTFLAGS: "${{ matrix.idf-version == 'release/v5.0' && '--cfg espidf_time64' || ''}}"
7676
run: cargo build --no-default-features --features nightly,experimental,alloc --target ${{ matrix.target }} -Zbuild-std=std,panic_abort -Zbuild-std-features=panic_immediate_abort
77+
78+
- name: Build | Examples
79+
env:
80+
ESP_IDF_VERSION: ${{ matrix.idf-version }}
81+
ESP_IDF_SDKCONFIG_DEFAULTS: $(pwd)/.github/configs/sdkconfig.defaults
82+
RUSTFLAGS: "${{ matrix.idf-version == 'release/v5.0' && '--cfg espidf_time64' || ''}} ${{ matrix.target == 'riscv32imc-esp-espidf' && '-C default-linker-libraries' || ''}}"
83+
run: cargo build --examples --target ${{ matrix.target }} -Zbuild-std=std,panic_abort -Zbuild-std-features=panic_immediate_abort

Cargo.toml

+14
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,25 @@ esp-idf-sys = { version = "0.32.1", default-features = false, features = ["nativ
3737
esp-idf-hal = { version = "0.40", default-features = false, features = ["esp-idf-sys"] }
3838
embassy-sync = { version = "0.1", optional = true }
3939
embassy-time = { version = "0.1", optional = true, features = ["tick-hz-1_000_000"] }
40+
embassy-futures = "0.1"
4041

4142
[build-dependencies]
4243
embuild = "0.31"
4344
anyhow = "1"
4445

46+
[dev-dependencies]
47+
anyhow = "1"
48+
esp-idf-sys = { version = "0.32", features = ["native", "binstart"] }
49+
futures = "0.3"
50+
4551
[[example]]
4652
name = "http_request"
4753
required-features = ["experimental"]
54+
55+
[[example]]
56+
name = "wifi"
57+
required-features = ["experimental"]
58+
59+
[[example]]
60+
name = "wifi_async"
61+
required-features = ["experimental"]

build.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
fn main() -> anyhow::Result<()> {
2-
embuild::build::CfgArgs::output_propagated("ESP_IDF")
2+
embuild::build::CfgArgs::output_propagated("ESP_IDF")?;
3+
4+
// Will not be available when built with a CMake-first or a PIO-first build
5+
// We need to output these only when building the examples' binaries anyway
6+
if let Ok(args) = embuild::build::LinkArgs::try_from_env("ESP_IDF") {
7+
args.output();
8+
}
9+
10+
Ok(())
311
}

examples/http_request.rs

+4-6
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,16 @@
44
//! Note: Requires `experimental` cargo feature to be enabled
55
66
use embedded_svc::{
7-
http::{client::Client as HttpClient, Method, Status},
7+
http::{client::Client as HttpClient, Method},
88
io::Write,
99
utils::io,
1010
};
11-
use esp_idf_svc::http::client::{Configuration as HttpConfiguration, EspHttpConnection};
11+
use esp_idf_svc::http::client::EspHttpConnection;
12+
use esp_idf_sys::{self as _}; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported
1213

1314
fn main() -> anyhow::Result<()> {
1415
// Create HTTP(S) client
15-
let mut client = HttpClient::wrap(EspHttpConnection::new(&HttpConfiguration {
16-
crt_bundle_attach: Some(esp_idf_sys::esp_crt_bundle_attach), // Needed for HTTPS support
17-
..Default::default()
18-
})?);
16+
let mut client = HttpClient::wrap(EspHttpConnection::new(&Default::default())?);
1917

2018
// GET
2119
get_request(&mut client)?;

examples/wifi.rs

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
//! Example of using async wifi.
2+
//!
3+
//! Add your own ssid and password
4+
//!
5+
//! Note: Requires `nightly` and `experimental` cargo feature to be enabled
6+
7+
use embedded_svc::wifi::{AuthMethod, ClientConfiguration, Configuration};
8+
use esp_idf_hal::prelude::Peripherals;
9+
use esp_idf_svc::log::EspLogger;
10+
use esp_idf_svc::wifi::{BlockingWifi, EspWifi};
11+
use esp_idf_svc::{eventloop::EspSystemEventLoop, nvs::EspDefaultNvsPartition};
12+
use esp_idf_sys::{self as _}; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported
13+
use futures::executor::block_on;
14+
use log::info;
15+
16+
const SSID: &'static str = env!("WIFI_SSID");
17+
const PASSWORD: &'static str = env!("WIFI_PASS");
18+
19+
fn main() -> anyhow::Result<()> {
20+
EspLogger::initialize_default();
21+
22+
let peripherals = Peripherals::take().unwrap();
23+
let sys_loop = EspSystemEventLoop::take()?;
24+
let nvs = EspDefaultNvsPartition::take()?;
25+
26+
let mut wifi = BlockingWifi::wrap(
27+
EspWifi::new(peripherals.modem, sys_loop.clone(), Some(nvs))?,
28+
sys_loop,
29+
)?;
30+
31+
block_on(connect_wifi(&mut wifi))?;
32+
33+
let ip_info = wifi.wifi().sta_netif().get_ip_info()?;
34+
35+
info!("Wifi DHCP info: {:?}", ip_info);
36+
37+
info!("Shutting down in 5s...");
38+
39+
std::thread::sleep(core::time::Duration::from_secs(5));
40+
41+
Ok(())
42+
}
43+
44+
async fn connect_wifi(wifi: &mut BlockingWifi<EspWifi<'static>>) -> anyhow::Result<()> {
45+
let wifi_configuration: Configuration = Configuration::Client(ClientConfiguration {
46+
ssid: SSID.into(),
47+
bssid: None,
48+
auth_method: AuthMethod::WPA2Personal,
49+
password: PASSWORD.into(),
50+
channel: None,
51+
});
52+
53+
wifi.set_configuration(&wifi_configuration)?;
54+
55+
wifi.start()?;
56+
info!("Wifi started");
57+
58+
wifi.connect()?;
59+
info!("Wifi connected");
60+
61+
wifi.wait_netif_up()?;
62+
info!("Wifi netif up");
63+
64+
Ok(())
65+
}

examples/wifi_async.rs

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
//! Example of using async wifi.
2+
//!
3+
//! Add your own ssid and password
4+
//!
5+
//! Note: Requires `nightly` and `experimental` cargo feature to be enabled
6+
7+
use embedded_svc::wifi::{AuthMethod, ClientConfiguration, Configuration};
8+
use esp_idf_hal::prelude::Peripherals;
9+
use esp_idf_svc::log::EspLogger;
10+
use esp_idf_svc::timer::EspTaskTimerService;
11+
use esp_idf_svc::wifi::{AsyncWifi, EspWifi};
12+
use esp_idf_svc::{eventloop::EspSystemEventLoop, nvs::EspDefaultNvsPartition};
13+
use esp_idf_sys::{self as _}; // If using the `binstart` feature of `esp-idf-sys`, always keep this module imported
14+
use futures::executor::block_on;
15+
use log::info;
16+
17+
const SSID: &'static str = env!("WIFI_SSID");
18+
const PASSWORD: &'static str = env!("WIFI_PASS");
19+
20+
fn main() -> anyhow::Result<()> {
21+
EspLogger::initialize_default();
22+
23+
let peripherals = Peripherals::take().unwrap();
24+
let sys_loop = EspSystemEventLoop::take()?;
25+
let timer_service = EspTaskTimerService::new()?;
26+
let nvs = EspDefaultNvsPartition::take()?;
27+
28+
let mut wifi = AsyncWifi::wrap(
29+
EspWifi::new(peripherals.modem, sys_loop.clone(), Some(nvs))?,
30+
sys_loop,
31+
timer_service.clone(),
32+
)?;
33+
34+
block_on(connect_wifi(&mut wifi))?;
35+
36+
let ip_info = wifi.wifi().sta_netif().get_ip_info()?;
37+
38+
info!("Wifi DHCP info: {:?}", ip_info);
39+
40+
info!("Shutting down in 5s...");
41+
42+
std::thread::sleep(core::time::Duration::from_secs(5));
43+
44+
Ok(())
45+
}
46+
47+
async fn connect_wifi(wifi: &mut AsyncWifi<EspWifi<'static>>) -> anyhow::Result<()> {
48+
let wifi_configuration: Configuration = Configuration::Client(ClientConfiguration {
49+
ssid: SSID.into(),
50+
bssid: None,
51+
auth_method: AuthMethod::WPA2Personal,
52+
password: PASSWORD.into(),
53+
channel: None,
54+
});
55+
56+
wifi.set_configuration(&wifi_configuration)?;
57+
58+
wifi.start().await?;
59+
info!("Wifi started");
60+
61+
wifi.connect().await?;
62+
info!("Wifi connected");
63+
64+
wifi.wait_netif_up().await?;
65+
info!("Wifi netif up");
66+
67+
Ok(())
68+
}

0 commit comments

Comments
 (0)