refactor: unify moisture handling, update config structure, and add peer dependencies
This commit is contained in:
		
							
								
								
									
										8
									
								
								Software/MainBoard/.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Software/MainBoard/.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| # Default ignored files | ||||
| /shelf/ | ||||
| /workspace.xml | ||||
| # Editor-based HTTP Client requests | ||||
| /httpRequests/ | ||||
| # Datasource local storage ignored files | ||||
| /dataSources/ | ||||
| /dataSources.local.xml | ||||
							
								
								
									
										11
									
								
								Software/MainBoard/.idea/MainBoard.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								Software/MainBoard/.idea/MainBoard.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <module type="EMPTY_MODULE" version="4"> | ||||
|   <component name="NewModuleRootManager"> | ||||
|     <content url="file://$MODULE_DIR$"> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/rust/src" isTestSource="false" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/rust/target" /> | ||||
|     </content> | ||||
|     <orderEntry type="inheritedJdk" /> | ||||
|     <orderEntry type="sourceFolder" forTests="false" /> | ||||
|   </component> | ||||
| </module> | ||||
							
								
								
									
										8
									
								
								Software/MainBoard/.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Software/MainBoard/.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="ProjectModuleManager"> | ||||
|     <modules> | ||||
|       <module fileurl="file://$PROJECT_DIR$/.idea/MainBoard.iml" filepath="$PROJECT_DIR$/.idea/MainBoard.iml" /> | ||||
|     </modules> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										7
									
								
								Software/MainBoard/.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								Software/MainBoard/.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="VcsDirectoryMappings"> | ||||
|     <mapping directory="$PROJECT_DIR$/../.." vcs="Git" /> | ||||
|     <mapping directory="$PROJECT_DIR$/../../website/themes/blowfish" vcs="Git" /> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										172
									
								
								Software/MainBoard/d1.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								Software/MainBoard/d1.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,172 @@ | ||||
