Compare commits

..

No commits in common. "bfee21796a47eae49f1ffe5aebdf8cda53bbf92a" and "9d1a80780574451870d8ce98fb7811573b16f324" have entirely different histories.

7 changed files with 109 additions and 190 deletions

View File

@ -4,8 +4,8 @@ target = "xtensa-esp32-espidf"
[target.xtensa-esp32-espidf] [target.xtensa-esp32-espidf]
linker = "ldproxy" linker = "ldproxy"
#runner = "espflash flash --monitor --partition-table partitions.csv" # Select this runner for espflash v2.x.x #runner = "espflash flash --monitor --partition-table partitions.csv" # Select this runner for espflash v2.x.x
#runner = "espflash flash --monitor --baud 921600 --partition-table partitions.csv" # Select this runner for espflash v2.x.x runner = "espflash flash --monitor --baud 921600 --partition-table partitions.csv" # Select this runner for espflash v2.x.x
runner = "cargo runner" #runner = "cargo runner"
rustflags = [ "--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110 rustflags = [ "--cfg", "espidf_time64"] # Extending time_t for ESP IDF 5: https://github.com/esp-rs/rust/issues/110
[unstable] [unstable]

View File

@ -6,37 +6,20 @@ fn main() {
Command::new("rm") Command::new("rm")
.arg("./src/webserver/bundle.js") .arg("./src/webserver/bundle.js")
.output() .output()
.unwrap(); .expect("failed to execute process");
match Command::new("cmd").spawn() { let output = Command::new("npx")
Ok(_) => {
println!("Assuming build on windows");
let output = Command::new("cmd")
.arg("/K")
.arg("npx")
.arg("webpack") .arg("webpack")
.current_dir("./src_webpack") .current_dir("./src_webpack")
.output() .output()
.unwrap(); .expect("failed to execute process");
println!("status: {}", output.status); println!("status: {}", output.status);
println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
assert!(output.status.success()); assert!(output.status.success());
}
Err(_) => {
println!("Assuming build on linux");
let output = Command::new("bash")
.arg("webpack")
.current_dir("./src_webpack")
.output()
.unwrap();
println!("status: {}", output.status);
println!("stdout: {}", String::from_utf8_lossy(&output.stdout));
println!("stderr: {}", String::from_utf8_lossy(&output.stderr));
assert!(output.status.success());
}
}
embuild::espidf::sysenv::output(); embuild::espidf::sysenv::output();
let _ = EmitBuilder::builder().all_git().all_build().emit(); let _ = EmitBuilder::builder().all_git().emit();
} }

View File

@ -1,3 +1,3 @@
[toolchain] [toolchain]
channel = "nightly" channel = "nightly"
toolchain = "esp"

View File

@ -5,4 +5,4 @@ CONFIG_ESP_MAIN_TASK_STACK_SIZE=20000
# This allows to use 1 ms granuality for thread sleeps (10 ms by default). # This allows to use 1 ms granuality for thread sleeps (10 ms by default).
CONFIG_FREERTOS_HZ=1000 CONFIG_FREERTOS_HZ=1000
CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=y CONFIG_BOOTLOADER_APP_ROLLBACK_ENABLE=true

View File

@ -60,7 +60,7 @@ pub struct Plant {
pub pump_hour_start: u8, pub pump_hour_start: u8,
pub pump_hour_end: u8, pub pump_hour_end: u8,
pub sensor_b: bool, pub sensor_b: bool,
pub sensor_p: bool, pub sensor_p: bool
} }
impl Default for Plant { impl Default for Plant {
fn default() -> Self { fn default() -> Self {
@ -71,8 +71,8 @@ impl Default for Plant {
pump_hour_start: 8, pump_hour_start: 8,
pump_hour_end: 20, pump_hour_end: 20,
mode: Mode::OFF, mode: Mode::OFF,
sensor_b: false, sensor_b : false,
sensor_p: false, sensor_p : false
} }
} }
} }

View File

