# How to build a smart plant sensor with LoRa
###### tags: `Tutorial` `LoRa` `TTN` `Datacake` `IoT` `Plants` `Pycom`
> **Author:** Andreas Sjövall
> **Student ID:** as226zg
> **Date:** 22 Jul 2021
:watch: **Estimated time:** 3 hours
In this quick tutorial we're going to build a simple but smart plant sensor that can measure soil moisture and send the data over a LoRa network (TTN), which we will then finally store and display in a neat looking graph using the IoT platform Datacake.
:::warning
:warning: **Disclaimer:** Since we're going to utilize LoRa networks in this project, it would be a good idea to check the coverage in your area before proceeding. I've had some difficulties myself even from a major city in Sweden.
[**TTN coverage map**](https://www.thethingsnetwork.org/map)
[**Helium coverage map**](https://explorer.helium.com/)
:::
[ToC]
## Objective
We all know that plants can be quite difficult to deal with sometimes, (especially for those who don't have green fingers!). Sometimes they're too dry, sometimes too wet, and more than often it's kind of hard to tell what's actually going on. There must be an easier way to deal with this!
In this quick tutorial we're going to construct a simple, (yet very useful) plant sensor that can measure and transfer soil moisture data over a LoRa network that we'll display in a neat looking graph to top it off. The main objective here is to explore the capabilities of LoRaWAN together with IoT platforms that will enable us to send and render data efficiently without too much effort.
My hope is that this project will act as a good foundation for any type of plant sensor or monitoring system that can be used off the grid using LoRaWAN.
I'm very much learning this as we go through this journey together, and I hope you're as excited as I am! Let's get started! :sunglasses:
## Material
Before we can start jumping into this project, you need to go and get some basic components to work with. I've listed everything that's needed in the table below, but note that the retailer links are just suggestions.
| Device | Retailer | Description | Cost
| ----------------- |------------ |-----|---|
| LoPy4 | [Pycom Webshop](https://pycom.io/product/lopy4/) | An excellent development board with lots of options for network connections (LoRa, SigFox, WiFi). Programmed with MicroPython. | €38.45
| Expansion board 3.0 | [Pycom Webshop](https://pycom.io/product/expansion-board-3-0/) | A handy expansion board with GPIO ports, MicroSD card slot, LiPo battery charger and more. | €17.60
| LoRa & SigFox Antenna | [Pycom webshop](https://pycom.io/product/lora-868mhz-915mhz-sigfox-antenna-kit/) | Antenna for LoRa. :warning: Using LoRa without the antenna could potentially damage the board! | €9.00
| Soil sensor* | [Electrokit](https://www.electrokit.com/en/product/soil-hygrometer-module/) | A sensor that can measure soil moisture | ~€3.00
| Jumper wires M/F | [Electrokit](https://www.electrokit.com/en/product/jumper-wires-1-pin-male-female-150mm-10-pack/) | Cables to connect to sensor | ~€3.00
| Micro USB cable | [Electrokit](https://www.electrokit.com/produkt/usb-kabel-a-hane-micro-b-5p-hane-1-8m/) | Cable to connect the LoPy4 to a PC | ~4.00
| A plant | Any florist? | A plant to test our masterpiece! | N/A
:::warning
:warning: **\*Disclaimer:** The soil sensor used in this project is pretty low quality and should not be used in a production environment since it degrades over time. There are better options for this such as this [capacitive soil sensor](https://www.electrokit.com/en/product/jordfuktighetssensor-kapacitiv-i2c/).
:::
## Computer setup
### Step 1: Pick your IDE of choice
Since we're going do develop our software in MicroPython we'll be using the very handy Pymakr plugin which is currently supported in the two IDE's VS Code and Atom. Both editors are excellent choices and it's merely a matter of preference, although I will be using VS Code in this tutorial since I'm more used to it.
[:link: Link to download Atom](https://atom.io/)
[:link: Link to download VS Code](https://code.visualstudio.com/download) :arrow_left: (what I will be using)
### Step 2: Install PyMakr plugin
Open VS Code and head to the extensions tab (shortcut `Ctrl` `Shift` `X`) and search for Pymakr and install it. Now restart the IDE.
### Step 3: Prepare project structure
Create a new project folder (e.g `plant-sensor`) and open it in VS Code. Now in the root of your project folder, create a new file for the main code called `main.py`, as well as a `lib` folder that can hold any libraries.
### Step 4: Connect your Pycom device
Connect your LoPy4 via USB and press `Upload` located down in the blue panel at the bottom. If everything went well, you're good to go! :rocket:
## Putting everything together
Now it's time to connect everything together! Since we're only using one sensor here, it's a very straight forward setup and we don't even need a breadboard (unless you prefer to use one anyway). One thing to note is that we're not running the sensor directly from the **3.3V** port, but rather from a GPIO pin (**P20**). This is so that we can control the duration that the sensor is powered on in order to reduce corrosion.
Don't worry about pin **D0** on the sensor board - it's only used in digital mode which we won't be using in this case.
This is only a prototype setup. If you'd like to use this in a production environment, I'd suggest that you replace the expansion board with a custom PCB where the sensor board can be mounted and connected.

## Platform
When it comes to storing and displaying our very important plant data, there are pretty much endless possibilities on how to achieve this. One could create a completely custom application and manage everything from development to hosting, or opt in for a more plug and play solution. For this case, (and for many other use cases) all we need is a couple of graphs to visualize the soil moisture, which can easily be handled by IoT platforms such as **Pybytes**, **Ubidots** or **Datacake** just to name a few. These platforms are generally easy to use and require very little or no coding at all to get started.
Since we're using a Pycom device, Pybytes is a very straight forward choice for many use cases since it's very well integrated and easy to use. However, other platforms such as Datacake does offer some more options for customizability and scalability which I think is worth exploring, hence the reason why I choose this over Pybytes. Ubidots is another option that I have yet to explore.
## The code
### boot.py
Code in the boot.py is executed during boot which makes it very appropriate for configuring and initializing our LoRa connection. The main thing that you have to worry about here is the `app_key`, which is your personal key to connect to to TTN. :warning:**Do not expose this publicly!**
If you happen to be in a different region than Europe, make sure to change the `region` option.
```python=0
from network import LoRa
import time
import binascii
lora = LoRa(mode=LoRa.LORAWAN, region=LoRa.EU868)
app_eui = binascii.unhexlify('0000000000000000')
app_key = binascii.unhexlify(YOUR_APP_KEY)
lora.join(activation=LoRa.OTAA, auth=(app_eui, app_key), timeout=0)
# wait until the module has joined the network
while not lora.has_joined():
time.sleep(2.5)
print('Not joined yet...')
print('Network joined!')
# Your old code from main.py should be here
```
### main.py
This code is executed during normal operation and takes care of initializing and reading sensors, as well as sending the values to our TTN application. Since the analog values coming from the soil sensors are kind of arbitrary and non-intuitive, I've made a function ``valueToPercent()`` that maps an analog value to a percentage (0-100%). The one parameter that you might want to play around with is the ``min`` value in the conversion as this should be calibrated when the sensor is fully submerged in water. Also note that we're "inverting" the value so that a higher value corresponds to a higher moisture level and not the other way around.
Like I mentioned before, the sensor used here is prone to corrosion during prolonged periods of use, which is the reason why we only turn it on for a very short period while measuring in order to extend its lifetime.
```python=0
from machine import Pin, ADC
import time
import socket
# Convert analog value (min <-> max) to percentage (0 <-> 100)
def valueToPercent(value, min, max):
if value <= min:
return 0
elif value >= max:
return 100
return round(((value - min) * 100) / (max - min))
# Configure socket for TTN
s = socket.socket(socket.AF_LORA, socket.SOCK_RAW)
s.setsockopt(socket.SOL_LORA, socket.SO_DR, 5)
s.setblocking(False)
adc = ADC(bits=9)
soilValuePin = adc.channel(
pin = Pin('P16', mode=Pin.IN),
attn = ADC.ATTN_11DB # Make input range 0 <-> 3.3V.
)
soilPowerPin = Pin('P20', mode=Pin.OUT)
while True:
# Enable sensor for short duration to avoid corrosion
soilPowerPin.value(1)
time.sleep_ms(200)
# Min value based on full water submersion, max based on ADC (9 bits)
soilMoisture = abs(valueToPercent(soilValuePin(), 180, 511) - 100)
s.send(bytes([soilMoisture]))
print("Moisture: ", soilMoisture)
soilPowerPin.value(0)
time.sleep(60 * 10)
```
## Transmitting the data & connectivity
With this current setup, one byte of data is being transmitted over [**The Things Network**](https://www.thethingsnetwork.org/) (**TTN**) with **LoRaWAN** every 15 minutes and then forwarded to [**Datacake**](https://datacake.co/) for storage and display. In practice, there's no reason why this device couldn't just use a standard WiFi connection instead since it's mainly going to be used within close proximity to a router. However, I wanted this project to be a bit of a trial of LoRa since it offers so many possibilities for IoT devices in general. It provides longer range and is much more power efficient which would enable you to use this device completely off the grid far away from your home router if you so wish.
TTN is however not the only option when it comes to open LoRa networks. I've also tried [**Helium**](https://www.helium.com/) which is another great network that's growing very rapidly at the moment. Helium is however not completely free as you pay based on the amount of transmitted data, but it's still very cheap. In my case however, the coverage wasn't really that great, so I ended up using TTN instead. The limited coverage in some locations is definitely a big drawback of this technology at the moment, but is something that will most likely improve drastically in the near future as the networks grows bigger.
### Configure TTN
To configure TTN, you first need to create an account and then make an application in the `Console` where you'll register the LoPy4 using its `DevEUI`. Finally, you will have to include the generated `AppKey` from the TTN application in `main.py`.
For a more in depth step by step tutorial, have a look [here](https://hackmd.io/5RbTAtCxTPu-hRi3k4p3dQ?view).
### Configure Datacake
In order to connect TTN to Datacake, we'll be using a webhook. To create a webhook, go to `Console` in TTN and navigate to `Integrations -> Webhooks`. Create a new webhook with the Datacake template and provide the API token which can be found under `Account settings -> API` in your Datacake account.
Back in Datacake, create a new custom device under `Devices` with TTN as provider and use the `DevEUI` from your LoPy. We're also going to create a payload decoder under `Your Device -> Downlinks` where we can parse and and pass the data to a field that can be referenced through graphs and charts.
```javascript=0
function Decoder(payload, port) {
return [
{
field: "SOIL_MOISTURE",
value: payload[0]
}
];
}
```
Since we're only concerned about one type of data, we're not even going to worry about ports in the encoder. The `SOIL_MOISTURE` field is an integer that will hold the actual value that can be rendered in graphs and charts in the dashboard. Fields can be created under `Your Device -> Configuration`.
For a more in depth tutorial, have a look [here](https://www.thethingsindustries.com/docs/integrations/cloud-integrations/datacake/).
## Presenting the data
Let's get ready for the final and most exciting step - to display the actual data!
Add a new dashboard in Datacake with a widget that you like (I'm going to use the `Chart` widget). While configuring the widget, go to the `Devices` tab and pick you device as well as the field that we created for the data.
And voila! We now have a graph displaying our soil moisture that is being updated and saved every 15 minutes!

## Finalizing the desgin
To summarize our achievements, we've learned how to build a smart soil sensor that can:
- Measure the soil moisture of a plant
- Send the data over a LoRa network (TTN) using LoRaWAN
- Display and store the data on an IoT platform (Datacake) using webhooks
To top things off I built a neat little case out of the box that the LoPy4 came with.

Now when we've built a solid foundation to continue working on, the sensor can be further improved in many ways such as:
- Including more sensors like temperature and humidity
- Creating algorithms for when to water the plant, possibly based on machine learning
- Make it customizable for different types of plants with different needs
- Connect it to an irrigation system for full automation
Thanks for reading and I hope you enjoyed this project as much as I did! :smile: