proceeed further

This commit is contained in:
Empire 2023-11-21 01:05:12 +01:00
parent 825d389a8c
commit 3c409ea339
3 changed files with 260 additions and 88 deletions

View File

@ -50,6 +50,7 @@ paste = "1.0.14"
embedded-hal = "0.2.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"
#?bq34z100 required #?bq34z100 required

View File

@ -1,17 +1,34 @@
use chrono::Utc;
use std::sync::{Mutex, Arc};
use chrono::{Utc, NaiveDateTime, DateTime};
use ds18b20::Ds18b20;
use embedded_hal::digital::v1_compat::OldOutputPin; 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::config::Config;
use esp_idf_hal::adc::{AdcDriver, AdcChannelDriver, attenuation}; use esp_idf_hal::adc::{AdcDriver, AdcChannelDriver, attenuation};
use esp_idf_hal::delay::Delay;
use esp_idf_hal::reset::ResetReason; use esp_idf_hal::reset::ResetReason;
use shift_register_driver::sipo::{ShiftRegister24, ShiftRegisterPin}; use esp_idf_sys::EspError;
use esp_idf_hal::gpio::PinDriver; 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; use esp_idf_hal::prelude::Peripherals;
const PLANT_COUNT:usize = 8; 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"] #[link_section = ".rtc.data"]
static mut LAST_WATERING_TIMESTAMP: [u64; PLANT_COUNT] = [0; PLANT_COUNT]; static mut LAST_WATERING_TIMESTAMP: [i64; PLANT_COUNT] = [0; PLANT_COUNT];
#[link_section = ".rtc.data"] #[link_section = ".rtc.data"]
static mut CONSECUTIVE_WATERING_PLANT: [u64; PLANT_COUNT] = [0; PLANT_COUNT]; static mut CONSECUTIVE_WATERING_PLANT: [u32; PLANT_COUNT] = [0; PLANT_COUNT];
#[link_section = ".rtc.data"] #[link_section = ".rtc.data"]
static mut LOW_VOLTAGE_DETECTED:bool = false; static mut LOW_VOLTAGE_DETECTED:bool = false;
@ -31,147 +48,203 @@ struct BatteryState {
state_health_percent: u8 state_health_percent: u8
} }
trait PlantCtrlBoardInteraction{ trait PlantCtrlBoardInteraction{
fn battery_state(&self,) -> BatteryState; fn battery_state(&mut self,) -> BatteryState;
fn is_day(&self,) -> bool; fn is_day(&self,) -> bool;
fn water_temperature_c(&self,) -> u16; fn water_temperature_c(&mut self,) -> Option<f32>;
fn tank_sensor_mv(&self,) -> u16; fn tank_sensor_mv(&mut self,) -> u16;
fn set_low_voltage_in_cycle(&self,); fn set_low_voltage_in_cycle(&mut self,);
fn clear_low_voltage_in_cycle(&self,); fn clear_low_voltage_in_cycle(&mut self,);
fn low_voltage_in_cycle(&self) -> bool; fn low_voltage_in_cycle(&mut self) -> bool;
fn any_pump(&self, enabled:bool); fn any_pump(&mut self, enabled:bool);
//keep state during deepsleep //keep state during deepsleep
fn light(&self,enable:bool); fn light(&self,enable:bool);
fn plant_count(&self,) -> i8; fn plant_count(&self,) -> usize;
fn measure_moisture_b_hz(&self,plant:i8) -> i16; fn measure_moisture_b_hz(&self,plant:usize) -> i16;
fn measure_moisture_a_hz(&self,plant:i8) -> i16; fn measure_moisture_a_hz(&self,plant:usize) -> i16;
fn measure_moisture_p_hz(&self,plant:i8) -> i16; fn measure_moisture_p_hz(&self,plant:usize) -> i16;
fn pump(&self,plant:i8, enable:bool); fn pump(&self,plant:usize, enable:bool);
fn last_pump_time(&self,plant:i8) -> chrono::DateTime<Utc>; fn last_pump_time(&self,plant:usize) -> chrono::DateTime<Utc>;
fn store_last_pump_time(&self,plant:i8, time: chrono::DateTime<Utc>); fn store_last_pump_time(&mut self,plant:usize, time: chrono::DateTime<Utc>);
fn store_consecutive_pump_count(&self,plant:i8, count:i16); fn store_consecutive_pump_count(&mut self,plant:usize, count:u32);
fn consecutive_pump_count(&self,plant:i8) -> i16; fn consecutive_pump_count(&mut self,plant:usize) -> u32;
//keep state during deepsleep //keep state during deepsleep
fn fault(&self,plant:i8, enable:bool); fn fault(&self,plant:usize, enable:bool);
fn default() -> Self; fn default() -> Self;
} }
trait Plant{
fn setPump(pump:bool);
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>>
} }
struct PlantHal<'d>{ impl PlantCtrlBoardInteraction for PlantCtrlBoard<'_> {
pump:ShiftRegisterPin<'d>
}
struct PlantCtrlBoard{
dummy:i32
}
impl PlantCtrlBoardInteraction for PlantCtrlBoard {
fn default() -> Self { fn default() -> Self {
let peripherals = Peripherals::take().unwrap(); let peripherals = Peripherals::take().unwrap();
let mut adc = AdcDriver::new(peripherals.adc1, &Config::new().calibration(true)).unwrap();
let mut adc_pin: esp_idf_hal::adc::AdcChannelDriver<{ attenuation::DB_11 }, _> = AdcChannelDriver::new(peripherals.pins.gpio39).unwrap();
let analog_value = adc.read(&mut adc_pin);
let clock = OldOutputPin::from(PinDriver::output(peripherals.pins.gpio21).unwrap()); let clock = OldOutputPin::from(PinDriver::output(peripherals.pins.gpio21).unwrap());
let latch = OldOutputPin::from(PinDriver::output(peripherals.pins.gpio22).unwrap()); let latch = OldOutputPin::from(PinDriver::output(peripherals.pins.gpio22).unwrap());
let data = OldOutputPin::from(PinDriver::output(peripherals.pins.gpio19).unwrap()); let data = OldOutputPin::from(PinDriver::output(peripherals.pins.gpio19).unwrap());
let shift_register = ShiftRegister24::new(clock, latch, data); let one_wire_pin = PinDriver::input_output_od(peripherals.pins.gpio4).unwrap();
let registerOutput = shift_register.decompose();
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() {
Self { dummy: 12 } //TODO check a lot of one wire error conditions here
} }
fn battery_state(&self,) -> BatteryState { 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!() todo!()
} }
fn is_day(&self,) -> bool { fn is_day(&self,) -> bool {
todo!() return self.solar_is_day.get_level().into();
} }
fn water_temperature_c(&self,) -> u16 { fn water_temperature_c(&mut self,) -> Option<f32> {
todo!() 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)
} }
},
fn tank_sensor_mv(&self,) -> u16 { None => Option::None,
todo!()
}
fn set_low_voltage_in_cycle(&self,) {
unsafe {
LOW_VOLTAGE_DETECTED = true;
} }
} }
fn clear_low_voltage_in_cycle(&self,) { fn tank_sensor_mv(&mut self,) -> u16 {
unsafe { return self.tank_driver.read(&mut self.tank_channel).unwrap();
LOW_VOLTAGE_DETECTED = false;
} }
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) { fn light(&self,enable:bool) {
todo!() todo!()
} }
fn plant_count(&self,) -> i8 { fn plant_count(&self,) -> usize {
PLANT_COUNT
}
fn measure_moisture_b_hz(&self,plant:usize) -> i16 {
todo!() todo!()
} }
fn measure_moisture_b_hz(&self,plant:i8) -> i16 { fn measure_moisture_a_hz(&self,plant:usize) -> i16 {
todo!() todo!()
} }
fn measure_moisture_a_hz(&self,plant:i8) -> i16 { fn measure_moisture_p_hz(&self,plant:usize) -> i16 {
todo!() todo!()
} }
fn measure_moisture_p_hz(&self,plant:i8) -> i16 { fn pump(&self,plant:usize, enable:bool) {
todo!() let index = plant*PINS_PER_PLANT*PLANT_PUMP_OFFSET;
self.shift_register.decompose()[index].set_state(enable.into()).unwrap()
} }
fn pump(&self,plant:i8, enable:bool) { fn last_pump_time(&self,plant:usize) -> chrono::DateTime<Utc> {
todo!() 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 last_pump_time(&self,plant:i8) -> chrono::DateTime<Utc> { fn store_last_pump_time(&mut self,plant:usize, time: chrono::DateTime<Utc>) {
todo!() self.last_watering_timestamp.get_mut().unwrap()[plant] = time.timestamp_millis();
} }
fn store_last_pump_time(&self,plant:i8, time: chrono::DateTime<Utc>) { fn store_consecutive_pump_count(&mut self,plant:usize, count:u32) {
todo!() self.consecutive_watering_plant.get_mut().unwrap()[plant] = count;
} }
fn store_consecutive_pump_count(&self,plant:i8, count:i16) { fn consecutive_pump_count(&mut self,plant:usize) -> u32 {
todo!() return self.consecutive_watering_plant.get_mut().unwrap()[plant]
} }
fn consecutive_pump_count(&self,plant:i8) -> i16 { fn fault(&self,plant:usize, enable:bool) {
todo!() let index = plant*PINS_PER_PLANT*PLANT_FAULT_OFFSET;
self.shift_register.decompose()[index].set_state(enable.into()).unwrap()
} }
fn fault(&self,plant:i8, enable:bool) { fn low_voltage_in_cycle(&mut self) -> bool {
todo!() return *self.low_voltage_detected.get_mut().unwrap()
} }
fn low_voltage_in_cycle(&self) -> bool { fn any_pump(&mut self, enabled:bool) {
unsafe {
return LOW_VOLTAGE_DETECTED; todo!()
}
} }
@ -194,16 +267,18 @@ fn main() {
log::info!("Hello, world!"); log::info!("Hello, world!");
let reasons = ResetReason::get();
//init,reset rtc memory depending on cause
let board = PlantCtrlBoard::default(); let board = PlantCtrlBoard::default();
//check if we know the time current > 2020 //check if we know the time current > 2020
//if failed assume its 1.1.1970 //if failed assume its 1.1.1970
//12:00 if solar reports day //12:00 if solar reports day
//00:00 if solar repors night //00:00 if solar repors night
//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 //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
@ -224,16 +299,49 @@ fn main() {
//if no mqtt, set general fault persistent //if no mqtt, set general fault persistent
//measure each plant moisture //measure each plant moisture
//check which plants need to be watered
//() //if config battery mode
//read battery level
//if not possible set general fault persistent, but do continue
//else
//assume 12v and max capacity
//if tank sensor is enabled //if tank sensor is enabled
//if tank sensor fault abort if config require is set //if tank sensor fault abort if config require is set
//check if water is > minimum allowed || fault //check if water is > minimum allowed || fault
//if not, set all plants requiring water to persistent fault //if not, set all plants requiring water to persistent fault
// pump water for first plant update last water timestamp
// wait for config time per plant //for each plant
// //check if moisture is < target
//state += dry
//check if in cooldown
//state += cooldown
//check if consecutive pumps > limit
//state += notworking
//set plant fault persistent
//pump one cycle
// set last pump time to now
//during pump state += active
//after pump check if Pump moisture value is increased by config delta x
// state -= active
// state += cooldown
// if not set plant error persistent fault
// state += notworking
//set consecutive pumps+=1
//check if during light time
//lightstate += out of worktime
//check battery level
//lightstate += battery empty
//check solar level if config requires
//lightstate += stillday
//if no preventing lightstate, enable light
//lightstate = active

63
rust/src/second.rs Normal file
View File

@ -0,0 +1,63 @@
use anyhow::{Context, Error};
use esp_idf_hal::gpio::AnyInputPin;
use esp_idf_hal::pcnt::{PcntChannel, PcntChannelConfig, PcntControlMode, PcntCountMode, PcntDriver, PinIndex};
use esp_idf_hal::peripherals::Peripherals;
#[link_section = ".rtc.data"]
static mut MY_FANCY_VARIABLE: u32 = 0;
fn read_moisture() -> Result<i16,Error> {
let p = Peripherals::take().unwrap();
let counter1 = p.pcnt0;
let mut counter_unit1 = PcntDriver::new(
counter1,
Some(p.pins.gpio19),
Option::<AnyInputPin>::None,
Option::<AnyInputPin>::None,
Option::<AnyInputPin>::None,
).context("Could not obtain counter unit")?;
counter_unit1.channel_config(
PcntChannel::Channel0,
PinIndex::Pin0,
PinIndex::Pin1,
&PcntChannelConfig {
lctrl_mode: PcntControlMode::Reverse,
hctrl_mode: PcntControlMode::Keep,
pos_mode: PcntCountMode::Decrement,
neg_mode: PcntCountMode::Increment,
counter_h_lim: i16::MAX,
counter_l_lim: 0,
},
).context("Failed to configure pulse counter")?;
counter_unit1.set_filter_value(u16::min(10 * 80, 1023))?;
counter_unit1.filter_enable().context("Failed to enable pulse counter filter")?;
counter_unit1.counter_pause().context("Failed to pause pulse counter")?;
counter_unit1.counter_clear().context("Failed to clear pulse counter")?;
counter_unit1.counter_resume().context("Failed to start pulse counter")?;
let measurement = 100;
let waitFor = 1000/100;
//delay(measurement);
counter_unit1.counter_pause().context("Failed to end pulse counter measurement")?;
return Ok(counter_unit1.get_counter_value().context("Failed to read pulse counter value")?*waitFor);
}
fn main() {
// 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
esp_idf_svc::sys::link_patches();
// Bind the log crate to the ESP Logging facilities
esp_idf_svc::log::EspLogger::initialize_default();
log::info!("Hello, world!");
}