---
title: 'Automatic Watering'
disqus: hackmd
---
Tutorial: Automatic Watering
===
Author: Seth, sr223bt
## Table of Contents
[TOC]
## Summary
This tutorial describes how you can create your very own automatic watering system that checks the current weather (no reason to start watering if it is currently raining) and soil moisture and if the parameters are correct it will start watering.
Estimated time is about 1h (only assembly and no 3d printed case), please let me know in the comments if this is accurate.
## Objective
The reason behind this build are so that you do not have to manually start the watering of your vegetables, lawn or other outdoor application.
This project can be combined with a sprinkler system that pops up out from the grass. That kind of sprinkler uses water pressure to pop up, so just activating the water will activate the sprinkler system.
This IoT project also sends data to a dashboard so you could also monitor temperature and humidity in the air, like a small weather station.
Hopefully by doing this project you will learn how to set up a small weather station and use that data to make a conclution, example start the water.
## Bill of Materials
To be able to follow along this tutorial you need to purchase the following items:
- [LoPy4, 879kr](https://docs.pycom.io/datasheets/development/lopy4/) ++Can recommended the breakout board if you don't have it+++
- [DHT11/DHT22, 49kr](https://www.electrokit.com/produkt/digital-temperatur-och-fuktsensor-dht11/)
- [SeeSaw STEMMA, 115kr](https://www.electrokit.com/produkt/jordfuktighetssensor-kapacitiv-i2c/)
- [Water valve with solonoid, 114kr](https://www.electrokit.com/produkt/magnetventil-12v-1-2/) ++There are different versions, pick one that suits your project++
- [5V Relay, 39kr](https://www.electrokit.com/produkt/relamodul-5v/)
- [Step-down, Voltage regulator, 100kr](https://www.kjell.com/se/produkter/el-verktyg/arduino/stromforsorjning/variabel-spanningsregulator-switchad-p87049)
- 12V DC from walloutlet ++I had one laying around, so unknown what that costs++
- Some wires to connect everything
- Might need a soldering iron, depending on connections of the things you bought, I needed to solder on the wires on the water valve
Total: 1300kr or 130 dollar/euro.
> I am swedish so all prices are in kr, to get dollar or euro divide by 10, there are others that sell similar items.
## External libraries
## How To Guide
If you want to use other pins than in this guide you will need to change them in the code.
If you use a DHT22 instead of a DHT11 you have to change the code aswell.
### Construction
Assemble the items accordingto the schematic
#### Schematic

#### Step 1, DHT11:
- Connect 3V3 to VCC on DHT11, middle pin.
- Connect Ground to Ground on DHT11, right of VCC.
- Connect P22 to Signal on DHT11, left of VCC.
#### Step 2, Relay in:
- Connect 3V3 to VCC on Relay, middle pin.
- Connect Ground to Ground on Relay, right of VCC.
- Connect P21 to Signal on Relay, left of VCC.
#### Step 3, Relay out:
To be able to controll a object that requires another voltage arelay is used.
For this connect the VCC from your power source this case a 12V DC to the middle therminal on the Relay.
Second connect the Ground to your power source to the solonoids ground.
Then connect the right most terminal, red in schematic, to the VCC on the solonoid.
> Side note: To check if you connected it correctly you could, as a test, connect the VCC on the Solonoid to the VCC on your power source. You should now be able to blow in the valve and air will pass through it, it will not pass through if you remove the VCC.
#### Step 4, Seesaw:
- Connect the Ground to the Ground on the Seesaw.
- Connect the 3V3 to VCC on the Seesaw.
- Connect P9, default SDA, to SDA on Seesaw.
- Connect P10, default SCL, to SCL on the Seesaw.
#### Step 5, Voltage regulator
- Connect another cable from the VCC on the 12V to the In+ on the voltage regulator.
- Connect another cable from ground on the 12V to the In- on the voltage regulator.
- IMPORTANT: Check that the voltage regulator outputs 3-5V before connecting it to the LoPy.
- Connect the Out+ on the voltage regulator to the VIN on the LoPy.
- Connect Out- to the same ground as the rest of the system.
### Computer setup and code
For this project I used Atom with the PyBytes extension installed.
[Start with flashing the firmware on the board by using these settings.](https://docs.pycom.io/pytrackpysense/installation/firmware/)
Everything runs on PyBytes and uploads data to PyBytes.
To transmit data we are using WiFi and are setup using PyBytes webpage and the data is transmitted every 5 minutes, setup to 5 minutes so that the water is checked every 5 minutes, this could be improved by checking if the water is on and if it is not on sleep for longer.
#### Presentation of data

In the code I try and minimize the data sent so that we do not send to much data, it could be expensive if we decide to change from WiFi to some other media like cellular network, when every byte costs.
#### Code
Code that is used in the project consists of a folder named lib that contains dht.py and seesaw.py and then a main.py file.
```python
#main.py
import time
from machine import Pin
import _thread
from dht import DHT
from seesaw import Seesaw
_MOISTURE_THRESHOLD = const(930) #should be around 1000. 2000 = very wet
_MOISTURE_THRESHOLD_DONE = const(980)
_HUMIDITY_THRESHOLD = const(60)
def Calculate_Mean_Value():
sum = 0
for val in range(10):
sum += seesaw.moisture_read()
time.sleep(2)
return sum / 10
th = DHT('P22', 0)
water = Pin('P21', mode=Pin.OUT)
seesaw = Seesaw(0, 0x36)
while True:
result = th.read()
moisture = Calculate_Mean_Value()
print('Moisture:', moisture, ' format:', '{:.0f}'.format(moisture))
pybytes.send_signal(3, '{:.0f}'.format(moisture))
if result.is_valid():
if result.humidity < _HUMIDITY_THRESHOLD and moisture <= _MOISTURE_THRESHOLD:
water(1)
pybytes.send_signal(4, bin(1))
print('Temp:', result.temperature)
print('RH:', result.humidity)
pybytes.send_signal(1, '{:.0f}'.format(result.temperature))
pybytes.send_signal(2, '{:.0f}'.format(result.humidity))
if moisture > (_MOISTURE_THRESHOLD_DONE):
water(0)
pybytes.send_signal(4, bin(0))
time.sleep(60*5) #5 minutes
#time.sleep(2) #for debugging purposes
```
```python
#lib/dht.py
import time
import pycom
from machine import enable_irq, disable_irq, Pin
class DHTResult:
'DHT sensor result returned by DHT.read() method'
ERR_NO_ERROR = 0
ERR_MISSING_DATA = 1
ERR_CRC = 2
error_code = ERR_NO_ERROR
temperature = -1
humidity = -1
def __init__(self, error_code, temperature, humidity):
self.error_code = error_code
self.temperature = temperature
self.humidity = humidity
def is_valid(self):
return self.error_code == DHTResult.ERR_NO_ERROR
class DHT:
'DHT sensor (dht11, dht21,dht22) reader class for Pycom'
#__pin = Pin('P3', mode=Pin.OPEN_DRAIN)
__dhttype = 0
def __init__(self, pin, sensor=0):
self.__pin = Pin(pin, mode=Pin.OPEN_DRAIN)
self.__dhttype = sensor
self.__pin(1)
time.sleep(1.0)
def read(self):
# pull down to low
self.__send_and_sleep(0, 0.019)
data = pycom.pulses_get(self.__pin,100)
self.__pin.init(Pin.OPEN_DRAIN)
self.__pin(1)
#print(data)
bits = []
for a,b in data:
if a ==1 and 18 <= b <= 28:
bits.append(0)
if a ==1 and 65 <= b <= 75:
bits.append(1)
#print("longueur bits : %d " % len(bits))
if len(bits) != 40:
return DHTResult(DHTResult.ERR_MISSING_DATA, 0, 0)
#print(bits)
# we have the bits, calculate bytes
the_bytes = self.__bits_to_bytes(bits)
# calculate checksum and check
checksum = self.__calculate_checksum(the_bytes)
if the_bytes[4] != checksum:
return DHTResult(DHTResult.ERR_CRC, 0, 0)
# ok, we have valid data, return it
[int_rh, dec_rh, int_t, dec_t, csum] = the_bytes
if self.__dhttype==0: #dht11
rh = int_rh #dht11 20% ~ 90%
t = int_t #dht11 0..50°C
else: #dht21,dht22
rh = ((int_rh * 256) + dec_rh)/10
t = (((int_t & 0x7F) * 256) + dec_t)/10
if (int_t & 0x80) > 0:
t *= -1
return DHTResult(DHTResult.ERR_NO_ERROR, t, rh)
def __send_and_sleep(self, output, mysleep):
self.__pin(output)
time.sleep(mysleep)
def __bits_to_bytes(self, bits):
the_bytes = []
byte = 0
for i in range(0, len(bits)):
byte = byte << 1
if (bits[i]):
byte = byte | 1
else:
byte = byte | 0
if ((i + 1) % 8 == 0):
the_bytes.append(byte)
byte = 0
#print(the_bytes)
return the_bytes
def __calculate_checksum(self, the_bytes):
return the_bytes[0] + the_bytes[1] + the_bytes[2] + the_bytes[3] & 255
```
```python
#lib/seesaw.py
# The MIT License (MIT)
#
# Copyright (c) 2017 Dean Miller for Adafruit Industries
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
"""
`seesaw`
====================================================
Based on Adafruit seesaw library
An I2C to whatever helper chip.
* Author(s): Dean Miller (origin), modified by: Seth Ramström
"""
import time
from machine import I2C
import struct
_STATUS_BASE = const(0x00)
_TOUCH_BASE = const(0x0F)
_STATUS_HW_ID = const(0x01)
_STATUS_TEMP = const(0x04)
_STATUS_SWRST = const(0x7F)
_TOUCH_CHANNEL_OFFSET = const(0x10)
_HW_ID_CODE = const(0x55)
class Seesaw:
address = 0x36
def __init__(self, i2c_bus, addr=0x36, drdy=None):
self._drdy = drdy
if drdy is not None:
drdy.switch_to_input()
self.i2c_device = I2C(i2c_bus, I2C.MASTER)
address = addr
print("start scan")
devices = self.i2c_device.scan()
for device in devices:
print("device addr:", device, " hex:", hex(device))
print("End scan")
self.sw_reset()
def sw_reset(self):
"""Trigger a software reset of the SeeSaw chip"""
self.write8(_STATUS_BASE, _STATUS_SWRST, 0xFF)
time.sleep(0.500)
chip_id = self.read8(_STATUS_BASE, _STATUS_HW_ID)
if chip_id != _HW_ID_CODE:
raise RuntimeError(
"Seesaw hardware ID returned (0x{:x}) is not "
"correct! Expected 0x{:x}. Please check your wiring.".format(
chip_id, _HW_ID_CODE
)
)
def moisture_read(self):
"""Read the value of the moisture sensor"""
buf = bytearray(2)
self.read(_TOUCH_BASE, _TOUCH_CHANNEL_OFFSET, buf, 0.005)
ret = struct.unpack(">H", buf)[0]
time.sleep(0.001)
# retry if reading was bad
count = 0
while ret > 4095:
self.read(_TOUCH_BASE, _TOUCH_CHANNEL_OFFSET, buf, 0.005)
ret = struct.unpack(">H", buf)[0]
time.sleep(0.001)
count += 1
if count > 3:
raise RuntimeError("Could not get a valid moisture reading.")
return ret
def get_temp(self):
"""Read the temperature"""
buf = bytearray(4)
self.read(_STATUS_BASE, _STATUS_TEMP, buf, 0.005)
buf[0] = buf[0] & 0x3F
ret = struct.unpack(">I", buf)[0]
return 0.00001525878 * ret
def write8(self, reg_base, reg, value):
"""Write an arbitrary I2C byte register on the device"""
self.write(reg_base, reg, bytearray([value]))
def read8(self, reg_base, reg):
"""Read an arbitrary I2C byte register on the device"""
ret = bytearray(1)
self.read(reg_base, reg, ret)
return ret[0]
def read(self, reg_base, reg, buf, delay=0.008):
"""Read an arbitrary I2C register range on the device"""
self.write(reg_base, reg)
if self._drdy is not None:
while self._drdy.value is False:
pass
else:
time.sleep(delay)
self.i2c_device.readfrom_into(self.address, buf)
def write(self, reg_base, reg, buf=None):
"""Write an arbitrary I2C register range on the device"""
full_buffer = bytearray([reg_base, reg])
if buf is not None:
full_buffer += buf
if self._drdy is not None:
while self._drdy.value is False:
pass
self.i2c_device.writeto(self.address, full_buffer)
```
## Final Words
This project was great fun and I have wanted to do this project for some time now. It went on smothely, the only issue is that it is hard to be able to think ahead of what components you might need.
This project can always be improved with more sensors to check if there is anyone on the yard or check the time and only be active during the night.
There is also alot of ports left on the LoPy to connect some other outdoor project.
## Appendix and FAQ
:::info
**Find this document incomplete?** Leave a comment!
:::
###### tags: `Templates` `Documentation` `Automatic` `Watering`