Skip to content

Commit

Permalink
Async WiFi (esp-rs#125)
Browse files Browse the repository at this point in the history
* Async WiFi with embassy

- Add embassy_net::Driver impl for WifiDevice
- Add embassy_dhcp example
- Update README with embassy_dhcp running instruction

Notes:

TCP connection may fail a few times until the esp gets an IP address, it
seems there is no way to await dhcp completion in embassy_net yet.

* Put embassy-net behind feature

- Whilst other embassy crates can be used behind the async feature,
  embassy-net should be put behind its own feature

* wait for dhcp to complete in example

* fmt

* Simplify embedded-io import

* Update README, use new bleps
  • Loading branch information
MabezDev authored and bjoernQ committed May 24, 2024
1 parent bc26768 commit 00599fd
Show file tree
Hide file tree
Showing 3 changed files with 348 additions and 5 deletions.
19 changes: 14 additions & 5 deletions esp-wifi/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,22 @@ log = "0.4.17"
embedded-svc = { version = "0.23.1", default-features = false, features = [], optional = true }
enumset = { version = "1", default-features = false, optional = true }
linked_list_allocator = { version = "0.10.3", default-features = false, features = ["const_mut_refs"] }
embedded-io = "0.3.1"
embedded-io = "0.4.0"
fugit = "0.3.6"
heapless = { version = "0.7.14", default-features = false }
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-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 }

[build-dependencies]
riscv-target = { version = "0.1.2", optional = true }

[dev-dependencies]
bleps = { git = "https://github.com/bjoernQ/bleps", package = "bleps", rev = "5ce5ca139fe85c0120e9c2f9607d165d1c32d725" }
bleps-macros = { git = "https://github.com/bjoernQ/bleps", package = "bleps-macros", rev = "5ce5ca139fe85c0120e9c2f9607d165d1c32d725" }
bleps = { git = "https://github.com/bjoernQ/bleps", package = "bleps", rev = "33fde67257bfbc6c0aebf7649fd302c82ed94c64" }
bleps-macros = { git = "https://github.com/bjoernQ/bleps", package = "bleps-macros", rev = "33fde67257bfbc6c0aebf7649fd302c82ed94c64" }
embassy-executor = { package = "embassy-executor", git = "https://github.com/embassy-rs/embassy/", rev = "cd9a65b", features = ["nightly", "integrated-timers"] }
embassy-time = { version = "0.1.0", features = ["nightly"] }
embassy-futures = "0.1.0"
Expand All @@ -65,16 +67,24 @@ xtensa-atomic-emulation-trap = "0.3.0"

[features]
default = [ "utils" ]

# chip features
esp32c3 = [ "riscv-target", "riscv", "riscv-rt", "esp32c3-hal", "dep:esp32c3", "esp-wifi-sys/esp32c3" ]
esp32c2 = [ "riscv-target", "riscv", "riscv-rt", "esp32c2-hal", "dep:esp32c2", "esp-wifi-sys/esp32c2" ]
esp32 = [ "esp32-hal", "xtensa-lx-rt/esp32", "xtensa-lx/esp32", "esp-wifi-sys/esp32" ]
esp32s3 = [ "esp32s3-hal", "xtensa-lx-rt/esp32s3", "xtensa-lx/esp32s3", "esp-wifi-sys/esp32s3" ]
esp32s2 = [ "esp32s2-hal", "xtensa-lx-rt/esp32s2", "xtensa-lx/esp32s2", "esp-wifi-sys/esp32s2" ]

# async features
esp32c3-async = [ "esp32c3-hal/embassy", "esp32c3-hal/embassy-time-timg0", "async" ]
esp32c2-async = [ "esp32c2-hal/embassy", "esp32c2-hal/embassy-time-timg0", "async" ]
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"]
embassy-net = ["dep:embassy-net", "dep:embassy-net-driver", "async"]

# misc features
wifi-logs = []
dump-packets = []
utils = []
Expand All @@ -84,5 +94,4 @@ wifi = []
ble = [ "esp32-hal?/bluetooth" ]
phy-enable-usb = []
ps-min-modem = []
esp-now = [ "wifi", "embedded-svc" ]
async = [ "dep:embassy-sync"]
esp-now = [ "wifi", "embedded-svc" ]
219 changes: 219 additions & 0 deletions esp-wifi/examples/embassy_dhcp.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,219 @@
#![no_std]
#![no_main]
#![feature(c_variadic)]
#![feature(const_mut_refs)]
#![feature(type_alias_impl_trait)]

use embassy_executor::_export::StaticCell;
use embassy_net::tcp::TcpSocket;
use embassy_net::{Config, Ipv4Address, Stack, StackResources};
#[cfg(feature = "esp32")]
use esp32_hal as hal;
#[cfg(feature = "esp32c2")]
use esp32c2_hal as hal;
#[cfg(feature = "esp32c3")]
use esp32c3_hal as hal;
#[cfg(feature = "esp32s2")]
use esp32s2_hal as hal;
#[cfg(feature = "esp32s3")]
use esp32s3_hal as hal;

