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.