Gravity: Tipping Bucket Rainfall Sensor - I2C & UART (SEN0575)

This is a placeholder topic for “Gravity: Tipping Bucket Rainfall Sensor - I2C & UART” comments.

Arduino Gravity: Tipping bucket rainfall sensor is the perfect tool for measuring rainfall. With no electronic components inside and a hollow bottom…

Read more

This item arrived today. I will be using it in I2C mode connected to a Raspberry Pi Pico for my weather station. The software from DFRobot comes with a Python Library for the Pi 4 / 5. It looks like a quick translate of the Arduino C script and doesn’t work with the Pico.

The code below I created from the DFRobot library code and uses the PiicoDev Unified Library code. It has had limited testing on a Pico. Posting here to help the community.

Regards
Jim

Library Code. DFRobot_RainfallSensor.py

from PiicoDev_Unified import *
compat_str = '\nUnified PiicoDev library out of date.  Get the latest module: https://piico.dev/unified \n'

class DFRobot_RainfallSensor_I2C:

    SEN0575_INPUT_REG_PID                        = 0x0000
    SEN0575_INPUT_REG_VID                        = 0x0001
    SEN0575_INPUT_REG_ADDR                       = 0x0002
    SEN0575_INPUT_REG_BAUD                       = 0x0003
    SEN0575_INPUT_REG_VERIFYANDSTOP              = 0x0004
    SEN0575_INPUT_REG_VERSION                    = 0x0005
    SEN0575_INPUT_REG_TIME_RAIN_FALL_L           = 0x0006
    SEN0575_INPUT_REG_TIME_RAIN_FALL_H           = 0x0007
    SEN0575_INPUT_REG_CUMULATIVE_RAINFALL_L      = 0x0008  
    SEN0575_INPUT_REG_CUMULATIVE_RAINFALL_H      = 0x0009  
    SEN0575_INPUT_REG_RAW_DATA_L                 = 0x000A  
    SEN0575_INPUT_REG_RAW_DATA_H                 = 0x000B  
    SEN0575_INPUT_REG_SYS_TIME                   = 0x000C
    SEN0575_HOLDING_REG_RAW_RAIN_HOUR            = 0x0006
    SEN0575_HOLDING_REG_RAW_BASE_RAINFALL        = 0x0007
    
    SEN0575_I2C_REG_PID                          = 0x00
    SEN0575_I2C_REG_VID                          = 0x02
    
    SEN0575_I2C_REG_VERSION                      = 0x0A
    SEN0575_I2C_REG_TIME_RAINFALL                = 0x0C
    SEN0575_I2C_REG_CUMULATIVE_RAINFALL          = 0x10
    SEN0575_I2C_REG_RAW_DATA                     = 0x14
    SEN0575_I2C_REG_SYS_TIME                     = 0x18
    SEN0575_I2C_REG_RAW_RAIN_HOUR                = 0x26
    SEN0575_I2C_REG_RAW_BASE_RAINFALL            = 0x28

    def __init__(self, bus=None, freq=None, sda=None, scl=None, address=0x1D):
        try:
            if compat_ind >= 1: pass
            else: print(compat_str)
        except:
            print(compat_str)
        print('Rain Sensor init ...',bus,freq,sda,scl,address)
        self.i2c = I2CUnifiedMachine(bus=bus, freq=freq, sda=sda, scl=scl)
        
        self.addr = address
        self.vid = 0
        self.pid = 0
        sleep_ms(1)
        self.get_pid_vid()
        if (self.vid != 0x3343) or (self.pid != 0x100C0): 
            raise RuntimeError('Fail: expected ID values incorrect.')

    def get_pid_vid(self):
        d = self._read_register(self.SEN0575_I2C_REG_PID, 4)
        self.pid = d[0] | ( d[1] << 8 ) | ( ( d[3] & 0xC0 ) << 10 )
        self.vid = d[2] | ( ( d[3] & 0x3F ) << 8 )
        return
    
    def get_firmware_version(self):
        d = self._read_register(self.SEN0575_I2C_REG_VERSION, 2)
        version = d[0] | ( d[1] << 8 )
        return str(version >> 12) + '.'+ str( ( (version >> 8) & 0x0F ) ) + '.' + str( ( (version >> 4) & 0x0F ) ) + '.' + str( (version & 0x0F) )

    def get_sensor_working_time(self):
        d = self._read_register(self.SEN0575_I2C_REG_SYS_TIME, 2)
        working_time = d[0] | (d[1] << 8)
        return working_time

    def get_rainfall(self):
        data = self._read_register(self.SEN0575_I2C_REG_CUMULATIVE_RAINFALL, 4)
        rainfall = data[0] | ( data[1] << 8 ) | ( data[2] << 16 ) | ( data[3] << 24 )
        return rainfall / 10000.0

    def get_rainfall_time(self,hour):
        if hour > 24: return 0
        self._write_register(self.SEN0575_I2C_REG_RAW_RAIN_HOUR, hour)
        d = self._read_register(self.SEN0575_I2C_REG_TIME_RAINFALL, 4)
        rainfall = d[0] | ( d[1] << 8 ) | ( d[2] << 16 ) | ( d[3] << 24 )
        return rainfall / 10000.0

    def get_raw_data(self):
        d = self._read_register(self.SEN0575_I2C_REG_RAW_DATA, 4)
        raw_data = d[0] | ( d[1] << 8 ) | ( d[2] << 16 ) | ( d[3] << 24 )
        return raw_data

    def set_calibration_value(self, msb, lsb):
        self._write_register(self.SEN0575_I2C_REG_RAW_BASE_RAINFALL, lsb)
        self._write_register(self.SEN0575_I2C_REG_RAW_BASE_RAINFALL+1, msb)
        return 

    def _write_register(self, reg, data):
        self.i2c.writeto_mem(self.addr, reg, bytes([data]))
        sleep_ms(50)
        return True
    
    def _read_register(self, reg, length):
        return self.i2c.readfrom_mem(self.addr, reg, length)
    
    def _write_reg(self, reg, data): raise NotImplementedError()
    def _read_reg(self, reg, length): raise NotImplementedError()