use embassy_executor::Executor;
use embassy_time::{Duration, Timer};
use embedded_svc::wifi::{AccessPointInfo, 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 hal::clock::{ClockControl, CpuClock};
use hal::Rng;
use hal::{embassy, peripherals::Peripherals, prelude::*, timer::TimerGroup, Rtc};

#[cfg(any(feature = "esp32c3", feature = "esp32c2"))]
use hal::system::SystemExt;

#[cfg(any(feature = "esp32c3", feature = "esp32c2"))]
use riscv_rt::entry;
#[cfg(any(feature = "esp32", feature = "esp32s3", feature = "esp32s2"))]
use xtensa_lx_rt::entry;

const SSID: &str = env!("SSID");
const PASSWORD: &str = env!("PASSWORD");

macro_rules! singleton {
($val:expr) => {{
type T = impl Sized;
static STATIC_CELL: StaticCell<T> = StaticCell::new();
let (x,) = STATIC_CELL.init(($val,));
x
}};
}

static EXECUTOR: StaticCell<Executor> = StaticCell::new();

#[entry]
fn main() -> ! {
init_logger(log::LevelFilter::Info);
esp_wifi::init_heap();

let peripherals = Peripherals::take();

#[cfg(not(feature = "esp32"))]
let system = peripherals.SYSTEM.split();
#[cfg(feature = "esp32")]
let system = peripherals.DPORT.split();

#[cfg(feature = "esp32c3")]
let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock160MHz).freeze();
#[cfg(feature = "esp32c2")]
let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock120MHz).freeze();
#[cfg(any(feature = "esp32", feature = "esp32s3", feature = "esp32s2"))]
let clocks = ClockControl::configure(system.clock_control, CpuClock::Clock240MHz).freeze();

let mut rtc = Rtc::new(peripherals.RTC_CNTL);

// Disable watchdog timers
#[cfg(not(any(feature = "esp32", feature = "esp32s2")))]
rtc.swd.disable();

rtc.rwdt.disable();

#[cfg(any(feature = "esp32c3", feature = "esp32c2"))]
{
use hal::systimer::SystemTimer;
let syst = SystemTimer::new(peripherals.SYSTIMER);
initialize(syst.alarm0, Rng::new(peripherals.RNG), &clocks).unwrap();
}
#[cfg(any(feature = "esp32", feature = "esp32s3", feature = "esp32s2"))]
{
use hal::timer::TimerGroup;
let timg1 = TimerGroup::new(peripherals.TIMG1, &clocks);
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 timer_group0 = TimerGroup::new(peripherals.TIMG0, &clocks);
embassy::init(&clocks, timer_group0.timer0);

let config = Config::Dhcp(Default::default());

let seed = 1234; // very random, very secure seed

// Init network stack
let stack = &*singleton!(Stack::new(
wifi_interface,
config,
singleton!(StackResources::<3>::new()),
seed
));

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

#[embassy_executor::task]
async fn net_task(stack: &'static Stack<WifiDevice>) {
stack.run().await
}

#[embassy_executor::task]
async fn task(stack: &'static Stack<WifiDevice>) {
let mut rx_buffer = [0; 4096];
let mut tx_buffer = [0; 4096];

println!("Waiting to get IP address...");
loop {
if let Some(config) = stack.config() {
println!("Got IP: {}", config.address);
break;
}
Timer::after(Duration::from_millis(500)).await;
}

loop {
Timer::after(Duration::from_millis(1_000)).await;

let mut socket = TcpSocket::new(&stack, &mut rx_buffer, &mut tx_buffer);

socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));

let remote_endpoint = (Ipv4Address::new(142, 250, 185, 115), 80);
println!("connecting...");
let r = socket.connect(remote_endpoint).await;
if let Err(e) = r {
println!("connect error: {:?}", e);
continue;
}
println!("connected!");
let mut buf = [0; 1024];
loop {
use embedded_io::asynch::Write;
let r = socket
.write_all(b"GET / HTTP/1.0\r\nHost: www.mobile-j.de\r\n\r\n")
.await;
if let Err(e) = r {
println!("write error: {:?}", e);
break;
}
let n = match socket.read(&mut buf).await {
Ok(0) => {
println!("read EOF");
break;
}
Ok(n) => n,
Err(e) => {
println!("read error: {:?}", e);
break;
}
};
println!("{}", core::str::from_utf8(&buf[..n]).unwrap());
}
Timer::after(Duration::from_millis(1000)).await;
}
}
Loading

0 comments on commit 00599fd

Please sign in to comment.