## Plant Monitoring Project Ludvig Juelsson Larsen (lj223ed) ### Overview For my project I decided to create something that has been in my mind for some time, a device that monitors a plants environment, and to check this wirelessly from where ever you are. This projects contains a couple of sensors that monitors temperature, humidity, amount of water in the soil, and the amount of light the plant receives. My friend Pontus Folke and I worked on this project as a team, albeit he decided to use a different platform to send his data to, Ubidots, while I will be sending my data to PyBytes. The code will be pretty much identical, the only difference being how and where we send the data, the connecting of components will of course be the same as we use one set of hardware. #### Time The project, with guidance from this tutorial, will take approximately 3-5 hours depending on the amount of experience on the field. ### Objective The objective with this sort of device and project, where to get some insight in how we will be connecting our home to the internet and other devices. Always having access to data regarding our environment, monitoring our surroundings and making every day chores less cumbersome. By having a device like our's at the end of this tutorial will give you one less thing to think about, as you can easily check the data and know exactly how your plant is doing and monitor its environment from anywhere. With the data collection, you can make easier and better descisions regarding the location of your plant, as many plants are sensitive to direct sunlight or mabye they need a lot of watering. ### Materials & Components | Item: | From: | Price(kr, without shipping): | | ------------------------------ | ------------------------------------------------------------ | ---------------------------- | | 1. (fig 1) Pycom LoPy4 | [se.rs](https://se.rs-online.com/web/p/radio-frequency-development-kits/1628047?cm_mmc=SE-PLA-DS3A-_-google-_-PLA_SE_SE_Semiconductors_Whoop-_-(SE:Whoop!)+Radio+Frequency+Development+Kits-_-1628047&matchtype=&pla-323868530598&gclid=Cj0KCQjwoub3BRC6ARIsABGhnyYv5lA87SHpd4iYNz-dP4N0Uk0A0JiALQP1DvOGRP-9nj54YLgJvSgaAnY5EALw_wcB&gclsrc=aw.ds) | 443 | | 2. (fig 2) Expansionboard V3.1 | [m.nu](https://www.m.nu/pycom/expansion-board-31?gclid=Cj0KCQjwoub3BRC6ARIsABGhnybbZAhS18XPU3KXLwx1xvVMTd60_zFS6KOdmBvIcVlH-FkxDVK-Zg8aApdREALw_wcB) | 269 | | 3. (fig 3) Breadboard | [cdon.se](https://cdon.se/hem-tradgard/kopplingsplatta-breadboard-med-400-punkter-p36235354) | 34 | | 4. (fig 4) DHT11 sensor | [electrokit.com](https://www.electrokit.com/produkt/digital-temperatur-och-fuktsensor-dht11/) | 49 | | 5. (fig 5) Photoresistor | [conrad.se](https://www.conrad.se/p/iduino-1485310-ljusmotstand-arduino-1485310?utm_campaign=google+shopping+ads&utm_content=Cj0KCQjwoub3BRC6ARIsABGhnybvWzDDXJNjPjqxFTHxnQvcFc0Mf99zR1BZ5COcps2UbdA-BwP2reIaAvz6EALw_wcB%3B337%3B0&utm_medium=cpc&utm_source=shopello_se&utm_term=övrigt-13&utm_term=1485310&vat=true) | 42 | | 6. (fig 6) Soil Moist Sensor | [m.nu](https://www.m.nu/sensorer-matinstrument/soil-moisture-sensor) | 89 | | 7. (fig 7) Jumper Cables M/M | [electrokit.com](https://www.electrokit.com/produkt/labbsladdar-100mm-hane-hane-30-pack/) | 33 | | 8. (fig 8) Resistor 1 Kohm | [electrokit.com](https://www.electrokit.com/produkt/motstand-kolfilm-0-25w-1kohm-1k/) | 1 | #### What does the component do? 1. Pycom's LoPy4 is a device, programmable with MicroPython, which is perfect for our needs with several ways to connect to different wireless services. It has both digital and analog input and outputs, allowing a wide variety of sensors. With the Expansionboard V3.1 it is very easy to use with all the wiring (see fig 2). ![](https://i.imgur.com/59SOnoU.png =250x250)fig 1. LoPy4 2. To make the use of our LoPy4, we used an Expansionboard. This allows us to use all the pins of the LoPy with ease, as it is plug-and-play. This board might need some updating before we get started, which we will be covering later in the tutorial. ![](https://i.imgur.com/tpU0czU.jpg =300x)fig 2. Expansionboard V3.1 3. The breadboard is not really essential for the success of our project, but it will make things tremendously easier to connect and to understand the wiring that you will do. Remember, the breadboard only allows for serial connection row by row, not in columns. ![](https://i.imgur.com/DNjv104.jpg)fig 3. Breadboard 4. Measuring the temperature and moisture is done by the same sensor, soldered on to a small MCU, Micro-computer Unite (single chip computer), that also contains a resistor. Note, we had to use an external resistor as tha pin needs to be pulled up, i.e. high. ![](https://i.imgur.com/gyqGHvM.jpg =250x)fig 4. DHT11 Temperature and Moisture sensor 5. A photoresistor, also known as an LDR (Light Dependent Resistor), is an easy way to measure the amount of light a certain location is being exposed to. As light hits the resistor electrons are exited to the conductive strip, equaling in increased conductivity, that we can measure. It is a simple little device that works great as a light detector! The one we are using also includes an MCU. ![](https://i.imgur.com/0SPDEL5.jpg =220x)fig 5. Photoresistor on MCU 6. Next on our list is measuring the amount of moisture in the soil of the plant which is done by our soil moisture sensor. The sensor ships with an LM393 chip, allowing for both analog and digital output with adjustable digital sensitivity. The sensors functionality is quite trivial really, it simply reads the amount of conductivity there is between the two "teeth" which then can be read by our LoPy through the use of the LM393. ![](https://i.imgur.com/odV2MJv.jpg =280x)fig 6. Soil Moisture Sensor with LM393 chip 7.& 8. To connect our different components we need cables and resistors. For this project we only need male/male cables and two 1 Kohm resistors. Again, the resistors are most likely only needed in special cases such as our own. ![](https://i.imgur.com/Mu4vNQ7.jpg =200x)fig 7. Male/Male Jumper Cables ![](https://i.imgur.com/3H5Akid.png =250x)fig 8. 1 Kohm resistor ### Computer Setup For our project we choose to use the IDE **Atom**, as it resembles that of the Arduino IDE, which both of us where familiar with from before. The LoPy4 uses the language MicroPython, but before we start programming we need to get the hardware updated together with some plugin installations. #### Software After we have downloaded the Atom IDE, we need to install the Pymakr plugin so that we can flash and run the code. Once Atom is open, head over to Setting -> Install, search for pymakr and install it. This will add a REPL console to Atom that connects to our Pycom board, with this we can flash code to our LoPy4 as well as run commands directly on the LoPy4 and see outputs and different errors, if they where to occur. #### Hardware Now that we have the IDE we need to look at getting our hardware up to date and ready for programming. If **Windows 8/10/+** is used for this project, congratulations! Your Expansion Board is ready to go, but, if Windows 7 is used, we need to install drivers on our Expansion Board V3.1 in order to work. **Expansion Board:** Install this [driver](https://docs.pycom.io/gitbook/assets/pycom.inf) and save the file on your computer, and connect your Expansion Board with the USB cable. 1. Now open up device manager and find your device, it should be under *other devices*. 2. Right click your device and select *update driver software*. 3. Select the option *browse my computer for driver software* -> find the driver file that you downloaded. Note: if the file is ziped, the file need to be extracted beforehand. 4. A warning might appear, this can be ignored and we can select *install this driver software anyway*. 5. Now to confirm that the installation went as expected, head back to device manager and your device should now be visible. Now that the drivers of the board are all up to date, we need to follow pycom's example and update the firmware of the Expansion Board. Bellow is an easy-to-follow image from their [site](https://docs.pycom.io/pytrackpysense/installation/firmware/), but we will start with downloading the latest [firmware DFU file](https://software.pycom.io/findupgrade?key=expansion31.dfu&type=all&redirect=true). ![](https://i.imgur.com/DaZdYOA.jpg) **LoPy4 and PyBytes:** It is strongly recommended by Pycom to update your firmware to the latest version on your LoPy4 and this part will be dedicated to just that. This part will also make it available for us to send data to PyBytes. Firstly, we need a PyBytes account which can be made on their [site](https://pybytes.pycom.io) for free. 1. Head over to *Getting started* on PyBytes website -> *Configure networks* -> choose *add WiFi* -> put in your WiFi's *SSID* and *password* 2. Once finished, navigate back to *Getting started* -> *add a device* -> *LoPy4* -> *Enable WiFi* ->Enter a name of your liking -> choose what WiFi to use. You have now added a device! 3. This wouldn't be a true update if we didn't download yet another [tool](https://software.pycom.io/findupgrade?product=pycom-firmware-updater&type=all&platform=win32&redirect=true), luckily this is the last of the updates, I promise! When you have chosen your device in PyBytes, copy the *Activation Token* under *activate your device with firmware updater*. 4. Close Atom if you have it open and make sure that your device is plugged in. 5. Open your *pycom_firmware_update* tool 6. **Updating:** 1. Communication: 1. Port: ​ Choose Port. 2. Type: ​ pybytes 3. Leave all boxes checked **except** for *Flash from local file* 4. Press Continue 2. PyBytes registration: 1. Paste your copied *Activation Token* in the text field 2. Press Continue 3. Advanced settings: 1. Information: ​ Device type -> LoPy 4 2. Type/version: ​ pybytes -> 1.20.2.rc7 (or your latest version) 3. File system: ​ LittleFS -> check box to *Erase during update* 4. LoRa Region: Manual selection -> EU868 5. Press Continue 7. Result -> Done! You have now successfully updated your device, added it to PyBytes and in the process made it possible to send data from your device to PyBytes, brilliant! #### Using Atom Now that the hardware is setup and the pymakr plugin is installed we can connect our device to Atom. ![](https://i.imgur.com/bpuq8v6.jpg) 1. Choose your device. 2. This "toggle" will be green if your device connects properly. 3. If you have flashed code onto your device, it will execute your main file automatically, you can also simply press "play" to run the code. 4. This is used to upload, flash, your project to your device so it can run independently of your PC, including all libraries and other files. 5. If you feel the need to retrieve the code from your device, you can press "download from device". 6. "get device info" can be pressed to get information about your connected device e.g. software version, free memory etc. ### Putting everything together ![](https://i.imgur.com/ZFoPDBc.png) The pins we will be using in this project are ground (GND), 3V (3V3) and pins 18, 20, 23. These pins can of course be changed, though remember to change these in the code, also look at the datasheet for LoPy4 so no occupied pins are used. For data from our soil moisture sensor pin 18 is used, which is then connected to D0 on the LM393 chip. There is also an analog pin on that same chip, but we can ignore it. 3V3 will be connected to VCC and our GNG on our LoPy will be connected to GNG on the LM393. For our LDR we will use pin 20 to receive data, GNG on the LoPy will be connected to GNG on our sensor and lastly 3V3 will be connected to VCC on the sensor. For our last sensor we will connect GNG as we did on the other sensors. Though between our Data and VCC on the DHT11 sensor, we will need an external resistor of 1 Kohm. The reasons for this are still unknown but we suspect a broken or defect built in resistor on the DHT11 as this should not be needed. These components, preferably not defect ones of course, could all be used in production. Although I would like then soldered onto the same chip to minimize the number of separate parts being used. The wiring would be pretty much the same, though longer jumper cables might be needed to easier mount the different sensors. ### Platform The platform I choose was, as described earlier, PyBytes since it is easily compatible with our LoPy4. PyBytes gives a good graphical representation of the data that is received and is very user friendly, as creating more signals with matching graphs are only a few clicks away. One of the great advantages about PyBytes is that it is cloud based, meaning that wherever you are, you have access to your data from the LoPy4. PyBytes even has an app making the data even more accessible, all though we did not try this app out. As for now, we are using the free version for this project, although if it where to be expanded upon, we would most likely switch to a paid subscription. Since our project work wirelessly, we could technically build more like it, and send all this data to PyBytes as different devices named after different plants or rooms. If we where to scale our idea I would probably choose PyBytes because of its simplicity and ease of use with multiple devices. Since my partner chose Ubidots, I have tried both these platforms and I can say that I prefer PyBytes. All it took was some registration and a WiFi connection and you are good to go. Using Ubidots was a bit more of a hassle, since you had to download code and implement this into your project. As we imported the code later in the project it took probably 1-2 hours so get it working and sending data to both PyBytes and Ubidots. Had we implemented this earlier, I believe it would have been a much smoother transition. That being said, I feel as if they both are quite similar to one another. ### The code The different libraries needed are: 1. **dht.py**, imported from the web: ```python 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 ``` #### **Main:** This is the main code which we have written. ```python from network import WLAN import time from machine import Pin import machine import _thread from dht import DHT th = DHT(Pin('P23', mode = Pin.OPEN_DRAIN), 0) #set Pin 23 as an input for temperature/humidity adc1 = machine.ADC() adc2 = machine.ADC() inLumPin = adc1.channel(pin ='P20') inMoistPin = adc2.channel(pin = 'P18') #set 2 different pins as ADC channels for our light sensor and moist sensor def send_moist_data(): # function to receive & handle data from sensor, sends data while True: x = 0 Moist_sum = 0 while x < 100: # iterate the loop 100 times Moist_data = inMoistPin() Moist_sum += Moist_data # add new moist_data with previous x += 1 time.sleep(144.0) # makes sure to send data every 4 h if x == 99: # when x = 99, divide the sum with 100 to get average Moist_av = Moist_sum / 100 print("Median Soil Moist", Moist_av) # print average pybytes.send_signal(4, Moist_av) # send data to PyBytes break elif x < 99: continue # if the loop is not finished, continue time.sleep(0.1) _thread.start_new_thread(send_moist_data, ()) # keeps send_moist_data on a seperate thread def send_Lominous_data(): # function to receive & handle data from light sensor (photoresistor) while True: Lom_data = inLumPin() # reads from the inLumPin Pin if Lom_data < 400: # changes output depending on the signal print("Direct sunlight =", Lom_data) pybytes.send_signal(3, Lom_data) # send data to PyBytes elif Lom_data < 1000: print("Bright = ", Lom_data) pybytes.send_signal(3, Lom_data) elif Lom_data < 2800: print("Light = ", Lom_data) pybytes.send_signal(3, Lom_data) elif Lom_data < 3800: print("Dark = ", Lom_data) pybytes.send_signal(3, Lom_data) else: print("Very Dark = ", Lom_data) pybytes.send_signal(3, Lom_data) time.sleep(1800) # repeat every 30 minutes _thread.start_new_thread(send_Lominous_data, ()) # keeps send_Lominous_data on a seperate thread def send_TH_data(): # function to receive & handle temperature/humidity data while True: result = th.read() # reads assigned pin while not result.is_valid(): # tries after sleep to read data again time.sleep(.5) result = th.read() print('Temperature:', result.temperature) print('Relative Humidity:', result.humidity) pybytes.send_signal(1,result.temperature) # sends data to pycom pybytes.send_signal(2,result.humidity) # sends data to pycom time.sleep(1800) # repeat every 30 minutes _thread.start_new_thread(send_TH_data, ()) # keeps send_TH_data on a seperate thread ``` With the firmware from PyBytes a json file is included, this should have the same information as the PyByte's website regarding WiFi SSID and Password. ```python {"wifi": {"ssid": "ABCDEF_ABCDEF", "password": "12345678"} ``` #### Structure The structure of a project is more important than many might think. For this project, your structure should look something like this. ![](https://i.imgur.com/t0Rf5RS.jpg) Note: **urequests.py** is only used when we send data to Ubidots and is therefor not used by us. ### Transmitting the data / connectivity Right now the data is sent to PyBytes at 2 different times. 1. The moisture of my plant's soil goes down pretty slow so it only send data every 4 hours (As 1h = 3600 seconds -> 4h = 14400 seconds -> 14400 seconds/100 iterations = 144.0). 2. The temperature, humidity and level of light on the other hand might change a bit quicker, so I put that timer on 30 minutes, 1800 seconds. These timers can always be changed in the future if there is a need or desire to do so. Anytime we send data to our platform, the data is also saved in its database, in our case every 30 minutes for temperature/humidity and every 4 hours for the soil moisture. As mentioned before we strictly use WiFi as there where no need for any other wireless protocol since the hardware will always be at home or in an office. Therefor there is also no need to compress the data that we send since there is no issues with saving battery, because it will always be connected by USB. Also , we do not need to worry about the device's range, as it will always be in range of a router. Since we used PyBytes, MQTT is used in the background as a transport protocol for transporting our data. ### Presenting the data **Dashboard** Image of the dashboard on PyBytes website, viewing our 4 signals, temperatur, humidity, lominous and soil moist. ![](https://i.imgur.com/BegwRU2.jpg =850x) **Temperature** Image of the line chart from our temperature data, the graphs appearance might look drastic, although the sensor is only fluctuating between 25-26 degrees. ![](https://i.imgur.com/JTY369R.jpg) **Humidity** Here we can see our humidity close to the plant. Yet again the graph look pretty drastic but it fluctuates between only a few values. ![](https://i.imgur.com/l18RqtR.jpg) **Luminous**, level of light by the plant ![](https://i.imgur.com/WgV2sfH.jpg) **Soil moisture** Finally we have the soil moist sensor, where low values means freshly watered i.e. moist soil, and higher values means dryer soil. In the image bellow I had just watered the plant, resulting in some pretty low values. When it had stabilized I quickly moved the sensor to slightly dryer soil, which in return resulted in much higher values. ![](https://i.imgur.com/OjyQkv1.jpg) Here is another image of me watering my plant. The values dropped drastically and I could easily see the sensor working. ![](https://i.imgur.com/NPSZeJL.jpg) ### Finalizing the design ![](https://i.imgur.com/MipJ8hQ.jpg) ![](https://i.imgur.com/yoFWrND.jpg) In my opinion, the finished product might need some finishing touches, like a box to hold the LoPy and breadboard, with the sensors then easily placed where they do the most good. If there where some more time and we did not have other things on the side, we would like to expand this into an automatic plant watering device as there is quite an amount of cheap pumps on the internet. We also discussed the use of a small solar panel to power the device with the help of batteries. This would make the product more eco-friendly, cordless and self sufficient. This would require some tweaking in how many bytes we send since battery saving would then be a priority. Both me and Pontus really enjoyed the project and being able to do something we had thought of for a while, we might continue on this project later this summer!