startup and ota state detection working, now wifi_ap next

This commit is contained in:
2025-09-13 20:56:11 +02:00
parent 4160202cdc
commit be3c4a5095
9 changed files with 294 additions and 225 deletions

View File

@@ -1,23 +1,20 @@
use alloc::string::String;
use crate::hal::Box;
use anyhow::anyhow;
use alloc::string::String;
use async_trait::async_trait;
use bq34z100::{Bq34Z100Error, Bq34z100g1Driver};
use measurements::Temperature;
use serde::Serialize;
#[async_trait]
pub trait BatteryInteraction {
async fn state_charge_percent(& mut self) -> Result<f32, BatteryError>;
async fn remaining_milli_ampere_hour(& mut self) -> Result<u16, BatteryError>;
async fn max_milli_ampere_hour(& mut self) -> Result<u16, BatteryError>;
async fn design_milli_ampere_hour(& mut self) -> Result<u16, BatteryError>;
async fn voltage_milli_volt(& mut self) -> Result<u16, BatteryError>;
async fn average_current_milli_ampere(& mut self) -> Result<i16, BatteryError>;
async fn cycle_count(& mut self) -> Result<u16, BatteryError>;
async fn state_health_percent(& mut self) -> Result<u16, BatteryError>;
async fn bat_temperature(& mut self) -> Result<u16, BatteryError>;
async fn get_battery_state(& mut self) -> Result<BatteryState, BatteryError>;
async fn state_charge_percent(&mut self) -> Result<f32, BatteryError>;
async fn remaining_milli_ampere_hour(&mut self) -> Result<u16, BatteryError>;
async fn max_milli_ampere_hour(&mut self) -> Result<u16, BatteryError>;
async fn design_milli_ampere_hour(&mut self) -> Result<u16, BatteryError>;
async fn voltage_milli_volt(&mut self) -> Result<u16, BatteryError>;
async fn average_current_milli_ampere(&mut self) -> Result<i16, BatteryError>;
async fn cycle_count(&mut self) -> Result<u16, BatteryError>;
async fn state_health_percent(&mut self) -> Result<u16, BatteryError>;
async fn bat_temperature(&mut self) -> Result<u16, BatteryError>;
async fn get_battery_state(&mut self) -> Result<BatteryState, BatteryError>;
}
#[derive(Debug, Serialize)]
@@ -56,31 +53,31 @@ pub enum BatteryState {
pub struct NoBatteryMonitor {}
#[async_trait]
impl BatteryInteraction for NoBatteryMonitor {
async fn state_charge_percent(& mut self) -> Result<f32, BatteryError> {
async fn state_charge_percent(&mut self) -> Result<f32, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
}
async fn remaining_milli_ampere_hour(& mut self) -> Result<u16, BatteryError> {
async fn remaining_milli_ampere_hour(&mut self) -> Result<u16, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
}
async fn max_milli_ampere_hour(& mut self) -> Result<u16, BatteryError> {
async fn max_milli_ampere_hour(&mut self) -> Result<u16, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
}
async fn design_milli_ampere_hour(& mut self) -> Result<u16, BatteryError> {
async fn design_milli_ampere_hour(&mut self) -> Result<u16, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
}
async fn voltage_milli_volt(& mut self) -> Result<u16, BatteryError> {
async fn voltage_milli_volt(&mut self) -> Result<u16, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
}
async fn average_current_milli_ampere(& mut self) -> Result<i16, BatteryError> {
async fn average_current_milli_ampere(&mut self) -> Result<i16, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
}
async fn cycle_count(& mut self) -> Result<u16, BatteryError> {
async fn cycle_count(&mut self) -> Result<u16, BatteryError> {
Err(BatteryError::NoBatteryMonitor)
}
@@ -92,7 +89,7 @@ impl BatteryInteraction for NoBatteryMonitor {
Err(BatteryError::NoBatteryMonitor)
}
async fn get_battery_state(& mut self) -> Result<BatteryState, BatteryError> {
async fn get_battery_state(&mut self) -> Result<BatteryState, BatteryError> {
Ok(BatteryState::Unknown)
}
}

View File

@@ -6,14 +6,14 @@ use anyhow::{anyhow, bail, Context};
use chrono::{DateTime, Utc};
use serde::Serialize;
use alloc::{
string::{String, ToString},
vec::Vec,
};
use alloc::{string::String, vec::Vec};
use core::marker::PhantomData;
use core::net::IpAddr;
use core::str::FromStr;
use embassy_time::{Instant, Timer};
use embassy_time::Instant;
use esp_bootloader_esp_idf::ota::OtaImageState;
use esp_hal::gpio::Input;
use esp_storage::FlashStorage;
#[link_section = ".rtc.data"]
static mut LAST_WATERING_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT];
@@ -51,11 +51,16 @@ pub struct MqttClient<'a> {
base_topic: heapless::String<64>,
}
pub struct Esp<'a> {
pub boot_button: Input<'a>,
pub(crate) mqtt_client: Option<MqttClient<'a>>,
pub(crate) dummy: PhantomData<&'a ()>,
pub(crate) wall_clock_offset: u64
pub(crate) wall_clock_offset: u64,
//pub(crate) wifi_driver: EspWifi<'a>,
//pub(crate) boot_button: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, esp_idf_hal::gpio::Input>,
pub storage: FlashStorage,
pub slot: usize,
pub next_slot: usize,
pub ota_state: OtaImageState,
}
pub struct IpInfo {
@@ -72,10 +77,9 @@ impl Esp<'_> {
const BASE_PATH: &'static str = "/spiffs";
pub(crate) fn mode_override_pressed(&mut self) -> bool {
todo!();
//self.boot_button.get_level() == Level::Low
self.boot_button.is_low()
}
pub(crate) async fn sntp(&mut self, max_wait_ms: u32) -> anyhow::Result<DateTime<Utc>> {
pub(crate) async fn sntp(&mut self, _max_wait_ms: u32) -> anyhow::Result<DateTime<Utc>> {
//let sntp = sntp::EspSntp::new_default()?;
//let mut counter = 0;
//while sntp.get_sync_status() != SyncStatus::Completed {
@@ -147,10 +151,11 @@ impl Esp<'_> {
}
pub(crate) async fn wifi_ap(&mut self) -> anyhow::Result<()> {
let ssid = match self.load_config() {
let _ssid = match self.load_config() {
Ok(config) => config.network.ap_ssid.clone(),
Err(_) => heapless::String::from_str("PlantCtrl Emergency Mode").unwrap(),
};
todo!("todo");
//
// let apconfig = AccessPointConfiguration {
@@ -165,15 +170,13 @@ impl Esp<'_> {
// anyhow::Ok(())
}
pub(crate) async fn wifi(&mut self, network_config: &NetworkConfig) -> anyhow::Result<IpInfo> {
let ssid = network_config
let _ssid = network_config
.ssid
.clone()
.ok_or(anyhow!("No ssid configured"))?;
let password = network_config.password.clone();
let max_wait = network_config.max_wait;
let _password = network_config.password.clone();
let _max_wait = network_config.max_wait;
bail!("todo")
// match password {
// Some(pw) => {
@@ -237,7 +240,7 @@ impl Esp<'_> {
}
pub(crate) async fn save_config(
&mut self,
config: &PlantControllerConfig,
_config: &PlantControllerConfig,
) -> anyhow::Result<()> {
bail!("todo");
// let mut cfg = File::create(Self::CONFIG_FILE)?;
@@ -247,9 +250,9 @@ impl Esp<'_> {
}
pub(crate) fn mount_file_system(&mut self) -> anyhow::Result<()> {
bail!("fail");
log(LogMessage::MountingFilesystem, 0, 0, "", "");
let base_path = String::try_from("/spiffs")?;
let storage = String::try_from(Self::SPIFFS_PARTITION_NAME)?;
// log(LogMessage::MountingFilesystem, 0, 0, "", "");
// let base_path = String::try_from("/spiffs")?;
// let storage = String::try_from(Self::SPIFFS_PARTITION_NAME)?;
//let conf = todo!();
//let conf = esp_idf_sys::esp_vfs_spiffs_conf_t {
@@ -272,7 +275,7 @@ impl Esp<'_> {
// &free_space.used_size.to_string(),
// "",
// );
anyhow::Ok(())
// anyhow::Ok(())
}
async fn file_system_size(&mut self) -> anyhow::Result<FileSystemSizeInfo> {
bail!("fail");
@@ -348,7 +351,7 @@ impl Esp<'_> {
// iter_error,
// }
}
pub(crate) async fn delete_file(&self, filename: &str) -> anyhow::Result<()> {
pub(crate) async fn delete_file(&self, _filename: &str) -> anyhow::Result<()> {
bail!("todo");
// let filepath = Path::new(Self::BASE_PATH).join(Path::new(filename));
// match fs::remove_file(filepath) {
@@ -425,7 +428,7 @@ impl Esp<'_> {
if base_topic.is_empty() {
bail!("Mqtt base_topic was empty")
}
let base_topic_copy = base_topic.clone();
let _base_topic_copy = base_topic.clone();
let mqtt_url = network_config
.mqtt_url
.as_ref()
@@ -583,8 +586,8 @@ impl Esp<'_> {
}
pub(crate) async fn mqtt_publish(
&mut self,
subtopic: &str,
message: &[u8],
_subtopic: &str,
_message: &[u8],
) -> anyhow::Result<()> {
bail!("todo");
//

View File

@@ -1,7 +1,8 @@
use alloc::vec::Vec;
use crate::hal::esp::Esp;
use crate::hal::rtc::{BackupHeader, RTCModuleInteraction};
use alloc::vec::Vec;
//use crate::hal::water::TankSensor;
use crate::alloc::boxed::Box;
use crate::hal::{deep_sleep, BoardInteraction, FreePeripherals, Sensor};
use crate::{
config::PlantControllerConfig,
@@ -10,9 +11,7 @@ use crate::{
use anyhow::{bail, Result};
use async_trait::async_trait;
use chrono::{DateTime, Utc};
use embedded_hal::digital::OutputPin;
use measurements::{Current, Voltage};
use crate::alloc::boxed::Box;
pub struct Initial<'a> {
//pub(crate) general_fault: PinDriver<'a, esp_idf_hal::gpio::AnyIOPin, InputOutput>,
@@ -49,7 +48,7 @@ impl RTCModuleInteraction for NoRTC {
pub(crate) fn create_initial_board(
//free_pins: FreePeripherals,
fs_mount_error: bool,
_fs_mount_error: bool,
config: PlantControllerConfig,
esp: Esp<'static>,
) -> Result<Box<dyn BoardInteraction<'static> + Send>> {
@@ -123,7 +122,7 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
bail!("Please configure board revision")
}
async fn general_fault(&mut self, enable: bool) {
async fn general_fault(&mut self, _enable: bool) {
//let _ = self.general_fault.set_state(enable.into());
}
@@ -134,7 +133,7 @@ impl<'a> BoardInteraction<'a> for Initial<'a> {
async fn set_config(&mut self, config: PlantControllerConfig) -> anyhow::Result<()> {
self.config = config;
//TODO
// self.esp.save_config(&self.config)?;
// self.esp.save_config(&self.config)?;
anyhow::Ok(())
}

View File

@@ -5,7 +5,7 @@ mod rtc;
//mod water;
use crate::alloc::string::ToString;
use crate::hal::rtc::{RTCModuleInteraction};
use crate::hal::rtc::RTCModuleInteraction;
//use crate::hal::water::TankSensor;
use crate::{
config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig},
@@ -17,20 +17,24 @@ use crate::{
};
use alloc::boxed::Box;
use alloc::format;
use core::marker::PhantomData;
use anyhow::{Ok, Result};
use async_trait::async_trait;
use core::marker::PhantomData;
use embassy_executor::Spawner;
//use battery::BQ34Z100G1;
use bq34z100::Bq34z100g1Driver;
use ds323x::{DateTimeAccess, Ds323x};
use eeprom24x::{Eeprom24x, SlaveAddr, Storage};
use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex};
//use bq34z100::Bq34z100g1Driver;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::mutex::Mutex;
use embassy_sync::lazy_lock::LazyLock;
use esp_bootloader_esp_idf::partitions::DataPartitionSubType;
use esp_hal::clock::CpuClock;
use esp_hal::gpio::{Input, InputConfig, Pull};
use esp_hal::timer::systimer::SystemTimer;
use esp_println::println;
use measurements::{Current, Voltage};
use esp_alloc as _;
use esp_backtrace as _;
//Only support for 8 right now!
pub const PLANT_COUNT: usize = 8;
@@ -38,24 +42,34 @@ const TANK_MULTI_SAMPLE: usize = 11;
//pub static I2C_DRIVER: LazyLock<Mutex<CriticalSectionRawMutex,I2cDriver<'static>>> = LazyLock::new(PlantHal::create_i2c);
fn deep_sleep(duration_in_ms: u64) -> ! {
// When you are okay with using a nightly compiler it's better to use https://docs.rs/static_cell/2.1.0/static_cell/macro.make_static.html
macro_rules! mk_static {
($t:ty,$val:expr) => {{
static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new();
#[deny(unused_attributes)]
let x = STATIC_CELL.uninit().write(($val));
x
}};
}
fn deep_sleep(_duration_in_ms: u64) -> ! {
//unsafe {
// //if we don't do this here, we might just revert newly flashed firmware
// mark_app_valid();
// //allow early wakeup by pressing the boot button
// if duration_in_ms == 0 {
// esp_restart();
// } else {
// //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);
// }
loop {
todo!()
}
// //if we don't do this here, we might just revert newly flashed firmware
// mark_app_valid();
// //allow early wakeup by pressing the boot button
// if duration_in_ms == 0 {
// esp_restart();
// } else {
// //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);
// }
loop {
todo!()
}
//};
}
@@ -95,7 +109,6 @@ pub trait BoardInteraction<'a> {
async fn get_mptt_current(&mut self) -> anyhow::Result<Current>;
}
impl dyn BoardInteraction<'_> {
//the counter is just some arbitrary number that increases whenever some progress was made, try to keep the updates < 10 per second for ux reasons
async fn _progress(&mut self, counter: u32) {
@@ -163,17 +176,19 @@ impl PlantHal {
// Mutex::new(I2cDriver::new(i2c, sda, scl, &config).unwrap())
// }
pub fn create() -> Result<Mutex<CriticalSectionRawMutex, HAL<'static>>> {
pub fn create(spawner: Spawner) -> Result<Mutex<CriticalSectionRawMutex, HAL<'static>>> {
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
let peripherals = esp_hal::init(config);
esp_alloc::heap_allocator!(size: 64 * 1024);
let timer0 = SystemTimer::new(peripherals.SYSTIMER);
esp_hal_embassy::init(timer0.alarm0);
let systimer = SystemTimer::new(peripherals.SYSTIMER);
esp_hal_embassy::init(systimer.alarm0);
let boot_button = Input::new(
peripherals.GPIO9,
InputConfig::default().with_pull(Pull::None),
);
// let mut boot_button = PinDriver::input(peripherals.pins.gpio9.downgrade())?;
// boot_button.set_pull(Pull::Floating)?;
//
// let free_pins = FreePeripherals {
// can: peripherals.can,
@@ -210,13 +225,46 @@ impl PlantHal {
// gpio30: peripherals.pins.gpio30,
// };
//
let mut storage = esp_storage::FlashStorage::new();
let mut buffer = [0u8; esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN];
let pt =
esp_bootloader_esp_idf::partitions::read_partition_table(&mut storage, &mut buffer)?;
// List all partitions - this is just FYI
for i in 0..pt.len() {
println!("{:?}", pt.get_partition(i));
}
// Find the OTA-data partition and show the currently active partition
let ota_part = pt
.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::Data(
DataPartitionSubType::Ota,
))?
.unwrap();
let mut ota_part = ota_part.as_embedded_storage(&mut storage);
println!("Found ota data");
let mut ota = esp_bootloader_esp_idf::ota::Ota::new(&mut ota_part)?;
let current = ota.current_slot()?;
println!(
"current image state {:?} (only relevant if the bootloader was built with auto-rollback support)",
ota.current_ota_state()
);
println!("current {:?} - next {:?}", current, current.next());
let ota_state = ota.current_ota_state()?;
let mut esp = Esp {
mqtt_client: None,
boot_button,
mqtt_client: None,
storage,
slot: current.number(),
next_slot: current.next().number(),
ota_state,
dummy: PhantomData::default(),
wall_clock_offset : 0
// wifi_driver,
// boot_button
};
wall_clock_offset: 0, // wifi_driver,
// boot_button
};
//init,reset rtc memory depending on cause
let mut init_rtc_store: bool = false;