@ -1,11 +1,16 @@
use std::sync::{atomic::AtomicBool, Arc, Mutex}; use std::{
env,
sync::{atomic::AtomicBool, Arc, Mutex},
};
use chrono::{DateTime, Datelike, Duration, NaiveDateTime, Timelike}; use chrono::{DateTime, Datelike, Duration, NaiveDateTime, Timelike};
use chrono_tz::{Europe::Berlin, Tz}; use chrono_tz::{Europe::Berlin, Tz};
use config::Plant;
use embedded_svc::mqtt;
use esp_idf_hal::delay::Delay; use esp_idf_hal::delay::Delay;
use esp_idf_sys::{ use esp_idf_sys::{
esp_deep_sleep, esp_ota_get_app_partition_count, esp_ota_get_running_partition, esp_ota_get_state_partition, esp_ota_img_states_t, esp_ota_img_states_t_ESP_OTA_IMG_ABORTED, esp_ota_img_states_t_ESP_OTA_IMG_INVALID, esp_ota_img_states_t_ESP_OTA_IMG_NEW, esp_ota_img_states_t_ESP_OTA_IMG_PENDING_VERIFY, esp_ota_img_states_t_ESP_OTA_IMG_UNDEFINED, esp_ota_img_states_t_ESP_OTA_IMG_VALID, esp_restart, gpio_deep_sleep_hold_dis, gpio_deep_sleep_hold_en, vTaskDelay, CONFIG_FREERTOS_HZ esp_deep_sleep, esp_restart, gpio_deep_sleep_hold_dis, gpio_deep_sleep_hold_en, vTaskDelay,
CONFIG_FREERTOS_HZ,
}; };
use log::error; use log::error;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
@ -14,7 +19,7 @@ use serde::{Deserialize, Serialize};
use crate::{ use crate::{
config::{Config, WifiConfig}, config::{Config, WifiConfig},
espota::{mark_app_valid, rollback_and_reboot}, espota::rollback_and_reboot,
webserver::webserver::{httpd, httpd_initial}, webserver::webserver::{httpd, httpd_initial},
}; };
mod config; mod config;
@ -79,7 +84,7 @@ struct PlantState {
sensor_error_b: Option<SensorError>, sensor_error_b: Option<SensorError>,
sensor_error_p: Option<SensorError>, sensor_error_p: Option<SensorError>,
out_of_work_hour: bool, out_of_work_hour: bool,
next_pump: Option<DateTime<Tz>>, next_pump: Option<DateTime<Tz>>
} }
#[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)] #[derive(Serialize, Deserialize, Copy, Clone, Debug, PartialEq)]
@ -116,41 +121,28 @@ fn safe_main() -> anyhow::Result<()> {
log::info!("Startup Rust"); log::info!("Startup Rust");
let git_hash = env!("VERGEN_GIT_DESCRIBE"); let git_hash = env!("VERGEN_GIT_DESCRIBE");
let build_timestamp = env!("VERGEN_BUILD_TIMESTAMP"); println!("Version useing git has {}", git_hash);
println!(
"Version useing git has {} build on {}",
git_hash, build_timestamp
);
let count = unsafe { esp_ota_get_app_partition_count() }; let partition_state: embedded_svc::ota::SlotState = embedded_svc::ota::SlotState::Unknown;
println!("Partition count is {}", count); match esp_idf_svc::ota::EspOta::new() {
let mut ota_state: esp_ota_img_states_t = 0; Ok(ota) => {
let running_partition = unsafe { esp_ota_get_running_partition() }; //match ota.get_running_slot(){
let address = unsafe { // Ok(slot) => {
(*running_partition).address // partition_state = slot.state;
// println!(
}; // "Booting from {} with state {:?}",
println!("Partition address is {}", address); // slot.label, partition_state
// );
let ota_state_string = unsafe { //},
esp_ota_get_state_partition(running_partition, &mut ota_state); // Err(err) => {
if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_NEW { // println!("Error getting running slot {}", err);
format!("Partition state is {}", "ESP_OTA_IMG_NEW") // },
} else if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_PENDING_VERIFY { //}
format!("Partition state is {}", "ESP_OTA_IMG_PENDING_VERIFY") }
} else if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_VALID { Err(err) => {
format!("Partition state is {}", "ESP_OTA_IMG_VALID") println!("Error obtaining ota info {}", err);
} else if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_INVALID { }
format!("Partition state is {}", "ESP_OTA_IMG_INVALID")
} else if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_ABORTED {
format!("Partition state is {}", "ESP_OTA_IMG_ABORTED")
} else if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_UNDEFINED {
format!("Partition state is {}", "ESP_OTA_IMG_UNDEFINED")
} else {
format!("Partition state is {}", ota_state)
} }
};
println!("{}", ota_state_string);
println!("Board hal init"); println!("Board hal init");
let mut board: std::sync::MutexGuard<'_, PlantCtrlBoard<'_>> = BOARD_ACCESS.lock().unwrap(); let mut board: std::sync::MutexGuard<'_, PlantCtrlBoard<'_>> = BOARD_ACCESS.lock().unwrap();
@ -220,10 +212,14 @@ fn safe_main() -> anyhow::Result<()> {
} }
Err(err) => { Err(err) => {
if board.is_wifi_config_file_existant() { if board.is_wifi_config_file_existant() {
if ota_state == esp_ota_img_states_t_ESP_OTA_IMG_PENDING_VERIFY { match partition_state {
embedded_svc::ota::SlotState::Invalid
| embedded_svc::ota::SlotState::Unverified => {
println!("Config seem to be unparsable after upgrade, reverting"); println!("Config seem to be unparsable after upgrade, reverting");
rollback_and_reboot()?; rollback_and_reboot()?;
} }
_ => {}
}
} }
println!("Missing wifi config, entering initial config mode {}", err); println!("Missing wifi config, entering initial config mode {}", err);
board.wifi_ap().unwrap(); board.wifi_ap().unwrap();
@ -293,26 +289,15 @@ fn safe_main() -> anyhow::Result<()> {
if online_mode == OnlineMode::Online { if online_mode == OnlineMode::Online {
let _ = board.mqtt_publish(&config, "/firmware/githash", git_hash.as_bytes()); let _ = board.mqtt_publish(&config, "/firmware/githash", git_hash.as_bytes());
let _ = board.mqtt_publish(&config, "/firmware/buildtime", build_timestamp.as_bytes());
let _ = board.mqtt_publish(&config, "/firmware/last_online", europe_time.to_rfc3339().as_bytes());
let _ = board.mqtt_publish(&config, "/firmware/ota_state", ota_state_string.as_bytes());
let _ = board.mqtt_publish(&config, "/firmware/partition_address", format!("{:#06x}",address).as_bytes());
let _ = board.mqtt_publish(&config, "/state", "online".as_bytes()); let _ = board.mqtt_publish(&config, "/state", "online".as_bytes());
let _ = board.mqtt_publish(&config, "/last_online", europe_time.to_rfc3339().as_bytes());
publish_battery_state(&mut board, &config); publish_battery_state(&mut board, &config);
} }
let tank_state = determine_tank_state(&mut board, &config); let tank_state = determine_tank_state(&mut board, &config);
if online_mode == OnlineMode::Online {
if tank_state.sensor_error {
let _ = board.mqtt_publish(&config, "/water/ml", "error".to_string().as_bytes());
} else {
let _ = board.mqtt_publish(&config, "/water/ml", tank_state.left_ml.to_string().as_bytes());
let _ = board.mqtt_publish(&config, "/water/enough_water", tank_state.enough_water.to_string().as_bytes());
let _ = board.mqtt_publish(&config, "/water/raw", tank_state.raw.to_string().as_bytes());
}
}
let mut water_frozen = false; let mut water_frozen = false;
for _attempt in 0..5 { for _attempt in 0..5 {
@ -369,9 +354,10 @@ fn safe_main() -> anyhow::Result<()> {
board.store_consecutive_pump_count(plant, consecutive_pump_count); board.store_consecutive_pump_count(plant, consecutive_pump_count);
let plant_config = config.plants[plant]; let plant_config = config.plants[plant];
if plant_config.sensor_p { if plant_config.sensor_p {
match map_range_moisture( match map_range_moisture(
board.measure_moisture_hz(plant, plant_hal::Sensor::PUMP)? as f32, board.measure_moisture_hz(plant, plant_hal::Sensor::PUMP)? as f32
) { ) {
Ok(p) => state.p = Some(p), Ok(p) => state.p = Some(p),
Err(err) => { Err(err) => {
@ -391,34 +377,36 @@ fn safe_main() -> anyhow::Result<()> {
board.pump(plant, true)?; board.pump(plant, true)?;
board.last_pump_time(plant); board.last_pump_time(plant);
state.active = true; state.active = true;
for _ in 0..plant_config.pump_time_s { for t in 0..plant_config.pump_time_s {
//FIXME do periodic pump test here and state update
unsafe { vTaskDelay(CONFIG_FREERTOS_HZ) }; unsafe { vTaskDelay(CONFIG_FREERTOS_HZ) };
if plant_config.sensor_p { if plant_config.sensor_p {
let moist = map_range_moisture( let moist = map_range_moisture(
board.measure_moisture_hz(plant, plant_hal::Sensor::PUMP)? as f32, board.measure_moisture_hz(plant, plant_hal::Sensor::PUMP)? as f32);
);
if online_mode == OnlineMode::Online { if online_mode == OnlineMode::Online {
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Sensor P after", plant + 1).as_str(), format!("/plant{}/Sensor P after", plant+1).as_str(),
option_to_string(moist.ok()).as_bytes(), option_to_string(moist.ok()).as_bytes(),
); );
} }
} else { } else {
if online_mode == OnlineMode::Online { if online_mode == OnlineMode::Online {
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Sensor P after", plant + 1).as_str(), format!("/plant{}/Sensor P after", plant+1).as_str(),
"disabled".as_bytes(), "disabled".as_bytes(),
); );
} }
} }
} }
board.pump(plant, false)?; board.pump(plant, false)?;
if plant_config.sensor_p { if plant_config.sensor_p {
match map_range_moisture( match map_range_moisture(
board.measure_moisture_hz(plant, plant_hal::Sensor::PUMP)? as f32, board.measure_moisture_hz(plant, plant_hal::Sensor::PUMP)? as f32
) { ) {
Ok(p) => state.after_p = Some(p), Ok(p) => state.after_p = Some(p),
Err(err) => { Err(err) => {
@ -435,6 +423,7 @@ fn safe_main() -> anyhow::Result<()> {
//mqtt sync pump error value //mqtt sync pump error value
} }
} }
} }
None => { None => {
println!("Nothing to do"); println!("Nothing to do");
@ -446,8 +435,7 @@ fn safe_main() -> anyhow::Result<()> {
let mut light_state = LightState { let mut light_state = LightState {
..Default::default() ..Default::default()
}; };
let is_day = board.is_day(); light_state.is_day = board.is_day();
light_state.is_day = is_day;
light_state.out_of_work_hour = !in_time_range( light_state.out_of_work_hour = !in_time_range(
europe_time, europe_time,
config.night_lamp_hour_start, config.night_lamp_hour_start,
@ -502,42 +490,12 @@ fn safe_main() -> anyhow::Result<()> {
unsafe { gpio_deep_sleep_hold_dis() }; unsafe { gpio_deep_sleep_hold_dis() };
unsafe { gpio_deep_sleep_hold_en() }; unsafe { gpio_deep_sleep_hold_en() };
let deep_sleep_duration_minutes: u32 = if state_of_charge < 10 {
if online_mode == OnlineMode::Online {
let _ = board.mqtt_publish(
&config,
"/deepsleep",
"Entering low voltage long deep sleep".as_bytes(),
);
}
12 * 60
} else if is_day {
if online_mode == OnlineMode::Online {
let _ = board.mqtt_publish(
&config,
"/deepsleep",
"Entering normal mode 20m deep sleep".as_bytes(),
);
}
20
} else {
if online_mode == OnlineMode::Online {
let _ = board.mqtt_publish(
&config,
"/deepsleep",
"Entering night mode 1h deep sleep".as_bytes(),
);
}
60
};
//determine next event //determine next event
//is light out of work trigger soon? //is light out of work trigger soon?
//is battery low ?? //is battery low ??
//is deep sleep //is deep sleep
mark_app_valid(); unsafe { esp_deep_sleep(1000 * 1000 * 20) };
unsafe { esp_deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64) };
} }
fn publish_battery_state( fn publish_battery_state(
@ -813,10 +771,9 @@ fn determine_state_target_moisture_for_plant(
if a_low || b_low { if a_low || b_low {
state.dry = true; state.dry = true;
if tank_state.sensor_error && !config.tank_allow_pumping_if_sensor_error { if tank_state.sensor_error && !config.tank_allow_pumping_if_sensor_error
//ignore is ok || !tank_state.enough_water
} {
else if !tank_state.enough_water {
state.no_water = true; state.no_water = true;
} }
} }
@ -944,39 +901,24 @@ fn determine_next_plant(
return None; return None;
} }
fn update_plant_state( fn update_plant_state(plantstate: &mut [PlantState; PLANT_COUNT], board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>, config: &Config){
plantstate: &mut [PlantState; PLANT_COUNT],
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
config: &Config,
) {
for plant in 0..PLANT_COUNT { for plant in 0..PLANT_COUNT {
let state = &plantstate[plant]; let state = &plantstate[plant];
let plant_config = config.plants[plant]; let plant_config = config.plants[plant];
let _ = board.mqtt_publish(
&config,
format!("/plant{}/mode", plant + 1).as_str(),
match plant_config.mode {
config::Mode::OFF => "OFF".as_bytes(),
config::Mode::TargetMoisture => "TargetMoisture".as_bytes(),
config::Mode::TimerOnly => "TimerOnly".as_bytes(),
config::Mode::TimerAndDeadzone => "TimerAndDeadzone".as_bytes(),
},
);
let last_time = board.last_pump_time(plant); let last_time = board.last_pump_time(plant);
let europe_time = last_time.with_timezone(&Berlin); let europe_time = last_time.with_timezone(&Berlin);
if europe_time.year() > 2023 { if europe_time.year() > 2023 {
let time = europe_time.to_rfc3339(); let time = europe_time.to_rfc3339();
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/last pump", plant + 1).as_str(), format!("/plant{}/last pump", plant+1).as_str(),
time.as_bytes(), time.as_bytes(),
); );
} else { } else {
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/last pump", plant + 1).as_str(), format!("/plant{}/last pump", plant+1).as_str(),
"N/A".as_bytes(), "N/A".as_bytes(),
); );
} }
@ -986,34 +928,35 @@ fn update_plant_state(
let time = next.to_rfc3339(); let time = next.to_rfc3339();
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/next pump", plant + 1).as_str(), format!("/plant{}/next pump", plant+1).as_str(),
time.as_bytes(), time.as_bytes(),
); );
} },
None => { None => {
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/next pump", plant + 1).as_str(), format!("/plant{}/next pump", plant+1).as_str(),
"N/A".as_bytes(), "N/A".as_bytes(),
); );
},
} }
}
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Sensor A", plant + 1).as_str(), format!("/plant{}/Sensor A", plant+1).as_str(),
option_to_string(state.a).as_bytes(), option_to_string(state.a).as_bytes(),
); );
if plant_config.sensor_b { if plant_config.sensor_b {
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Sensor B", plant + 1).as_str(), format!("/plant{}/Sensor B", plant+1).as_str(),
option_to_string(state.b).as_bytes(), option_to_string(state.b).as_bytes(),
); );
} else { } else {
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Sensor B", plant + 1).as_str(), format!("/plant{}/Sensor B", plant+1).as_str(),
"disabled".as_bytes(), "disabled".as_bytes(),
); );
} }
@ -1021,65 +964,65 @@ fn update_plant_state(
if plant_config.sensor_p { if plant_config.sensor_p {
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Sensor P before", plant + 1).as_str(), format!("/plant{}/Sensor P before", plant+1).as_str(),
option_to_string(state.p).as_bytes(), option_to_string(state.p).as_bytes(),
); );
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Sensor P after", plant + 1).as_str(), format!("/plant{}/Sensor P after", plant+1).as_str(),
option_to_string(state.after_p).as_bytes(), option_to_string(state.after_p).as_bytes(),
); );
} else { } else {
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Sensor P before", plant + 1).as_str(), format!("/plant{}/Sensor P before", plant+1).as_str(),
"disabled".as_bytes(), "disabled".as_bytes(),
); );
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Sensor P after", plant + 1).as_str(), format!("/plant{}/Sensor P after", plant+1).as_str(),
"disabled".as_bytes(), "disabled".as_bytes(),
); );
} }
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Should water", plant + 1).as_str(), format!("/plant{}/Should water", plant+1).as_str(),
state.do_water.to_string().as_bytes(), state.do_water.to_string().as_bytes(),
); );
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Is frozen", plant + 1).as_str(), format!("/plant{}/Is frozen", plant+1).as_str(),
state.frozen.to_string().as_bytes(), state.frozen.to_string().as_bytes(),
); );
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Is dry", plant + 1).as_str(), format!("/plant{}/Is dry", plant+1).as_str(),
state.dry.to_string().as_bytes(), state.dry.to_string().as_bytes(),
); );
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Pump Error", plant + 1).as_str(), format!("/plant{}/Pump Error", plant+1).as_str(),
state.pump_error.to_string().as_bytes(), state.pump_error.to_string().as_bytes(),
); );
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Pump Ineffective", plant + 1).as_str(), format!("/plant{}/Pump Ineffective", plant+1).as_str(),
state.not_effective.to_string().as_bytes(), state.not_effective.to_string().as_bytes(),
); );
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Is in Cooldown", plant + 1).as_str(), format!("/plant{}/Is in Cooldown", plant+1).as_str(),
state.cooldown.to_string().as_bytes(), state.cooldown.to_string().as_bytes(),
); );
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/No Water", plant + 1).as_str(), format!("/plant{}/No Water", plant+1).as_str(),
state.no_water.to_string().as_bytes(), state.no_water.to_string().as_bytes(),
); );
let _ = board.mqtt_publish( let _ = board.mqtt_publish(
&config, &config,
format!("/plant{}/Out of Work Hour", plant + 1).as_str(), format!("/plant{}/Out of Work Hour", plant+1).as_str(),
state.out_of_work_hour.to_string().as_bytes(), state.out_of_work_hour.to_string().as_bytes(),
); );
} }
@ -1126,18 +1069,7 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
fn main() { fn main() {
let result = safe_main(); let result = safe_main();
match result { result.unwrap();
Ok(_) => {
println!("Main app finished, restarting");
unsafe { esp_restart() };
},
Err(err) => {
println!("Failed main {}", err);
let rollback_successful = rollback_and_reboot();
println!("Failed to rollback :(");
rollback_successful.unwrap();
},
}
} }
//error codes //error codes
//error_reading_config_after_upgrade //error_reading_config_after_upgrade

