11import machine
22import utime
3-
4- from utils import MQTTWriter , Slack , Ubidots , current_time , force_garbage_collect
3+ from utils import (
4+ MQTTWriter ,
5+ Slack ,
6+ Ubidots ,
7+ adc_map ,
8+ average ,
9+ current_time ,
10+ force_garbage_collect ,
11+ )
12+ from water_pump import WaterPump
513
614
715class MoistureSensor (object ):
8- def __init__ (self , adc_pin , config_dict ):
16+ def __init__ (self , config_dict ):
917 """
1018 Sensor calibration
1119 ######################
@@ -16,35 +24,55 @@ def __init__(self, adc_pin, config_dict):
1624 Expects a dict:
1725 config_dict = {"moisture_sensor_cal": {"dry": 841, "wet": 470}
1826 """
19- self .adc_pin = adc_pin
2027 self .config = config_dict
21- self .setup_adc
28+ self ._adc = None
29+ self ._mqtt = None
2230 self ._slack = None
23- self .ubidots = Ubidots (
24- self .config ["ubidots" ]["token" ], self .config ["ubidots" ]["device" ]
25- )
26- # self._mqtt = None
31+ self ._ubidots = None
32+ self ._water_me = False
33+ self ._water_pump = None
34+
35+ @property
36+ def ubidots (self ):
37+ if (self .config ["ubidots" ]["token" ]) and (not self ._ubidots ):
38+ self ._ubidots = Ubidots (
39+ self .config ["ubidots" ]["token" ], self .config ["ubidots" ]["device" ]
40+ )
41+ return self ._ubidots
42+
43+ @property
44+ def water_pump (self ):
45+ if (isinstance (self .config ["Pin_Config" ]["Water_Pump_Pin" ], int )) and (
46+ not self ._water_pump
47+ ):
48+ self ._water_pump = WaterPump (
49+ self .config ["Pin_Config" ]["Water_Pump_Pin" ],
50+ self .config ["water_pump_time" ]["delay_pump_on" ],
51+ )
52+ return self ._water_pump
2753
2854 @property
29- def setup_adc (self ):
30- self .adc = machine .ADC (self .adc_pin )
55+ def adc (self ):
56+ if (isinstance (self .config ["Pin_Config" ]["ADC_Pin" ], int )) and (not self ._adc ):
57+ self ._adc = machine .ADC (self .config ["Pin_Config" ]["ADC_Pin" ])
58+ return self ._adc
3159
3260 @property
3361 def slack (self ):
3462 """Slack message init"""
35- config = self .config ["slack_auth" ]
36- self ._slack = Slack (config ["app_id" ], config ["secret_id" ], config ["token" ])
63+ if (self .config ["slack_auth" ].get ("app_id" )) and (not self ._slack ):
64+ self ._slack = Slack (
65+ self .config ["slack_auth" ]["app_id" ],
66+ self .config ["slack_auth" ]["secret_id" ],
67+ self .config ["slack_auth" ]["token" ],
68+ )
3769 return self ._slack .slack_it
3870
39- # @property
40- # def mqtt(self):
41- # host = self.config["MQTT_config"]["Host"]
42- # self._mqtt = MQTTWriter(host)
43- # return self._mqtt
44-
45- def average (self , samples ):
46- ave = sum (samples , 0.0 ) / len (samples )
47- return ave if ave > 0 else 0
71+ @property
72+ def mqtt (self ):
73+ if (self .config ["MQTT_config" ].get ("Host" )) and (not self ._mqtt ):
74+ self ._mqtt = MQTTWriter (self .config ["MQTT_config" ]["Host" ])
75+ return self ._mqtt
4876
4977 def read_samples (self , n_samples = 10 , rate = 0.5 ):
5078 sampled_adc = []
@@ -54,69 +82,62 @@ def read_samples(self, n_samples=10, rate=0.5):
5482 force_garbage_collect ()
5583 return sampled_adc
5684
57- def adc_map (self , current_val , fromLow , fromHigh , toLow , toHigh ):
58- """
59- Re-maps a number from one range to another.
60- That is, a value of 'fromLow' would get mapped to 'toLow',
61- a value of 'fromHigh' to 'toHigh', values in-between to values in-between, etc.
62-
63- Does not constrain values to within the range, because out-of-range values are
64- sometimes intended and useful.
65-
66- y = adc_map(x, 1, 50, 50, 1);
67-
68- The function also handles negative numbers well, so that this example
69-
70- y = adc_map(x, 1, 50, 50, -100);
71-
72- is also valid and works well.
73-
74- The adc_map() function uses integer math so will not generate fractions,
75- when the math might indicate that it should do so.
76- Fractional remainders are truncated, and are not rounded or averaged.
77-
78- Parameters
79- ----------
80- value: the number to map.
81- fromLow: the lower bound of the value’s current range.
82- fromHigh: the upper bound of the value’s current range.
83- toLow: the lower bound of the value’s target range.
84- toHigh: the upper bound of the value’s target range.
85-
86- Adapted from https://www.arduino.cc/reference/en/language/functions/math/map/
87- """
88-
89- return (current_val - fromLow ) * (toHigh - toLow ) / (fromHigh - fromLow ) + toLow
85+ def message_send (self , msg ):
86+ try :
87+ print ("[INFO] Sending message to SLACK" )
88+ self .slack (msg )
89+ print ("[INFO] Message sent..." )
90+ except Exception as exc :
91+ print ("[ERROR] Could not send SLACK message: %s" % str (exc ))
92+ finally :
93+ print ("[INFO] %s" % msg )
9094
9195 def soil_sensor_check (self ):
9296 try :
9397 samples = self .read_samples ()
94- sampled_adc = self . average (samples )
95- SoilMoistPerc = self . adc_map (
98+ sampled_adc = average (samples )
99+ self . _soilmoistperc = adc_map (
96100 sampled_adc ,
97101 self .config ["moisture_sensor_cal" ]["dry" ],
98102 self .config ["moisture_sensor_cal" ]["wet" ],
99- 0 ,
100- 100 ,
101103 )
102- self .ubidots .post_request ({"soil_moisture" : SoilMoistPerc })
103- if SoilMoistPerc <= self .config ["moisture_sensor_cal" ].get ("Threshold" , 15 ):
104- msg = "Soil Moisture Sensor: %.2f%% \t %s" % (
105- SoilMoistPerc ,
106- current_time (),
104+ self .ubidots .post_request ({"soil_moisture" : self ._soilmoistperc })
105+ if self ._soilmoistperc <= self .config ["moisture_sensor_cal" ].get (
106+ "Threshold" , 50
107+ ):
108+ self ._water_me = True
109+ self .message_send (
110+ "[INFO] Soil Moisture Sensor: %.2f%% \t %s"
111+ % (self ._soilmoistperc , current_time ())
107112 )
108- self .slack (msg )
109- print (msg )
110- elif SoilMoistPerc <= 70 :
111- msg = "Soil Moisture is at 50% You should probably Water the plant."
112- self .slack (msg )
113- print (msg )
114- force_garbage_collect ()
113+ else :
114+ self ._water_me = False
115115 except Exception as exc :
116116 print ("Exception: %s" , exc )
117+ finally :
118+ force_garbage_collect ()
117119
118120 def run_timer (self , secs = 60 ):
121+ print ("[INFO] Timer Initialised, callback will be ran every %s seconds!!!" % secs )
119122 while True :
120123 self .soil_sensor_check ()
124+ while self ._water_me :
125+ self .message_send (
126+ "Note: Automatically watering the plant(s):\t %s" % current_time ()
127+ )
128+ if not self .water_pump .pump_status :
129+ self .water_pump .pump_on ()
130+ print (
131+ "[DEBUG] Setting Pump ON as water is @ %.2f%%"
132+ % self ._soilmoistperc
133+ )
134+ if self .water_pump .pump_status :
135+ print ("[DEBUG] Checking Soil Moisture Status..." )
136+ self .soil_sensor_check ()
137+ print ("[DEBUG] Soil Moisture Percent @ %.2f%%" % self ._soilmoistperc )
138+ self .water_pump .pump_off ()
139+ print (
140+ "[DEBUG] Setting Pump OFF as water is @ %.2f%%" % self ._soilmoistperc
141+ )
142+
121143 utime .sleep (secs )
122- print ("Timer Initialised, callback will be ran every %s seconds!!!" % secs )
0 commit comments