//! Using a static IP example //! //! - 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 //! //% FEATURES: esp-wifi esp-wifi/wifi esp-wifi/utils esp-hal/unstable //% CHIPS: esp32 esp32s2 esp32s3 esp32c2 esp32c3 esp32c6 #![no_std] #![no_main] use core::ops::Deref; use core::str::FromStr; use blocking_network_stack::Stack; use embedded_io::*; use esp_alloc as _; use esp_backtrace as _; use esp_hal::delay::Delay; use esp_hal::{ clock::CpuClock, main, rng::Rng, time::{self, Duration}, timer::timg::TimerGroup, }; use esp_println::{print, println}; use esp_wifi::{ init, wifi::{ utils::create_network_interface, AccessPointInfo, ClientConfiguration, Configuration, WifiError, WifiStaDevice, }, }; use heapless::String; use log::{debug, info}; use smoltcp::iface::{SocketSet, SocketStorage}; const SSID: &str = "Chaostreff Bern"; const PASSWORD: &str = "-"; const STATIC_IP: &str = "10.0.0.77"; const GATEWAY_IP: &str = "10.0.0.1"; #[main] fn main() -> ! { esp_println::logger::init_logger_from_env(); let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); let peripherals = esp_hal::init(config); let delay = Delay::new(); debug!("Waiting 500ms pre-init"); delay.delay_millis(500); esp_alloc::heap_allocator!(72 * 1024); let timg0 = TimerGroup::new(peripherals.TIMG0); let mut rng = Rng::new(peripherals.RNG); let init = init(timg0.timer0, rng.clone(), peripherals.RADIO_CLK).unwrap(); let mut wifi = peripherals.WIFI; let (iface, device, mut controller) = create_network_interface(&init, &mut wifi, WifiStaDevice).unwrap(); let mut socket_set_entries: [SocketStorage; 3] = Default::default(); let socket_set = SocketSet::new(&mut socket_set_entries[..]); let now = || time::now().duration_since_epoch().to_millis(); let mut stack = Stack::new(iface, device, socket_set, now, rng.random()); let client_config = Configuration::Client(ClientConfiguration { ssid: SSID.try_into().unwrap(), password: PASSWORD.try_into().unwrap(), ..Default::default() }); let res = controller.set_configuration(&client_config); debug!("wifi_set_configuration returned {:?}", res); controller.start().unwrap(); debug!("is wifi started: {:?}", controller.is_started()); info!("Start Wifi Scan"); let res: Result<(heapless::Vec, usize), WifiError> = controller.scan_n(); if let Ok((res, _count)) = res { for ap in res { debug!("{:?}", ap); } } debug!("{:?}", controller.capabilities()); debug!("wifi_connect {:?}", controller.connect()); // wait to get connected debug!("Wait to get connected"); loop { match controller.is_connected() { Ok(true) => break, Ok(false) => {} Err(err) => { println!("{:?}", err); loop {} } } } debug!("{:?}", controller.is_connected()); debug!("Setting static IP {}", STATIC_IP); stack .set_iface_configuration(&blocking_network_stack::ipv4::Configuration::Client( blocking_network_stack::ipv4::ClientConfiguration::Fixed( blocking_network_stack::ipv4::ClientSettings { ip: blocking_network_stack::ipv4::Ipv4Addr::from(parse_ip(STATIC_IP)), subnet: blocking_network_stack::ipv4::Subnet { gateway: blocking_network_stack::ipv4::Ipv4Addr::from(parse_ip(GATEWAY_IP)), mask: blocking_network_stack::ipv4::Mask(24), }, dns: None, secondary_dns: None, }, ), )) .unwrap(); info!( "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 = stack.get_socket(&mut rx_buffer, &mut tx_buffer); let mut is_open = false; socket.listen(8080).unwrap(); loop { socket.work(); if !socket.is_open() { socket.listen(8080).unwrap(); } if socket.is_connected() { info!("Connected"); let mut time_out = false; let deadline = time::now() + Duration::millis(500); let mut buffer = [0u8; 2048]; let mut pos = 0; while 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") { break; } pos += len; if time::now() > deadline { println!("Timeout"); time_out = true; break; } } let parts = [ (r##"{ "api": "0.13", "api_compatibility": [ "14", "15" ], "space": "Chaostreff Bern", "logo": "https://www.chaostreffbern.ch/images/logo_v1.1.png", "url": "https://www.chaostreffbern.ch","##) .as_bytes(), // TODO: Possibly add location.hint that we are in the basement? // TODO: Possibly add location.areas for each area in the space? (r##" "location": { "address": "Zwyssigstrasse 45, 3007 Bern, Switzerland", "lon": 7.421927, "lat": 46.944178, "timezone": "Europe/Zurich", "country_code": "CH" }, "spacefed": { "spacenet": false, "spacesaml": false, "spacephone": false }, "state": { "open": "##) .as_bytes(), (if is_open { "true" } else { "false" }).as_bytes(), r##", "message": "Open every Tuesday from 19h" }, "contact": { "email": "info@chaostreffbern.ch", "ml": "bern@chaostreff.ch", "matrix": "#chaostreffbern:chaostreffbern.ch", "jabber": "xmpp://chaostreffbern@conference.chaostreffbern.ch", "mastodon": "@chaostreffbern@chaos.social" }, "issue_report_channels": [ "email" ], "feeds": { "blog": { "type": "rss", "url": "https://www.chaosbern.ch/feeds/chaosbern_rss.xml" }, "calendar": { "type": "caldav", "url": "https://nextcloud.jenix.ch/remote.php/dav/public-calendars/xFMZfKSBNfp3mRNR/" } }, "ext_ccc": "chaostreff" } "## .as_bytes(), ]; if !time_out { socket .write_all( b"HTTP/1.0 200 OK\r\n\ Content-Type: application/json\r\n\ UwU: if u read this u have been catgirled :3\r\n\ \r\n\ ", ) .unwrap(); for part in parts { socket.write_all(part).unwrap(); } debug!("wrote {parts:?}"); socket.flush().unwrap(); } socket.close(); println!("Done\n"); println!(); } // TODO: what let deadline = time::now() + Duration::millis(1000); while time::now() < deadline { 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 }