adust pcb to new 3.3 software improvements

This commit is contained in:
2024-12-30 20:19:37 +01:00
parent 58b63fc8ee
commit 1927449c1d
22 changed files with 16833 additions and 13984 deletions

View File

@@ -10,11 +10,7 @@ use chrono_tz::{Europe::Berlin, Tz};
use config::Mode;
use esp_idf_hal::delay::Delay;
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, vTaskDelay, CONFIG_FREERTOS_HZ,
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, esp_sleep_enable_ext1_wakeup, esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW, vTaskDelay, CONFIG_FREERTOS_HZ
};
use log::error;
use once_cell::sync::Lazy;
@@ -126,16 +122,6 @@ struct PlantStateMQTT<'a> {
last_pump: &'a str,
next_pump: &'a str,
}
#[derive(Serialize)]
struct BatteryState<'a> {
voltage_milli_volt: &'a str,
current_milli_ampere: &'a str,
cycle_count: &'a str,
design_milli_ampere: &'a str,
remaining_milli_ampere: &'a str,
state_of_charge: &'a str,
state_of_health: &'a str,
}
fn safe_main() -> anyhow::Result<()> {
// It is necessary to call this function once. Otherwise some patches to the runtime
@@ -155,15 +141,16 @@ fn safe_main() -> anyhow::Result<()> {
log::info!("Startup Rust");
let git_hash = env!("VERGEN_GIT_DESCRIBE");
let build_timestamp = env!("VERGEN_BUILD_TIMESTAMP");
let mut to_config = false;
let version = get_version();
println!(
"Version useing git has {} build on {}",
git_hash, build_timestamp
version.git_hash, version.build_time
);
let count = unsafe { esp_ota_get_app_partition_count() };
println!("Partit ion count is {}", count);
println!("Partition count is {}", count);
let mut ota_state: esp_ota_img_states_t = 0;
let running_partition = unsafe { esp_ota_get_running_partition() };
let address = unsafe { (*running_partition).address };
@@ -192,6 +179,7 @@ fn safe_main() -> anyhow::Result<()> {
println!("Board hal init");
let mut board: std::sync::MutexGuard<'_, PlantCtrlBoard<'_>> = BOARD_ACCESS.lock().unwrap();
board.general_fault(false);
println!("Mounting filesystem");
board.mount_file_system()?;
let free_space = board.file_system_size()?;
@@ -232,8 +220,18 @@ fn safe_main() -> anyhow::Result<()> {
println!("cur is {}", cur);
let mut to_config = false;
if board.is_mode_override() {
if board.get_restart_to_conf() {
println!("config mode software override");
for _i in 0..2 {
board.general_fault(true);
Delay::new_default().delay_ms(100);
board.general_fault(false);
Delay::new_default().delay_ms(100);
}
to_config = true;
board.general_fault(true);
board.set_restart_to_conf(false);
} else if board.is_mode_override() {
board.general_fault(true);
println!("config mode override is pressed, waiting 5s");
for _i in 0..5 {
@@ -244,11 +242,15 @@ fn safe_main() -> anyhow::Result<()> {
}
if board.is_mode_override() {
board.general_fault(true);
to_config = true;
} else {
board.general_fault(false);
}
} else {
println!("No config override start detected");
}
let config: PlantControllerConfig;
match board.get_config() {
@@ -293,7 +295,7 @@ fn safe_main() -> anyhow::Result<()> {
board.general_fault(true);
}
}
if (config.network.mqtt_url.is_some()) {
if config.network.mqtt_url.is_some() {
match board.mqtt(&config) {
Ok(_) => {
println!("Mqtt connection ready");
@@ -338,8 +340,12 @@ fn safe_main() -> anyhow::Result<()> {
if mqtt {
let ip_string = ip_address.unwrap_or("N/A".to_owned());
let _ = board.mqtt_publish(&config, "/firmware/address", ip_string.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/githash", version.git_hash.as_bytes());
let _ = board.mqtt_publish(
&config,
"/firmware/buildtime",
version.build_time.as_bytes(),
);
let _ = board.mqtt_publish(
&config,
"/firmware/last_online",
@@ -425,6 +431,7 @@ fn safe_main() -> anyhow::Result<()> {
);
let stay_alive_mqtt = STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed);
let stay_alive = stay_alive_mqtt;
println!("Check stay alive, current state is {}", stay_alive);
@@ -550,23 +557,17 @@ fn safe_main() -> anyhow::Result<()> {
let _webserver = httpd(reboot_now.clone());
wait_infinity(WaitType::MqttConfig, reboot_now.clone());
}
unsafe { esp_deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64) };
unsafe {
esp_sleep_enable_ext1_wakeup(0b10u64, esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW);
esp_deep_sleep(1000 * 1000 * 60 * deep_sleep_duration_minutes as u64)
};
}
fn publish_battery_state(
board: &mut std::sync::MutexGuard<'_, PlantCtrlBoard<'_>>,
config: &PlantControllerConfig,
) {
let bat = BatteryState {
voltage_milli_volt: &to_string(&board.voltage_milli_volt()),
current_milli_ampere: &to_string(&board.average_current_milli_ampere()),
cycle_count: &to_string(&board.cycle_count()),
design_milli_ampere: &to_string(&board.design_milli_ampere_hour()),
remaining_milli_ampere: &to_string(&board.remaining_milli_ampere_hour()),
state_of_charge: &to_string(&board.state_charge_percent()),
state_of_health: &to_string(&board.state_health_percent()),
};
let bat = board.get_battery_state();
match serde_json::to_string(&bat) {
Ok(state) => {
let _ = board.mqtt_publish(&config, "/battery", state.as_bytes());
@@ -1005,7 +1006,7 @@ fn sensor_to_string(value: &Option<u8>, error: &Option<SensorError>, enabled: bo
};
}
fn to_string<T: Display>(value: &Result<T>) -> String {
fn to_string<T: Display>(value: Result<T>) -> String {
return match value {
Ok(v) => v.to_string(),
Err(err) => {
@@ -1054,3 +1055,19 @@ fn in_time_range(cur: &DateTime<Tz>, start: u8, end: u8) -> bool {
return curhour > start || curhour < end;
}
}
fn get_version() -> VersionInfo {
let branch = env!("VERGEN_GIT_BRANCH").to_owned();
let hash = &env!("VERGEN_GIT_SHA")[0..8];
return VersionInfo {
git_hash: (branch + "@" + hash),
build_time: env!("VERGEN_BUILD_TIMESTAMP").to_owned(),
};
}
#[derive(Serialize, Debug)]
struct VersionInfo {
git_hash: String,
build_time: String,
}

View File

@@ -53,7 +53,7 @@ use esp_idf_sys::{esp, esp_spiffs_check, gpio_hold_dis, gpio_hold_en, vTaskDelay
use one_wire_bus::OneWire;
use crate::config::{self, PlantControllerConfig};
use crate::{plant_hal, STAY_ALIVE};
use crate::{plant_hal, to_string, STAY_ALIVE};
//Only support for 8 right now!
pub const PLANT_COUNT: usize = 8;
@@ -117,6 +117,9 @@ static mut CONSECUTIVE_WATERING_PLANT: [u32; PLANT_COUNT] = [0; PLANT_COUNT];
#[link_section = ".rtc.data"]
static mut LOW_VOLTAGE_DETECTED: bool = false;
#[link_section = ".rtc.data"]
static mut RESTART_TO_CONF: bool = false;
pub struct FileSystemSizeInfo {
pub total_size: usize,
pub used_size: usize,
@@ -180,7 +183,33 @@ pub struct FileList {
iter_error: Option<String>,
}
#[derive(Serialize)]
pub struct BatteryState {
voltage_milli_volt: String,
current_milli_ampere: String,
cycle_count: String,
design_milli_ampere: String,
remaining_milli_ampere: String,
state_of_charge: String,
state_of_health: String,
temperature: String,
}
impl PlantCtrlBoard<'_> {
pub fn get_battery_state(&mut self) -> BatteryState {
let bat = BatteryState {
voltage_milli_volt: to_string(self.voltage_milli_volt()),
current_milli_ampere: to_string(self.average_current_milli_ampere()),
cycle_count: to_string(self.cycle_count()),
design_milli_ampere: to_string(self.design_milli_ampere_hour()),
remaining_milli_ampere: to_string(self.remaining_milli_ampere_hour()),
state_of_charge: to_string(self.state_charge_percent()),
state_of_health: to_string(self.state_health_percent()),
temperature: to_string(self.bat_temperature()),
};
return bat;
}
pub fn list_files(&self, filename: &str) -> FileList {
let storage = CString::new(SPIFFS_PARTITION_NAME).unwrap();
let error = unsafe {
@@ -956,6 +985,16 @@ impl PlantCtrlBoard<'_> {
};
}
pub fn get_restart_to_conf(&mut self) -> bool {
return unsafe { RESTART_TO_CONF };
}
pub fn set_restart_to_conf(&mut self, to_conf: bool) {
unsafe {
RESTART_TO_CONF = to_conf;
}
}
pub fn state_charge_percent(&mut self) -> Result<u8> {
match self.battery_driver.state_of_charge() {
OkStd(r) => Ok(r),
@@ -1012,6 +1051,13 @@ impl PlantCtrlBoard<'_> {
}
}
pub fn bat_temperature(&mut self) -> Result<u16> {
match self.battery_driver.temperature() {
OkStd(r) => Ok(r as u16),
Err(err) => bail!("Error reading Temperature {:?}", err),
}
}
pub fn flash_bq34_z100(&mut self, line: &str, dryrun: bool) -> Result<()> {
match self.battery_driver.write_flash_stream_i2c(line, dryrun) {
OkStd(r) => Ok(r),
@@ -1193,32 +1239,60 @@ impl PlantHal {
ms4.set_high()?;
//init,reset rtc memory depending on cause
let mut init_rtc_store: bool = false;
let mut to_config_mode: bool = false;
let reasons = ResetReason::get();
let reset_store = match reasons {
ResetReason::Software => false,
ResetReason::ExternalPin => false,
ResetReason::Watchdog => true,
ResetReason::Sdio => true,
ResetReason::Panic => true,
ResetReason::InterruptWatchdog => true,
ResetReason::PowerOn => true,
ResetReason::Unknown => true,
ResetReason::Brownout => true,
ResetReason::TaskWatchdog => true,
ResetReason::DeepSleep => false,
ResetReason::USBPeripheral => true,
ResetReason::JTAG => true,
match reasons {
ResetReason::Software => {},
ResetReason::ExternalPin => {},
ResetReason::Watchdog => {
init_rtc_store = true;
},
ResetReason::Sdio => {
init_rtc_store = true
},
ResetReason::Panic => {
init_rtc_store = true
},
ResetReason::InterruptWatchdog => {
init_rtc_store = true
},
ResetReason::PowerOn => {
init_rtc_store = true
},
ResetReason::Unknown => {
init_rtc_store = true
},
ResetReason::Brownout => {
init_rtc_store = true
},
ResetReason::TaskWatchdog => {
init_rtc_store = true
},
ResetReason::DeepSleep => {},
ResetReason::USBPeripheral => {
init_rtc_store = true;
to_config_mode = true;
},
ResetReason::JTAG => {
init_rtc_store = true
},
};
if reset_store {
println!("Clear and reinit RTC store");
println!("Reset due to {:?} requires rtc clear {} and force config mode {}", reasons, init_rtc_store, to_config_mode);
if init_rtc_store {
unsafe {
LAST_WATERING_TIMESTAMP = [0; PLANT_COUNT];
CONSECUTIVE_WATERING_PLANT = [0; PLANT_COUNT];
LOW_VOLTAGE_DETECTED = false;
RESTART_TO_CONF = to_config_mode;
};
} else {
println!("Keeping RTC store");
unsafe {
if to_config_mode{
RESTART_TO_CONF = true;
}
println!("Current restart to conf mode{:?}", RESTART_TO_CONF);
println!(
"Current low voltage detection is {:?}",
LOW_VOLTAGE_DETECTED

2050
rust/src/webserver/bootstrap-grid.css vendored Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,9 @@ use std::{
sync::{atomic::AtomicBool, Arc},
};
use crate::{espota::OtaUpdate, map_range_moisture, plant_hal::FileInfo, BOARD_ACCESS};
use crate::{
espota::OtaUpdate, get_version, map_range_moisture, plant_hal::FileInfo, BOARD_ACCESS,
};
use anyhow::bail;
use chrono::DateTime;
use core::result::Result::Ok;
@@ -29,12 +31,6 @@ struct FileList {
file: Vec<FileInfo>,
}
#[derive(Serialize, Debug)]
struct VersionInfo<'a> {
git_hash: &'a str,
build_time: &'a str,
}
#[derive(Serialize, Debug)]
struct LoadData<'a> {
rtc: &'a str,
@@ -158,17 +154,19 @@ fn set_config(
anyhow::Ok(Some("saved".to_owned()))
}
fn get_version(
fn get_battery_state(
_request: &mut Request<&mut EspHttpConnection>,
) -> Result<Option<std::string::String>, anyhow::Error> {
let branch = env!("VERGEN_GIT_BRANCH").to_owned();
let hash = &env!("VERGEN_GIT_SHA")[0..8];
let version_info: VersionInfo<'_> = VersionInfo {
git_hash: &(branch + "@" + hash),
build_time: env!("VERGEN_BUILD_TIMESTAMP"),
};
anyhow::Ok(Some(serde_json::to_string(&version_info)?))
let mut board = BOARD_ACCESS.lock().unwrap();
let battery_state = board.get_battery_state();
let battery_json = serde_json::to_string(&battery_state)?;
anyhow::Ok(Some(battery_json))
}
fn get_version_web(
_request: &mut Request<&mut EspHttpConnection>,
) -> Result<Option<std::string::String>, anyhow::Error> {
anyhow::Ok(Some(serde_json::to_string(&get_version())?))
}
fn pump_test(
@@ -296,10 +294,14 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
Box::new(EspHttpServer::new(&server_config).unwrap());
server
.fn_handler("/version", Method::Get, |request| {
handle_error_to500(request, get_version)
handle_error_to500(request, get_version_web)
})
.unwrap();
server
.fn_handler("/battery", Method::Get, |request| {
handle_error_to500(request, get_battery_state)
})
.unwrap();
server
.fn_handler("/time", Method::Get, |request| {
handle_error_to500(request, get_data)
@@ -505,13 +507,13 @@ pub fn httpd(_reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
})
.unwrap();
server
.fn_handler("/bootstrap-grid.css", Method::Get, |request| {
request
.into_ok_response()?
.write(include_bytes!("bootstrap-grid.css"))?;
anyhow::Ok(())
})
.unwrap();
.fn_handler("/bootstrap-grid.css", Method::Get, |request| {
request
.into_ok_response()?
.write(include_bytes!("bootstrap-grid.css"))?;
anyhow::Ok(())
})
.unwrap();
server
}
@@ -522,7 +524,7 @@ fn cors_response(
) -> Result<(), anyhow::Error> {
let headers = [
("Access-Control-Allow-Origin", "*"),
("Access-Control-Allow-Headers", "*")
("Access-Control-Allow-Headers", "*"),
];
let mut response = request.into_response(status, None, &headers)?;
response.write(body.as_bytes())?;