startup and ota state detection working, now wifi_ap next

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

View File

@ -4,6 +4,15 @@ name = "plant-ctrl2"
rust-version = "1.86" rust-version = "1.86"
version = "0.1.0" version = "0.1.0"
# Explicitly configure the binary target and disable building it as a test/bench.
[[bin]]
name = "plant-ctrl2"
path = "src/main.rs"
# Prevent IDEs/Cargo from trying to compile a test harness for this no_std binary.
test = false
bench = false
doc = false
[package.metadata.cargo_runner] [package.metadata.cargo_runner]
# The string `$TARGET_FILE` will be replaced with the path from cargo. # The string `$TARGET_FILE` will be replaced with the path from cargo.
command = [ command = [
@ -66,6 +75,8 @@ esp-backtrace = { version = "0.17.0", features = [
"exception-handler", "exception-handler",
"panic-handler", "panic-handler",
"println", "println",
"colors",
"custom-halt"
] } ] }
esp-println = { version = "0.15.0", features = ["esp32c6", "log-04"] } esp-println = { version = "0.15.0", features = ["esp32c6", "log-04"] }
# for more networking protocol support see https://crates.io/crates/edge-net # for more networking protocol support see https://crates.io/crates/edge-net
@ -75,14 +86,16 @@ embassy-executor = { version = "0.7.0", features = [
] } ] }
embassy-time = { version = "0.4.0", features = ["log"] } embassy-time = { version = "0.4.0", features = ["log"] }
esp-hal-embassy = { version = "0.9.0", features = ["esp32c6", "log-04"] } esp-hal-embassy = { version = "0.9.0", features = ["esp32c6", "log-04"] }
#esp-wifi = { version = "0.15.0", features = [ esp-storage = { version = "0.7.0", features = ["esp32c6"] }
# "builtin-scheduler",
# "esp-alloc", esp-wifi = { version = "0.15.0", features = [
# "esp32c6", "builtin-scheduler",
# "log-04", "esp-alloc",
# "smoltcp", "esp32c6",
# "wifi", "log-04",
#] } "smoltcp",
"wifi",
] }
#smoltcp = { version = "0.12.0", default-features = false, features = [ #smoltcp = { version = "0.12.0", default-features = false, features = [
# "log", # "log",
# "medium-ethernet", # "medium-ethernet",
@ -126,7 +139,6 @@ eeprom24x = "0.7.2"
crc = "3.2.1" crc = "3.2.1"
bincode = { version = "2.0.1", default-features = false, features = ["alloc", "serde"] } bincode = { version = "2.0.1", default-features = false, features = ["alloc", "serde"] }
ringbuffer = "0.15.0" ringbuffer = "0.15.0"
#text-template = "0.1.0"
strum_macros = "0.27.0" strum_macros = "0.27.0"
#esp-ota = { version = "0.2.2", features = ["log"] } #esp-ota = { version = "0.2.2", features = ["log"] }
unit-enum = "1.4.1" unit-enum = "1.4.1"
@ -139,6 +151,10 @@ portable-atomic = "1.11.1"
embassy-sync = { version = "0.7.2", features = ["log"] } embassy-sync = { version = "0.7.2", features = ["log"] }
async-trait = "0.1.89" async-trait = "0.1.89"
bq34z100 = { version = "0.4.0", default-features = false } bq34z100 = { version = "0.4.0", default-features = false }
edge-dhcp = "0.6.0"
edge-nal = "0.5.0"
edge-nal-embassy = "0.6.0"
static_cell = "2.1.1"
[patch.crates-io] [patch.crates-io]

View File

