diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 03f7496..315069f 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -57,6 +57,7 @@ esp-hal = { version = "=1.0.0-rc.0", features = [ "esp32c6", "log-04", "unstable", + "rt" ] } log = "0.4.27" diff --git a/rust/src/hal/esp.rs b/rust/src/hal/esp.rs index 4fe30f5..3c430ef 100644 --- a/rust/src/hal/esp.rs +++ b/rust/src/hal/esp.rs @@ -175,7 +175,7 @@ impl Esp<'_> { } } - pub(crate) async fn wifi_ap(&mut self) -> anyhow::Result { + pub(crate) async fn wifi_ap(&mut self) -> anyhow::Result> { let _ssid = match self.load_config() { Ok(config) => config.network.ap_ssid.clone(), Err(_) => heapless::String::from_str("PlantCtrl Emergency Mode").unwrap(), @@ -207,7 +207,7 @@ impl Esp<'_> { let controller = self.controller.take().unwrap(); spawner.spawn(connection(controller)).ok(); spawner.spawn(net_task(runner)).ok(); - spawner.spawn(run_dhcp(stack, gw_ip_addr_str)).ok(); + spawner.spawn(run_dhcp(stack.clone(), gw_ip_addr_str)).ok(); loop { if stack.is_link_up() { @@ -699,7 +699,7 @@ impl Esp<'_> { } #[embassy_executor::task] -async fn run_dhcp(stack: &'static Stack<'static>, gw_ip_addr: &'static str) { +async fn run_dhcp(stack: Stack<'static>, gw_ip_addr: &'static str) { use core::net::{Ipv4Addr, SocketAddrV4}; use edge_dhcp::{ @@ -716,7 +716,7 @@ async fn run_dhcp(stack: &'static Stack<'static>, gw_ip_addr: &'static str) { let mut gw_buf = [Ipv4Addr::UNSPECIFIED]; let buffers = UdpBuffers::<3, 1024, 1024, 10>::new(); - let unbound_socket = Udp::new(*stack, &buffers); + let unbound_socket = Udp::new(stack, &buffers); let mut bound_socket = unbound_socket .bind(core::net::SocketAddr::V4(SocketAddrV4::new( Ipv4Addr::UNSPECIFIED, diff --git a/rust/src/hal/initial_hal.rs b/rust/src/hal/initial_hal.rs index 89ecf45..736f1d8 100644 --- a/rust/src/hal/initial_hal.rs +++ b/rust/src/hal/initial_hal.rs @@ -11,10 +11,12 @@ use crate::{ use anyhow::{bail, Result}; use async_trait::async_trait; use chrono::{DateTime, Utc}; +use esp_hal::gpio::{Level, Output, OutputConfig}; +use log::info; use measurements::{Current, Voltage}; pub struct Initial<'a> { - //pub(crate) general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>, + pub(crate) general_fault: Output<'a>, pub(crate) esp: Esp<'a>, pub(crate) config: PlantControllerConfig, pub(crate) battery: Box, @@ -47,21 +49,14 @@ impl RTCModuleInteraction for NoRTC { } pub(crate) fn create_initial_board( - //free_pins: FreePeripherals, - _fs_mount_error: bool, + free_pins: FreePeripherals<'static>, config: PlantControllerConfig, esp: Esp<'static>, ) -> Result + Send>> { log::info!("Start initial"); - // let mut general_fault = PinDriver::input_output(free_pins.gpio6.downgrade())?; - // general_fault.set_pull(Pull::Floating)?; - // general_fault.set_low()?; - // - // if fs_mount_error { - // general_fault.set_high()? - // } + let general_fault = Output::new(free_pins.gpio23, Level::Low, OutputConfig::default()); let v = Initial { - //general_fault, + general_fault, config, esp, battery: Box::new(NoBatteryMonitor {}), @@ -122,8 +117,8 @@ impl<'a> BoardInteraction<'a> for Initial<'a> { bail!("Please configure board revision") } - async fn general_fault(&mut self, _enable: bool) { - //let _ = self.general_fault.set_state(enable.into()); + async fn general_fault(&mut self, enable: bool) { + self.general_fault.set_level(enable.into()); } async fn test(&mut self) -> Result<()> { diff --git a/rust/src/hal/mod.rs b/rust/src/hal/mod.rs index cc514d0..d45c319 100644 --- a/rust/src/hal/mod.rs +++ b/rust/src/hal/mod.rs @@ -6,6 +6,10 @@ mod rtc; use crate::alloc::string::ToString; use crate::hal::rtc::RTCModuleInteraction; +use esp_hal::peripherals::Peripherals; +use esp_hal::peripherals::GPIO23; +use esp_hal::peripherals::GPIO6; + //use crate::hal::water::TankSensor; use crate::{ config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig}, @@ -33,7 +37,7 @@ use embassy_sync::once_lock::OnceLock; use embassy_time::{Duration, Timer}; use esp_bootloader_esp_idf::partitions::DataPartitionSubType; use esp_hal::clock::CpuClock; -use esp_hal::gpio::{Input, InputConfig, Pull}; +use esp_hal::gpio::{Input, InputConfig, Io, Pull}; use esp_hal::timer::systimer::SystemTimer; use esp_println::{print, println}; use measurements::{Current, Voltage}; @@ -41,6 +45,7 @@ use measurements::{Current, Voltage}; use embassy_sync::mutex::{Mutex, MutexGuard}; use esp_alloc as _; use esp_backtrace as _; +use esp_hal::analog::adc::Adc; use esp_hal::rng::Rng; use esp_hal::timer::timg::TimerGroup; use esp_wifi::wifi::{ @@ -125,14 +130,14 @@ impl dyn BoardInteraction<'_> { } #[allow(dead_code)] -pub struct FreePeripherals { +pub struct FreePeripherals<'a> { // pub gpio0: Gpio0, // pub gpio1: Gpio1, // pub gpio2: Gpio2, // pub gpio3: Gpio3, // pub gpio4: Gpio4, // pub gpio5: Gpio5, - // pub gpio6: Gpio6, + pub gpio6: GPIO6<'a>, // pub gpio7: Gpio7, // pub gpio8: Gpio8, // //config button here @@ -148,7 +153,7 @@ pub struct FreePeripherals { // //i2c here // pub gpio21: Gpio21, // pub gpio22: Gpio22, - // pub gpio23: Gpio23, + pub gpio23: GPIO23<'a>, // pub gpio24: Gpio24, // pub gpio25: Gpio25, // pub gpio26: Gpio26, @@ -192,7 +197,7 @@ impl PlantHal { pub async fn create(spawner: Spawner) -> Result>> { let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max()); - let peripherals = esp_hal::init(config); + let peripherals: Peripherals = esp_hal::init(config); esp_alloc::heap_allocator!(size: 64 * 1024); let systimer = SystemTimer::new(peripherals.SYSTIMER); @@ -215,41 +220,42 @@ impl PlantHal { use esp_hal::timer::systimer::SystemTimer; esp_hal_embassy::init(systimer.alarm0); + //let mut adc1 = Adc::new(peripherals.ADC1, adc1_config); // - // let free_pins = FreePeripherals { - // can: peripherals.can, - // adc1: peripherals.adc1, - // pcnt0: peripherals.pcnt0, - // pcnt1: peripherals.pcnt1, - // gpio0: peripherals.pins.gpio0, - // gpio1: peripherals.pins.gpio1, - // gpio2: peripherals.pins.gpio2, - // gpio3: peripherals.pins.gpio3, - // gpio4: peripherals.pins.gpio4, - // gpio5: peripherals.pins.gpio5, - // gpio6: peripherals.pins.gpio6, - // gpio7: peripherals.pins.gpio7, - // gpio8: peripherals.pins.gpio8, - // gpio10: peripherals.pins.gpio10, - // gpio11: peripherals.pins.gpio11, - // gpio12: peripherals.pins.gpio12, - // gpio13: peripherals.pins.gpio13, - // gpio14: peripherals.pins.gpio14, - // gpio15: peripherals.pins.gpio15, - // gpio16: peripherals.pins.gpio16, - // gpio17: peripherals.pins.gpio17, - // gpio18: peripherals.pins.gpio18, - // gpio21: peripherals.pins.gpio21, - // gpio22: peripherals.pins.gpio22, - // gpio23: peripherals.pins.gpio23, - // gpio24: peripherals.pins.gpio24, - // gpio25: peripherals.pins.gpio25, - // gpio26: peripherals.pins.gpio26, - // gpio27: peripherals.pins.gpio27, - // gpio28: peripherals.pins.gpio28, - // gpio29: peripherals.pins.gpio29, - // gpio30: peripherals.pins.gpio30, - // }; + let free_pins = FreePeripherals { + // can: peripherals.can, + // adc1: peripherals.adc1, + // pcnt0: peripherals.pcnt0, + // pcnt1: peripherals.pcnt1, + // gpio0: peripherals.pins.gpio0, + // gpio1: peripherals.pins.gpio1, + // gpio2: peripherals.pins.gpio2, + // gpio3: peripherals.pins.gpio3, + // gpio4: peripherals.pins.gpio4, + // gpio5: peripherals.pins.gpio5, + gpio6: peripherals.GPIO6, + // gpio7: peripherals.pins.gpio7, + // gpio8: peripherals.pins.gpio8, + // gpio10: peripherals.pins.gpio10, + // gpio11: peripherals.pins.gpio11, + // gpio12: peripherals.pins.gpio12, + // gpio13: peripherals.pins.gpio13, + // gpio14: peripherals.pins.gpio14, + // gpio15: peripherals.pins.gpio15, + // gpio16: peripherals.pins.gpio16, + // gpio17: peripherals.pins.gpio17, + // gpio18: peripherals.pins.gpio18, + // gpio21: peripherals.pins.gpio21, + // gpio22: peripherals.pins.gpio22, + gpio23: peripherals.GPIO23, + // gpio24: peripherals.pins.gpio24, + // gpio25: peripherals.pins.gpio25, + // gpio26: peripherals.pins.gpio26, + // gpio27: peripherals.pins.gpio27, + // gpio28: peripherals.pins.gpio28, + // gpio29: peripherals.pins.gpio29, + // gpio30: peripherals.pins.gpio30, + }; // let mut storage = esp_storage::FlashStorage::new(); @@ -392,7 +398,7 @@ impl PlantHal { let board_hal: Box = match config.hardware.board { BoardVersion::INITIAL => { - initial_hal::create_initial_board(fs_mount_error, config, esp)? + initial_hal::create_initial_board(free_pins, config, esp)? } // BoardVersion::V3 => { // v3_hal::create_v3(free_pins, esp, config, battery_interaction, rtc_module)? @@ -417,7 +423,7 @@ impl PlantHal { ); HAL { board_hal: initial_hal::create_initial_board( - fs_mount_error, + free_pins, PlantControllerConfig::default(), esp, )?, diff --git a/rust/src/log/mod.rs b/rust/src/log/mod.rs index 338ac4c..ae3514a 100644 --- a/rust/src/log/mod.rs +++ b/rust/src/log/mod.rs @@ -5,6 +5,7 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::lazy_lock::LazyLock; use embassy_sync::mutex::Mutex; use embassy_time::Instant; +use esp_println::println; use log::info; use serde::Serialize; use strum_macros::IntoStaticStr; diff --git a/rust/src/main.rs b/rust/src/main.rs index 93146e1..d1c4ed4 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -64,7 +64,7 @@ mod webserver; extern crate alloc; //mod webserver; -pub static BOARD_ACCESS: OnceLock> = OnceLock::new(); +pub static BOARD_ACCESS: OnceLock>> = OnceLock::new(); pub static STAY_ALIVE: AtomicBool = AtomicBool::new(false); @@ -143,7 +143,7 @@ enum NetworkMode { OFFLINE, } -async fn safe_main() -> anyhow::Result<()> { +async fn safe_main(spawner: Spawner) -> anyhow::Result<()> { info!("Startup Rust"); let mut to_config = false; @@ -179,10 +179,8 @@ async fn safe_main() -> anyhow::Result<()> { //}; //log(LogMessage::PartitionState, 0, 0, "", ota_state_string); let _ota_state_string = "unknown"; - println!("faul led"); let mut board = BOARD_ACCESS.get().await.lock().await; board.board_hal.general_fault(false).await; - println!("faul led2"); let cur = board .board_hal .get_rtc_module() @@ -196,7 +194,7 @@ async fn safe_main() -> anyhow::Result<()> { //check if we know the time current > 2020 (plausibility checks, this code is newer than 2020) if cur.year() < 2020 { to_config = true; - log(LogMessage::YearInplausibleForceConfig, 0, 0, "", ""); + log(LogMessage::YearInplausibleForceConfig, 0, 0, "", "").await; } info!("cur is {}", cur); @@ -237,12 +235,15 @@ async fn safe_main() -> anyhow::Result<()> { && board.board_hal.get_config().network.ssid.is_none() { info!("No wifi configured, starting initial config mode"); + let stack = board.board_hal.get_esp().wifi_ap().await?; let reboot_now = Arc::new(AtomicBool::new(false)); println!("starting webserver"); - let _webserver = httpd(reboot_now.clone(), stack).await; - wait_infinity(WaitType::MissingConfig, reboot_now.clone()).await; + + spawner.spawn(httpd(reboot_now.clone(), stack))?; + + wait_infinity(board, WaitType::MissingConfig, reboot_now.clone()).await; } info!("attempting to connect wifi"); @@ -257,7 +258,12 @@ async fn safe_main() -> anyhow::Result<()> { if matches!(network_mode, NetworkMode::OFFLINE) && to_config { info!("Could not connect to station and config mode forced, switching to ap mode!"); - match board.board_hal.get_esp().wifi_ap().await { + + let res = { + let esp = board.board_hal.get_esp(); + esp.wifi_ap().await + }; + match res { Ok(_) => { info!("Started ap, continuing") } @@ -274,8 +280,6 @@ async fn safe_main() -> anyhow::Result<()> { // }; let _timezone = Tz::UTC; - drop(board); - let timezone_time = cur; //TODO.with_timezone(&timezone); info!( "Running logic at utc {} and {} {}", @@ -283,9 +287,9 @@ async fn safe_main() -> anyhow::Result<()> { ); if let NetworkMode::WIFI { ref ip_address, .. } = network_mode { - publish_firmware_info(version, ip_address, &timezone_time.to_rfc3339()).await; - publish_battery_state().await; - let _ = publish_mppt_state().await; + publish_firmware_info(&mut board, version, ip_address, &timezone_time.to_rfc3339()).await; + publish_battery_state(&mut board).await; + let _ = publish_mppt_state(&mut board).await; } log( @@ -312,7 +316,8 @@ async fn safe_main() -> anyhow::Result<()> { let reboot_now = Arc::new(AtomicBool::new(false)); //TODO //let _webserver = httpd(reboot_now.clone()); - wait_infinity(WaitType::ConfigButton, reboot_now.clone()).await; + let board = BOARD_ACCESS.get().await.lock().await; + wait_infinity(board, WaitType::ConfigButton, reboot_now.clone()).await; } else { log(LogMessage::NormalRun, 0, 0, "", "").await; } @@ -589,11 +594,10 @@ async fn safe_main() -> anyhow::Result<()> { if stay_alive { info!("Go to stay alive move"); - drop(board); let reboot_now = Arc::new(AtomicBool::new(false)); //TODO //let _webserver = httpd(reboot_now.clone()); - wait_infinity(WaitType::MqttConfig, reboot_now.clone()).await; + wait_infinity(board, WaitType::MqttConfig, reboot_now.clone()).await; } else { board.board_hal.get_esp().set_restart_to_conf(false); board @@ -831,8 +835,12 @@ async fn update_charge_indicator(board: &mut MutexGuard<'_, CriticalSectionRawMu // } // } -async fn publish_firmware_info(version: VersionInfo, ip_address: &String, timezone_time: &String) { - let board = &mut BOARD_ACCESS.get().await.lock().await; +async fn publish_firmware_info( + mut board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>, + version: VersionInfo, + ip_address: &String, + timezone_time: &String, +) { let esp = board.board_hal.get_esp(); let _ = esp .mqtt_publish("/firmware/address", ip_address.as_bytes()) @@ -952,10 +960,11 @@ async fn pump_info( }; } -async fn publish_mppt_state() -> anyhow::Result<()> { - let board_hal = &mut BOARD_ACCESS.get().await.lock().await.board_hal; - let current = board_hal.get_mptt_current().await?; - let voltage = board_hal.get_mptt_voltage().await?; +async fn publish_mppt_state( + board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>, +) -> anyhow::Result<()> { + let current = board.board_hal.get_mptt_current().await?; + let voltage = board.board_hal.get_mptt_voltage().await?; let solar_state = Solar { current_ma: current.as_milliamperes() as u32, voltage_ma: voltage.as_millivolts() as u32, @@ -963,15 +972,17 @@ async fn publish_mppt_state() -> anyhow::Result<()> { if let Ok(serialized_solar_state_bytes) = serde_json::to_string(&solar_state).map(|s| s.into_bytes()) { - let _ = board_hal + let _ = board + .board_hal .get_esp() .mqtt_publish("/mppt", &serialized_solar_state_bytes); } Ok(()) } -async fn publish_battery_state() -> () { - let board = &mut BOARD_ACCESS.get().await.lock().await; +async fn publish_battery_state( + board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>, +) -> () { let state = board .board_hal .get_battery_monitor() @@ -983,55 +994,65 @@ async fn publish_battery_state() -> () { board .board_hal .get_esp() - .mqtt_publish("/battery", &serialized_battery_state_bytes); + .mqtt_publish("/battery", &serialized_battery_state_bytes) + .await; } } -async fn wait_infinity(wait_type: WaitType, reboot_now: Arc) -> ! { +async fn wait_infinity( + board: MutexGuard<'_, CriticalSectionRawMutex, HAL<'static>>, + wait_type: WaitType, + reboot_now: Arc, +) -> ! { + //since we force to have the lock when entering, we can release it to ensure the caller does not forget to dispose of it + drop(board); + let delay = wait_type.blink_pattern(); let mut led_count = 8; let mut pattern_step = 0; loop { - let mut board = BOARD_ACCESS.get().await.lock().await; - update_charge_indicator(&mut board).await; + { + let mut board = BOARD_ACCESS.get().await.lock().await; + update_charge_indicator(&mut board).await; - match wait_type { - WaitType::MissingConfig => { - // Keep existing behavior: circular filling pattern - led_count %= 8; - led_count += 1; - for i in 0..8 { - let _ = board.board_hal.fault(i, i < led_count); - } - } - WaitType::ConfigButton => { - // Alternating pattern: 1010 1010 -> 0101 0101 - pattern_step = (pattern_step + 1) % 2; - for i in 0..8 { - let _ = board.board_hal.fault(i, (i + pattern_step) % 2 == 0); - } - } - WaitType::MqttConfig => { - // Moving dot pattern - pattern_step = (pattern_step + 1) % 8; - for i in 0..8 { - let _ = board.board_hal.fault(i, i == pattern_step); + match wait_type { + WaitType::MissingConfig => { + // Keep existing behavior: circular filling pattern + led_count %= 8; + led_count += 1; + for i in 0..8 { + let _ = board.board_hal.fault(i, i < led_count); + } + } + WaitType::ConfigButton => { + // Alternating pattern: 1010 1010 -> 0101 0101 + pattern_step = (pattern_step + 1) % 2; + for i in 0..8 { + let _ = board.board_hal.fault(i, (i + pattern_step) % 2 == 0); + } + } + WaitType::MqttConfig => { + // Moving dot pattern + pattern_step = (pattern_step + 1) % 8; + for i in 0..8 { + let _ = board.board_hal.fault(i, i == pattern_step); + } } } + board.board_hal.general_fault(true).await; } - board.board_hal.general_fault(true); - drop(board); - Timer::after_millis(delay).await; - let mut board = BOARD_ACCESS.get().await.lock().await; - board.board_hal.general_fault(false); + { + let mut board = BOARD_ACCESS.get().await.lock().await; + board.board_hal.general_fault(false).await; - // Clear all LEDs - for i in 0..8 { - let _ = board.board_hal.fault(i, false); + // Clear all LEDs + for i in 0..8 { + let _ = board.board_hal.fault(i, false); + } } - drop(board); + Timer::after_millis(delay).await; if wait_type == WaitType::MqttConfig && !STAY_ALIVE.load(Ordering::Relaxed) { @@ -1065,7 +1086,7 @@ async fn main(spawner: Spawner) { } println!("Hal init done, starting logic"); - match safe_main().await { + match safe_main(spawner).await { // this should not get triggered, safe_main should not return but go into deep sleep with sensible // timeout, this is just a fallback Ok(_) => { diff --git a/rust/src/webserver/mod.rs b/rust/src/webserver/mod.rs index 1048a06..9f85133 100644 --- a/rust/src/webserver/mod.rs +++ b/rust/src/webserver/mod.rs @@ -16,10 +16,12 @@ use anyhow::{bail, Context}; use chrono::DateTime; use core::result::Result::Ok; use core::sync::atomic::AtomicBool; +use embassy_executor::Spawner; use embassy_net::tcp::TcpSocket; use embassy_net::{IpListenEndpoint, Stack}; use embassy_time::{Duration, Timer}; use esp_println::{print, println}; +use esp_wifi::wifi::WifiController; use serde::{Deserialize, Serialize}; #[derive(Serialize, Debug)] @@ -388,7 +390,8 @@ pub struct NightLampCommand { // } // } -pub async fn httpd(reboot_now: Arc, stack: Stack<'_>) { +#[embassy_executor::task] +pub async fn httpd(reboot_now: Arc, stack: Stack<'static>) { let mut rx_buffer = [0; 1536]; let mut tx_buffer = [0; 1536]; println!("Stack {}", stack.is_config_up());