############################################################################
############################################################################
############################################################################

Progam to use. Tipping_Bucket_Test.py

from time import sleep
from machine import Pin, I2C, RTC
from DFRobot_RainfallSensor import *

LED_PIN = 25
dt = RTC()
LED = Pin(LED_PIN, Pin.OUT)
sensor=DFRobot_RainfallSensor_I2C()

############################################################################
# Get Time
############################################################################
def Get_Time():
    return '{:02d}:{:02d}:{:02d}'.format(dt.datetime()[4],dt.datetime()[5],dt.datetime()[6])
############################################################################
# Main Program
############################################################################
def main():

    while True:
        active = int(sensor.get_sensor_working_time())
        rainfall=sensor.get_rainfall()
        one_hour_rainfall=sensor.get_rainfall_time(1)
        rainfall_raw=sensor.get_raw_data()
        print(Get_Time(),'Active:',active,'mins,','rainfall: %f mm,'%(rainfall),'One hour: %f mm,'%(one_hour_rainfall),'Original value: %d'%(rainfall_raw))
        sleep(10)
    return
############################################################################
# Startup 
############################################################################
if __name__ == '__main__':
    
    LED.value(1)
    print(' ')
    print(Get_Time(),'Test Tipping Bucket Rain Guage ... ')
    print('Version: '+ sensor.get_firmware_version())
    print("vid: %#x"%(sensor.vid))
    print("pid: %#x"%(sensor.pid))
    sleep(1)
    try:
        main()
#    except Exception as e:
#        pass
#        print('Error occurred : {}'.format(e))
    finally:
        print('... Shutting Down')
        sleep(1)
        LED.value(0)
############################################################################
############################################################################
3 Likes

Hi Jim,

Well done and thanks a heap. You’re certainly going to save your fellow makers some time troubleshooting.

1 Like

EDIT: Ignore this post, see the one below.

Warning: def set_rain_accumulated_value(self, value): does not work.

In trying to understand how this function works I managed to set something in the processor on the board that now returns NO rain values no matter how many times the bucket has tipped. However the number of raw tips does go up. Power off does not fix it.

