Skip to content

Commit 08e0e7c

Browse files
authored
Automatic Water Pump [feature] (#1)
* More Logging and Bug fixes Added more logging and bug fixes related to the water pump Signed-off-by: Mpho Mphego <[email protected]>
1 parent ec4c660 commit 08e0e7c

File tree

6 files changed

+206
-95
lines changed

6 files changed

+206
-95
lines changed

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ FILES=boot.py \
3434
config.json \
3535
main.py \
3636
soil_moisture.py \
37+
water_pump.py \
3738
utils.py
3839

3940
erase:

config.json

+9-2
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,16 @@
1515
"wet": 470,
1616
"Threshold": 70,
1717
},
18+
"water_pump_time": {
19+
"": 2
20+
},
1821
"slack_auth": {
19-
"app_id":"",
22+
"app_id": "",
2023
"secret_id": "",
21-
"token":""
24+
"token": ""
25+
},
26+
"ubidots":{
27+
"token": "",
28+
"device": ""
2229
}
2330
}

main.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
if __name__ == "__main__":
55
filename = "config.json"
66
config = read_config(filename)
7-
moisture_Sensor = MoistureSensor(0, config)
7+
moisture_Sensor = MoistureSensor(config)
8+
print("[INFO] Running moisture_Sensor...")
89
moisture_Sensor.run_timer(900)

soil_moisture.py

+93-72
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,19 @@
11
import machine
22
import 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

715
class 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

Comments
 (0)