Water Level Sensor

I’m looking to use this for my hydroponics system to monitor water level in a bucket. However, it doesn’t have any python code listed. Is there anyone who has started writing this and would like to share or point me in the right direction?

Hi @vondalej, which sensor you are using?

From the Grove - Water Level Sensor WiKi page it shows only Arduino library is completed. @Baozhu any updates on this.

Capture

Not yet, I’m sure we’ll have an update in the coming months.

2 Likes

That’s great :slightly_smiling_face::+1:

Any movement on getting this written??

Hi there,

I’m also interested in working with this sensor in Python. Anyone tried this already?

Actually, I just tried it. My code seems to work, but at some point it showed “Remote I/O error” (I’m not sure if I might have induced the error by accidentally touching the wires on the breadboard, though)

I had to compile the micropython library on my Pi: https://www.raspberrypi.org/forums/viewtopic.php?t=191744

Please see the code and the screenshots:

import time
import smbus
import numpy as np

NO_TOUCH = 0xFE
THRESHOLD = 100
ATTINY1_HIGH_ADDR = 0x78
ATTINY2_LOW_ADDR = 0x77

i2c_ch = 1
i2c_address1 = 0x77
i2c_address2 = 0x78

# Register addresses -- don't know what they are for
reg_temp = 0x00
reg_config = 0x01 # offset?

bus = smbus.SMBus(i2c_ch)

def getHigh12SectionValue():
    high_data = bus.read_i2c_block_data(ATTINY1_HIGH_ADDR, reg_config, 12)
    return high_data

def getLow8SectionValue():
    low_data = bus.read_i2c_block_data(ATTINY2_LOW_ADDR, reg_config, 8)
    return low_data
    
def check_water_level():
    sensorvalue_min = int(250)
    sensorvalue_max = int(255)
    low_count = int(0)
    high_count = int(0)
    
    touch_val = int(0)
    trig_section = int(0)
    low_count = 0
    high_count = 0
    
    low_data = getLow8SectionValue()
    high_data = getHigh12SectionValue()
    
    for i in range(8):
        if low_data[i] > THRESHOLD:
            touch_val += 1
       
    for i in range(12):
        if high_data[i] > THRESHOLD:
            touch_val += 1
    
    value = touch_val * 5
    print("water level = " + str(value) + "%")


while True:
    check_water_level()
    time.sleep(2)

Here’s a short video on how my code works:

Here’s the error message I got in the end, but now the code runs for a few minutes and nothing happens - so it probably was by accident:

I believe my code lacks some sensor diagnostics, e.g. if the bus actually could be read, or - I’m not using the NO_TOUCH variable, which probably determines some sensor parameters or readouts. I did not figure out how to use it from the Arduino code. If you would have any ideas on how to improve this, please share :slight_smile:

1 Like

Hi @mormegil6
WaW!Very good python code, can I update this code to our Wiki?
(I will sign your name for thank you.)
The OSError: [Errno 121] Remote I/O error offer occurred when the external hardware cannot be found, so you may not have the I2C permission, or the I2C address setting is incorrect, or the wiring is bad.

1 Like

Thanks @jiachenglu :slight_smile:

I am not sure if the code is that good - I think it could be smoother without the for loops, and use vector operations instead. My code might be polished a bit in this regard. Sure, you might use this code for the Wiki as it is right now, or you might wait for the polished version :wink:

Generally, some parts of code could be shorter, but I wanted my code to resemble the Arduino code as much as possible.
Furthermore, the part where I am declaring reg_temp and reg_config variables - I have found them in some other Python example regarding reading raw data from I2C bus (which was about the temperature sensor, hence reg_temp). I am also quite unsure about the meaning reg_config variable - from what I read in the documentation of the function it’s used, read_i2c_block_data(), the second value this function accepts is “offset”.
Additionally, I forgot to remove some unused variables and unused references, ie. i2c_address1 and i2c_address2, which are duplicates of ATTINY2_LOW_ADDR and ATTINY1_HIGH_ADDR. Also, I am not using the numpy library; I was considering using it but in its current shape it’s not needed.

