From b268466b89d8689980e2d8bce8e36e860c0c184a Mon Sep 17 00:00:00 2001 From: Empire Date: Tue, 21 Nov 2023 23:45:15 +0100 Subject: [PATCH] adjustments --- rust/Cargo.toml | 1 + rust/src/main.rs | 304 +++++++---------------------------------------- 2 files changed, 41 insertions(+), 264 deletions(-) diff --git a/rust/Cargo.toml b/rust/Cargo.toml index 63143b8..3d97cdd 100644 --- a/rust/Cargo.toml +++ b/rust/Cargo.toml @@ -51,6 +51,7 @@ embedded-hal = "0.2.7" dummy-pin = "0.1.1" shift-register-driver = "0.1.1" one-wire-bus = "0.1.1" +anyhow = "1.0.75" #?bq34z100 required diff --git a/rust/src/main.rs b/rust/src/main.rs index 3f817d9..3c96e2a 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -1,259 +1,8 @@ +use chrono::{Datelike, Timelike}; -use std::sync::{Mutex, Arc}; - -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; - 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; - fn store_last_pump_time(&mut self,plant:usize, time: chrono::DateTime); - 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>, OldOutputPin>>, - consecutive_watering_plant: Mutex<[u32; PLANT_COUNT]>, - last_watering_timestamp: Mutex<[i64; PLANT_COUNT]>, - low_voltage_detected: Mutex, - 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, - one_wire_bus: OneWire> -} - -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> = 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::new::(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 { - 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 { - let ts = unsafe { LAST_WATERING_TIMESTAMP }[plant]; - let timestamp = NaiveDateTime::from_timestamp_millis(ts).unwrap(); - return DateTime::::from_naive_utc_and_offset(timestamp, Utc); - } - - fn store_last_pump_time(&mut self,plant:usize, time: chrono::DateTime) { - 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!() - } - - -} - - - - +use crate::plant_hal::{PlantCtrlBoardInteraction, PlantHal, CreatePlantHal, PLANT_COUNT}; +pub mod plant_hal; fn main() { @@ -266,19 +15,28 @@ fn main() { log::info!("Hello, world!"); - - - let board = PlantCtrlBoard::default(); - - - //check if we know the time current > 2020 - //if failed assume its 1.1.1970 - //12:00 if solar reports day - //00:00 if solar repors night + let mut board = PlantHal::create(); + + + let mut cur = board.time(); + //check if we know the time current > 2020 + if cur.year() < 2020 { + if board.is_day() { + //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? //check if boot button is pressed, if longer than 5s delete config and reboot into config mode + //check if we have a config file // 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 @@ -289,16 +47,34 @@ fn main() { //is tank sensor enabled in config? //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 + board.general_fault(true); //set general fault persistent //set tank sensor state to fault - + //try connect wifi and do mqtt roundtrip // if no wifi, 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 + 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 //read battery level