warnings to zero :)
This commit is contained in:
		@@ -86,6 +86,7 @@ bincode = "1.3.3"
 | 
				
			|||||||
ringbuffer = "0.15.0"
 | 
					ringbuffer = "0.15.0"
 | 
				
			||||||
text-template = "0.1.0"
 | 
					text-template = "0.1.0"
 | 
				
			||||||
strum_macros = "0.27.0"
 | 
					strum_macros = "0.27.0"
 | 
				
			||||||
 | 
					esp-ota = { version = "0.2.2", features = ["log"] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[patch.crates-io]
 | 
					[patch.crates-io]
 | 
				
			||||||
@@ -94,7 +95,6 @@ strum_macros = "0.27.0"
 | 
				
			|||||||
#esp-idf-sys = { git = "https://github.com/empirephoenix/esp-idf-sys.git" }
 | 
					#esp-idf-sys = { git = "https://github.com/empirephoenix/esp-idf-sys.git" }
 | 
				
			||||||
#esp-idf-sys = { git = "https://github.com/esp-rs/esp-idf-sys.git" }
 | 
					#esp-idf-sys = { git = "https://github.com/esp-rs/esp-idf-sys.git" }
 | 
				
			||||||
#esp-idf-svc = { git = "https://github.com/esp-rs/esp-idf-svc.git" }
 | 
					#esp-idf-svc = { git = "https://github.com/esp-rs/esp-idf-svc.git" }
 | 
				
			||||||
ds323x = { git = "https://github.com/empirephoenix/ds323x-rs.git" }
 | 
					 | 
				
			||||||
#bq34z100 = { path = "../../bq34z100_rust" }
 | 
					#bq34z100 = { path = "../../bq34z100_rust" }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[build-dependencies]
 | 
					[build-dependencies]
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,266 +0,0 @@
 | 
				
			|||||||
use core::fmt;
 | 
					 | 
				
			||||||
use core::mem;
 | 
					 | 
				
			||||||
use core::ptr;
 | 
					 | 
				
			||||||
use esp_idf_sys::{
 | 
					 | 
				
			||||||
    esp_ota_abort, esp_ota_begin, esp_ota_end, esp_ota_get_next_update_partition, esp_ota_handle_t,
 | 
					 | 
				
			||||||
    esp_ota_mark_app_invalid_rollback_and_reboot, esp_ota_mark_app_valid_cancel_rollback,
 | 
					 | 
				
			||||||
    esp_ota_set_boot_partition, esp_ota_write, esp_partition_t, esp_restart, ESP_ERR_FLASH_OP_FAIL,
 | 
					 | 
				
			||||||
    ESP_ERR_FLASH_OP_TIMEOUT, ESP_ERR_INVALID_ARG, ESP_ERR_INVALID_SIZE, ESP_ERR_INVALID_STATE,
 | 
					 | 
				
			||||||
    ESP_ERR_NOT_FOUND, ESP_ERR_NO_MEM, ESP_ERR_OTA_PARTITION_CONFLICT, ESP_ERR_OTA_ROLLBACK_FAILED,
 | 
					 | 
				
			||||||
    ESP_ERR_OTA_ROLLBACK_INVALID_STATE, ESP_ERR_OTA_SELECT_INFO_INVALID,
 | 
					 | 
				
			||||||
    ESP_ERR_OTA_VALIDATE_FAILED, ESP_FAIL, ESP_OK, OTA_SIZE_UNKNOWN,
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub type Result<T> = core::result::Result<T, Error>;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// An error that can happen during ESP OTA operations.
 | 
					 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
pub struct Error {
 | 
					 | 
				
			||||||
    kind: ErrorKind,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Error {
 | 
					 | 
				
			||||||
    pub(crate) fn from_kind(kind: ErrorKind) -> Self {
 | 
					 | 
				
			||||||
        Self { kind }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Returns the kind of error as an enum, that can be matched on.
 | 
					 | 
				
			||||||
    pub fn kind(&self) -> ErrorKind {
 | 
					 | 
				
			||||||
        self.kind
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl fmt::Display for Error {
 | 
					 | 
				
			||||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
					 | 
				
			||||||
        self.kind.fmt(f)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl std::error::Error for Error {}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
 | 
					 | 
				
			||||||
#[non_exhaustive]
 | 
					 | 
				
			||||||
pub enum ErrorKind {
 | 
					 | 
				
			||||||
    /// No suitable partition for writing OTA update to found.
 | 
					 | 
				
			||||||
    NoOtaPartition,
 | 
					 | 
				
			||||||
    /// Cannot allocate memory for OTA operation.
 | 
					 | 
				
			||||||
    AllocFailed,
 | 
					 | 
				
			||||||
    /// Rollback enabled, but the currently running application is still pending. The currently
 | 
					 | 
				
			||||||
    /// running application must confirm itself before downloading and flashing a new app.
 | 
					 | 
				
			||||||
    InvalidRollbackState,
 | 
					 | 
				
			||||||
    /// First byte of image contains invalid app image magic byte.
 | 
					 | 
				
			||||||
    InvalidMagicByte,
 | 
					 | 
				
			||||||
    /// Flash write operation timed out.
 | 
					 | 
				
			||||||
    FlashTimeout,
 | 
					 | 
				
			||||||
    /// Flash write operation failed.
 | 
					 | 
				
			||||||
    FlashFailed,
 | 
					 | 
				
			||||||
    /// OTA data partition has invalid contents.
 | 
					 | 
				
			||||||
    InvalidOtaPartitionData,
 | 
					 | 
				
			||||||
    /// The [`OtaUpdate`] handle was finalized before any app image was written to it.
 | 
					 | 
				
			||||||
    NothingWritten,
 | 
					 | 
				
			||||||
    /// OTA image is invalid (either not a valid app image, or - if secure boot is enabled - signature failed to verify.)
 | 
					 | 
				
			||||||
    InvalidImage,
 | 
					 | 
				
			||||||
    /// If flash encryption is enabled, this result indicates an internal error writing the final encrypted bytes to flash.
 | 
					 | 
				
			||||||
    WritingEncryptedFailed,
 | 
					 | 
				
			||||||
    /// The rollback failed.
 | 
					 | 
				
			||||||
    RollbackFailed,
 | 
					 | 
				
			||||||
    /// The rollback is not possible due to flash does not have any apps.
 | 
					 | 
				
			||||||
    RollbackFailedNoApps,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl fmt::Display for ErrorKind {
 | 
					 | 
				
			||||||
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
 | 
					 | 
				
			||||||
        use ErrorKind::*;
 | 
					 | 
				
			||||||
        match self {
 | 
					 | 
				
			||||||
            NoOtaPartition => "No suitable partition for writing OTA update to found",
 | 
					 | 
				
			||||||
            AllocFailed => "Cannot allocate memory for OTA operation",
 | 
					 | 
				
			||||||
            InvalidRollbackState => {
 | 
					 | 
				
			||||||
                "Rollback enabled, but the currently running application is still pending"
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            InvalidMagicByte => "First byte of image contains invalid app image magic byte",
 | 
					 | 
				
			||||||
            FlashTimeout => "Flash write operation timed out",
 | 
					 | 
				
			||||||
            FlashFailed => "Flash write operation failed",
 | 
					 | 
				
			||||||
            InvalidOtaPartitionData => "OTA data partition has invalid contents",
 | 
					 | 
				
			||||||
            NothingWritten => "OtaUpdate was never written to",
 | 
					 | 
				
			||||||
            InvalidImage => "OTA image is invalid",
 | 
					 | 
				
			||||||
            WritingEncryptedFailed => "Internal error writing the final encrypted bytes to flash",
 | 
					 | 
				
			||||||
            RollbackFailed => "The rollback failed",
 | 
					 | 
				
			||||||
            RollbackFailedNoApps => {
 | 
					 | 
				
			||||||
                "The rollback is not possible due to flash does not have any apps"
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        .fmt(f)
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Represents an ongoing OTA update.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// Dropping this object before calling [`finalize`](OtaUpdate::finalize) will abort the update.
 | 
					 | 
				
			||||||
#[derive(Debug)]
 | 
					 | 
				
			||||||
pub struct OtaUpdate {
 | 
					 | 
				
			||||||
    partition: *const esp_partition_t,
 | 
					 | 
				
			||||||
    ota_handle: esp_ota_handle_t,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl OtaUpdate {
 | 
					 | 
				
			||||||
    /// Starts an OTA update to the next OTA compatible partition.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// Finds next partition round-robin, starting from the current running partition.
 | 
					 | 
				
			||||||
    /// The entire partition is erased.
 | 
					 | 
				
			||||||
    pub fn begin() -> Result<Self> {
 | 
					 | 
				
			||||||
        let partition = unsafe { esp_ota_get_next_update_partition(ptr::null()) };
 | 
					 | 
				
			||||||
        if partition.is_null() {
 | 
					 | 
				
			||||||
            return Err(Error::from_kind(ErrorKind::NoOtaPartition));
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let mut ota_handle = 0;
 | 
					 | 
				
			||||||
        match unsafe { esp_ota_begin(partition, OTA_SIZE_UNKNOWN as usize, &mut ota_handle) } {
 | 
					 | 
				
			||||||
            ESP_OK => Ok(()),
 | 
					 | 
				
			||||||
            ESP_ERR_INVALID_ARG => panic!("Invalid partition or out_handle"),
 | 
					 | 
				
			||||||
            ESP_ERR_NO_MEM => Err(Error::from_kind(ErrorKind::AllocFailed)),
 | 
					 | 
				
			||||||
            ESP_ERR_OTA_PARTITION_CONFLICT => Err(Error::from_kind(ErrorKind::NoOtaPartition)),
 | 
					 | 
				
			||||||
            ESP_ERR_NOT_FOUND => panic!("Partition argument not found in partition table"),
 | 
					 | 
				
			||||||
            ESP_ERR_OTA_SELECT_INFO_INVALID => {
 | 
					 | 
				
			||||||
                Err(Error::from_kind(ErrorKind::InvalidOtaPartitionData))
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            ESP_ERR_INVALID_SIZE => panic!("Partition doesn’t fit in configured flash size"),
 | 
					 | 
				
			||||||
            ESP_ERR_FLASH_OP_TIMEOUT => Err(Error::from_kind(ErrorKind::FlashTimeout)),
 | 
					 | 
				
			||||||
            ESP_ERR_FLASH_OP_FAIL => Err(Error::from_kind(ErrorKind::FlashFailed)),
 | 
					 | 
				
			||||||
            ESP_ERR_OTA_ROLLBACK_INVALID_STATE => {
 | 
					 | 
				
			||||||
                Err(Error::from_kind(ErrorKind::InvalidRollbackState))
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            code => panic!("Unexpected esp_ota_begin return code: {}", code),
 | 
					 | 
				
			||||||
        }?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(Self {
 | 
					 | 
				
			||||||
            partition,
 | 
					 | 
				
			||||||
            ota_handle,
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Write app image data to partition.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// This method can be called multiple times as data is received during the OTA operation.
 | 
					 | 
				
			||||||
    /// Data is written sequentially to the partition.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// The format of the app image can be read about in the main README and crate documentation.
 | 
					 | 
				
			||||||
    pub fn write(&mut self, app_image_chunk: &[u8]) -> Result<()> {
 | 
					 | 
				
			||||||
        let chunk_ptr = app_image_chunk.as_ptr() as *const _;
 | 
					 | 
				
			||||||
        let chunk_len = app_image_chunk.len();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        match unsafe { esp_ota_write(self.ota_handle, chunk_ptr, chunk_len) } {
 | 
					 | 
				
			||||||
            ESP_OK => Ok(()),
 | 
					 | 
				
			||||||
            ESP_ERR_INVALID_ARG => panic!("Invalid OTA handle"),
 | 
					 | 
				
			||||||
            ESP_ERR_OTA_VALIDATE_FAILED => Err(Error::from_kind(ErrorKind::InvalidMagicByte)),
 | 
					 | 
				
			||||||
            ESP_ERR_FLASH_OP_TIMEOUT => Err(Error::from_kind(ErrorKind::FlashTimeout)),
 | 
					 | 
				
			||||||
            ESP_ERR_FLASH_OP_FAIL => Err(Error::from_kind(ErrorKind::FlashFailed)),
 | 
					 | 
				
			||||||
            ESP_ERR_OTA_SELECT_INFO_INVALID => {
 | 
					 | 
				
			||||||
                Err(Error::from_kind(ErrorKind::InvalidOtaPartitionData))
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            code => panic!("Unexpected esp_ota_write return code: {code}"),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Finish OTA update and validate newly written app image.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// Unless you also call [`set_as_boot_partition`](CompletedOtaUpdate::set_as_boot_partition) the new app will not
 | 
					 | 
				
			||||||
    /// start.
 | 
					 | 
				
			||||||
    pub fn finalize(self) -> Result<CompletedOtaUpdate> {
 | 
					 | 
				
			||||||
        match unsafe { esp_ota_end(self.ota_handle) } {
 | 
					 | 
				
			||||||
            ESP_OK => Ok(()),
 | 
					 | 
				
			||||||
            ESP_ERR_NOT_FOUND => panic!("Invalid OTA handle"),
 | 
					 | 
				
			||||||
            ESP_ERR_INVALID_ARG => Err(Error::from_kind(ErrorKind::NothingWritten)),
 | 
					 | 
				
			||||||
            ESP_ERR_OTA_VALIDATE_FAILED => Err(Error::from_kind(ErrorKind::InvalidImage)),
 | 
					 | 
				
			||||||
            ESP_ERR_INVALID_STATE => Err(Error::from_kind(ErrorKind::WritingEncryptedFailed)),
 | 
					 | 
				
			||||||
            code => panic!("Unexpected esp_ota_end return code: {code}"),
 | 
					 | 
				
			||||||
        }?;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let partition = self.partition;
 | 
					 | 
				
			||||||
        mem::forget(self);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        Ok(CompletedOtaUpdate { partition })
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Returns a raw pointer to the partition that the new app is/will be written to.
 | 
					 | 
				
			||||||
    pub fn raw_partition(&self) -> *const esp_partition_t {
 | 
					 | 
				
			||||||
        self.partition
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl Drop for OtaUpdate {
 | 
					 | 
				
			||||||
    fn drop(&mut self) {
 | 
					 | 
				
			||||||
        #[cfg(feature = "log")]
 | 
					 | 
				
			||||||
        log::debug!("Aborting OTA update");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        let abort_result_code = unsafe { esp_ota_abort(self.ota_handle) };
 | 
					 | 
				
			||||||
        if abort_result_code != ESP_OK {
 | 
					 | 
				
			||||||
            #[cfg(feature = "log")]
 | 
					 | 
				
			||||||
            log::error!(
 | 
					 | 
				
			||||||
                "Aborting the OTA update returned an unexpected code: {}",
 | 
					 | 
				
			||||||
                abort_result_code
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
pub struct CompletedOtaUpdate {
 | 
					 | 
				
			||||||
    partition: *const esp_partition_t,
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
impl CompletedOtaUpdate {
 | 
					 | 
				
			||||||
    /// Sets the boot partition to the newly flashed OTA partition.
 | 
					 | 
				
			||||||
    pub fn set_as_boot_partition(&mut self) -> Result<()> {
 | 
					 | 
				
			||||||
        match unsafe { esp_ota_set_boot_partition(self.partition) } {
 | 
					 | 
				
			||||||
            ESP_OK => Ok(()),
 | 
					 | 
				
			||||||
            ESP_ERR_INVALID_ARG => panic!("Invalid partition sent to esp_ota_set_boot_partition"),
 | 
					 | 
				
			||||||
            ESP_ERR_OTA_VALIDATE_FAILED => Err(Error::from_kind(ErrorKind::InvalidImage)),
 | 
					 | 
				
			||||||
            ESP_ERR_NOT_FOUND => panic!("OTA data partition not found"),
 | 
					 | 
				
			||||||
            ESP_ERR_FLASH_OP_TIMEOUT => Err(Error::from_kind(ErrorKind::FlashTimeout)),
 | 
					 | 
				
			||||||
            ESP_ERR_FLASH_OP_FAIL => Err(Error::from_kind(ErrorKind::FlashFailed)),
 | 
					 | 
				
			||||||
            code => panic!("Unexpected esp_ota_set_boot_partition code: {}", code),
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Restarts the CPU. If [`set_as_boot_partition`](CompletedOtaUpdate::set_as_boot_partition) was
 | 
					 | 
				
			||||||
    /// called and completed successfully, the CPU will boot into the newly written app.
 | 
					 | 
				
			||||||
    ///
 | 
					 | 
				
			||||||
    /// After successful restart, CPU reset reason will be SW_CPU_RESET. Peripherals
 | 
					 | 
				
			||||||
    /// (except for WiFi, BT, UART0, SPI1, and legacy timers) are not reset.
 | 
					 | 
				
			||||||
    pub fn restart(self) -> ! {
 | 
					 | 
				
			||||||
        unsafe { esp_restart() }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    /// Returns a raw pointer to the partition that the new app was written to.
 | 
					 | 
				
			||||||
    pub fn raw_partition(&self) -> *const esp_partition_t {
 | 
					 | 
				
			||||||
        self.partition
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Call this function to indicate that the running app is working well.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// Should be called (at least) the first time a new app starts up after
 | 
					 | 
				
			||||||
/// being flashed.
 | 
					 | 
				
			||||||
pub fn mark_app_valid() {
 | 
					 | 
				
			||||||
    match unsafe { esp_ota_mark_app_valid_cancel_rollback() } {
 | 
					 | 
				
			||||||
        ESP_OK => (),
 | 
					 | 
				
			||||||
        code => panic!(
 | 
					 | 
				
			||||||
            "Unexpected esp_ota_mark_app_valid_cancel_rollback code: {}",
 | 
					 | 
				
			||||||
            code
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/// Call this function to roll back to the previously workable app with reboot.
 | 
					 | 
				
			||||||
///
 | 
					 | 
				
			||||||
/// If rolling back failed, it returns an error, otherwise this function never returns,
 | 
					 | 
				
			||||||
/// as the CPU is rebooting.
 | 
					 | 
				
			||||||
pub fn rollback_and_reboot() -> Result<core::convert::Infallible> {
 | 
					 | 
				
			||||||
    match unsafe { esp_ota_mark_app_invalid_rollback_and_reboot() } {
 | 
					 | 
				
			||||||
        ESP_FAIL => Err(Error::from_kind(ErrorKind::RollbackFailed)),
 | 
					 | 
				
			||||||
        ESP_ERR_OTA_ROLLBACK_FAILED => Err(Error::from_kind(ErrorKind::RollbackFailedNoApps)),
 | 
					 | 
				
			||||||
        code => panic!(
 | 
					 | 
				
			||||||
            "Unexpected esp_ota_mark_app_invalid_rollback_and_reboot code: {}",
 | 
					 | 
				
			||||||
            code
 | 
					 | 
				
			||||||
        ),
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -15,7 +15,7 @@ const BUFFER_SIZE:usize = 210;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#[link_section = ".rtc.data"]
 | 
					#[link_section = ".rtc.data"]
 | 
				
			||||||
static mut BUFFER:ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE> = ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE>::new();
 | 
					static mut BUFFER:ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE> = ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE>::new();
 | 
				
			||||||
 | 
					#[allow(static_mut_refs)]
 | 
				
			||||||
static BUFFER_ACCESS: Lazy<Mutex<&mut ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE>>> = Lazy::new(|| unsafe { Mutex::new(&mut BUFFER) });
 | 
					static BUFFER_ACCESS: Lazy<Mutex<&mut ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE>>> = Lazy::new(|| unsafe { Mutex::new(&mut BUFFER) });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -133,53 +133,53 @@ mod tests {
 | 
				
			|||||||
#[derive(IntoStaticStr, EnumIter, Serialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
 | 
					#[derive(IntoStaticStr, EnumIter, Serialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
 | 
				
			||||||
pub enum LogMessage {
 | 
					pub enum LogMessage {
 | 
				
			||||||
    #[strum(serialize = "Reset due to {{txt_long}} requires rtc clear {{a}} and force config mode {{b}}")]   
 | 
					    #[strum(serialize = "Reset due to {{txt_long}} requires rtc clear {{a}} and force config mode {{b}}")]   
 | 
				
			||||||
    reset_reason,
 | 
					    ResetReason,
 | 
				
			||||||
    #[strum(serialize = "Current restart to conf mode {{a}}")]   
 | 
					    #[strum(serialize = "Current restart to conf mode {{a}}")]   
 | 
				
			||||||
    restart_to_config,
 | 
					    RestartToConfig,
 | 
				
			||||||
    #[strum(serialize = "Current low voltage detection is {{a}}")]   
 | 
					    #[strum(serialize = "Current low voltage detection is {{a}}")]   
 | 
				
			||||||
    low_voltage,
 | 
					    LowVoltage,
 | 
				
			||||||
    #[strum(serialize = "Error communicating with battery!! {{txt_long}}")]   
 | 
					    #[strum(serialize = "Error communicating with battery!! {{txt_long}}")]   
 | 
				
			||||||
    battery_communication_error,
 | 
					    BatteryCommunicationError,
 | 
				
			||||||
    #[strum(serialize = "Tank sensor raw {{a}} percent {{b}}")]   
 | 
					    #[strum(serialize = "Tank sensor raw {{a}} percent {{b}}")]   
 | 
				
			||||||
    sensor_tank_raw,
 | 
					    SensorTankRaw,
 | 
				
			||||||
    #[strum(serialize = "raw measure unscaled {{a}} hz {{b}}, plant {{txt_short}} sensor {{txt_long}}")]   
 | 
					    #[strum(serialize = "raw measure unscaled {{a}} hz {{b}}, plant {{txt_short}} sensor {{txt_long}}")]   
 | 
				
			||||||
    raw_measure,
 | 
					    RawMeasure,
 | 
				
			||||||
    #[strum(serialize = "IP info: {{txt_long}}")]   
 | 
					    #[strum(serialize = "IP info: {{txt_long}}")]   
 | 
				
			||||||
    wifi_info,
 | 
					    WifiInfo,
 | 
				
			||||||
    #[strum(serialize = "Plant:{{txt_short}} a:{{a}} b:{{b}}")]   
 | 
					    #[strum(serialize = "Plant:{{txt_short}} a:{{a}} b:{{b}}")]   
 | 
				
			||||||
    test_sensor,
 | 
					    TestSensor,
 | 
				
			||||||
    #[strum(serialize = "Stay alive topic is {{txt_long}}")]   
 | 
					    #[strum(serialize = "Stay alive topic is {{txt_long}}")]   
 | 
				
			||||||
    stay_alive,
 | 
					    StayAlive,
 | 
				
			||||||
    #[strum(serialize = "Connecting mqtt {{txt_short}} with id {{txt_long}}")]   
 | 
					    #[strum(serialize = "Connecting mqtt {{txt_short}} with id {{txt_long}}")]   
 | 
				
			||||||
    mqtt_info,
 | 
					    MqttInfo,
 | 
				
			||||||
    #[strum(serialize = "Received stay alive with value {{txt_short}}")]   
 | 
					    #[strum(serialize = "Received stay alive with value {{txt_short}}")]   
 | 
				
			||||||
    mqtt_stay_alive_rec,
 | 
					    MqttStayAliveRec,
 | 
				
			||||||
    #[strum(serialize = "Unknown topic recieved {{txt_long}}")]   
 | 
					    #[strum(serialize = "Unknown topic recieved {{txt_long}}")]   
 | 
				
			||||||
    unknown_topic,
 | 
					    UnknownTopic,
 | 
				
			||||||
    #[strum(serialize = "Partition state is {{txt_long}}")]   
 | 
					    #[strum(serialize = "Partition state is {{txt_long}}")]   
 | 
				
			||||||
    partition_state,
 | 
					    PartitionState,
 | 
				
			||||||
    #[strum(serialize = "Mounted Filesystem free {{a}} total {{b}} use {{txt_short}}")]
 | 
					    #[strum(serialize = "Mounted Filesystem free {{a}} total {{b}} use {{txt_short}}")]
 | 
				
			||||||
    filesystem_mount,
 | 
					    FilesystemMount,
 | 
				
			||||||
    #[strum(serialize = "Mounting Filesystem, this will format the first time and needs quite some time!")]
 | 
					    #[strum(serialize = "Mounting Filesystem, this will format the first time and needs quite some time!")]
 | 
				
			||||||
    mounting_filesystem,
 | 
					    MountingFilesystem,
 | 
				
			||||||
    #[strum(serialize = "Year inplausible, force config mode")]
 | 
					    #[strum(serialize = "Year inplausible, force config mode")]
 | 
				
			||||||
    year_inplausible_force_config,
 | 
					    YearInplausibleForceConfig,
 | 
				
			||||||
    #[strum(serialize = "Going to config mode, due to request from prior run")]
 | 
					    #[strum(serialize = "Going to config mode, due to request from prior run")]
 | 
				
			||||||
    config_mode_software_override,
 | 
					    ConfigModeSoftwareOverride,
 | 
				
			||||||
    #[strum(serialize = "Going to config mode, due to request via config mode button")]
 | 
					    #[strum(serialize = "Going to config mode, due to request via config mode button")]
 | 
				
			||||||
    config_mode_button_override,
 | 
					    ConfigModeButtonOverride,
 | 
				
			||||||
    #[strum(serialize = "Going to normal mode")]
 | 
					    #[strum(serialize = "Going to normal mode")]
 | 
				
			||||||
    normal_run,
 | 
					    NormalRun,
 | 
				
			||||||
    #[strum(serialize = "Missing normal config, entering config mode {{txt_long}}")]
 | 
					    #[strum(serialize = "Missing normal config, entering config mode {{txt_long}}")]
 | 
				
			||||||
    config_mode_missing_config,
 | 
					    ConfigModeMissingConfig,
 | 
				
			||||||
    #[strum(serialize = "startup state wifi {{a}} sntp {{b}} mqtt {{txt_short}}")]
 | 
					    #[strum(serialize = "startup state wifi {{a}} sntp {{b}} mqtt {{txt_short}}")]
 | 
				
			||||||
    startup_info,
 | 
					    StartupInfo,
 | 
				
			||||||
    #[strum(serialize = "Trying to pump for {{b}}s with pump {{a}} now dryrun: {{txt_short}}")]
 | 
					    #[strum(serialize = "Trying to pump for {{b}}s with pump {{a}} now dryrun: {{txt_short}}")]
 | 
				
			||||||
    pump_plant,
 | 
					    PumpPlant,
 | 
				
			||||||
    #[strum(serialize = "Enable main power dryrun: {{a}}")]
 | 
					    #[strum(serialize = "Enable main power dryrun: {{a}}")]
 | 
				
			||||||
    enable_main,
 | 
					    EnableMain,
 | 
				
			||||||
    #[strum(serialize = "Pumped multiple times, but plant is still to try attempt: {{a}} limit :: {{b}} plant: {{txt_short}}")]
 | 
					    #[strum(serialize = "Pumped multiple times, but plant is still to try attempt: {{a}} limit :: {{b}} plant: {{txt_short}}")]
 | 
				
			||||||
    consecutive_pump_count_limit
 | 
					    ConsecutivePumpCountLimit
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
impl LogMessage {
 | 
					impl LogMessage {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,6 +22,7 @@ use esp_idf_sys::{
 | 
				
			|||||||
    esp_ota_img_states_t_ESP_OTA_IMG_VALID, 
 | 
					    esp_ota_img_states_t_ESP_OTA_IMG_VALID, 
 | 
				
			||||||
    vTaskDelay
 | 
					    vTaskDelay
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					use esp_ota::{mark_app_valid, rollback_and_reboot};
 | 
				
			||||||
use log::log;
 | 
					use log::log;
 | 
				
			||||||
use once_cell::sync::Lazy;
 | 
					use once_cell::sync::Lazy;
 | 
				
			||||||
use plant_hal::{PlantCtrlBoard, PlantHal, PLANT_COUNT};
 | 
					use plant_hal::{PlantCtrlBoard, PlantHal, PLANT_COUNT};
 | 
				
			||||||
@@ -29,12 +30,10 @@ use serde::{Deserialize, Serialize};
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    config::PlantControllerConfig,
 | 
					    config::PlantControllerConfig,
 | 
				
			||||||
    espota::{mark_app_valid, rollback_and_reboot},
 | 
					 | 
				
			||||||
    webserver::webserver::httpd,
 | 
					    webserver::webserver::httpd,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
mod log;
 | 
					mod log;
 | 
				
			||||||
mod config;
 | 
					mod config;
 | 
				
			||||||
pub mod espota;
 | 
					 | 
				
			||||||
pub mod plant_hal;
 | 
					pub mod plant_hal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -147,8 +146,7 @@ fn safe_main() -> anyhow::Result<()> {
 | 
				
			|||||||
        bail!(
 | 
					        bail!(
 | 
				
			||||||
            "stack too small: {} bail!",
 | 
					            "stack too small: {} bail!",
 | 
				
			||||||
            esp_idf_sys::CONFIG_MAIN_TASK_STACK_SIZE
 | 
					            esp_idf_sys::CONFIG_MAIN_TASK_STACK_SIZE
 | 
				
			||||||
        );
 | 
					        )
 | 
				
			||||||
        return Ok(());
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    println!("Startup Rust");
 | 
					    println!("Startup Rust");
 | 
				
			||||||
@@ -186,16 +184,16 @@ fn safe_main() -> anyhow::Result<()> {
 | 
				
			|||||||
            &format!("unknown {ota_state}")
 | 
					            &format!("unknown {ota_state}")
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    log(log::LogMessage::partition_state, 0,0, "", ota_state_string);
 | 
					    log(log::LogMessage::PartitionState, 0,0, "", ota_state_string);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
    let mut board: std::sync::MutexGuard<'_, PlantCtrlBoard<'_>> = BOARD_ACCESS.lock().unwrap();
 | 
					    let mut board: std::sync::MutexGuard<'_, PlantCtrlBoard<'_>> = BOARD_ACCESS.lock().unwrap();
 | 
				
			||||||
    board.general_fault(false);
 | 
					    board.general_fault(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    log(log::LogMessage::mounting_filesystem, 0,0,"","");
 | 
					    log(log::LogMessage::MountingFilesystem, 0,0,"","");
 | 
				
			||||||
    board.mount_file_system()?;
 | 
					    board.mount_file_system()?;
 | 
				
			||||||
    let free_space = board.file_system_size()?;
 | 
					    let free_space = board.file_system_size()?;
 | 
				
			||||||
    log(log::LogMessage::filesystem_mount, free_space.free_size as u32,
 | 
					    log(log::LogMessage::FilesystemMount, free_space.free_size as u32,
 | 
				
			||||||
        free_space.total_size as u32, &free_space.used_size.to_string(), "");
 | 
					        free_space.total_size as u32, &free_space.used_size.to_string(), "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -209,7 +207,6 @@ fn safe_main() -> anyhow::Result<()> {
 | 
				
			|||||||
                Ok(cur) => cur,
 | 
					                Ok(cur) => cur,
 | 
				
			||||||
                Err(err) => {
 | 
					                Err(err) => {
 | 
				
			||||||
                    bail!("time error {}", err);
 | 
					                    bail!("time error {}", err);
 | 
				
			||||||
                    DateTime::from_timestamp_millis(0).unwrap()
 | 
					 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -218,13 +215,13 @@ fn safe_main() -> anyhow::Result<()> {
 | 
				
			|||||||
    //check if we know the time current > 2020
 | 
					    //check if we know the time current > 2020
 | 
				
			||||||
    if cur.year() < 2020 {
 | 
					    if cur.year() < 2020 {
 | 
				
			||||||
        to_config = true;
 | 
					        to_config = true;
 | 
				
			||||||
        log(log::LogMessage::year_inplausible_force_config, 0,0,"","");
 | 
					        log(log::LogMessage::YearInplausibleForceConfig, 0,0,"","");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    println!("cur is {}", cur);
 | 
					    println!("cur is {}", cur);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if board.get_restart_to_conf() {
 | 
					    if board.get_restart_to_conf() {
 | 
				
			||||||
        log(log::LogMessage::config_mode_software_override, 0,0,"","");
 | 
					        log(log::LogMessage::ConfigModeSoftwareOverride, 0,0,"","");
 | 
				
			||||||
        for _i in 0..2 {
 | 
					        for _i in 0..2 {
 | 
				
			||||||
            board.general_fault(true);
 | 
					            board.general_fault(true);
 | 
				
			||||||
            Delay::new_default().delay_ms(100);
 | 
					            Delay::new_default().delay_ms(100);
 | 
				
			||||||
@@ -236,7 +233,7 @@ fn safe_main() -> anyhow::Result<()> {
 | 
				
			|||||||
        board.set_restart_to_conf(false);
 | 
					        board.set_restart_to_conf(false);
 | 
				
			||||||
    } else if board.is_mode_override() {
 | 
					    } else if board.is_mode_override() {
 | 
				
			||||||
        board.general_fault(true);
 | 
					        board.general_fault(true);
 | 
				
			||||||
        log(log::LogMessage::config_mode_button_override, 0,0,"","");
 | 
					        log(log::LogMessage::ConfigModeButtonOverride, 0,0,"","");
 | 
				
			||||||
        for _i in 0..5 {
 | 
					        for _i in 0..5 {
 | 
				
			||||||
            board.general_fault(true);
 | 
					            board.general_fault(true);
 | 
				
			||||||
            Delay::new_default().delay_ms(100);
 | 
					            Delay::new_default().delay_ms(100);
 | 
				
			||||||
@@ -258,7 +255,7 @@ fn safe_main() -> anyhow::Result<()> {
 | 
				
			|||||||
            config = valid;
 | 
					            config = valid;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Err(err) => {
 | 
					        Err(err) => {
 | 
				
			||||||
            log(log::LogMessage::config_mode_missing_config, 0,0,"",&err.to_string());
 | 
					            log(log::LogMessage::ConfigModeMissingConfig, 0,0,"",&err.to_string());
 | 
				
			||||||
            //config upload will trigger reboot!
 | 
					            //config upload will trigger reboot!
 | 
				
			||||||
            let _ = board.wifi_ap(Option::None);
 | 
					            let _ = board.wifi_ap(Option::None);
 | 
				
			||||||
            drop(board);
 | 
					            drop(board);
 | 
				
			||||||
@@ -362,7 +359,7 @@ fn safe_main() -> anyhow::Result<()> {
 | 
				
			|||||||
        publish_battery_state(&mut board, &config);
 | 
					        publish_battery_state(&mut board, &config);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    log(log::LogMessage::startup_info, wifi as u32, sntp as u32,&mqtt.to_string(),"");
 | 
					    log(log::LogMessage::StartupInfo, wifi as u32, sntp as u32,&mqtt.to_string(),"");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if to_config {
 | 
					    if to_config {
 | 
				
			||||||
        //check if client or ap mode and init wifi
 | 
					        //check if client or ap mode and init wifi
 | 
				
			||||||
@@ -373,7 +370,7 @@ fn safe_main() -> anyhow::Result<()> {
 | 
				
			|||||||
        let _webserver = httpd(reboot_now.clone());
 | 
					        let _webserver = httpd(reboot_now.clone());
 | 
				
			||||||
        wait_infinity(WaitType::ConfigButton, reboot_now.clone());
 | 
					        wait_infinity(WaitType::ConfigButton, reboot_now.clone());
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
        log(log::LogMessage::normal_run, 0,0,"","");
 | 
					        log(log::LogMessage::NormalRun, 0,0,"","");
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -428,11 +425,10 @@ fn safe_main() -> anyhow::Result<()> {
 | 
				
			|||||||
    let mut plantstate: [PlantState; PLANT_COUNT] = core::array::from_fn(|_| PlantState {
 | 
					    let mut plantstate: [PlantState; PLANT_COUNT] = core::array::from_fn(|_| PlantState {
 | 
				
			||||||
        ..Default::default()
 | 
					        ..Default::default()
 | 
				
			||||||
    });
 | 
					    });
 | 
				
			||||||
    let plant_to_pump = determine_next_plant(
 | 
					    determine_plant_state(
 | 
				
			||||||
        &mut plantstate,
 | 
					        &mut plantstate,
 | 
				
			||||||
        timezone_time,
 | 
					        timezone_time,
 | 
				
			||||||
        &tank_state,
 | 
					        &tank_state,
 | 
				
			||||||
        water_frozen,
 | 
					 | 
				
			||||||
        &config,
 | 
					        &config,
 | 
				
			||||||
        &mut board,
 | 
					        &mut board,
 | 
				
			||||||
    );
 | 
					    );
 | 
				
			||||||
@@ -440,7 +436,7 @@ fn safe_main() -> anyhow::Result<()> {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    let pump_required = plantstate.iter().any(|it| it.do_water) && !water_frozen;
 | 
					    let pump_required = plantstate.iter().any(|it| it.do_water) && !water_frozen;
 | 
				
			||||||
    if pump_required {
 | 
					    if pump_required {
 | 
				
			||||||
        log(log::LogMessage::enable_main, dry_run as u32,0,"","");
 | 
					        log(log::LogMessage::EnableMain, dry_run as u32,0,"","");
 | 
				
			||||||
        if !dry_run{
 | 
					        if !dry_run{
 | 
				
			||||||
            board.any_pump(true)?;    
 | 
					            board.any_pump(true)?;    
 | 
				
			||||||
        }       
 | 
					        }       
 | 
				
			||||||
@@ -452,11 +448,11 @@ fn safe_main() -> anyhow::Result<()> {
 | 
				
			|||||||
                state.consecutive_pump_count = board.consecutive_pump_count(plant) + 1;
 | 
					                state.consecutive_pump_count = board.consecutive_pump_count(plant) + 1;
 | 
				
			||||||
                board.store_consecutive_pump_count(plant, state.consecutive_pump_count);
 | 
					                board.store_consecutive_pump_count(plant, state.consecutive_pump_count);
 | 
				
			||||||
                if state.consecutive_pump_count > plant_config.max_consecutive_pump_count as u32 {
 | 
					                if state.consecutive_pump_count > plant_config.max_consecutive_pump_count as u32 {
 | 
				
			||||||
                    log(log::LogMessage::consecutive_pump_count_limit, state.consecutive_pump_count as u32,plant_config.max_consecutive_pump_count as u32,&plant.to_string(),"");
 | 
					                    log(log::LogMessage::ConsecutivePumpCountLimit, state.consecutive_pump_count as u32,plant_config.max_consecutive_pump_count as u32,&plant.to_string(),"");
 | 
				
			||||||
                    state.not_effective = true;
 | 
					                    state.not_effective = true;
 | 
				
			||||||
                    board.fault(plant, true);
 | 
					                    board.fault(plant, true);
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                log(log::LogMessage::pump_plant, (plant + 1) as u32,plant_config.pump_time_s as u32,&dry_run.to_string(),"");
 | 
					                log(log::LogMessage::PumpPlant, (plant + 1) as u32,plant_config.pump_time_s as u32,&dry_run.to_string(),"");
 | 
				
			||||||
                board.store_last_pump_time(plant, cur);
 | 
					                board.store_last_pump_time(plant, cur);
 | 
				
			||||||
                board.last_pump_time(plant);
 | 
					                board.last_pump_time(plant);
 | 
				
			||||||
                state.active = true;
 | 
					                state.active = true;
 | 
				
			||||||
@@ -812,11 +808,10 @@ fn determine_state_timer_and_deadzone_for_plant(
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn determine_next_plant(
 | 
					fn determine_plant_state(
 | 
				
			||||||
    plantstate: &mut [PlantState; PLANT_COUNT],
 | 
					    plantstate: &mut [PlantState; PLANT_COUNT],
 | 
				
			||||||
    cur: DateTime<Tz>,
 | 
					    cur: DateTime<Tz>,
 | 
				
			||||||
    tank_state: &TankState,
 | 
					    tank_state: &TankState,
 | 
				
			||||||
    water_frozen: bool,
 | 
					 | 
				
			||||||
    config: &PlantControllerConfig,
 | 
					    config: &PlantControllerConfig,
 | 
				
			||||||
    board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
 | 
					    board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
 | 
				
			||||||
) {
 | 
					) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,8 @@
 | 
				
			|||||||
use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver};
 | 
					use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use chrono_tz::Tz;
 | 
					 | 
				
			||||||
use ds323x::{DateTimeAccess, Ds323x};
 | 
					use ds323x::{DateTimeAccess, Ds323x};
 | 
				
			||||||
use crate::log::{init, LogMessage};
 | 
					use esp_ota::mark_app_valid;
 | 
				
			||||||
 | 
					use crate::log::LogMessage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use eeprom24x::{Eeprom24x, Eeprom24xTrait, SlaveAddr};
 | 
					use eeprom24x::{Eeprom24x, Eeprom24xTrait, SlaveAddr};
 | 
				
			||||||
use embedded_hal_bus::i2c::MutexDevice;
 | 
					use embedded_hal_bus::i2c::MutexDevice;
 | 
				
			||||||
@@ -23,7 +23,6 @@ use esp_idf_svc::mqtt::client::{EspMqttClient, LwtConfiguration, MqttClientConfi
 | 
				
			|||||||
use esp_idf_svc::nvs::EspDefaultNvsPartition;
 | 
					use esp_idf_svc::nvs::EspDefaultNvsPartition;
 | 
				
			||||||
use esp_idf_svc::wifi::config::{ScanConfig, ScanType};
 | 
					use esp_idf_svc::wifi::config::{ScanConfig, ScanType};
 | 
				
			||||||
use esp_idf_svc::wifi::EspWifi;
 | 
					use esp_idf_svc::wifi::EspWifi;
 | 
				
			||||||
use log::logger;
 | 
					 | 
				
			||||||
use measurements::Temperature;
 | 
					use measurements::Temperature;
 | 
				
			||||||
use once_cell::sync::Lazy;
 | 
					use once_cell::sync::Lazy;
 | 
				
			||||||
use plant_ctrl2::sipo::ShiftRegister40;
 | 
					use plant_ctrl2::sipo::ShiftRegister40;
 | 
				
			||||||
@@ -32,7 +31,6 @@ use esp_idf_sys::esp_restart;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
use anyhow::{anyhow, Context};
 | 
					use anyhow::{anyhow, Context};
 | 
				
			||||||
use anyhow::{bail, Ok, Result};
 | 
					use anyhow::{bail, Ok, Result};
 | 
				
			||||||
use ringbuffer::{ConstGenericRingBuffer, RingBuffer};
 | 
					 | 
				
			||||||
use serde::{Deserialize, Serialize};
 | 
					use serde::{Deserialize, Serialize};
 | 
				
			||||||
use std::ffi::CString;
 | 
					use std::ffi::CString;
 | 
				
			||||||
use std::fs::{self, File};
 | 
					use std::fs::{self, File};
 | 
				
			||||||
@@ -60,7 +58,6 @@ use esp_idf_sys::{gpio_hold_dis, gpio_hold_en, vTaskDelay, EspError};
 | 
				
			|||||||
use one_wire_bus::OneWire;
 | 
					use one_wire_bus::OneWire;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
use crate::config::{self, PlantControllerConfig};
 | 
					use crate::config::{self, PlantControllerConfig};
 | 
				
			||||||
use crate::espota::mark_app_valid;
 | 
					 | 
				
			||||||
use crate::log::log;
 | 
					use crate::log::log;
 | 
				
			||||||
use crate::{plant_hal, to_string, STAY_ALIVE};
 | 
					use crate::{plant_hal, to_string, STAY_ALIVE};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -350,7 +347,7 @@ impl PlantCtrlBoard<'_> {
 | 
				
			|||||||
        return bat;
 | 
					        return bat;
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    pub fn list_files(&self, filename: &str) -> FileList {
 | 
					    pub fn list_files(&self) -> FileList {
 | 
				
			||||||
        let storage = CString::new(SPIFFS_PARTITION_NAME).unwrap();
 | 
					        let storage = CString::new(SPIFFS_PARTITION_NAME).unwrap();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mut file_system_corrupt = Option::None;
 | 
					        let mut file_system_corrupt = Option::None;
 | 
				
			||||||
@@ -483,7 +480,7 @@ impl PlantCtrlBoard<'_> {
 | 
				
			|||||||
        let r2 = median * 50.0 / (3.3 - median);
 | 
					        let r2 = median * 50.0 / (3.3 - median);
 | 
				
			||||||
        let mut percent = r2 / 190_f32 * 100_f32;
 | 
					        let mut percent = r2 / 190_f32 * 100_f32;
 | 
				
			||||||
        percent = percent.clamp(0.0, 100.0);
 | 
					        percent = percent.clamp(0.0, 100.0);
 | 
				
			||||||
        log(LogMessage::sensor_tank_raw, median as u32, percent as u32, "","");
 | 
					        log(LogMessage::SensorTankRaw, median as u32, percent as u32, "","");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return Ok(percent as u16);
 | 
					        return Ok(percent as u16);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -656,7 +653,7 @@ impl PlantCtrlBoard<'_> {
 | 
				
			|||||||
            delay.delay_ms(10);
 | 
					            delay.delay_ms(10);
 | 
				
			||||||
            let unscaled = self.signal_counter.get_counter_value()? as i32;
 | 
					            let unscaled = self.signal_counter.get_counter_value()? as i32;
 | 
				
			||||||
            let hz = (unscaled as f32 * factor) as u32;
 | 
					            let hz = (unscaled as f32 * factor) as u32;
 | 
				
			||||||
            log(LogMessage::raw_measure, unscaled as u32, hz as u32, &plant.to_string(), &format!("{sensor:?}"));
 | 
					            log(LogMessage::RawMeasure, unscaled as u32, hz as u32, &plant.to_string(), &format!("{sensor:?}"));
 | 
				
			||||||
            results[repeat] = hz;
 | 
					            results[repeat] = hz;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        results.sort();
 | 
					        results.sort();
 | 
				
			||||||
@@ -744,7 +741,7 @@ impl PlantCtrlBoard<'_> {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        //update freertos registers ;)
 | 
					        //update freertos registers ;)
 | 
				
			||||||
        let address = self.wifi_driver.sta_netif().get_ip_info()?;
 | 
					        let address = self.wifi_driver.sta_netif().get_ip_info()?;
 | 
				
			||||||
        log(LogMessage::wifi_info, 0 ,0,"", &format!("{address:?}"));
 | 
					        log(LogMessage::WifiInfo, 0 ,0,"", &format!("{address:?}"));
 | 
				
			||||||
        Ok(address)
 | 
					        Ok(address)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -890,7 +887,7 @@ impl PlantCtrlBoard<'_> {
 | 
				
			|||||||
                OkStd(b) => b as u32,
 | 
					                OkStd(b) => b as u32,
 | 
				
			||||||
                Err(_) => u32::MAX
 | 
					                Err(_) => u32::MAX
 | 
				
			||||||
            };
 | 
					            };
 | 
				
			||||||
            log(LogMessage::test_sensor, aa ,bb,&plant.to_string(), "");
 | 
					            log(LogMessage::TestSensor, aa ,bb,&plant.to_string(), "");
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        Delay::new_default().delay_ms(10);
 | 
					        Delay::new_default().delay_ms(10);
 | 
				
			||||||
        Ok(())
 | 
					        Ok(())
 | 
				
			||||||
@@ -939,7 +936,7 @@ impl PlantCtrlBoard<'_> {
 | 
				
			|||||||
        let round_trip_ok = Arc::new(AtomicBool::new(false));
 | 
					        let round_trip_ok = Arc::new(AtomicBool::new(false));
 | 
				
			||||||
        let round_trip_topic = format!("{}/internal/roundtrip", base_topic);
 | 
					        let round_trip_topic = format!("{}/internal/roundtrip", base_topic);
 | 
				
			||||||
        let stay_alive_topic = format!("{}/stay_alive", base_topic);
 | 
					        let stay_alive_topic = format!("{}/stay_alive", base_topic);
 | 
				
			||||||
        log(LogMessage::stay_alive, 0 ,0,"", &stay_alive_topic);
 | 
					        log(LogMessage::StayAlive, 0 ,0,"", &stay_alive_topic);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        let mqtt_connected_event_received_copy = mqtt_connected_event_received.clone();
 | 
					        let mqtt_connected_event_received_copy = mqtt_connected_event_received.clone();
 | 
				
			||||||
        let mqtt_connected_event_ok_copy = mqtt_connected_event_ok.clone();
 | 
					        let mqtt_connected_event_ok_copy = mqtt_connected_event_ok.clone();
 | 
				
			||||||
@@ -947,7 +944,7 @@ impl PlantCtrlBoard<'_> {
 | 
				
			|||||||
        let round_trip_topic_copy = round_trip_topic.clone();
 | 
					        let round_trip_topic_copy = round_trip_topic.clone();
 | 
				
			||||||
        let round_trip_ok_copy = round_trip_ok.clone();
 | 
					        let round_trip_ok_copy = round_trip_ok.clone();
 | 
				
			||||||
        let client_id = mqtt_client_config.client_id.unwrap_or("not set");
 | 
					        let client_id = mqtt_client_config.client_id.unwrap_or("not set");
 | 
				
			||||||
        log(LogMessage::mqtt_info, 0 ,0,client_id, &mqtt_url);
 | 
					        log(LogMessage::MqttInfo, 0 ,0,client_id, &mqtt_url);
 | 
				
			||||||
        let mut client = EspMqttClient::new_cb(&mqtt_url, &mqtt_client_config, move |event| {
 | 
					        let mut client = EspMqttClient::new_cb(&mqtt_url, &mqtt_client_config, move |event| {
 | 
				
			||||||
            let payload = event.payload();
 | 
					            let payload = event.payload();
 | 
				
			||||||
            match payload {
 | 
					            match payload {
 | 
				
			||||||
@@ -965,10 +962,10 @@ impl PlantCtrlBoard<'_> {
 | 
				
			|||||||
                        } else if topic.eq(stay_alive_topic_copy.as_str()) {
 | 
					                        } else if topic.eq(stay_alive_topic_copy.as_str()) {
 | 
				
			||||||
                            let value =
 | 
					                            let value =
 | 
				
			||||||
                                data.eq_ignore_ascii_case("true") || data.eq_ignore_ascii_case("1");
 | 
					                                data.eq_ignore_ascii_case("true") || data.eq_ignore_ascii_case("1");
 | 
				
			||||||
                            log(LogMessage::mqtt_stay_alive_rec, 0 ,0,&data, "");
 | 
					                            log(LogMessage::MqttStayAliveRec, 0 ,0,&data, "");
 | 
				
			||||||
                            STAY_ALIVE.store(value, std::sync::atomic::Ordering::Relaxed);
 | 
					                            STAY_ALIVE.store(value, std::sync::atomic::Ordering::Relaxed);
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                            log(LogMessage::unknown_topic, 0 ,0,"", &topic);
 | 
					                            log(LogMessage::UnknownTopic, 0 ,0,"", &topic);
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
@@ -1417,7 +1414,7 @@ impl PlantHal {
 | 
				
			|||||||
                init_rtc_store = true
 | 
					                init_rtc_store = true
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        };
 | 
					        };
 | 
				
			||||||
        log(LogMessage::reset_reason, init_rtc_store as u32, to_config_mode as u32, "",&format!("{reasons:?}"));
 | 
					        log(LogMessage::ResetReason, init_rtc_store as u32, to_config_mode as u32, "",&format!("{reasons:?}"));
 | 
				
			||||||
        if init_rtc_store {
 | 
					        if init_rtc_store {
 | 
				
			||||||
            unsafe {
 | 
					            unsafe {
 | 
				
			||||||
                LAST_WATERING_TIMESTAMP = [0; PLANT_COUNT];
 | 
					                LAST_WATERING_TIMESTAMP = [0; PLANT_COUNT];
 | 
				
			||||||
@@ -1431,8 +1428,8 @@ impl PlantHal {
 | 
				
			|||||||
                if to_config_mode{
 | 
					                if to_config_mode{
 | 
				
			||||||
                    RESTART_TO_CONF = true; 
 | 
					                    RESTART_TO_CONF = true; 
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                log(LogMessage::restart_to_config, RESTART_TO_CONF as u32, 0, "","");
 | 
					                log(LogMessage::RestartToConfig, RESTART_TO_CONF as u32, 0, "","");
 | 
				
			||||||
                log(LogMessage::low_voltage, LOW_VOLTAGE_DETECTED as u32, 0, "","");
 | 
					                log(LogMessage::LowVoltage, LOW_VOLTAGE_DETECTED as u32, 0, "","");
 | 
				
			||||||
                for i in 0..PLANT_COUNT {
 | 
					                for i in 0..PLANT_COUNT {
 | 
				
			||||||
                    println!(
 | 
					                    println!(
 | 
				
			||||||
                        "LAST_WATERING_TIMESTAMP[{}] = UTC {}",
 | 
					                        "LAST_WATERING_TIMESTAMP[{}] = UTC {}",
 | 
				
			||||||
@@ -1510,7 +1507,7 @@ impl PlantHal {
 | 
				
			|||||||
            
 | 
					            
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
            Err(err) => {
 | 
					            Err(err) => {
 | 
				
			||||||
                log(LogMessage::battery_communication_error, 0 as u32, 0, "",&format!("{err:?})"));
 | 
					                log(LogMessage::BatteryCommunicationError, 0 as u32, 0, "",&format!("{err:?})"));
 | 
				
			||||||
            },
 | 
					            },
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        let shift_register_enable_invert = PinDriver::output(peripherals.pins.gpio21.downgrade())?;
 | 
					        let shift_register_enable_invert = PinDriver::output(peripherals.pins.gpio21.downgrade())?;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,11 +5,12 @@ use std::{
 | 
				
			|||||||
    sync::{atomic::AtomicBool, Arc},
 | 
					    sync::{atomic::AtomicBool, Arc},
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use crate::{
 | 
					use crate::{
 | 
				
			||||||
    espota::OtaUpdate, get_version, log::LogMessage, map_range_moisture, plant_hal::{FileInfo, PLANT_COUNT}, BOARD_ACCESS
 | 
					    get_version, log::LogMessage, map_range_moisture, plant_hal::PLANT_COUNT, BOARD_ACCESS
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
use anyhow::bail;
 | 
					use anyhow::bail;
 | 
				
			||||||
use chrono::DateTime;
 | 
					use chrono::DateTime;
 | 
				
			||||||
use esp_idf_sys::{esp_set_time_from_rtc, settimeofday, timeval, vTaskDelay};
 | 
					use esp_idf_sys::{settimeofday, timeval, vTaskDelay};
 | 
				
			||||||
 | 
					use esp_ota::OtaUpdate;
 | 
				
			||||||
use core::result::Result::Ok;
 | 
					use core::result::Result::Ok;
 | 
				
			||||||
use embedded_svc::http::Method;
 | 
					use embedded_svc::http::Method;
 | 
				
			||||||
use esp_idf_hal::delay::Delay;
 | 
					use esp_idf_hal::delay::Delay;
 | 
				
			||||||
@@ -248,11 +249,10 @@ fn wifi_scan(
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fn list_files(
 | 
					fn list_files(
 | 
				
			||||||
    request: &mut Request<&mut EspHttpConnection>,
 | 
					    _request: &mut Request<&mut EspHttpConnection>,
 | 
				
			||||||
) -> Result<Option<std::string::String>, anyhow::Error> {
 | 
					) -> Result<Option<std::string::String>, anyhow::Error> {
 | 
				
			||||||
    let filename = query_param(request.uri(), "filename").unwrap_or_default();
 | 
					 | 
				
			||||||
    let board = BOARD_ACCESS.lock().unwrap();
 | 
					    let board = BOARD_ACCESS.lock().unwrap();
 | 
				
			||||||
    let result = board.list_files(&filename);
 | 
					    let result = board.list_files();
 | 
				
			||||||
    let file_list_json = serde_json::to_string(&result)?;
 | 
					    let file_list_json = serde_json::to_string(&result)?;
 | 
				
			||||||
    return anyhow::Ok(Some(file_list_json));
 | 
					    return anyhow::Ok(Some(file_list_json));
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user