Hi @mormegil6
Thanks for your python code, I followed the steps to build micropython and got the below output.
By the way, we are waiting for your polished version.
water

Hi @jiachenglu,
Sorry for the long time without a reply. I was extremely busy for the past 2 weeks, and I still am but not for long. I hope to get back to this code by the end of this week :slight_smile:

Hello @jiachenglu,
Sorry for the time it took. I reviewed my code, and I deleted unused variables etc. I did leave the for loops. I believe they can be done in a more fancy way ie. using vector logic, but I think they work quite straight-forward and they don’t need such fanciness.
Here, you can post this code on your Wiki page for Raspberry Pi / Python:

import time
import smbus
import numpy as np

NO_TOUCH = 0xFE # unused variable
THRESHOLD = 100
ATTINY1_HIGH_ADDR = 0x78
ATTINY2_LOW_ADDR = 0x77

i2c_ch = 1

reg_temp = 0x00 # register address -- temperature? / Unused variable
reg_config = 0x01 # register address -- offset?

bus = smbus.SMBus(i2c_ch)

def getHigh12SectionValue():
    high_data = bus.read_i2c_block_data(ATTINY1_HIGH_ADDR, reg_config, 12)
    return high_data
    
def getLow8SectionValue():
    low_data = bus.read_i2c_block_data(ATTINY2_LOW_ADDR, reg_config, 8)
    return low_data
    
def check_water_level():
    touch_val = int(0)
    
    low_data = getLow8SectionValue()
    high_data = getHigh12SectionValue()
    
    for i in range(8):
        if low_data[i] > THRESHOLD:
            touch_val += 1
        
    for i in range(12):
        if high_data[i] > THRESHOLD:
            touch_val += 1
        
    value = touch_val * 5
    print("water level = " + str(value) + "%")
	return value / 100

while True:
    sensorLevel = check_water_level()
    return sensorLevel
	time.sleep(2)

One more thing - I have made a little script that logs the sensor data. It was logging the water level with 5min time gap between readouts. I have noticed occasional spikes in the water level read. It should always show 0%, but sometimes it shows 5%, or even 60%:

Thanks for doing this! Had a few issues though. There’s a few indents when spaces were used for the rest. Also I’m having an issue with return sensorLevel is giving an error that it’s out of the function.

If I remove it and use print instead it starts functioning. The biggest issue is that it is always reading 100%. There were a few times where the first reading was 0 or 10% (which was right), but now i’ve got it out of the water and its 100%.

In looking further I’m seeing issues with 2 things. The threshold figure and the * 5.

If I make the threshold 101 and remove the * 5 i’m getting the 20% that it should be. I haven’t been able to test it as it’s jammed into my hydroponics system, but I’ll watch it.

@vondalej I don’t know why you needed to change the threshold value or remove the * 5 multiplier. I took it from the Arduino code example, and it worked just fine - as you can see on my YouTube video. I think @jiachenglu might comment more on that.

Regarding the other part, you’re right - the last return sensorLevel shouldn’t be there if you want to read the water level values in the command line terminal. I am using this as part of another program, therefore the return is needed - I’m logging the data to Google Spreadsheets. Please see the code corrected once again, which should run straigt out of “copy paste” :wink:

Also, I have found another issue with the sensor. When I start my code with the sensor submerged in the water container up to 100%, it shows 0% - as if it calibrated the initial state and detected it as 0. When I take my sensor out of water, dry it, then start it, and then submerge it, it shows the values correctly. I think it is quite an issue, that the sensor does not show correct values regardless of its initial state.
@vondalej maybe your issue also has something to do with sensor’s initial state?

The code:

import time
import smbus
import numpy as np

NO_TOUCH = 0xFE # unused variable
THRESHOLD = 100
ATTINY1_HIGH_ADDR = 0x78
ATTINY2_LOW_ADDR = 0x77

i2c_ch = 1

