- use crate::util::mk_static in network module - use crate::util::mk_static in mqtt module - use crate::util::mk_static in hal module - remove dead mk_static macro from esp module
630 lines
22 KiB
Rust
630 lines
22 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>,
|
|
}
|
|
|
|
use crate::util::mk_static;
|
|
|
|
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();
|
|
});
|
|
}
|
|
}
|
|
}
|