chore: cargo fmt

This commit is contained in:
ju6ge 2025-03-13 20:32:28 +01:00
parent 3cdaacabac
commit f340278236
Signed by: judge
GPG Key ID: 6512C30DD8E017B5
5 changed files with 318 additions and 288 deletions

View File

@ -33,7 +33,7 @@ pub struct NightLampConfig {
pub night_lamp_hour_end: u8, pub night_lamp_hour_end: u8,
pub night_lamp_only_when_dark: bool, pub night_lamp_only_when_dark: bool,
pub low_soc_cutoff: u8, pub low_soc_cutoff: u8,
pub low_soc_restore: u8 pub low_soc_restore: u8,
} }
impl Default for NightLampConfig { impl Default for NightLampConfig {
fn default() -> Self { fn default() -> Self {
@ -43,7 +43,7 @@ impl Default for NightLampConfig {
night_lamp_hour_end: 2, night_lamp_hour_end: 2,
night_lamp_only_when_dark: true, night_lamp_only_when_dark: true,
low_soc_cutoff: 30, low_soc_cutoff: 30,
low_soc_restore: 50 low_soc_restore: 50,
} }
} }
} }

View File

@ -1,7 +1,7 @@
use std::{collections::HashMap, sync::Mutex};
use serde::Serialize; use serde::Serialize;
use std::{collections::HashMap, sync::Mutex};
use strum::{EnumIter, IntoEnumIterator}; use strum::{EnumIter, IntoEnumIterator};
use strum_macros::IntoStaticStr; use strum_macros::IntoStaticStr;
use esp_idf_svc::systime::EspSystemTime; use esp_idf_svc::systime::EspSystemTime;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
@ -9,16 +9,17 @@ use ringbuffer::{ConstGenericRingBuffer, RingBuffer};
use text_template::Template; use text_template::Template;
use unit_enum::UnitEnum; use unit_enum::UnitEnum;
const TXT_SHORT_LENGTH:usize = 8; const TXT_SHORT_LENGTH: usize = 8;
const TXT_LONG_LENGTH:usize = 32; const TXT_LONG_LENGTH: usize = 32;
const BUFFER_SIZE:usize = 220; const BUFFER_SIZE: usize = 220;
#[link_section = ".rtc.data"] #[link_section = ".rtc.data"]
static mut BUFFER:ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE> = ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE>::new(); static mut BUFFER: ConstGenericRingBuffer<LogEntry, BUFFER_SIZE> =
ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE>::new();
#[allow(static_mut_refs)] #[allow(static_mut_refs)]
static BUFFER_ACCESS: Lazy<Mutex<&mut ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE>>> = Lazy::new(|| unsafe { Mutex::new(&mut BUFFER) }); static BUFFER_ACCESS: Lazy<Mutex<&mut ConstGenericRingBuffer<LogEntry, BUFFER_SIZE>>> =
Lazy::new(|| unsafe { Mutex::new(&mut BUFFER) });
#[derive(Serialize, Debug, Clone)] #[derive(Serialize, Debug, Clone)]
pub struct LogEntry { pub struct LogEntry {
@ -27,36 +28,36 @@ pub struct LogEntry {
pub a: u32, pub a: u32,
pub b: u32, pub b: u32,
pub txt_short: heapless::String<TXT_SHORT_LENGTH>, pub txt_short: heapless::String<TXT_SHORT_LENGTH>,
pub txt_long: heapless::String<TXT_LONG_LENGTH> pub txt_long: heapless::String<TXT_LONG_LENGTH>,
} }
pub fn init(){ pub fn init() {
unsafe { unsafe {
BUFFER = ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE>::new(); BUFFER = ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE>::new();
}; };
let mut access = BUFFER_ACCESS.lock().unwrap(); let mut access = BUFFER_ACCESS.lock().unwrap();
access.drain().for_each(|_| {}); access.drain().for_each(|_| {});
} }
fn limit_length <const LIMIT:usize> (input: &str, target: &mut heapless::String<LIMIT>){ fn limit_length<const LIMIT: usize>(input: &str, target: &mut heapless::String<LIMIT>) {
for char in input.chars() { for char in input.chars() {
match target.push(char) { match target.push(char) {
Ok(_) => {}, //continue adding chars Ok(_) => {} //continue adding chars
Err(_) => { Err(_) => {
//clear space for two asci chars //clear space for two asci chars
while target.len()+2 >= LIMIT { while target.len() + 2 >= LIMIT {
target.pop().unwrap(); target.pop().unwrap();
} }
//add .. to shortened strings //add .. to shortened strings
target.push('.').unwrap(); target.push('.').unwrap();
target.push('.').unwrap(); target.push('.').unwrap();
return; return;
}, }
} }
} }
} }
pub fn get_log() -> Vec<LogEntry>{ pub fn get_log() -> Vec<LogEntry> {
let buffer = BUFFER_ACCESS.lock().unwrap(); let buffer = BUFFER_ACCESS.lock().unwrap();
let mut read_copy = Vec::new(); let mut read_copy = Vec::new();
for entry in buffer.iter() { for entry in buffer.iter() {
@ -67,19 +68,17 @@ pub fn get_log() -> Vec<LogEntry>{
return read_copy; return read_copy;
} }
pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short:&str, txt_long:&str){ pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short: &str, txt_long: &str) {
let mut txt_short_stack:heapless::String<TXT_SHORT_LENGTH> = heapless::String::new(); let mut txt_short_stack: heapless::String<TXT_SHORT_LENGTH> = heapless::String::new();
let mut txt_long_stack:heapless::String<TXT_LONG_LENGTH> = heapless::String::new(); let mut txt_long_stack: heapless::String<TXT_LONG_LENGTH> = heapless::String::new();
limit_length(txt_short, &mut txt_short_stack); limit_length(txt_short, &mut txt_short_stack);
limit_length(txt_long, &mut txt_long_stack); limit_length(txt_long, &mut txt_long_stack);
let time = EspSystemTime {}.now().as_millis() as u64; let time = EspSystemTime {}.now().as_millis() as u64;
let ordinal = message_key.ordinal() as u16; let ordinal = message_key.ordinal() as u16;
let template_string:&str = message_key.into(); let template_string: &str = message_key.into();
let mut values: HashMap<&str, &str> = HashMap::new(); let mut values: HashMap<&str, &str> = HashMap::new();
let number_a_str = number_a.to_string(); let number_a_str = number_a.to_string();
@ -92,12 +91,10 @@ pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short:&str
let template = Template::from(template_string); let template = Template::from(template_string);
let serial_entry = template.fill_in(&values); let serial_entry = template.fill_in(&values);
println!("{serial_entry}"); println!("{serial_entry}");
let entry = LogEntry {
let entry = LogEntry{
timestamp: time, timestamp: time,
message_id: ordinal, message_id: ordinal,
a: number_a, a: number_a,
@ -106,14 +103,10 @@ pub fn log(message_key: LogMessage, number_a: u32, number_b: u32, txt_short:&str
txt_long: txt_long_stack, txt_long: txt_long_stack,
}; };
let mut buffer = BUFFER_ACCESS.lock().unwrap();
let mut buffer = BUFFER_ACCESS.lock().unwrap();
buffer.push(entry); buffer.push(entry);
} }
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -121,9 +114,9 @@ mod tests {
#[test] #[test]
fn within_limit() { fn within_limit() {
let test = "12345678"; let test = "12345678";
let mut txt_short_stack:heapless::String<TXT_SHORT_LENGTH> = heapless::String::new(); let mut txt_short_stack: heapless::String<TXT_SHORT_LENGTH> = heapless::String::new();
let mut txt_long_stack:heapless::String<TXT_LONG_LENGTH> = heapless::String::new(); let mut txt_long_stack: heapless::String<TXT_LONG_LENGTH> = heapless::String::new();
limit_length(test, &mut txt_short_stack); limit_length(test, &mut txt_short_stack);
limit_length(test, &mut txt_long_stack); limit_length(test, &mut txt_long_stack);
@ -132,39 +125,43 @@ mod tests {
} }
} }
#[derive(IntoStaticStr, EnumIter, Serialize, PartialEq, Eq, PartialOrd, Ord, Clone, UnitEnum)] #[derive(IntoStaticStr, EnumIter, Serialize, PartialEq, Eq, PartialOrd, Ord, Clone, UnitEnum)]
pub enum LogMessage { pub enum LogMessage {
#[strum(serialize = "Reset due to ${txt_long} requires rtc clear ${number_a} and force config mode ${number_b}")] #[strum(
serialize = "Reset due to ${txt_long} requires rtc clear ${number_a} and force config mode ${number_b}"
)]
ResetReason, ResetReason,
#[strum(serialize = "Current restart to conf mode ${number_a}")] #[strum(serialize = "Current restart to conf mode ${number_a}")]
RestartToConfig, RestartToConfig,
#[strum(serialize = "Current low voltage detection is ${number_a}")] #[strum(serialize = "Current low voltage detection is ${number_a}")]
LowVoltage, LowVoltage,
#[strum(serialize = "Error communicating with battery!! ${txt_long}")] #[strum(serialize = "Error communicating with battery!! ${txt_long}")]
BatteryCommunicationError, BatteryCommunicationError,
#[strum(serialize = "Tank sensor raw ${number_a} percent ${number_b}")] #[strum(serialize = "Tank sensor raw ${number_a} percent ${number_b}")]
SensorTankRaw, SensorTankRaw,
#[strum(serialize = "raw measure unscaled ${number_a} hz ${number_b}, plant ${txt_short} sensor ${txt_long}")] #[strum(
serialize = "raw measure unscaled ${number_a} hz ${number_b}, plant ${txt_short} sensor ${txt_long}"
)]
RawMeasure, RawMeasure,
#[strum(serialize = "IP info: ${txt_long}")] #[strum(serialize = "IP info: ${txt_long}")]
WifiInfo, WifiInfo,
#[strum(serialize = "Plant:${txt_short} a:${number_a} b:${number_b}")] #[strum(serialize = "Plant:${txt_short} a:${number_a} b:${number_b}")]
TestSensor, TestSensor,
#[strum(serialize = "Stay alive topic is ${txt_long}")] #[strum(serialize = "Stay alive topic is ${txt_long}")]
StayAlive, StayAlive,
#[strum(serialize = "Connecting mqtt ${txt_short} with id ${txt_long}")] #[strum(serialize = "Connecting mqtt ${txt_short} with id ${txt_long}")]
MqttInfo, MqttInfo,
#[strum(serialize = "Received stay alive with value ${txt_short}")] #[strum(serialize = "Received stay alive with value ${txt_short}")]
MqttStayAliveRec, MqttStayAliveRec,
#[strum(serialize = "Unknown topic recieved ${txt_long}")] #[strum(serialize = "Unknown topic recieved ${txt_long}")]
UnknownTopic, UnknownTopic,
#[strum(serialize = "Partition state is ${txt_long}")] #[strum(serialize = "Partition state is ${txt_long}")]
PartitionState, PartitionState,
#[strum(serialize = "Mounted Filesystem free ${number_a} total ${number_b} use ${txt_short}")] #[strum(serialize = "Mounted Filesystem free ${number_a} total ${number_b} use ${txt_short}")]
FilesystemMount, FilesystemMount,
#[strum(serialize = "Mounting Filesystem, this will format the first time and needs quite some time!")] #[strum(
serialize = "Mounting Filesystem, this will format the first time and needs quite some time!"
)]
MountingFilesystem, MountingFilesystem,
#[strum(serialize = "Year inplausible, force config mode")] #[strum(serialize = "Year inplausible, force config mode")]
YearInplausibleForceConfig, YearInplausibleForceConfig,
@ -178,12 +175,16 @@ pub enum LogMessage {
ConfigModeMissingConfig, ConfigModeMissingConfig,
#[strum(serialize = "startup state wifi ${number_a} sntp ${number_b} mqtt ${txt_short}")] #[strum(serialize = "startup state wifi ${number_a} sntp ${number_b} mqtt ${txt_short}")]
StartupInfo, StartupInfo,
#[strum(serialize = "Trying to pump for ${number_b}s with pump ${number_a} now dryrun: ${txt_short}")] #[strum(
serialize = "Trying to pump for ${number_b}s with pump ${number_a} now dryrun: ${txt_short}"
)]
PumpPlant, PumpPlant,
#[strum(serialize = "Enable main power dryrun: ${number_a}")] #[strum(serialize = "Enable main power dryrun: ${number_a}")]
EnableMain, EnableMain,
#[strum(serialize = "Pumped multiple times, but plant is still to try attempt: ${number_a} limit :: ${number_b} plant: ${txt_short}")] #[strum(
ConsecutivePumpCountLimit serialize = "Pumped multiple times, but plant is still to try attempt: ${number_a} limit :: ${number_b} plant: ${txt_short}"
)]
ConsecutivePumpCountLimit,
} }
#[derive(Serialize)] #[derive(Serialize)]

