From 8341bc99e8956b4e941bfaa4638c8b02b3dddd0d Mon Sep 17 00:00:00 2001 From: memdmp Date: Sat, 15 Mar 2025 19:22:46 +0100 Subject: feat: ok more minimal --- src/bin/btn-test.rs | 2 +- src/bin/main.rs | 381 ------------------------------------------------- src/bin/server.rs | 400 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 401 insertions(+), 382 deletions(-) delete mode 100644 src/bin/main.rs create mode 100644 src/bin/server.rs (limited to 'src') diff --git a/src/bin/btn-test.rs b/src/bin/btn-test.rs index b33e402..87663ef 100644 --- a/src/bin/btn-test.rs +++ b/src/bin/btn-test.rs @@ -16,7 +16,7 @@ fn main() -> ! { // Set GPIO7 as an output, and set its state high initially. let mut led = Output::new(peripherals.GPIO1, Level::Low); - let button = Input::new(peripherals.GPIO3, Pull::Up); + let button = Input::new(peripherals.GPIO3, Pull::Down); // Check the button state and set the LED state accordingly. loop { diff --git a/src/bin/main.rs b/src/bin/main.rs deleted file mode 100644 index e9b2386..0000000 --- a/src/bin/main.rs +++ /dev/null @@ -1,381 +0,0 @@ -//! 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 { - /// No value. - None, - /// Some value of type `T`. - Some(T), -} - -static BUTTON: Mutex>> = Mutex::new(RefCell::new(None)); -static mut IS_OPEN: bool = false; -static mut LED: OurOption>> = OurOption::None; - -fn set_led_state(val: bool) { - 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 the interrupt handler for GPIO interrupts. - io.set_interrupt_handler(handler); - - // 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::Up); - - critical_section::with(|cs| { - button.listen(Event::FallingEdge); - BUTTON.borrow_ref_mut(cs).replace(button) - }); - - 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, 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(); - } - - 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 = unsafe { 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 { - 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(); - } - - socket.flush().unwrap(); - socket.work(); - // } - - socket.close(); - } - // TODO: what - let deadline = time::now() + Duration::millis(100); - while time::now() < deadline && !socket.is_connected() { - 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 -} - -#[handler] -fn handler() { - critical_section::with(|cs| { - info!("GPIO interrupt"); - let mut binding = BUTTON.borrow_ref_mut(cs); - let v = binding.as_mut().unwrap(); - if v.is_low() { - unsafe { - IS_OPEN = !IS_OPEN; - set_led_state(IS_OPEN); - } - } - v.clear_interrupt(); - }); -} 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 { + /// No value. + None, + /// Some value of type `T`. + Some(T), +} + +static BUTTON: Mutex>> = Mutex::new(RefCell::new(None)); +static mut IS_OPEN: bool = false; +static mut LED: OurOption>> = 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, 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 -- cgit v1.2.3