warnings to zero :)
This commit is contained in:
parent
2ae363b6d2
commit
b2d5cf6416
@ -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));
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user