diff --git a/.gitignore b/.gitignore index 7f88f54..1680b08 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,5 @@ target/ # MSVC Windows builds of rustc generate these, which store debugging information *.pdb + +.env diff --git a/Cargo.lock b/Cargo.lock index 0fbdb50..d851208 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -217,6 +217,47 @@ dependencies = [ "syn 2.0.112", ] +[[package]] +name = "defmt" +version = "0.3.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad" +dependencies = [ + "defmt 1.0.1", +] + +[[package]] +name = "defmt" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "548d977b6da32fa1d1fda2876453da1e7df63ad0304c8b3dae4dbe7b96f39b78" +dependencies = [ + "bitflags 1.3.2", + "defmt-macros", +] + +[[package]] +name = "defmt-macros" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d4fc12a85bcf441cfe44344c4b72d58493178ce635338a3f3b78943aceb258e" +dependencies = [ + "defmt-parser", + "proc-macro-error2", + "proc-macro2", + "quote", + "syn 2.0.112", +] + +[[package]] +name = "defmt-parser" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" +dependencies = [ + "thiserror", +] + [[package]] name = "delegate" version = "0.13.5" @@ -247,6 +288,12 @@ dependencies = [ "litrs", ] +[[package]] +name = "dotenvy" +version = "0.15.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" + [[package]] name = "embassy-embedded-hal" version = "0.5.0" @@ -959,6 +1006,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ + "defmt 0.3.100", "hash32", "stable_deref_trait", ] @@ -1193,6 +1241,28 @@ dependencies = [ "toml_edit", ] +[[package]] +name = "proc-macro-error-attr2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "proc-macro-error2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" +dependencies = [ + "proc-macro-error-attr2", + "proc-macro2", + "quote", + "syn 2.0.112", +] + [[package]] name = "proc-macro2" version = "1.0.104" @@ -1408,6 +1478,7 @@ dependencies = [ "bitflags 1.3.2", "byteorder", "cfg-if", + "defmt 0.3.100", "heapless 0.8.0", "managed", ] @@ -1513,6 +1584,26 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.112", +] + [[package]] name = "toml_datetime" version = "0.7.5+spec-1.1.0" @@ -1618,6 +1709,7 @@ version = "0.1.0" dependencies = [ "bosch-bme680", "critical-section", + "dotenvy", "embassy-embedded-hal", "embassy-executor", "embassy-net", @@ -1632,6 +1724,7 @@ dependencies = [ "esp-rtos", "lcd-lcm1602-i2c", "scd4x", + "smoltcp", "static_cell", ] diff --git a/Cargo.toml b/Cargo.toml index fa7ea60..7e1961d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,12 +25,7 @@ esp-rtos = { version = "0.2.0", features = [ esp-bootloader-esp-idf = { version = "0.4.0", features = ["esp32", "log-04"] } -embassy-net = { version = "0.7.1", features = [ - "dhcpv4", - "log", - "tcp", - "udp", -] } +embassy-net = { version = "0.7.1", features = ["dhcpv4", "dns", "log", "tcp", "udp"] } esp-alloc = "0.9.0" esp-backtrace = { version = "0.18.1", features = [ "esp32", @@ -59,6 +54,23 @@ static_cell = "2.1.1" embassy-sync = "0.7.2" embassy-embedded-hal = "0.5.0" +smoltcp = { version = "0.12.0", default-features = false, features = [ + "defmt", + "medium-ethernet", + "multicast", + "proto-dhcpv4", + "proto-dns", + "proto-ipv4", + "socket-dns", + "socket-icmp", + "socket-raw", + "socket-tcp", + "socket-udp", + "dns-max-server-count-4", +] } + +[build-dependencies] +dotenvy = "0.15" [profile.dev] # Rust debug is too slow. diff --git a/build.rs b/build.rs index 2b6a670..3d2d3be 100644 --- a/build.rs +++ b/build.rs @@ -1,4 +1,12 @@ fn main() { + dotenvy::dotenv().ok(); + if let Ok(ssid) = std::env::var("SSID") { + println!("cargo:rustc-env=SSID={ssid}"); + } + if let Ok(password) = std::env::var("PASSWORD") { + println!("cargo:rustc-env=PASSWORD={password}"); + } + linker_be_nice(); // make sure linkall.x is the last linker script (otherwise might cause problems with flip-link) println!("cargo:rustc-link-arg=-Tlinkall.x"); diff --git a/src/lib.rs b/src/lib.rs index 0c9ac1a..31371c0 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1 +1,3 @@ #![no_std] + +pub mod wifi; diff --git a/src/main.rs b/src/main.rs index f729e77..68298a9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,10 +20,12 @@ use esp_hal::Async; use esp_hal::clock::CpuClock; use esp_hal::gpio::{Input, InputConfig}; use esp_hal::i2c::master::{Config as I2cConfig, I2c}; +use esp_hal::peripherals::Peripherals; use esp_hal::timer::timg::TimerGroup; use lcd_lcm1602_i2c::async_lcd::Lcd; use scd4x::Scd4xAsync; use static_cell::StaticCell; +use weather_station::wifi::init_wifi; extern crate alloc; @@ -60,14 +62,7 @@ type EventChannel = Channel; type EventSender = Sender<'static, NoopRawMutex, Events, 1>; type EventReceiver = Receiver<'static, NoopRawMutex, Events, 1>; -#[allow( - clippy::large_stack_frames, - reason = "it's not unusual to allocate larger buffers etc. in main" -)] -#[esp_rtos::main] -async fn main(spawner: Spawner) { - // generator version: 1.1.0 - +fn init_hardware() -> Peripherals { esp_println::logger::init_logger_from_env(); let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); @@ -75,16 +70,21 @@ async fn main(spawner: Spawner) { esp_alloc::heap_allocator!(#[esp_hal::ram(reclaimed)] size: 98768); - let timg0 = TimerGroup::new(peripherals.TIMG0); - esp_rtos::start(timg0.timer0); + peripherals +} - esp_println::println!("RTOS initialized!"); +#[allow( + clippy::large_stack_frames, + reason = "it's not unusual to allocate larger buffers etc. in main" +)] +#[esp_rtos::main] +async fn main(spawner: Spawner) { + let peripherals = init_hardware(); - let radio_init = esp_radio::init().expect("Failed to initialize Wi-Fi/BLE controller"); - let (mut _wifi_controller, _interfaces) = - esp_radio::wifi::new(&radio_init, peripherals.WIFI, Default::default()) - .expect("Failed to initialize Wi-Fi controller"); + let timg0 = TimerGroup::new(peripherals.TIMG0); + esp_rtos::start(timg0.timer0); + init_wifi(peripherals.WIFI, &spawner).await; esp_println::println!("WiFi initialized!"); let button_pin = Input::new( @@ -98,7 +98,7 @@ async fn main(spawner: Spawner) { .with_sda(peripherals.GPIO21) .into_async(); static I2C_V33_BUS: StaticCell = StaticCell::new(); - let i2c_v33_bus = I2C_V33_BUS.init(Mutex::new(i2c_v33)); + let i2c_v33 = I2C_V33_BUS.init(Mutex::new(i2c_v33)); let i2c_v5 = I2c::new(peripherals.I2C1, I2cConfig::default()) .unwrap() @@ -110,10 +110,10 @@ async fn main(spawner: Spawner) { let event_channel = CHANNEL.init(Channel::new()); spawner - .spawn(read_bme_680(i2c_v33_bus, event_channel.sender())) + .spawn(read_bme_680(i2c_v33, event_channel.sender())) .unwrap(); spawner - .spawn(read_scd_41(i2c_v33_bus, event_channel.sender())) + .spawn(read_scd_41(i2c_v33, event_channel.sender())) .unwrap(); spawner .spawn(listen_button(button_pin, event_channel.sender())) diff --git a/src/wifi.rs b/src/wifi.rs new file mode 100644 index 0000000..4fc7d12 --- /dev/null +++ b/src/wifi.rs @@ -0,0 +1,113 @@ +use core::ops::Not; + +use embassy_executor::Spawner; +use embassy_net::{DhcpConfig, Runner, Stack, StackResources}; +use embassy_time::{Duration, Timer}; +use esp_hal::rng::Rng; +use esp_println::println; +use esp_radio::wifi::{ + ClientConfig, ModeConfig, ScanConfig, WifiController, WifiDevice, WifiEvent, WifiStaState, +}; +use static_cell::StaticCell; + +async fn wait_for_connection(stack: Stack<'_>) { + println!("Waiting for link to be up"); + loop { + if stack.is_link_up() { + break; + } + Timer::after(Duration::from_millis(500)).await; + } + + println!("Waiting to get IP address..."); + loop { + if let Some(config) = stack.config_v4() { + println!("Got IP: {}", config.address); + break; + } + Timer::after(Duration::from_millis(500)).await; + } +} + +pub async fn init_wifi(wifi: esp_hal::peripherals::WIFI<'static>, spawner: &Spawner) { + static RADIO_INIT: StaticCell> = StaticCell::new(); + let radio_init = + RADIO_INIT.init(esp_radio::init().expect("Failed to initialize Wi-Fi/BLE controller")); + + let (wifi_controller, interfaces) = esp_radio::wifi::new(radio_init, wifi, Default::default()) + .expect("Failed to initialize Wi-Fi controller"); + let wifi_interface = interfaces.sta; + + let rng = Rng::new(); + let net_seed = rng.random() as u64 | ((rng.random() as u64) << 32); + let _tls_seed = rng.random() as u64 | ((rng.random() as u64) << 32); + + static STACK_RESOURCES: StaticCell> = StaticCell::new(); + let stack_resources = STACK_RESOURCES.init(StackResources::new()); + + let (stack, runner) = embassy_net::new( + wifi_interface, + embassy_net::Config::dhcpv4(DhcpConfig::default()), + stack_resources, + net_seed, + ); + + spawner.spawn(connection(wifi_controller)).ok(); + spawner.spawn(net_task(runner)).ok(); + + wait_for_connection(stack).await; +} + +#[embassy_executor::task] +async fn connection(mut controller: WifiController<'static>) { + const SSID: &str = env!("SSID"); + const PASSWORD: &str = env!("PASSWORD"); + + println!("start connection task"); + println!("Device capabilities: {:?}", controller.capabilities()); + loop { + match esp_radio::wifi::sta_state() { + WifiStaState::Connected => { + // wait until we're no longer connected + controller.wait_for_event(WifiEvent::StaDisconnected).await; + Timer::after(Duration::from_millis(5000)).await + } + _ => {} + } + if matches!(controller.is_started(), Ok(true)).not() { + let client_config = ModeConfig::Client( + ClientConfig::default() + .with_ssid(SSID.into()) + .with_password(PASSWORD.into()), + ); + controller.set_config(&client_config).unwrap(); + println!("Starting wifi"); + controller.start_async().await.unwrap(); + println!("Wifi started!"); + + println!("Scan"); + let scan_config = ScanConfig::default().with_max(10); + let result = controller + .scan_with_config_async(scan_config) + .await + .unwrap(); + for ap in result { + println!("{:?}", ap); + } + } + println!("About to connect..."); + + match controller.connect_async().await { + Ok(_) => println!("Wifi connected!"), + Err(e) => { + println!("Failed to connect to wifi: {:?}", e); + Timer::after(Duration::from_millis(5000)).await + } + } + } +} + +#[embassy_executor::task] +async fn net_task(mut runner: Runner<'static, WifiDevice<'static>>) { + runner.run().await +}