---
# System prepended metadata

title: Liquid level detection system

---

# Liquid level detection system
By Erik Vasquez Petersson, ep222fj

This tutorial will guide you through setting up an IoT system that allows you to monitor the liquid level of a container.

If you follow this guide without deviating in choice of hardware and software, it shouldn't take more than a day or two to complete.

# Objective
There are many reasons why knowing the liquid level of a container might be interesting.

You might want to connect some sort of alarm that triggers based on the liquid level, with one example being a water tank that is too hard to reach to regularly check. Another example could be a tank that gets filled as a result of some other process and needs to be emptied in a timely manner.

Other than simply acting as an alarm trigger, the data collected could be used to calculate how quickly a container gets filled or drained, especially if building upon this system and expanding it with several liquid detection sensors.

Personally, I wanted to monitor the water level in a container used for automatic watering of houseplants.

# Material

| Item     | Price  |
| -------- | -------|
| LoPy4 bundle | [~100 Eur*](https://pycom.io/product/lopy4-multipack/) |
| Breadboard | [39 kr](https://www.electrokit.com/en/product/bread-board-270-connections/) |
| Jumper wires, male - female | [29 kr](https://www.electrokit.com/en/product/jumper-wires-1-pin-male-female-150mm-10-pack/) |
| Jumper wires, male - male | [29 kr](https://www.electrokit.com/en/product/jumper-wires-1-pin-female-female-150mm-10-pack/) |
| 2 pole copper wire | [10 kr/meter](https://www.kjell.com/se/produkter/hem-fritid/fast-telefoni/installation/telekablar/installationskabel/telekabel-eku-2-polig-p39650) |
| DC connector, female | [60 kr](https://www.kjell.com/se/produkter/el-verktyg/el-produkter/svagstrom/dc-kontakter/terminalblock-med-dc-hona-55x21-mm-p39981) |
| Sensor, Xkc-Y25-Npn | [~200 kr](https://www.amazon.se/dp/B088PGKPJ4?ref_=pe_24982401_513610551_E_301_dt_1) |
| Power supply | [139 kr](https://www.electrokit.com/en/product/power-supply-3-12v-1500ma-stab-switched/)   |
| Multimeter (optional) | [500 kr](https://www.kjell.com/se/produkter/el-verktyg/matinstrument/multimetrar/uni-t-ut139a-multimeter-p48385) |

\*If ordering to Sweden it should be about 100 Euro after shipping and customs has been paid. 

## LoPy4 bundle
This bundle contains Pycoms LoPy4 development board on which we will run our code. It also contains an expansion board that provides us with a micro USB port for connecting the board to a computer. The expansion board also features lots of connectors, making it easier to connect the LoPy to multiple things at once.

Two antennas, one for Wi-Fi and one for LoRa are also included.

## USB-A to Micro USB data cable
This is so that we can flash code to the LoPy board.
Most people have a ton of these lying around at home, which is why I didn't include it in the table.
Some USB cables only carry current, so make sure that you use one that can handle data as well.

## Breadboard
To avoid having to solder any connections a breadboard is perfect.

## Wires
There are three different kinds of wires we will need.
#### male to male
This is for connecting the LoPy expansion board and sensor to the breadboard.
#### male to female
The wire coming out of the DC connector is a lot thicker than the regular jumper wires. To avoid damaging any female connectors on the breadboard, we will sacrifice two male to female jumper wires, plugging the wires coming from the DC connector into the female ends of the jumper wires.
#### wire for dc connector
The DC connector has screw connectors, meaning we need a sufficently thick wire. This one does the job well.

## Capacitive proximity sensor, Xkc-Y25-Npn 
This sensor is able to sense liquids through a container or pipe walls as long as they are non-metallic.

## Power supply
Since the sensor we use is rated for 5-12V, powering both the LoPy and sensor with the same 5V power is the best choice. This power supply can deliver a range of voltages, so remember to set the voltage to 5V.

## DC connector
To connect the power supply to the LoPy and sensor we'll use a female DC connector.

## Multimeter (optional)
It's important to get the polarity from the power supply right. The power supply linked in this guide has exchangable connectors, and the manual is a bit unclear on the right way of connecting them. The safest way to be 100% sure about the polarity is to get a multimeter and test it. You can probably get a cheaper one than the one linked.

# Computer setup
First of all you'll want to update the firmware of your expansion board. This can be done by following [these instructions](https://docs.pycom.io/updatefirmware/device/).

To write code for our LoPy4 we'll need an [IDE](https://en.wikipedia.org/wiki/Integrated_development_environment). For this, we have the choice between [Visual Studio Code](https://code.visualstudio.com/) (vscode) or Atom, since these are the only IDEs with a Pymakr extension. We need that extension to be able to save and execute code on our LoPy4. In this section I'll be focusing on vscode, but the process should be roughly the same for Atom.

For Pymakr to work, we'll need to install [Node.js](https://nodejs.org/en/). When you've done that installing the extension is as easy as clicking on the "Extensions" button in the vscode menu to the left. Now all you need to do is to search for Pymakr and install it.

Once you have pymakr installed, you can use the upload button in the pymakr console to flash code to the LoPy4.

# Putting everything together
Connecting the sensor to our LoPy is very straight forward, just follow the wiring diagram below.
IMPORTANT: Make sure that you get the polarity of the DC connector right.

![](https://i.imgur.com/37vtDnZ.png)

# Platform
The platform we'll be using is [pybytes](https://pybytes.pycom.io/). It's a cloud based solution developed by Pycom specifically for their devices and is free to use for Pycom devices.

It allows us to easily send and store data from our sensor over multiple kinds of networks, although in this tutorial we'll stick to Wi-Fi. You can also use it to create dashboards with tables and charts that display your sensor data.

Another neat thing with pybytes is that it allows you to set up [integrations](https://docs.pycom.io/pybytes/integrations/) for your devices, enabling you to use them along with other IoT platforms. This is helpful if you want to store data longer, or setup alarms based on the data from the sensor.

As you'll see further down, using pybytes along with the LoPy4 is very easy. It even lets you store all your networking credentials in the cloud.

# The code
The block below contains all the code you should need if you've followed the tutorial so far.

```python=
import pycom
import time
import machine

from machine import ADC
from network import LTE

adc = ADC()  

levelSensorWarning = adc.channel(pin='P14')

def init():
    print("init")
    disableLTE()
    pycom.heartbeat(False)
    loop()

def disableLTE():
    # Need to disable LTE radio for better deepsleep
    lte = LTE()
    lte.deinit()

def loop():
    time.sleep(2)

    waterLevelWarning = readWaterLevelSensorValue(levelSensorWarning)
    
    sendPayload(2, waterLevelWarning)
    
    # Sleep before deep sleep to give the pybytes thread sending the data time to complete
    # Consider trying the proposed solution here https://forum.pycom.io/topic/5936/pybytes-deepsleep/2 in it's own function.
    time.sleep(10)

    machine.deepsleep(60000)

def readWaterLevelSensorValue(pin):
    sensorValues = dict()
    # Since the sensor readings seem a bit unstable we'll read them a lot of times and then pick out the most read value. 
    for i in range(100):
        sensorValue = pin()
        
        # if value doesn't exist, set value to 1
        if sensorValue in sensorValues:
            sensorValues[sensorValue] = sensorValues[sensorValue] + 1
        else:
            sensorValues[sensorValue] = 1

    sensorValue = max(sensorValues, key=lambda key: sensorValues[key])

    return sensorValue

def sendPayload(signal, payload):
    pybytes.send_signal(signal, payload)

init()
```

# Transmitting the data / connectivity
For this tutorial we're using [MQTT](https://mqtt.org/) over Wi-Fi to send our sensor data to pybytes. With the very small amounts of data that we'll be sending, Wi-Fi provides far more bandwith and speed than we need. But it is also available widely available in peoples homes, something that LoRa can't boast about quite yet, in Sweden at least.

The biggest drawbacks of Wi-Fi are the power consumption and limited range, neither of which are issues for us since this setup is meant for home use, where wired access to the power grid is available.

Thanks to pybytes, changing the network used from Wi-Fi to one of the other supported types is as easy as changing the [configuration in pybytes](https://docs.pycom.io/pybytes/networks/).

The data is sent once a minute, which might be far too often or perhaps not often enough, depending on the kind of container and liquid you want the sensor to monitor.
However, changing the frequency with which we send the sensor data in the code is trivial.

# Presenting the data
Even though pybytes will save the data as often as we send it (once a minute in our case), it also allows us to specify the time resolution of our charts. This can be configured down to milliseconds, but for my own purposes a resolution of hours or days is more useful. You will have to decide on a resolution that fits the goals of your project. Changing it in pybytes is simple.

Below are two examples of charts that I have setup, the first covering 1,5 hours and the second covering slightly longer than a day.

![](https://i.imgur.com/qbjE6zH.png)
![](https://i.imgur.com/nGt46RP.png)

One might expect the charts to only have two data points, 0 or 1. More on that in the next section.

# Finalizing the design
This is what I ended up with.

![](https://i.imgur.com/3y5onn1.jpg)
Here you can see the sensor lit up and active since the water level is above it.

![](https://i.imgur.com/rLqtN4j.jpg)
After moving the sensor above the water level it is no longer lit up.

I'm happy with the end result, but there is one major point which could be improved: the reading inaccuracies.
As seen in the earlier graphs, the sensor will always send a stable 0 value when it senses liquid. However, when it comes to an empty container the sensor is less reliable. I've tried to fix this in the code, but I was only successful to a certain degree. The readings got more consistens when I started aggregating the reading from the sensor and picking the most read value, but there where still times when it would send the wrong data.

Since the readings for when the sensor detects liquid are reliable, you are still able to extrapolate from that and tell from the graph when the container is empty. I would however like to fine tune this more and see if I can get it 100% reliable.

I would also like to integrate this with other IoT platforms where it's possible to trigger alarms based on the data received.