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_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,
}
}
}

View File

@ -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)]

View File

@ -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(),
};
}

View File

@ -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())?;

View File

@ -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());