in main deep sleep was larger than required by a factor of 1000, fixed this an renamed function to make expected duration count size obvious from the name
637 lines
23 KiB
Rust
637 lines
23 KiB
Rust
pub(crate) mod battery;
|
|
pub mod esp;
|
|
mod initial_hal;
|
|
mod little_fs2storage_adapter;
|
|
pub(crate) mod rtc;
|
|
mod shared_flash;
|
|
mod v3_hal;
|
|
mod v3_shift_register;
|
|
mod v4_hal;
|
|
mod v4_sensor;
|
|
mod water;
|
|
|
|
use crate::alloc::string::ToString;
|
|
use crate::hal::rtc::{DS3231Module, RTCModuleInteraction};
|
|
use esp_hal::interrupt::software::SoftwareInterruptControl;
|
|
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::GPIO10;
|
|
use esp_hal::peripherals::GPIO11;
|
|
use esp_hal::peripherals::GPIO12;
|
|
use esp_hal::peripherals::GPIO13;
|
|
use esp_hal::peripherals::GPIO14;
|
|
use esp_hal::peripherals::GPIO15;
|
|
use esp_hal::peripherals::GPIO16;
|
|
use esp_hal::peripherals::GPIO17;
|
|
use esp_hal::peripherals::GPIO18;
|
|
use esp_hal::peripherals::GPIO2;
|
|
use esp_hal::peripherals::GPIO21;
|
|
use esp_hal::peripherals::GPIO22;
|
|
use esp_hal::peripherals::GPIO23;
|
|
use esp_hal::peripherals::GPIO24;
|
|
use esp_hal::peripherals::GPIO25;
|
|
use esp_hal::peripherals::GPIO26;
|
|
use esp_hal::peripherals::GPIO27;
|
|
use esp_hal::peripherals::GPIO28;
|
|
use esp_hal::peripherals::GPIO29;
|
|
use esp_hal::peripherals::GPIO3;
|
|
use esp_hal::peripherals::GPIO30;
|
|
use esp_hal::peripherals::GPIO4;
|
|
use esp_hal::peripherals::GPIO5;
|
|
use esp_hal::peripherals::GPIO6;
|
|
use esp_hal::peripherals::GPIO7;
|
|
use esp_hal::peripherals::GPIO8;
|
|
use esp_hal::peripherals::PCNT;
|
|
use esp_hal::peripherals::TWAI0;
|
|
use portable_atomic::AtomicBool;
|
|
|
|
use crate::{
|
|
bail,
|
|
config::{BatteryBoardVersion, BoardVersion, PlantControllerConfig},
|
|
hal::{
|
|
battery::{BatteryInteraction, NoBatteryMonitor},
|
|
esp::Esp,
|
|
},
|
|
log::LogMessage,
|
|
BOARD_ACCESS,
|
|
};
|
|
use alloc::boxed::Box;
|
|
use alloc::format;
|
|
use alloc::sync::Arc;
|
|
use async_trait::async_trait;
|
|
use bq34z100::Bq34z100g1Driver;
|
|
use chrono::{DateTime, FixedOffset, Utc};
|
|
use core::cell::RefCell;
|
|
use ds323x::ic::DS3231;
|
|
use ds323x::interface::I2cInterface;
|
|
use ds323x::{DateTimeAccess, Ds323x};
|
|
use eeprom24x::addr_size::TwoBytes;
|
|
use eeprom24x::page_size::B32;
|
|
use eeprom24x::unique_serial::No;
|
|
use eeprom24x::{Eeprom24x, SlaveAddr, Storage};
|
|
use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice;
|
|
use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
|
use embassy_sync::blocking_mutex::CriticalSectionMutex;
|
|
use embedded_storage::nor_flash::RmwNorFlashStorage;
|
|
use embedded_storage::ReadStorage;
|
|
use esp_bootloader_esp_idf::partitions::{
|
|
AppPartitionSubType, DataPartitionSubType, FlashRegion, PartitionEntry, PartitionTable,
|
|
PartitionType,
|
|
};
|
|
use esp_hal::clock::CpuClock;
|
|
use esp_hal::gpio::{Input, InputConfig, Pull};
|
|
use esp_hal::uart::{Config as UartConfig, Uart};
|
|
use esp_storage::FlashStorage;
|
|
use lib_bms_protocol::{BmsReadable, ProtocolVersion};
|
|
use measurements::{Current, Voltage};
|
|
|
|
use crate::fat_error::{ContextExt, FatError, FatResult};
|
|
use crate::hal::battery::{print_battery_bq34z100, BQ34Z100G1};
|
|
use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem;
|
|
use crate::hal::water::TankSensor;
|
|
use crate::log::log;
|
|
use embassy_sync::mutex::Mutex;
|
|
use embassy_sync::once_lock::OnceLock;
|
|
use esp_alloc as _;
|
|
use esp_backtrace as _;
|
|
use esp_bootloader_esp_idf::ota::{OtaImageState, Ota};
|
|
use esp_hal::delay::Delay;
|
|
use esp_hal::i2c::master::{BusTimeout, Config, I2c};
|
|
use esp_hal::pcnt::unit::Unit;
|
|
use esp_hal::pcnt::Pcnt;
|
|
use esp_hal::rng::Rng;
|
|
use esp_hal::rtc_cntl::{Rtc, SocResetReason};
|
|
use esp_hal::system::reset_reason;
|
|
use esp_hal::time::Rate;
|
|
use esp_hal::timer::timg::{TimerGroup, Wdt};
|
|
use esp_hal::Blocking;
|
|
use littlefs2::fs::{Allocation, Filesystem as lfs2Filesystem};
|
|
use littlefs2::object_safe::DynStorage;
|
|
use log::{error, info, warn};
|
|
use shared_flash::MutexFlashStorage;
|
|
|
|
pub static PROGRESS_ACTIVE: AtomicBool = AtomicBool::new(false);
|
|
|
|
//Only support for 8 right now!
|
|
pub const PLANT_COUNT: usize = 8;
|
|
|
|
pub static WATCHDOG: OnceLock<
|
|
embassy_sync::blocking_mutex::Mutex<
|
|
CriticalSectionRawMutex,
|
|
RefCell<Wdt<esp_hal::peripherals::TIMG0>>,
|
|
>,
|
|
> = OnceLock::new();
|
|
|
|
const TANK_MULTI_SAMPLE: usize = 11;
|
|
pub static I2C_DRIVER: OnceLock<
|
|
embassy_sync::blocking_mutex::Mutex<CriticalSectionRawMutex, RefCell<I2c<Blocking>>>,
|
|
> = OnceLock::new();
|
|
|
|
#[derive(Debug, PartialEq)]
|
|
pub enum Sensor {
|
|
A,
|
|
B,
|
|
}
|
|
|
|
pub struct PlantHal {}
|
|
|
|
pub struct HAL<'a> {
|
|
pub board_hal: Box<dyn BoardInteraction<'a> + Send>,
|
|
}
|
|
|
|
fn ota_state(
|
|
slot: AppPartitionSubType,
|
|
ota_data: &mut FlashRegion<RmwNorFlashStorage<&mut MutexFlashStorage>>,
|
|
) -> OtaImageState {
|
|
// Read and log OTA states for both slots before constructing Ota
|
|
// Each OTA select entry is 32 bytes: [seq:4][label:20][state:4][crc:4]
|
|
// Offsets within the OTA data partition: slot0 @ 0x0000, slot1 @ 0x1000
|
|
let mut slot_buf = [0u8; 32];
|
|
if slot == AppPartitionSubType::Ota0 {
|
|
let _ = ReadStorage::read(ota_data, 0x0000, &mut slot_buf);
|
|
} else {
|
|
let _ = ReadStorage::read(ota_data, 0x1000, &mut slot_buf);
|
|
}
|
|
let raw_state = u32::from_le_bytes(slot_buf[24..28].try_into().unwrap_or([0xff; 4]));
|
|
|
|
OtaImageState::try_from(raw_state).unwrap_or(OtaImageState::Undefined)
|
|
}
|
|
fn get_current_slot(
|
|
pt: &PartitionTable,
|
|
ota: &mut Ota<RmwNorFlashStorage<&mut MutexFlashStorage>>,
|
|
) -> Result<AppPartitionSubType, FatError> {
|
|
let booted = pt.booted_partition()?.ok_or(FatError::OTAError)?;
|
|
let booted_type = booted.partition_type();
|
|
let booted_ota_type = match booted_type {
|
|
PartitionType::App(subtype) => subtype,
|
|
_ => {
|
|
bail!("Booted partition is not an app partition");
|
|
}
|
|
};
|
|
|
|
let expected_partition = ota.current_app_partition()?;
|
|
if expected_partition == booted_ota_type {
|
|
info!("Booted partition matches expected partition");
|
|
} else {
|
|
info!("Booted partition does not match expected partition, fixing ota entry");
|
|
ota.set_current_app_partition(booted_ota_type)?;
|
|
}
|
|
|
|
let fixed = ota.current_app_partition()?;
|
|
let state = ota.current_ota_state();
|
|
info!("Expected partition: {expected_partition:?}, current partition: {booted_ota_type:?}, state: {state:?}");
|
|
|
|
if fixed != booted_ota_type {
|
|
bail!(
|
|
"Could not fix ota entry, booted partition is still not correct: {:?} != {:?}",
|
|
booted_ota_type,
|
|
fixed
|
|
);
|
|
}
|
|
|
|
Ok(booted_ota_type)
|
|
}
|
|
|
|
pub fn next_partition(current: AppPartitionSubType) -> FatResult<AppPartitionSubType> {
|
|
let next = match current {
|
|
AppPartitionSubType::Ota0 => AppPartitionSubType::Ota1,
|
|
AppPartitionSubType::Ota1 => AppPartitionSubType::Ota0,
|
|
_ => {
|
|
bail!("Current slot is not ota0 or ota1");
|
|
}
|
|
};
|
|
Ok(next)
|
|
}
|
|
|
|
#[async_trait]
|
|
pub trait BoardInteraction<'a> {
|
|
fn get_tank_sensor(&mut self) -> Result<&mut TankSensor<'a>, FatError>;
|
|
fn get_esp(&mut self) -> &mut Esp<'a>;
|
|
fn get_config(&mut self) -> &PlantControllerConfig;
|
|
fn get_battery_monitor(&mut self) -> &mut Box<dyn BatteryInteraction + Send>;
|
|
fn get_rtc_module(&mut self) -> &mut Box<dyn RTCModuleInteraction + Send>;
|
|
async fn get_time(&mut self) -> DateTime<Utc>;
|
|
async fn set_time(&mut self, time: &DateTime<FixedOffset>) -> FatResult<()>;
|
|
async fn set_charge_indicator(&mut self, charging: bool) -> Result<(), FatError>;
|
|
async fn deep_sleep_ms(&mut self, duration_in_ms: u64) -> !;
|
|
|
|
fn is_day(&self) -> bool;
|
|
//should be multsampled
|
|
async fn light(&mut self, enable: bool) -> Result<(), FatError>;
|
|
async fn pump(&mut self, plant: usize, enable: bool) -> Result<(), FatError>;
|
|
async fn pump_current(&mut self, plant: usize) -> Result<Current, FatError>;
|
|
async fn fault(&mut self, plant: usize, enable: bool) -> Result<(), FatError>;
|
|
async fn measure_moisture_hz(&mut self, plant: usize, sensor: Sensor) -> Result<f32, FatError>;
|
|
async fn general_fault(&mut self, enable: bool);
|
|
async fn test(&mut self) -> Result<(), FatError>;
|
|
fn set_config(&mut self, config: PlantControllerConfig);
|
|
async fn get_mptt_voltage(&mut self) -> Result<Voltage, FatError>;
|
|
async fn get_mptt_current(&mut self) -> Result<Current, FatError>;
|
|
|
|
async fn progress(&mut self, counter: u32) {
|
|
let current = counter % PLANT_COUNT as u32;
|
|
for led in 0..PLANT_COUNT {
|
|
if let Err(err) = self.fault(led, current == led as u32).await {
|
|
warn!("Fault on plant {}: {:?}", led, err);
|
|
}
|
|
}
|
|
let even = counter % 2 == 0;
|
|
let _ = self.general_fault(even.into()).await;
|
|
}
|
|
|
|
async fn clear_progress(&mut self) {
|
|
for led in 0..PLANT_COUNT {
|
|
if let Err(err) = self.fault(led, false).await {
|
|
warn!("Fault on plant {}: {:?}", led, err);
|
|
}
|
|
}
|
|
let _ = self.general_fault(false).await;
|
|
}
|
|
}
|
|
|
|
#[allow(dead_code)]
|
|
pub struct FreePeripherals<'a> {
|
|
pub gpio0: GPIO0<'a>,
|
|
pub gpio2: GPIO2<'a>,
|
|
pub gpio3: GPIO3<'a>,
|
|
pub gpio4: GPIO4<'a>,
|
|
pub gpio5: GPIO5<'a>,
|
|
pub gpio6: GPIO6<'a>,
|
|
pub gpio7: GPIO7<'a>,
|
|
pub gpio8: GPIO8<'a>,
|
|
// //config button here
|
|
pub gpio10: GPIO10<'a>,
|
|
pub gpio11: GPIO11<'a>,
|
|
pub gpio12: GPIO12<'a>,
|
|
pub gpio13: GPIO13<'a>,
|
|
pub gpio14: GPIO14<'a>,
|
|
pub gpio15: GPIO15<'a>,
|
|
pub gpio16: GPIO16<'a>,
|
|
pub gpio17: GPIO17<'a>,
|
|
pub gpio18: GPIO18<'a>,
|
|
// //i2c here
|
|
pub gpio21: GPIO21<'a>,
|
|
pub gpio22: GPIO22<'a>,
|
|
pub gpio23: GPIO23<'a>,
|
|
pub gpio27: GPIO27<'a>,
|
|
pub twai: TWAI0<'a>,
|
|
pub pcnt0: Unit<'a, 0>,
|
|
pub pcnt1: Unit<'a, 1>,
|
|
pub adc1: ADC1<'a>,
|
|
}
|
|
|
|
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
|
|
}};
|
|
}
|
|
|
|
impl PlantHal {
|
|
pub async fn create() -> Result<Mutex<CriticalSectionRawMutex, HAL<'static>>, FatError> {
|
|
let config = esp_hal::Config::default().with_cpu_clock(CpuClock::max());
|
|
let peripherals: Peripherals = esp_hal::init(config);
|
|
|
|
esp_alloc::heap_allocator!(size: 64 * 1024);
|
|
esp_alloc::heap_allocator!(#[link_section = ".dram2_uninit"] size: 64000);
|
|
|
|
let mut rtc_peripheral: Rtc = Rtc::new(peripherals.LPWR);
|
|
rtc_peripheral.rwdt.disable();
|
|
|
|
let timg0 = TimerGroup::new(peripherals.TIMG0);
|
|
let sw_int = SoftwareInterruptControl::new(peripherals.SW_INTERRUPT);
|
|
esp_rtos::start(timg0.timer0, sw_int.software_interrupt0);
|
|
|
|
let boot_button = Input::new(
|
|
peripherals.GPIO9,
|
|
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();
|
|
let (controller, interfaces) = esp_radio::wifi::new(peripherals.WIFI, Default::default())
|
|
.expect("Could not init wifi");
|
|
|
|
let pcnt_module = Pcnt::new(peripherals.PCNT);
|
|
|
|
let free_pins = FreePeripherals {
|
|
gpio0: peripherals.GPIO0,
|
|
gpio2: peripherals.GPIO2,
|
|
gpio3: peripherals.GPIO3,
|
|
gpio4: peripherals.GPIO4,
|
|
gpio5: peripherals.GPIO5,
|
|
gpio6: peripherals.GPIO6,
|
|
gpio7: peripherals.GPIO7,
|
|
gpio8: peripherals.GPIO8,
|
|
gpio10: peripherals.GPIO10,
|
|
gpio11: peripherals.GPIO11,
|
|
gpio12: peripherals.GPIO12,
|
|
gpio13: peripherals.GPIO13,
|
|
gpio14: peripherals.GPIO14,
|
|
gpio15: peripherals.GPIO15,
|
|
gpio16: peripherals.GPIO16,
|
|
gpio17: peripherals.GPIO17,
|
|
gpio18: peripherals.GPIO18,
|
|
gpio21: peripherals.GPIO21,
|
|
gpio22: peripherals.GPIO22,
|
|
gpio23: peripherals.GPIO23,
|
|
gpio27: peripherals.GPIO27,
|
|
twai: peripherals.TWAI0,
|
|
pcnt0: pcnt_module.unit0,
|
|
pcnt1: pcnt_module.unit1,
|
|
adc1: peripherals.ADC1,
|
|
};
|
|
|
|
let tablebuffer = mk_static!(
|
|
[u8; esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN],
|
|
[0u8; esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN]
|
|
);
|
|
|
|
let bullshit = MutexFlashStorage {
|
|
inner: Arc::new(CriticalSectionMutex::new(RefCell::new(FlashStorage::new(
|
|
peripherals.FLASH,
|
|
)))),
|
|
};
|
|
let flash_storage = mk_static!(MutexFlashStorage, bullshit.clone());
|
|
let flash_storage_2 = mk_static!(MutexFlashStorage, bullshit.clone());
|
|
let flash_storage_3 = mk_static!(MutexFlashStorage, bullshit.clone());
|
|
|
|
let pt =
|
|
esp_bootloader_esp_idf::partitions::read_partition_table(flash_storage, tablebuffer)?;
|
|
|
|
let ota_data = mk_static!(
|
|
PartitionEntry,
|
|
pt.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::Data(
|
|
DataPartitionSubType::Ota,
|
|
))?
|
|
.expect("No OTA data partition found")
|
|
);
|
|
|
|
let mut ota_data = ota_data.as_embedded_storage(mk_static!(
|
|
RmwNorFlashStorage<&mut MutexFlashStorage>,
|
|
RmwNorFlashStorage::new(flash_storage_2, mk_static!([u8; 4096], [0_u8; 4096]))
|
|
));
|
|
|
|
let state_0 = ota_state(AppPartitionSubType::Ota0, &mut ota_data);
|
|
let state_1 = ota_state(AppPartitionSubType::Ota1, &mut ota_data);
|
|
let mut ota = Ota::new(ota_data, 2)?;
|
|
let running = get_current_slot(&pt, &mut ota)?;
|
|
let target = next_partition(running)?;
|
|
|
|
info!("Currently running OTA slot: {running:?}");
|
|
info!("Updates will be stored in OTA slot: {target:?}");
|
|
info!("Slot0 state: {state_0:?}");
|
|
info!("Slot1 state: {state_1:?}");
|
|
|
|
//get current_state and next_state here!
|
|
let ota_target = match target {
|
|
AppPartitionSubType::Ota0 => pt
|
|
.find_partition(PartitionType::App(AppPartitionSubType::Ota0))?
|
|
.context("Partition table invalid no ota0")?,
|
|
AppPartitionSubType::Ota1 => pt
|
|
.find_partition(PartitionType::App(AppPartitionSubType::Ota1))?
|
|
.context("Partition table invalid no ota1")?,
|
|
_ => {
|
|
bail!("Invalid target partition");
|
|
}
|
|
};
|
|
|
|
let ota_target = mk_static!(PartitionEntry, ota_target);
|
|
let ota_target = mk_static!(
|
|
FlashRegion<MutexFlashStorage>,
|
|
ota_target.as_embedded_storage(flash_storage)
|
|
);
|
|
|
|
let data_partition = pt
|
|
.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::Data(
|
|
DataPartitionSubType::LittleFs,
|
|
))?
|
|
.expect("Data partition with littlefs not found");
|
|
let data_partition = mk_static!(PartitionEntry, data_partition);
|
|
|
|
let data = mk_static!(
|
|
FlashRegion<MutexFlashStorage>,
|
|
data_partition.as_embedded_storage(flash_storage_3)
|
|
);
|
|
let lfs2filesystem = mk_static!(LittleFs2Filesystem, LittleFs2Filesystem { storage: data });
|
|
let alloc = mk_static!(Allocation<LittleFs2Filesystem>, lfs2Filesystem::allocate());
|
|
if lfs2filesystem.is_mountable() {
|
|
info!("Littlefs2 filesystem is mountable");
|
|
} else {
|
|
match lfs2filesystem.format() {
|
|
Ok(..) => {
|
|
info!("Littlefs2 filesystem is formatted");
|
|
}
|
|
Err(err) => {
|
|
error!("Littlefs2 filesystem could not be formatted: {err:?}");
|
|
}
|
|
}
|
|
}
|
|
|
|
#[allow(clippy::arc_with_non_send_sync)]
|
|
let fs = Arc::new(Mutex::new(
|
|
lfs2Filesystem::mount(alloc, lfs2filesystem).expect("Could not mount lfs2 filesystem"),
|
|
));
|
|
|
|
|
|
let uart0 =
|
|
Uart::new(peripherals.UART0, UartConfig::default()).map_err(|_| FatError::String {
|
|
error: "Uart creation failed".to_string(),
|
|
})?;
|
|
|
|
let ap = interfaces.access_point;
|
|
let sta = interfaces.station;
|
|
let mut esp = Esp {
|
|
fs,
|
|
rng,
|
|
controller: Arc::new(Mutex::new(controller)),
|
|
interface_sta: Some(sta),
|
|
interface_ap: Some(ap),
|
|
boot_button,
|
|
wake_gpio1,
|
|
ota,
|
|
ota_target,
|
|
current: running,
|
|
slot0_state: state_0,
|
|
slot1_state: state_1,
|
|
uart0,
|
|
rtc: rtc_peripheral,
|
|
};
|
|
|
|
//init,reset rtc memory depending on cause
|
|
let mut init_rtc_store: bool = false;
|
|
let mut to_config_mode: bool = false;
|
|
let reasons = match reset_reason() {
|
|
None => "unknown",
|
|
Some(reason) => match reason {
|
|
SocResetReason::ChipPowerOn => "power on",
|
|
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::CoreEfuseCrc => "core efuse crc",
|
|
SocResetReason::CoreUsbUart => {
|
|
//TODO still required? or via button ignore? to_config_mode = true;
|
|
to_config_mode = true;
|
|
"core usb uart"
|
|
}
|
|
SocResetReason::CoreUsbJtag => "core usb jtag",
|
|
SocResetReason::Cpu0JtagCpu => "cpu0 jtag cpu",
|
|
},
|
|
};
|
|
log(
|
|
LogMessage::ResetReason,
|
|
init_rtc_store as u32,
|
|
to_config_mode as u32,
|
|
"",
|
|
&format!("{reasons:?}"),
|
|
);
|
|
|
|
|
|
esp.init_rtc_deepsleep_memory(init_rtc_store, to_config_mode)
|
|
.await;
|
|
|
|
let config = esp.load_config().await;
|
|
|
|
info!("Init rtc driver");
|
|
|
|
let sda = peripherals.GPIO20;
|
|
let scl = peripherals.GPIO19;
|
|
|
|
let i2c = I2c::new(
|
|
peripherals.I2C0,
|
|
Config::default()
|
|
.with_frequency(Rate::from_hz(100))
|
|
.with_timeout(BusTimeout::Maximum),
|
|
)?
|
|
.with_scl(scl)
|
|
.with_sda(sda);
|
|
let i2c_bus: embassy_sync::blocking_mutex::Mutex<
|
|
CriticalSectionRawMutex,
|
|
RefCell<I2c<Blocking>>,
|
|
> = CriticalSectionMutex::new(RefCell::new(i2c));
|
|
|
|
|
|
I2C_DRIVER.init(i2c_bus).expect("Could not init i2c driver");
|
|
|
|
let i2c_bus = I2C_DRIVER.get().await;
|
|
let rtc_device = I2cDevice::new(i2c_bus);
|
|
let mut bms_device = I2cDevice::new(i2c_bus);
|
|
let eeprom_device = I2cDevice::new(i2c_bus);
|
|
|
|
|
|
let mut rtc: Ds323x<
|
|
I2cInterface<I2cDevice<CriticalSectionRawMutex, I2c<Blocking>>>,
|
|
DS3231,
|
|
> = Ds323x::new_ds3231(rtc_device);
|
|
|
|
|
|
info!("Init rtc eeprom driver");
|
|
let eeprom = Eeprom24x::new_24x32(eeprom_device, SlaveAddr::Alternative(true, true, true));
|
|
let rtc_time = rtc.datetime();
|
|
match rtc_time {
|
|
Ok(tt) => {
|
|
info!("Rtc Module reports time at UTC {tt}");
|
|
}
|
|
Err(err) => {
|
|
info!("Rtc Module could not be read {err:?}");
|
|
}
|
|
}
|
|
|
|
let storage: Storage<
|
|
I2cDevice<'static, CriticalSectionRawMutex, I2c<Blocking>>,
|
|
B32,
|
|
TwoBytes,
|
|
No,
|
|
Delay,
|
|
> = Storage::new(eeprom, Delay::new());
|
|
let rtc_module: Box<dyn RTCModuleInteraction + Send> =
|
|
Box::new(DS3231Module { rtc, storage }) as Box<dyn RTCModuleInteraction + Send>;
|
|
|
|
let hal = match config {
|
|
Ok(config) => {
|
|
let battery_interaction: Box<dyn BatteryInteraction + Send> =
|
|
match config.hardware.battery {
|
|
BatteryBoardVersion::Disabled => Box::new(NoBatteryMonitor {}),
|
|
BatteryBoardVersion::WchI2cSlave => {
|
|
let version = ProtocolVersion::read_from_i2c(&mut bms_device);
|
|
let version_val = match version {
|
|
Ok(v) => unsafe { core::mem::transmute::<ProtocolVersion, u32>(v) },
|
|
Err(_) => 0,
|
|
};
|
|
if version_val == 1 {
|
|
//Box::new(WCHI2CSlave { i2c: bms_device })
|
|
// todo fix the type above
|
|
Box::new(NoBatteryMonitor {})
|
|
} else {
|
|
//todo should be an error variant instead?
|
|
Box::new(NoBatteryMonitor {})
|
|
}
|
|
}
|
|
BatteryBoardVersion::BQ34Z100G1 => Box::new(NoBatteryMonitor {}),
|
|
};
|
|
|
|
let board_hal: Box<dyn BoardInteraction + Send> = match config.hardware.board {
|
|
BoardVersion::INITIAL => {
|
|
initial_hal::create_initial_board(free_pins, config, esp)?
|
|
}
|
|
BoardVersion::V3 => {
|
|
v3_hal::create_v3(free_pins, esp, config, battery_interaction, rtc_module)?
|
|
}
|
|
BoardVersion::V4 => {
|
|
v4_hal::create_v4(free_pins, esp, config, battery_interaction, rtc_module)
|
|
.await?
|
|
}
|
|
};
|
|
|
|
HAL { board_hal }
|
|
}
|
|
Err(err) => {
|
|
log(
|
|
LogMessage::ConfigModeMissingConfig,
|
|
0,
|
|
0,
|
|
"",
|
|
&err.to_string(),
|
|
);
|
|
HAL {
|
|
board_hal: initial_hal::create_initial_board(
|
|
free_pins,
|
|
PlantControllerConfig::default(),
|
|
esp,
|
|
)?,
|
|
}
|
|
}
|
|
};
|
|
|
|
Ok(Mutex::new(hal))
|
|
}
|
|
|
|
/// Feed the watchdog timer to prevent system reset
|
|
pub fn feed_watchdog() {
|
|
if let Some(wdt_mutex) = WATCHDOG.try_get() {
|
|
wdt_mutex.lock(|cell| {
|
|
cell.borrow_mut().feed();
|
|
});
|
|
}
|
|
}
|
|
}
|