I thought setting the value to zero would reset all the values, it did not, just messed up how it calculated the values. I then tried to setting the location after the accumulated value location to zero and that was when nothing is now returned.

I wanted a way to zero the values without powering off the device, guess that is not possible.
I can still use it to count tips and my program can calculate from that. The device indicated 0.2794 mm for each tip, so that is the value I would use.

DFRobot should provide a way to factory reset the firmware in the processor or at least explain what the functions do and how to use them. Bit annoyed, mainly at myself and upset that it is just a glorified counter now. I could easily build that myself … Oh well such is life.

Yet to contact DFRobot, most likely it is something that cannot be fixed by me.

Regards
Jim

2 Likes

… and now its fixed.
set_rain_accumulated_value(value) is the CALIBRATION value for the device.
It is a 2 byte word that the other values are calculated from.
SEN0575_I2C_REG_RAW_BASE_RAINFALL = 0x28 LSbyte
SEN0575_I2C_REG_RAW_BASE_RAINFALL = 0x29 MSbyte

I found setting 0x29 to 10 and 0x28 to 234 returned a value of 0.2794 for 1 bucket tip.
Which was the value the device was reporting for 1 bucket tip. This solves one of my questions, “how to calibrate the device”.

I have changed the code above to reflect this and renamed the function to better say what it does. Happier now.

Cheers
Jim

PS will add another post on how to use all the functions.

3 Likes

Rainfall Sensor Library function usage.

get_firmware_version()
Returns version number of the firmware as a dot separated string. ie 0.0.1.0

get_sensor_working_time()
Returns the time the device has been powered on, in minutes.

get_rainfall()
Returns the total rainfall since the device has been powered on, in mm.

get_rainfall_time(hour)
Returns the rainfall for the last hour up to 24 hours, in mm.
ie hour = 1 last hour, hour = 3 last 3 hours, … hour = 24 last day.

get_raw_data()
Returns the number of bucket tips since power on, 1 = 1 tip.

set_calibrate_value(msb, lsb)
Sets the value in mm for 1 tip of the bucket, 2 byte word, lsb msb.
Factory setting msb = 10, lsb = 234;. equates to 0.2794 mm for each bucket tip
Change this value to calibrate the device if necessary.

The only way I have found to reset the data is to power the device off. If running for a long, long, long time eventually the count of bucket tips will overflow. 4 byte word = 4,294,967,295. A rather larger number.

Regards
Jim

2 Likes

Thanks for this information, @James46717!

This will be super useful for the next person to play around with this sensor. Thanks for the code and good job on fixing that issue.

I am puzzled that Core are so adamant about “no electronic components inside”, despite there clearly being a wire coming from the device, and the product description stating “When a certain amount of water accumulates, the tipping bucket loses its balance and tips over, generating a pulse signal with each tip.”

I bought two (without the DFrobot modules) from china for a garden/greenhouse monitor project (which I am still procrastinating over), but realised that the Raspberry Pi would then have to be powered on 24/7 to catch each and every pulse from the bucket tipping … which would not work well with the whole thing being battery/solar powered.

I assume therefore that the DFrobot signal adapter board is recording the number of pulses/hour (which would allow the RasPi to take a more relaxed approach) and supplying data via a more convenient interface. That is great !

That leaves me with a couple of questions:

  1. Does the DFRobot signal adaptor board only work in hourly units ?
  2. Are the hours from the time it was powered on (so the current hour is actually a part hour); or going backward from now ?
  3. Presumably there is a maximum quantity that the signal adaptor software allows. Is that the 4billion count of bucket flips mentioned by James46717 per hour, or total over 24 hours ?
  4. James mentioned resetting the data. Is it necessary to reset every day, or does it simply retain the last 24 hours data and forget the old data once it gets to 24 hours ?
1 Like

Hey @Donald23173, Thanks for the questions!

Pulling apart one of these buckets it seems like the inside contains a tipping bucket with a small magnet attached to it. As the bucket tips, this magnet moves past a small reed switch contained within the plastic housing.

This disturbance to the reed switch is passed through the included board to your controller as input from the sensor which is how the rainfall is measured. It would be more accurate to say that the bucket sensor contains no “active” electronic components.