| { | ||||
|   "hardware": { | ||||
|     "board": "V4", | ||||
|     "battery": "Disabled" | ||||
|   }, | ||||
|   "network": { | ||||
|     "max_wait": 10000, | ||||
|     "ap_ssid": "PlantCtrl Init", | ||||
|     "ssid": "private", | ||||
|     "password": "wertertzu", | ||||
|     "mqtt_url": "", | ||||
|     "mqtt_user": null, | ||||
|     "mqtt_password": null, | ||||
|     "base_topic": "" | ||||
|   }, | ||||
|   "tank": { | ||||
|     "tank_allow_pumping_if_sensor_error": true, | ||||
|     "tank_empty_percent": 5, | ||||
|     "tank_full_percent": 95, | ||||
|     "tank_sensor_enabled": false, | ||||
|     "tank_useable_ml": 50000, | ||||
|     "tank_warn_percent": 40, | ||||
|     "ml_per_pulse": 0 | ||||
|   }, | ||||
|   "night_lamp": { | ||||
|     "night_lamp_hour_start": 19, | ||||
|     "night_lamp_hour_end": 2, | ||||
|     "night_lamp_only_when_dark": true, | ||||
|     "enabled": true, | ||||
|     "low_soc_cutoff": 30, | ||||
|     "low_soc_restore": 50 | ||||
|   }, | ||||
|   "plants": [ | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_b": false, | ||||
|       "sensor_a": true, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     }, | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_b": false, | ||||
|       "sensor_a": true, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     }, | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_b": false, | ||||
|       "sensor_a": true, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     }, | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_b": false, | ||||
|       "sensor_a": true, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     }, | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_b": false, | ||||
|       "sensor_a": true, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     }, | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_b": false, | ||||
|       "sensor_a": true, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     }, | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_b": false, | ||||
|       "sensor_a": true, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     }, | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_b": false, | ||||
|       "sensor_a": true, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     } | ||||
|   ], | ||||
|   "timezone": "Europe/Berlin" | ||||
| } | ||||
							
								
								
									
										180
									
								
								Software/MainBoard/d2.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										180
									
								
								Software/MainBoard/d2.json
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,180 @@ | ||||
| { | ||||
|   "hardware": { | ||||
|     "board": "V4", | ||||
|     "battery": "Disabled" | ||||
|   }, | ||||
|   "network": { | ||||
|     "ap_ssid": "PlantCtrl Init", | ||||
|     "ssid": "private", | ||||
|     "password": "wertertzu", | ||||
|     "mqtt_url": "", | ||||
|     "base_topic": "", | ||||
|     "mqtt_user": null, | ||||
|     "mqtt_password": null, | ||||
|     "max_wait": 10000 | ||||
|   }, | ||||
|   "tank": { | ||||
|     "tank_sensor_enabled": false, | ||||
|     "tank_allow_pumping_if_sensor_error": true, | ||||
|     "tank_useable_ml": 50000, | ||||
|     "tank_warn_percent": 40, | ||||
|     "tank_empty_percent": 5, | ||||
|     "tank_full_percent": 95, | ||||
|     "ml_per_pulse": 0.0 | ||||
|   }, | ||||
|   "night_lamp": { | ||||
|     "enabled": true, | ||||
|     "night_lamp_hour_start": 19, | ||||
|     "night_lamp_hour_end": 2, | ||||
|     "night_lamp_only_when_dark": true, | ||||
|     "low_soc_cutoff": 30, | ||||
|     "low_soc_restore": 50 | ||||
|   }, | ||||
|   "plants": [ | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_limit_ml": 5000, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_a": true, | ||||
|       "sensor_b": false, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     }, | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_limit_ml": 5000, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_a": true, | ||||
|       "sensor_b": false, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     }, | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_limit_ml": 5000, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_a": true, | ||||
|       "sensor_b": false, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     }, | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_limit_ml": 5000, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_a": true, | ||||
|       "sensor_b": false, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     }, | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_limit_ml": 5000, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_a": true, | ||||
|       "sensor_b": false, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     }, | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_limit_ml": 5000, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_a": true, | ||||
|       "sensor_b": false, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     }, | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_limit_ml": 5000, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_a": true, | ||||
|       "sensor_b": false, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     }, | ||||
|     { | ||||
|       "mode": "Off", | ||||
|       "target_moisture": 40, | ||||
|       "min_moisture": 30, | ||||
|       "pump_time_s": 30, | ||||
|       "pump_limit_ml": 5000, | ||||
|       "pump_cooldown_min": 60, | ||||
|       "pump_hour_start": 9, | ||||
|       "pump_hour_end": 20, | ||||
|       "sensor_a": true, | ||||
|       "sensor_b": false, | ||||
|       "max_consecutive_pump_count": 10, | ||||
|       "moisture_sensor_min_frequency": null, | ||||
|       "moisture_sensor_max_frequency": null, | ||||
|       "min_pump_current_ma": 10, | ||||
|       "max_pump_current_ma": 3000, | ||||
|       "ignore_current_error": true | ||||
|     } | ||||
|   ], | ||||
|   "timezone": "Europe/Berlin" | ||||
| } | ||||
| @@ -151,6 +151,8 @@ heapless = { version = "0.7.17", features = ["serde"] } | ||||
| mcutie = { version = "0.3.0", default-features = false, features = ["log", "homeassistant"] } | ||||
| nb = "1.1.0" | ||||
| embedded-can = "0.4.1" | ||||
| no-panic = "0.1.35" | ||||
| dont_panic = "0.1.0" | ||||
|  | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -2,6 +2,11 @@ | ||||
|  | ||||
| rm ./src/webserver/index.html.gz | ||||
| rm ./src/webserver/bundle.js.gz | ||||
| rm ./src_webpack/index.html.gz | ||||
| rm ./src_webpack/bundle.js.gz | ||||
| rm ./src_webpack/index.html | ||||
| rm ./src_webpack/bundle.js | ||||
|  | ||||
| set -e | ||||
|  | ||||
| pushd ./src_webpack/ | ||||
|   | ||||
| @@ -115,8 +115,8 @@ pub struct PlantControllerConfig { | ||||
| #[serde(default)] | ||||
| pub struct PlantConfig { | ||||
|     pub mode: PlantWateringMode, | ||||
|     pub target_moisture: f32, | ||||
|     pub min_moisture: f32, | ||||
|     pub target_moisture: u8, | ||||
|     pub min_moisture: u8, | ||||
|     pub pump_time_s: u16, | ||||
|     pub pump_limit_ml: u16, | ||||
|     pub pump_cooldown_min: u16, | ||||
| @@ -125,8 +125,8 @@ pub struct PlantConfig { | ||||
|     pub sensor_a: bool, | ||||
|     pub sensor_b: bool, | ||||
|     pub max_consecutive_pump_count: u8, | ||||
|     pub moisture_sensor_min_frequency: Option<f32>, // Optional min frequency | ||||
|     pub moisture_sensor_max_frequency: Option<f32>, // Optional max frequency | ||||
|     pub moisture_sensor_min_frequency: Option<u16>, // Optional min frequency | ||||
|     pub moisture_sensor_max_frequency: Option<u16>, // Optional max frequency | ||||
|     pub min_pump_current_ma: u16, | ||||
|     pub max_pump_current_ma: u16, | ||||
|     pub ignore_current_error: bool, | ||||
| @@ -136,8 +136,8 @@ impl Default for PlantConfig { | ||||
|     fn default() -> Self { | ||||
|         Self { | ||||
|             mode: PlantWateringMode::Off, | ||||
|             target_moisture: 40., | ||||
|             min_moisture: 30., | ||||
|             target_moisture: 40, | ||||
|             min_moisture: 30, | ||||
|             pump_time_s: 30, | ||||
|             pump_limit_ml: 5000, | ||||
|             pump_cooldown_min: 60, | ||||
|   | ||||
| @@ -316,3 +316,4 @@ impl From<sntpc::Error> for FatError { | ||||
|         FatError::SNTPError { error: value } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -146,7 +146,7 @@ macro_rules! mk_static { | ||||
|     ($t:ty,$val:expr) => {{ | ||||
|         static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); | ||||
|         #[deny(unused_attributes)] | ||||
|         let x = STATIC_CELL.uninit().write(($val)); | ||||
|         let x = STATIC_CELL.uninit().write($val); | ||||
|         x | ||||
|     }}; | ||||
| } | ||||
|   | ||||
| @@ -238,7 +238,7 @@ macro_rules! mk_static { | ||||
|     ($t:ty,$val:expr) => {{ | ||||
|         static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); | ||||
|         #[deny(unused_attributes)] | ||||
|         let x = STATIC_CELL.uninit().write(($val)); | ||||
|         let x = STATIC_CELL.uninit().write($val); | ||||
|         x | ||||
|     }}; | ||||
| } | ||||
|   | ||||
| @@ -1,17 +1,16 @@ | ||||
| use crate::bail; | ||||
| use crate::fat_error::{ContextExt, FatError, FatResult}; | ||||
| use crate::fat_error::{ContextExt, FatResult}; | ||||
| use crate::hal::Box; | ||||
| use crate::hal::{DetectionResult, Moistures, Sensor}; | ||||
| use crate::log::{LogMessage, LOG_ACCESS}; | ||||
| use alloc::format; | ||||
| use alloc::string::ToString; | ||||
| use async_trait::async_trait; | ||||
| use bincode::config; | ||||
| use canapi::id::{classify, plant_id, MessageKind, IDENTIFY_CMD_OFFSET}; | ||||
| use canapi::SensorSlot; | ||||
| use embassy_embedded_hal::shared_bus::blocking::i2c::I2cDevice; | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| use embassy_time::{Duration, Instant, Timer, WithTimeout}; | ||||
| use embassy_time::{Duration, Timer, WithTimeout}; | ||||
| use embedded_can::{Frame, Id}; | ||||
| use esp_hal::gpio::Output; | ||||
| use esp_hal::i2c::master::I2c; | ||||
| @@ -86,7 +85,7 @@ impl SensorInteraction for SensorImpl { | ||||
|             } => { | ||||
|                 can_power.set_high(); | ||||
|                 let config = twai_config.take().expect("twai config not set"); | ||||
|                 let mut twai = config.start(); | ||||
|                 let mut twai = config.into_async().start(); | ||||
|  | ||||
|                 loop { | ||||
|                     let rec = twai.receive(); | ||||
| @@ -100,15 +99,17 @@ impl SensorInteraction for SensorImpl { | ||||
|                 } | ||||
|  | ||||
|                 Timer::after_millis(10).await; | ||||
|                 let can = Self::inner_can(&mut twai).await; | ||||
|  | ||||
|                 let mut moistures = Moistures::default(); | ||||
|                 let _ = Self::wait_for_can_measurements(&mut twai, &mut moistures).with_timeout(Duration::from_millis(5000)).await; | ||||
|  | ||||
|  | ||||
|                 can_power.set_low(); | ||||
|  | ||||
|                 let config = twai.stop(); | ||||
|                 let config = twai.stop().into_blocking(); | ||||
|                 twai_config.replace(config); | ||||
|  | ||||
|                 let value = can?; | ||||
|                 Ok(value) | ||||
|                 Ok(moistures) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| @@ -160,9 +161,8 @@ impl SensorImpl { | ||||
|                     } | ||||
|                 } | ||||
|  | ||||
|                 let mut result = DetectionResult::default(); | ||||
|                 // Wait for messages to arrive | ||||
|                 let _ = Self::wait_for_can_measurements(&mut as_async, &mut result) | ||||
|                 let mut moistures = Moistures::default(); | ||||
|                 let _ = Self::wait_for_can_measurements(&mut as_async, &mut moistures) | ||||
|                     .with_timeout(Duration::from_millis(5000)) | ||||
|                     .await; | ||||
|  | ||||
| @@ -170,6 +170,8 @@ impl SensorImpl { | ||||
|                 can_power.set_low(); | ||||
|                 twai_config.replace(config); | ||||
|  | ||||
|                 let result= moistures.into(); | ||||
|  | ||||
|                 info!("Autodetection result: {result:?}"); | ||||
|                 Ok(result) | ||||
|             } | ||||
| @@ -178,8 +180,9 @@ impl SensorImpl { | ||||
|  | ||||
|     async fn wait_for_can_measurements( | ||||
|         as_async: &mut Twai<'_, Async>, | ||||
|         result: &mut DetectionResult, | ||||
|     ) { | ||||
|         moistures: &mut Moistures, | ||||
|     ) -> FatResult<()> { | ||||
|  | ||||
|         loop { | ||||
|             match as_async.receive_async().await { | ||||
|                 Ok(can_frame) => match can_frame.id() { | ||||
| @@ -196,12 +199,14 @@ impl SensorImpl { | ||||
|                                 if msg.0 == MessageKind::MoistureData { | ||||
|                                     let plant = msg.1 as usize; | ||||
|                                     let sensor = msg.2; | ||||
|                                     let data = can_frame.data(); | ||||
|                                      | ||||
|                                     match sensor { | ||||
|                                         SensorSlot::A => { | ||||
|                                             result.plant[plant].sensor_a = true; | ||||
|                                             moistures.sensor_a_hz[plant] = data[0] as f32; | ||||
|                                         } | ||||
|                                         SensorSlot::B => { | ||||
|                                             result.plant[plant].sensor_b = true; | ||||
|                                             moistures.sensor_b_hz[plant] = data[0] as f32; | ||||
|                                         } | ||||
|                                     } | ||||
|                                 } | ||||
| @@ -214,7 +219,6 @@ impl SensorImpl { | ||||
|                 }, | ||||
|                 Err(err) => { | ||||
|                     error!("Error receiving CAN message: {err:?}"); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -302,31 +306,17 @@ impl SensorImpl { | ||||
|         let median = results[mid]; | ||||
|         Ok(median) | ||||
|     } | ||||
|  | ||||
|     async fn inner_can(twai: &mut Twai<'static, Blocking>) -> FatResult<Moistures> { | ||||
|         config::standard(); | ||||
|  | ||||
|         let timeout = Instant::now() | ||||
|             .checked_add(embassy_time::Duration::from_millis(100)) | ||||
|             .context("Timeout")?; | ||||
|         loop { | ||||
|             let answer = twai.receive(); | ||||
|             match answer { | ||||
|                 Ok(answer) => { | ||||
|                     info!("Received CAN message: {answer:?}"); | ||||
|                 } | ||||
|                 Err(error) => match error { | ||||
|                     nb::Error::Other(error) => { | ||||
|                         return Err(FatError::CanBusError { error }); | ||||
|                     } | ||||
|                     nb::Error::WouldBlock => { | ||||
|                         if Instant::now() > timeout { | ||||
|                             bail!("Timeout waiting for CAN answer"); | ||||
|                         } | ||||
|                         Timer::after_millis(10).await; | ||||
|                     } | ||||
|                 }, | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<Moistures> for DetectionResult { | ||||
|     fn from(value: Moistures) -> Self { | ||||
|         let mut result = DetectionResult::default(); | ||||
|         for (plant, sensor) in value.sensor_a_hz.iter().enumerate() { | ||||
|             result.plant[plant].sensor_a = *sensor > 1.0_f32; | ||||
|         } | ||||
|         for (plant, sensor) in value.sensor_b_hz.iter().enumerate() { | ||||
|             result.plant[plant].sensor_b = *sensor > 1.0_f32; | ||||
|         } | ||||
|         result | ||||
|     } | ||||
| } | ||||
| @@ -7,7 +7,7 @@ use deranged::RangedU8; | ||||
| use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; | ||||
| use embassy_sync::mutex::Mutex; | ||||
| use esp_hal::Persistable; | ||||
| use log::info; | ||||
| use log::{info, warn}; | ||||
| use serde::Serialize; | ||||
| use strum_macros::IntoStaticStr; | ||||
| use unit_enum::UnitEnum; | ||||
| @@ -159,24 +159,41 @@ impl LogArray { | ||||
|     } | ||||
| } | ||||
|  | ||||
|  | ||||
| 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 | ||||
|             Err(_) => { | ||||
|                 //clear space for two asci chars | ||||
|                 info!("pushing char {char} to limit {LIMIT} current value {target} input {input}"); | ||||
|                 while target.len() + 2 >= LIMIT { | ||||
|                     target.pop().unwrap(); | ||||
|                     target.pop(); | ||||
|                 } | ||||
|                 //add .. to shortened strings | ||||
|                 target.push('.').unwrap(); | ||||
|                 target.push('.').unwrap(); | ||||
|                 match target.push('.'){ | ||||
|                     Ok(_) => {} | ||||
|                     Err(_) => { | ||||
|                         warn!("Error pushin . to limit {LIMIT} current value {target} input {input}") | ||||
|                     } | ||||
|                 } | ||||
|                 match target.push('.'){ | ||||
|                     Ok(_) => {} | ||||
|                     Err(_) => { | ||||
|                         warn!("Error pushin . to limit {LIMIT} current value {target} input {input}") | ||||
|                     } | ||||
|                 } | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     while target.len() < LIMIT { | ||||
|         target.push(' ').unwrap(); | ||||
|         match target.push(' ') { | ||||
|             Ok(_) => {} | ||||
|             Err(_) => { | ||||
|                 warn!("Error pushing space to limit {LIMIT} current value {target} input {input}") | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -832,7 +832,7 @@ macro_rules! mk_static { | ||||
|     ($t:ty,$val:expr) => {{ | ||||
|         static STATIC_CELL: static_cell::StaticCell<$t> = static_cell::StaticCell::new(); | ||||
|         #[deny(unused_attributes)] | ||||
|         let x = STATIC_CELL.uninit().write(($val)); | ||||
|         let x = STATIC_CELL.uninit().write($val); | ||||
|         x | ||||
|     }}; | ||||
| } | ||||
|   | ||||
| @@ -120,8 +120,8 @@ impl PlantState { | ||||
|             let raw = moistures.sensor_a_hz[plant_id]; | ||||
|             match map_range_moisture( | ||||
|                 raw, | ||||
|                 board.board_hal.get_config().plants[plant_id].moisture_sensor_min_frequency, | ||||
|                 board.board_hal.get_config().plants[plant_id].moisture_sensor_max_frequency, | ||||
|                 board.board_hal.get_config().plants[plant_id].moisture_sensor_min_frequency.map(|a| a as f32), | ||||
|                 board.board_hal.get_config().plants[plant_id].moisture_sensor_max_frequency.map(|b| b as f32), | ||||
|             ) { | ||||
|                 Ok(moisture_percent) => MoistureSensorState::MoistureValue { | ||||
|                     raw_hz: raw, | ||||
| @@ -137,8 +137,8 @@ impl PlantState { | ||||
|             let raw = moistures.sensor_b_hz[plant_id]; | ||||
|             match map_range_moisture( | ||||
|                 raw, | ||||
|                 board.board_hal.get_config().plants[plant_id].moisture_sensor_min_frequency, | ||||
|                 board.board_hal.get_config().plants[plant_id].moisture_sensor_max_frequency, | ||||
|                 board.board_hal.get_config().plants[plant_id].moisture_sensor_min_frequency.map(|a| a as f32), | ||||
|                 board.board_hal.get_config().plants[plant_id].moisture_sensor_max_frequency.map(|b| b as f32) | ||||
|             ) { | ||||
|                 Ok(moisture_percent) => MoistureSensorState::MoistureValue { | ||||
|                     raw_hz: raw, | ||||
| @@ -186,7 +186,7 @@ impl PlantState { | ||||
|     pub fn plant_moisture( | ||||
|         &self, | ||||
|     ) -> ( | ||||
|         Option<f32>, | ||||
|         Option<u8>, | ||||
|         (Option<&MoistureSensorError>, Option<&MoistureSensorError>), | ||||
|     ) { | ||||
|         match ( | ||||
| @@ -194,10 +194,10 @@ impl PlantState { | ||||
|             self.sensor_b.moisture_percent(), | ||||
|         ) { | ||||
|             (Some(moisture_a), Some(moisture_b)) => { | ||||
|                 (Some((moisture_a + moisture_b) / 2.), (None, None)) | ||||
|                 (Some(((moisture_a + moisture_b) / 2.) as u8), (None, None)) | ||||
|             } | ||||
|             (Some(moisture_percent), _) => (Some(moisture_percent), (None, self.sensor_b.is_err())), | ||||
|             (_, Some(moisture_percent)) => (Some(moisture_percent), (self.sensor_a.is_err(), None)), | ||||
|             (Some(moisture_percent), _) => (Some(moisture_percent as u8), (None, self.sensor_b.is_err())), | ||||
|             (_, Some(moisture_percent)) => (Some(moisture_percent as u8), (self.sensor_a.is_err(), None)), | ||||
|             _ => (None, (self.sensor_a.is_err(), self.sensor_b.is_err())), | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -707,6 +707,7 @@ | ||||
|       "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", | ||||
|       "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", | ||||
|       "license": "MIT", | ||||
|       "peer": true, | ||||
|       "bin": { | ||||
|         "acorn": "bin/acorn" | ||||
|       }, | ||||
| @@ -731,6 +732,7 @@ | ||||
|       "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", | ||||
|       "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", | ||||
|       "license": "MIT", | ||||
|       "peer": true, | ||||
|       "dependencies": { | ||||
|         "fast-deep-equal": "^3.1.3", | ||||
|         "fast-uri": "^3.0.1", | ||||
| @@ -957,6 +959,7 @@ | ||||
|         } | ||||
|       ], | ||||
|       "license": "MIT", | ||||
|       "peer": true, | ||||
|       "dependencies": { | ||||
|         "baseline-browser-mapping": "^2.8.3", | ||||
|         "caniuse-lite": "^1.0.30001741", | ||||
| @@ -2274,6 +2277,7 @@ | ||||
|       "integrity": "sha512-V/PZeWsqhfpE27nKeX9EO2sbR+D17A+tLf6qU+ht66jdUsN0QLKJN27Z+1+gHrVMKgndBahes0PU6rRihDgHTw==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "peer": true, | ||||
|       "dependencies": { | ||||
|         "@types/html-minifier-terser": "^6.0.0", | ||||
|         "html-minifier-terser": "^6.0.2", | ||||
| @@ -3380,6 +3384,7 @@ | ||||
|       "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "peer": true, | ||||
|       "dependencies": { | ||||
|         "fast-deep-equal": "^3.1.1", | ||||
|         "fast-json-stable-stringify": "^2.0.0", | ||||
| @@ -4338,7 +4343,8 @@ | ||||
|       "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", | ||||
|       "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", | ||||
|       "dev": true, | ||||
|       "license": "0BSD" | ||||
|       "license": "0BSD", | ||||
|       "peer": true | ||||
|     }, | ||||
|     "node_modules/type-is": { | ||||
|       "version": "1.6.18", | ||||
| @@ -4360,6 +4366,7 @@ | ||||
|       "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", | ||||
|       "dev": true, | ||||
|       "license": "Apache-2.0", | ||||
|       "peer": true, | ||||
|       "bin": { | ||||
|         "tsc": "bin/tsc", | ||||
|         "tsserver": "bin/tsserver" | ||||
| @@ -4508,6 +4515,7 @@ | ||||
|       "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.101.3.tgz", | ||||
|       "integrity": "sha512-7b0dTKR3Ed//AD/6kkx/o7duS8H3f1a4w3BYpIriX4BzIhjkn4teo05cptsxvLesHFKK5KObnadmCHBwGc+51A==", | ||||
|       "license": "MIT", | ||||
|       "peer": true, | ||||
|       "dependencies": { | ||||
|         "@types/eslint-scope": "^3.7.7", | ||||
|         "@types/estree": "^1.0.8", | ||||
| @@ -4557,6 +4565,7 @@ | ||||
|       "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", | ||||
|       "dev": true, | ||||
|       "license": "MIT", | ||||
|       "peer": true, | ||||
|       "dependencies": { | ||||
|         "@discoveryjs/json-ext": "^0.5.0", | ||||
|         "@webpack-cli/configtest": "^2.1.1", | ||||
|   | ||||
| @@ -112,6 +112,7 @@ export interface PlantConfig { | ||||
|     pump_cooldown_min: number, | ||||
|     pump_hour_start: number, | ||||
|     pump_hour_end: number, | ||||
|     pump_limit_ml: number, | ||||
|     sensor_a: boolean, | ||||
|     sensor_b: boolean, | ||||
|     max_consecutive_pump_count: number, | ||||
|   | ||||
| @@ -296,6 +296,7 @@ export class Controller { | ||||
|     configChanged() { | ||||
|         const current = controller.getConfig(); | ||||
|         var pretty = JSON.stringify(current, undefined, 0); | ||||
|  | ||||
|         controller.submitView.setJson(pretty); | ||||
|  | ||||
|  | ||||
|   | ||||
| @@ -53,7 +53,7 @@ | ||||
|             Mode: | ||||
|         </div> | ||||
|         <select class="plantvalue" id="plant_${plantId}_mode"> | ||||
|             <option value="OFF">Off</option> | ||||
|             <option value="Off">Off</option> | ||||
|             <option value="TargetMoisture">Target</option> | ||||
|             <option value="MinMoisture">Min Moisture</option> | ||||
|             <option value="TimerOnly">Timer</option> | ||||
|   | ||||
| @@ -213,7 +213,7 @@ export class PlantView { | ||||
|  | ||||
|         console.log("updateVisibility plantConfig: " + plantConfig.mode) | ||||
|         let showSensor = plantConfig.sensor_a || plantConfig.sensor_b | ||||
|         let showPump = plantConfig.mode !== "OFF" | ||||
|         let showPump = plantConfig.mode !== "Off" | ||||
|         let showTarget = plantConfig.mode === "TargetMoisture" | ||||
|         let showMin = plantConfig.mode === "MinMoisture" | ||||
|  | ||||
| @@ -299,6 +299,7 @@ export class PlantView { | ||||
|             target_moisture: this.targetMoisture.valueAsNumber, | ||||
|             min_moisture: this.minMoisture.valueAsNumber, | ||||
|             pump_time_s: this.pumpTimeS.valueAsNumber, | ||||
|             pump_limit_ml: 5000, | ||||
|             pump_cooldown_min: this.pumpCooldown.valueAsNumber, | ||||
|             pump_hour_start: +this.pumpHourStart.value, | ||||
|             pump_hour_end: +this.pumpHourEnd.value, | ||||
|   | ||||
| @@ -10,7 +10,7 @@ console.log("Dev server is " + isDevServer); | ||||
| var host; | ||||
| if (isDevServer) { | ||||
|     //ensure no trailing / | ||||
|     host = 'http://10.23.44.186'; | ||||
|     host = 'http://192.168.1.105'; | ||||
| } else { | ||||
|     host = ''; | ||||
| } | ||||
|   | ||||
							
								
								
									
										8
									
								
								Software/Shared/canapi/.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Software/Shared/canapi/.idea/.gitignore
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| # Default ignored files | ||||
| /shelf/ | ||||
| /workspace.xml | ||||
| # Editor-based HTTP Client requests | ||||
| /httpRequests/ | ||||
| # Datasource local storage ignored files | ||||
| /dataSources/ | ||||
| /dataSources.local.xml | ||||
							
								
								
									
										11
									
								
								Software/Shared/canapi/.idea/canapi.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								Software/Shared/canapi/.idea/canapi.iml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <module type="EMPTY_MODULE" version="4"> | ||||
|   <component name="NewModuleRootManager"> | ||||
|     <content url="file://$MODULE_DIR$"> | ||||
|       <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> | ||||
|       <excludeFolder url="file://$MODULE_DIR$/target" /> | ||||
|     </content> | ||||
|     <orderEntry type="inheritedJdk" /> | ||||
|     <orderEntry type="sourceFolder" forTests="false" /> | ||||
|   </component> | ||||
| </module> | ||||
							
								
								
									
										8
									
								
								Software/Shared/canapi/.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Software/Shared/canapi/.idea/modules.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="ProjectModuleManager"> | ||||
|     <modules> | ||||
|       <module fileurl="file://$PROJECT_DIR$/.idea/canapi.iml" filepath="$PROJECT_DIR$/.idea/canapi.iml" /> | ||||
|     </modules> | ||||
|   </component> | ||||
| </project> | ||||
							
								
								
									
										7
									
								
								Software/Shared/canapi/.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								Software/Shared/canapi/.idea/vcs.xml
									
									
									
										generated
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,7 @@ | ||||
| <?xml version="1.0" encoding="UTF-8"?> | ||||
| <project version="4"> | ||||
|   <component name="VcsDirectoryMappings"> | ||||
|     <mapping directory="$PROJECT_DIR$/../../.." vcs="Git" /> | ||||
|     <mapping directory="$PROJECT_DIR$/../../../website/themes/blowfish" vcs="Git" /> | ||||
|   </component> | ||||
| </project> | ||||
		Reference in New Issue
	
	Block a user