From 2c87f25bd09d1a299335badf45cee92f475407fc Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Thu, 1 Dec 2022 11:29:04 +0100 Subject: [PATCH 1/2] Add static IP and socket listening example --- README.md | 29 ++++-- examples/static_ip.rs | 223 ++++++++++++++++++++++++++++++++++++++++++ src/wifi_interface.rs | 95 +++++++++++++++++- 3 files changed, 335 insertions(+), 12 deletions(-) create mode 100644 examples/static_ip.rs diff --git a/README.md b/README.md index db11b687..b119bff5 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ WiFi / BTLE coexistence is implemented but currently only works (to some extend) On ESP32-S3 only WiFi is currently supported. -THIS CURRENTLY DOESN'T WORK WITH THE XTENSA ENABLED RUST COMPILER 1.63.0.2. Use 1.64.0.0! +THIS CURRENTLY DOESN'T WORK WITH THE XTENSA ENABLED RUST COMPILER 1.63.0.2. Use 1.65 or better This uses the WiFi drivers from https://github.com/esp-rs/esp-wireless-drivers-3rdparty @@ -25,6 +25,13 @@ https://github.com/esp-rs/esp-wireless-drivers-3rdparty/ (commit 839bcd7cb89d695 - gets an ip address via DHCP - performs an HTTP get request to some "random" server +- static_ip + - set SSID and PASSWORD env variable + - set STATIC_IP and GATEWAY_IP env variable (e.g. "192.168.2.191" / "192.168.2.1") + - might be necessary to configure your WiFi access point accordingly + - uses the given static IP + - responds with some HTML content when connecting to port 8080 + - ble - starts Bluetooth advertising - offers one service with two characteristics (one is read/write, one is write only) @@ -37,15 +44,19 @@ https://github.com/esp-rs/esp-wireless-drivers-3rdparty/ (commit 839bcd7cb89d695 - does BLE advertising - coex support is still somewhat flaky -| Command | Chip | -| ---------------------------------------------------------------------------------------------------------------------------- | ------- | -| `cargo "+nightly" run --example ble --release --target riscv32imc-unknown-none-elf --features "esp32c3,ble"` | ESP32-C3 | -| `cargo "+nightly" run --example dhcp --release --target riscv32imc-unknown-none-elf --features "esp32c3,embedded-svc,wifi"` | ESP32-C3 | -| `cargo "+nightly" run --example coex --release --target riscv32imc-unknown-none-elf --features "esp32c3,embedded-svc,wifi,ble"` | ESP32-C3 | -| `cargo "+esp" run --example ble --release --target xtensa-esp32-none-elf --features "esp32,ble"` | ESP32 | -| `cargo "+esp" run --example dhcp --release --target xtensa-esp32-none-elf --features "esp32,embedded-svc,wifi"` | ESP32 | +| Command | Chip | +| ------------------------------------------------------------------------------------------------------------------------------- | ------- | +| `cargo "+nightly" run --example ble --release --target riscv32imc-unknown-none-elf --features "esp32c3,ble"` | ESP32-C3| +| `cargo "+nightly" run --example dhcp --release --target riscv32imc-unknown-none-elf --features "esp32c3,embedded-svc,wifi"` | ESP32-C3| +| `cargo "+nightly" run --example static_ip --release --target riscv32imc-unknown-none-elf --features "esp32c3,embedded-svc,wifi"`| ESP32-C3| +| `cargo "+nightly" run --example coex --release --target riscv32imc-unknown-none-elf --features "esp32c3,embedded-svc,wifi,ble"` | ESP32-C3| +| `cargo "+esp" run --example ble --release --target xtensa-esp32-none-elf --features "esp32,ble"` | ESP32 | +| `cargo "+esp" run --example dhcp --release --target xtensa-esp32-none-elf --features "esp32,embedded-svc,wifi"` | ESP32 | +| `cargo "+esp" run --example static_ip --release --target xtensa-esp32-none-elf --features "esp32,embedded-svc,wifi"` | ESP32 | | `cargo "+esp" run --example dhcp --release --target xtensa-esp32s3-none-elf --features "esp32s3,embedded-svc,wifi"` | ESP32-S3| +| `cargo "+esp" run --example static_ip --release --target xtensa-esp32s3-none-elf --features "esp32s3,embedded-svc,wifi"` | ESP32-S3| | `cargo "+esp" run --example dhcp --release --target xtensa-esp32s2-none-elf --features "esp32s2,embedded-svc,wifi"` | ESP32-S2| +| `cargo "+esp" run --example static_ip --release --target xtensa-esp32s2-none-elf --features "esp32s2,embedded-svc,wifi"` | ESP32-S2| Additional you can specify these features |Feature|Meaning| @@ -95,8 +106,6 @@ Additionally it uses CCOMPARE0 - so don't touch that, too. Currently your mileage might vary a lot for different opt-levels on Xtensa targets! If something doesn't work as expected try a different opt-level. -e.g. that's why the ESP32-S2 example needs to be built with `opt-level=2` - ## Directory Structure - `src/timer-espXXX.rs`: systimer code used for timing and task switching diff --git a/examples/static_ip.rs b/examples/static_ip.rs new file mode 100644 index 00000000..ccba3eb1 --- /dev/null +++ b/examples/static_ip.rs @@ -0,0 +1,223 @@ +#![no_std] +#![no_main] +#![feature(c_variadic)] +#![feature(const_mut_refs)] + +#[cfg(feature = "esp32")] +use esp32_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 embedded_io::blocking::*; +use embedded_svc::ipv4::Interface; +use embedded_svc::wifi::{AccessPointInfo, ClientConfiguration, Configuration, Wifi}; + +use esp_backtrace as _; +use esp_println::logger::init_logger; +use esp_println::{print, println}; +use esp_wifi::wifi::utils::create_network_interface; +use esp_wifi::wifi_interface::{Network, WifiError}; +use esp_wifi::{create_network_stack_storage, network_stack_storage}; +use esp_wifi::{current_millis, initialize}; +use hal::clock::{ClockControl, CpuClock}; +use hal::{pac::Peripherals, prelude::*, Rtc}; + +#[cfg(any(feature = "esp32c3"))] +use hal::system::SystemExt; + +#[cfg(feature = "esp32c3")] +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"); +const STATIC_IP: &str = env!("STATIC_IP"); +const GATEWAY_IP: &str = env!("GATEWAY_IP"); + +#[entry] +fn main() -> ! { + init_logger(log::LevelFilter::Info); + esp_wifi::init_heap(); + + let peripherals = Peripherals::take().unwrap(); + + #[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(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(); + + let mut storage = create_network_stack_storage!(3, 8, 1, 1); + let ethernet = create_network_interface(network_stack_storage!(storage)); + let mut wifi_interface = esp_wifi::wifi_interface::Wifi::new(ethernet); + + #[cfg(feature = "esp32c3")] + { + use hal::systimer::SystemTimer; + let syst = SystemTimer::new(peripherals.SYSTIMER); + initialize(syst.alarm0, 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, peripherals.RNG, &clocks).unwrap(); + } + + println!("is wifi started: {:?}", wifi_interface.is_started()); + + println!("Start Wifi Scan"); + let res: Result<(heapless::Vec, 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()); + + println!("Setting static IP {}", STATIC_IP); + let mut network = Network::new(wifi_interface, current_millis); + network + .set_iface_configuration(&embedded_svc::ipv4::Configuration::Client( + embedded_svc::ipv4::ClientConfiguration::Fixed(embedded_svc::ipv4::ClientSettings { + ip: embedded_svc::ipv4::Ipv4Addr::from(parse_ip(STATIC_IP)), + subnet: embedded_svc::ipv4::Subnet { + gateway: embedded_svc::ipv4::Ipv4Addr::from(parse_ip(GATEWAY_IP)), + mask: embedded_svc::ipv4::Mask(24), + }, + dns: None, + secondary_dns: None, + }), + )) + .unwrap(); + + println!( + "Start busy loop on main. Point your browser to http://{}:8080/", + STATIC_IP + ); + + let mut rx_buffer = [0u8; 1536]; + let mut tx_buffer = [0u8; 1536]; + let mut socket = network.get_socket(&mut rx_buffer, &mut tx_buffer); + + socket.listen(8080).unwrap(); + + loop { + socket.work(); + + if !socket.is_open() { + socket.listen(8080).unwrap(); + } + + if socket.is_connected() { + println!("Connected"); + + let mut time_out = false; + let wait_end = current_millis() + 2 * 1000; + let mut buffer = [0u8; 1024]; + let mut pos = 0; + loop { + if let Ok(len) = socket.read(&mut buffer[pos..]) { + let to_print = + unsafe { core::str::from_utf8_unchecked(&buffer[..(pos + len)]) }; + + if to_print.contains("\r\n\r\n") { + print!("{}", to_print); + println!(); + break; + } + + pos += len; + } else { + break; + } + + if current_millis() > wait_end { + println!("Timeout"); + time_out = true; + break; + } + } + + if !time_out { + socket.write_all( + b"HTTP/1.0 200 OK\r\n\r\n\ + \ + \ +

