warnings to zero :)
This commit is contained in:
		@@ -86,6 +86,7 @@ bincode = "1.3.3"
 | 
			
		||||
ringbuffer = "0.15.0"
 | 
			
		||||
text-template = "0.1.0"
 | 
			
		||||
strum_macros = "0.27.0"
 | 
			
		||||
esp-ota = { version = "0.2.2", features = ["log"] }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[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/esp-rs/esp-idf-sys.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" }
 | 
			
		||||
 | 
			
		||||
[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"]
 | 
			
		||||
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) });
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -133,53 +133,53 @@ mod tests {
 | 
			
		||||
#[derive(IntoStaticStr, EnumIter, Serialize, PartialEq, Eq, PartialOrd, Ord, Clone)]
 | 
			
		||||
pub enum LogMessage {
 | 
			
		||||
    #[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}}")]   
 | 
			
		||||
    restart_to_config,
 | 
			
		||||
    RestartToConfig,
 | 
			
		||||
    #[strum(serialize = "Current low voltage detection is {{a}}")]   
 | 
			
		||||
    low_voltage,
 | 
			
		||||
    LowVoltage,
 | 
			
		||||
    #[strum(serialize = "Error communicating with battery!! {{txt_long}}")]   
 | 
			
		||||
    battery_communication_error,
 | 
			
		||||
    BatteryCommunicationError,
 | 
			
		||||
    #[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}}")]   
 | 
			
		||||
    raw_measure,
 | 
			
		||||
    RawMeasure,
 | 
			
		||||
    #[strum(serialize = "IP info: {{txt_long}}")]   
 | 
			
		||||
    wifi_info,
 | 
			
		||||
    WifiInfo,
 | 
			
		||||
    #[strum(serialize = "Plant:{{txt_short}} a:{{a}} b:{{b}}")]   
 | 
			
		||||
    test_sensor,
 | 
			
		||||
    TestSensor,
 | 
			
		||||
    #[strum(serialize = "Stay alive topic is {{txt_long}}")]   
 | 
			
		||||
    stay_alive,
 | 
			
		||||
    StayAlive,
 | 
			
		||||
    #[strum(serialize = "Connecting mqtt {{txt_short}} with id {{txt_long}}")]   
 | 
			
		||||
    mqtt_info,
 | 
			
		||||
    MqttInfo,
 | 
			
		||||
    #[strum(serialize = "Received stay alive with value {{txt_short}}")]   
 | 
			
		||||
    mqtt_stay_alive_rec,
 | 
			
		||||
    MqttStayAliveRec,
 | 
			
		||||
    #[strum(serialize = "Unknown topic recieved {{txt_long}}")]   
 | 
			
		||||
    unknown_topic,
 | 
			
		||||
    UnknownTopic,
 | 
			
		||||
    #[strum(serialize = "Partition state is {{txt_long}}")]   
 | 
			
		||||
    partition_state,
 | 
			
		||||
    PartitionState,
 | 
			
		||||
    #[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!")]
 | 
			
		||||
    mounting_filesystem,
 | 
			
		||||
    MountingFilesystem,
 | 
			
		||||
    #[strum(serialize = "Year inplausible, force config mode")]
 | 
			
		||||
    year_inplausible_force_config,
 | 
			
		||||
    YearInplausibleForceConfig,
 | 
			
		||||
    #[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")]
 | 
			
		||||
    config_mode_button_override,
 | 
			
		||||
    ConfigModeButtonOverride,
 | 
			
		||||
    #[strum(serialize = "Going to normal mode")]
 | 
			
		||||
    normal_run,
 | 
			
		||||
    NormalRun,
 | 
			
		||||
    #[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}}")]
 | 
			
		||||
    startup_info,
 | 
			
		||||
    StartupInfo,
 | 
			
		||||
    #[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}}")]
 | 
			
		||||
    enable_main,
 | 
			
		||||
    EnableMain,
 | 
			
		||||
    #[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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -22,6 +22,7 @@ use esp_idf_sys::{
 | 
			
		||||
    esp_ota_img_states_t_ESP_OTA_IMG_VALID, 
 | 
			
		||||
    vTaskDelay
 | 
			
		||||
};
 | 
			
		||||
use esp_ota::{mark_app_valid, rollback_and_reboot};
 | 
			
		||||
use log::log;
 | 
			
		||||
use once_cell::sync::Lazy;
 | 
			
		||||
use plant_hal::{PlantCtrlBoard, PlantHal, PLANT_COUNT};
 | 
			
		||||
@@ -29,12 +30,10 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    config::PlantControllerConfig,
 | 
			
		||||
    espota::{mark_app_valid, rollback_and_reboot},
 | 
			
		||||
    webserver::webserver::httpd,
 | 
			
		||||
};
 | 
			
		||||
