enable deepsleep and gpio1 + timer wake

This commit is contained in:
2025-09-26 22:07:48 +02:00
parent 7fc8d0c882
commit 76f59b093d
7 changed files with 79 additions and 82 deletions

View File

@@ -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> {

View File

@@ -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

View File

@@ -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

View File

@@ -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 {

View File

@@ -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();

View File

@@ -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 ??

View File

@@ -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
}