@ -50,13 +50,13 @@ fn linker_be_nice() {
} }
fn main() { fn main() {
//webpack(); webpack();
//linker_be_nice(); linker_be_nice();
let _ = EmitBuilder::builder().all_git().all_build().emit(); let _ = EmitBuilder::builder().all_git().all_build().emit();
} }
fn webpack() { fn webpack() {
println!("cargo:rerun-if-changed=./src/src_webpack"); //println!("cargo:rerun-if-changed=./src/src_webpack");
Command::new("rm") Command::new("rm")
.arg("./src/webserver/bundle.js") .arg("./src/webserver/bundle.js")
.output() .output()

View File

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

View File

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

View File

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

View File

@ -5,7 +5,7 @@ mod rtc;
//mod water; //mod water;
use crate::alloc::string::ToString; use crate::alloc::string::ToString;
use crate::hal::rtc::{RTCModuleInteraction}; use crate::hal::rtc::RTCModuleInteraction;
//use crate::hal::water::TankSensor; //use crate::hal::water::TankSensor;
use crate::{ use crate::{
config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig}, config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig},
@ -17,20 +17,24 @@ use crate::{
}; };
use alloc::boxed::Box; use alloc::boxed::Box;
use alloc::format; use alloc::format;
use core::marker::PhantomData;
use anyhow::{Ok, Result}; use anyhow::{Ok, Result};
use async_trait::async_trait; use async_trait::async_trait;
use core::marker::PhantomData;
use embassy_executor::Spawner;
//use battery::BQ34Z100G1; //use battery::BQ34Z100G1;
use bq34z100::Bq34z100g1Driver; //use bq34z100::Bq34z100g1Driver;
use ds323x::{DateTimeAccess, Ds323x}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use eeprom24x::{Eeprom24x, SlaveAddr, Storage};
use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex};
use embassy_sync::mutex::Mutex; 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::clock::CpuClock;
use esp_hal::gpio::{Input, InputConfig, Pull};
use esp_hal::timer::systimer::SystemTimer; use esp_hal::timer::systimer::SystemTimer;
use esp_println::println;
use measurements::{Current, Voltage}; use measurements::{Current, Voltage};
use esp_alloc as _;
use esp_backtrace as _;
//Only support for 8 right now! //Only support for 8 right now!
pub const PLANT_COUNT: usize = 8; pub const PLANT_COUNT: usize = 8;
@ -38,7 +42,17 @@ const TANK_MULTI_SAMPLE: usize = 11;
//pub static I2C_DRIVER: LazyLock<Mutex<CriticalSectionRawMutex,I2cDriver<'static>>> = LazyLock::new(PlantHal::create_i2c); //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 { //unsafe {
// //if we don't do this here, we might just revert newly flashed firmware // //if we don't do this here, we might just revert newly flashed firmware
// mark_app_valid(); // mark_app_valid();
@ -95,7 +109,6 @@ pub trait BoardInteraction<'a> {
async fn get_mptt_current(&mut self) -> anyhow::Result<Current>; async fn get_mptt_current(&mut self) -> anyhow::Result<Current>;
} }
impl dyn BoardInteraction<'_> { 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 //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) { async fn _progress(&mut self, counter: u32) {
@ -163,17 +176,19 @@ impl PlantHal {
// Mutex::new(I2cDriver::new(i2c, sda, scl, &config).unwrap()) // 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 config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
let peripherals = esp_hal::init(config); let peripherals = esp_hal::init(config);
esp_alloc::heap_allocator!(size: 64 * 1024); esp_alloc::heap_allocator!(size: 64 * 1024);
let timer0 = SystemTimer::new(peripherals.SYSTIMER); let systimer = SystemTimer::new(peripherals.SYSTIMER);
esp_hal_embassy::init(timer0.alarm0); 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 { // let free_pins = FreePeripherals {
// can: peripherals.can, // can: peripherals.can,
@ -210,11 +225,44 @@ impl PlantHal {
// gpio30: peripherals.pins.gpio30, // 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 { let mut esp = Esp {
boot_button,
mqtt_client: None, mqtt_client: None,
storage,
slot: current.number(),
next_slot: current.next().number(),
ota_state,
dummy: PhantomData::default(), dummy: PhantomData::default(),
wall_clock_offset : 0 wall_clock_offset: 0, // wifi_driver,
// wifi_driver,
// boot_button // boot_button
}; };

View File

@ -4,8 +4,10 @@ use alloc::vec::Vec;
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::lazy_lock::LazyLock; use embassy_sync::lazy_lock::LazyLock;
use embassy_sync::mutex::Mutex; use embassy_sync::mutex::Mutex;
use embassy_time::Instant;
use log::info;
use serde::Serialize; use serde::Serialize;
use strum_macros::{EnumIter, IntoStaticStr}; use strum_macros::IntoStaticStr;
use ringbuffer::{ConstGenericRingBuffer, RingBuffer}; use ringbuffer::{ConstGenericRingBuffer, RingBuffer};
use unit_enum::UnitEnum; use unit_enum::UnitEnum;
@ -19,8 +21,9 @@ const BUFFER_SIZE: usize = 220;
static mut BUFFER: ConstGenericRingBuffer<LogEntry, BUFFER_SIZE> = static mut BUFFER: ConstGenericRingBuffer<LogEntry, BUFFER_SIZE> =
ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE>::new(); ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE>::new();
#[allow(static_mut_refs)] #[allow(static_mut_refs)]
static BUFFER_ACCESS: LazyLock<Mutex<CriticalSectionRawMutex,&mut ConstGenericRingBuffer<LogEntry, BUFFER_SIZE>>> = static BUFFER_ACCESS: LazyLock<
LazyLock::new(|| unsafe { Mutex::new(&mut BUFFER) }); Mutex<CriticalSectionRawMutex, &mut ConstGenericRingBuffer<LogEntry, BUFFER_SIZE>>,
> = LazyLock::new(|| unsafe { Mutex::new(&mut BUFFER) });
#[derive(Serialize, Debug, Clone)] #[derive(Serialize, Debug, Clone)]
pub struct LogEntry { pub struct LogEntry {
@ -69,7 +72,13 @@ pub async fn get_log() -> Vec<LogEntry> {
read_copy read_copy
} }
pub async fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &str, txt_long: &str) { pub async fn log(
message_key: LogMessage,
number_a: u32,
number_b: u32,
txt_short: &str,
txt_long: &str,
) {
let mut txt_short_stack: heapless::String<TXT_SHORT_LENGTH> = heapless::String::new(); let mut txt_short_stack: heapless::String<TXT_SHORT_LENGTH> = heapless::String::new();
let mut txt_long_stack: heapless::String<TXT_LONG_LENGTH> = heapless::String::new(); let mut txt_long_stack: heapless::String<TXT_LONG_LENGTH> = heapless::String::new();
@ -77,27 +86,18 @@ pub async fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_shor
limit_length(txt_long, &mut txt_long_stack); limit_length(txt_long, &mut txt_long_stack);
//TODO //TODO
let time = 0; let time = Instant::now().as_secs();
// let time = EspSystemTime {}.now().as_millis() as u64; // let time = EspSystemTime {}.now().as_millis() as u64;
// //
let ordinal = message_key.ordinal() as u16; let ordinal = message_key.ordinal() as u16;
// let template_string: &str = message_key.into(); let template: &str = message_key.into();
// let mut template_string = template.to_string();
// let mut values: HashMap<&str, &str> = HashMap::new(); template_string = template_string.replace("${number_a}", number_a.to_string().as_str());
// let number_a_str = number_a.to_string(); template_string = template_string.replace("${number_b}", number_b.to_string().as_str());
// let number_b_str = number_b.to_string(); template_string = template_string.replace("${txt_long}", txt_long);
// template_string = template_string.replace("${txt_short}", txt_short);
// values.insert("number_a", &number_a_str);
// values.insert("number_b", &number_b_str); info!("LOG: {} : {}", time, template_string);
// values.insert("txt_short", txt_short);
// values.insert("txt_long", txt_long);
//
// let template = Template::from(template_string);
// let serial_entry = template.fill_in(&values);
//
// log::info!("{serial_entry}");
// //TODO push to mqtt?
let entry = LogEntry { let entry = LogEntry {
timestamp: time, timestamp: time,

View File

@ -10,6 +10,7 @@ esp_bootloader_esp_idf::esp_app_desc!();
use esp_backtrace as _; use esp_backtrace as _;
use crate::config::PlantConfig; use crate::config::PlantConfig;
use crate::tank::WATER_FROZEN_THRESH;
use crate::{ use crate::{
config::BoardVersion::INITIAL, config::BoardVersion::INITIAL,
hal::{PlantHal, HAL, PLANT_COUNT}, hal::{PlantHal, HAL, PLANT_COUNT},
@ -20,23 +21,36 @@ use alloc::borrow::ToOwned;
use alloc::string::{String, ToString}; use alloc::string::{String, ToString};
use alloc::sync::Arc; use alloc::sync::Arc;
use alloc::{format, vec}; use alloc::{format, vec};
use core::any::Any;
use anyhow::{bail, Context};
use chrono::{DateTime, Datelike, Timelike, Utc}; use chrono::{DateTime, Datelike, Timelike, Utc};
use chrono_tz::Tz::{self}; use chrono_tz::Tz::{self};
use core::sync::atomic::Ordering; use core::sync::atomic::Ordering;
use embassy_executor::Spawner; use embassy_executor::Spawner;
use embassy_sync::blocking_mutex::raw::{CriticalSectionRawMutex, NoopRawMutex}; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
use embassy_sync::lazy_lock::LazyLock; use embassy_sync::mutex::{Mutex, MutexGuard};
use embassy_sync::mutex::Mutex; use embassy_sync::once_lock::OnceLock;
use embassy_time::Timer; use embassy_time::Timer;
use esp_bootloader_esp_idf::ota::OtaImageState;
use esp_hal::rom::ets_delay_us;
use esp_hal::system::software_reset;
use esp_println::{logger, println}; use esp_println::{logger, println};
use hal::battery::BatteryState; use hal::battery::BatteryState;
use log::{log, LogMessage}; use log::{log, LogMessage};
use plant_state::PlantState; use plant_state::PlantState;
use portable_atomic::AtomicBool; use portable_atomic::AtomicBool;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::tank::{TankError, WATER_FROZEN_THRESH};
#[no_mangle]
extern "C" fn custom_halt() -> ! {
println!("Fatal error occurred. Restarting in 10 seconds...");
for _delay in 0..30 {
ets_delay_us(1_000_000);
}
println!("resetting");
//give serial transmit time to finish
ets_delay_us(500_000);
software_reset()
}
//use tank::*; //use tank::*;
mod config; mod config;
@ -48,10 +62,8 @@ mod tank;
extern crate alloc; extern crate alloc;
//mod webserver; //mod webserver;
pub static BOARD_ACCESS: LazyLock<Mutex<CriticalSectionRawMutex, HAL>> = pub static BOARD_ACCESS: OnceLock<Mutex<CriticalSectionRawMutex, HAL>> = OnceLock::new();
LazyLock::new(|| {
PlantHal::create().unwrap()
});
pub static STAY_ALIVE: AtomicBool = AtomicBool::new(false); pub static STAY_ALIVE: AtomicBool = AtomicBool::new(false);
#[derive(Serialize, Deserialize, Debug, PartialEq)] #[derive(Serialize, Deserialize, Debug, PartialEq)]
@ -140,14 +152,9 @@ async fn safe_main() -> anyhow::Result<()> {
version.git_hash, version.build_time version.git_hash, version.build_time
); );
let _esp = BOARD_ACCESS.get().await.lock().await.board_hal.get_esp();
//TODO //TODO
//let count = unsafe { esp_ota_get_app_partition_count() };
//log::info!("Partition count is {}", count);
//let mut ota_state: esp_ota_img_states_t = 0;
//let running_partition = unsafe { esp_ota_get_running_partition() };
//let partition_address = unsafe { (*running_partition).address };
//log::info!("Partition address is {}", address);
let partition_address = 0x1337;
// TODO // TODO
//let ota_state_string = unsafe { //let ota_state_string = unsafe {
@ -169,9 +176,9 @@ async fn safe_main() -> anyhow::Result<()> {
//} //}
//}; //};
//log(LogMessage::PartitionState, 0, 0, "", ota_state_string); //log(LogMessage::PartitionState, 0, 0, "", ota_state_string);
let ota_state_string = "unknown"; let _ota_state_string = "unknown";
println!("faul led"); println!("faul led");
let mut board = BOARD_ACCESS.get().lock().await; let mut board = BOARD_ACCESS.get().await.lock().await;
board.board_hal.general_fault(false).await; board.board_hal.general_fault(false).await;
println!("faul led2"); println!("faul led2");
let cur = board let cur = board
@ -184,7 +191,6 @@ async fn safe_main() -> anyhow::Result<()> {
board.board_hal.general_fault(true); board.board_hal.general_fault(true);
anyhow::Ok(board.board_hal.get_esp().time()) anyhow::Ok(board.board_hal.get_esp().time())
})?; })?;
//check if we know the time current > 2020 (plausibility checks, this code is newer than 2020) //check if we know the time current > 2020 (plausibility checks, this code is newer than 2020)
if cur.year() < 2020 { if cur.year() < 2020 {
to_config = true; to_config = true;
@ -192,10 +198,10 @@ async fn safe_main() -> anyhow::Result<()> {
} }
info!("cur is {}", cur); info!("cur is {}", cur);
update_charge_indicator().await; update_charge_indicator(&mut board).await;
println!("faul led3");
if board.board_hal.get_esp().get_restart_to_conf() { if board.board_hal.get_esp().get_restart_to_conf() {
log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", ""); log(LogMessage::ConfigModeSoftwareOverride, 0, 0, "", "").await;
for _i in 0..2 { for _i in 0..2 {
board.board_hal.general_fault(true).await; board.board_hal.general_fault(true).await;
Timer::after_millis(100).await; Timer::after_millis(100).await;
@ -221,12 +227,15 @@ async fn safe_main() -> anyhow::Result<()> {
} else { } else {
board.board_hal.general_fault(false).await; board.board_hal.general_fault(false).await;
} }
} else {
info!("no mode override");
} }
if board.board_hal.get_config().hardware.board == INITIAL if board.board_hal.get_config().hardware.board == INITIAL
&& board.board_hal.get_config().network.ssid.is_none() && board.board_hal.get_config().network.ssid.is_none()
{ {
let _ = board.board_hal.get_esp().wifi_ap(); info!("No wifi configured, starting initial config mode");
board.board_hal.get_esp().wifi_ap().await.unwrap();
drop(board); drop(board);
let reboot_now = Arc::new(AtomicBool::new(false)); let reboot_now = Arc::new(AtomicBool::new(false));
//TODO //TODO
@ -254,7 +263,6 @@ async fn safe_main() -> anyhow::Result<()> {
} }
} }
// let timezone = match &board.board_hal.get_config().timezone { // let timezone = match &board.board_hal.get_config().timezone {
// Some(tz_str) => tz_str.parse::<Tz>().unwrap_or_else(|_| { // Some(tz_str) => tz_str.parse::<Tz>().unwrap_or_else(|_| {
// info!("Invalid timezone '{}', falling back to UTC", tz_str); // info!("Invalid timezone '{}', falling back to UTC", tz_str);
@ -262,25 +270,18 @@ async fn safe_main() -> anyhow::Result<()> {
// }), // }),
// None => UTC, // Fallback to UTC if no timezone is set // None => UTC, // Fallback to UTC if no timezone is set
// }; // };
let timezone = Tz::UTC; let _timezone = Tz::UTC;
let timezone_time = cur;//TODO.with_timezone(&timezone); drop(board);
let timezone_time = cur; //TODO.with_timezone(&timezone);
info!( info!(
"Running logic at utc {} and {} {}", "Running logic at utc {} and {} {}",
cur, cur, "todo timezone.name()", timezone_time
"todo timezone.name()",
timezone_time
); );
if let NetworkMode::WIFI { ref ip_address, .. } = network_mode { if let NetworkMode::WIFI { ref ip_address, .. } = network_mode {
publish_firmware_info( publish_firmware_info(version, ip_address, &timezone_time.to_rfc3339()).await;
version,
partition_address,
ota_state_string,
ip_address,
&timezone_time.to_rfc3339(),
)
.await;
publish_battery_state().await; publish_battery_state().await;
let _ = publish_mppt_state().await; let _ = publish_mppt_state().await;
} }
@ -299,10 +300,8 @@ async fn safe_main() -> anyhow::Result<()> {
.to_string() .to_string()
.as_str(), .as_str(),
"", "",
).await; )
.await;
//TODO must drop board here?
//drop(board);
if to_config { if to_config {
//check if client or ap mode and init Wi-Fi //check if client or ap mode and init Wi-Fi
@ -316,7 +315,7 @@ async fn safe_main() -> anyhow::Result<()> {
log(LogMessage::NormalRun, 0, 0, "", "").await; log(LogMessage::NormalRun, 0, 0, "", "").await;
} }
let dry_run = false; let _dry_run = false;
// //
// let tank_state = determine_tank_state(&mut board); // let tank_state = determine_tank_state(&mut board);
// //
@ -353,7 +352,7 @@ async fn safe_main() -> anyhow::Result<()> {
// } // }
// } // }
let mut water_frozen = false; let mut _water_frozen = false;
//TODO //TODO
let water_temp = anyhow::Ok(12_f32); let water_temp = anyhow::Ok(12_f32);
// board // board
@ -364,14 +363,14 @@ async fn safe_main() -> anyhow::Result<()> {
if let Ok(res) = water_temp { if let Ok(res) = water_temp {
if res < WATER_FROZEN_THRESH { if res < WATER_FROZEN_THRESH {
water_frozen = true; _water_frozen = true;
} }
} }
//publish_tank_state(&tank_state, &water_temp).await; //publish_tank_state(&tank_state, &water_temp).await;
board = BOARD_ACCESS.get().await.lock().await;
let plantstate: [PlantState; PLANT_COUNT] = [ let _plantstate: [PlantState; PLANT_COUNT] = [
PlantState::read_hardware_state(0, &mut board).await, PlantState::read_hardware_state(0, &mut board).await,
PlantState::read_hardware_state(1, &mut board).await, PlantState::read_hardware_state(1, &mut board).await,
PlantState::read_hardware_state(2, &mut board).await, PlantState::read_hardware_state(2, &mut board).await,
@ -468,7 +467,7 @@ async fn safe_main() -> anyhow::Result<()> {
.await .await
.unwrap_or(BatteryState::Unknown); .unwrap_or(BatteryState::Unknown);
let mut light_state = LightState { let light_state = LightState {
enabled: board.board_hal.get_config().night_lamp.enabled, enabled: board.board_hal.get_config().night_lamp.enabled,
..Default::default() ..Default::default()
}; };
@ -611,7 +610,7 @@ pub async fn do_secure_pump(
let mut error = false; let mut error = false;
let mut first_error = true; let mut first_error = true;
let mut pump_time_s = 0; let mut pump_time_s = 0;
let board = &mut BOARD_ACCESS.get().lock().await; let board = &mut BOARD_ACCESS.get().await.lock().await;
if !dry_run { if !dry_run {
// board // board
// .board_hal // .board_hal
@ -733,8 +732,7 @@ pub async fn do_secure_pump(
}) })
} }
async fn update_charge_indicator() { async fn update_charge_indicator(board: &mut MutexGuard<'_, CriticalSectionRawMutex, HAL<'_>>) {
let board = &mut BOARD_ACCESS.get().lock().await;
//we have mppt controller, ask it for charging current //we have mppt controller, ask it for charging current
// let tank_state = determine_tank_state(&mut board); // let tank_state = determine_tank_state(&mut board);
// //
@ -779,7 +777,8 @@ async fn update_charge_indicator() {
else if let Ok(charging) = board else if let Ok(charging) = board
.board_hal .board_hal
.get_battery_monitor() .get_battery_monitor()
.average_current_milli_ampere().await .average_current_milli_ampere()
.await
{ {
let _ = board.board_hal.set_charge_indicator(charging > 20); let _ = board.board_hal.set_charge_indicator(charging > 20);
} else { } else {
@ -830,51 +829,45 @@ async fn update_charge_indicator() {
// } // }
// } // }
async fn publish_firmware_info( async fn publish_firmware_info(version: VersionInfo, ip_address: &String, timezone_time: &String) {
version: VersionInfo, let board = &mut BOARD_ACCESS.get().await.lock().await;
address: u32, let esp = board.board_hal.get_esp();
ota_state_string: &str, let _ = esp
ip_address: &String,
timezone_time: &String,
) {
let board = &mut BOARD_ACCESS.get().lock().await;
let _ = board
.board_hal
.get_esp()
.mqtt_publish("/firmware/address", ip_address.as_bytes()) .mqtt_publish("/firmware/address", ip_address.as_bytes())
.await; .await;
let _ = board let _ = esp
.board_hal
.get_esp()
.mqtt_publish("/firmware/githash", version.git_hash.as_bytes()) .mqtt_publish("/firmware/githash", version.git_hash.as_bytes())
.await; .await;
let _ = board let _ = esp
.board_hal
.get_esp()
.mqtt_publish("/firmware/buildtime", version.build_time.as_bytes()) .mqtt_publish("/firmware/buildtime", version.build_time.as_bytes())
.await; .await;
let _ = board.board_hal.get_esp().mqtt_publish( let _ = esp.mqtt_publish("/firmware/last_online", timezone_time.as_bytes());
"/firmware/last_online", let _ = esp
timezone_time.as_bytes(), .mqtt_publish(
); "/firmware/ota_state",
let _ = board state_to_string(esp.ota_state).as_bytes(),
.board_hal )
.get_esp()
.mqtt_publish("/firmware/ota_state", ota_state_string.as_bytes())
.await; .await;
let _ = board.board_hal.get_esp().mqtt_publish( let slot = esp.slot;
"/firmware/partition_address", let _ = esp
format!("{:#06x}", address).as_bytes(), .mqtt_publish("/firmware/ota_slot", format!("slot{slot}").as_bytes())
);
let _ = board
.board_hal
.get_esp()
.mqtt_publish("/state", "online".as_bytes())
.await; .await;
let _ = esp.mqtt_publish("/state", "online".as_bytes()).await;
}
fn state_to_string(state: OtaImageState) -> &'static str {
match state {
OtaImageState::New => "New",
OtaImageState::PendingVerify => "PendingVerify",
OtaImageState::Valid => "Valid",
OtaImageState::Invalid => "Invalid",
OtaImageState::Aborted => "Aborted",
OtaImageState::Undefined => "Undefined",
}
} }
async fn try_connect_wifi_sntp_mqtt() -> NetworkMode { async fn try_connect_wifi_sntp_mqtt() -> NetworkMode {
let board = &mut BOARD_ACCESS.get().lock().await; let board = &mut BOARD_ACCESS.get().await.lock().await;
let nw_conf = &board.board_hal.get_config().network.clone(); let nw_conf = &board.board_hal.get_config().network.clone();
match board.board_hal.get_esp().wifi(nw_conf).await { match board.board_hal.get_esp().wifi(nw_conf).await {
Ok(ip_info) => { Ok(ip_info) => {
@ -941,6 +934,7 @@ async fn pump_info(
Ok(state) => { Ok(state) => {
let _ = BOARD_ACCESS let _ = BOARD_ACCESS
.get() .get()
.await
.lock() .lock()
.await .await
.board_hal .board_hal
@ -957,7 +951,7 @@ async fn pump_info(
} }
async fn publish_mppt_state() -> anyhow::Result<()> { async fn publish_mppt_state() -> anyhow::Result<()> {
let board_hal = &mut BOARD_ACCESS.get().lock().await.board_hal; let board_hal = &mut BOARD_ACCESS.get().await.lock().await.board_hal;
let current = board_hal.get_mptt_current().await?; let current = board_hal.get_mptt_current().await?;
let voltage = board_hal.get_mptt_voltage().await?; let voltage = board_hal.get_mptt_voltage().await?;
let solar_state = Solar { let solar_state = Solar {
@ -975,8 +969,12 @@ async fn publish_mppt_state() -> anyhow::Result<()> {
} }
async fn publish_battery_state() -> () { async fn publish_battery_state() -> () {
let board = &mut BOARD_ACCESS.get().lock().await; let board = &mut BOARD_ACCESS.get().await.lock().await;
let state = board.board_hal.get_battery_monitor().get_battery_state().await; let state = board
.board_hal
.get_battery_monitor()
.get_battery_state()
.await;
if let Ok(serialized_battery_state_bytes) = if let Ok(serialized_battery_state_bytes) =
serde_json::to_string(&state).map(|s| s.into_bytes()) serde_json::to_string(&state).map(|s| s.into_bytes())
{ {
@ -992,8 +990,9 @@ async fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
let mut led_count = 8; let mut led_count = 8;
let mut pattern_step = 0; let mut pattern_step = 0;
loop { loop {
update_charge_indicator().await; let mut board = BOARD_ACCESS.get().await.lock().await;
let mut board = BOARD_ACCESS.get().lock().await; update_charge_indicator(&mut board).await;
match wait_type { match wait_type {
WaitType::MissingConfig => { WaitType::MissingConfig => {
// Keep existing behavior: circular filling pattern // Keep existing behavior: circular filling pattern
@ -1023,7 +1022,7 @@ async fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
drop(board); drop(board);
Timer::after_millis(delay).await; Timer::after_millis(delay).await;
let mut board = BOARD_ACCESS.get().lock().await; let mut board = BOARD_ACCESS.get().await.lock().await;
board.board_hal.general_fault(false); board.board_hal.general_fault(false);
// Clear all LEDs // Clear all LEDs
@ -1039,7 +1038,13 @@ async fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
if reboot_now.load(Ordering::Relaxed) { if reboot_now.load(Ordering::Relaxed) {
//ensure clean http answer //ensure clean http answer
Timer::after_millis(500).await; Timer::after_millis(500).await;
BOARD_ACCESS.get().lock().await.board_hal.deep_sleep(1); BOARD_ACCESS
.get()
.await
.lock()
.await
.board_hal
.deep_sleep(1);
} }
} }
} }
@ -1049,19 +1054,21 @@ async fn main(spawner: Spawner) {
// intialize embassy // intialize embassy
logger::init_logger_from_env(); logger::init_logger_from_env();
//force init here! //force init here!
let board = BOARD_ACCESS.get().lock().await; println!("Hal init");
drop(board); match BOARD_ACCESS.init(PlantHal::create(spawner).unwrap()) {
println!("test"); Ok(_) => {}
Err(_) => {
panic!("Could not set hal to static")
}
}
println!("Hal init done, starting logic");
info!("Embassy initialized!"); match safe_main().await {
let result = safe_main().await;
match result {
// this should not get triggered, safe_main should not return but go into deep sleep with sensible // this should not get triggered, safe_main should not return but go into deep sleep with sensible
// timeout, this is just a fallback // timeout, this is just a fallback
Ok(_) => { Ok(_) => {
warn!("Main app finished, but should never do, restarting"); warn!("Main app finished, but should never do, restarting");
let board = &mut BOARD_ACCESS.get().lock().await.board_hal; let board = &mut BOARD_ACCESS.get().await.lock().await.board_hal;
board.get_esp().set_restart_to_conf(false); board.get_esp().set_restart_to_conf(false);
board.deep_sleep(1); board.deep_sleep(1);

View File

@ -1,6 +1,5 @@
use crate::{config::TankConfig, hal::HAL};
use anyhow::Context;
use crate::alloc::string::{String, ToString}; use crate::alloc::string::{String, ToString};
use crate::config::TankConfig;
use serde::Serialize; use serde::Serialize;
const OPEN_TANK_VOLTAGE: f32 = 3.0; const OPEN_TANK_VOLTAGE: f32 = 3.0;