summaryrefslogtreecommitdiffstats
path: root/src/bin/server.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/server.rs')
-rw-r--r--src/bin/server.rs400
1 files changed, 400 insertions, 0 deletions
diff --git a/src/bin/server.rs b/src/bin/server.rs
new file mode 100644
index 0000000..106295f
--- /dev/null
+++ b/src/bin/server.rs
@@ -0,0 +1,400 @@
+//! 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::cell::{OnceCell, RefCell};
+use core::ops::Deref;
+use core::str::FromStr;
+
+use blocking_network_stack::Stack;
+use critical_section::Mutex;
+use dumbswitch::network_data;
+use embedded_io::*;
+use esp_alloc as _;
+use esp_backtrace as _;
+use esp_hal::delay::Delay;
+use esp_hal::gpio::{Event, Input, Io, Level, Output, Pull};
+use esp_hal::handler;
+use esp_hal::interrupt::InterruptConfigurable;
+use esp_hal::{
+ clock::CpuClock,
+ main,
+ rng::Rng,
+ time::{self, Duration},
+ timer::timg::TimerGroup,
+};
+use esp_println::println;
+use esp_wifi::{
+ init,
+ wifi::{
+ utils::create_network_interface, AccessPointInfo, ClientConfiguration, Configuration,
+ WifiError, WifiStaDevice,
+ },
+};
+use log::{debug, error, info, trace, warn};
+use smoltcp::iface::{SocketSet, SocketStorage};
+
+pub enum OurOption<T> {
+ /// No value.
+ None,
+ /// Some value of type `T`.
+ Some(T),
+}
+
+static BUTTON: Mutex<RefCell<Option<Input>>> = Mutex::new(RefCell::new(None));
+static mut IS_OPEN: bool = false;
+static mut LED: OurOption<RefCell<Output<'_>>> = OurOption::None;
+
+fn set_open_led_state(val: bool) {
+ #[cfg(not(feature="led-as-busy-led"))]
+ {
+ let v = unsafe {
+ #[allow(static_mut_refs)]
+ &LED
+ };
+ let v = match v {
+ OurOption::None => {
+ return;
+ }
+ OurOption::Some(v) => v,
+ };
+ let mut b = v.borrow_mut();
+ b.set_level(if val { Level::High } else { Level::Low });
+ }
+}
+
+// TODO: in future, add another LED for busy
+fn set_busy_led_state(val: bool) {
+ #[cfg(feature="led-as-busy-led")]
+ {
+ let v = unsafe {
+ #[allow(static_mut_refs)]
+ &LED
+ };
+ let v = match v {
+ OurOption::None => {
+ return;
+ }
+ OurOption::Some(v) => v,
+ };
+ let mut b = v.borrow_mut();
+ b.set_level(if val { Level::High } else { Level::Low });
+ }
+}
+
+#[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();
+
+ esp_alloc::heap_allocator!(72 * 1024);
+
+ info!("Preparing GPIO");
+
+ let mut io = Io::new(peripherals.IO_MUX);
+
+ // Set GPIO1 as an output, and set its state low initially.
+ let led = RefCell::new(Output::new(peripherals.GPIO1, Level::Low));
+ let led = unsafe {
+ LED = OurOption::Some(led);
+ #[allow(static_mut_refs)]
+ match &LED {
+ OurOption::None => panic!(),
+ OurOption::Some(v) => v,
+ }
+ };
+
+ // Set GPIO4 as an input
+ let mut button = Input::new(peripherals.GPIO3, Pull::Down);
+
+ info!("Waiting 500ms pre-init");
+ delay.delay_millis(500);
+
+ led.borrow_mut().set_low();
+
+ 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: network_data::SSID.try_into().unwrap(),
+ password: network_data::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<AccessPointInfo, 30>, usize), WifiError> = controller.scan_n();
+ if let Ok((res, _count)) = res {
+ for ap in res {
+ debug!("AP Info: {:?}", ap);
+ }
+ }
+
+ debug!("{:?}", controller.capabilities());
+ debug!("wifi_connect {:?}", controller.connect());
+
+ // wait to get connected
+ info!("Wait to get connected");
+ loop {
+ match controller.is_connected() {
+ Ok(true) => break,
+ Ok(false) => {}
+ Err(err) => {
+ error!("Failed to connect to wifi: {:?}", err);
+ let mut high = false;
+ loop {
+ delay.delay_millis(1000);
+ high = !high;
+ if high {
+ led.borrow_mut().set_high();
+ } else {
+ led.borrow_mut().set_low();
+ }
+ }
+ }
+ }
+ }
+ debug!("{:?}", controller.is_connected());
+
+ info!("Setting static IP {}", network_data::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(network_data::STATIC_IP)),
+ subnet: blocking_network_stack::ipv4::Subnet {
+ gateway: blocking_network_stack::ipv4::Ipv4Addr::from(parse_ip(
+ network_data::GATEWAY_IP,
+ )),
+ mask: blocking_network_stack::ipv4::Mask(24),
+ },
+ dns: None,
+ secondary_dns: None,
+ },
+ ),
+ ))
+ .unwrap();
+
+ for i in 0..4 {
+ delay.delay_millis(100);
+ if i % 2 == 0 {
+ led.borrow_mut().set_high();
+ } else {
+ led.borrow_mut().set_low();
+ }
+ }
+
+ info!(
+ "Start busy loop on main. Point your browser to http://{}:8080/",
+ network_data::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);
+
+ socket.listen(8080).unwrap();
+
+ // fn update_open(is_open: bool, button: &Input<'_>, led: &mut Output<'_>, delay: &Delay) -> bool {
+ // let mut is_open = if is_open { true } else { false };
+ // if button.is_low() {
+ // is_open = !is_open;
+
+ // if is_open {
+ // led.set_high();
+ // } else {
+ // led.set_low();
+ // }
+ // while button.is_low() {
+ // trace!("waiting for unpress btn");
+ // delay.delay_millis(10);
+ // }
+ // }
+ // is_open
+ // }
+
+ loop {
+ socket.work();
+
+ if !socket.is_open() {
+ socket.listen(8080).unwrap();
+ }
+
+ set_busy_led_state(true);
+ if socket.is_connected() {
+ // debug!("Established Connection");
+
+ // let mut time_out = false;
+ // let deadline = time::now() + Duration::millis(500);
+ // let mut buffer = [0u8; 8192];
+ // let mut pos = 0;
+ // while let Ok(len) = socket.read(&mut buffer[pos..]) {
+ // if pos + len > buffer.len() {
+ // error!(
+ // "We got {} bytes. Buffer overflowed, treating as timeout.",
+ // pos + len
+ // );
+ // time_out = true;
+ // break;
+ // }
+
+ // 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 is_open = button.is_high();
+ unsafe {
+ set_open_led_state(is_open);
+ }
+ 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 {
+ let r =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\
+",
+ );
+ if !r.is_ok() {
+ error!("{:#?}",r.unwrap_err());
+ continue;
+ };
+ for part in parts {
+ let r = socket.write_all(part);
+
+ if !r.is_ok() {
+ error!("{:#?}", r.unwrap_err());
+ continue;
+ };
+ }
+
+ let r = socket.flush();
+ if !r.is_ok() {
+ error!("{:#?}",r.unwrap_err());
+ continue;
+ };
+ socket.work();
+ // }
+
+ socket.close();
+ }
+ set_busy_led_state(false);
+ // TODO: what
+ let deadline = time::now() + Duration::millis(100);
+ while time::now() < deadline && !socket.is_connected() {
+ socket.work();
+ }
+
+ let is_open = button.is_high();
+ set_open_led_state(is_open);
+ }
+}
+
+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
+} \ No newline at end of file