chore: cargo fmt
This commit is contained in:
parent
3cdaacabac
commit
f340278236
@ -33,7 +33,7 @@ pub struct NightLampConfig {
|
||||
pub night_lamp_hour_end: u8,
|
||||
pub night_lamp_only_when_dark: bool,
|
||||
pub low_soc_cutoff: u8,
|
||||
pub low_soc_restore: u8
|
||||
pub low_soc_restore: u8,
|
||||
}
|
||||
impl Default for NightLampConfig {
|
||||
fn default() -> Self {
|
||||
@ -43,7 +43,7 @@ impl Default for NightLampConfig {
|
||||
night_lamp_hour_end: 2,
|
||||
night_lamp_only_when_dark: true,
|
||||
low_soc_cutoff: 30,
|
||||
low_soc_restore: 50
|
||||
low_soc_restore: 50,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,7 +1,7 @@
|
||||
use std::{collections::HashMap, sync::Mutex};
|
||||
use serde::Serialize;
|
||||
use std::{collections::HashMap, sync::Mutex};
|
||||
use strum::{EnumIter, IntoEnumIterator};
|
||||
use strum_macros::IntoStaticStr;
|
||||
use strum_macros::IntoStaticStr;
|
||||
|
||||
use esp_idf_svc::systime::EspSystemTime;
|
||||
use once_cell::sync::Lazy;
|
||||
@ -9,16 +9,17 @@ use ringbuffer::{ConstGenericRingBuffer, RingBuffer};
|
||||
use text_template::Template;
|
||||
use unit_enum::UnitEnum;
|
||||
|
||||
const TXT_SHORT_LENGTH:usize = 8;
|
||||
const TXT_LONG_LENGTH:usize = 32;
|
||||
const TXT_SHORT_LENGTH: usize = 8;
|
||||
const TXT_LONG_LENGTH: usize = 32;
|
||||
|
||||
const BUFFER_SIZE:usize = 220;
|
||||
const BUFFER_SIZE: usize = 220;
|
||||
|
||||
#[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)]
|
||||
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)]
|
||||
pub struct LogEntry {
|
||||
@ -27,36 +28,36 @@ pub struct LogEntry {
|
||||
pub a: u32,
|
||||
pub b: u32,
|
||||
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(){
|
||||
unsafe {
|
||||
pub fn init() {
|
||||
unsafe {
|
||||
BUFFER = ConstGenericRingBuffer::<LogEntry, BUFFER_SIZE>::new();
|
||||
};
|
||||
let mut access = BUFFER_ACCESS.lock().unwrap();
|
||||
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() {
|
||||
match target.push(char) {
|
||||
Ok(_) => {}, //continue adding chars
|
||||
Ok(_) => {} //continue adding chars
|
||||
Err(_) => {
|
||||
//clear space for two asci chars
|
||||
while target.len()+2 >= LIMIT {
|
||||
while target.len() + 2 >= LIMIT {
|
||||
target.pop().unwrap();
|
||||
}
|
||||
//add .. to shortened strings
|
||||
target.push('.').unwrap();
|
||||
target.push('.').unwrap();
|
||||
return;
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_log() -> Vec<LogEntry>{
|
||||
pub fn get_log() -> Vec<LogEntry> {
|
||||
let buffer = BUFFER_ACCESS.lock().unwrap();
|
||||
let mut read_copy = Vec::new();
|
||||
for entry in buffer.iter() {
|
||||
@ -67,19 +68,17 @@ pub fn get_log() -> Vec<LogEntry>{
|
||||
return read_copy;
|
||||
}
|
||||
|
||||
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_long_stack:heapless::String<TXT_LONG_LENGTH> = heapless::String::new();
|
||||
|
||||
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_long_stack: heapless::String<TXT_LONG_LENGTH> = heapless::String::new();
|
||||
|
||||
limit_length(txt_short, &mut txt_short_stack);
|
||||
limit_length(txt_long, &mut txt_long_stack);
|
||||
|
||||
|
||||
let time = EspSystemTime {}.now().as_millis() as u64;
|
||||
|
||||
|
||||
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 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 serial_entry = template.fill_in(&values);
|
||||
|
||||
|
||||
println!("{serial_entry}");
|
||||
|
||||
|
||||
|
||||
let entry = LogEntry{
|
||||
let entry = LogEntry {
|
||||
timestamp: time,
|
||||
message_id: ordinal,
|
||||
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,
|
||||
};
|
||||
|
||||
|
||||
let mut buffer = BUFFER_ACCESS.lock().unwrap();
|
||||
let mut buffer = BUFFER_ACCESS.lock().unwrap();
|
||||
buffer.push(entry);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@ -121,9 +114,9 @@ mod tests {
|
||||
#[test]
|
||||
fn within_limit() {
|
||||
let test = "12345678";
|
||||
|
||||
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_short_stack: heapless::String<TXT_SHORT_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_long_stack);
|
||||
|
||||
@ -132,39 +125,43 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#[derive(IntoStaticStr, EnumIter, Serialize, PartialEq, Eq, PartialOrd, Ord, Clone, UnitEnum)]
|
||||
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,
|
||||
#[strum(serialize = "Current restart to conf mode ${number_a}")]
|
||||
#[strum(serialize = "Current restart to conf mode ${number_a}")]
|
||||
RestartToConfig,
|
||||
#[strum(serialize = "Current low voltage detection is ${number_a}")]
|
||||
#[strum(serialize = "Current low voltage detection is ${number_a}")]
|
||||
LowVoltage,
|
||||
#[strum(serialize = "Error communicating with battery!! ${txt_long}")]
|
||||
#[strum(serialize = "Error communicating with battery!! ${txt_long}")]
|
||||
BatteryCommunicationError,
|
||||
#[strum(serialize = "Tank sensor raw ${number_a} percent ${number_b}")]
|
||||
#[strum(serialize = "Tank sensor raw ${number_a} percent ${number_b}")]
|
||||
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,
|
||||
#[strum(serialize = "IP info: ${txt_long}")]
|
||||
#[strum(serialize = "IP info: ${txt_long}")]
|
||||
WifiInfo,
|
||||
#[strum(serialize = "Plant:${txt_short} a:${number_a} b:${number_b}")]
|
||||
#[strum(serialize = "Plant:${txt_short} a:${number_a} b:${number_b}")]
|
||||
TestSensor,
|
||||
#[strum(serialize = "Stay alive topic is ${txt_long}")]
|
||||
#[strum(serialize = "Stay alive topic is ${txt_long}")]
|
||||
StayAlive,
|
||||
#[strum(serialize = "Connecting mqtt ${txt_short} with id ${txt_long}")]
|
||||
#[strum(serialize = "Connecting mqtt ${txt_short} with id ${txt_long}")]
|
||||
MqttInfo,
|
||||
#[strum(serialize = "Received stay alive with value ${txt_short}")]
|
||||
#[strum(serialize = "Received stay alive with value ${txt_short}")]
|
||||
MqttStayAliveRec,
|
||||
#[strum(serialize = "Unknown topic recieved ${txt_long}")]
|
||||
#[strum(serialize = "Unknown topic recieved ${txt_long}")]
|
||||
UnknownTopic,
|
||||
#[strum(serialize = "Partition state is ${txt_long}")]
|
||||
#[strum(serialize = "Partition state is ${txt_long}")]
|
||||
PartitionState,
|
||||
#[strum(serialize = "Mounted Filesystem free ${number_a} total ${number_b} use ${txt_short}")]
|
||||
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,
|
||||
#[strum(serialize = "Year inplausible, force config mode")]
|
||||
YearInplausibleForceConfig,
|
||||
@ -178,12 +175,16 @@ pub enum LogMessage {
|
||||
ConfigModeMissingConfig,
|
||||
#[strum(serialize = "startup state wifi ${number_a} sntp ${number_b} mqtt ${txt_short}")]
|
||||
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,
|
||||
#[strum(serialize = "Enable main power dryrun: ${number_a}")]
|
||||
EnableMain,
|
||||
#[strum(serialize = "Pumped multiple times, but plant is still to try attempt: ${number_a} limit :: ${number_b} plant: ${txt_short}")]
|
||||
ConsecutivePumpCountLimit
|
||||
#[strum(
|
||||
serialize = "Pumped multiple times, but plant is still to try attempt: ${number_a} limit :: ${number_b} plant: ${txt_short}"
|
||||
)]
|
||||
ConsecutivePumpCountLimit,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
|
129
rust/src/main.rs
129
rust/src/main.rs
@ -10,17 +10,11 @@ use chrono_tz::{Europe::Berlin, Tz};
|
||||
use config::Mode;
|
||||
use esp_idf_hal::delay::Delay;
|
||||
use esp_idf_sys::{
|
||||
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,
|
||||
vTaskDelay
|
||||
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, vTaskDelay,
|
||||
};
|
||||
use esp_ota::{mark_app_valid, rollback_and_reboot};
|
||||
use log::log;
|
||||
@ -28,15 +22,11 @@ use once_cell::sync::Lazy;
|
||||
use plant_hal::{PlantCtrlBoard, PlantHal, PLANT_COUNT};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
config::PlantControllerConfig,
|
||||
webserver::webserver::httpd,
|
||||
};
|
||||
mod log;
|
||||
use crate::{config::PlantControllerConfig, webserver::webserver::httpd};
|
||||
mod config;
|
||||
mod log;
|
||||
pub mod plant_hal;
|
||||
|
||||
|
||||
const TIME_ZONE: Tz = Berlin;
|
||||
|
||||
const MOIST_SENSOR_MAX_FREQUENCY: u32 = 5000; // 60kHz (500Hz margin)
|
||||
@ -72,7 +62,6 @@ impl WaitType {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#[derive(Serialize, Deserialize, Debug, PartialEq, Default)]
|
||||
/// Light State tracking data for mqtt
|
||||
struct LightState {
|
||||
@ -182,8 +171,6 @@ struct PlantStateMQTT<'a> {
|
||||
next_pump: &'a str,
|
||||
}
|
||||
|
||||
|
||||
|
||||
fn safe_main() -> anyhow::Result<()> {
|
||||
// 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
|
||||
@ -234,38 +221,45 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
&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();
|
||||
board.general_fault(false);
|
||||
|
||||
log(log::LogMessage::MountingFilesystem, 0,0,"","");
|
||||
log(log::LogMessage::MountingFilesystem, 0, 0, "", "");
|
||||
board.mount_file_system()?;
|
||||
let free_space = board.file_system_size()?;
|
||||
log(log::LogMessage::FilesystemMount, free_space.free_size as u32,
|
||||
free_space.total_size as u32, &free_space.used_size.to_string(), "");
|
||||
log(
|
||||
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| {
|
||||
println!("rtc module error: {:?}", err);
|
||||
board.general_fault(true);
|
||||
board.time()
|
||||
}).map_err(|err| -> Result<(), _>{
|
||||
bail!("time error {}", err);
|
||||
}).unwrap();
|
||||
let mut cur = board
|
||||
.get_rtc_time()
|
||||
.or_else(|err| {
|
||||
println!("rtc module error: {:?}", err);
|
||||
board.general_fault(true);
|
||||
board.time()
|
||||
})
|
||||
.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)
|
||||
if cur.year() < 2020 {
|
||||
to_config = true;
|
||||
log(log::LogMessage::YearInplausibleForceConfig, 0,0,"","");
|
||||
log(log::LogMessage::YearInplausibleForceConfig, 0, 0, "", "");
|
||||
}
|
||||
|
||||
|
||||
println!("cur is {}", cur);
|
||||
board.update_charge_indicator();
|
||||
|
||||
if board.get_restart_to_conf() {
|
||||
log(log::LogMessage::ConfigModeSoftwareOverride, 0,0,"","");
|
||||
log(log::LogMessage::ConfigModeSoftwareOverride, 0, 0, "", "");
|
||||
for _i in 0..2 {
|
||||
board.general_fault(true);
|
||||
Delay::new_default().delay_ms(100);
|
||||
@ -277,7 +271,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::ConfigModeButtonOverride, 0,0,"","");
|
||||
log(log::LogMessage::ConfigModeButtonOverride, 0, 0, "", "");
|
||||
for _i in 0..5 {
|
||||
board.general_fault(true);
|
||||
Delay::new_default().delay_ms(100);
|
||||
@ -291,7 +285,7 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
} else {
|
||||
board.general_fault(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let config: PlantControllerConfig;
|
||||
match board.get_config() {
|
||||
@ -299,7 +293,13 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
config = valid;
|
||||
}
|
||||
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!
|
||||
let _ = board.wifi_ap(Option::None);
|
||||
drop(board);
|
||||
@ -403,7 +403,13 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
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 {
|
||||
//check if client or ap mode and init wifi
|
||||
@ -414,10 +420,9 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
let _webserver = httpd(reboot_now.clone());
|
||||
wait_infinity(WaitType::ConfigButton, reboot_now.clone());
|
||||
} else {
|
||||
log(log::LogMessage::NormalRun, 0,0,"","");
|
||||
log(log::LogMessage::NormalRun, 0, 0, "", "");
|
||||
}
|
||||
|
||||
|
||||
let dry_run = false;
|
||||
|
||||
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 {
|
||||
..Default::default()
|
||||
});
|
||||
@ -477,26 +480,36 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
&mut board,
|
||||
);
|
||||
|
||||
|
||||
let pump_required = plantstate.iter().any(|it| it.do_water) && !water_frozen;
|
||||
if pump_required {
|
||||
log(log::LogMessage::EnableMain, dry_run as u32,0,"","");
|
||||
if !dry_run{
|
||||
board.any_pump(true)?;
|
||||
}
|
||||
log(log::LogMessage::EnableMain, dry_run as u32, 0, "", "");
|
||||
if !dry_run {
|
||||
board.any_pump(true)?;
|
||||
}
|
||||
for plant in 0..PLANT_COUNT {
|
||||
let state = &mut plantstate[plant];
|
||||
if state.do_water {
|
||||
|
||||
let plant_config = &config.plants[plant];
|
||||
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::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;
|
||||
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.last_pump_time(plant);
|
||||
state.active = true;
|
||||
@ -519,21 +532,21 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
enabled: config.night_lamp.enabled,
|
||||
..Default::default()
|
||||
};
|
||||
if light_state.enabled {
|
||||
if light_state.enabled {
|
||||
light_state.is_day = is_day;
|
||||
light_state.out_of_work_hour = !in_time_range(
|
||||
&timezone_time,
|
||||
config.night_lamp.night_lamp_hour_start,
|
||||
config.night_lamp.night_lamp_hour_end,
|
||||
);
|
||||
|
||||
|
||||
if state_of_charge < config.night_lamp.low_soc_cutoff {
|
||||
board.set_low_voltage_in_cycle();
|
||||
} else if state_of_charge > config.night_lamp.low_soc_restore {
|
||||
board.clear_low_voltage_in_cycle();
|
||||
}
|
||||
light_state.battery_low = board.low_voltage_in_cycle();
|
||||
|
||||
|
||||
if !light_state.out_of_work_hour {
|
||||
if config.night_lamp.night_lamp_only_when_dark {
|
||||
if !light_state.is_day {
|
||||
@ -556,7 +569,7 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
light_state.active = false;
|
||||
board.light(false).unwrap();
|
||||
}
|
||||
|
||||
|
||||
println!("Lightstate is {:?}", light_state);
|
||||
}
|
||||
|
||||
@ -587,13 +600,11 @@ fn safe_main() -> anyhow::Result<()> {
|
||||
//is deep sleep
|
||||
mark_app_valid();
|
||||
|
||||
|
||||
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);
|
||||
|
||||
|
||||
if stay_alive {
|
||||
println!("Go to stay alive move");
|
||||
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) {
|
||||
//ensure clean http answer
|
||||
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 {
|
||||
git_hash: (branch + "@" + hash),
|
||||
build_time: env!("VERGEN_BUILD_TIMESTAMP").to_owned(),
|
||||
partition: partition.to_owned() + &address.to_string()
|
||||
partition: partition.to_owned() + &address.to_string(),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
use bq34z100::{Bq34Z100Error, Bq34z100g1, Bq34z100g1Driver};
|
||||
|
||||
use crate::log::LogMessage;
|
||||
use ds323x::{DateTimeAccess, Ds323x};
|
||||
use esp_ota::mark_app_valid;
|
||||
use crate::log::LogMessage;
|
||||
|
||||
use eeprom24x::{Eeprom24x, Eeprom24xTrait, SlaveAddr};
|
||||
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::wifi::config::{ScanConfig, ScanType};
|
||||
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 once_cell::sync::Lazy;
|
||||
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::{bail, Ok, Result};
|
||||
@ -80,7 +83,6 @@ const PUMP5_BIT: usize = 5;
|
||||
const PUMP6_BIT: usize = 6;
|
||||
const PUMP7_BIT: usize = 7;
|
||||
|
||||
|
||||
const MS_0: usize = 8;
|
||||
const MS_4: usize = 9;
|
||||
const MS_2: usize = 10;
|
||||
@ -129,8 +131,6 @@ 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,
|
||||
@ -209,25 +209,26 @@ pub struct BatteryState {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
pub struct BackupHeader{
|
||||
pub struct BackupHeader {
|
||||
pub timestamp: i64,
|
||||
crc16: u16,
|
||||
pub size: usize
|
||||
pub size: usize,
|
||||
}
|
||||
|
||||
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() {
|
||||
OkStd(current) => current < 20,
|
||||
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();
|
||||
unsafe {
|
||||
unsafe {
|
||||
//if we dont do this here, we might just revert a newly flashed firmeware
|
||||
mark_app_valid();
|
||||
//allow early wakup by pressing the boot button
|
||||
@ -235,14 +236,17 @@ impl PlantCtrlBoard<'_> {
|
||||
esp_restart();
|
||||
} else {
|
||||
//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_deep_sleep(duration_in_ms);
|
||||
esp_sleep_enable_ext1_wakeup(
|
||||
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> {
|
||||
let dummy = BackupHeader{
|
||||
let dummy = BackupHeader {
|
||||
timestamp: 0,
|
||||
crc16: 0,
|
||||
size: 0,
|
||||
@ -251,17 +255,16 @@ impl PlantCtrlBoard<'_> {
|
||||
let mut header_page_buffer = vec![0_u8; store];
|
||||
|
||||
match self.eeprom.read_data(0, &mut header_page_buffer) {
|
||||
OkStd(_) => {},
|
||||
OkStd(_) => {}
|
||||
Err(err) => bail!("Error reading eeprom header {:?}", err),
|
||||
};
|
||||
println!("Raw header is {:?} with size {}", header_page_buffer , store);
|
||||
let header:BackupHeader = bincode::deserialize(&header_page_buffer)?;
|
||||
println!("Raw header is {:?} with size {}", header_page_buffer, store);
|
||||
let header: BackupHeader = bincode::deserialize(&header_page_buffer)?;
|
||||
Ok(header)
|
||||
}
|
||||
|
||||
|
||||
pub fn get_backup_config(&mut self) -> Result<Vec<u8>> {
|
||||
let dummy = BackupHeader{
|
||||
let dummy = BackupHeader {
|
||||
timestamp: 0,
|
||||
crc16: 0,
|
||||
size: 0,
|
||||
@ -270,27 +273,31 @@ impl PlantCtrlBoard<'_> {
|
||||
let mut header_page_buffer = vec![0_u8; store];
|
||||
|
||||
match self.eeprom.read_data(0, &mut header_page_buffer) {
|
||||
OkStd(_) => {},
|
||||
OkStd(_) => {}
|
||||
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];
|
||||
match self.eeprom.read_data(data_start_address, &mut data_buffer) {
|
||||
OkStd(_) => {},
|
||||
OkStd(_) => {}
|
||||
Err(err) => bail!("Error reading eeprom data {:?}", err),
|
||||
};
|
||||
|
||||
let checksum = X25.checksum(&data_buffer);
|
||||
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)
|
||||
}
|
||||
|
||||
pub fn backup_config(&mut self, bytes: &[u8]) -> Result<()>{
|
||||
pub fn backup_config(&mut self, bytes: &[u8]) -> Result<()> {
|
||||
let delay = Delay::new_default();
|
||||
|
||||
let checksum = X25.checksum(bytes);
|
||||
@ -298,49 +305,52 @@ impl PlantCtrlBoard<'_> {
|
||||
|
||||
let time = self.get_rtc_time()?.timestamp_millis();
|
||||
|
||||
let header = BackupHeader{
|
||||
crc16 : checksum,
|
||||
timestamp : time,
|
||||
let header = BackupHeader {
|
||||
crc16: checksum,
|
||||
timestamp: time,
|
||||
size: bytes.len(),
|
||||
};
|
||||
|
||||
let encoded = bincode::serialize(&header)?;
|
||||
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) {
|
||||
OkStd(_) => {},
|
||||
OkStd(_) => {}
|
||||
Err(err) => bail!("Error writing eeprom {:?}", err),
|
||||
};
|
||||
delay.delay_ms(5);
|
||||
|
||||
let to_write= bytes.chunks(page_size);
|
||||
let to_write = bytes.chunks(page_size);
|
||||
|
||||
let mut lastiter = 0;
|
||||
let mut current_page = 1;
|
||||
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) {
|
||||
OkStd(_) => {},
|
||||
OkStd(_) => {}
|
||||
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 {
|
||||
for i in 0..PLANT_COUNT {
|
||||
self.fault(i, iter==i);
|
||||
self.fault(i, iter == i);
|
||||
}
|
||||
lastiter = iter;
|
||||
}
|
||||
}
|
||||
|
||||
//update led here?
|
||||
delay.delay_ms(5);
|
||||
}
|
||||
return Ok(())
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
pub fn get_battery_state(&mut self) -> BatteryState {
|
||||
@ -393,8 +403,8 @@ impl PlantCtrlBoard<'_> {
|
||||
file_system_corrupt = Some(format!("{err:?}"));
|
||||
}
|
||||
}
|
||||
let mut total:usize = 0;
|
||||
let mut used:usize = 0;
|
||||
let mut total: usize = 0;
|
||||
let mut used: usize = 0;
|
||||
unsafe {
|
||||
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 mut percent = r2 / 190_f32 * 100_f32;
|
||||
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);
|
||||
}
|
||||
@ -663,7 +679,13 @@ 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::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.sort();
|
||||
@ -751,7 +773,7 @@ impl PlantCtrlBoard<'_> {
|
||||
}
|
||||
//update freertos registers ;)
|
||||
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)
|
||||
}
|
||||
|
||||
@ -891,13 +913,13 @@ impl PlantCtrlBoard<'_> {
|
||||
let b = self.measure_moisture_hz(plant, plant_hal::Sensor::B);
|
||||
let aa = match a {
|
||||
OkStd(a) => a as u32,
|
||||
Err(_) => u32::MAX
|
||||
Err(_) => u32::MAX,
|
||||
};
|
||||
let bb = match b {
|
||||
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);
|
||||
Ok(())
|
||||
@ -946,7 +968,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::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_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_ok_copy = round_trip_ok.clone();
|
||||
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 payload = event.payload();
|
||||
match payload {
|
||||
@ -972,10 +994,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::MqttStayAliveRec, 0 ,0,&data, "");
|
||||
log(LogMessage::MqttStayAliveRec, 0, 0, &data, "");
|
||||
STAY_ALIVE.store(value, std::sync::atomic::Ordering::Relaxed);
|
||||
} 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 {
|
||||
return unsafe { RESTART_TO_CONF };
|
||||
}
|
||||
|
||||
|
||||
pub fn set_restart_to_conf(&mut self, to_conf: bool) {
|
||||
unsafe {
|
||||
RESTART_TO_CONF = to_conf;
|
||||
@ -1327,10 +1349,9 @@ impl PlantHal {
|
||||
let awake = &mut shift_register.decompose()[AWAKE];
|
||||
awake.set_high()?;
|
||||
|
||||
|
||||
let charging = &mut shift_register.decompose()[CHARGING];
|
||||
charging.set_high()?;
|
||||
|
||||
|
||||
let ms0 = &mut shift_register.decompose()[MS_0];
|
||||
ms0.set_low()?;
|
||||
let ms1 = &mut shift_register.decompose()[MS_1];
|
||||
@ -1343,7 +1364,6 @@ impl PlantHal {
|
||||
let ms4 = &mut shift_register.decompose()[MS_4];
|
||||
ms4.set_high()?;
|
||||
|
||||
|
||||
println!("Init battery driver");
|
||||
let mut battery_driver = Bq34z100g1Driver {
|
||||
i2c: MutexDevice::new(&I2C_DRIVER),
|
||||
@ -1365,7 +1385,6 @@ impl PlantHal {
|
||||
let mut one_wire_pin = PinDriver::input_output_od(peripherals.pins.gpio18)?;
|
||||
one_wire_pin.set_pull(Pull::Floating).unwrap();
|
||||
|
||||
|
||||
let rtc_time = rtc.datetime();
|
||||
match rtc_time {
|
||||
OkStd(tt) => {
|
||||
@ -1389,42 +1408,32 @@ impl PlantHal {
|
||||
let mut to_config_mode: bool = false;
|
||||
let reasons = ResetReason::get();
|
||||
match reasons {
|
||||
ResetReason::Software => {},
|
||||
ResetReason::ExternalPin => {},
|
||||
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::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
|
||||
},
|
||||
}
|
||||
ResetReason::JTAG => 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 {
|
||||
unsafe {
|
||||
LAST_WATERING_TIMESTAMP = [0; PLANT_COUNT];
|
||||
@ -1435,11 +1444,23 @@ impl PlantHal {
|
||||
};
|
||||
} else {
|
||||
unsafe {
|
||||
if to_config_mode{
|
||||
RESTART_TO_CONF = true;
|
||||
if to_config_mode {
|
||||
RESTART_TO_CONF = true;
|
||||
}
|
||||
log(LogMessage::RestartToConfig, RESTART_TO_CONF as u32, 0, "","");
|
||||
log(LogMessage::LowVoltage, 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 {}",
|
||||
@ -1513,12 +1534,16 @@ impl PlantHal {
|
||||
|
||||
let status = print_battery(&mut battery_driver);
|
||||
match status {
|
||||
OkStd(_) => {
|
||||
|
||||
},
|
||||
OkStd(_) => {}
|
||||
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())?;
|
||||
|
||||
|
@ -1,22 +1,22 @@
|
||||
//offer ota and config mode
|
||||
|
||||
use std::{
|
||||
str::from_utf8,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
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 chrono::DateTime;
|
||||
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;
|
||||
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 serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
str::from_utf8,
|
||||
sync::{atomic::AtomicBool, Arc},
|
||||
};
|
||||
use url::Url;
|
||||
|
||||
use crate::config::PlantControllerConfig;
|
||||
@ -49,14 +49,14 @@ pub struct TestPump {
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize, PartialEq, Debug)]
|
||||
pub struct WebBackupHeader{
|
||||
pub struct WebBackupHeader {
|
||||
timestamp: std::string::String,
|
||||
size: usize
|
||||
size: usize,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct NightLampCommand {
|
||||
active: bool
|
||||
pub struct NightLampCommand {
|
||||
active: bool,
|
||||
}
|
||||
|
||||
fn write_time(
|
||||
@ -66,10 +66,10 @@ fn write_time(
|
||||
let time: SetTime = serde_json::from_slice(&actual_data)?;
|
||||
let parsed = DateTime::parse_from_rfc3339(time.time).map_err(|err| anyhow::anyhow!(err))?;
|
||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||
|
||||
|
||||
let now = timeval {
|
||||
tv_sec: parsed.to_utc().timestamp(),
|
||||
tv_usec: 0
|
||||
tv_usec: 0,
|
||||
};
|
||||
unsafe { settimeofday(&now, core::ptr::null_mut()) };
|
||||
board.set_rtc_time(&parsed.to_utc())?;
|
||||
@ -173,7 +173,6 @@ fn get_backup_config(
|
||||
anyhow::Ok(Some(json))
|
||||
}
|
||||
|
||||
|
||||
fn backup_info(
|
||||
_request: &mut Request<&mut EspHttpConnection>,
|
||||
) -> Result<Option<std::string::String>, anyhow::Error> {
|
||||
@ -182,13 +181,13 @@ fn backup_info(
|
||||
let json = match header {
|
||||
Ok(h) => {
|
||||
let timestamp = DateTime::from_timestamp_millis(h.timestamp).unwrap();
|
||||
let wbh = WebBackupHeader{
|
||||
let wbh = WebBackupHeader {
|
||||
timestamp: timestamp.to_rfc3339(),
|
||||
size: h.size,
|
||||
};
|
||||
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))
|
||||
}
|
||||
@ -203,7 +202,6 @@ fn set_config(
|
||||
anyhow::Ok(Some("saved".to_owned()))
|
||||
}
|
||||
|
||||
|
||||
fn get_battery_state(
|
||||
_request: &mut Request<&mut EspHttpConnection>,
|
||||
) -> 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> {
|
||||
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(
|
||||
_request: &mut Request<&mut EspHttpConnection>,
|
||||
) -> Result<Option<std::string::String>, anyhow::Error> {
|
||||
|
||||
anyhow::Ok(Some(serde_json::to_string(&get_version())?))
|
||||
}
|
||||
|
||||
@ -278,7 +277,7 @@ fn ota(
|
||||
let mut board = BOARD_ACCESS.lock().unwrap();
|
||||
let mut ota = OtaUpdate::begin()?;
|
||||
println!("start ota");
|
||||
|
||||
|
||||
//having a larger buffer is not really faster, requires more stack and prevents the progress bar from working ;)
|
||||
const BUFFER_SIZE: usize = 512;
|
||||
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
||||
@ -289,14 +288,13 @@ fn ota(
|
||||
total_read += read;
|
||||
let to_write = &buffer[0..read];
|
||||
|
||||
let iter = (total_read/1024)%8;
|
||||
let iter = (total_read / 1024) % 8;
|
||||
if iter != lastiter {
|
||||
for i in 0..PLANT_COUNT {
|
||||
board.fault(i, iter==i);
|
||||
|
||||
board.fault(i, iter == i);
|
||||
}
|
||||
lastiter = iter;
|
||||
}
|
||||
}
|
||||
|
||||
ota.write(to_write)?;
|
||||
if read == 0 {
|
||||
@ -380,15 +378,15 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
||||
})
|
||||
.unwrap();
|
||||
server
|
||||
.fn_handler("/log", Method::Get, |request| {
|
||||
handle_error_to500(request, get_log)
|
||||
})
|
||||
.unwrap();
|
||||
server.fn_handler("/log_localization", Method::Get, |request| {
|
||||
cors_response(request, 200, &get_log_localization_config().unwrap())
|
||||
|
||||
})
|
||||
.unwrap();
|
||||
.fn_handler("/log", Method::Get, |request| {
|
||||
handle_error_to500(request, get_log)
|
||||
})
|
||||
.unwrap();
|
||||
server
|
||||
.fn_handler("/log_localization", Method::Get, |request| {
|
||||
cors_response(request, 200, &get_log_localization_config().unwrap())
|
||||
})
|
||||
.unwrap();
|
||||
server
|
||||
.fn_handler("/battery", Method::Get, |request| {
|
||||
handle_error_to500(request, get_battery_state)
|
||||
@ -415,10 +413,10 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
||||
})
|
||||
.unwrap();
|
||||
server
|
||||
.fn_handler("/lamptest", Method::Post, |request| {
|
||||
handle_error_to500(request, night_lamp_test)
|
||||
})
|
||||
.unwrap();
|
||||
.fn_handler("/lamptest", Method::Post, |request| {
|
||||
handle_error_to500(request, night_lamp_test)
|
||||
})
|
||||
.unwrap();
|
||||
server
|
||||
.fn_handler("/boardtest", Method::Post, move |_| {
|
||||
BOARD_ACCESS.lock().unwrap().test()
|
||||
@ -456,15 +454,15 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
||||
})
|
||||
.unwrap();
|
||||
server
|
||||
.fn_handler("/backup_config", Method::Post, move |request| {
|
||||
handle_error_to500(request, backup_config)
|
||||
})
|
||||
.unwrap();
|
||||
.fn_handler("/backup_config", Method::Post, move |request| {
|
||||
handle_error_to500(request, backup_config)
|
||||
})
|
||||
.unwrap();
|
||||
server
|
||||
.fn_handler("/backup_info", Method::Get, move |request| {
|
||||
handle_error_to500(request, backup_info)
|
||||
})
|
||||
.unwrap();
|
||||
.fn_handler("/backup_info", Method::Get, move |request| {
|
||||
handle_error_to500(request, backup_info)
|
||||
})
|
||||
.unwrap();
|
||||
server
|
||||
.fn_handler("/files", Method::Get, move |request| {
|
||||
handle_error_to500(request, list_files)
|
||||
@ -472,25 +470,22 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
||||
.unwrap();
|
||||
let reboot_now_for_reboot = reboot_now.clone();
|
||||
server
|
||||
.fn_handler("/reboot", Method::Post, move |_| {
|
||||
BOARD_ACCESS
|
||||
.lock()
|
||||
.unwrap()
|
||||
.set_restart_to_conf(true);
|
||||
reboot_now_for_reboot.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
.fn_handler("/reboot", Method::Post, move |_| {
|
||||
BOARD_ACCESS.lock().unwrap().set_restart_to_conf(true);
|
||||
reboot_now_for_reboot.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
unsafe { vTaskDelay(1) };
|
||||
|
||||
let reboot_now_for_exit = reboot_now.clone();
|
||||
server
|
||||
.fn_handler("/exit", Method::Post, move |_| {
|
||||
reboot_now_for_exit.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
.fn_handler("/exit", Method::Post, move |_| {
|
||||
reboot_now_for_exit.store(true, std::sync::atomic::Ordering::Relaxed);
|
||||
anyhow::Ok(())
|
||||
})
|
||||
.unwrap();
|
||||
server
|
||||
.fn_handler("/file", Method::Get, move |request| {
|
||||
let filename = query_param(request.uri(), "filename").unwrap();
|
||||
@ -532,28 +527,23 @@ pub fn httpd(reboot_now: Arc<AtomicBool>) -> Box<EspHttpServer<'static>> {
|
||||
server
|
||||
.fn_handler("/file", Method::Post, move |mut request| {
|
||||
let filename = query_param(request.uri(), "filename").unwrap();
|
||||
let lock = BOARD_ACCESS
|
||||
.lock()
|
||||
.unwrap();
|
||||
let file_handle =
|
||||
lock.get_file_handle(&filename, true);
|
||||
let lock = BOARD_ACCESS.lock().unwrap();
|
||||
let file_handle = lock.get_file_handle(&filename, true);
|
||||
match file_handle {
|
||||
//TODO get free filesystem size, check against during write if not to large
|
||||
|
||||
Ok(mut file_handle) => {
|
||||
const BUFFER_SIZE: usize = 512;
|
||||
let mut buffer: [u8; BUFFER_SIZE] = [0; BUFFER_SIZE];
|
||||
let mut total_read: usize = 0;
|
||||
let mut lastiter = 0;
|
||||
loop {
|
||||
|
||||
let iter = (total_read/1024)%8;
|
||||
let iter = (total_read / 1024) % 8;
|
||||
if iter != lastiter {
|
||||
for i in 0..PLANT_COUNT {
|
||||
lock.fault(i, iter==i);
|
||||
lock.fault(i, iter == i);
|
||||
}
|
||||
lastiter = iter;
|
||||
}
|
||||
}
|
||||
|
||||
let read = request.read(&mut buffer)?;
|
||||
total_read += read;
|
||||
@ -697,11 +687,14 @@ fn handle_error_to500(
|
||||
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 mut data_store = Vec::new();
|
||||
let mut total_read = 0;
|
||||
loop{
|
||||
loop {
|
||||
let mut buf = [0_u8; 64];
|
||||
let read = request.read(&mut buf)?;
|
||||
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];
|
||||
total_read += read;
|
||||
if total_read > max_read{
|
||||
if total_read > max_read {
|
||||
bail!("Request too large {total_read} > {max_read}");
|
||||
}
|
||||
data_store.push(actual_data.to_owned());
|
||||
|
Loading…
x
Reference in New Issue
Block a user