The signal adapter board seems to only be acting to translate this signal into either UART or I2C and does not act to store any of this data, only to pass it on to your controller. The included Arduino library, which @James46717 has kindly adapted to work on a Raspberry Pi uses get_raw_data() to record the number of times the bucket has tipped over since power on.

As far as I can tell this value, and the current uptime, is all that the daughter board is storing between power cycles and this value is being used by the code to give you any additional information.

The ‘hours’ value used is based on how many hours the sensor has been powered on and does not reference the actual system time.

To answer your questions directly:

  1. Using get_sensor_working_time() will return the current uptime in minutes. This is the smallest time unit the sensor can return.
  2. The current hour starts from the time the sensor is powered on and ends in 60 minutes from power on. The actual time is not considered. For example, if the sensor is powered on at 11:28 am the hour will end at 12:28 pm for calculation purposes.
  3. Based on how the raw bucket tip count is stored it should be able to store a value up to 4294967295 which would be about 1.2 million litres of rainfall passing through the sensor. If you manage to hit this limit the count would reset back to 0 and start counting up again.
  4. Using get_rainfall_time(hour) will only allow you to see the rainfall for the last recorded 24-hour period. A regular reset is not needed for this to work as expected.

I hope this helps! Let me know if you have any more questions!

The interface board contains a microprocessor which stores the number of bucket tips for as long as it has power. It also has a clock to count the passage of time. This is used to calculate the rainfall in mm for the last hour or number of hours up to 24.

A thought occurred to me reading the post by @Donald23173. It should be possible for the tipping bucket to record rainfall with the controller off. (as long as the interface board has power) Then the controller can wake and read the values whenever needed and go back to sleep again.

When setting this up I triggered the bucket manually when the program was not running and it must have counted pulses because they showed up when the program was started again.
I cannot test how much current the unit will draw by itself. DFRobot claim less than 3mA.

@Donald23173
1. Does the DFRobot signal adaptor board only work in hourly units ?
It counts the pulses from the reed switch, and calculates the hourly units. You could read the raw value and use the time on your controller to calculate different rates. The interface board only provides hourly rates.

2. Are the hours from the time it was powered on (so the current hour is actually a part hour); or going backward from now ?
From the recording I have done (every 5 minutes) it is from now going backwards. There was rain from approx 11pm to 3am; the 24 hour count reduced as time progressed and there was no more rainfall. ie It was accurate until 11pm the next day.

3. Presumably there is a maximum quantity that the signal adaptor software allows. Is that the 4billion count of bucket flips mentioned by James46717 per hour, or total over 24 hours ?
Yes, 4 byte word, 4.29 billion total count of pulses. Not related to 24 hours. Once that is exceeded my guess is it rolls over to 0 again.

4. James mentioned resetting the data. Is it necessary to reset every day, or does it simply retain the last 24 hours data and forget the old data once it gets to 24 hours ?
There is no way to reset the count other than power off and on that I have found. The library only allows up to 24 hours, if you change it to allow a higher number, it is not clear what the firmware on the interface board would do. Its easy enough to just read the 24 hour count every day at a certain time, and power cycle the interface board to reset it.

Thanks @Samuel for your response.

Regards
Jim

This had been up and working nicely for a while then it suddenly stopped. Finally got around to checking it out.

I found about 6 dead spiders inside it and lots of spider webs. Not sure if they caused the bucket not to move or the water not to tip it properly. After cleaning it I noticed the magnet slightly rubs against the plastic as the bucket moves. Maybe in the heat it had become stuck, unsure.

There is a bit of sideways movement in the bucket the reed switches active without the bucket rubbing, a small nylon washer should have been used to keep the bucket away from rubbing the plastic.

Anyway device still works nicely now and it will be placed where it can be accessed more easily.

Regards
Jim

EDIT: Thought of something else. The pivot for the bucket is a metal rod through plastic, on a hot day the metal might expand too much and seize up the bucket. Will see how it goes when installed again.

Thanks for the update @James46717.

Let us know if you find that metal rod to be an issue (or the magnet being stuck for that matter), If the heat causes problems with the device then I’ll pass it on to DFR, for product feedback purposes.

P.S. Can always count on spiders to get into everything!