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