2024-02-15 23:00:05 +01:00
use bq34z100 ::{ Bq34Z100Error , Bq34z100g1 , Bq34z100g1Driver } ;
2023-12-04 00:41:29 +01:00
//mod config;
2024-02-15 23:00:05 +01:00
use chrono_tz ::Europe ::Berlin ;
2023-12-22 01:35:08 +01:00
use embedded_svc ::wifi ::{
2024-01-07 14:33:02 +01:00
AccessPointConfiguration , AccessPointInfo , AuthMethod , ClientConfiguration , Configuration ,
2023-12-22 01:35:08 +01:00
} ;
2024-02-02 21:35:04 +01:00
use esp_idf_hal ::i2c ::{ I2cConfig , I2cDriver , I2cError } ;
2024-01-17 21:25:01 +01:00
use esp_idf_hal ::units ::FromValueType ;
2023-11-23 22:50:17 +01:00
use esp_idf_svc ::eventloop ::EspSystemEventLoop ;
2024-02-21 22:30:03 +01:00
use esp_idf_svc ::ipv4 ::IpInfo ;
2024-02-15 23:00:05 +01:00
use esp_idf_svc ::mqtt ::client ::QoS ::AtLeastOnce ;
2024-01-07 14:33:02 +01:00
use esp_idf_svc ::mqtt ::client ::QoS ::ExactlyOnce ;
2024-02-15 23:00:05 +01:00
use esp_idf_svc ::mqtt ::client ::{ EspMqttClient , LwtConfiguration , MqttClientConfiguration } ;
2023-11-23 22:50:17 +01:00
use esp_idf_svc ::nvs ::EspDefaultNvsPartition ;
2023-12-22 01:35:08 +01:00
use esp_idf_svc ::wifi ::config ::{ ScanConfig , ScanType } ;
2023-12-22 17:26:00 +01:00
use esp_idf_svc ::wifi ::EspWifi ;
2024-02-02 21:35:04 +01:00
use measurements ::Temperature ;
2023-12-22 01:35:08 +01:00
use plant_ctrl2 ::sipo ::ShiftRegister40 ;
2023-11-23 22:50:17 +01:00
2023-12-22 01:35:08 +01:00
use anyhow ::anyhow ;
2023-12-22 17:26:00 +01:00
use anyhow ::{ bail , Ok , Result } ;
2023-12-12 03:46:53 +01:00
use std ::ffi ::CString ;
2023-12-04 00:41:29 +01:00
use std ::fs ::File ;
2023-12-12 03:46:53 +01:00
use std ::path ::Path ;
2024-01-07 14:33:02 +01:00
2024-02-15 23:00:05 +01:00
use chrono ::{ DateTime , NaiveDateTime , Utc } ;
use ds18b20 ::Ds18b20 ;
use std ::result ::Result ::Ok as OkStd ;
use std ::str ::FromStr ;
2024-01-07 14:33:02 +01:00
use std ::sync ::atomic ::AtomicBool ;
use std ::sync ::{ Arc , Mutex } ;
2023-12-22 01:35:08 +01:00
use std ::time ::Duration ;
2023-11-23 22:50:17 +01:00
2024-02-15 23:00:05 +01:00
use embedded_hal ::digital ::OutputPin ;
2023-12-22 01:35:08 +01:00
use esp_idf_hal ::adc ::{ attenuation , AdcChannelDriver , AdcDriver } ;
2023-12-22 17:26:00 +01:00
use esp_idf_hal ::delay ::Delay ;
2024-02-02 21:35:04 +01:00
use esp_idf_hal ::gpio ::{ AnyInputPin , Gpio39 , Gpio4 , InputOutput , Level , PinDriver , Pull } ;
2023-12-22 01:35:08 +01:00
use esp_idf_hal ::pcnt ::{
PcntChannel , PcntChannelConfig , PcntControlMode , PcntCountMode , PcntDriver , PinIndex ,
} ;
use esp_idf_hal ::prelude ::Peripherals ;
2023-11-23 22:50:17 +01:00
use esp_idf_hal ::reset ::ResetReason ;
use esp_idf_svc ::sntp ::{ self , SyncStatus } ;
use esp_idf_svc ::systime ::EspSystemTime ;
2024-02-15 23:00:05 +01:00
use esp_idf_sys ::{ esp , gpio_hold_dis , gpio_hold_en , vTaskDelay , EspError } ;
2023-11-23 22:50:17 +01:00
use one_wire_bus ::OneWire ;
2024-01-07 14:33:02 +01:00
use crate ::config ::{ self , Config , WifiConfig } ;
use crate ::STAY_ALIVE ;
2023-12-04 00:41:29 +01:00
2023-12-22 01:35:08 +01:00
pub 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 ;
2024-02-15 23:00:05 +01:00
const PLANT_MOIST_A_OFFSET : usize = 3 ;
const PLANT_MOIST_B_OFFSET : usize = 4 ;
2023-11-23 22:50:17 +01:00
2023-12-12 03:46:53 +01:00
const SPIFFS_PARTITION_NAME : & str = " storage " ;
const WIFI_CONFIG_FILE : & str = " /spiffs/wifi.cfg " ;
const CONFIG_FILE : & str = " /spiffs/config.cfg " ;
2023-11-23 22:50:17 +01:00
2024-02-02 21:35:04 +01:00
const TANK_MULTI_SAMPLE : usize = 11 ;
2023-11-23 22:50:17 +01:00
#[ 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 " ]
2023-12-22 01:35:08 +01:00
static mut LOW_VOLTAGE_DETECTED : bool = false ;
2023-11-23 22:50:17 +01:00
2023-12-22 01:35:08 +01:00
pub struct FileSystemSizeInfo {
2023-12-12 03:46:53 +01:00
pub total_size : usize ,
pub used_size : usize ,
2023-12-22 01:35:08 +01:00
pub free_size : usize ,
2023-12-12 03:46:53 +01:00
}
2023-12-27 17:33:11 +01:00
#[ derive(strum::Display) ]
pub enum ClearConfigType {
WifiConfig ,
Config ,
2024-01-07 14:33:02 +01:00
None ,
2023-12-27 17:33:11 +01:00
}
2023-11-29 18:27:55 +01:00
#[ derive(Debug) ]
2023-12-22 01:35:08 +01:00
pub enum Sensor {
2023-11-23 22:50:17 +01:00
A ,
B ,
2023-12-22 01:35:08 +01:00
PUMP ,
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
pub trait PlantCtrlBoardInteraction {
2023-11-23 22:50:17 +01:00
fn time ( & mut self ) -> Result < chrono ::DateTime < Utc > > ;
2024-02-15 23:00:05 +01:00
fn wifi (
& mut self ,
ssid : heapless ::String < 32 > ,
password : Option < heapless ::String < 64 > > ,
max_wait : u32 ,
2024-02-21 22:30:03 +01:00
) -> Result < IpInfo > ;
2023-12-22 01:35:08 +01:00
fn sntp ( & mut self , max_wait : u32 ) -> Result < chrono ::DateTime < Utc > > ;
2024-01-07 14:33:02 +01:00
fn mount_file_system ( & mut self ) -> Result < ( ) > ;
fn file_system_size ( & mut self ) -> Result < FileSystemSizeInfo > ;
2023-11-23 22:50:17 +01:00
2024-02-15 23:00:05 +01:00
fn state_charge_percent ( & mut self ) -> Result < u8 > ;
fn remaining_milli_ampere_hour ( & mut self ) -> Result < u16 > ;
fn max_milli_ampere_hour ( & mut self ) -> Result < u16 > ;
fn design_milli_ampere_hour ( & mut self ) -> Result < u16 > ;
fn voltage_milli_volt ( & mut self ) -> Result < u16 > ;
fn average_current_milli_ampere ( & mut self ) -> Result < i16 > ;
fn cycle_count ( & mut self ) -> Result < u16 > ;
fn state_health_percent ( & mut self ) -> Result < u8 > ;
2023-11-23 22:50:17 +01:00
fn general_fault ( & mut self , enable : bool ) ;
2023-12-22 01:35:08 +01:00
fn is_day ( & self ) -> bool ;
fn water_temperature_c ( & mut self ) -> Result < f32 > ;
2024-02-02 21:35:04 +01:00
fn tank_sensor_percent ( & mut self ) -> Result < u16 > ;
2023-12-22 01:35:08 +01:00
fn set_low_voltage_in_cycle ( & mut self ) ;
fn clear_low_voltage_in_cycle ( & mut self ) ;
2023-11-23 22:50:17 +01:00
fn low_voltage_in_cycle ( & mut self ) -> bool ;
2023-12-22 01:35:08 +01:00
fn any_pump ( & mut self , enabled : bool ) -> Result < ( ) > ;
2023-11-23 22:50:17 +01:00
//keep state during deepsleep
2023-12-22 01:35:08 +01:00
fn light ( & mut self , enable : bool ) -> Result < ( ) > ;
2023-11-23 22:50:17 +01:00
2023-12-22 01:35:08 +01:00
fn measure_moisture_hz ( & self , plant : usize , sensor : Sensor ) -> Result < i32 > ;
fn pump ( & self , plant : usize , enable : bool ) -> Result < ( ) > ;
2024-01-09 00:16:13 +01:00
fn last_pump_time ( & self , plant : usize ) -> chrono ::DateTime < Utc > ;
2023-12-22 01:35:08 +01:00
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 ;
2023-11-23 22:50:17 +01:00
//keep state during deepsleep
2023-12-22 01:35:08 +01:00
fn fault ( & self , plant : usize , enable : bool ) ;
2023-11-29 18:27:55 +01:00
2023-12-12 03:46:53 +01:00
//config
fn is_config_reset ( & mut self ) -> bool ;
2023-12-27 17:33:11 +01:00
fn remove_configs ( & mut self ) -> Result < ClearConfigType > ;
2023-12-04 00:41:29 +01:00
fn get_config ( & mut self ) -> Result < config ::Config > ;
2023-12-27 17:33:11 +01:00
fn set_config ( & mut self , wifi : & Config ) -> Result < ( ) > ;
2023-12-12 03:46:53 +01:00
fn get_wifi ( & mut self ) -> Result < config ::WifiConfig > ;
fn set_wifi ( & mut self , wifi : & WifiConfig ) -> Result < ( ) > ;
2023-12-22 01:35:08 +01:00
fn wifi_ap ( & mut self ) -> Result < ( ) > ;
fn wifi_scan ( & mut self ) -> Result < Vec < AccessPointInfo > > ;
2023-12-23 01:59:00 +01:00
fn test ( & mut self ) -> Result < ( ) > ;
2024-01-07 14:33:02 +01:00
fn is_wifi_config_file_existant ( & mut self ) -> bool ;
fn mqtt ( & mut self , config : & Config ) -> Result < ( ) > ;
2024-02-15 23:00:05 +01:00
fn mqtt_publish ( & mut self , config : & Config , subtopic : & str , message : & [ u8 ] ) -> Result < ( ) > ;
2023-11-23 22:50:17 +01:00
}
pub trait CreatePlantHal < ' a > {
2023-12-27 20:00:06 +01:00
fn create ( ) -> Result < Mutex < PlantCtrlBoard < 'static > > > ;
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
pub struct PlantHal { }
pub struct PlantCtrlBoard < ' a > {
2024-01-07 14:33:02 +01:00
shift_register : ShiftRegister40 <
2024-01-17 21:25:01 +01:00
PinDriver < ' a , esp_idf_hal ::gpio ::Gpio21 , InputOutput > ,
PinDriver < ' a , esp_idf_hal ::gpio ::Gpio22 , InputOutput > ,
PinDriver < ' a , esp_idf_hal ::gpio ::Gpio19 , InputOutput > ,
2024-01-07 14:33:02 +01:00
> ,
2023-11-23 22:50:17 +01:00
tank_driver : AdcDriver < ' a , esp_idf_hal ::adc ::ADC1 > ,
2023-12-22 01:35:08 +01:00
tank_channel : esp_idf_hal ::adc ::AdcChannelDriver < ' a , { attenuation ::DB_11 } , Gpio39 > ,
2023-11-23 22:50:17 +01:00
solar_is_day : PinDriver < ' a , esp_idf_hal ::gpio ::Gpio25 , esp_idf_hal ::gpio ::Input > ,
2023-12-12 03:46:53 +01:00
boot_button : PinDriver < ' a , esp_idf_hal ::gpio ::Gpio0 , esp_idf_hal ::gpio ::Input > ,
2023-11-23 22:50:17 +01:00
signal_counter : PcntDriver < ' a > ,
2024-01-17 21:25:01 +01:00
light : PinDriver < ' a , esp_idf_hal ::gpio ::Gpio26 , InputOutput > ,
main_pump : PinDriver < ' a , esp_idf_hal ::gpio ::Gpio23 , InputOutput > ,
tank_power : PinDriver < ' a , esp_idf_hal ::gpio ::Gpio27 , InputOutput > ,
general_fault : PinDriver < ' a , esp_idf_hal ::gpio ::Gpio13 , InputOutput > ,
2023-11-29 18:27:55 +01:00
pub wifi_driver : EspWifi < ' a > ,
2023-11-23 22:50:17 +01:00
one_wire_bus : OneWire < PinDriver < ' a , Gpio4 , esp_idf_hal ::gpio ::InputOutput > > ,
2024-01-07 14:33:02 +01:00
mqtt_client : Option < EspMqttClient < ' a > > ,
2024-02-15 23:00:05 +01:00
battery_driver : Bq34z100g1Driver < I2cDriver < ' a > , Delay > ,
2023-11-23 22:50:17 +01:00
}
impl PlantCtrlBoardInteraction for PlantCtrlBoard < '_ > {
2023-12-22 01:35:08 +01:00
fn is_day ( & self ) -> bool {
2024-01-07 14:33:02 +01:00
self . solar_is_day . get_level ( ) . into ( )
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
fn water_temperature_c ( & mut self ) -> Result < f32 > {
2023-11-23 22:50:17 +01:00
let mut delay = Delay ::new_default ( ) ;
2023-12-22 01:35:08 +01:00
self . one_wire_bus
. reset ( & mut delay )
. map_err ( | err | -> anyhow ::Error { anyhow! ( " Missing attribute: {:?} " , err ) } ) ? ;
2023-11-23 22:50:17 +01:00
let first = self . one_wire_bus . devices ( false , & mut delay ) . next ( ) ;
if first . is_none ( ) {
bail! ( " Not found any one wire Ds18b20 " ) ;
}
2023-12-22 01:35:08 +01:00
let device_address = first
. unwrap ( )
. map_err ( | err | -> anyhow ::Error { anyhow! ( " Missing attribute: {:?} " , err ) } ) ? ;
let water_temp_sensor = Ds18b20 ::new ::< EspError > ( device_address )
. map_err ( | err | -> anyhow ::Error { anyhow! ( " Missing attribute: {:?} " , err ) } ) ? ;
water_temp_sensor
. start_temp_measurement ( & mut self . one_wire_bus , & mut delay )
. map_err ( | err | -> anyhow ::Error { anyhow! ( " Missing attribute: {:?} " , err ) } ) ? ;
ds18b20 ::Resolution ::Bits12 . delay_for_measurement_time ( & mut delay ) ;
let sensor_data = water_temp_sensor
. read_data ( & mut self . one_wire_bus , & mut delay )
. map_err ( | err | -> anyhow ::Error { anyhow! ( " Missing attribute: {:?} " , err ) } ) ? ;
2023-11-23 22:50:17 +01:00
if sensor_data . temperature = = 85_ f32 {
bail! ( " Ds18b20 dummy temperature returned " ) ;
}
2024-02-02 21:35:04 +01:00
Ok ( sensor_data . temperature / 10_ f32 )
2023-11-23 22:50:17 +01:00
}
2024-02-02 21:35:04 +01:00
fn tank_sensor_percent ( & mut self ) -> Result < u16 > {
2023-11-23 22:50:17 +01:00
let delay = Delay ::new_default ( ) ;
self . tank_power . set_high ( ) ? ;
//let stabilize
delay . delay_ms ( 100 ) ;
2024-02-02 21:35:04 +01:00
unsafe {
vTaskDelay ( 100 ) ;
}
let mut store = [ 0_ u16 ; TANK_MULTI_SAMPLE ] ;
for multisample in 0 .. TANK_MULTI_SAMPLE {
let value = self . tank_driver . read ( & mut self . tank_channel ) ? ;
store [ multisample ] = value ;
}
store . sort ( ) ;
let median = store [ 6 ] as f32 / 1000_ f32 ;
let config_open_voltage_mv = 3.0 ;
if config_open_voltage_mv < median {
self . tank_power . set_low ( ) ? ;
bail! (
" Tank sensor missing, open loop voltage {} on tank sensor input {} " ,
config_open_voltage_mv ,
median
) ;
}
let r2 = median * 50.0 / ( 3.3 - median ) ;
let mut percent = r2 / 190_ f32 * 100_ f32 ;
percent = percent . clamp ( 0.0 , 100.0 ) ;
let quantizised = quantize_to_next_5_percent ( percent as f64 ) as u16 ;
println! (
" Tank sensor raw {} percent {} quantized {} " ,
median , percent , quantizised
) ;
return Ok ( quantizised ) ;
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
fn set_low_voltage_in_cycle ( & mut self ) {
2024-02-15 23:00:05 +01:00
unsafe {
LOW_VOLTAGE_DETECTED = true ;
}
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
fn clear_low_voltage_in_cycle ( & mut self ) {
2024-02-15 23:00:05 +01:00
unsafe {
LOW_VOLTAGE_DETECTED = false ;
}
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
fn light ( & mut self , enable : bool ) -> Result < ( ) > {
2024-02-02 21:35:04 +01:00
unsafe { gpio_hold_dis ( self . light . pin ( ) ) } ;
2023-11-23 22:50:17 +01:00
self . light . set_state ( enable . into ( ) ) ? ;
2024-02-02 21:35:04 +01:00
unsafe { gpio_hold_en ( self . light . pin ( ) ) } ;
2023-11-23 22:50:17 +01:00
Ok ( ( ) )
}
2023-12-22 01:35:08 +01:00
fn pump ( & self , plant : usize , enable : bool ) -> Result < ( ) > {
let index = plant * PINS_PER_PLANT + PLANT_PUMP_OFFSET ;
2023-11-23 22:50:17 +01:00
//currently infailable error, keep for future as result anyway
2024-01-07 14:33:02 +01:00
self . shift_register . decompose ( ) [ index ]
. set_state ( enable . into ( ) )
. unwrap ( ) ;
2023-11-23 22:50:17 +01:00
Ok ( ( ) )
}
2024-01-09 00:16:13 +01:00
fn last_pump_time ( & self , plant : usize ) -> chrono ::DateTime < Utc > {
2023-11-23 22:50:17 +01:00
let ts = unsafe { LAST_WATERING_TIMESTAMP } [ plant ] ;
2024-01-09 00:16:13 +01:00
let timestamp = NaiveDateTime ::from_timestamp_millis ( ts ) . unwrap ( ) ;
DateTime ::< Utc > ::from_naive_utc_and_offset ( timestamp , Utc )
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
fn store_last_pump_time ( & mut self , plant : usize , time : chrono ::DateTime < Utc > ) {
2024-02-15 23:00:05 +01:00
unsafe {
LAST_WATERING_TIMESTAMP [ plant ] = time . timestamp_millis ( ) ;
}
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
fn store_consecutive_pump_count ( & mut self , plant : usize , count : u32 ) {
2024-02-15 23:00:05 +01:00
unsafe {
CONSECUTIVE_WATERING_PLANT [ plant ] = count ;
}
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
fn consecutive_pump_count ( & mut self , plant : usize ) -> u32 {
2024-02-15 23:00:05 +01:00
unsafe {
return CONSECUTIVE_WATERING_PLANT [ plant ] ;
}
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
fn fault ( & self , plant : usize , enable : bool ) {
let index = plant * PINS_PER_PLANT + PLANT_FAULT_OFFSET ;
2024-01-07 14:33:02 +01:00
self . shift_register . decompose ( ) [ index ]
. set_state ( enable . into ( ) )
. unwrap ( )
2023-11-23 22:50:17 +01:00
}
fn low_voltage_in_cycle ( & mut self ) -> bool {
2024-02-15 23:00:05 +01:00
unsafe {
return LOW_VOLTAGE_DETECTED ;
}
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
fn any_pump ( & mut self , enable : bool ) -> Result < ( ) > {
2024-01-07 14:33:02 +01:00
{
self . main_pump . set_state ( enable . into ( ) ) . unwrap ( ) ;
Ok ( ( ) )
}
2023-11-23 22:50:17 +01:00
}
fn time ( & mut self ) -> Result < chrono ::DateTime < Utc > > {
2023-12-22 01:35:08 +01:00
let time = EspSystemTime { } . now ( ) . as_millis ( ) ;
2023-11-23 22:50:17 +01:00
let smaller_time = time as i64 ;
2023-12-22 01:35:08 +01:00
let local_time = NaiveDateTime ::from_timestamp_millis ( smaller_time )
. ok_or ( anyhow! ( " could not convert timestamp " ) ) ? ;
2024-01-07 14:33:02 +01:00
Ok ( local_time . and_utc ( ) )
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
fn sntp ( & mut self , max_wait_ms : u32 ) -> Result < chrono ::DateTime < Utc > > {
2023-11-23 22:50:17 +01:00
let sntp = sntp ::EspSntp ::new_default ( ) ? ;
let mut counter = 0 ;
2023-12-22 01:35:08 +01:00
while sntp . get_sync_status ( ) ! = SyncStatus ::Completed {
2023-11-23 22:50:17 +01:00
let delay = Delay ::new_default ( ) ;
delay . delay_ms ( 100 ) ;
counter + = 100 ;
if counter > max_wait_ms {
bail! ( " Reached sntp timeout, aborting " )
}
}
2024-01-07 14:33:02 +01:00
self . time ( )
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
fn measure_moisture_hz ( & self , plant : usize , sensor : Sensor ) -> Result < i32 > {
2023-11-23 22:50:17 +01:00
self . signal_counter . counter_pause ( ) ? ;
self . signal_counter . counter_clear ( ) ? ;
//
let offset = match sensor {
Sensor ::A = > PLANT_MOIST_A_OFFSET ,
Sensor ::B = > PLANT_MOIST_B_OFFSET ,
Sensor ::PUMP = > PLANT_MOIST_PUMP_OFFSET ,
} ;
2023-12-22 01:35:08 +01:00
let index = plant * PINS_PER_PLANT + offset ;
2023-11-23 22:50:17 +01:00
let delay = Delay ::new_default ( ) ;
let measurement = 100 ;
2024-02-15 23:00:05 +01:00
let factor = 1000 as f32 / measurement as f32 ;
2023-11-23 22:50:17 +01:00
2023-12-23 01:59:00 +01:00
self . shift_register . decompose ( ) [ index ] . set_high ( ) . unwrap ( ) ;
2023-11-23 22:50:17 +01:00
//give some time to stabilize
delay . delay_ms ( 10 ) ;
self . signal_counter . counter_resume ( ) ? ;
delay . delay_ms ( measurement ) ;
self . signal_counter . counter_pause ( ) ? ;
2023-12-23 01:59:00 +01:00
self . shift_register . decompose ( ) [ index ] . set_low ( ) . unwrap ( ) ;
2023-11-23 22:50:17 +01:00
let unscaled = self . signal_counter . get_counter_value ( ) ? as i32 ;
2024-02-15 23:00:05 +01:00
let hz = ( unscaled as f32 * factor ) as i32 ;
2023-11-29 18:27:55 +01:00
println! ( " Measuring {:?} @ {} with {} " , sensor , plant , hz ) ;
2024-01-07 14:33:02 +01:00
Ok ( hz )
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
fn general_fault ( & mut self , enable : bool ) {
2023-11-23 22:50:17 +01:00
self . general_fault . set_state ( enable . into ( ) ) . unwrap ( ) ;
}
2023-12-22 01:35:08 +01:00
fn wifi_ap ( & mut self ) -> Result < ( ) > {
let apconfig = AccessPointConfiguration {
2024-02-15 23:00:05 +01:00
ssid : heapless ::String ::from_str ( " PlantCtrl " ) . unwrap ( ) ,
2023-12-22 01:35:08 +01:00
auth_method : AuthMethod ::None ,
ssid_hidden : false ,
.. Default ::default ( )
} ;
let clientconfig = ClientConfiguration ::default ( ) ;
2024-01-07 14:33:02 +01:00
self . wifi_driver
. set_configuration ( & Configuration ::Mixed ( clientconfig , apconfig ) ) ? ;
2023-12-22 01:35:08 +01:00
self . wifi_driver . start ( ) ? ;
Ok ( ( ) )
}
2024-02-15 23:00:05 +01:00
fn wifi (
& mut self ,
ssid : heapless ::String < 32 > ,
password : Option < heapless ::String < 64 > > ,
max_wait : u32 ,
2024-02-21 22:30:03 +01:00
) -> Result < IpInfo > {
2023-12-22 01:35:08 +01:00
match password {
2023-11-23 22:50:17 +01:00
Some ( pw ) = > {
//TODO expect error due to invalid pw or similar! //call this during configuration and check if works, revert to config mode if not
2023-12-22 01:35:08 +01:00
self . wifi_driver . set_configuration ( & Configuration ::Client (
ClientConfiguration {
2024-02-15 23:00:05 +01:00
ssid : ssid ,
password : pw ,
2023-12-22 01:35:08 +01:00
.. Default ::default ( )
} ,
) ) ? ;
}
2023-11-23 22:50:17 +01:00
None = > {
2023-12-22 01:35:08 +01:00
self . wifi_driver
. set_configuration ( & Configuration ::Client ( ClientConfiguration {
2024-02-15 23:00:05 +01:00
ssid : ssid ,
2023-12-22 01:35:08 +01:00
auth_method : AuthMethod ::None ,
.. Default ::default ( )
} ) )
. unwrap ( ) ;
}
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
self . wifi_driver . start ( ) ? ;
self . wifi_driver . connect ( ) ? ;
2023-11-23 22:50:17 +01:00
let delay = Delay ::new_default ( ) ;
let mut counter = 0_ u32 ;
2023-12-22 01:35:08 +01:00
while ! self . wifi_driver . is_connected ( ) ? {
2023-11-29 18:27:55 +01:00
println! ( " Waiting for station connection " ) ;
2023-11-23 22:50:17 +01:00
delay . delay_ms ( 250 ) ;
counter + = 250 ;
if counter > max_wait {
//ignore these errors, wifi will not be used this
self . wifi_driver . disconnect ( ) . unwrap_or ( ( ) ) ;
self . wifi_driver . stop ( ) . unwrap_or ( ( ) ) ;
bail! ( " Did not manage wifi connection within timeout " ) ;
}
}
println! ( " Should be connected now " ) ;
2023-11-29 18:27:55 +01:00
2024-01-07 14:33:02 +01:00
while ! self . wifi_driver . is_up ( ) . unwrap ( ) {
2023-11-29 18:27:55 +01:00
println! ( " Waiting for network being up " ) ;
delay . delay_ms ( 250 ) ;
counter + = 250 ;
if counter > max_wait {
//ignore these errors, wifi will not be used this
self . wifi_driver . disconnect ( ) . unwrap_or ( ( ) ) ;
self . wifi_driver . stop ( ) . unwrap_or ( ( ) ) ;
bail! ( " Did not manage wifi connection within timeout " ) ;
}
}
//update freertos registers ;)
2023-11-23 22:50:17 +01:00
let address = self . wifi_driver . sta_netif ( ) . get_ip_info ( ) . unwrap ( ) ;
println! ( " IP info: {:?} " , address ) ;
2024-02-21 22:30:03 +01:00
Ok ( address )
2023-11-23 22:50:17 +01:00
}
2024-01-07 14:33:02 +01:00
fn mount_file_system ( & mut self ) -> Result < ( ) > {
2023-12-12 03:46:53 +01:00
let base_path = CString ::new ( " /spiffs " ) ? ;
let storage = CString ::new ( SPIFFS_PARTITION_NAME ) ? ;
let conf = esp_idf_sys ::esp_vfs_spiffs_conf_t {
base_path : base_path . as_ptr ( ) ,
partition_label : storage . as_ptr ( ) ,
max_files : 2 ,
format_if_mount_failed : true ,
} ;
2023-12-04 00:41:29 +01:00
2023-12-12 03:46:53 +01:00
unsafe {
esp_idf_sys ::esp! ( esp_idf_sys ::esp_vfs_spiffs_register ( & conf ) ) ? ;
Ok ( ( ) )
}
2023-11-29 18:27:55 +01:00
}
2024-01-07 14:33:02 +01:00
fn file_system_size ( & mut self ) -> Result < FileSystemSizeInfo > {
2023-12-12 03:46:53 +01:00
let storage = CString ::new ( SPIFFS_PARTITION_NAME ) ? ;
let mut total_size = 0 ;
let mut used_size = 0 ;
unsafe {
2023-12-22 01:35:08 +01:00
esp_idf_sys ::esp! ( esp_idf_sys ::esp_spiffs_info (
storage . as_ptr ( ) ,
& mut total_size ,
& mut used_size
) ) ? ;
2023-12-12 03:46:53 +01:00
}
2024-01-07 14:33:02 +01:00
Ok ( FileSystemSizeInfo {
2023-12-22 01:35:08 +01:00
total_size ,
used_size ,
free_size : total_size - used_size ,
2024-01-07 14:33:02 +01:00
} )
2023-12-12 03:46:53 +01:00
}
fn is_config_reset ( & mut self ) -> bool {
2024-01-07 14:33:02 +01:00
self . boot_button . get_level ( ) = = Level ::Low
2023-12-12 03:46:53 +01:00
}
2023-12-27 17:33:11 +01:00
fn remove_configs ( & mut self ) -> Result < ClearConfigType > {
2023-12-12 03:46:53 +01:00
let config = Path ::new ( CONFIG_FILE ) ;
if config . exists ( ) {
println! ( " Removing config " ) ;
2023-12-22 01:35:08 +01:00
std ::fs ::remove_file ( config ) ? ;
2023-12-27 17:33:11 +01:00
return Ok ( ClearConfigType ::Config ) ;
2023-12-12 03:46:53 +01:00
}
2024-01-07 14:33:02 +01:00
2023-12-27 17:33:11 +01:00
let wifi_config = Path ::new ( WIFI_CONFIG_FILE ) ;
if wifi_config . exists ( ) {
println! ( " Removing wifi config " ) ;
std ::fs ::remove_file ( wifi_config ) ? ;
2023-12-27 20:00:06 +01:00
return Ok ( ClearConfigType ::WifiConfig ) ;
2023-12-27 17:33:11 +01:00
}
2024-01-07 14:33:02 +01:00
Ok ( ClearConfigType ::None )
2023-12-12 03:46:53 +01:00
}
fn get_wifi ( & mut self ) -> Result < config ::WifiConfig > {
let cfg = File ::open ( WIFI_CONFIG_FILE ) ? ;
let config : WifiConfig = serde_json ::from_reader ( cfg ) ? ;
2024-01-07 14:33:02 +01:00
Ok ( config )
2023-12-12 03:46:53 +01:00
}
2023-12-22 01:35:08 +01:00
fn set_wifi ( & mut self , wifi : & WifiConfig ) -> Result < ( ) > {
2023-12-12 03:46:53 +01:00
let mut cfg = File ::create ( WIFI_CONFIG_FILE ) ? ;
serde_json ::to_writer ( & mut cfg , & wifi ) ? ;
println! ( " Wrote wifi config {} " , wifi ) ;
2024-01-07 14:33:02 +01:00
Ok ( ( ) )
2023-12-12 03:46:53 +01:00
}
fn get_config ( & mut self ) -> Result < config ::Config > {
2023-12-27 17:33:11 +01:00
let cfg = File ::open ( CONFIG_FILE ) ? ;
2024-02-15 23:00:05 +01:00
let mut config : Config = serde_json ::from_reader ( cfg ) ? ;
//remove duplicate end of topic
if config . base_topic . ends_with ( " / " ) {
config . base_topic . pop ( ) ;
}
2024-01-07 14:33:02 +01:00
Ok ( config )
2023-12-27 17:33:11 +01:00
}
2023-12-12 03:46:53 +01:00
2023-12-27 17:33:11 +01:00
fn set_config ( & mut self , config : & Config ) -> Result < ( ) > {
let mut cfg = File ::create ( CONFIG_FILE ) ? ;
serde_json ::to_writer ( & mut cfg , & config ) ? ;
println! ( " Wrote config config {:?} " , config ) ;
2024-01-07 14:33:02 +01:00
Ok ( ( ) )
2023-12-12 03:46:53 +01:00
}
2023-11-23 22:50:17 +01:00
2023-12-22 01:35:08 +01:00
fn wifi_scan ( & mut self ) -> Result < Vec < AccessPointInfo > > {
2023-12-22 17:26:00 +01:00
//remove this parts
2023-12-22 01:35:08 +01:00
for i in 1 .. 11 {
println! ( " Scanning channel {} " , i ) ;
2024-01-07 14:33:02 +01:00
self . wifi_driver . start_scan (
& ScanConfig {
scan_type : ScanType ::Passive ( Duration ::from_secs ( 1 ) ) ,
show_hidden : false ,
channel : Some ( i ) ,
.. Default ::default ( )
} ,
true ,
) ? ;
2023-12-22 01:35:08 +01:00
let sr = self . wifi_driver . get_scan_result ( ) ? ;
2024-01-07 14:33:02 +01:00
for r in sr . iter ( ) {
2023-12-22 01:35:08 +01:00
println! ( " Found wifi {} " , r . ssid ) ;
}
}
2024-01-07 14:33:02 +01:00
self . wifi_driver . start_scan (
& ScanConfig {
scan_type : ScanType ::Passive ( Duration ::from_secs ( 1 ) ) ,
show_hidden : false ,
.. Default ::default ( )
} ,
true ,
) ? ;
Ok ( self . wifi_driver . get_scan_result ( ) ? )
2023-12-22 01:35:08 +01:00
}
2023-12-23 01:59:00 +01:00
fn test ( & mut self ) -> Result < ( ) > {
self . general_fault ( true ) ;
unsafe { vTaskDelay ( 100 ) } ;
self . general_fault ( false ) ;
unsafe { vTaskDelay ( 100 ) } ;
self . any_pump ( true ) ? ;
unsafe { vTaskDelay ( 500 ) } ;
self . any_pump ( false ) ? ;
unsafe { vTaskDelay ( 500 ) } ;
self . light ( true ) ? ;
unsafe { vTaskDelay ( 500 ) } ;
self . light ( false ) ? ;
unsafe { vTaskDelay ( 500 ) } ;
2024-01-07 14:33:02 +01:00
for i in 0 .. 8 {
2023-12-23 01:59:00 +01:00
self . fault ( i , true ) ;
unsafe { vTaskDelay ( 500 ) } ;
self . fault ( i , false ) ;
unsafe { vTaskDelay ( 500 ) } ;
}
2024-01-07 14:33:02 +01:00
for i in 0 .. 8 {
2023-12-23 01:59:00 +01:00
self . pump ( i , true ) ? ;
2024-02-17 17:25:50 +01:00
unsafe { vTaskDelay ( 100 ) } ;
2023-12-23 01:59:00 +01:00
self . pump ( i , false ) ? ;
2024-02-17 17:25:50 +01:00
unsafe { vTaskDelay ( 100 ) } ;
2023-12-23 01:59:00 +01:00
}
2024-01-07 14:33:02 +01:00
for i in 0 .. 8 {
2023-12-23 01:59:00 +01:00
self . measure_moisture_hz ( i , Sensor ::A ) ? ;
}
2024-01-07 14:33:02 +01:00
for i in 0 .. 8 {
2023-12-23 01:59:00 +01:00
self . measure_moisture_hz ( i , Sensor ::B ) ? ;
}
2024-01-07 14:33:02 +01:00
for i in 0 .. 8 {
2023-12-23 01:59:00 +01:00
self . measure_moisture_hz ( i , Sensor ::PUMP ) ? ;
}
2024-01-07 14:33:02 +01:00
Ok ( ( ) )
}
fn is_wifi_config_file_existant ( & mut self ) -> bool {
let config = Path ::new ( CONFIG_FILE ) ;
config . exists ( )
}
fn mqtt ( & mut self , config : & Config ) -> Result < ( ) > {
2024-02-15 23:00:05 +01:00
let last_will_topic = format! ( " {} /state " , config . base_topic ) ;
2024-01-07 14:33:02 +01:00
let mqtt_client_config = MqttClientConfiguration {
2024-02-15 23:00:05 +01:00
lwt : Some ( LwtConfiguration {
topic : & last_will_topic ,
payload : " lost " . as_bytes ( ) ,
qos : AtLeastOnce ,
retain : true ,
} ) ,
2024-03-02 13:21:12 +01:00
client_id : Some ( " plantctrl " ) ,
2024-02-21 22:30:03 +01:00
keep_alive_interval : Some ( Duration ::from_secs ( 60 * 60 * 2 ) ) ,
2024-01-07 14:33:02 +01:00
//room for improvement
.. Default ::default ( )
} ;
let round_trip_ok = Arc ::new ( AtomicBool ::new ( false ) ) ;
let round_trip_topic = format! ( " {} /internal/roundtrip " , config . base_topic ) ;
let stay_alive_topic = format! ( " {} /stay_alive " , config . base_topic ) ;
println! ( " Round trip topic is {} " , round_trip_topic ) ;
println! ( " Stay alive topic is {} " , stay_alive_topic ) ;
let stay_alive_topic_copy = stay_alive_topic . clone ( ) ;
let round_trip_topic_copy = round_trip_topic . clone ( ) ;
let round_trip_ok_copy = round_trip_ok . clone ( ) ;
let mut client =
2024-02-15 23:00:05 +01:00
EspMqttClient ::new_cb ( & config . mqtt_url , & mqtt_client_config , move | event | {
let payload = event . payload ( ) ;
match payload {
embedded_svc ::mqtt ::client ::EventPayload ::Received {
id : _ ,
topic ,
data ,
details : _ ,
} = > {
let data = String ::from_utf8_lossy ( data ) ;
if let Some ( topic ) = topic {
//todo use enums
if topic . eq ( round_trip_topic_copy . as_str ( ) ) {
round_trip_ok_copy
. store ( true , std ::sync ::atomic ::Ordering ::Relaxed ) ;
} else if topic . eq ( stay_alive_topic_copy . as_str ( ) ) {
let value = data . eq_ignore_ascii_case ( " true " )
| | data . eq_ignore_ascii_case ( " 1 " ) ;
println! ( " Received stay alive with value {} " , value ) ;
STAY_ALIVE . store ( value , std ::sync ::atomic ::Ordering ::Relaxed ) ;
} else {
println! ( " Unknown topic recieved {} " , topic ) ;
2024-01-07 14:33:02 +01:00
}
}
}
2024-02-15 23:00:05 +01:00
_ = > { }
2024-01-07 14:33:02 +01:00
}
} ) ? ;
//subscribe to roundtrip
client . subscribe ( round_trip_topic . as_str ( ) , ExactlyOnce ) ? ;
client . subscribe ( stay_alive_topic . as_str ( ) , ExactlyOnce ) ? ;
//publish to roundtrip
client . publish (
round_trip_topic . as_str ( ) ,
ExactlyOnce ,
false ,
" online_test " . as_bytes ( ) ,
) ? ;
let wait_for_roundtrip = 0 ;
while wait_for_roundtrip < 100 {
match round_trip_ok . load ( std ::sync ::atomic ::Ordering ::Relaxed ) {
true = > {
println! ( " Round trip registered, proceeding " ) ;
self . mqtt_client = Some ( client ) ;
return Ok ( ( ) ) ;
}
false = > {
unsafe { vTaskDelay ( 10 ) } ;
}
}
}
bail! ( " Mqtt did not complete roundtrip in time " ) ;
}
2024-02-15 23:00:05 +01:00
fn mqtt_publish ( & mut self , config : & Config , subtopic : & str , message : & [ u8 ] ) -> Result < ( ) > {
if ! subtopic . starts_with ( " / " ) {
println! ( " Subtopic without / at start {} " , subtopic ) ;
bail! ( " Subtopic without / at start {} " , subtopic ) ;
}
if subtopic . len ( ) > 192 {
println! ( " Subtopic exceeds 192 chars {} " , subtopic ) ;
bail! ( " Subtopic exceeds 192 chars {} " , subtopic ) ;
}
if self . mqtt_client . is_none ( ) {
println! ( " Not connected to mqtt " ) ;
bail! ( " Not connected to mqtt " ) ;
}
let client = self . mqtt_client . as_mut ( ) . unwrap ( ) ;
let mut full_topic : heapless ::String < 256 > = heapless ::String ::new ( ) ;
if full_topic . push_str ( & config . base_topic ) . is_err ( ) {
println! ( " Some error assembling full_topic 1 " ) ;
bail! ( " Some error assembling full_topic 1 " )
} ;
if full_topic . push_str ( subtopic ) . is_err ( ) {
println! ( " Some error assembling full_topic 2 " ) ;
bail! ( " Some error assembling full_topic 2 " )
} ;
2024-03-02 14:01:48 +01:00
let publish = client . publish (
2024-02-15 23:00:05 +01:00
& full_topic ,
embedded_svc ::mqtt ::client ::QoS ::ExactlyOnce ,
true ,
message ,
2024-03-02 14:01:48 +01:00
) ;
match publish {
OkStd ( _ ) = > return Ok ( ( ) ) ,
Err ( err ) = > {
println! ( " Error during mqtt send on topic {} with message {:#?} error is {:?} " , full_topic , message , err ) ;
return Err ( err ) ?
} ,
}
2024-02-15 23:00:05 +01:00
2024-03-02 14:01:48 +01:00
;
2024-02-15 23:00:05 +01:00
}
fn state_charge_percent ( & mut self ) -> Result < u8 > {
match self . battery_driver . state_of_charge ( ) {
OkStd ( r ) = > Ok ( r ) ,
Err ( err ) = > bail! ( " Error reading SoC {:?} " , err ) ,
}
}
fn remaining_milli_ampere_hour ( & mut self ) -> Result < u16 > {
match self . battery_driver . remaining_capacity ( ) {
OkStd ( r ) = > Ok ( r ) ,
Err ( err ) = > bail! ( " Error reading Remaining Capacity {:?} " , err ) ,
}
}
fn max_milli_ampere_hour ( & mut self ) -> Result < u16 > {
match self . battery_driver . full_charge_capacity ( ) {
OkStd ( r ) = > Ok ( r ) ,
Err ( err ) = > bail! ( " Error reading Full Charge Capacity {:?} " , err ) ,
}
}
fn design_milli_ampere_hour ( & mut self ) -> Result < u16 > {
match self . battery_driver . design_capacity ( ) {
OkStd ( r ) = > Ok ( r ) ,
Err ( err ) = > bail! ( " Error reading Design Capacity {:?} " , err ) ,
}
}
fn voltage_milli_volt ( & mut self ) -> Result < u16 > {
return match self . battery_driver . voltage ( ) {
OkStd ( r ) = > Ok ( r ) ,
Err ( err ) = > bail! ( " Error reading voltage {:?} " , err ) ,
} ;
}
fn average_current_milli_ampere ( & mut self ) -> Result < i16 > {
match self . battery_driver . average_current ( ) {
OkStd ( r ) = > Ok ( r ) ,
Err ( err ) = > bail! ( " Error reading Average Current {:?} " , err ) ,
}
}
fn cycle_count ( & mut self ) -> Result < u16 > {
match self . battery_driver . cycle_count ( ) {
OkStd ( r ) = > Ok ( r ) ,
Err ( err ) = > bail! ( " Error reading Cycle Count {:?} " , err ) ,
}
}
fn state_health_percent ( & mut self ) -> Result < u8 > {
match self . battery_driver . state_of_health ( ) {
OkStd ( r ) = > Ok ( r as u8 ) ,
Err ( err ) = > bail! ( " Error reading State of Health {:?} " , err ) ,
}
}
2024-01-07 14:33:02 +01:00
}
2024-02-02 21:35:04 +01:00
fn print_battery (
battery_driver : & mut Bq34z100g1Driver < I2cDriver , Delay > ,
) -> Result < ( ) , Bq34Z100Error < I2cError > > {
2024-02-15 23:00:05 +01:00
let fwversion = battery_driver . fw_version ( ) . unwrap_or_else ( | e | {
println! ( " Firmeware {:?} " , e ) ;
0
} ) ;
2024-02-02 21:35:04 +01:00
println! ( " fw version is {} " , fwversion ) ;
2024-02-15 23:00:05 +01:00
let design_capacity = battery_driver . design_capacity ( ) . unwrap_or_else ( | e | {
println! ( " Design capacity {:?} " , e ) ;
0
} ) ;
2024-02-02 21:35:04 +01:00
println! ( " Design Capacity {} " , design_capacity ) ;
if design_capacity = = 1000 {
println! ( " Still stock configuring battery, readouts are likely to be wrong! " ) ;
}
let flags = battery_driver . get_flags_decoded ( ) ? ;
println! ( " Flags {:?} " , flags ) ;
2024-02-15 23:00:05 +01:00
let chem_id = battery_driver . chem_id ( ) . unwrap_or_else ( | e | {
println! ( " Chemid {:?} " , e ) ;
0
} ) ;
let bat_temp = battery_driver . internal_temperature ( ) . unwrap_or_else ( | e | {
println! ( " Bat Temp {:?} " , e ) ;
0
} ) ;
2024-02-02 21:35:04 +01:00
let temp_c = Temperature ::from_kelvin ( bat_temp as f64 / 10_ f64 ) . as_celsius ( ) ;
2024-02-15 23:00:05 +01:00
let voltage = battery_driver . voltage ( ) . unwrap_or_else ( | e | {
println! ( " Bat volt {:?} " , e ) ;
0
} ) ;
let current = battery_driver . current ( ) . unwrap_or_else ( | e | {
println! ( " Bat current {:?} " , e ) ;
0
} ) ;
let state = battery_driver . state_of_charge ( ) . unwrap_or_else ( | e | {
println! ( " Bat Soc {:?} " , e ) ;
0
} ) ;
let charge_voltage = battery_driver . charge_voltage ( ) . unwrap_or_else ( | e | {
println! ( " Bat Charge Volt {:?} " , e ) ;
0
} ) ;
let charge_current = battery_driver . charge_current ( ) . unwrap_or_else ( | e | {
println! ( " Bat Charge Current {:?} " , e ) ;
0
} ) ;
2024-02-02 21:35:04 +01:00
println! ( " ChemId: {} Current voltage {} and current {} with charge {} % and temp {} CVolt: {} CCur {} " , chem_id , voltage , current , state , temp_c , charge_voltage , charge_current ) ;
2024-02-15 23:00:05 +01:00
let _ = battery_driver . unsealed ( ) ;
let _ = battery_driver . it_enable ( ) ;
2024-02-02 21:35:04 +01:00
return Result ::Ok ( ( ) ) ;
}
2024-01-07 14:33:02 +01:00
impl CreatePlantHal < '_ > for PlantHal {
fn create ( ) -> Result < Mutex < PlantCtrlBoard < 'static > > > {
let peripherals = Peripherals ::take ( ) ? ;
2024-02-02 21:35:04 +01:00
2024-01-17 21:25:01 +01:00
let i2c = peripherals . i2c1 ;
let config = I2cConfig ::new ( )
2024-02-02 21:35:04 +01:00
. scl_enable_pullup ( false )
. sda_enable_pullup ( false )
. baudrate ( 10_ u32 . kHz ( ) . into ( ) ) ;
2024-01-17 21:25:01 +01:00
let scl = peripherals . pins . gpio16 ;
let sda = peripherals . pins . gpio17 ;
let driver = I2cDriver ::new ( i2c , sda , scl , & config ) . unwrap ( ) ;
2024-02-02 21:35:04 +01:00
2024-02-15 23:00:05 +01:00
let i2c_port = driver . port ( ) ;
esp! ( unsafe { esp_idf_sys ::i2c_set_timeout ( i2c_port , 1048000 ) } ) . unwrap ( ) ;
2024-02-02 21:35:04 +01:00
let mut battery_driver : Bq34z100g1Driver < I2cDriver , Delay > = Bq34z100g1Driver {
i2c : driver ,
2024-01-21 06:11:06 +01:00
delay : Delay ::new_default ( ) ,
2024-02-02 21:35:04 +01:00
flash_block_data : [ 0 ; 32 ] ,
} ;
2024-01-17 21:25:01 +01:00
let mut clock = PinDriver ::input_output ( peripherals . pins . gpio21 ) ? ;
2024-02-02 21:35:04 +01:00
clock . set_pull ( Pull ::Floating ) . unwrap ( ) ;
2024-01-17 21:25:01 +01:00
let mut latch = PinDriver ::input_output ( peripherals . pins . gpio22 ) ? ;
2024-02-02 21:35:04 +01:00
latch . set_pull ( Pull ::Floating ) . unwrap ( ) ;
2024-01-17 21:25:01 +01:00
let mut data = PinDriver ::input_output ( peripherals . pins . gpio19 ) ? ;
2024-02-02 21:35:04 +01:00
data . set_pull ( Pull ::Floating ) . unwrap ( ) ;
2024-01-17 21:25:01 +01:00
let shift_register = ShiftRegister40 ::new ( clock . into ( ) , latch . into ( ) , data . into ( ) ) ;
for mut pin in shift_register . decompose ( ) {
pin . set_low ( ) . unwrap ( ) ;
}
let mut one_wire_pin = PinDriver ::input_output_od ( peripherals . pins . gpio4 ) ? ;
2024-02-02 21:35:04 +01:00
one_wire_pin . set_pull ( Pull ::Floating ) . unwrap ( ) ;
2024-01-07 14:33:02 +01:00
//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 " ) ;
2024-02-15 23:00:05 +01:00
unsafe {
println! (
" Current low voltage detection is {:?} " ,
LOW_VOLTAGE_DETECTED
) ;
for i in 0 .. PLANT_COUNT {
let smaller_time = LAST_WATERING_TIMESTAMP [ i ] ;
let local_time = NaiveDateTime ::from_timestamp_millis ( smaller_time )
. ok_or ( anyhow! ( " could not convert timestamp " ) ) ? ;
let utc_time = local_time . and_utc ( ) ;
let europe_time = utc_time . with_timezone ( & Berlin ) ;
println! (
" LAST_WATERING_TIMESTAMP[{}] = {} as europe {} " ,
i , LAST_WATERING_TIMESTAMP [ i ] , europe_time
) ;
}
for i in 0 .. PLANT_COUNT {
println! (
" CONSECUTIVE_WATERING_PLANT[{}] = {} " ,
i , CONSECUTIVE_WATERING_PLANT [ i ]
) ;
}
}
2024-01-07 14:33:02 +01:00
}
let mut counter_unit1 = PcntDriver ::new (
peripherals . pcnt0 ,
Some ( peripherals . pins . gpio18 ) ,
Option ::< AnyInputPin > ::None ,
Option ::< AnyInputPin > ::None ,
Option ::< AnyInputPin > ::None ,
) ? ;
println! ( " Channel config start " ) ;
counter_unit1 . channel_config (
PcntChannel ::Channel0 ,
PinIndex ::Pin0 ,
PinIndex ::Pin1 ,
& PcntChannelConfig {
2024-02-15 23:00:05 +01:00
lctrl_mode : PcntControlMode ::Keep ,
2024-01-07 14:33:02 +01:00
hctrl_mode : PcntControlMode ::Keep ,
2024-02-15 23:00:05 +01:00
pos_mode : PcntCountMode ::Increment ,
neg_mode : PcntCountMode ::Hold ,
2024-01-07 14:33:02 +01:00
counter_h_lim : i16 ::MAX ,
counter_l_lim : 0 ,
} ,
) ? ;
println! ( " Setup filter " ) ;
//TODO validate filter value! currently max allowed value
counter_unit1 . set_filter_value ( 1023 ) ? ;
counter_unit1 . filter_enable ( ) ? ;
println! ( " Wifi start " ) ;
let sys_loop = EspSystemEventLoop ::take ( ) ? ;
let nvs = EspDefaultNvsPartition ::take ( ) ? ;
let wifi_driver = EspWifi ::new ( peripherals . modem , sys_loop , Some ( nvs ) ) ? ;
2024-02-02 21:35:04 +01:00
let adc_config = esp_idf_hal ::adc ::config ::Config {
resolution : esp_idf_hal ::adc ::config ::Resolution ::Resolution12Bit ,
calibration : true ,
} ;
let tank_driver = AdcDriver ::new ( peripherals . adc1 , & adc_config ) ? ;
2024-01-07 14:33:02 +01:00
let tank_channel : AdcChannelDriver < '_ , { attenuation ::DB_11 } , Gpio39 > =
AdcChannelDriver ::new ( peripherals . pins . gpio39 ) ? ;
2024-01-17 21:25:01 +01:00
let mut solar_is_day = PinDriver ::input ( peripherals . pins . gpio25 ) ? ;
solar_is_day . set_pull ( Pull ::Floating ) ? ;
let mut boot_button = PinDriver ::input ( peripherals . pins . gpio0 ) ? ;
boot_button . set_pull ( Pull ::Floating ) ? ;
2024-02-02 21:35:04 +01:00
2024-01-17 21:25:01 +01:00
let mut light = PinDriver ::input_output ( peripherals . pins . gpio26 ) ? ;
2024-02-02 21:35:04 +01:00
light . set_pull ( Pull ::Floating ) . unwrap ( ) ;
2024-01-17 21:25:01 +01:00
let mut main_pump = PinDriver ::input_output ( peripherals . pins . gpio23 ) ? ;
main_pump . set_pull ( Pull ::Floating ) ? ;
main_pump . set_low ( ) ? ;
let mut tank_power = PinDriver ::input_output ( peripherals . pins . gpio27 ) ? ;
tank_power . set_pull ( Pull ::Floating ) ? ;
let mut general_fault = PinDriver ::input_output ( peripherals . pins . gpio13 ) ? ;
general_fault . set_pull ( Pull ::Floating ) ? ;
general_fault . set_low ( ) ? ;
2024-01-07 14:33:02 +01:00
let one_wire_bus = OneWire ::new ( one_wire_pin )
. map_err ( | err | -> anyhow ::Error { anyhow! ( " Missing attribute: {:?} " , err ) } ) ? ;
println! ( " After stuff " ) ;
2024-02-02 21:35:04 +01:00
let status = print_battery ( & mut battery_driver ) ;
if status . is_err ( ) {
println! ( " Error communicating with battery!! {:?} " , status . err ( ) ) ;
2024-01-21 06:11:06 +01:00
}
2024-01-07 14:33:02 +01:00
let rv = Mutex ::new ( PlantCtrlBoard {
shift_register ,
tank_driver ,
tank_channel ,
solar_is_day ,
boot_button ,
light ,
main_pump ,
tank_power ,
general_fault ,
one_wire_bus ,
signal_counter : counter_unit1 ,
wifi_driver ,
mqtt_client : None ,
2024-02-15 23:00:05 +01:00
battery_driver ,
2024-01-07 14:33:02 +01:00
} ) ;
Ok ( rv )
2023-12-23 01:59:00 +01:00
}
2023-12-22 01:35:08 +01:00
}
2024-02-02 21:35:04 +01:00
fn quantize_to_next_5_percent ( value : f64 ) -> i32 {
// Multiply by 100 to work with integer values
let multiplied_value = ( value * 100.0 ) . round ( ) as i32 ;
// Calculate the remainder when divided by 5
let remainder = multiplied_value % 5 ;
// If the remainder is greater than or equal to half of 5, round up to the next 5%
let rounded_value = if remainder > = 2 {
multiplied_value + ( 5 - remainder )
} else {
multiplied_value - remainder
} ;
// Divide by 100 to get back to a float
rounded_value / 100
}