reg_temp = 0x00 # register address -- temperature? / Unused variable
reg_config = 0x01 # register address -- offset?

bus = smbus.SMBus(i2c_ch)

def getHigh12SectionValue():
    high_data = bus.read_i2c_block_data(ATTINY1_HIGH_ADDR, reg_config, 12)
    return high_data
    
def getLow8SectionValue():
    low_data = bus.read_i2c_block_data(ATTINY2_LOW_ADDR, reg_config, 8)
    return low_data
    
def check_water_level():
    touch_val = int(0)
    
    low_data = getLow8SectionValue()
    high_data = getHigh12SectionValue()
    
    for i in range(8):
        if low_data[i] > THRESHOLD:
            touch_val += 1
        
    for i in range(12):
        if high_data[i] > THRESHOLD:
            touch_val += 1
        
    value = (touch_val * 5) / 100
    return value

while True:
    sensorLevel = check_water_level()
    print("water level = " + str(sensorLevel * 100) + "%")
    time.sleep(2)

I am using the Grove Water Level Sensor 10CM SKU 101020635 on a Raspberry Pico RP2040.
The odd thing is, that it always start at 0%, if it is soaked in water.
To get a valid reading, I have to pull it out of the water, dry it, and put it back in.
Is there a way around this, or it it by design, that I need to pull it out first, and dry this sensor before running my code?

Result when soaked & code started:
bytearray(b’\x00\x00\x00\x00\x00\x00\x00\x00’) bytearray(b’\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00’)
water level = 0%

Result when dry & code started & then soaked:
bytearray(b’\xf9\xfa\xf8\xf9\xfa\xf9\xfa\xf9’)
bytearray(b’\xf8\xf8\xf9\xf8\xf9\xf8\xf8\xf8\xf8\xf8\xf8\xf9’)
water level = 100%

My code in MicroPython running in the RP2040:
import board
import busio
import time
#import smbus
from adafruit_bus_device.i2c_device import I2CDevice
#import numpy as np

NO_TOUCH = 0xFE
THRESHOLD = 100
grovewatersensorhigh = 0x78
grovewatersensorlow = 0x77
grovewatersensorhighsize = 12
grovewatersensorlowsize = 8

i2cwaterlevelsensorbus = busio.I2C(scl=board.GP3, sda=board.GP2)

#i2cwaterlevelsensorbus = busio.I2C(scl=board.GP3, sda=board.GP2)
waterlevelinterfacelow = I2CDevice(i2cwaterlevelsensorbus, grovewatersensorlow)
waterlevelinterfacehigh = I2CDevice(i2cwaterlevelsensorbus, grovewatersensorhigh)

def getHigh12SectionValue():
watersensorhighdata = bytearray(grovewatersensorhighsize)
with waterlevelinterfacehigh:
time.sleep(0.01)
waterlevelinterfacehigh.readinto(watersensorhighdata)
return watersensorhighdata

def getLow8SectionValue():
watersensorlowdata = bytearray(grovewatersensorlowsize)
with waterlevelinterfacelow:
time.sleep(0.01)
waterlevelinterfacelow.readinto(watersensorlowdata)
return watersensorlowdata

def check_water_level():
sensorvalue_min = int(250)
sensorvalue_max = int(255)
low_count = int(0)
high_count = int(0)

touch_val = int(0)
trig_section = int(0)
low_count = 0
high_count = 0

low_data = getLow8SectionValue()
high_data = getHigh12SectionValue()
print(low_data, high_data) 
for i in range(8):
    if low_data[i] > THRESHOLD:
        touch_val += 1
   
for i in range(12):
    if high_data[i] > THRESHOLD:
        touch_val += 1

value = touch_val * 5
print("water level = " + str(value) + "%")

while True:
check_water_level()
time.sleep(2)

1 Like

Guys, do you have any solutions to the above problem?

A Water Level Sensor is used to detect the level of substances that can flow. There are powerful IoT sensors that are installed into your tanks and wells that send info to your phone via the cloud. It receives alerts when the water level or quality is below.