View File

@ -10,17 +10,11 @@ use chrono_tz::{Europe::Berlin, Tz};
use config::Mode; use config::Mode;
use esp_idf_hal::delay::Delay; use esp_idf_hal::delay::Delay;
use esp_idf_sys::{ use esp_idf_sys::{
esp_ota_get_app_partition_count, esp_ota_get_app_partition_count, esp_ota_get_running_partition, esp_ota_get_state_partition,
esp_ota_get_running_partition, esp_ota_img_states_t, esp_ota_img_states_t_ESP_OTA_IMG_ABORTED,
esp_ota_get_state_partition, 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_states_t_ESP_OTA_IMG_PENDING_VERIFY, esp_ota_img_states_t_ESP_OTA_IMG_UNDEFINED,
esp_ota_img_states_t_ESP_OTA_IMG_ABORTED, esp_ota_img_states_t_ESP_OTA_IMG_VALID, vTaskDelay,
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,
vTaskDelay
}; };
use esp_ota::{mark_app_valid, rollback_and_reboot}; use esp_ota::{mark_app_valid, rollback_and_reboot};
use log::log; use log::log;
@ -28,15 +22,11 @@ use once_cell::sync::Lazy;
use plant_hal::{PlantCtrlBoard, PlantHal, PLANT_COUNT}; use plant_hal::{PlantCtrlBoard, PlantHal, PLANT_COUNT};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{config::PlantControllerConfig, webserver::webserver::httpd};
config::PlantControllerConfig,
webserver::webserver::httpd,
};
mod log;
mod config; mod config;
mod log;
pub mod plant_hal; pub mod plant_hal;
const TIME_ZONE: Tz = Berlin; const TIME_ZONE: Tz = Berlin;
const MOIST_SENSOR_MAX_FREQUENCY: u32 = 5000; // 60kHz (500Hz margin) const MOIST_SENSOR_MAX_FREQUENCY: u32 = 5000; // 60kHz (500Hz margin)
@ -72,7 +62,6 @@ impl WaitType {
} }
} }
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)] #[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
/// Light State tracking data for mqtt /// Light State tracking data for mqtt
struct LightState { struct LightState {
@ -182,8 +171,6 @@ struct PlantStateMQTT<'a> {
next_pump: &'a str, next_pump: &'a str,
} }
fn safe_main() -> anyhow::Result<()> { fn safe_main() -> anyhow::Result<()> {
// It is necessary to call this function once. Otherwise some patches to the runtime // It is necessary to call this function once. Otherwise some patches to the runtime
// implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71 // implemented by esp-idf-sys might not link properly. See https://github.com/esp-rs/esp-idf-template/issues/71
@ -234,38 +221,45 @@ fn safe_main() -> anyhow::Result<()> {
&format!("unknown {ota_state}") &format!("unknown {ota_state}")
} }
}; };
log(log::LogMessage::PartitionState, 0,0, "", ota_state_string); log(log::LogMessage::PartitionState, 0, 0, "", ota_state_string);
let mut board: std::sync::MutexGuard<'_, PlantCtrlBoard<'_>> = BOARD_ACCESS.lock().unwrap(); let mut board: std::sync::MutexGuard<'_, PlantCtrlBoard<'_>> = BOARD_ACCESS.lock().unwrap();
board.general_fault(false); board.general_fault(false);
log(log::LogMessage::MountingFilesystem, 0,0,"",""); log(log::LogMessage::MountingFilesystem, 0, 0, "", "");
board.mount_file_system()?; board.mount_file_system()?;
let free_space = board.file_system_size()?; let free_space = board.file_system_size()?;
log(log::LogMessage::FilesystemMount, free_space.free_size as u32, log(
free_space.total_size as u32, &free_space.used_size.to_string(), ""); log::LogMessage::FilesystemMount,
free_space.free_size as u32,
free_space.total_size as u32,
&free_space.used_size.to_string(),
"",
);
let mut cur = board.get_rtc_time().or_else(|err| { let mut cur = board
println!("rtc module error: {:?}", err); .get_rtc_time()
board.general_fault(true); .or_else(|err| {
board.time() println!("rtc module error: {:?}", err);
}).map_err(|err| -> Result<(), _>{ board.general_fault(true);
bail!("time error {}", err); board.time()
}).unwrap(); })
.map_err(|err| -> Result<(), _> {
bail!("time error {}", err);
})
.unwrap();
//check if we know the time current > 2020 (plausibility check, this code is newer than 2020) //check if we know the time current > 2020 (plausibility check, this code is newer than 2020)
if cur.year() < 2020 { if cur.year() < 2020 {
to_config = true; to_config = true;
log(log::LogMessage::YearInplausibleForceConfig, 0,0,"",""); log(log::LogMessage::YearInplausibleForceConfig, 0, 0, "", "");
} }
println!("cur is {}", cur); println!("cur is {}", cur);
board.update_charge_indicator(); board.update_charge_indicator();
if board.get_restart_to_conf() { if board.get_restart_to_conf() {
log(log::LogMessage::ConfigModeSoftwareOverride, 0,0,"",""); log(log::LogMessage::ConfigModeSoftwareOverride, 0, 0, "", "");
for _i in 0..2 { for _i in 0..2 {
board.general_fault(true); board.general_fault(true);
Delay::new_default().delay_ms(100); Delay::new_default().delay_ms(100);
@ -277,7 +271,7 @@ fn safe_main() -> anyhow::Result<()> {
board.set_restart_to_conf(false); board.set_restart_to_conf(false);
} else if board.is_mode_override() { } else if board.is_mode_override() {
board.general_fault(true); board.general_fault(true);
log(log::LogMessage::ConfigModeButtonOverride, 0,0,"",""); log(log::LogMessage::ConfigModeButtonOverride, 0, 0, "", "");
for _i in 0..5 { for _i in 0..5 {
board.general_fault(true); board.general_fault(true);
Delay::new_default().delay_ms(100); Delay::new_default().delay_ms(100);
@ -291,7 +285,7 @@ fn safe_main() -> anyhow::Result<()> {
} else { } else {
board.general_fault(false); board.general_fault(false);
} }
} }
let config: PlantControllerConfig; let config: PlantControllerConfig;
match board.get_config() { match board.get_config() {
@ -299,7 +293,13 @@ fn safe_main() -> anyhow::Result<()> {
config = valid; config = valid;
} }
Err(err) => { Err(err) => {
log(log::LogMessage::ConfigModeMissingConfig, 0,0,"",&err.to_string()); log(
log::LogMessage::ConfigModeMissingConfig,
0,
0,
"",
&err.to_string(),
);
//config upload will trigger reboot! //config upload will trigger reboot!
let _ = board.wifi_ap(Option::None); let _ = board.wifi_ap(Option::None);
drop(board); drop(board);
@ -403,7 +403,13 @@ fn safe_main() -> anyhow::Result<()> {
publish_battery_state(&mut board, &config); publish_battery_state(&mut board, &config);
} }
log(log::LogMessage::StartupInfo, wifi as u32, sntp as u32,&mqtt.to_string(),""); log(
log::LogMessage::StartupInfo,
wifi as u32,
sntp as u32,
&mqtt.to_string(),
"",
);
if to_config { if to_config {
//check if client or ap mode and init wifi //check if client or ap mode and init wifi
@ -414,10 +420,9 @@ fn safe_main() -> anyhow::Result<()> {
let _webserver = httpd(reboot_now.clone()); let _webserver = httpd(reboot_now.clone());
wait_infinity(WaitType::ConfigButton, reboot_now.clone()); wait_infinity(WaitType::ConfigButton, reboot_now.clone());
} else { } else {
log(log::LogMessage::NormalRun, 0,0,"",""); log(log::LogMessage::NormalRun, 0, 0, "", "");
} }
let dry_run = false; let dry_run = false;
let tank_state = determine_tank_state(&mut board, &config); let tank_state = determine_tank_state(&mut board, &config);
@ -464,8 +469,6 @@ fn safe_main() -> anyhow::Result<()> {
} }
}; };
let mut plantstate: [PlantState; PLANT_COUNT] = core::array::from_fn(|_| PlantState { let mut plantstate: [PlantState; PLANT_COUNT] = core::array::from_fn(|_| PlantState {
..Default::default() ..Default::default()
}); });
@ -477,26 +480,36 @@ fn safe_main() -> anyhow::Result<()> {
&mut board, &mut board,
); );
let pump_required = plantstate.iter().any(|it| it.do_water) && !water_frozen; let pump_required = plantstate.iter().any(|it| it.do_water) && !water_frozen;
if pump_required { if pump_required {
log(log::LogMessage::EnableMain, dry_run as u32,0,"",""); log(log::LogMessage::EnableMain, dry_run as u32, 0, "", "");
if !dry_run{ if !dry_run {
board.any_pump(true)?; board.any_pump(true)?;
} }
for plant in 0..PLANT_COUNT { for plant in 0..PLANT_COUNT {
let state = &mut plantstate[plant]; let state = &mut plantstate[plant];
if state.do_water { if state.do_water {
let plant_config = &config.plants[plant]; let plant_config = &config.plants[plant];
state.consecutive_pump_count = board.consecutive_pump_count(plant) + 1; state.consecutive_pump_count = board.consecutive_pump_count(plant) + 1;
board.store_consecutive_pump_count(plant, state.consecutive_pump_count); board.store_consecutive_pump_count(plant, state.consecutive_pump_count);
if state.consecutive_pump_count > plant_config.max_consecutive_pump_count as u32 { if state.consecutive_pump_count > plant_config.max_consecutive_pump_count as u32 {
log(log::LogMessage::ConsecutivePumpCountLimit, state.consecutive_pump_count as u32,plant_config.max_consecutive_pump_count as u32,&plant.to_string(),""); log(
log::LogMessage::ConsecutivePumpCountLimit,
state.consecutive_pump_count as u32,
plant_config.max_consecutive_pump_count as u32,
&plant.to_string(),
"",
);
state.not_effective = true; state.not_effective = true;
board.fault(plant, true); board.fault(plant, true);
} }
log(log::LogMessage::PumpPlant, (plant + 1) as u32,plant_config.pump_time_s as u32,&dry_run.to_string(),""); log(
log::LogMessage::PumpPlant,
(plant + 1) as u32,
plant_config.pump_time_s as u32,
&dry_run.to_string(),
"",
);
board.store_last_pump_time(plant, cur); board.store_last_pump_time(plant, cur);
board.last_pump_time(plant); board.last_pump_time(plant);
state.active = true; state.active = true;
@ -519,21 +532,21 @@ fn safe_main() -> anyhow::Result<()> {
enabled: config.night_lamp.enabled, enabled: config.night_lamp.enabled,
..Default::default() ..Default::default()
}; };
if light_state.enabled { if light_state.enabled {
light_state.is_day = 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(
&timezone_time, &timezone_time,
config.night_lamp.night_lamp_hour_start, config.night_lamp.night_lamp_hour_start,
config.night_lamp.night_lamp_hour_end, config.night_lamp.night_lamp_hour_end,
); );
if state_of_charge < config.night_lamp.low_soc_cutoff { if state_of_charge < config.night_lamp.low_soc_cutoff {
board.set_low_voltage_in_cycle(); board.set_low_voltage_in_cycle();
} else if state_of_charge > config.night_lamp.low_soc_restore { } else if state_of_charge > config.night_lamp.low_soc_restore {
board.clear_low_voltage_in_cycle(); board.clear_low_voltage_in_cycle();
} }
light_state.battery_low = board.low_voltage_in_cycle(); light_state.battery_low = board.low_voltage_in_cycle();
if !light_state.out_of_work_hour { if !light_state.out_of_work_hour {
if config.night_lamp.night_lamp_only_when_dark { if config.night_lamp.night_lamp_only_when_dark {
if !light_state.is_day { if !light_state.is_day {
@ -556,7 +569,7 @@ fn safe_main() -> anyhow::Result<()> {
light_state.active = false; light_state.active = false;
board.light(false).unwrap(); board.light(false).unwrap();
} }
println!("Lightstate is {:?}", light_state); println!("Lightstate is {:?}", light_state);
} }
@ -587,13 +600,11 @@ fn safe_main() -> anyhow::Result<()> {
//is deep sleep //is deep sleep
mark_app_valid(); mark_app_valid();
let stay_alive_mqtt = STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed); let stay_alive_mqtt = STAY_ALIVE.load(std::sync::atomic::Ordering::Relaxed);
let stay_alive = stay_alive_mqtt; let stay_alive = stay_alive_mqtt;
println!("Check stay alive, current state is {}", stay_alive); println!("Check stay alive, current state is {}", stay_alive);
if stay_alive { if stay_alive {
println!("Go to stay alive move"); println!("Go to stay alive move");
drop(board); drop(board);
@ -972,7 +983,7 @@ fn wait_infinity(wait_type: WaitType, reboot_now: Arc<AtomicBool>) -> ! {
if reboot_now.load(std::sync::atomic::Ordering::Relaxed) { if reboot_now.load(std::sync::atomic::Ordering::Relaxed) {
//ensure clean http answer //ensure clean http answer
Delay::new_default().delay_ms(500); Delay::new_default().delay_ms(500);
BOARD_ACCESS.lock().unwrap().deep_sleep( 1); BOARD_ACCESS.lock().unwrap().deep_sleep(1);
} }
} }
} }
@ -1095,7 +1106,7 @@ fn get_version() -> VersionInfo {
return VersionInfo { return VersionInfo {
git_hash: (branch + "@" + hash), git_hash: (branch + "@" + hash),
build_time: env!("VERGEN_BUILD_TIMESTAMP").to_owned(), build_time: env!("VERGEN_BUILD_TIMESTAMP").to_owned(),
partition: partition.to_owned() + &address.to_string() partition: partition.to_owned() + &address.to_string(),
}; };
} }

View File

@ -1,8 +1,8 @@
use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver}; use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver};
use crate::log::LogMessage;
use ds323x::{DateTimeAccess, Ds323x}; use ds323x::{DateTimeAccess, Ds323x};
use esp_ota::mark_app_valid; use esp_ota::mark_app_valid;
use crate::log::LogMessage;
use eeprom24x::{Eeprom24x, Eeprom24xTrait, SlaveAddr}; use eeprom24x::{Eeprom24x, Eeprom24xTrait, SlaveAddr};
use embedded_hal_bus::i2c::MutexDevice; use embedded_hal_bus::i2c::MutexDevice;
@ -23,11 +23,14 @@ use esp_idf_svc::mqtt::client::{EspMqttClient, LwtConfiguration, MqttClientConfi
use esp_idf_svc::nvs::EspDefaultNvsPartition; use esp_idf_svc::nvs::EspDefaultNvsPartition;
use esp_idf_svc::wifi::config::{ScanConfig, ScanType}; use esp_idf_svc::wifi::config::{ScanConfig, ScanType};
use esp_idf_svc::wifi::EspWifi; use esp_idf_svc::wifi::EspWifi;
use esp_idf_sys::esp_restart;
use esp_idf_sys::{
esp_deep_sleep, esp_sleep_enable_ext1_wakeup,
esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW, esp_spiffs_info,
};
use measurements::Temperature; use measurements::Temperature;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use plant_ctrl2::sipo::ShiftRegister40; use plant_ctrl2::sipo::ShiftRegister40;
use esp_idf_sys::{esp_deep_sleep, esp_sleep_enable_ext1_wakeup, esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW, esp_spiffs_info};
use esp_idf_sys::esp_restart;
use anyhow::{anyhow, Context}; use anyhow::{anyhow, Context};
use anyhow::{bail, Ok, Result}; use anyhow::{bail, Ok, Result};
@ -80,7 +83,6 @@ const PUMP5_BIT: usize = 5;
const PUMP6_BIT: usize = 6; const PUMP6_BIT: usize = 6;
const PUMP7_BIT: usize = 7; const PUMP7_BIT: usize = 7;
const MS_0: usize = 8; const MS_0: usize = 8;
const MS_4: usize = 9; const MS_4: usize = 9;
const MS_2: usize = 10; const MS_2: usize = 10;
@ -129,8 +131,6 @@ static mut LOW_VOLTAGE_DETECTED: bool = false;
#[link_section = ".rtc.data"] #[link_section = ".rtc.data"]
static mut RESTART_TO_CONF: bool = false; static mut RESTART_TO_CONF: bool = false;
pub struct FileSystemSizeInfo { pub struct FileSystemSizeInfo {
pub total_size: usize, pub total_size: usize,
pub used_size: usize, pub used_size: usize,
@ -209,25 +209,26 @@ pub struct BatteryState {
} }
#[derive(Serialize, Deserialize, PartialEq, Debug)] #[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct BackupHeader{ pub struct BackupHeader {
pub timestamp: i64, pub timestamp: i64,
crc16: u16, crc16: u16,
pub size: usize pub size: usize,
} }
impl PlantCtrlBoard<'_> { impl PlantCtrlBoard<'_> {
pub fn update_charge_indicator(&mut self){ pub fn update_charge_indicator(&mut self) {
let is_charging = match self.battery_driver.average_current() { let is_charging = match self.battery_driver.average_current() {
OkStd(current) => current < 20, OkStd(current) => current < 20,
Err(_) => false, Err(_) => false,
}; };
self.shift_register.decompose()[CHARGING].set_state(is_charging.into()).unwrap(); self.shift_register.decompose()[CHARGING]
.set_state(is_charging.into())
.unwrap();
} }
pub fn deep_sleep(&mut self, duration_in_ms: u64) -> ! {
pub fn deep_sleep(&mut self, duration_in_ms:u64) -> !{
self.shift_register.decompose()[AWAKE].set_low().unwrap(); self.shift_register.decompose()[AWAKE].set_low().unwrap();
unsafe { unsafe {
//if we dont do this here, we might just revert a newly flashed firmeware //if we dont do this here, we might just revert a newly flashed firmeware
mark_app_valid(); mark_app_valid();
//allow early wakup by pressing the boot button //allow early wakup by pressing the boot button
@ -235,14 +236,17 @@ impl PlantCtrlBoard<'_> {
esp_restart(); esp_restart();
} else { } else {
//configure gpio 1 to wakeup on low, reused boot button for this //configure gpio 1 to wakeup on low, reused boot button for this
esp_sleep_enable_ext1_wakeup(0b10u64, esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW); esp_sleep_enable_ext1_wakeup(
esp_deep_sleep(duration_in_ms); 0b10u64,
esp_sleep_ext1_wakeup_mode_t_ESP_EXT1_WAKEUP_ANY_LOW,
);
esp_deep_sleep(duration_in_ms);
} }
}; };
} }
pub fn get_backup_info(&mut self) -> Result<BackupHeader> { pub fn get_backup_info(&mut self) -> Result<BackupHeader> {
let dummy = BackupHeader{ let dummy = BackupHeader {
timestamp: 0, timestamp: 0,
crc16: 0, crc16: 0,
size: 0, size: 0,
@ -251,17 +255,16 @@ impl PlantCtrlBoard<'_> {
let mut header_page_buffer = vec![0_u8; store]; let mut header_page_buffer = vec![0_u8; store];
match self.eeprom.read_data(0, &mut header_page_buffer) { match self.eeprom.read_data(0, &mut header_page_buffer) {
OkStd(_) => {}, OkStd(_) => {}
Err(err) => bail!("Error reading eeprom header {:?}", err), Err(err) => bail!("Error reading eeprom header {:?}", err),
}; };
println!("Raw header is {:?} with size {}", header_page_buffer , store); println!("Raw header is {:?} with size {}", header_page_buffer, store);
let header:BackupHeader = bincode::deserialize(&header_page_buffer)?; let header: BackupHeader = bincode::deserialize(&header_page_buffer)?;
Ok(header) Ok(header)
} }
pub fn get_backup_config(&mut self) -> Result<Vec<u8>> { pub fn get_backup_config(&mut self) -> Result<Vec<u8>> {
let dummy = BackupHeader{ let dummy = BackupHeader {
timestamp: 0, timestamp: 0,
crc16: 0, crc16: 0,
size: 0, size: 0,
@ -270,27 +273,31 @@ impl PlantCtrlBoard<'_> {
let mut header_page_buffer = vec![0_u8; store]; let mut header_page_buffer = vec![0_u8; store];
match self.eeprom.read_data(0, &mut header_page_buffer) { match self.eeprom.read_data(0, &mut header_page_buffer) {
OkStd(_) => {}, OkStd(_) => {}
Err(err) => bail!("Error reading eeprom header {:?}", err), Err(err) => bail!("Error reading eeprom header {:?}", err),
}; };
let header:BackupHeader = bincode::deserialize(&header_page_buffer)?; let header: BackupHeader = bincode::deserialize(&header_page_buffer)?;
let data_start_address = 1*self.eeprom.page_size() as u32; let data_start_address = 1 * self.eeprom.page_size() as u32;
let mut data_buffer = vec![0_u8; header.size]; let mut data_buffer = vec![0_u8; header.size];
match self.eeprom.read_data(data_start_address, &mut data_buffer) { match self.eeprom.read_data(data_start_address, &mut data_buffer) {
OkStd(_) => {}, OkStd(_) => {}
Err(err) => bail!("Error reading eeprom data {:?}", err), Err(err) => bail!("Error reading eeprom data {:?}", err),
}; };
let checksum = X25.checksum(&data_buffer); let checksum = X25.checksum(&data_buffer);
if checksum != header.crc16 { if checksum != header.crc16 {
bail!("Invalid checksum, got {} but expected {}", checksum, header.crc16 ); bail!(
"Invalid checksum, got {} but expected {}",
checksum,
header.crc16
);
} }
Ok(data_buffer) Ok(data_buffer)
} }
pub fn backup_config(&mut self, bytes: &[u8]) -> Result<()>{ pub fn backup_config(&mut self, bytes: &[u8]) -> Result<()> {
let delay = Delay::new_default(); let delay = Delay::new_default();
let checksum = X25.checksum(bytes); let checksum = X25.checksum(bytes);
@ -298,49 +305,52 @@ impl PlantCtrlBoard<'_> {
let time = self.get_rtc_time()?.timestamp_millis(); let time = self.get_rtc_time()?.timestamp_millis();
let header = BackupHeader{ let header = BackupHeader {
crc16 : checksum, crc16: checksum,
timestamp : time, timestamp: time,
size: bytes.len(), size: bytes.len(),
}; };
let encoded = bincode::serialize(&header)?; let encoded = bincode::serialize(&header)?;
if encoded.len() > page_size { if encoded.len() > page_size {
bail!("Size limit reached header is {}, but firest page is only {}",encoded.len(), page_size) bail!(
"Size limit reached header is {}, but firest page is only {}",
encoded.len(),
page_size
)
} }
let as_u8:&[u8] = &encoded; let as_u8: &[u8] = &encoded;
match self.eeprom.write_page(0, as_u8) { match self.eeprom.write_page(0, as_u8) {
OkStd(_) => {}, OkStd(_) => {}
Err(err) => bail!("Error writing eeprom {:?}", err), Err(err) => bail!("Error writing eeprom {:?}", err),
}; };
delay.delay_ms(5); delay.delay_ms(5);
let to_write= bytes.chunks(page_size); let to_write = bytes.chunks(page_size);
let mut lastiter = 0; let mut lastiter = 0;
let mut current_page = 1; let mut current_page = 1;
for chunk in to_write { for chunk in to_write {
let address = current_page*page_size as u32; let address = current_page * page_size as u32;
match self.eeprom.write_page(address, chunk) { match self.eeprom.write_page(address, chunk) {
OkStd(_) => {}, OkStd(_) => {}
Err(err) => bail!("Error writing eeprom {:?}", err), Err(err) => bail!("Error writing eeprom {:?}", err),
}; };
current_page = current_page+1; current_page = current_page + 1;
let iter = ((current_page/1)%8 ) as usize; let iter = ((current_page / 1) % 8) as usize;
if iter != lastiter { if iter != lastiter {
for i in 0..PLANT_COUNT { for i in 0..PLANT_COUNT {
self.fault(i, iter==i); self.fault(i, iter == i);
} }
lastiter = iter; lastiter = iter;
} }
//update led here? //update led here?
delay.delay_ms(5); delay.delay_ms(5);
} }
return Ok(()) return Ok(());
} }
pub fn get_battery_state(&mut self) -> BatteryState { pub fn get_battery_state(&mut self) -> BatteryState {
@ -393,8 +403,8 @@ impl PlantCtrlBoard<'_> {
file_system_corrupt = Some(format!("{err:?}")); file_system_corrupt = Some(format!("{err:?}"));
} }
} }
let mut total:usize = 0; let mut total: usize = 0;
let mut used:usize = 0; let mut used: usize = 0;
unsafe { unsafe {
esp_spiffs_info(storage.as_ptr(), &mut total, &mut used); esp_spiffs_info(storage.as_ptr(), &mut total, &mut used);
} }
@ -490,7 +500,13 @@ impl PlantCtrlBoard<'_> {
let r2 = median * 50.0 / (3.3 - median); let r2 = median * 50.0 / (3.3 - median);
let mut percent = r2 / 190_f32 * 100_f32; let mut percent = r2 / 190_f32 * 100_f32;
percent = percent.clamp(0.0, 100.0); percent = percent.clamp(0.0, 100.0);
log(LogMessage::SensorTankRaw, median as u32, percent as u32, "",""); log(
LogMessage::SensorTankRaw,
median as u32,
percent as u32,
"",
"",
);
return Ok(percent as u16); return Ok(percent as u16);
} }
@ -663,7 +679,13 @@ impl PlantCtrlBoard<'_> {
delay.delay_ms(10); delay.delay_ms(10);
let unscaled = self.signal_counter.get_counter_value()? as i32; let unscaled = self.signal_counter.get_counter_value()? as i32;
let hz = (unscaled as f32 * factor) as u32; let hz = (unscaled as f32 * factor) as u32;
log(LogMessage::RawMeasure, unscaled as u32, hz as u32, &plant.to_string(), &format!("{sensor:?}")); log(
LogMessage::RawMeasure,
unscaled as u32,
hz as u32,
&plant.to_string(),
&format!("{sensor:?}"),
);
results[repeat] = hz; results[repeat] = hz;
} }
results.sort(); results.sort();
@ -751,7 +773,7 @@ impl PlantCtrlBoard<'_> {
} }
//update freertos registers ;) //update freertos registers ;)
let address = self.wifi_driver.sta_netif().get_ip_info()?; let address = self.wifi_driver.sta_netif().get_ip_info()?;
log(LogMessage::WifiInfo, 0 ,0,"", &format!("{address:?}")); log(LogMessage::WifiInfo, 0, 0, "", &format!("{address:?}"));
Ok(address) Ok(address)
} }
@ -891,13 +913,13 @@ impl PlantCtrlBoard<'_> {
let b = self.measure_moisture_hz(plant, plant_hal::Sensor::B); let b = self.measure_moisture_hz(plant, plant_hal::Sensor::B);
let aa = match a { let aa = match a {
OkStd(a) => a as u32, OkStd(a) => a as u32,
Err(_) => u32::MAX Err(_) => u32::MAX,
}; };
let bb = match b { let bb = match b {
OkStd(b) => b as u32, OkStd(b) => b as u32,
Err(_) => u32::MAX Err(_) => u32::MAX,
}; };
log(LogMessage::TestSensor, aa ,bb,&plant.to_string(), ""); log(LogMessage::TestSensor, aa, bb, &plant.to_string(), "");
} }
Delay::new_default().delay_ms(10); Delay::new_default().delay_ms(10);
Ok(()) Ok(())
@ -946,7 +968,7 @@ impl PlantCtrlBoard<'_> {
let round_trip_ok = Arc::new(AtomicBool::new(false)); let round_trip_ok = Arc::new(AtomicBool::new(false));
let round_trip_topic = format!("{}/internal/roundtrip", base_topic); let round_trip_topic = format!("{}/internal/roundtrip", base_topic);
let stay_alive_topic = format!("{}/stay_alive", base_topic); let stay_alive_topic = format!("{}/stay_alive", base_topic);
log(LogMessage::StayAlive, 0 ,0,"", &stay_alive_topic); log(LogMessage::StayAlive, 0, 0, "", &stay_alive_topic);
let mqtt_connected_event_received_copy = mqtt_connected_event_received.clone(); let mqtt_connected_event_received_copy = mqtt_connected_event_received.clone();
let mqtt_connected_event_ok_copy = mqtt_connected_event_ok.clone(); let mqtt_connected_event_ok_copy = mqtt_connected_event_ok.clone();
@ -954,7 +976,7 @@ impl PlantCtrlBoard<'_> {
let round_trip_topic_copy = round_trip_topic.clone(); let round_trip_topic_copy = round_trip_topic.clone();
let round_trip_ok_copy = round_trip_ok.clone(); let round_trip_ok_copy = round_trip_ok.clone();
let client_id = mqtt_client_config.client_id.unwrap_or("not set"); let client_id = mqtt_client_config.client_id.unwrap_or("not set");
log(LogMessage::MqttInfo, 0 ,0,client_id, &mqtt_url); log(LogMessage::MqttInfo, 0, 0, client_id, &mqtt_url);
let mut client = EspMqttClient::new_cb(&mqtt_url, &mqtt_client_config, move |event| { let mut client = EspMqttClient::new_cb(&mqtt_url, &mqtt_client_config, move |event| {
let payload = event.payload(); let payload = event.payload();
match payload { match payload {
@ -972,10 +994,10 @@ impl PlantCtrlBoard<'_> {
} else if topic.eq(stay_alive_topic_copy.as_str()) { } else if topic.eq(stay_alive_topic_copy.as_str()) {
let value = let value =
data.eq_ignore_ascii_case("true") || data.eq_ignore_ascii_case("1"); data.eq_ignore_ascii_case("true") || data.eq_ignore_ascii_case("1");
log(LogMessage::MqttStayAliveRec, 0 ,0,&data, ""); log(LogMessage::MqttStayAliveRec, 0, 0, &data, "");
STAY_ALIVE.store(value, std::sync::atomic::Ordering::Relaxed); STAY_ALIVE.store(value, std::sync::atomic::Ordering::Relaxed);
} else { } else {
log(LogMessage::UnknownTopic, 0 ,0,"", &topic); log(LogMessage::UnknownTopic, 0, 0, "", &topic);
} }
} }
} }
@ -1125,7 +1147,7 @@ impl PlantCtrlBoard<'_> {
pub fn get_restart_to_conf(&mut self) -> bool { pub fn get_restart_to_conf(&mut self) -> bool {
return unsafe { RESTART_TO_CONF }; return unsafe { RESTART_TO_CONF };
} }
pub fn set_restart_to_conf(&mut self, to_conf: bool) { pub fn set_restart_to_conf(&mut self, to_conf: bool) {
unsafe { unsafe {
RESTART_TO_CONF = to_conf; RESTART_TO_CONF = to_conf;
@ -1327,10 +1349,9 @@ impl PlantHal {
let awake = &mut shift_register.decompose()[AWAKE]; let awake = &mut shift_register.decompose()[AWAKE];
awake.set_high()?; awake.set_high()?;
let charging = &mut shift_register.decompose()[CHARGING]; let charging = &mut shift_register.decompose()[CHARGING];
charging.set_high()?; charging.set_high()?;
let ms0 = &mut shift_register.decompose()[MS_0]; let ms0 = &mut shift_register.decompose()[MS_0];
ms0.set_low()?; ms0.set_low()?;
let ms1 = &mut shift_register.decompose()[MS_1]; let ms1 = &mut shift_register.decompose()[MS_1];
@ -1343,7 +1364,6 @@ impl PlantHal {
let ms4 = &mut shift_register.decompose()[MS_4]; let ms4 = &mut shift_register.decompose()[MS_4];
ms4.set_high()?; ms4.set_high()?;
println!("Init battery driver"); println!("Init battery driver");
let mut battery_driver = Bq34z100g1Driver { let mut battery_driver = Bq34z100g1Driver {
i2c: MutexDevice::new(&I2C_DRIVER), i2c: MutexDevice::new(&I2C_DRIVER),
@ -1365,7 +1385,6 @@ impl PlantHal {
let mut one_wire_pin = PinDriver::input_output_od(peripherals.pins.gpio18)?; let mut one_wire_pin = PinDriver::input_output_od(peripherals.pins.gpio18)?;
one_wire_pin.set_pull(Pull::Floating).unwrap(); one_wire_pin.set_pull(Pull::Floating).unwrap();
let rtc_time = rtc.datetime(); let rtc_time = rtc.datetime();
match rtc_time { match rtc_time {
OkStd(tt) => { OkStd(tt) => {
@ -1389,42 +1408,32 @@ impl PlantHal {
let mut to_config_mode: bool = false; let mut to_config_mode: bool = false;
let reasons = ResetReason::get(); let reasons = ResetReason::get();
match reasons { match reasons {
ResetReason::Software => {}, ResetReason::Software => {}
ResetReason::ExternalPin => {}, ResetReason::ExternalPin => {}
ResetReason::Watchdog => { ResetReason::Watchdog => {
init_rtc_store = true; init_rtc_store = true;
}, }
ResetReason::Sdio => { ResetReason::Sdio => init_rtc_store = true,
init_rtc_store = true ResetReason::Panic => init_rtc_store = true,
}, ResetReason::InterruptWatchdog => init_rtc_store = true,
ResetReason::Panic => { ResetReason::PowerOn => init_rtc_store = true,
init_rtc_store = true ResetReason::Unknown => init_rtc_store = true,
}, ResetReason::Brownout => init_rtc_store = true,
ResetReason::InterruptWatchdog => { ResetReason::TaskWatchdog => init_rtc_store = true,
init_rtc_store = true ResetReason::DeepSleep => {}
},
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 => { ResetReason::USBPeripheral => {
init_rtc_store = true; init_rtc_store = true;
to_config_mode = true; to_config_mode = true;
}, }
ResetReason::JTAG => { ResetReason::JTAG => init_rtc_store = true,
init_rtc_store = true
},
}; };
log(LogMessage::ResetReason, init_rtc_store as u32, to_config_mode as u32, "",&format!("{reasons:?}")); log(
LogMessage::ResetReason,
init_rtc_store as u32,
to_config_mode as u32,
"",
&format!("{reasons:?}"),
);
if init_rtc_store { if init_rtc_store {
unsafe { unsafe {
LAST_WATERING_TIMESTAMP = [0; PLANT_COUNT]; LAST_WATERING_TIMESTAMP = [0; PLANT_COUNT];
@ -1435,11 +1444,23 @@ impl PlantHal {
}; };
} else { } else {
unsafe { unsafe {
if to_config_mode{ if to_config_mode {
RESTART_TO_CONF = true; RESTART_TO_CONF = true;
} }
log(LogMessage::RestartToConfig, RESTART_TO_CONF as u32, 0, "",""); log(
log(LogMessage::LowVoltage, LOW_VOLTAGE_DETECTED as u32, 0, "",""); LogMessage::RestartToConfig,
RESTART_TO_CONF as u32,
0,
"",
"",
);
log(
LogMessage::LowVoltage,
LOW_VOLTAGE_DETECTED as u32,
0,
"",
"",
);
for i in 0..PLANT_COUNT { for i in 0..PLANT_COUNT {
println!( println!(
"LAST_WATERING_TIMESTAMP[{}] = UTC {}", "LAST_WATERING_TIMESTAMP[{}] = UTC {}",
@ -1513,12 +1534,16 @@ impl PlantHal {
let status = print_battery(&mut battery_driver); let status = print_battery(&mut battery_driver);
match status { match status {
OkStd(_) => { OkStd(_) => {}
},
Err(err) => { Err(err) => {
log(LogMessage::BatteryCommunicationError, 0 as u32, 0, "",&format!("{err:?})")); log(
}, LogMessage::BatteryCommunicationError,
0 as u32,
0,
"",
&format!("{err:?})"),
);
}
} }
let shift_register_enable_invert = PinDriver::output(peripherals.pins.gpio21.downgrade())?; let shift_register_enable_invert = PinDriver::output(peripherals.pins.gpio21.downgrade())?;

View File

@ -1,22 +1,22 @@
//offer ota and config mode //offer ota and config mode
use std::{
str::from_utf8,
sync::{atomic::AtomicBool, Arc},
};
use crate::{ use crate::{
get_version, log::LogMessage, map_range_moisture, plant_hal::PLANT_COUNT, BOARD_ACCESS get_version, log::LogMessage, map_range_moisture, plant_hal::PLANT_COUNT, BOARD_ACCESS,
}; };
use anyhow::bail; use anyhow::bail;
use chrono::DateTime; use chrono::DateTime;
use esp_idf_sys::{settimeofday, timeval, vTaskDelay};
use esp_ota::OtaUpdate;
use core::result::Result::Ok; use core::result::Result::Ok;
use embedded_svc::http::Method; use embedded_svc::http::Method;
use esp_idf_hal::delay::Delay; use esp_idf_hal::delay::Delay;
use esp_idf_svc::http::server::{Configuration, EspHttpConnection, EspHttpServer, Request}; use esp_idf_svc::http::server::{Configuration, EspHttpConnection, EspHttpServer, Request};
use esp_idf_sys::{settimeofday, timeval, vTaskDelay};
use esp_ota::OtaUpdate;
use heapless::String; use heapless::String;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use std::{
str::from_utf8,
sync::{atomic::AtomicBool, Arc},
};
use url::Url; use url::Url;
use crate::config::PlantControllerConfig; use crate::config::PlantControllerConfig;
@ -49,14 +49,14 @@ pub struct TestPump {
} }
#[derive(Serialize, Deserialize, PartialEq, Debug)] #[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct WebBackupHeader{ pub struct WebBackupHeader {
timestamp: std::string::String, timestamp: std::string::String,
size: usize size: usize,
} }
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct NightLampCommand { pub struct NightLampCommand {
active: bool active: bool,
} }
fn write_time( fn write_time(
@ -66,10 +66,10 @@ fn write_time(
let time: SetTime = serde_json::from_slice(&actual_data)?; let time: SetTime = serde_json::from_slice(&actual_data)?;
let parsed = DateTime::parse_from_rfc3339(time.time).map_err(|err| anyhow::anyhow!(err))?; let parsed = DateTime::parse_from_rfc3339(time.time).map_err(|err| anyhow::anyhow!(err))?;
let mut board = BOARD_ACCESS.lock().unwrap(); let mut board = BOARD_ACCESS.lock().unwrap();
let now = timeval { let now = timeval {
tv_sec: parsed.to_utc().timestamp(), tv_sec: parsed.to_utc().timestamp(),
tv_usec: 0 tv_usec: 0,
}; };
unsafe { settimeofday(&now, core::ptr::null_mut()) }; unsafe { settimeofday(&now, core::ptr::null_mut()) };
board.set_rtc_time(&parsed.to_utc())?; board.set_rtc_time(&parsed.to_utc())?;
@ -173,7 +173,6 @@ fn get_backup_config(
anyhow::Ok(Some(json)) anyhow::Ok(Some(json))
} }
fn backup_info( fn backup_info(
_request: &mut Request<&mut EspHttpConnection>, _request: &mut Request<&mut EspHttpConnection>,
) -> Result<Option<std::string::String>, anyhow::Error> { ) -> Result<Option<std::string::String>, anyhow::Error> {
@ -182,13 +181,13 @@ fn backup_info(
let json = match header { let json = match header {
Ok(h) => { Ok(h) => {
let timestamp = DateTime::from_timestamp_millis(h.timestamp).unwrap(); let timestamp = DateTime::from_timestamp_millis(h.timestamp).unwrap();
let wbh = WebBackupHeader{ let wbh = WebBackupHeader {
timestamp: timestamp.to_rfc3339(), timestamp: timestamp.to_rfc3339(),
size: h.size, size: h.size,
}; };
serde_json::to_string(&wbh)? serde_json::to_string(&wbh)?
}, }
Err(_) => "{\"error\":\"Header could not be parsed\"".to_owned() Err(_) => "{\"error\":\"Header could not be parsed\"".to_owned(),
}; };
anyhow::Ok(Some(json)) anyhow::Ok(Some(json))
} }
@ -203,7 +202,6 @@ fn set_config(
anyhow::Ok(Some("saved".to_owned())) anyhow::Ok(Some("saved".to_owned()))
} }
fn get_battery_state( fn get_battery_state(
_request: &mut Request<&mut EspHttpConnection>, _request: &mut Request<&mut EspHttpConnection>,
) -> Result<Option<std::string::String>, anyhow::Error> { ) -> Result<Option<std::string::String>, anyhow::Error> {
@ -221,13 +219,14 @@ fn get_log(
} }
fn get_log_localization_config() -> Result<std::string::String, anyhow::Error> { fn get_log_localization_config() -> Result<std::string::String, anyhow::Error> {
anyhow::Ok(serde_json::to_string(&LogMessage::to_log_localisation_config())?) anyhow::Ok(serde_json::to_string(
&LogMessage::to_log_localisation_config(),
)?)
} }
fn get_version_web( fn get_version_web(
_request: &mut Request<&mut EspHttpConnection>, _request: &mut Request<&mut EspHttpConnection>,
) -> Result<Option<std::string::String>, anyhow::Error> { ) -> Result<Option<std::string::String>, anyhow::Error> {
anyhow::Ok(Some(serde_json::to_string(&get_version())?)) anyhow::Ok(Some(serde_json::to_string(&get_version())?))
} }
@ -278,7 +277,7 @@ fn ota(
let mut board = BOARD_ACCESS.lock().unwrap(); let mut board = BOARD_ACCESS.lock().unwrap();
let mut ota = OtaUpdate::begin()?; let mut ota = OtaUpdate::begin()?;
println!("start ota"); println!("start ota");
//having a larger buffer is not really faster, requires more stack and prevents the progress bar from working ;) //having a larger buffer is not really faster, requires more stack and prevents the progress bar from working ;)
const BUFFER_SIZE: usize = 512; const BUFFER_SIZE: usize = 512;
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
@ -289,14 +288,13 @@ fn ota(
total_read += read; total_read += read;
let to_write = &buffer[0..read]; let to_write = &buffer[0..read];
let iter = (total_read/1024)%8; let iter = (total_read / 1024) % 8;
if iter != lastiter { if iter != lastiter {
for i in 0..PLANT_COUNT { for i in 0..PLANT_COUNT {
board.fault(i, iter==i); board.fault(i, iter == i);
} }
lastiter = iter; lastiter = iter;
} }
ota.write(to_write)?; ota.write(to_write)?;
if read == 0 { if read == 0 {
@ -380,15 +378,15 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
}) })
.unwrap(); .unwrap();
server server
.fn_handler("/log", Method::Get, |request| { .fn_handler("/log", Method::Get, |request| {
handle_error_to500(request, get_log) handle_error_to500(request, get_log)
}) })
.unwrap(); .unwrap();
server.fn_handler("/log_localization", Method::Get, |request| { server
cors_response(request, 200, &get_log_localization_config().unwrap()) .fn_handler("/log_localization", Method::Get, |request| {
cors_response(request, 200, &get_log_localization_config().unwrap())
}) })
.unwrap(); .unwrap();
server server
.fn_handler("/battery", Method::Get, |request| { .fn_handler("/battery", Method::Get, |request| {
handle_error_to500(request, get_battery_state) handle_error_to500(request, get_battery_state)
@ -415,10 +413,10 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
}) })
.unwrap(); .unwrap();
server server
.fn_handler("/lamptest", Method::Post, |request| { .fn_handler("/lamptest", Method::Post, |request| {
handle_error_to500(request, night_lamp_test) handle_error_to500(request, night_lamp_test)
}) })
.unwrap(); .unwrap();
server server
.fn_handler("/boardtest", Method::Post, move |_| { .fn_handler("/boardtest", Method::Post, move |_| {
BOARD_ACCESS.lock().unwrap().test() BOARD_ACCESS.lock().unwrap().test()
@ -456,15 +454,15 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
}) })
.unwrap(); .unwrap();
server server
.fn_handler("/backup_config", Method::Post, move |request| { .fn_handler("/backup_config", Method::Post, move |request| {
handle_error_to500(request, backup_config) handle_error_to500(request, backup_config)
}) })
.unwrap(); .unwrap();
server server
.fn_handler("/backup_info", Method::Get, move |request| { .fn_handler("/backup_info", Method::Get, move |request| {
handle_error_to500(request, backup_info) handle_error_to500(request, backup_info)
}) })
.unwrap(); .unwrap();
server server
.fn_handler("/files", Method::Get, move |request| { .fn_handler("/files", Method::Get, move |request| {
handle_error_to500(request, list_files) handle_error_to500(request, list_files)
@ -472,25 +470,22 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
.unwrap(); .unwrap();
let reboot_now_for_reboot = reboot_now.clone(); let reboot_now_for_reboot = reboot_now.clone();
server server
.fn_handler("/reboot", Method::Post, move |_| { .fn_handler("/reboot", Method::Post, move |_| {
BOARD_ACCESS BOARD_ACCESS.lock().unwrap().set_restart_to_conf(true);
.lock() reboot_now_for_reboot.store(true, std::sync::atomic::Ordering::Relaxed);
.unwrap() anyhow::Ok(())
.set_restart_to_conf(true); })
reboot_now_for_reboot.store(true, std::sync::atomic::Ordering::Relaxed); .unwrap();
anyhow::Ok(())
})
.unwrap();
unsafe { vTaskDelay(1) }; unsafe { vTaskDelay(1) };
let reboot_now_for_exit = reboot_now.clone(); let reboot_now_for_exit = reboot_now.clone();
server server
.fn_handler("/exit", Method::Post, move |_| { .fn_handler("/exit", Method::Post, move |_| {
reboot_now_for_exit.store(true, std::sync::atomic::Ordering::Relaxed); reboot_now_for_exit.store(true, std::sync::atomic::Ordering::Relaxed);
anyhow::Ok(()) anyhow::Ok(())
}) })
.unwrap(); .unwrap();
server server
.fn_handler("/file", Method::Get, move |request| { .fn_handler("/file", Method::Get, move |request| {
let filename = query_param(request.uri(), "filename").unwrap(); let filename = query_param(request.uri(), "filename").unwrap();
@ -532,28 +527,23 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
server server
.fn_handler("/file", Method::Post, move |mut request| { .fn_handler("/file", Method::Post, move |mut request| {
let filename = query_param(request.uri(), "filename").unwrap(); let filename = query_param(request.uri(), "filename").unwrap();
let lock = BOARD_ACCESS let lock = BOARD_ACCESS.lock().unwrap();
.lock() let file_handle = lock.get_file_handle(&filename, true);
.unwrap();
let file_handle =
lock.get_file_handle(&filename, true);
match file_handle { match file_handle {
//TODO get free filesystem size, check against during write if not to large //TODO get free filesystem size, check against during write if not to large
Ok(mut file_handle) => { Ok(mut file_handle) => {
const BUFFER_SIZE: usize = 512; const BUFFER_SIZE: usize = 512;
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE]; let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
let mut total_read: usize = 0; let mut total_read: usize = 0;
let mut lastiter = 0; let mut lastiter = 0;
loop { loop {
let iter = (total_read / 1024) % 8;
let iter = (total_read/1024)%8;
if iter != lastiter { if iter != lastiter {
for i in 0..PLANT_COUNT { for i in 0..PLANT_COUNT {
lock.fault(i, iter==i); lock.fault(i, iter == i);
} }
lastiter = iter; lastiter = iter;
} }
let read = request.read(&mut buffer)?; let read = request.read(&mut buffer)?;
total_read += read; total_read += read;
@ -697,11 +687,14 @@ fn handle_error_to500(
return anyhow::Ok(()); return anyhow::Ok(());
} }
fn read_up_to_bytes_from_request(request: &mut Request<&mut EspHttpConnection<'_>>, limit: Option<usize>) -> Result<Vec<u8>, anyhow::Error> { fn read_up_to_bytes_from_request(
request: &mut Request<&mut EspHttpConnection<'_>>,
limit: Option<usize>,
) -> Result<Vec<u8>, anyhow::Error> {
let max_read = limit.unwrap_or(1024); let max_read = limit.unwrap_or(1024);
let mut data_store = Vec::new(); let mut data_store = Vec::new();
let mut total_read = 0; let mut total_read = 0;
loop{ loop {
let mut buf = [0_u8; 64]; let mut buf = [0_u8; 64];
let read = request.read(&mut buf)?; let read = request.read(&mut buf)?;
if read == 0 { if read == 0 {
@ -709,7 +702,7 @@ fn read_up_to_bytes_from_request(request: &mut Request<&mut EspHttpConnection<'_
} }
let actual_data = &buf[0..read]; let actual_data = &buf[0..read];
total_read += read; total_read += read;
if total_read > max_read{ if total_read > max_read {
bail!("Request too large {total_read} > {max_read}"); bail!("Request too large {total_read} > {max_read}");
} }
data_store.push(actual_data.to_owned()); data_store.push(actual_data.to_owned());