2024-02-15 23:00:05 +01:00
use bq34z100 ::{ Bq34Z100Error , Bq34z100g1 , Bq34z100g1Driver } ;
2024-06-18 20:34:35 +02:00
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-05-14 21:57:11 +02:00
use esp_idf_hal ::adc ::attenuation ;
use esp_idf_hal ::adc ::oneshot ::config ::AdcChannelConfig ;
use esp_idf_hal ::adc ::oneshot ::{ AdcChannelDriver , AdcDriver } ;
2024-05-01 18:06:20 +02:00
use esp_idf_hal ::i2c ::{ APBTickType , 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-06-10 23:40:50 +02: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-03-27 21:10:37 +01:00
use chrono ::{ DateTime , Utc } ;
2024-02-15 23:00:05 +01:00
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 17:26:00 +01:00
use esp_idf_hal ::delay ::Delay ;
2024-05-01 18:06:20 +02:00
use esp_idf_hal ::gpio ::{ AnyInputPin , Gpio18 , Gpio5 , IOPin , 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-04-25 20:26:58 +02: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 } ;
2024-07-31 20:45:06 +02:00
use crate ::{ plant_hal , STAY_ALIVE } ;
2023-12-04 00:41:29 +01:00
2024-06-18 20:34:35 +02:00
//Only support for 8 right now!
2023-12-22 01:35:08 +01:00
pub const PLANT_COUNT : usize = 8 ;
2024-07-31 20:45:06 +02:00
const REPEAT_MOIST_MEASURE : usize = 3 ;
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 ;
2024-06-18 20:34:35 +02:00
const PUMP8_BIT : usize = 0 ;
const PUMP1_BIT : usize = 1 ;
const PUMP2_BIT : usize = 2 ;
const PUMP3_BIT : usize = 3 ;
const PUMP4_BIT : usize = 4 ;
const PUMP5_BIT : usize = 5 ;
const PUMP6_BIT : usize = 6 ;
const PUMP7_BIT : usize = 7 ;
const MS_0 : usize = 8 ;
const MS_4 : usize = 9 ;
const MS_2 : usize = 10 ;
const MS_3 : usize = 11 ;
const SENSOR_ON : usize = 12 ;
const MS_1 : usize = 13 ;
//unused 14
//unused 15
const FAULT_3 : usize = 16 ;
const FAULT_8 : usize = 17 ;
const FAULT_7 : usize = 18 ;
const FAULT_6 : usize = 19 ;
const FAULT_5 : usize = 20 ;
const FAULT_4 : usize = 21 ;
const FAULT_1 : usize = 22 ;
const FAULT_2 : usize = 23 ;
const SENSOR_A_1 : u8 = 7 ;
const SENSOR_A_2 : u8 = 6 ;
const SENSOR_A_3 : u8 = 5 ;
const SENSOR_A_4 : u8 = 4 ;
const SENSOR_A_5 : u8 = 3 ;
const SENSOR_A_6 : u8 = 2 ;
const SENSOR_A_7 : u8 = 1 ;
const SENSOR_A_8 : u8 = 0 ;
const SENSOR_B_1 : u8 = 8 ;
const SENSOR_B_2 : u8 = 9 ;
const SENSOR_B_3 : u8 = 10 ;
const SENSOR_B_4 : u8 = 11 ;
const SENSOR_B_5 : u8 = 12 ;
const SENSOR_B_6 : u8 = 13 ;
const SENSOR_B_7 : u8 = 14 ;
const SENSOR_B_8 : u8 = 15 ;
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
}
2024-04-22 13:03:42 +02:00
#[ derive(Debug, PartialEq) ]
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
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
2024-06-18 20:34:35 +02:00
fn measure_moisture_hz ( & mut self , plant : usize , sensor : Sensor ) -> Result < i32 > ;
2023-12-22 01:35:08 +01:00
fn pump ( & self , plant : usize , enable : bool ) -> Result < ( ) > ;
2024-04-22 13:03:42 +02:00
fn last_pump_time ( & self , plant : usize ) -> Option < 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-04-22 13:03:42 +02:00
fn test_pump ( & mut self , plant : usize ) -> 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 < ( ) > ;
2024-06-18 20:34:35 +02:00
fn sensor_multiplexer ( & mut self , n : u8 ) -> Result < ( ) > ;
2024-08-11 18:44:24 +02:00
fn flash_bq34_z100 ( & mut self , line : & str , dryrun : bool ) -> 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-05-01 18:06:20 +02:00
PinDriver < ' a , esp_idf_hal ::gpio ::AnyIOPin , InputOutput > ,
PinDriver < ' a , esp_idf_hal ::gpio ::AnyIOPin , InputOutput > ,
PinDriver < ' a , esp_idf_hal ::gpio ::AnyIOPin , InputOutput > ,
2024-01-07 14:33:02 +01:00
> ,
2024-08-11 18:44:24 +02:00
shift_register_enable_invert :
PinDriver < ' a , esp_idf_hal ::gpio ::AnyIOPin , esp_idf_hal ::gpio ::Output > ,
2024-05-14 21:57:11 +02:00
tank_channel : AdcChannelDriver < ' a , Gpio5 , AdcDriver < ' a , esp_idf_hal ::adc ::ADC1 > > ,
2024-05-01 18:06:20 +02:00
solar_is_day : PinDriver < ' a , esp_idf_hal ::gpio ::AnyIOPin , esp_idf_hal ::gpio ::Input > ,
boot_button : PinDriver < ' a , esp_idf_hal ::gpio ::AnyIOPin , esp_idf_hal ::gpio ::Input > ,
2023-11-23 22:50:17 +01:00
signal_counter : PcntDriver < ' a > ,
2024-05-01 18:06:20 +02:00
light : PinDriver < ' a , esp_idf_hal ::gpio ::AnyIOPin , InputOutput > ,
main_pump : PinDriver < ' a , esp_idf_hal ::gpio ::AnyIOPin , InputOutput > ,
tank_power : PinDriver < ' a , esp_idf_hal ::gpio ::AnyIOPin , InputOutput > ,
general_fault : PinDriver < ' a , esp_idf_hal ::gpio ::AnyIOPin , InputOutput > ,
2023-11-29 18:27:55 +01:00
pub wifi_driver : EspWifi < ' a > ,
2024-04-20 23:32:44 +02:00
one_wire_bus : OneWire < PinDriver < ' a , Gpio18 , esp_idf_hal ::gpio ::InputOutput > > ,
2024-01-07 14:33:02 +01:00
mqtt_client : Option < EspMqttClient < ' a > > ,
2024-04-22 13:03:42 +02:00
battery_driver : Option < 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 {
2024-05-14 21:57:11 +02:00
let value = self . tank_channel . read ( ) ? ;
2024-02-02 21:35:04 +01:00
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 ) ;
2024-04-22 13:03:42 +02:00
println! ( " Tank sensor raw {} percent {} " , median , percent ) ;
2024-03-09 15:21:52 +01:00
return Ok ( percent as u16 ) ;
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 < ( ) > {
2024-06-18 20:34:35 +02:00
let index = match plant {
0 = > PUMP1_BIT ,
1 = > PUMP2_BIT ,
2 = > PUMP3_BIT ,
3 = > PUMP4_BIT ,
4 = > PUMP5_BIT ,
5 = > PUMP6_BIT ,
6 = > PUMP7_BIT ,
7 = > PUMP8_BIT ,
_ = > bail! ( " Invalid pump {plant} " , ) ,
} ;
2023-11-23 22:50:17 +01:00
//currently infailable error, keep for future as result anyway
2024-04-22 13:03:42 +02:00
self . shift_register . decompose ( ) [ index ] . set_state ( enable . into ( ) ) ? ;
2023-11-23 22:50:17 +01:00
Ok ( ( ) )
}
2024-04-22 13:03:42 +02:00
fn last_pump_time ( & self , plant : usize ) -> Option < chrono ::DateTime < Utc > > {
2023-11-23 22:50:17 +01:00
let ts = unsafe { LAST_WATERING_TIMESTAMP } [ plant ] ;
2024-04-22 13:03:42 +02:00
return Some ( DateTime ::from_timestamp_millis ( ts ) ? ) ;
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 ) {
2024-06-18 20:34:35 +02:00
let index = match plant {
0 = > FAULT_1 ,
1 = > FAULT_2 ,
2 = > FAULT_3 ,
3 = > FAULT_4 ,
4 = > FAULT_5 ,
5 = > FAULT_6 ,
6 = > FAULT_7 ,
7 = > FAULT_8 ,
2024-08-11 18:44:24 +02:00
_ = > panic! ( " Invalid plant id {} " , plant ) ,
2024-06-18 20:34:35 +02:00
} ;
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 ;
2024-03-27 21:10:37 +01:00
let local_time = DateTime ::from_timestamp_millis ( smaller_time )
2023-12-22 01:35:08 +01:00
. ok_or ( anyhow! ( " could not convert timestamp " ) ) ? ;
2024-03-27 21:10:37 +01:00
Ok ( local_time )
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
}
2024-06-18 20:34:35 +02:00
fn measure_moisture_hz ( & mut self , plant : usize , sensor : Sensor ) -> Result < i32 > {
let sensor_channel = match sensor {
Sensor ::A = > match plant {
0 = > SENSOR_A_1 ,
1 = > SENSOR_A_2 ,
2 = > SENSOR_A_3 ,
3 = > SENSOR_A_4 ,
4 = > SENSOR_A_5 ,
5 = > SENSOR_A_6 ,
6 = > SENSOR_A_7 ,
7 = > SENSOR_A_8 ,
2024-08-11 18:44:24 +02:00
_ = > bail! ( " Invalid plant id {} " , plant ) ,
2024-06-18 20:34:35 +02:00
} ,
Sensor ::B = > match plant {
0 = > SENSOR_B_1 ,
1 = > SENSOR_B_2 ,
2 = > SENSOR_B_3 ,
3 = > SENSOR_B_4 ,
4 = > SENSOR_B_5 ,
5 = > SENSOR_B_6 ,
6 = > SENSOR_B_7 ,
7 = > SENSOR_B_8 ,
2024-08-11 18:44:24 +02:00
_ = > bail! ( " Invalid plant id {} " , plant ) ,
2024-06-18 20:34:35 +02:00
} ,
2023-11-23 22:50:17 +01:00
} ;
2024-06-18 20:34:35 +02:00
2024-08-11 18:44:24 +02:00
let mut results = [ 0 ; REPEAT_MOIST_MEASURE ] ;
2024-07-31 20:45:06 +02:00
for repeat in 0 .. REPEAT_MOIST_MEASURE {
self . signal_counter . counter_pause ( ) ? ;
self . signal_counter . counter_clear ( ) ? ;
//Disable all
2024-08-11 18:44:24 +02:00
self . shift_register . decompose ( ) [ MS_4 ] . set_high ( ) . unwrap ( ) ;
2024-07-31 20:45:06 +02:00
self . sensor_multiplexer ( sensor_channel ) ? ;
2024-08-11 18:44:24 +02:00
self . shift_register . decompose ( ) [ MS_4 ] . set_low ( ) . unwrap ( ) ;
self . shift_register . decompose ( ) [ SENSOR_ON ]
. set_high ( )
2024-07-31 20:45:06 +02:00
. unwrap ( ) ;
2024-08-11 18:44:24 +02:00
2024-07-31 20:45:06 +02:00
let delay = Delay ::new_default ( ) ;
let measurement = 10 ;
let factor = 1000 as f32 / measurement as f32 ;
2024-08-11 18:44:24 +02:00
2024-07-31 20:45:06 +02:00
//give some time to stabilize
delay . delay_ms ( 10 ) ;
self . signal_counter . counter_resume ( ) ? ;
delay . delay_ms ( measurement ) ;
self . signal_counter . counter_pause ( ) ? ;
2024-08-11 18:44:24 +02:00
self . shift_register . decompose ( ) [ MS_4 ] . set_high ( ) . unwrap ( ) ;
2024-07-31 20:45:06 +02:00
self . shift_register . decompose ( ) [ SENSOR_ON ]
2024-08-11 18:44:24 +02:00
. set_low ( )
. unwrap ( ) ;
delay . delay_ms ( 10 ) ;
2024-07-31 20:45:06 +02:00
let unscaled = self . signal_counter . get_counter_value ( ) ? as i32 ;
let hz = ( unscaled as f32 * factor ) as i32 ;
results [ repeat ] = hz ;
//println!("Measuring {:?} @ {} with {}", sensor, plant, hz);
}
results . sort ( ) ;
2024-08-11 18:44:24 +02:00
2024-07-31 20:45:06 +02:00
let mid = results . len ( ) / 2 ;
2023-12-22 01:35:08 +01:00
2024-07-31 20:45:06 +02:00
Ok ( results [ mid ] )
2023-11-23 22:50:17 +01:00
}
2023-12-22 01:35:08 +01:00
fn general_fault ( & mut self , enable : bool ) {
2024-06-08 00:27:12 +02:00
unsafe { gpio_hold_dis ( self . general_fault . pin ( ) ) } ;
2023-11-23 22:50:17 +01:00
self . general_fault . set_state ( enable . into ( ) ) . unwrap ( ) ;
2024-06-10 21:10:46 +02:00
unsafe { gpio_hold_en ( self . general_fault . pin ( ) ) } ;
2023-11-23 22:50:17 +01:00
}
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 = > {
2024-04-22 13:03:42 +02: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 ( )
2024-04-22 13:03:42 +02:00
} ,
) ) ? ;
2023-12-22 01:35:08 +01:00
}
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-04-22 13:03:42 +02:00
while ! self . wifi_driver . is_up ( ) ? {
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 ;)
2024-04-22 13:03:42 +02:00
let address = self . wifi_driver . sta_netif ( ) . get_ip_info ( ) ? ;
2023-11-23 22:50:17 +01:00
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 > > {
2024-01-07 14:33:02 +01:00
self . wifi_driver . start_scan (
& ScanConfig {
2024-07-31 20:45:06 +02:00
scan_type : ScanType ::Passive ( Duration ::from_secs ( 5 ) ) ,
2024-01-07 14:33:02 +01:00
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
2024-04-22 13:03:42 +02:00
fn test_pump ( & mut self , plant : usize ) -> Result < ( ) > {
2024-04-06 21:14:56 +02:00
self . any_pump ( true ) ? ;
self . pump ( plant , true ) ? ;
unsafe { vTaskDelay ( 30000 ) } ;
self . pump ( plant , false ) ? ;
self . any_pump ( false ) ? ;
Ok ( ( ) )
}
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-07-31 20:45:06 +02:00
for i in 0 .. PLANT_COUNT {
2023-12-23 01:59:00 +01:00
self . fault ( i , true ) ;
unsafe { vTaskDelay ( 500 ) } ;
self . fault ( i , false ) ;
unsafe { vTaskDelay ( 500 ) } ;
}
2024-07-31 20:45:06 +02:00
for i in 0 .. PLANT_COUNT {
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-07-31 20:45:06 +02:00
for plant in 0 .. PLANT_COUNT {
let a = self . measure_moisture_hz ( plant , plant_hal ::Sensor ::A ) ;
let b = self . measure_moisture_hz ( plant , plant_hal ::Sensor ::B ) ;
2024-08-11 18:44:24 +02:00
print! ( " P: {} a: {:?} b: {:?} " , plant , a , b )
2023-12-23 01:59:00 +01:00
}
2024-07-31 20:45:06 +02:00
println! ( ) ;
Delay ::new_default ( ) . delay_ms ( 10 ) ;
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-04-22 13:03:42 +02:00
keep_alive_interval : Some ( Duration ::from_secs ( 60 * 60 * 2 ) ) ,
2024-01-07 14:33:02 +01:00
//room for improvement
.. Default ::default ( )
} ;
2024-04-22 13:03:42 +02:00
let mqtt_connected_event_received = Arc ::new ( AtomicBool ::new ( false ) ) ;
let mqtt_connected_event_ok = Arc ::new ( AtomicBool ::new ( false ) ) ;
2024-01-07 14:33:02 +01:00
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 ) ;
2024-04-22 13:03:42 +02:00
let mqtt_connected_event_received_copy = mqtt_connected_event_received . clone ( ) ;
let mqtt_connected_event_ok_copy = mqtt_connected_event_ok . clone ( ) ;
2024-01-07 14:33:02 +01:00
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 ( ) ;
2024-04-22 13:03:42 +02:00
println! (
" Connecting mqtt {} with id {} " ,
config . mqtt_url ,
mqtt_client_config . client_id . unwrap_or ( " not set " )
) ;
2024-01-07 14:33:02 +01:00
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-04-26 18:52:53 +02:00
embedded_svc ::mqtt ::client ::EventPayload ::Connected ( _ ) = > {
2024-04-22 13:03:42 +02:00
mqtt_connected_event_received_copy
. store ( true , std ::sync ::atomic ::Ordering ::Relaxed ) ;
mqtt_connected_event_ok_copy
. store ( true , std ::sync ::atomic ::Ordering ::Relaxed ) ;
println! ( " Mqtt connected " ) ;
}
embedded_svc ::mqtt ::client ::EventPayload ::Disconnected = > {
mqtt_connected_event_received_copy
. store ( true , std ::sync ::atomic ::Ordering ::Relaxed ) ;
mqtt_connected_event_ok_copy
. store ( false , std ::sync ::atomic ::Ordering ::Relaxed ) ;
println! ( " Mqtt disconnected " ) ;
}
2024-04-25 20:26:58 +02:00
embedded_svc ::mqtt ::client ::EventPayload ::Error ( esp_error ) = > {
println! ( " EspMqttError reported {:?} " , esp_error ) ;
2024-04-22 13:03:42 +02:00
mqtt_connected_event_received_copy
. store ( true , std ::sync ::atomic ::Ordering ::Relaxed ) ;
mqtt_connected_event_ok_copy
. store ( false , std ::sync ::atomic ::Ordering ::Relaxed ) ;
println! ( " Mqtt error " ) ;
}
2024-05-14 21:57:11 +02:00
_ = > { }
2024-01-07 14:33:02 +01:00
}
} ) ? ;
2024-04-22 13:03:42 +02:00
let wait_for_connections_event = 0 ;
while wait_for_connections_event < 100 {
2024-04-25 20:26:58 +02:00
match mqtt_connected_event_received . load ( std ::sync ::atomic ::Ordering ::Relaxed ) {
2024-01-07 14:33:02 +01:00
true = > {
2024-04-22 13:03:42 +02:00
println! ( " Mqtt connection callback received, progressing " ) ;
match mqtt_connected_event_ok . load ( std ::sync ::atomic ::Ordering ::Relaxed ) {
true = > {
println! ( " Mqtt did callback as connected, testing with roundtrip now " ) ;
//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 " ) ;
}
false = > {
bail! ( " Mqtt did respond but with failure " )
}
}
2024-01-07 14:33:02 +01:00
}
false = > {
unsafe { vTaskDelay ( 10 ) } ;
}
}
}
2024-04-22 13:03:42 +02:00
bail! ( " Mqtt did not fire connection callback in time " ) ;
2024-01-07 14:33:02 +01:00
}
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 " ) ;
}
2024-04-22 13:03:42 +02:00
match & mut self . mqtt_client {
Some ( client ) = > {
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 " )
} ;
let publish = client . publish (
& full_topic ,
embedded_svc ::mqtt ::client ::QoS ::ExactlyOnce ,
true ,
message ,
) ;
Delay ::new ( 10 ) . delay_ms ( 50 ) ;
match publish {
OkStd ( message_id ) = > {
println! (
" Published mqtt topic {} with message {:#?} msgid is {:?} " ,
full_topic ,
String ::from_utf8_lossy ( message ) ,
message_id
) ;
return Ok ( ( ) ) ;
}
Err ( err ) = > {
println! (
" Error during mqtt send on topic {} with message {:#?} error is {:?} " ,
full_topic ,
String ::from_utf8_lossy ( message ) ,
err
) ;
return Err ( err ) ? ;
}
} ;
}
None = > {
2024-06-10 21:10:46 +02:00
bail! ( " No mqtt client " ) ;
2024-04-22 13:03:42 +02: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 > {
2024-04-22 13:03:42 +02:00
match & mut self . battery_driver {
Some ( driver ) = > match driver . state_of_charge ( ) {
OkStd ( r ) = > Ok ( r ) ,
Err ( err ) = > bail! ( " Error reading SoC {:?} " , err ) ,
} ,
None = > bail! ( " Error reading SoC bq34z100 not found " ) ,
2024-02-15 23:00:05 +01:00
}
}
fn remaining_milli_ampere_hour ( & mut self ) -> Result < u16 > {
2024-04-22 13:03:42 +02:00
match & mut self . battery_driver {
Some ( driver ) = > match driver . remaining_capacity ( ) {
OkStd ( r ) = > Ok ( r ) ,
Err ( err ) = > bail! ( " Error reading Remaining Capacity {:?} " , err ) ,
} ,
None = > bail! ( " Error reading Remaining Capacity bq34z100 not found " ) ,
2024-02-15 23:00:05 +01:00
}
}
fn max_milli_ampere_hour ( & mut self ) -> Result < u16 > {
2024-04-22 13:03:42 +02:00
match & mut self . battery_driver {
Some ( driver ) = > match driver . full_charge_capacity ( ) {
OkStd ( r ) = > Ok ( r ) ,
Err ( err ) = > bail! ( " Error reading Full Charge Capacity {:?} " , err ) ,
} ,
None = > bail! ( " Error reading Full Charge Capacity bq34z100 not found " ) ,
2024-02-15 23:00:05 +01:00
}
}
fn design_milli_ampere_hour ( & mut self ) -> Result < u16 > {
2024-04-22 13:03:42 +02:00
match & mut self . battery_driver {
Some ( driver ) = > match driver . design_capacity ( ) {
OkStd ( r ) = > Ok ( r ) ,
Err ( err ) = > bail! ( " Error reading Design Capacity {:?} " , err ) ,
} ,
None = > bail! ( " Error reading Design Capacity bq34z100 not found " ) ,
2024-02-15 23:00:05 +01:00
}
}
fn voltage_milli_volt ( & mut self ) -> Result < u16 > {
2024-04-22 13:03:42 +02:00
match & mut self . battery_driver {
Some ( driver ) = > match driver . voltage ( ) {
OkStd ( r ) = > Ok ( r ) ,
Err ( err ) = > bail! ( " Error reading voltage {:?} " , err ) ,
} ,
None = > bail! ( " Error reading voltage bq34z100 not found " ) ,
}
2024-02-15 23:00:05 +01:00
}
fn average_current_milli_ampere ( & mut self ) -> Result < i16 > {
2024-04-22 13:03:42 +02:00
match & mut self . battery_driver {
Some ( driver ) = > match driver . average_current ( ) {
OkStd ( r ) = > Ok ( r ) ,
Err ( err ) = > bail! ( " Error reading Average Current {:?} " , err ) ,
} ,
None = > bail! ( " Error reading Average Current bq34z100 not found " ) ,
2024-02-15 23:00:05 +01:00
}
}
fn cycle_count ( & mut self ) -> Result < u16 > {
2024-04-22 13:03:42 +02:00
match & mut self . battery_driver {
Some ( driver ) = > match driver . cycle_count ( ) {
OkStd ( r ) = > Ok ( r ) ,
Err ( err ) = > bail! ( " Error reading Cycle Count {:?} " , err ) ,
} ,
None = > bail! ( " Error reading Cycle Count bq34z100 not found " ) ,
2024-02-15 23:00:05 +01:00
}
}
fn state_health_percent ( & mut self ) -> Result < u8 > {
2024-04-22 13:03:42 +02:00
match & mut self . battery_driver {
Some ( driver ) = > match driver . state_of_health ( ) {
OkStd ( r ) = > Ok ( r as u8 ) ,
Err ( err ) = > bail! ( " Error reading State of Health {:?} " , err ) ,
} ,
None = > bail! ( " Error reading State of Health bq34z100 not found " ) ,
2024-02-15 23:00:05 +01:00
}
}
2024-06-18 20:34:35 +02:00
fn sensor_multiplexer ( & mut self , n : u8 ) -> Result < ( ) > {
assert! ( n < 16 ) ;
let is_bit_set = | b : u8 | -> bool { n & ( 1 < < b ) ! = 0 } ;
let pin_0 = & mut self . shift_register . decompose ( ) [ MS_0 ] ;
let pin_1 = & mut self . shift_register . decompose ( ) [ MS_1 ] ;
let pin_2 = & mut self . shift_register . decompose ( ) [ MS_2 ] ;
let pin_3 = & mut self . shift_register . decompose ( ) [ MS_3 ] ;
if is_bit_set ( 0 ) {
pin_0 . set_high ( ) ? ;
} else {
pin_0 . set_low ( ) ? ;
}
if is_bit_set ( 1 ) {
pin_1 . set_high ( ) ? ;
} else {
pin_1 . set_low ( ) ? ;
}
if is_bit_set ( 2 ) {
pin_2 . set_high ( ) ? ;
} else {
pin_2 . set_low ( ) ? ;
}
if is_bit_set ( 3 ) {
pin_3 . set_high ( ) ? ;
} else {
pin_3 . set_low ( ) ? ;
}
Ok ( ( ) )
}
2024-08-11 18:44:24 +02:00
2024-07-31 20:45:06 +02:00
fn flash_bq34_z100 ( & mut self , line : & str , dryrun : bool ) -> Result < ( ) > {
match & mut self . battery_driver {
Some ( driver ) = > match driver . write_flash_stream_i2c ( line , dryrun ) {
OkStd ( r ) = > Ok ( r ) ,
Err ( err ) = > bail! ( " Error reading SoC {:?} " , err ) ,
} ,
None = > bail! ( " Error reading SoC bq34z100 not found " ) ,
}
}
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-04-26 18:52:53 +02:00
println! ( " Try communicating with battery " ) ;
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-04-20 23:32:44 +02:00
let i2c = peripherals . i2c0 ;
2024-01-17 21:25:01 +01:00
let config = I2cConfig ::new ( )
2024-05-01 18:06:20 +02:00
. scl_enable_pullup ( true )
. sda_enable_pullup ( true )
2024-08-12 21:17:16 +02:00
. baudrate ( 400_ u32 . kHz ( ) . into ( ) )
2024-05-01 18:06:20 +02:00
. timeout ( APBTickType ::from ( Duration ::from_millis ( 100 ) ) ) ;
let scl = peripherals . pins . gpio19 . downgrade ( ) ;
let sda = peripherals . pins . gpio20 . downgrade ( ) ;
2024-01-17 21:25:01 +01:00
let driver = I2cDriver ::new ( i2c , sda , scl , & config ) . unwrap ( ) ;
2024-05-14 21:57:11 +02:00
2024-04-25 20:26:58 +02:00
let i2c_port = driver . port ( ) ;
2024-05-14 21:57:11 +02:00
let mut timeout : i32 = 0 ;
esp! ( unsafe { esp_idf_sys ::i2c_get_timeout ( i2c_port , & mut timeout ) } ) . unwrap ( ) ;
2024-05-01 18:06:20 +02:00
println! ( " init i2c timeout is {} " , timeout ) ;
//esp!(unsafe { esp_idf_sys::i2c_set_timeout(i2c_port, 22)}).unwrap();
2024-02-02 21:35:04 +01:00
let mut battery_driver : Bq34z100g1Driver < I2cDriver , Delay > = Bq34z100g1Driver {
i2c : driver ,
2024-07-31 20:45:06 +02:00
delay : Delay ::new ( 0 ) ,
2024-02-02 21:35:04 +01:00
flash_block_data : [ 0 ; 32 ] ,
} ;
2024-01-17 21:25:01 +01:00
2024-05-01 18:06:20 +02:00
let mut clock = PinDriver ::input_output ( peripherals . pins . gpio15 . downgrade ( ) ) ? ;
2024-02-02 21:35:04 +01:00
clock . set_pull ( Pull ::Floating ) . unwrap ( ) ;
2024-05-01 18:06:20 +02:00
let mut latch = PinDriver ::input_output ( peripherals . pins . gpio3 . downgrade ( ) ) ? ;
2024-02-02 21:35:04 +01:00
latch . set_pull ( Pull ::Floating ) . unwrap ( ) ;
2024-05-01 18:06:20 +02:00
let mut data = PinDriver ::input_output ( peripherals . pins . gpio23 . downgrade ( ) ) ? ;
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 ( ) ;
}
2024-04-20 23:32:44 +02:00
let mut one_wire_pin = PinDriver ::input_output_od ( peripherals . pins . gpio18 ) ? ;
2024-02-02 21:35:04 +01:00
one_wire_pin . set_pull ( Pull ::Floating ) . unwrap ( ) ;
2024-01-07 14:33:02 +01:00
2024-06-18 20:34:35 +02:00
//disable all
2024-07-31 20:45:06 +02:00
let ms0 = & mut shift_register . decompose ( ) [ MS_0 ] ;
ms0 . set_low ( ) ? ;
2024-07-05 19:54:42 +02:00
let ms1 = & mut shift_register . decompose ( ) [ MS_1 ] ;
ms1 . set_low ( ) ? ;
let ms2 = & mut shift_register . decompose ( ) [ MS_2 ] ;
ms2 . set_low ( ) ? ;
let ms3 = & mut shift_register . decompose ( ) [ MS_3 ] ;
ms3 . set_low ( ) ? ;
2024-08-11 18:44:24 +02:00
2024-06-18 20:34:35 +02:00
let ms4 = & mut shift_register . decompose ( ) [ MS_4 ] ;
2024-07-31 20:45:06 +02:00
ms4 . set_high ( ) ? ;
2024-08-11 18:44:24 +02:00
2024-01-07 14:33:02 +01:00
//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 ] ;
2024-03-27 21:10:37 +01:00
let utc_time = DateTime ::from_timestamp_millis ( smaller_time )
2024-02-15 23:00:05 +01:00
. ok_or ( anyhow! ( " could not convert timestamp " ) ) ? ;
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 ,
2024-04-20 23:32:44 +02:00
Some ( peripherals . pins . gpio22 ) ,
2024-01-07 14:33:02 +01:00
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-05-14 21:57:11 +02:00
let adc_config = AdcChannelConfig {
attenuation : attenuation ::DB_11 ,
2024-02-02 21:35:04 +01:00
resolution : esp_idf_hal ::adc ::config ::Resolution ::Resolution12Bit ,
2024-05-14 21:57:11 +02:00
calibration : true ,
2024-02-02 21:35:04 +01:00
} ;
2024-05-14 21:57:11 +02:00
let tank_driver = AdcDriver ::new ( peripherals . adc1 ) ? ;
let tank_channel : AdcChannelDriver < Gpio5 , AdcDriver < esp_idf_hal ::adc ::ADC1 > > =
AdcChannelDriver ::new ( tank_driver , peripherals . pins . gpio5 , & adc_config ) ? ;
2024-01-17 21:25:01 +01:00
2024-05-01 18:06:20 +02:00
let mut solar_is_day = PinDriver ::input ( peripherals . pins . gpio8 . downgrade ( ) ) ? ;
2024-01-17 21:25:01 +01:00
solar_is_day . set_pull ( Pull ::Floating ) ? ;
2024-05-01 18:06:20 +02:00
let mut boot_button = PinDriver ::input ( peripherals . pins . gpio9 . downgrade ( ) ) ? ;
2024-01-17 21:25:01 +01:00
boot_button . set_pull ( Pull ::Floating ) ? ;
2024-02-02 21:35:04 +01:00
2024-05-01 18:06:20 +02:00
let mut light = PinDriver ::input_output ( peripherals . pins . gpio10 . downgrade ( ) ) ? ;
2024-02-02 21:35:04 +01:00
light . set_pull ( Pull ::Floating ) . unwrap ( ) ;
2024-05-01 18:06:20 +02:00
let mut main_pump = PinDriver ::input_output ( peripherals . pins . gpio2 . downgrade ( ) ) ? ;
2024-01-17 21:25:01 +01:00
main_pump . set_pull ( Pull ::Floating ) ? ;
main_pump . set_low ( ) ? ;
2024-05-01 18:06:20 +02:00
let mut tank_power = PinDriver ::input_output ( peripherals . pins . gpio11 . downgrade ( ) ) ? ;
2024-01-17 21:25:01 +01:00
tank_power . set_pull ( Pull ::Floating ) ? ;
2024-05-01 18:06:20 +02:00
let mut general_fault = PinDriver ::input_output ( peripherals . pins . gpio6 . downgrade ( ) ) ? ;
2024-01-17 21:25:01 +01:00
general_fault . set_pull ( Pull ::Floating ) ? ;
general_fault . set_low ( ) ? ;
2024-07-05 19:54:42 +02:00
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-04-25 20:26:58 +02:00
let status = print_battery ( & mut battery_driver ) ;
if status . is_err ( ) {
println! ( " Error communicating with battery!! {:?} " , status . err ( ) ) ;
} else {
println! ( " Managed to comunnicate with battery " ) ;
}
2024-07-05 19:54:42 +02:00
2024-07-31 20:45:06 +02:00
let shift_register_enable_invert = PinDriver ::output ( peripherals . pins . gpio21 . downgrade ( ) ) ? ;
2024-07-05 19:54:42 +02:00
2024-01-07 14:33:02 +01:00
let rv = Mutex ::new ( PlantCtrlBoard {
shift_register ,
2024-07-05 19:54:42 +02:00
shift_register_enable_invert ,
2024-01-07 14:33:02 +01:00
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-04-25 20:26:58 +02:00
//battery_driver: None,
2024-05-14 21:57:11 +02:00
battery_driver : Some ( battery_driver ) ,
2024-01-07 14:33:02 +01:00
} ) ;
2024-07-31 20:45:06 +02:00
2024-08-11 18:44:24 +02:00
let _ = rv . lock ( ) . is_ok_and ( | mut board | {
unsafe { gpio_hold_dis ( board . shift_register_enable_invert . pin ( ) ) } ;
board . shift_register_enable_invert . set_low ( ) . unwrap ( ) ;
unsafe { gpio_hold_en ( board . shift_register_enable_invert . pin ( ) ) } ;
2024-07-31 20:45:06 +02:00
return true ;
} ) ;
2024-08-11 18:44:24 +02:00
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
}