enable deepsleep and gpio1 + timer wake
This commit is contained in:
		| @@ -21,14 +21,14 @@ use embassy_executor::Spawner; | ||||
| use embassy_net::udp::UdpSocket; | ||||
| use embassy_net::{DhcpConfig, Ipv4Cidr, Runner, Stack, StackResources, StaticConfigV4}; | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| use embassy_sync::mutex::Mutex; | ||||
| use embassy_sync::mutex::{Mutex, MutexGuard}; | ||||
| use embassy_time::{Duration, Timer}; | ||||
| use embedded_storage::nor_flash::ReadNorFlash; | ||||
| use esp_bootloader_esp_idf::ota::{Ota, OtaImageState}; | ||||
| use esp_bootloader_esp_idf::partitions::FlashRegion; | ||||
| use esp_hal::gpio::Input; | ||||
| use esp_hal::gpio::{Input, InputConfig, Pull, RtcPinWithResistors}; | ||||
| use esp_hal::rng::Rng; | ||||
| use esp_hal::rtc_cntl::sleep::RtcSleepConfig; | ||||
| use esp_hal::rtc_cntl::{sleep::{TimerWakeupSource, WakeupLevel}, Rtc}; | ||||
| use esp_hal::system::software_reset; | ||||
| use esp_println::println; | ||||
| use esp_storage::FlashStorage; | ||||
| @@ -113,6 +113,9 @@ pub struct Esp<'a> { | ||||
|  | ||||
|     pub boot_button: Input<'a>, | ||||
|  | ||||
|     // RTC-capable GPIO used as external wake source (store the raw peripheral) | ||||
|     pub wake_gpio1: esp_hal::peripherals::GPIO1<'static>, | ||||
|  | ||||
|     pub ota: Ota<'static, FlashStorage>, | ||||
|     pub ota_next: &'static mut FlashRegion<'static, FlashStorage>, | ||||
| } | ||||
| @@ -409,14 +412,12 @@ impl Esp<'_> { | ||||
|         spawner.spawn(run_dhcp(stack.clone(), gw_ip_addr_str)).ok(); | ||||
|  | ||||
|         loop { | ||||
|             println!("waiting for wifi ap link up"); | ||||
|             if stack.is_link_up() { | ||||
|                 break; | ||||
|             } | ||||
|             Timer::after(Duration::from_millis(500)).await; | ||||
|         } | ||||
|         while !stack.is_config_up() { | ||||
|             println!("waiting for wifi ap config up"); | ||||
|             Timer::after(Duration::from_millis(100)).await | ||||
|         } | ||||
|         println!("Connect to the AP `${ssid}` and point your browser to http://{gw_ip_addr_str}/"); | ||||
| @@ -481,92 +482,79 @@ impl Esp<'_> { | ||||
|         spawner.spawn(net_task(runner)).ok(); | ||||
|         self.controller.lock().await.start_async().await?; | ||||
|  | ||||
|         let timeout = TIME_ACCESS.get().await.current_time_us() + max_wait as u64 * 1000; | ||||
|         let timeout = { | ||||
|             let guard = TIME_ACCESS.get().await.lock().await; | ||||
|             guard.current_time_us() | ||||
|         } + max_wait as u64 * 1000; | ||||
|         loop { | ||||
|             let state = esp_wifi::wifi::sta_state(); | ||||
|             println!("waiting wifi sta ready  {:?}", state); | ||||
|             match state { | ||||
|                 WifiState::StaStarted => { | ||||
|                     self.controller.lock().await.connect()?; | ||||
|                     break; | ||||
|                 } | ||||
|                 WifiState::StaConnected => {} | ||||
|                 WifiState::StaDisconnected => {} | ||||
|                 WifiState::StaStopped => {} | ||||
|                 WifiState::ApStarted => {} | ||||
|                 WifiState::ApStopped => {} | ||||
|                 WifiState::Invalid => {} | ||||
|                 _ => {} | ||||
|             } | ||||
|             if TIME_ACCESS.get().await.current_time_us() > timeout { | ||||
|             if { let guard = TIME_ACCESS.get().await.lock().await; guard.current_time_us() } > timeout { | ||||
|                 bail!("Timeout waiting for wifi sta ready") | ||||
|             } | ||||
|             Timer::after(Duration::from_millis(500)).await; | ||||
|         } | ||||
|         let timeout = TIME_ACCESS.get().await.current_time_us() + max_wait as u64 * 1000; | ||||
|         let timeout = { let guard = TIME_ACCESS.get().await.lock().await; guard.current_time_us() } + max_wait as u64 * 1000; | ||||
|         loop { | ||||
|             let state = esp_wifi::wifi::sta_state(); | ||||
|             println!("waiting wifi sta connected  {:?}", state); | ||||
|             match state { | ||||
|                 WifiState::StaStarted => {} | ||||
|                 WifiState::StaConnected => { | ||||
|                     break; | ||||
|                 } | ||||
|                 WifiState::StaDisconnected => {} | ||||
|                 WifiState::StaStopped => {} | ||||
|                 WifiState::ApStarted => {} | ||||
|                 WifiState::ApStopped => {} | ||||
|                 WifiState::Invalid => {} | ||||
|                 _ => {} | ||||
|             } | ||||
|             if TIME_ACCESS.get().await.current_time_us() > timeout { | ||||
|             if { let guard = TIME_ACCESS.get().await.lock().await; guard.current_time_us() } > timeout { | ||||
|                 bail!("Timeout waiting for wifi sta connected") | ||||
|             } | ||||
|             Timer::after(Duration::from_millis(500)).await; | ||||
|         } | ||||
|         let timeout = TIME_ACCESS.get().await.current_time_us() + max_wait as u64 * 1000; | ||||
|         let timeout = { let guard = TIME_ACCESS.get().await.lock().await; guard.current_time_us() } + max_wait as u64 * 1000; | ||||
|         while !stack.is_link_up() { | ||||
|             if TIME_ACCESS.get().await.current_time_us() > timeout { | ||||
|             if { let guard = TIME_ACCESS.get().await.lock().await; guard.current_time_us() } > timeout { | ||||
|                 bail!("Timeout waiting for wifi link up") | ||||
|             } | ||||
|             println!("waiting for wifi link up"); | ||||
|             Timer::after(Duration::from_millis(500)).await; | ||||
|         } | ||||
|         let timeout = TIME_ACCESS.get().await.current_time_us() + max_wait as u64 * 1000; | ||||
|         let timeout = { let guard = TIME_ACCESS.get().await.lock().await; guard.current_time_us() } + max_wait as u64 * 1000; | ||||
|         while !stack.is_config_up() { | ||||
|             if TIME_ACCESS.get().await.current_time_us() > timeout { | ||||
|             if { let guard = TIME_ACCESS.get().await.lock().await; guard.current_time_us() } > timeout { | ||||
|                 bail!("Timeout waiting for wifi config up") | ||||
|             } | ||||
|             println!("waiting for wifi config up"); | ||||
|             Timer::after(Duration::from_millis(100)).await | ||||
|         } | ||||
|         Ok(stack.clone()) | ||||
|     } | ||||
|  | ||||
|     pub async fn deep_sleep(&mut self, duration_in_ms: u64) -> ! { | ||||
|         RtcSleepConfig::deep(); | ||||
|     pub fn deep_sleep(&mut self, duration_in_ms: u64, mut rtc: MutexGuard<CriticalSectionRawMutex, Rtc>) -> ! { | ||||
|         // Configure and enter deep sleep using esp-hal. Also keep prior behavior where | ||||
|         // duration_in_ms == 0 triggers an immediate reset. | ||||
|  | ||||
|         let cur = self.ota.current_ota_state().unwrap(); | ||||
|         //we made it till here, so fine | ||||
|         if cur == OtaImageState::PendingVerify { | ||||
|             self.ota | ||||
|                 .set_current_ota_state(OtaImageState::Valid) | ||||
|                 .expect("Could not set image to valid"); | ||||
|         // Mark the current OTA image as valid if we reached here while in pending verify. | ||||
|         if let Ok(cur) = self.ota.current_ota_state() { | ||||
|             if cur == OtaImageState::PendingVerify { | ||||
|                 self.ota | ||||
|                     .set_current_ota_state(OtaImageState::Valid) | ||||
|                     .expect("Could not set image to valid"); | ||||
|             } | ||||
|         } | ||||
|         //unsafe { | ||||
|         // //allow early wakeup by pressing the boot button | ||||
|  | ||||
|         if duration_in_ms == 0 { | ||||
|             software_reset(); | ||||
|         } else { | ||||
|             loop { | ||||
|                 info!("todo deepsleep") | ||||
|             } | ||||
|             //     //configure gpio 1 to wakeup on low, reused boot button for this | ||||
|             //     esp_sleep_enable_ext1_wakeup( | ||||
|             //         0b10u64, | ||||
|             //         esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW, | ||||
|             //     ); | ||||
|             //     esp_deep_sleep(duration_in_ms); | ||||
|             let timer = TimerWakeupSource::new(core::time::Duration::from_millis(duration_in_ms)); | ||||
|             let mut wake_pins: [(&mut dyn RtcPinWithResistors, WakeupLevel); 1] = [(&mut self.wake_gpio1, WakeupLevel::Low)]; | ||||
|             let ext1 = esp_hal::rtc_cntl::sleep::Ext1WakeupSource::new(&mut wake_pins); | ||||
|             rtc.sleep_deep(&[&timer, &ext1]); | ||||
|         } | ||||
|         //}; | ||||
|  | ||||
|         // We should never reach here because sleep_deep never returns, but just in case, reset. | ||||
|         software_reset(); | ||||
|     } | ||||
|  | ||||
|     pub(crate) async fn load_config(&mut self) -> FatResult<PlantControllerConfig> { | ||||
|   | ||||
| @@ -2,7 +2,7 @@ use crate::alloc::boxed::Box; | ||||
| use crate::hal::esp::Esp; | ||||
| use crate::hal::rtc::{BackupHeader, RTCModuleInteraction}; | ||||
| use crate::hal::water::TankSensor; | ||||
| use crate::hal::{BoardInteraction, FreePeripherals, Sensor}; | ||||
| use crate::hal::{BoardInteraction, FreePeripherals, Sensor, TIME_ACCESS}; | ||||
| use crate::fat_error::{FatError, FatResult}; | ||||
| use crate::{ | ||||
|     bail, | ||||
| @@ -95,7 +95,8 @@ impl<'a> BoardInteraction<'a> for Initial<'a> { | ||||
|     } | ||||
|  | ||||
|     async fn deep_sleep(&mut self, duration_in_ms: u64) -> ! { | ||||
|         self.esp.deep_sleep(duration_in_ms).await; | ||||
|         let rtc = TIME_ACCESS.get().await.lock().await; | ||||
|         self.esp.deep_sleep(duration_in_ms, rtc); | ||||
|     } | ||||
|     fn is_day(&self) -> bool { | ||||
|         false | ||||
|   | ||||
| @@ -13,7 +13,6 @@ use esp_hal::peripherals::Peripherals; | ||||
| use esp_hal::peripherals::ADC1; | ||||
| use esp_hal::peripherals::APB_SARADC; | ||||
| use esp_hal::peripherals::GPIO0; | ||||
| use esp_hal::peripherals::GPIO1; | ||||
| use esp_hal::peripherals::GPIO10; | ||||
| use esp_hal::peripherals::GPIO11; | ||||
| use esp_hal::peripherals::GPIO12; | ||||
| @@ -106,7 +105,7 @@ use littlefs2::fs::{Allocation, Filesystem as lfs2Filesystem}; | ||||
| use littlefs2::object_safe::DynStorage; | ||||
| use log::{info, warn}; | ||||
|  | ||||
| pub static TIME_ACCESS: OnceLock<Rtc> = OnceLock::new(); | ||||
| pub static TIME_ACCESS: OnceLock<Mutex<CriticalSectionRawMutex, Rtc>> = OnceLock::new(); | ||||
|  | ||||
| //Only support for 8 right now! | ||||
| pub const PLANT_COUNT: usize = 8; | ||||
| @@ -175,7 +174,6 @@ pub trait BoardInteraction<'a> { | ||||
| #[allow(dead_code)] | ||||
| pub struct FreePeripherals<'a> { | ||||
|     pub gpio0: GPIO0<'a>, | ||||
|     pub gpio1: GPIO1<'a>, | ||||
|     pub gpio2: GPIO2<'a>, | ||||
|     pub gpio3: GPIO3<'a>, | ||||
|     pub gpio4: GPIO4<'a>, | ||||
| @@ -232,7 +230,7 @@ impl PlantHal { | ||||
|         esp_alloc::heap_allocator!(#[link_section = ".dram2_uninit"] size: 64000); | ||||
|  | ||||
|         let rtc: Rtc = Rtc::new(peripherals.LPWR); | ||||
|         TIME_ACCESS.init(rtc).map_err(|_| FatError::String { | ||||
|         TIME_ACCESS.init(Mutex::new(rtc)).map_err(|_| FatError::String { | ||||
|             error: "Init error rct".to_string(), | ||||
|         })?; | ||||
|  | ||||
| @@ -243,6 +241,9 @@ impl PlantHal { | ||||
|             InputConfig::default().with_pull(Pull::None), | ||||
|         ); | ||||
|  | ||||
|         // Reserve GPIO1 for deep sleep wake (configured just before entering sleep) | ||||
|         let wake_gpio1 = peripherals.GPIO1; | ||||
|  | ||||
|         let rng = Rng::new(peripherals.RNG); | ||||
|         let timg0 = TimerGroup::new(peripherals.TIMG0); | ||||
|         let esp_wifi_ctrl = &*mk_static!( | ||||
| @@ -267,7 +268,6 @@ impl PlantHal { | ||||
|             //     pcnt0: peripherals.pcnt0, | ||||
|             //     pcnt1: peripherals.pcnt1, | ||||
|             gpio0: peripherals.GPIO0, | ||||
|             gpio1: peripherals.GPIO1, | ||||
|             gpio2: peripherals.GPIO2, | ||||
|             gpio3: peripherals.GPIO3, | ||||
|             gpio4: peripherals.GPIO4, | ||||
| @@ -390,6 +390,7 @@ impl PlantHal { | ||||
|             interface_sta : Some(sta), | ||||
|             interface_ap : Some(ap), | ||||
|             boot_button, | ||||
|             wake_gpio1, | ||||
|             mqtt_client: None, | ||||
|             ota, | ||||
|             ota_next, | ||||
| @@ -402,25 +403,25 @@ impl PlantHal { | ||||
|             None => "unknown", | ||||
|             Some(reason) => match reason { | ||||
|                 SocResetReason::ChipPowerOn => "power on", | ||||
|                 SocResetReason::CoreSw => "software reset", | ||||
|                 SocResetReason::CoreDeepSleep => "deep sleep", | ||||
|                 SocResetReason::CoreSDIO => "sdio reset", | ||||
|                 SocResetReason::CoreMwdt0 => "Watchdog Main", | ||||
|                 SocResetReason::CoreMwdt1 => "Watchdog 1", | ||||
|                 SocResetReason::CoreRtcWdt => "Watchdog RTC", | ||||
|                 SocResetReason::Cpu0Mwdt0 => "Watchdog MCpu0", | ||||
|                 SocResetReason::Cpu0Sw => "software reset cpu0", | ||||
|                 SocResetReason::SysRtcWdt => "Watchdog Sys rtc", | ||||
|                 SocResetReason::Cpu0Mwdt1 => "cpu0 mwdt1", | ||||
|                 SocResetReason::SysSuperWdt => "Watchdog Super", | ||||
|                 SocResetReason::Cpu0RtcWdt => { | ||||
|                     init_rtc_store = true; | ||||
|                     "Watchdog RTC cpu0" | ||||
|                 } | ||||
|                 SocResetReason::CoreSw => "software reset", | ||||
|                 SocResetReason::CoreDeepSleep => "deep sleep", | ||||
|                 SocResetReason::SysBrownOut => "sys brown out", | ||||
|                 SocResetReason::SysRtcWdt => "Watchdog Sys rtc", | ||||
|                 SocResetReason::Cpu0Mwdt1 => "cpu0 mwdt1", | ||||
|                 SocResetReason::SysSuperWdt => "Watchdog Super", | ||||
|                 SocResetReason::CoreEfuseCrc => "core efuse crc", | ||||
|                 SocResetReason::CoreUsbUart => { | ||||
|                     to_config_mode = true; | ||||
|                     //TODO still required? or via button ignore? to_config_mode = true; | ||||
|                     "core usb uart" | ||||
|                 } | ||||
|                 SocResetReason::CoreUsbJtag => "core usb jtag", | ||||
| @@ -577,14 +578,15 @@ impl PlantHal { | ||||
| } | ||||
|  | ||||
| pub async fn esp_time() -> DateTime<Utc> { | ||||
|     DateTime::from_timestamp_micros(TIME_ACCESS.get().await.current_time_us() as i64).unwrap() | ||||
|     let guard = TIME_ACCESS.get().await.lock().await; | ||||
|     DateTime::from_timestamp_micros(guard.current_time_us() as i64).unwrap() | ||||
| } | ||||
|  | ||||
| pub async fn esp_set_time(time: DateTime<FixedOffset>) { | ||||
|     TIME_ACCESS | ||||
|         .get() | ||||
|         .await | ||||
|         .set_current_time_us(time.timestamp_micros() as u64); | ||||
|     { | ||||
|         let mut guard = TIME_ACCESS.get().await.lock().await; | ||||
|         guard.set_current_time_us(time.timestamp_micros() as u64); | ||||
|     } | ||||
|     BOARD_ACCESS | ||||
|         .get() | ||||
|         .await | ||||
|   | ||||
| @@ -3,7 +3,7 @@ use crate::hal::battery::BatteryInteraction; | ||||
| use crate::hal::esp::Esp; | ||||
| use crate::hal::rtc::RTCModuleInteraction; | ||||
| use crate::hal::water::TankSensor; | ||||
| use crate::hal::{BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT}; | ||||
| use crate::hal::{BoardInteraction, FreePeripherals, Sensor, I2C_DRIVER, PLANT_COUNT, TIME_ACCESS}; | ||||
| use alloc::boxed::Box; | ||||
| use alloc::string::ToString; | ||||
| use async_trait::async_trait; | ||||
| @@ -347,7 +347,8 @@ impl<'a> BoardInteraction<'a> for V4<'a> { | ||||
|     async fn deep_sleep(&mut self, duration_in_ms: u64) -> ! { | ||||
|         self.awake.set_low(); | ||||
|         //self.charger.power_save(); | ||||
|         self.esp.deep_sleep(duration_in_ms).await; | ||||
|         let rtc = TIME_ACCESS.get().await.lock().await; | ||||
|         self.esp.deep_sleep(duration_in_ms, rtc); | ||||
|     } | ||||
|  | ||||
|     fn is_day(&self) -> bool { | ||||
|   | ||||
| @@ -111,7 +111,7 @@ impl LogArray { | ||||
|         limit_length(txt_short, &mut txt_short_stack); | ||||
|         limit_length(txt_long, &mut txt_long_stack); | ||||
|  | ||||
|         let time = TIME_ACCESS.get().await.current_time_us() / 1000; | ||||
|         let time = { let guard = TIME_ACCESS.get().await.lock().await; guard.current_time_us() } / 1000; | ||||
|  | ||||
|         let ordinal = message_key.ordinal() as u16; | ||||
|         let template: &str = message_key.into(); | ||||
|   | ||||
| @@ -166,10 +166,10 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|     board.board_hal.general_fault(false).await; | ||||
|     let cur = match board.board_hal.get_rtc_module().get_rtc_time().await { | ||||
|         Ok(value) => { | ||||
|             TIME_ACCESS | ||||
|                 .get() | ||||
|                 .await | ||||
|                 .set_current_time_us(value.timestamp_micros() as u64); | ||||
|             { | ||||
|                 let mut guard = TIME_ACCESS.get().await.lock().await; | ||||
|                 guard.set_current_time_us(value.timestamp_micros() as u64); | ||||
|             } | ||||
|             value | ||||
|         } | ||||
|         Err(err) => { | ||||
| @@ -396,10 +396,10 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|             _water_frozen = true; | ||||
|         } | ||||
|     } | ||||
|     info!("Water temp is {}", water_temp.unwrap_or(0.)); | ||||
|  | ||||
|     //publish_tank_state(&tank_state, &water_temp).await; | ||||
|  | ||||
|     board = BOARD_ACCESS.get().await.lock().await; | ||||
|     let _plantstate: [PlantState; PLANT_COUNT] = [ | ||||
|         PlantState::read_hardware_state(0, &mut board).await, | ||||
|         PlantState::read_hardware_state(1, &mut board).await, | ||||
| @@ -410,6 +410,8 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|         PlantState::read_hardware_state(6, &mut board).await, | ||||
|         PlantState::read_hardware_state(7, &mut board).await, | ||||
|     ]; | ||||
|  | ||||
|  | ||||
|     //publish_plant_states(&timezone_time.clone(), &plantstate).await; | ||||
|  | ||||
|     // let pump_required = plantstate | ||||
| @@ -481,6 +483,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|     //     } | ||||
|     // } | ||||
|  | ||||
|     info!("state of charg"); | ||||
|     let is_day = board.board_hal.is_day(); | ||||
|     let state_of_charge = board | ||||
|         .board_hal | ||||
| @@ -497,10 +500,12 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|         .await | ||||
|         .unwrap_or(BatteryState::Unknown); | ||||
|  | ||||
|     info!("Battery state is {:?}", battery_state); | ||||
|     let mut light_state = LightState { | ||||
|         enabled: board.board_hal.get_config().night_lamp.enabled, | ||||
|         ..Default::default() | ||||
|     }; | ||||
|     info!("Light state is {:?}", light_state); | ||||
|     if light_state.enabled { | ||||
|         light_state.is_day = is_day; | ||||
|         light_state.out_of_work_hour = !in_time_range( | ||||
| @@ -603,6 +608,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> { | ||||
|         .mqtt_publish("/state", "sleep".as_bytes()) | ||||
|         .await; | ||||
|  | ||||
|     info!("Go to sleep for {} minutes", deep_sleep_duration_minutes); | ||||
|     //determine next event | ||||
|     //is light out of work trigger soon? | ||||
|     //is battery low ?? | ||||
|   | ||||
| @@ -374,6 +374,12 @@ impl Handler for HttpHandler { | ||||
|                             self.reboot_now.store(true, Ordering::Relaxed); | ||||
|                             Some(Ok(None)) | ||||
|                         } | ||||
|                         "/exit" => { | ||||
|                             let mut board = BOARD_ACCESS.get().await.lock().await; | ||||
|                             board.board_hal.get_esp().set_restart_to_conf(false); | ||||
|                             self.reboot_now.store(true, Ordering::Relaxed); | ||||
|                             Some(Ok(None)) | ||||
|                         } | ||||
|                         _ => None, | ||||
|                     }; | ||||
|                     match json { | ||||
| @@ -850,13 +856,6 @@ pub async fn httpd(reboot_now: Arc<AtomicBool>, stack: Stack<'static>) { | ||||
|     // | ||||
|     // unsafe { vTaskDelay(1) }; | ||||
|     // | ||||
|     // let reboot_now_for_exit = reboot_now.clone(); | ||||
|     // server | ||||
|     //     .fn_handler("/exit", Method::Post, move |_| { | ||||
|     //         reboot_now_for_exit.store(true, std::sync::atomic::Ordering::Relaxed); | ||||
|     //         anyhow::Ok(()) | ||||
|     //     }) | ||||
|     //     .unwrap(); | ||||
|     // server | ||||
| } | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user