get most stuff working again, by upgrading to newer esp-hal version

this involved adding a lot of code from the develop branch step by step
there are still some bugs, but at least i can get into the web interface
and configure stuff again \o/ … measuring and pumping is working as well
This commit is contained in:
2026-05-04 23:46:27 +02:00
parent ecb7707357
commit db401aac55
23 changed files with 2029 additions and 1292 deletions

View File

@@ -3,6 +3,7 @@ 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;
@@ -11,6 +12,7 @@ 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;
@@ -43,6 +45,7 @@ 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,
@@ -71,23 +74,29 @@ 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,
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::{FatError, FatResult};
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_ACCESS;
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::Slot;
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;
@@ -96,19 +105,25 @@ 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;
use esp_hal::timer::timg::{TimerGroup, Wdt};
use esp_hal::Blocking;
use esp_storage::FlashStorage;
use esp_wifi::{init, EspWifiController};
use littlefs2::fs::{Allocation, Filesystem as lfs2Filesystem};
use littlefs2::object_safe::DynStorage;
use log::{info, warn};
use log::{error, info, warn};
use shared_flash::MutexFlashStorage;
pub static TIME_ACCESS: OnceLock<Mutex<CriticalSectionRawMutex, Rtc>> = OnceLock::new();
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>>>,
@@ -126,6 +141,70 @@ 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>;
@@ -133,6 +212,8 @@ pub trait BoardInteraction<'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(&mut self, duration_in_ms: u64) -> !;
@@ -194,13 +275,7 @@ pub struct FreePeripherals<'a> {
pub gpio21: GPIO21<'a>,
pub gpio22: GPIO22<'a>,
pub gpio23: GPIO23<'a>,
pub gpio24: GPIO24<'a>,
pub gpio25: GPIO25<'a>,
pub gpio26: GPIO26<'a>,
pub gpio27: GPIO27<'a>,
pub gpio28: GPIO28<'a>,
pub gpio29: GPIO29<'a>,
pub gpio30: GPIO30<'a>,
pub twai: TWAI0<'a>,
pub pcnt0: Unit<'a, 0>,
pub pcnt1: Unit<'a, 1>,
@@ -224,14 +299,12 @@ impl PlantHal {
esp_alloc::heap_allocator!(size: 64 * 1024);
esp_alloc::heap_allocator!(#[link_section = ".dram2_uninit"] size: 64000);
let rtc: Rtc = Rtc::new(peripherals.LPWR);
TIME_ACCESS
.init(Mutex::new(rtc))
.map_err(|_| FatError::String {
error: "Init error rct".to_string(),
})?;
let mut rtc_peripheral: Rtc = Rtc::new(peripherals.LPWR);
rtc_peripheral.rwdt.disable();
let systimer = SystemTimer::new(peripherals.SYSTIMER);
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,
@@ -241,29 +314,13 @@ impl PlantHal {
// Reserve GPIO1 for deep sleep wake (configured just before entering sleep)
let wake_gpio1 = peripherals.GPIO1;
let rng = Rng::new(peripherals.RNG);
let timg0 = TimerGroup::new(peripherals.TIMG0);
let esp_wifi_ctrl = &*mk_static!(
EspWifiController<'static>,
init(timg0.timer0, rng.clone()).expect("Could not init wifi controller")
);
let (controller, interfaces) =
esp_wifi::wifi::new(&esp_wifi_ctrl, peripherals.WIFI).expect("Could not init wifi");
use esp_hal::timer::systimer::SystemTimer;
esp_hal_embassy::init(systimer.alarm0);
//let mut adc1 = Adc::new(peripherals.ADC1, adc1_config);
//
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 {
// can: peripherals.can,
// adc1: peripherals.adc1,
// pcnt0: peripherals.pcnt0,
// pcnt1: peripherals.pcnt1,
gpio0: peripherals.GPIO0,
gpio2: peripherals.GPIO2,
gpio3: peripherals.GPIO3,
@@ -284,13 +341,7 @@ impl PlantHal {
gpio21: peripherals.GPIO21,
gpio22: peripherals.GPIO22,
gpio23: peripherals.GPIO23,
gpio24: peripherals.GPIO24,
gpio25: peripherals.GPIO25,
gpio26: peripherals.GPIO26,
gpio27: peripherals.GPIO27,
gpio28: peripherals.GPIO28,
gpio29: peripherals.GPIO29,
gpio30: peripherals.GPIO30,
twai: peripherals.TWAI0,
pcnt0: pcnt_module.unit0,
pcnt1: pcnt_module.unit1,
@@ -301,14 +352,19 @@ impl PlantHal {
[u8; esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN],
[0u8; esp_bootloader_esp_idf::partitions::PARTITION_TABLE_MAX_LEN]
);
let storage_ota = mk_static!(FlashStorage, FlashStorage::new());
let pt =
esp_bootloader_esp_idf::partitions::read_partition_table(storage_ota, tablebuffer)?;
// List all partitions - this is just FYI
for i in 0..pt.len() {
info!("{:?}", pt.get_partition(i));
}
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(
@@ -317,34 +373,39 @@ impl PlantHal {
.expect("No OTA data partition found")
);
let ota_data = mk_static!(
FlashRegion<FlashStorage>,
ota_data.as_embedded_storage(storage_ota)
);
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 mut ota = esp_bootloader_esp_idf::ota::Ota::new(ota_data)?;
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)?;
let ota_partition = match ota.current_slot()? {
Slot::None => {
panic!("No OTA slot active?");
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");
}
Slot::Slot0 => pt
.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::App(
AppPartitionSubType::Ota0,
))?
.expect("No OTA slot0 found"),
Slot::Slot1 => pt
.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::App(
AppPartitionSubType::Ota1,
))?
.expect("No OTA slot1 found"),
};
let ota_next = mk_static!(PartitionEntry, ota_partition);
let storage_ota = mk_static!(FlashStorage, FlashStorage::new());
let ota_next = mk_static!(
FlashRegion<FlashStorage>,
ota_next.as_embedded_storage(storage_ota)
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
@@ -354,32 +415,38 @@ impl PlantHal {
.expect("Data partition with littlefs not found");
let data_partition = mk_static!(PartitionEntry, data_partition);
let storage_data = mk_static!(FlashStorage, FlashStorage::new());
let data = mk_static!(
FlashRegion<FlashStorage>,
data_partition.as_embedded_storage(storage_data)
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() {
log::info!("Littlefs2 filesystem is mountable");
info!("Littlefs2 filesystem is mountable");
} else {
match lfs2filesystem.format() {
Result::Ok(..) => {
log::info!("Littlefs2 filesystem is formatted");
Ok(..) => {
info!("Littlefs2 filesystem is formatted");
}
Err(err) => {
bail!("Littlefs2 filesystem could not be formatted: {:?}", 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 ap = interfaces.ap;
let sta = interfaces.sta;
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,
@@ -389,7 +456,12 @@ impl PlantHal {
boot_button,
wake_gpio1,
ota,
ota_next,
ota_target,
current: running,
slot0_state: state_0,
slot1_state: state_1,
uart0,
rtc: rtc_peripheral,
};
//init,reset rtc memory depending on cause
@@ -425,24 +497,21 @@ impl PlantHal {
SocResetReason::Cpu0JtagCpu => "cpu0 jtag cpu",
},
};
LOG_ACCESS
.lock()
.await
.log(
LogMessage::ResetReason,
init_rtc_store as u32,
to_config_mode as u32,
"",
&format!("{reasons:?}"),
)
.await;
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;
log::info!("Init rtc driver");
info!("Init rtc driver");
let sda = peripherals.GPIO20;
let scl = peripherals.GPIO19;
@@ -460,26 +529,30 @@ impl PlantHal {
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 eeprom_device = I2cDevice::new(&i2c_bus);
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) => {
log::info!("Rtc Module reports time at UTC {}", tt);
info!("Rtc Module reports time at UTC {tt}");
}
Err(err) => {
log::info!("Rtc Module could not be read {:?}", err);
info!("Rtc Module could not be read {err:?}");
}
}
@@ -494,40 +567,26 @@ impl PlantHal {
Box::new(DS3231Module { rtc, storage }) as Box<dyn RTCModuleInteraction + Send>;
let hal = match config {
Result::Ok(config) => {
Ok(config) => {
let battery_interaction: Box<dyn BatteryInteraction + Send> =
match config.hardware.battery {
BatteryBoardVersion::Disabled => Box::new(NoBatteryMonitor {}),
BatteryBoardVersion::BQ34Z100G1 => {
let battery_device = I2cDevice::new(I2C_DRIVER.get().await);
let mut battery_driver = Bq34z100g1Driver {
i2c: battery_device,
delay: Delay::new(),
flash_block_data: [0; 32],
};
let status = print_battery_bq34z100(&mut battery_driver);
match status {
Ok(_) => {}
Err(err) => {
LOG_ACCESS
.lock()
.await
.log(
LogMessage::BatteryCommunicationError,
0u32,
0,
"",
&format!("{err:?})"),
)
.await;
}
}
Box::new(BQ34Z100G1 { battery_driver })
}
BatteryBoardVersion::WchI2cSlave => {
// TODO use correct implementation once availible
Box::new(NoBatteryMonitor {})
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 {
@@ -546,17 +605,13 @@ impl PlantHal {
HAL { board_hal }
}
Err(err) => {
LOG_ACCESS
.lock()
.await
.log(
LogMessage::ConfigModeMissingConfig,
0,
0,
"",
&err.to_string(),
)
.await;
log(
LogMessage::ConfigModeMissingConfig,
0,
0,
"",
&err.to_string(),
);
HAL {
board_hal: initial_hal::create_initial_board(
free_pins,
@@ -569,25 +624,13 @@ impl PlantHal {
Ok(Mutex::new(hal))
}
}
pub async fn esp_time() -> DateTime<Utc> {
let guard = TIME_ACCESS.get().await.lock().await;
DateTime::from_timestamp_micros(guard.current_time_us() as i64).unwrap()
}
pub async fn esp_set_time(time: DateTime<FixedOffset>) -> FatResult<()> {
{
let guard = TIME_ACCESS.get().await.lock().await;
guard.set_current_time_us(time.timestamp_micros() as u64);
/// 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();
});
}
}
BOARD_ACCESS
.get()
.await
.lock()
.await
.board_hal
.get_rtc_module()
.set_rtc_time(&time.to_utc())
.await
}