---
# System prepended metadata

title: Connected soil moisture sensor
tags: [es226ed, Elias Sjölin]

---

## Overview
In this project I will build a soil moisture sensor that sends an alert when plants need watering.        
This project should take no more than two days excluding waiting for materials and figuring everything out.

## Objective
I chose this project because I want to make sure that our tomatoes succeed this season.        
Thanks to this project I expect us not to miss a single day of watering.        
It should provide insight into:

* Keeping non waterproofed sensors in a wet environment
* Calibrating analog signals
* The perils of buying cheap sensors
* Working with the MQTT-protocol

## Material



| Thing |  Does | Costs |At|
|:-------- |:-------- |:-------- |:-|
| Raspberry Pico W     |  Microcontroller     | 109sek     | electrokit.com
|Breadbord|Makes connections easier|59sek|electrokit.com
|Wires^[Amount depending on your setup]|Wiring|49sek|electrokit.com
|Soil moisture sensor^[https://www.youtube.com/watch?v=IGP38bz-K48&t=12s]|Soil moisture sensing|25sek/pc|amazon.se
|Shrink tube|Waterproofing|100sek|Jula
Nail polish|Waterproofing|0sek|Stolen from partner
|Glue|Enlarge shrink tube since you bought too small ones|0sek|Found in wardrobe
|Rubber cement|See above|See above|See above

I have chosen to not include the specific seller for the moisture sensors i chose as their sensors were a "budget version" and not able to run at the specified 3V.

## Computer setup

### Flashing micropython
1. Download the latest micropython drivers from [here](https://www.raspberrypi.com/documentation/microcontrollers/micropython.html)
2. Connect your raspberry pico W to your computer while holding the BOOTSEL button. A device called RPI-RP2 will appear as a mass storage device.
3. Drag and drop the UF2-file from step 1 into RPI-RP2.
4. DONE

### Setting up an IDE
I used VScode since it's shiny and I'm (slightly) familiar with it. Thonny is probably a better choice for beginners (me) at these types of projects.

1. Download VScode
2. Download and install node.js (pymakr will not work without this)
3. Install pymakr from the extensions tab in VScode
4. Create a new project in the pymakr tab.
5. Connect your device and click "open terminal". Test it by typing print("lkfjkfpf")
6. Put the project on development mode which will cause files to be uploaded and the device to be restarted each time you save a file. (Can also be done manually from the pymakr tab)
7. Create main.py and boot.py, save them and make sure that you're able to load them to the device.

## Wiring

![](https://hackmd.io/_uploads/rJD-wtZKn.png)
![](https://hackmd.io/_uploads/rkrD7gGK3.jpg)

The basic circuit I used can be seen above. I connected 2 moisture sensors to GP27 and GP28 which are both ADC pins. They both run off the VBUS pin and have separate ground pins.

## Sensor "customization"
The sensors I used were purchased from amazon. However, according to [this video](https://www.youtube.com/watch?v=IGP38bz-K48&t=12s), it is common for these to not work as specified. Mine looked like they had every problem in the video. However, I was lucky enough to be able to run them at 5V without any other issues.

Another "issue" with these is that they are not waterproof. I solved this using shrink tubing combined with a mix of rubber cement, super glue and nail polish. All electronics on the sensor were covered in tubing and the edge was sealed using the nail polish.        
I would recommend removing the bulky connector on the sensor and soldering a wire directly to it. I was not able to do so and therefore had to glue two shrink tubes together lengthwise in order to fit them.        
I connected the sensors to the board using jumper wires with shrink tubing at each connection since I did not have a longer wire at hand.

<img src="https://hackmd.io/_uploads/S1hwCIWtn.jpg" width="200" height="250" align="left"/> <img src="https://hackmd.io/_uploads/Hk2PR8ZK3.jpg" width="200" height="250" align="right"/>




## Platform

I used adafruit.io and IFTTT.        
Measurements are first transmitted to adafruit using MQTT and visualized on a dashboard. IFTTT reads the values in the adafruit feeds and alerts me via their app if moisture drops below a specified value. If I ever add more apps/sensors to IFTTT I will have to look at other solutions and/or get a paid subscription.

## Code

<details>
    <summary><b>Code for reading sensors</b>
    </summary>

```python=
import machine
import time

def readmoisture(p):
    a = machine.ADC(machine.Pin(p))
    wettest = 10226 # Values acquired using calibrate_moisture
    driest = 65535 # Values acquired using calibrate_moisture
    L = []
    for i in range(10): # Read 10 values 0.1s apart
        L.append(a.read_u16())
        time.sleep(0.1)
    val = sum(L) / 10 # Take average of values
    return (1 - ((val - wettest) / (driest - wettest))) * 100 # Return the wet %

def calibrate_moisture(p):
    a = machine.ADC(machine.Pin(p))
    L = []
    for i in range(100): # Read 100 values over 10 seconds
        L.append(a.read_u16())
        time.sleep(0.1)
    return min(L) # Return the smallest value
```
    
</details>

The code for reading a sensor can be summarized as:
1. Define ```a``` as an analog to digital conversion (ADC) pin on pin ```p```
2. Define the range from completely wet to completely dry
3. Read 10 values over 1s and calculate the mean reading
4. The formula ```((val - wettest) / (driest - wettest))``` will return 0 when the sensor reading = ```wettest``` and 1 when reading = ```driest```. Since this is not very intuitive I reverse this by subtracting it from 1 and multiplying by 100 in order to get something resembling %moist.

The ```driest``` value is easily found by holding the sensor in the air and reading from it. The ```wettest``` value was obtained by submerging it in water and using the smallest of 100 values read over 10 seconds using the ```calibrate_moisture(p)``` function.

<details>
    <summary><b>Data transmit code</b></summary>

```python=
import machine
from umqtt.simple import MQTTClient
import ubinascii
import time
import readsensor
from hemligheter import hemlisar # Username, password etc
import network
import boot # Holds wifi connection functions

led = machine.Pin("LED", machine.Pin.OUT)
    
# WiFi Connection
try:
    ip = boot.do_connect()
except KeyboardInterrupt:
    print("Keyboard interrupt")

print("Zzzzz...")
time.sleep(10)

mqtt_host = "io.adafruit.com"
mqtt_username = hemlisar["mqttusr"]  #Adafruit IO username
mqtt_password = hemlisar["mqttpass"]  # Adafruit IO Key
mqtt_publish_fukt1 = "eliasss/feeds/temperaturgruppen.fuktighet-1"
mqtt_publish_fukt2 = "eliasss/feeds/temperaturgruppen.fuktighet-2"
mqtt_client_id = ubinascii.hexlify(machine.unique_id())

wlan = network.WLAN(network.STA_IF) 

mqtt_client = MQTTClient(
        client_id=mqtt_client_id,
        server=mqtt_host,
        user=mqtt_username,
        password=mqtt_password
        )

mqtt_client.connect()

try:
    while True:
moist1 = readsensor.readmoisture(27)
        mqtt_client.publish(mqtt_publish_fukt1, str(moist1))
        print(f"published soil moisture 1: {moist1}")
        for i in range(15):
            led.on()
            time.sleep(1)
            led.off()
            time.sleep(1)
        moist2 = readsensor.readmoisture(28)
        mqtt_client.publish(mqtt_publish_fukt2, str(moist2))
        print(f"published soil moisture 2: {moist2}")
        for i in range(15):
            led.on()
            time.sleep(1)
            led.off()
            time.sleep(1)
except Exception as e:
    print(f'Failed to publish message: {e}')
finally:
    mqtt_client.disconnect()
except Exception as e:
    print(f'Failed to publish message: {e}')
finally:
    mqtt_client.disconnect()
```

</details>

The above code does the following:
1. Import libraries - Mostly self explanatory. ```hemligheter``` holds a dictionary, ```hemlisar``` containing passwords, usernames etc. Basic functions for wifi connection are held in ```boot```.
2. Try to connect. Sometimes fail due to distance to router.
3. Wait for connection to establish before defining MQTT client and using it to connect.
4. Run a loop reading a sensor, then waiting 30 seconds, reading the other, wait 30 seconds and so on. While this loop is running the onboard LED will blink.

## Transmitting data

The microcontroller is connected to the internet via my home wifi. Due to the distance between my router and plants the microcontroller sometimes fails to connect. In order to tell when this happens the onboard LED will light upp when running the connection function and start blinking while the data reading/transmitting loop is running. If my plants were slightly further away I would have to look at a LoRa-style solution.        
Data is transmitted for each sensor once every 60 seconds to adafruit.io using the MQTT protocol. This is a very excessive rate of transmission and I will change it to once every 5-10 minutes in the future.        



## Data presentation
![](https://hackmd.io/_uploads/BkTgLF-Fh.png)

Data from the 2 sensors are presented on an adafruit.io dashboard as seen above. It is updated/saved once every minute for each sensor. As mentioned before, this is far more often than necessary and I will change it to *at most* once every 5 minutes in the future. At the moment I have one IFTTT app for each sensor set at 25 and 35 respectively.

![](https://hackmd.io/_uploads/HkdkVgMY3.png)

## Final thoughts

I would call this project a simple success. In the future I may complete the setup with a microdrip system controlled by the sensors. This, however, will have to wait due to time and budget constraints.        
Had I done this all over again I would have done more planning before buying components as I am now in possession of a pile of electronics that will probably never be used.

![Sensor in soil](https://hackmd.io/_uploads/HJ4_TgzK2.jpg)


