Compare commits
8 Commits
924a9ba228
...
086b0cbe4e
| Author | SHA1 | Date | |
|---|---|---|---|
| 086b0cbe4e | |||
| 39e4e733f3 | |||
| 66e1fe63e0 | |||
| ce981232f0 | |||
| 07cf97fffb | |||
| cca13f51d9 | |||
| 7c128a27eb | |||
| a069888341 |
BIN
Hardware/Sensor_Case/Body_v2.3mf
Normal file
BIN
Hardware/Sensor_Case/Body_v2.3mf
Normal file
Binary file not shown.
@@ -6,7 +6,7 @@ use crate::hal::peripherals::CAN1;
|
||||
use canapi::id::{plant_id, IDENTIFY_CMD_OFFSET, MOISTURE_DATA_OFFSET};
|
||||
use canapi::SensorSlot;
|
||||
use ch32_hal::adc::{Adc, SampleTime, ADC_MAX};
|
||||
use ch32_hal::can;
|
||||
use ch32_hal::{pac};
|
||||
use ch32_hal::can::{Can, CanFifo, CanFilter, CanFrame, CanMode};
|
||||
use ch32_hal::gpio::{Flex, Level, Output, Pull, Speed};
|
||||
use ch32_hal::mode::NonBlocking;
|
||||
@@ -52,7 +52,9 @@ async fn main(spawner: Spawner) {
|
||||
ch32_hal::pac::AFIO.pcfr1().write(|w| w.set_can1_rm(2));
|
||||
|
||||
unsafe {
|
||||
static mut HEAP_SPACE: [u8; 4096] = [0; 4096]; // 4 KiB heap, adjust as needed
|
||||
#[allow(static_mut_refs)]
|
||||
static mut HEAP_SPACE: [u8; 4096] = [0; 4096]; // 4 KiB heap
|
||||
#[allow(static_mut_refs)]
|
||||
HEAP.init(HEAP_SPACE.as_ptr() as usize, HEAP_SPACE.len());
|
||||
}
|
||||
|
||||
@@ -232,7 +234,7 @@ async fn main(spawner: Spawner) {
|
||||
// Create ADC on ADC1 and use PA1 as analog input (Threshold/Trigger)
|
||||
let adc = Adc::new(p.ADC1, Default::default());
|
||||
let ain = p.PA1;
|
||||
let config = can::can::Config::default();
|
||||
let config = Default::default();
|
||||
let can: Can<CAN1, NonBlocking> = Can::new_nb(
|
||||
p.CAN1,
|
||||
p.PB8,
|
||||
@@ -293,7 +295,7 @@ async fn detect_stable_pin(pin: &mut Flex<'static>) -> Option<bool> {
|
||||
}
|
||||
}
|
||||
async fn blink_error_loop(info_led: &mut Output<'static>, warn_led: &mut Output<'static>, c_i: u8, c_w: u8) -> ! {
|
||||
loop {
|
||||
for _loop_count in 0..5 {
|
||||
// code: 1-4 for PB4..PB7, 5 for PB3 (A/B), 7 for CAN address collision
|
||||
for _ in 0..c_i {
|
||||
info_led.set_high();
|
||||
@@ -310,6 +312,25 @@ async fn blink_error_loop(info_led: &mut Output<'static>, warn_led: &mut Output<
|
||||
// Pause between sequences
|
||||
Timer::after_millis(400).await;
|
||||
}
|
||||
|
||||
for _ in 0..5 {
|
||||
info_led.set_high();
|
||||
Timer::after_millis(50).await;
|
||||
info_led.set_low();
|
||||
Timer::after_millis(50).await;
|
||||
warn_led.set_high();
|
||||
Timer::after_millis(50).await;
|
||||
warn_led.set_low();
|
||||
Timer::after_millis(50).await;
|
||||
}
|
||||
|
||||
pac::PFIC.cfgr().modify(|w| {
|
||||
w.set_resetsys(true);
|
||||
w.set_keycode(pac::pfic::vals::Keycode::KEY3); // KEY3 is 0xBEEF, the System Reset key
|
||||
});
|
||||
loop{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#[task]
|
||||
|
||||
1
Software/MainBoard/rust/.idea/vcs.xml
generated
1
Software/MainBoard/rust/.idea/vcs.xml
generated
@@ -2,6 +2,5 @@
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/../../.." vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/../../../website/themes/blowfish" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -13,6 +13,10 @@ test = false
|
||||
bench = false
|
||||
doc = false
|
||||
|
||||
[features]
|
||||
default = ["esp32c6"]
|
||||
esp32c6 = []
|
||||
|
||||
#this strips the bootloader, we need that tho
|
||||
#strip = true
|
||||
|
||||
@@ -51,8 +55,6 @@ esp-storage = { version = "0.8.1", features = ["esp32c6"] }
|
||||
esp-radio = { version = "0.17.0", features = ["esp32c6", "log-04", "smoltcp", "wifi", "unstable"] }
|
||||
esp-alloc = { version = "0.9.0", features = ["esp32c6", "internal-heap-stats"] }
|
||||
|
||||
esp-hal-ota = { version = "0.4.6", features = ["esp32c6"] }
|
||||
|
||||
# Async runtime (Embassy core)
|
||||
embassy-executor = { version = "0.9.1", features = ["log", "nightly"] }
|
||||
embassy-time = { version = "0.5.0", features = ["log"], default-features = false }
|
||||
@@ -89,6 +91,8 @@ edge-nal = "0.5.0"
|
||||
edge-nal-embassy = "0.6.0"
|
||||
edge-http = { version = "0.6.1", features = ["log"] }
|
||||
|
||||
esp32c6 = { version = "0.22.0" }
|
||||
|
||||
# Hardware abstraction traits and HAL adapters
|
||||
embedded-hal = "1.0.0"
|
||||
embedded-storage = "0.3.1"
|
||||
|
||||
BIN
Software/MainBoard/rust/panic_image.bin
Normal file
BIN
Software/MainBoard/rust/panic_image.bin
Normal file
Binary file not shown.
@@ -11,7 +11,6 @@ use embedded_storage::nor_flash::NorFlashErrorKind;
|
||||
use esp_hal::i2c::master::ConfigError;
|
||||
use esp_hal::pcnt::unit::{InvalidHighLimit, InvalidLowLimit};
|
||||
use esp_hal::twai::EspTwaiError;
|
||||
use esp_hal_ota::OtaError;
|
||||
use esp_radio::wifi::WifiError;
|
||||
use ina219::errors::{BusVoltageReadError, ShuntVoltageReadError};
|
||||
use lib_bms_protocol::BmsProtocolError;
|
||||
@@ -53,6 +52,7 @@ pub enum FatError {
|
||||
SpawnError {
|
||||
error: SpawnError,
|
||||
},
|
||||
OTAError,
|
||||
PartitionError {
|
||||
error: esp_bootloader_esp_idf::partitions::Error,
|
||||
},
|
||||
@@ -74,9 +74,6 @@ pub enum FatError {
|
||||
SNTPError {
|
||||
error: sntpc::Error,
|
||||
},
|
||||
OtaError {
|
||||
error: OtaError,
|
||||
},
|
||||
}
|
||||
|
||||
pub type FatResult<T> = Result<T, FatError>;
|
||||
@@ -110,7 +107,9 @@ impl fmt::Display for FatError {
|
||||
}
|
||||
FatError::SNTPError { error } => write!(f, "SNTPError {error:?}"),
|
||||
FatError::BMSError { error } => write!(f, "BMSError, {error}"),
|
||||
FatError::OtaError { error } => write!(f, "OtaError {error:?}"),
|
||||
FatError::OTAError => {
|
||||
write!(f, "OTA missing partition")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -337,9 +336,3 @@ impl From<BmsProtocolError> for FatError {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<OtaError> for FatError {
|
||||
fn from(value: OtaError) -> Self {
|
||||
FatError::OtaError { error: value }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem;
|
||||
use crate::hal::shared_flash::MutexFlashStorage;
|
||||
use alloc::string::ToString;
|
||||
use alloc::sync::Arc;
|
||||
use alloc::{format, string::String, vec::Vec};
|
||||
use alloc::{format, string::String, vec, vec::Vec};
|
||||
use core::net::{IpAddr, Ipv4Addr, SocketAddr};
|
||||
use core::str::FromStr;
|
||||
use core::sync::atomic::Ordering;
|
||||
@@ -21,6 +21,10 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex;
|
||||
use embassy_sync::mutex::{Mutex, MutexGuard};
|
||||
use embassy_sync::once_lock::OnceLock;
|
||||
use embassy_time::{Duration, Timer, WithTimeout};
|
||||
use embedded_storage::nor_flash::{check_erase, NorFlash, ReadNorFlash, RmwNorFlashStorage};
|
||||
use esp_bootloader_esp_idf::ota::OtaImageState::Valid;
|
||||
use esp_bootloader_esp_idf::ota::{Ota, OtaImageState};
|
||||
use esp_bootloader_esp_idf::partitions::{AppPartitionSubType, FlashRegion};
|
||||
use esp_hal::gpio::{Input, RtcPinWithResistors};
|
||||
use esp_hal::rng::Rng;
|
||||
use esp_hal::rtc_cntl::{
|
||||
@@ -30,7 +34,6 @@ use esp_hal::rtc_cntl::{
|
||||
use esp_hal::system::software_reset;
|
||||
use esp_hal::uart::Uart;
|
||||
use esp_hal::Blocking;
|
||||
use esp_hal_ota::Ota;
|
||||
use esp_println::println;
|
||||
use esp_radio::wifi::{
|
||||
AccessPointConfig, AccessPointInfo, AuthMethod, ClientConfig, ModeConfig, ScanConfig,
|
||||
@@ -126,7 +129,12 @@ pub struct Esp<'a> {
|
||||
// RTC-capable GPIO used as external wake source (store the raw peripheral)
|
||||
pub wake_gpio1: esp_hal::peripherals::GPIO1<'static>,
|
||||
pub uart0: Uart<'a, Blocking>,
|
||||
pub ota: &'static mut Ota<&'static mut MutexFlashStorage>,
|
||||
|
||||
pub ota: Ota<'static, RmwNorFlashStorage<'static, &'static mut MutexFlashStorage>>,
|
||||
pub ota_target: &'static mut FlashRegion<'static, MutexFlashStorage>,
|
||||
pub current: AppPartitionSubType,
|
||||
pub slot0_state: OtaImageState,
|
||||
pub slot1_state: OtaImageState,
|
||||
}
|
||||
|
||||
// SAFETY: On this target we never move Esp across OS threads; the firmware runs single-core
|
||||
@@ -241,6 +249,49 @@ impl Esp<'_> {
|
||||
Ok((buf, read))
|
||||
}
|
||||
|
||||
pub(crate) async fn write_ota(&mut self, offset: u32, buf: &[u8]) -> Result<(), FatError> {
|
||||
let _ = check_erase(self.ota_target, offset, offset + 4096);
|
||||
info!("erasing and writing block 0x{offset:x}");
|
||||
self.ota_target.erase(offset, offset + 4096)?;
|
||||
|
||||
let mut temp = vec![0; buf.len()];
|
||||
let read_back = temp.as_mut_slice();
|
||||
//change to nor flash, align writes!
|
||||
self.ota_target.write(offset, buf)?;
|
||||
self.ota_target.read(offset, read_back)?;
|
||||
if buf != read_back {
|
||||
info!("Expected {buf:?} but got {read_back:?}");
|
||||
bail!(
|
||||
"Flash error, read back does not match write buffer at offset {:x}",
|
||||
offset
|
||||
)
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn finalize_ota(&mut self) -> Result<(), FatError> {
|
||||
let current = self.ota.current_app_partition()?;
|
||||
if self.ota.current_ota_state()? != Valid {
|
||||
info!("Validating current slot {current:?} as it was able to ota");
|
||||
self.ota.set_current_ota_state(Valid)?;
|
||||
}
|
||||
let next = match current {
|
||||
AppPartitionSubType::Ota0 => AppPartitionSubType::Ota1,
|
||||
AppPartitionSubType::Ota1 => AppPartitionSubType::Ota0,
|
||||
_ => {
|
||||
bail!("Invalid current slot {current:?} for ota");
|
||||
}
|
||||
};
|
||||
self.ota.set_current_app_partition(next)?;
|
||||
info!("switched slot");
|
||||
self.ota.set_current_ota_state(OtaImageState::New)?;
|
||||
info!("switched state for new partition");
|
||||
let state_new = self.ota.current_ota_state()?;
|
||||
info!("state on new partition now {state_new:?}");
|
||||
self.set_restart_to_conf(true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) fn mode_override_pressed(&mut self) -> bool {
|
||||
self.boot_button.is_low()
|
||||
}
|
||||
@@ -541,7 +592,17 @@ impl Esp<'_> {
|
||||
duration_in_ms: u64,
|
||||
mut rtc: MutexGuard<CriticalSectionRawMutex, Rtc>,
|
||||
) -> ! {
|
||||
//TODO HERE Mark the current OTA image as valid if we reached here while in pending verify.
|
||||
// Mark the current OTA image as valid if we reached here while in pending verify.
|
||||
if let Ok(cur) = self.ota.current_ota_state() {
|
||||
if cur == OtaImageState::PendingVerify {
|
||||
info!("Marking OTA image as valid");
|
||||
self.ota
|
||||
.set_current_ota_state(Valid)
|
||||
.expect("Could not set image to valid");
|
||||
}
|
||||
} else {
|
||||
info!("No OTA image to mark as valid");
|
||||
}
|
||||
|
||||
if duration_in_ms == 0 {
|
||||
software_reset();
|
||||
|
||||
@@ -65,20 +65,26 @@ 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 esp_bootloader_esp_idf::partitions::{DataPartitionSubType, FlashRegion, PartitionEntry};
|
||||
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 measurements::{Current, Voltage};
|
||||
|
||||
use crate::fat_error::{FatError, FatResult};
|
||||
use crate::fat_error::{ContextExt, FatError, FatResult};
|
||||
use crate::hal::battery::WCHI2CSlave;
|
||||
use crate::hal::little_fs2storage_adapter::LittleFs2Filesystem;
|
||||
use crate::hal::water::TankSensor;
|
||||
use crate::log::LOG_ACCESS;
|
||||
use embassy_sync::mutex::Mutex;
|
||||
use embassy_sync::once_lock::OnceLock;
|
||||
use embedded_storage::nor_flash::RmwNorFlashStorage;
|
||||
use embedded_storage::ReadStorage;
|
||||
use esp_alloc as _;
|
||||
use esp_backtrace as _;
|
||||
use esp_bootloader_esp_idf::ota::{Ota, OtaImageState};
|
||||
use esp_hal::delay::Delay;
|
||||
use esp_hal::i2c::master::{BusTimeout, Config, I2c};
|
||||
use esp_hal::interrupt::software::SoftwareInterruptControl;
|
||||
@@ -91,7 +97,6 @@ use esp_hal::time::Rate;
|
||||
use esp_hal::timer::timg::TimerGroup;
|
||||
use esp_hal::uart::Uart;
|
||||
use esp_hal::Blocking;
|
||||
use esp_hal_ota::Ota;
|
||||
use esp_radio::{init, Controller};
|
||||
use esp_storage::FlashStorage;
|
||||
use littlefs2::fs::{Allocation, Filesystem as lfs2Filesystem};
|
||||
@@ -315,15 +320,51 @@ impl PlantHal {
|
||||
let pt =
|
||||
esp_bootloader_esp_idf::partitions::read_partition_table(flash_storage, tablebuffer)?;
|
||||
|
||||
let ota = mk_static!(
|
||||
Ota<&mut MutexFlashStorage>,
|
||||
Ota::new(flash_storage_2).unwrap()
|
||||
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 ota_data = mk_static!(
|
||||
FlashRegion<RmwNorFlashStorage<&mut MutexFlashStorage>>,
|
||||
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, ota_data);
|
||||
let state_1 = ota_state(AppPartitionSubType::Ota1, 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)
|
||||
);
|
||||
info!("Ota initialized");
|
||||
let selected = ota.get_currently_booted_partition();
|
||||
let ota_state = ota.get_ota_image_state();
|
||||
info!("Currently running partition slot: {selected:?}");
|
||||
info!("Slot state: {ota_state:?}");
|
||||
|
||||
let data_partition = pt
|
||||
.find_partition(esp_bootloader_esp_idf::partitions::PartitionType::Data(
|
||||
@@ -372,6 +413,10 @@ impl PlantHal {
|
||||
boot_button,
|
||||
wake_gpio1,
|
||||
ota,
|
||||
ota_target,
|
||||
current: running,
|
||||
slot0_state: state_0,
|
||||
slot1_state: state_1,
|
||||
uart0,
|
||||
};
|
||||
|
||||
@@ -502,8 +547,8 @@ impl PlantHal {
|
||||
// initial_hal::create_initial_board(free_pins, config, esp)?
|
||||
//}
|
||||
//BoardVersion::V4 => {
|
||||
v4_hal::create_v4(free_pins, esp, config, battery_interaction, rtc_module)
|
||||
.await?;
|
||||
v4_hal::create_v4(free_pins, esp, config, battery_interaction, rtc_module)
|
||||
.await?;
|
||||
//}
|
||||
//};
|
||||
|
||||
@@ -538,6 +583,71 @@ impl PlantHal {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
@@ -3,27 +3,15 @@ use core::cell::RefCell;
|
||||
use core::ops::{Deref, DerefMut};
|
||||
use embassy_sync::blocking_mutex::CriticalSectionMutex;
|
||||
use embedded_storage::nor_flash::{ErrorType, NorFlash, ReadNorFlash};
|
||||
use embedded_storage::{ReadStorage, Storage};
|
||||
use embedded_storage::ReadStorage;
|
||||
use esp_storage::{FlashStorage, FlashStorageError};
|
||||
use log::info;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct MutexFlashStorage {
|
||||
pub(crate) inner: Arc<CriticalSectionMutex<RefCell<FlashStorage<'static>>>>,
|
||||
}
|
||||
|
||||
impl Storage for MutexFlashStorage {
|
||||
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
self.inner
|
||||
.lock(|f| NorFlash::write(f.borrow_mut().deref_mut(), offset, bytes))
|
||||
}
|
||||
}
|
||||
|
||||
impl Storage for &mut MutexFlashStorage {
|
||||
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
Storage::write(&mut (**self), offset, bytes)
|
||||
}
|
||||
}
|
||||
|
||||
impl ReadStorage for MutexFlashStorage {
|
||||
type Error = FlashStorageError;
|
||||
|
||||
@@ -38,29 +26,9 @@ impl ReadStorage for MutexFlashStorage {
|
||||
}
|
||||
}
|
||||
|
||||
impl ReadStorage for &mut MutexFlashStorage {
|
||||
type Error = FlashStorageError;
|
||||
|
||||
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), FlashStorageError> {
|
||||
ReadStorage::read(&mut (**self), offset, bytes)
|
||||
}
|
||||
|
||||
fn capacity(&self) -> usize {
|
||||
ReadStorage::capacity(&(**self))
|
||||
}
|
||||
}
|
||||
|
||||
impl ReadStorage for &MutexFlashStorage {
|
||||
type Error = FlashStorageError;
|
||||
|
||||
fn read(&mut self, offset: u32, bytes: &mut [u8]) -> Result<(), FlashStorageError> {
|
||||
self.inner
|
||||
.lock(|f| ReadStorage::read(f.borrow_mut().deref_mut(), offset, bytes))
|
||||
}
|
||||
|
||||
fn capacity(&self) -> usize {
|
||||
self.inner
|
||||
.lock(|f| ReadStorage::capacity(f.borrow().deref()))
|
||||
impl embedded_storage::Storage for MutexFlashStorage {
|
||||
fn write(&mut self, offset: u32, bytes: &[u8]) -> Result<(), Self::Error> {
|
||||
NorFlash::write(self, offset, bytes)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,10 +36,6 @@ impl ErrorType for MutexFlashStorage {
|
||||
type Error = FlashStorageError;
|
||||
}
|
||||
|
||||
impl ErrorType for &MutexFlashStorage {
|
||||
type Error = FlashStorageError;
|
||||
}
|
||||
|
||||
impl ReadNorFlash for MutexFlashStorage {
|
||||
const READ_SIZE: usize = 1;
|
||||
|
||||
@@ -89,6 +53,7 @@ impl NorFlash for MutexFlashStorage {
|
||||
const ERASE_SIZE: usize = 4096;
|
||||
|
||||
fn erase(&mut self, from: u32, to: u32) -> Result<(), Self::Error> {
|
||||
info!("Erasing flash from 0x{:x} to 0x{:x}", from, to);
|
||||
self.inner
|
||||
.lock(|f| NorFlash::erase(f.borrow_mut().deref_mut(), from, to))
|
||||
}
|
||||
|
||||
@@ -377,7 +377,7 @@ impl<'a> BoardInteraction<'a> for V4<'a> {
|
||||
|
||||
async fn detect_sensors(&mut self, request: Detection) -> FatResult<Detection> {
|
||||
self.can_power.set_high();
|
||||
Timer::after_millis(1000).await;
|
||||
Timer::after_millis(500).await;
|
||||
let config = self.twai_config.take().expect("twai config not set");
|
||||
let mut twai = config.into_async().start();
|
||||
|
||||
|
||||
@@ -42,7 +42,6 @@ use embassy_sync::once_lock::OnceLock;
|
||||
use embassy_time::{Duration, Instant, Timer, WithTimeout};
|
||||
use esp_hal::rom::ets_delay_us;
|
||||
use esp_hal::system::software_reset;
|
||||
use esp_hal_ota::OtaImgState;
|
||||
use esp_println::{logger, println};
|
||||
use hal::battery::BatteryState;
|
||||
use log::LogMessage;
|
||||
@@ -529,7 +528,7 @@ async fn safe_main(spawner: Spawner) -> FatResult<()> {
|
||||
match battery_state {
|
||||
BatteryState::Unknown => {
|
||||
light_state.battery_low = false;
|
||||
},
|
||||
}
|
||||
BatteryState::Info(data) => {
|
||||
if data.state_of_charge < board.board_hal.get_config().night_lamp.low_soc_cutoff {
|
||||
board.board_hal.get_esp().set_low_voltage_in_cycle();
|
||||
@@ -1038,7 +1037,8 @@ async fn wait_infinity(
|
||||
exit_hold_blink = !exit_hold_blink;
|
||||
|
||||
let progress = core::cmp::min(elapsed, exit_hold_duration);
|
||||
let lit = ((progress.as_millis() as u64 * 8) / exit_hold_duration.as_millis() as u64)
|
||||
let lit = ((progress.as_millis() as u64 * 8)
|
||||
/ exit_hold_duration.as_millis() as u64)
|
||||
.saturating_add(1)
|
||||
.min(8) as usize;
|
||||
|
||||
@@ -1052,9 +1052,7 @@ async fn wait_infinity(
|
||||
board.board_hal.get_esp().set_restart_to_conf(false);
|
||||
// ensure clean http answer / visible confirmation
|
||||
Timer::after_millis(500).await;
|
||||
board.board_hal
|
||||
.deep_sleep(0)
|
||||
.await;
|
||||
board.board_hal.deep_sleep(0).await;
|
||||
}
|
||||
|
||||
// Short tick while holding so the pattern updates smoothly.
|
||||
@@ -1208,7 +1206,6 @@ async fn main(spawner: Spawner) -> ! {
|
||||
}
|
||||
}
|
||||
println!("Hal init done, starting logic");
|
||||
|
||||
match safe_main(spawner).await {
|
||||
// this should not get triggered, safe_main should not return but go into deep sleep or reboot
|
||||
Ok(_) => {
|
||||
@@ -1238,35 +1235,13 @@ async fn get_version(
|
||||
let branch = env!("VERGEN_GIT_BRANCH").to_owned();
|
||||
let hash = &env!("VERGEN_GIT_SHA")[0..8];
|
||||
|
||||
let esp = board.board_hal.get_esp();
|
||||
let current_partition: String = match esp.ota.get_currently_booted_partition() {
|
||||
Some(partition) => match partition {
|
||||
0 => "OTA0".to_string(),
|
||||
1 => "OTA1".to_string(),
|
||||
_ => format!("Pentry: {:?}", partition),
|
||||
},
|
||||
None => "Missing otainfo".to_string(),
|
||||
};
|
||||
let state = esp.ota.get_ota_image_state();
|
||||
let current_state: String = match state {
|
||||
Ok(state) => match state {
|
||||
OtaImgState::EspOtaImgNew => "New".to_string(),
|
||||
OtaImgState::EspOtaImgPendingVerify => "PendingVerify".to_string(),
|
||||
OtaImgState::EspOtaImgValid => "Valid".to_string(),
|
||||
OtaImgState::EspOtaImgInvalid => "Invalid".to_string(),
|
||||
OtaImgState::EspOtaImgAborted => "Aborted".to_string(),
|
||||
OtaImgState::EspOtaImgUndefined => "Undefined".to_string(),
|
||||
},
|
||||
Err(err) => {
|
||||
format!("Error: {:?}", err)
|
||||
}
|
||||
};
|
||||
|
||||
let board = board.board_hal.get_esp();
|
||||
VersionInfo {
|
||||
git_hash: branch + "@" + hash,
|
||||
build_time: env!("VERGEN_BUILD_TIMESTAMP").to_owned(),
|
||||
current: format!("{:?}", current_partition),
|
||||
state: format!("{:?}", current_state),
|
||||
current: format!("{:?}", board.current),
|
||||
slot0_state: format!("{:?}", board.slot0_state),
|
||||
slot1_state: format!("{:?}", board.slot1_state),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1275,5 +1250,6 @@ struct VersionInfo {
|
||||
git_hash: String,
|
||||
build_time: String,
|
||||
current: String,
|
||||
state: String,
|
||||
slot0_state: String,
|
||||
slot1_state: String,
|
||||
}
|
||||
|
||||
@@ -4,8 +4,7 @@ use crate::BOARD_ACCESS;
|
||||
use edge_http::io::server::Connection;
|
||||
use edge_http::Method;
|
||||
use edge_nal::io::{Read, Write};
|
||||
use esp_hal_ota::OtaError;
|
||||
use log::{error, info};
|
||||
use log::info;
|
||||
|
||||
pub(crate) async fn ota_operations<T, const N: usize>(
|
||||
conn: &mut Connection<'_, T, { N }>,
|
||||
@@ -29,45 +28,26 @@ where
|
||||
Some(200)
|
||||
}
|
||||
Method::Post => {
|
||||
let size = read_up_to_bytes_from_request(conn, Some(4)).await?;
|
||||
let flash_size = u32::from_le_bytes(size[..4].try_into().unwrap());
|
||||
info!("flash size: {flash_size}");
|
||||
let crc32 = read_up_to_bytes_from_request(conn, Some(4)).await?;
|
||||
let target_crc = u32::from_le_bytes(crc32[..4].try_into().unwrap());
|
||||
info!("crc32: {target_crc}");
|
||||
|
||||
BOARD_ACCESS
|
||||
.get()
|
||||
.await
|
||||
.lock()
|
||||
.await
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.ota
|
||||
.ota_begin(flash_size, target_crc)?;
|
||||
|
||||
let mut offset = 0_usize;
|
||||
let mut chunk = 0;
|
||||
loop {
|
||||
let buf = read_up_to_bytes_from_request(conn, Some(4096)).await?;
|
||||
if buf.is_empty() {
|
||||
error!("Upload finished, but no enough data received.");
|
||||
return Err(OtaError::WrongCRC)?;
|
||||
info!("file request for ota finished");
|
||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||
board.board_hal.get_esp().finalize_ota().await?;
|
||||
break;
|
||||
} else {
|
||||
let mut board = BOARD_ACCESS.get().await.lock().await;
|
||||
board.board_hal.progress(chunk as u32).await;
|
||||
// Erase next block if we are at a 4K boundary (including the first block at offset 0)
|
||||
let finsihed = board.board_hal.get_esp().ota.ota_write_chunk(&buf)?;
|
||||
if finsihed {
|
||||
board.board_hal.get_esp().ota.ota_flush(true, true)?;
|
||||
|
||||
board.board_hal.get_esp().set_restart_to_conf(true);
|
||||
break;
|
||||
}
|
||||
info!(
|
||||
"Progress: {}%",
|
||||
(board.board_hal.get_esp().ota.get_ota_progress() * 100.0) as u8
|
||||
);
|
||||
board
|
||||
.board_hal
|
||||
.get_esp()
|
||||
.write_ota(offset as u32, &buf)
|
||||
.await?;
|
||||
}
|
||||
offset += buf.len();
|
||||
chunk += 1;
|
||||
}
|
||||
BOARD_ACCESS
|
||||
|
||||
@@ -163,7 +163,8 @@ export interface VersionInfo {
|
||||
git_hash: string,
|
||||
build_time: string,
|
||||
current: string,
|
||||
state: string
|
||||
slot0_state: string,
|
||||
slot1_state: string,
|
||||
}
|
||||
|
||||
export interface BatteryState {
|
||||
|
||||
@@ -415,9 +415,9 @@ export class Controller {
|
||||
|
||||
var pretty = JSON.stringify(detection, undefined, 1);
|
||||
|
||||
fetch(PUBLIC_URL + "/detect_sensors", {method: "POST", body: pretty})
|
||||
fetch(PUBLIC_URL + "/detect_sensors", { method: "POST", body: pretty })
|
||||
.then(response => response.json())
|
||||
.then(json => json as Detection)
|
||||
.then (json => json as Detection)
|
||||
.then(json => {
|
||||
clearTimeout(timerId);
|
||||
controller.progressview.removeProgress("detect_sensors");
|
||||
@@ -568,7 +568,7 @@ export class Controller {
|
||||
|
||||
private setCanPower(checked: boolean) {
|
||||
var body: CanPower = {
|
||||
state: checked
|
||||
state : checked
|
||||
}
|
||||
var pretty = JSON.stringify(body, undefined, 1);
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ export class OTAView {
|
||||
readonly firmware_buildtime: HTMLDivElement;
|
||||
readonly firmware_githash: HTMLDivElement;
|
||||
readonly firmware_partition: HTMLDivElement;
|
||||
readonly firmware_state: HTMLDivElement;
|
||||
|
||||
readonly firmware_state0: HTMLDivElement;
|
||||
readonly firmware_state1: HTMLDivElement;
|
||||
|
||||
constructor(controller: Controller) {
|
||||
(document.getElementById("firmwareview") as HTMLElement).innerHTML = require("./ota.html")
|
||||
@@ -18,7 +18,8 @@ export class OTAView {
|
||||
this.firmware_githash = document.getElementById("firmware_githash") as HTMLDivElement;
|
||||
this.firmware_partition = document.getElementById("firmware_partition") as HTMLDivElement;
|
||||
|
||||
this.firmware_state = document.getElementById("firmware_state") as HTMLDivElement;
|
||||
this.firmware_state0 = document.getElementById("firmware_state0") as HTMLDivElement;
|
||||
this.firmware_state1 = document.getElementById("firmware_state1") as HTMLDivElement;
|
||||
|
||||
|
||||
const file = document.getElementById("firmware_file") as HTMLInputElement;
|
||||
@@ -41,6 +42,7 @@ export class OTAView {
|
||||
this.firmware_buildtime.innerText = versionInfo.build_time;
|
||||
this.firmware_githash.innerText = versionInfo.git_hash;
|
||||
this.firmware_partition.innerText = versionInfo.current;
|
||||
this.firmware_state.innerText = versionInfo.state;
|
||||
this.firmware_state0.innerText = versionInfo.slot0_state;
|
||||
this.firmware_state1.innerText = versionInfo.slot1_state;
|
||||
}
|
||||
}
|
||||
Submodule website/themes/blowfish updated: f9eb1d4e81...26d1205439
Reference in New Issue
Block a user