mod log;
 | 
			
		||||
mod config;
 | 
			
		||||
pub mod espota;
 | 
			
		||||
pub mod plant_hal;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -147,8 +146,7 @@ fn safe_main() -> anyhow::Result<()> {
 | 
			
		||||
        bail!(
 | 
			
		||||
            "stack too small: {} bail!",
 | 
			
		||||
            esp_idf_sys::CONFIG_MAIN_TASK_STACK_SIZE
 | 
			
		||||
        );
 | 
			
		||||
        return Ok(());
 | 
			
		||||
        )
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    println!("Startup Rust");
 | 
			
		||||
@@ -186,16 +184,16 @@ fn safe_main() -> anyhow::Result<()> {
 | 
			
		||||
            &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();
 | 
			
		||||
    board.general_fault(false);
 | 
			
		||||
 | 
			
		||||
    log(log::LogMessage::mounting_filesystem, 0,0,"","");
 | 
			
		||||
    log(log::LogMessage::MountingFilesystem, 0,0,"","");
 | 
			
		||||
    board.mount_file_system()?;
 | 
			
		||||
    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(), "");
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -209,7 +207,6 @@ fn safe_main() -> anyhow::Result<()> {
 | 
			
		||||
                Ok(cur) => cur,
 | 
			
		||||
                Err(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
 | 
			
		||||
    if cur.year() < 2020 {
 | 
			
		||||
        to_config = true;
 | 
			
		||||
        log(log::LogMessage::year_inplausible_force_config, 0,0,"","");
 | 
			
		||||
        log(log::LogMessage::YearInplausibleForceConfig, 0,0,"","");
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    println!("cur is {}", cur);
 | 
			
		||||
 | 
			
		||||
    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 {
 | 
			
		||||
            board.general_fault(true);
 | 
			
		||||
            Delay::new_default().delay_ms(100);
 | 
			
		||||
@@ -236,7 +233,7 @@ fn safe_main() -> anyhow::Result<()> {
 | 
			
		||||
        board.set_restart_to_conf(false);
 | 
			
		||||
    } else if board.is_mode_override() {
 | 
			
		||||
        board.general_fault(true);
 | 
			
		||||
        log(log::LogMessage::config_mode_button_override, 0,0,"","");
 | 
			
		||||
        log(log::LogMessage::ConfigModeButtonOverride, 0,0,"","");
 | 
			
		||||
        for _i in 0..5 {
 | 
			
		||||
            board.general_fault(true);
 | 
			
		||||
            Delay::new_default().delay_ms(100);
 | 
			
		||||
@@ -258,7 +255,7 @@ fn safe_main() -> anyhow::Result<()> {
 | 
			
		||||
            config = valid;
 | 
			
		||||
        }
 | 
			
		||||
        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!
 | 
			
		||||
            let _ = board.wifi_ap(Option::None);
 | 
			
		||||
            drop(board);
 | 
			
		||||
@@ -362,7 +359,7 @@ fn safe_main() -> anyhow::Result<()> {
 | 
			
		||||
        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 {
 | 
			
		||||
        //check if client or ap mode and init wifi
 | 
			
		||||
@@ -373,7 +370,7 @@ fn safe_main() -> anyhow::Result<()> {
 | 
			
		||||
        let _webserver = httpd(reboot_now.clone());
 | 
			
		||||
        wait_infinity(WaitType::ConfigButton, reboot_now.clone());
 | 
			
		||||
    } 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 {
 | 
			
		||||
        ..Default::default()
 | 
			
		||||
    });
 | 
			
		||||
    let plant_to_pump = determine_next_plant(
 | 
			
		||||
    determine_plant_state(
 | 
			
		||||
        &mut plantstate,
 | 
			
		||||
        timezone_time,
 | 
			
		||||
        &tank_state,
 | 
			
		||||
        water_frozen,
 | 
			
		||||
        &config,
 | 
			
		||||
        &mut board,
 | 
			
		||||
    );
 | 
			
		||||
@@ -440,7 +436,7 @@ fn safe_main() -> anyhow::Result<()> {
 | 
			
		||||
 | 
			
		||||
    let pump_required = plantstate.iter().any(|it| it.do_water) && !water_frozen;
 | 
			
		||||
    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{
 | 
			
		||||
            board.any_pump(true)?;    
 | 
			
		||||
        }       
 | 
			
		||||
@@ -452,11 +448,11 @@ fn safe_main() -> anyhow::Result<()> {
 | 
			
		||||
                state.consecutive_pump_count = board.consecutive_pump_count(plant) + 1;
 | 
			
		||||
                board.store_consecutive_pump_count(plant, state.consecutive_pump_count);
 | 
			
		||||
                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;
 | 
			
		||||
                    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.last_pump_time(plant);
 | 
			
		||||
                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],
 | 
			
		||||
    cur: DateTime<Tz>,
 | 
			
		||||
    tank_state: &TankState,
 | 
			
		||||
    water_frozen: bool,
 | 
			
		||||
    config: &PlantControllerConfig,
 | 
			
		||||
    board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
 | 
			
		||||
) {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,8 +1,8 @@
 | 
			
		||||
use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver};
 | 
			
		||||
 | 
			
		||||
use chrono_tz::Tz;
 | 
			
		||||
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 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::wifi::config::{ScanConfig, ScanType};
 | 
			
		||||
use esp_idf_svc::wifi::EspWifi;
 | 
			
		||||
use log::logger;
 | 
			
		||||
use measurements::Temperature;
 | 
			
		||||
use once_cell::sync::Lazy;
 | 
			
		||||
use plant_ctrl2::sipo::ShiftRegister40;
 | 
			
		||||
@@ -32,7 +31,6 @@ use esp_idf_sys::esp_restart;
 | 
			
		||||
 | 
			
		||||
use anyhow::{anyhow, Context};
 | 
			
		||||
use anyhow::{bail, Ok, Result};
 | 
			
		||||
use ringbuffer::{ConstGenericRingBuffer, RingBuffer};
 | 
			
		||||
use serde::{Deserialize, Serialize};
 | 
			
		||||
use std::ffi::CString;
 | 
			
		||||
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 crate::config::{self, PlantControllerConfig};
 | 
			
		||||
use crate::espota::mark_app_valid;
 | 
			
		||||
use crate::log::log;
 | 
			
		||||
use crate::{plant_hal, to_string, STAY_ALIVE};
 | 
			
		||||
 | 
			
		||||
@@ -350,7 +347,7 @@ impl PlantCtrlBoard<'_> {
 | 
			
		||||
        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 mut file_system_corrupt = Option::None;
 | 
			
		||||
@@ -483,7 +480,7 @@ impl PlantCtrlBoard<'_> {
 | 
			
		||||
        let r2 = median * 50.0 / (3.3 - median);
 | 
			
		||||
        let mut percent = r2 / 190_f32 * 100_f32;
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
@@ -656,7 +653,7 @@ impl PlantCtrlBoard<'_> {
 | 
			
		||||
            delay.delay_ms(10);
 | 
			
		||||
            let unscaled = self.signal_counter.get_counter_value()? as i32;
 | 
			
		||||
            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.sort();
 | 
			
		||||
@@ -744,7 +741,7 @@ impl PlantCtrlBoard<'_> {
 | 
			
		||||
        }
 | 
			
		||||
        //update freertos registers ;)
 | 
			
		||||
        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)
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -890,7 +887,7 @@ impl PlantCtrlBoard<'_> {
 | 
			
		||||
                OkStd(b) => b as u32,
 | 
			
		||||
                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);
 | 
			
		||||
        Ok(())
 | 
			
		||||
@@ -939,7 +936,7 @@ impl PlantCtrlBoard<'_> {
 | 
			
		||||
        let round_trip_ok = Arc::new(AtomicBool::new(false));
 | 
			
		||||
        let round_trip_topic = format!("{}/internal/roundtrip", 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_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_ok_copy = round_trip_ok.clone();
 | 
			
		||||
        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 payload = event.payload();
 | 
			
		||||
            match payload {
 | 
			
		||||
@@ -965,10 +962,10 @@ impl PlantCtrlBoard<'_> {
 | 
			
		||||
                        } else if topic.eq(stay_alive_topic_copy.as_str()) {
 | 
			
		||||
                            let value =
 | 
			
		||||
                                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);
 | 
			
		||||
                        } else {
 | 
			
		||||
                            log(LogMessage::unknown_topic, 0 ,0,"", &topic);
 | 
			
		||||
                            log(LogMessage::UnknownTopic, 0 ,0,"", &topic);
 | 
			
		||||
                        }
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
@@ -1417,7 +1414,7 @@ impl PlantHal {
 | 
			
		||||
                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 {
 | 
			
		||||
            unsafe {
 | 
			
		||||
                LAST_WATERING_TIMESTAMP = [0; PLANT_COUNT];
 | 
			
		||||
@@ -1431,8 +1428,8 @@ impl PlantHal {
 | 
			
		||||
                if to_config_mode{
 | 
			
		||||
                    RESTART_TO_CONF = true; 
 | 
			
		||||
                }
 | 
			
		||||
                log(LogMessage::restart_to_config, RESTART_TO_CONF as u32, 0, "","");
 | 
			
		||||
                log(LogMessage::low_voltage, LOW_VOLTAGE_DETECTED as u32, 0, "","");
 | 
			
		||||
                log(LogMessage::RestartToConfig, RESTART_TO_CONF as u32, 0, "","");
 | 
			
		||||
                log(LogMessage::LowVoltage, LOW_VOLTAGE_DETECTED as u32, 0, "","");
 | 
			
		||||
                for i in 0..PLANT_COUNT {
 | 
			
		||||
                    println!(
 | 
			
		||||
                        "LAST_WATERING_TIMESTAMP[{}] = UTC {}",
 | 
			
		||||
@@ -1510,7 +1507,7 @@ impl PlantHal {
 | 
			
		||||
            
 | 
			
		||||
            },
 | 
			
		||||
            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())?;
 | 
			
		||||
 
 | 
			
		||||
@@ -5,11 +5,12 @@ use std::{
 | 
			
		||||
    sync::{atomic::AtomicBool, Arc},
 | 
			
		||||
};
 | 
			
		||||
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 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 embedded_svc::http::Method;
 | 
			
		||||
use esp_idf_hal::delay::Delay;
 | 
			
		||||
@@ -248,11 +249,10 @@ fn wifi_scan(
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn list_files(
 | 
			
		||||
    request: &mut Request<&mut EspHttpConnection>,
 | 
			
		||||
    _request: &mut Request<&mut EspHttpConnection>,
 | 
			
		||||
) -> Result<Option<std::string::String>, anyhow::Error> {
 | 
			
		||||
    let filename = query_param(request.uri(), "filename").unwrap_or_default();
 | 
			
		||||
    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)?;
 | 
			
		||||
    return anyhow::Ok(Some(file_list_json));
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user