adjustments
This commit is contained in:
		| @@ -51,6 +51,7 @@ embedded-hal = "0.2.7" | |||||||
| dummy-pin = "0.1.1" | dummy-pin = "0.1.1" | ||||||
| shift-register-driver = "0.1.1" | shift-register-driver = "0.1.1" | ||||||
| one-wire-bus = "0.1.1" | one-wire-bus = "0.1.1" | ||||||
|  | anyhow = "1.0.75" | ||||||
| #?bq34z100 required | #?bq34z100 required | ||||||
|  |  | ||||||
|  |  | ||||||
|   | |||||||
							
								
								
									
										294
									
								
								rust/src/main.rs
									
									
									
									
									
								
							
							
						
						
									
										294
									
								
								rust/src/main.rs
									
									
									
									
									
								
							| @@ -1,259 +1,8 @@ | |||||||
|  | use chrono::{Datelike, Timelike}; | ||||||
|  |  | ||||||
| use std::sync::{Mutex, Arc}; | use crate::plant_hal::{PlantCtrlBoardInteraction, PlantHal, CreatePlantHal, PLANT_COUNT}; | ||||||
|  |  | ||||||
| use chrono::{Utc, NaiveDateTime, DateTime}; |  | ||||||
|  |  | ||||||
| use ds18b20::Ds18b20; |  | ||||||
| use embedded_hal::digital::v1_compat::OldOutputPin; |  | ||||||
| use embedded_hal::digital::v2::OutputPin; |  | ||||||
| use esp_idf_hal::adc::config::Config; |  | ||||||
| use esp_idf_hal::adc::{AdcDriver, AdcChannelDriver, attenuation}; |  | ||||||
| use esp_idf_hal::delay::Delay; |  | ||||||
| use esp_idf_hal::reset::ResetReason; |  | ||||||
| use esp_idf_sys::EspError; |  | ||||||
| use one_wire_bus::OneWire; |  | ||||||
| use shift_register_driver::sipo::ShiftRegister24; |  | ||||||
| use esp_idf_hal::gpio::{PinDriver, Gpio39, Gpio4}; |  | ||||||
| use esp_idf_hal::prelude::Peripherals; |  | ||||||
|  |  | ||||||
| const PLANT_COUNT:usize = 8; |  | ||||||
| const PINS_PER_PLANT:usize = 5; |  | ||||||
| const PLANT_PUMP_OFFSET:usize = 0; |  | ||||||
| const PLANT_FAULT_OFFSET:usize = 1; |  | ||||||
| const PLANT_MOIST_PUMP_OFFSET:usize = 2; |  | ||||||
| const PLANT_MOIST_B_OFFSET:usize = 3; |  | ||||||
| const PLANT_MOIST_A_OFFSET:usize = 4; |  | ||||||
|  |  | ||||||
|  |  | ||||||
| #[link_section = ".rtc.data"] |  | ||||||
| static mut LAST_WATERING_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT]; |  | ||||||
| #[link_section = ".rtc.data"] |  | ||||||
| static mut CONSECUTIVE_WATERING_PLANT: [u32; PLANT_COUNT] = [0; PLANT_COUNT]; |  | ||||||
| #[link_section = ".rtc.data"] |  | ||||||
| static mut LOW_VOLTAGE_DETECTED:bool = false; |  | ||||||
|  |  | ||||||
| struct BatteryState { |  | ||||||
|     state_charge_percent: u8, |  | ||||||
|     max_error_percent: u8, |  | ||||||
|     remaining_milli_ampere_hour: u32, |  | ||||||
|     max_milli_ampere_hour: u32, |  | ||||||
|     design_milli_ampere_hour:u32, |  | ||||||
|     voltage_milli_volt: u16, |  | ||||||
|     average_current_milli_ampere: u16, |  | ||||||
|     temperature_tenth_kelvin: u32, |  | ||||||
|     average_time_to_empty_minute: u16, |  | ||||||
|     average_time_to_full_minute: u16, |  | ||||||
|     average_discharge_power_cycle_milli_watt: u16, |  | ||||||
|     cycle_count: u16, |  | ||||||
|     state_health_percent: u8 |  | ||||||
| } |  | ||||||
| trait PlantCtrlBoardInteraction{ |  | ||||||
|     fn battery_state(&mut self,) -> BatteryState; |  | ||||||
|      |  | ||||||
|     fn is_day(&self,) -> bool; |  | ||||||
|     fn water_temperature_c(&mut self,) -> Option<f32>; |  | ||||||
|     fn tank_sensor_mv(&mut self,) -> u16; |  | ||||||
|  |  | ||||||
|     fn set_low_voltage_in_cycle(&mut self,); |  | ||||||
|     fn clear_low_voltage_in_cycle(&mut self,); |  | ||||||
|     fn low_voltage_in_cycle(&mut self) -> bool; |  | ||||||
|     fn any_pump(&mut self, enabled:bool); |  | ||||||
|  |  | ||||||
|     //keep state during deepsleep |  | ||||||
|     fn light(&self,enable:bool); |  | ||||||
|  |  | ||||||
|     fn plant_count(&self,) -> usize; |  | ||||||
|     fn measure_moisture_b_hz(&self,plant:usize) -> i16; |  | ||||||
|     fn measure_moisture_a_hz(&self,plant:usize) -> i16; |  | ||||||
|     fn measure_moisture_p_hz(&self,plant:usize) -> i16; |  | ||||||
|     fn pump(&self,plant:usize, enable:bool); |  | ||||||
|     fn last_pump_time(&self,plant:usize) -> chrono::DateTime<Utc>; |  | ||||||
|     fn store_last_pump_time(&mut self,plant:usize, time: chrono::DateTime<Utc>); |  | ||||||
|     fn store_consecutive_pump_count(&mut self,plant:usize, count:u32); |  | ||||||
|     fn consecutive_pump_count(&mut self,plant:usize) -> u32; |  | ||||||
|  |  | ||||||
|     //keep state during deepsleep |  | ||||||
|     fn fault(&self,plant:usize, enable:bool); |  | ||||||
|      |  | ||||||
|     fn default() -> Self; |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| struct PlantCtrlBoard<'a>{ |  | ||||||
|     shift_register: ShiftRegister24<OldOutputPin<PinDriver<'a, esp_idf_hal::gpio::Gpio21, esp_idf_hal::gpio::Output>>, OldOutputPin<PinDriver<'a, esp_idf_hal::gpio::Gpio22, esp_idf_hal::gpio::Output>>, OldOutputPin<PinDriver<'a, esp_idf_hal::gpio::Gpio19, esp_idf_hal::gpio::Output>>>, |  | ||||||
|     consecutive_watering_plant: Mutex<[u32; PLANT_COUNT]>, |  | ||||||
|     last_watering_timestamp: Mutex<[i64; PLANT_COUNT]>, |  | ||||||
|     low_voltage_detected: Mutex<bool>, |  | ||||||
|     tank_driver: AdcDriver<'a, esp_idf_hal::adc::ADC1>, |  | ||||||
|     tank_channel: esp_idf_hal::adc::AdcChannelDriver<'a, { attenuation::DB_11 }, Gpio39 >, |  | ||||||
|     solar_is_day: PinDriver<'a, esp_idf_hal::gpio::Gpio25, esp_idf_hal::gpio::Input>, |  | ||||||
|     water_temp_sensor: Option<Ds18b20>, |  | ||||||
|     one_wire_bus: OneWire<PinDriver<'a, Gpio4, esp_idf_hal::gpio::InputOutput>> |  | ||||||
| } |  | ||||||
|  |  | ||||||
| impl PlantCtrlBoardInteraction for PlantCtrlBoard<'_> { |  | ||||||
|     fn default() -> Self { |  | ||||||
|         let peripherals = Peripherals::take().unwrap();  |  | ||||||
|          |  | ||||||
|         let clock = OldOutputPin::from(PinDriver::output(peripherals.pins.gpio21).unwrap()); |  | ||||||
|         let latch = OldOutputPin::from(PinDriver::output(peripherals.pins.gpio22).unwrap()); |  | ||||||
|         let data = OldOutputPin::from(PinDriver::output(peripherals.pins.gpio19).unwrap()); |  | ||||||
|  |  | ||||||
|  |  | ||||||
|         let one_wire_pin = PinDriver::input_output_od(peripherals.pins.gpio4).unwrap(); |  | ||||||
|  |  | ||||||
|         let mut one_wire_bus: OneWire<PinDriver<'_, Gpio4, esp_idf_hal::gpio::InputOutput>> = OneWire::new(one_wire_pin).unwrap(); |  | ||||||
|         let mut delay = Delay::new_default(); |  | ||||||
|  |  | ||||||
|         if one_wire_bus.reset(&mut delay).is_err() { |  | ||||||
|             //TODO check a lot of one wire error conditions here |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         let device_address = one_wire_bus.devices(false, &mut delay).next().unwrap().unwrap(); |  | ||||||
|         let water_temp_sensor: Option<Ds18b20> = Ds18b20::new::<EspError>(device_address).ok(); |  | ||||||
|         //TODO make to none if not possible to init |  | ||||||
|  |  | ||||||
|         //init,reset rtc memory depending on cause |  | ||||||
|         let reasons = ResetReason::get(); |  | ||||||
|         let reset_store = match reasons { |  | ||||||
|             ResetReason::Software => false, |  | ||||||
|             ResetReason::ExternalPin => false, |  | ||||||
|             ResetReason::Watchdog => true, |  | ||||||
|             ResetReason::Sdio => true, |  | ||||||
|             ResetReason::Panic => true, |  | ||||||
|             ResetReason::InterruptWatchdog => true, |  | ||||||
|             ResetReason::PowerOn => true, |  | ||||||
|             ResetReason::Unknown => true, |  | ||||||
|             ResetReason::Brownout => true, |  | ||||||
|             ResetReason::TaskWatchdog => true, |  | ||||||
|             ResetReason::DeepSleep => false, |  | ||||||
|         }; |  | ||||||
|         if reset_store { |  | ||||||
|             println!("Clear and reinit RTC store"); |  | ||||||
|             unsafe { |  | ||||||
|                 LAST_WATERING_TIMESTAMP = [0; PLANT_COUNT]; |  | ||||||
|                 CONSECUTIVE_WATERING_PLANT = [0; PLANT_COUNT]; |  | ||||||
|                 LOW_VOLTAGE_DETECTED = false; |  | ||||||
|             }; |  | ||||||
|         } else { |  | ||||||
|             println!("Keeping RTC store"); |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         Self {  |  | ||||||
|             shift_register : ShiftRegister24::new(clock, latch, data), |  | ||||||
|             last_watering_timestamp : Mutex::new(unsafe { LAST_WATERING_TIMESTAMP }), |  | ||||||
|             consecutive_watering_plant : Mutex::new(unsafe { CONSECUTIVE_WATERING_PLANT }), |  | ||||||
|             low_voltage_detected : Mutex::new(unsafe { LOW_VOLTAGE_DETECTED }), |  | ||||||
|             tank_driver : AdcDriver::new(peripherals.adc1, &Config::new().calibration(true)).unwrap(), |  | ||||||
|             tank_channel: AdcChannelDriver::new(peripherals.pins.gpio39).unwrap(), |  | ||||||
|             solar_is_day : PinDriver::input(peripherals.pins.gpio25).unwrap(), |  | ||||||
|             water_temp_sensor : water_temp_sensor, |  | ||||||
|             one_wire_bus: one_wire_bus, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn battery_state(&mut self,) -> BatteryState { |  | ||||||
|         todo!() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn is_day(&self,) -> bool { |  | ||||||
|         return self.solar_is_day.get_level().into(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn water_temperature_c(&mut self,) -> Option<f32> { |  | ||||||
|         return match &self.water_temp_sensor{ |  | ||||||
|             Some(sensor) => { |  | ||||||
|                 let mut delay = Delay::new_default(); |  | ||||||
|                 sensor.start_temp_measurement(&mut self.one_wire_bus, &mut delay); |  | ||||||
|                 ds18b20::Resolution::Bits12.delay_for_measurement_time(&mut delay);     |  | ||||||
|                 let sensor_data  = sensor.read_data(&mut self.one_wire_bus, &mut delay).unwrap(); |  | ||||||
|                 println!("Water Temp is {}°C", sensor_data.temperature); |  | ||||||
|                 if sensor_data.temperature == 85_f32 { |  | ||||||
|                     return Option::None; |  | ||||||
|                 } else { |  | ||||||
|                     Some(sensor_data.temperature) |  | ||||||
|                 } |  | ||||||
|             }, |  | ||||||
|             None => Option::None, |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn tank_sensor_mv(&mut self,) -> u16 { |  | ||||||
|         return self.tank_driver.read(&mut self.tank_channel).unwrap(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn set_low_voltage_in_cycle(&mut self,) { |  | ||||||
|         *self.low_voltage_detected.get_mut().unwrap() = true; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn clear_low_voltage_in_cycle(&mut self,) { |  | ||||||
|         *self.low_voltage_detected.get_mut().unwrap() = false; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn light(&self,enable:bool) { |  | ||||||
|         todo!() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn plant_count(&self,) -> usize { |  | ||||||
|         PLANT_COUNT |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn measure_moisture_b_hz(&self,plant:usize) -> i16 { |  | ||||||
|         todo!() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn measure_moisture_a_hz(&self,plant:usize) -> i16 { |  | ||||||
|         todo!() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn measure_moisture_p_hz(&self,plant:usize) -> i16 { |  | ||||||
|         todo!() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn pump(&self,plant:usize, enable:bool) { |  | ||||||
|         let index = plant*PINS_PER_PLANT*PLANT_PUMP_OFFSET; |  | ||||||
|         self.shift_register.decompose()[index].set_state(enable.into()).unwrap() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn last_pump_time(&self,plant:usize) -> chrono::DateTime<Utc> { |  | ||||||
|         let ts = unsafe { LAST_WATERING_TIMESTAMP }[plant]; |  | ||||||
|         let timestamp = NaiveDateTime::from_timestamp_millis(ts).unwrap(); |  | ||||||
|         return DateTime::<Utc>::from_naive_utc_and_offset(timestamp, Utc); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn store_last_pump_time(&mut self,plant:usize, time: chrono::DateTime<Utc>) { |  | ||||||
|         self.last_watering_timestamp.get_mut().unwrap()[plant] = time.timestamp_millis(); |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn store_consecutive_pump_count(&mut self,plant:usize, count:u32) { |  | ||||||
|         self.consecutive_watering_plant.get_mut().unwrap()[plant] = count; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn consecutive_pump_count(&mut self,plant:usize) -> u32 { |  | ||||||
|         return self.consecutive_watering_plant.get_mut().unwrap()[plant] |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn fault(&self,plant:usize, enable:bool) { |  | ||||||
|         let index = plant*PINS_PER_PLANT*PLANT_FAULT_OFFSET; |  | ||||||
|         self.shift_register.decompose()[index].set_state(enable.into()).unwrap() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn low_voltage_in_cycle(&mut self) -> bool { |  | ||||||
|         return *self.low_voltage_detected.get_mut().unwrap() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fn any_pump(&mut self, enabled:bool) { |  | ||||||
|  |  | ||||||
|         todo!() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|  |  | ||||||
| } |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|  | pub mod plant_hal; | ||||||
|  |  | ||||||
| fn main() { | fn main() { | ||||||
|  |  | ||||||
| @@ -267,18 +16,27 @@ fn main() { | |||||||
|     log::info!("Hello, world!"); |     log::info!("Hello, world!"); | ||||||
|  |  | ||||||
|      |      | ||||||
|  |     let mut board = PlantHal::create(); | ||||||
|     let board = PlantCtrlBoard::default(); |  | ||||||
|           |           | ||||||
|      |      | ||||||
|  |     let mut cur = board.time(); | ||||||
|     //check if we know the time current > 2020 |     //check if we know the time current > 2020 | ||||||
|         //if failed assume its 1.1.1970  |     if cur.year() < 2020 { | ||||||
|             //12:00 if solar reports day |         if board.is_day() { | ||||||
|             //00:00 if solar repors night |             //assume 13:00 if solar reports day | ||||||
|  |             cur = *cur.with_hour(13).get_or_insert(cur); | ||||||
|  |         } else { | ||||||
|  |             //assume 01:00 if solar reports night | ||||||
|  |             cur = *cur.with_hour(1).get_or_insert(cur); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|  |  | ||||||
|     //continous/interrupt? |     //continous/interrupt? | ||||||
|         //check if boot button is pressed, if longer than 5s delete config and reboot into config mode |         //check if boot button is pressed, if longer than 5s delete config and reboot into config mode | ||||||
|  |  | ||||||
|  |      | ||||||
|     //check if we have a config file |     //check if we have a config file | ||||||
|         // if not found or parsing error -> error very fast blink general fault |         // if not found or parsing error -> error very fast blink general fault | ||||||
|             //if this happens after a firmeware upgrade (check image state), mark as invalid |             //if this happens after a firmeware upgrade (check image state), mark as invalid | ||||||
| @@ -289,7 +47,10 @@ fn main() { | |||||||
|  |  | ||||||
|     //is tank sensor enabled in config? |     //is tank sensor enabled in config? | ||||||
|         //measure tank level (without wifi due to interference) |         //measure tank level (without wifi due to interference) | ||||||
|  |         //TODO this should be a result// detect invalid measurement value | ||||||
|  |         let tank_value = board.tank_sensor_mv(); | ||||||
|             //if not possible value, blink general fault error_tank_sensor_fault |             //if not possible value, blink general fault error_tank_sensor_fault | ||||||
|  |                 board.general_fault(true); | ||||||
|                 //set general fault persistent |                 //set general fault persistent | ||||||
|                 //set tank sensor state to fault |                 //set tank sensor state to fault | ||||||
|  |  | ||||||
| @@ -298,7 +59,22 @@ fn main() { | |||||||
|         // if no wifi, set general fault persistent |         // if no wifi, set general fault persistent | ||||||
|             //if no mqtt, set general fault persistent |             //if no mqtt, set general fault persistent | ||||||
|  |  | ||||||
|  |      | ||||||
|  |     match board.sntp(1000*120) { | ||||||
|  |         Ok(new_time) => cur = new_time, | ||||||
|  |         Err(_) => todo!(), | ||||||
|  |     } | ||||||
|  |      | ||||||
|     //measure each plant moisture |     //measure each plant moisture | ||||||
|  |     let mut initial_measurements_a: [i32;PLANT_COUNT] = [0;PLANT_COUNT]; | ||||||
|  |     let mut initial_measurements_b: [i32;PLANT_COUNT] = [0;PLANT_COUNT]; | ||||||
|  |     let mut initial_measurements_p: [i32;PLANT_COUNT] = [0;PLANT_COUNT]; | ||||||
|  |     for plant in 0..PLANT_COUNT { | ||||||
|  |         initial_measurements_a[plant] = board.measure_moisture_hz(plant, plant_hal::Sensor::A); | ||||||
|  |         initial_measurements_b[plant] = board.measure_moisture_hz(plant, plant_hal::Sensor::B); | ||||||
|  |         initial_measurements_p[plant] = board.measure_moisture_hz(plant, plant_hal::Sensor::PUMP); | ||||||
|  |     } | ||||||
|  |  | ||||||
|  |  | ||||||
|     //if config battery mode |     //if config battery mode | ||||||
|         //read battery level |         //read battery level | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user