Hello Rust! Hello esp-wifi!

\ + + \ + \r\n\ + " + ).unwrap(); + + socket.flush().unwrap(); + } + + socket.close(); + + println!("Done\n"); + println!(); + } + + let wait_end = current_millis() + 5 * 1000; + while current_millis() < wait_end { + socket.work(); + } + } +} + +fn parse_ip(ip: &str) -> [u8; 4] { + let mut result = [0u8; 4]; + for (idx, octet) in ip.split(".").into_iter().enumerate() { + result[idx] = u8::from_str_radix(octet, 10).unwrap(); + } + result +} diff --git a/src/wifi_interface.rs b/src/wifi_interface.rs index b86cdd48..03d71b89 100644 --- a/src/wifi_interface.rs +++ b/src/wifi_interface.rs @@ -504,6 +504,56 @@ impl<'s, 'n: 's> Socket<'s, 'n> { Ok(()) } + pub fn listen<'i>(&'i mut self, port: u16) -> Result<(), IoError> + where + 's: 'i, + { + { + let res = self.network.with_interface(|interface| { + let (sock, _cx) = interface + .network_interface() + .get_socket_and_context::(self.socket_handle); + sock.listen(port) + }); + + if let Err(err) = res { + return Err(err.into()); + } + } + + loop { + let can_send = self.network.with_interface(|interface| { + let sock = interface + .network_interface() + .get_socket::(self.socket_handle); + if sock.can_send() { + true + } else { + false + } + }); + + if can_send { + break; + } + + self.work(); + } + + Ok(()) + } + + pub fn close(&mut self) { + self.network.with_interface(|interface| { + interface + .network_interface() + .get_socket::(self.socket_handle) + .close(); + }); + + self.work(); + } + pub fn disconnect(&mut self) { self.network.with_interface(|interface| { interface @@ -515,10 +565,51 @@ impl<'s, 'n: 's> Socket<'s, 'n> { self.work(); } + pub fn is_open(&mut self) -> bool { + self.network.with_interface(|interface| { + interface + .network_interface() + .get_socket::(self.socket_handle) + .is_open() + }) + } + + pub fn is_connected(&mut self) -> bool { + self.network.with_interface(|interface| { + let socket = interface + .network_interface() + .get_socket::(self.socket_handle); + + socket.may_recv() && socket.may_send() + }) + } + pub fn work(&mut self) { loop { - self.network - .with_interface(|interface| interface.poll_dhcp().ok()); + self.network.with_interface(|interface| { + if let ipv4::Configuration::Client(ipv4::ClientConfiguration::DHCP(_)) = + interface.network_config + { + interface.poll_dhcp().ok(); + } else if let ipv4::Configuration::Client(ipv4::ClientConfiguration::Fixed( + settings, + )) = interface.network_config + { + let addr = Ipv4Address::from_bytes(&settings.ip.octets()); + if !interface.network_interface().has_ip_addr(addr) { + let gateway = Ipv4Address::from_bytes(&settings.subnet.gateway.octets()); + interface + .network_interface() + .routes_mut() + .add_default_ipv4_route(gateway) + .ok(); + interface.network_interface().update_ip_addrs(|addrs| { + addrs[0] = IpCidr::new(addr.into(), settings.subnet.mask.0); + }); + } + } + }); + if let Ok(false) = self.network.with_interface(|interface| { interface .network_interface() From 9b644387a54a5ace93d4280f29075ad2a9e9a8ed Mon Sep 17 00:00:00 2001 From: bjoernQ Date: Thu, 1 Dec 2022 17:26:59 +0100 Subject: [PATCH 2/2] README.md: Rust compiler 1.65 required --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b119bff5..db3df5c8 100644 --- a/README.md +++ b/README.md @@ -8,7 +8,7 @@ WiFi / BTLE coexistence is implemented but currently only works (to some extend) On ESP32-S3 only WiFi is currently supported. -THIS CURRENTLY DOESN'T WORK WITH THE XTENSA ENABLED RUST COMPILER 1.63.0.2. Use 1.65 or better +Minimum support Rust compiler version: 1.65.0.0 This uses the WiFi drivers from https://github.com/esp-rs/esp-wireless-drivers-3rdparty