View File

@ -159,6 +159,7 @@ pub struct PlantCtrlBoard<'a> {
PinDriver<'a, esp_idf_hal::gpio::Gpio22, InputOutput>, PinDriver<'a, esp_idf_hal::gpio::Gpio22, InputOutput>,
PinDriver<'a, esp_idf_hal::gpio::Gpio19, InputOutput>, PinDriver<'a, esp_idf_hal::gpio::Gpio19, InputOutput>,
>, >,
low_voltage_detected: Mutex<bool>,
tank_driver: AdcDriver<'a, esp_idf_hal::adc::ADC1>, tank_driver: AdcDriver<'a, esp_idf_hal::adc::ADC1>,
tank_channel: esp_idf_hal::adc::AdcChannelDriver<'a, { attenuation::DB_11 }, Gpio39>, tank_channel: esp_idf_hal::adc::AdcChannelDriver<'a, { attenuation::DB_11 }, Gpio39>,
solar_is_day: PinDriver<'a, esp_idf_hal::gpio::Gpio25, esp_idf_hal::gpio::Input>, solar_is_day: PinDriver<'a, esp_idf_hal::gpio::Gpio25, esp_idf_hal::gpio::Input>,
@ -962,6 +963,8 @@ impl CreatePlantHal<'_> for PlantHal {
let nvs = EspDefaultNvsPartition::take()?; let nvs = EspDefaultNvsPartition::take()?;
let wifi_driver = EspWifi::new(peripherals.modem, sys_loop, Some(nvs))?; let wifi_driver = EspWifi::new(peripherals.modem, sys_loop, Some(nvs))?;
let low_voltage_detected = Mutex::new(unsafe { LOW_VOLTAGE_DETECTED });
let adc_config = esp_idf_hal::adc::config::Config { let adc_config = esp_idf_hal::adc::config::Config {
resolution: esp_idf_hal::adc::config::Resolution::Resolution12Bit, resolution: esp_idf_hal::adc::config::Resolution::Resolution12Bit,
calibration: true, calibration: true,
@ -998,6 +1001,7 @@ impl CreatePlantHal<'_> for PlantHal {
} }
let rv = Mutex::new(PlantCtrlBoard { let rv = Mutex::new(PlantCtrlBoard {
shift_register, shift_register,
low_voltage_detected,
tank_driver, tank_driver,
tank_channel, tank_channel,
solar_is_day, solar_is_day,