SS4H-RG Smart Rain Gauge – DIY project based on ESP32 and ESPHome

In this article, I’ll show you how to build a rain sensor that not only detects when it’s raining but also tells you how heavy the rainfall is. And the best part? It’s more accurate and reliable than any online service out there.

If you have a piece of land where you’re growing anything that needs watering, this information is invaluable. For example, I’ve got a lawn and a few fruit bushes in my backyard, and watering them on a daily basis is a must during summer.

That’s where my own, local, independent-of-the-internet rain gauge comes in. It tells me exactly how much rain has fallen right where I live.

Video version

Source files

The project is totally open-source, which means you can grab it and tweak it however you please. You can snag my design, the PCB layout, the 3D model, the bill of materials, and firmware – and make it exactly as is. Or, you can use it as a jumping-off point, make changes, and whip up something totally fresh.

If you want to receive all of them put the email below – I’ll send a link to you straight away.

Where can I buy it?

If you don’t feel like building it yourself or simply don’t have the time, I invite you to visit my online store. You will find assembled, and ready-to-use device, or just the circuit board itself.


The sponsor of this project is JLCPCB. They made that beautiful board for this project. For a few bucks, and in a matter of days, you can have a professional PCB on your deck, ready to solder 🙂 Now, for new users, they provide up to $54 sign-up coupons.
And if you don’t feel up to it, they can solder all SMD components for you as well.

The schematic isn’t exactly mind-blowing, but that’s the beauty of it – it’s stripped down to only the essentials. While creating it, I followed the YAGNI (You Aren’t Gonna Need It) principle. It basically means that you should only add features when you really need them.

The ESP32 is the main computing unit, and it’s great for low power consumption in battery-powered devices.

I also added a circuit to monitor the battery level, so I can swap them out before they completely die.

But the real star of the show is the HALL sensor. It’s a magnetic sensor that detects rainfall. I’ll go into more detail once we get to the mechanical part of things.


I designed the PCB to be no bigger than the battery box. I wanted it to be as small as possible since it will be sealed in a container inside the housing. And I don’t want the Rain Gauge to be any bigger than it needs to be.


When it comes to component assembly, it’s easy. Start with SMD components, and then move on to THT.

However, you need to solder them on the TOP side, not on the BOTTOM as you would normally do. The reason is that we’ll later put the battery basket on the bottom, and it needs to fit snugly against the PCB. That’s why you should cut the THT components as close to the board as possible after assembly.

The Rain Gauge (as the name implies) is gonna be functioning in a damp setting. So, aside from enclosing the PCB in a reasonably airtight case, you can coat it with a waterproof varnish. I used PVB16 and it works pretty well, but you can use whatever is available locally to you.

Mechanical Part

The enclosure’s outer appearance resembles a basic cylinder topped with a funnel. This funnel connects to one of the two small containers, let’s call them “buckets.” Once a sufficient amount of water has gathered, the bucket tips over, allowing the water to flow through the holes. Consequently, the other bucket begins to collect water.
And that’s how it will continue swinging back and forth repeatedly…

How does a rain gauge work?

Since a picture is worth a thousand words, a short animation consisting of a dozen or so images should fully explain how it works.

By knowing the diameter of the funnel’s upper part, the capacity of one bucket, and the time interval between each tipping, we can accurately determine the intensity of the current rainfall.

But how does ESP32 know when the overflow takes place?

There’s a magnet at the top part of the tipping bucket. Inside this tightly sealed container, we’ve got our PCB with a HALL sensor.
It detects the change in the magnetic field when the bucket swings, and notifies the EPS about it.
The microcontroller measures how much time passed since the last tick, converts the units to l/m2, and sends that info to the Home Assistant.
If there’s no tipping for an hour or so, we can safely assume that the rain has stopped.


To make the results as precise as possible, both buckets must be identical. They need to react to exactly the same amount of water. Even though they are identical in Fusion 360, it may not be the case after printing.

To calibrate the sensor, use the two screws located beneath the tipping bucket.

Just pour a measured amount of water (e.g. 6 ml) and slowly turn the screw until the bucket tips. Do the same for the other side.
This will ensure that both buckets respond to the same weight.

You can also use these screws to adjust the sensitivity of the Rain Gauge. The less water you need to trigger it, the faster response you’ll get.
But keep in mind that the battery life will be shorter due to more frequent tipping.


The software part applies to both Home Assistant and ESPHome, so I divided this paragraph into two stages. In the first one, we’ll handle the ESPHome configuration, and then I’ll show you what else needs to be done in HA.


The vast majority of handling is done on the ESPHome side. It’s where we physically receive the information that the tipping bucket has been triggered, calculate the time between events, and convert units to “mm/h”. It’s the same with battery measurement. The ESP32 measures and converts the ADC result into percentages, which, in my opinion, are more convenient.

  #Deep Sleep 
  wake_time: '5s'

  max_battery_vlotage: '6'
  min_battery_voltage: '3.6'

This first segment contains variables that will potentially be modified most frequently. They are placed at the top of the configuration file for easy access and convenience.

  • wake_time: the duration for which the ESP chip will stay active after being awakened by the tipping bucket.
  • max(min)_battery_vlotage: at this point, you determine what percentage represents a fully charged battery and what percentage represents 0%. For regular AAA batteries, the setting I’ve chosen should be fine, but if you’re using rechargeable batteries, you might want to consider adjusting it.
  name: rain-gauge

  board: esp32dev
    type: arduino

# Enable logging

# Enable Home Assistant API
    key: "XXX"

  password: "XXX"

  ssid: !secret wifi_ssid
  password: !secret wifi_password
  fast_connect: True

  # Enable fallback hotspot (captive portal) in case wifi connection fails
    ssid: "Rain-Gauge Fallback Hotspot"
    password: "ahypg3nlyxHN"


These are standard things that can be found in every freshly generated configuration file, so I don’t think I need to explain them.

  - platform: gpio
    pin: GPIO27
    name: "RG Battery Switch"
    id: rg_battery_switch
    restore_mode: ALWAYS_ON
  - platform: gpio
    pin: GPIO12
    name: "RG LED"
    id: rg_led 

Here we’re just initializing two GPIOs as outputs.

“RG Battery Switch” disconnects the ADC line from the microcontroller during Deep Sleep mode to save battery power.

“RG LED” is connected to an LED. I use it to indicate that the Deep Sleep mode is blocked. (I’ll explain why we do this in a moment).

  - platform: status
    name: "RG Status"
  - platform: homeassistant
    id: rain_gauge_deep_sleep
    name: "rain_gauge_deep_sleep"
    entity_id: input_boolean.rain_gauge_deep_sleep
    publish_initial_state: True
            lambda: return x;
            - deep_sleep.allow: init_deep_sleep
            - switch.turn_on: rg_led
            - deep_sleep.prevent: init_deep_sleep

In this section, we set up the binary sensors.

The “Status” sensor allows Home Assistant to check if the Rain Gauge is online. That’s what we’re gonna use to figure out the average amount of rainfall during a given period.

As for the “RG Deep Sleep” sensor, it requires further explanation. I’ll tell you about it in the subsection “How to use OTA?

  - platform: adc
    pin: GPIO32
    name: "RG Battery Level"
    id: rg_battery_level
    device_class: "battery"
    unit_of_measurement: "%"
    accuracy_decimals: 0
    setup_priority: -200
      - lambda: |-
          float current_voltage = x * 5.94;

          if(current_voltage < $min_battery_voltage)
            return 0;
          else if(current_voltage > $max_battery_vlotage)
            return 100;
            return ((current_voltage - $min_battery_voltage) / ($max_battery_vlotage - $min_battery_voltage)) * 100;

The “RG Battery Level” analog sensor is used to measure the current voltage of the batteries (which is directly proportional to the charge level). And convert volts into more user-friendly percentages, where 100% corresponds to 6V and 0% corresponds to 3.6V.

  run_duration: $wake_time
  id: init_deep_sleep
    pins: GPIO25
    mode: ALL_LOW

Finally, we configure the Deep Sleep mode. We don’t set a specific time for the ESP to enter this mode because the only wake-up option is the tipping bucket.

Home Assistant

Alright, we’ve reached the final stage of the puzzle. Now we’ll gather all the information provided by the Rain Gauge and transform it into something we can actually use. For example, to decide whether the lawn needs watering.
Let’s get to work!

Some Math

We need to start by calculating the amount of rainfall represented by one tilt of the cup.
For this, we need two things: the diameter of the funnel’s inlet and the amount of water that causes the cup to overflow. While the first thing is constant (r = 55mm), you have control over the latter.
As I mentioned in one of the previous chapters about calibration using two screws, you can modify the required amount of water. For further calculations, I’ll assume it’s v = 6ml.

P_f = \pi r^2  \approxeq 9503,32mm^2 \\
k = 1000000/P_f \approxeq 105,23 \\
A = k * v = 0.631356l/m^2



That’s all. Pretty simple, right? 🙂
One tilt of the cup means that exactly 0.63135 liters per square meter have fallen, and since 1 mm is equivalent to 1 liter per square meter, one impulse corresponds to 0.63135 mm of the “water column”.

Sensor Configuration

The general principle of operation is as follows:
When a sufficient amount of water is collected in the bucket, it will tip and wake up the ESP32 from Deep Sleep mode.

Now, using the binary sensor we configured in ESPHome called “RG Status,” we will know that the Rain Gauge has awakened and connected to Home Assistant.
We’re gonna utilize this information for our calculations.

Each time this happens, the previously calculated amount of rainfall (0.63135 mm) will have occurred in our backyard.
Now, all we need to know is how many times the Rain Gauge has awakened within a given time period.

To achieve this, we will configure an integration called “history stats” to count the triggers and use the “template” to format it into a meaningful representation.

History Stats sensor
  - platform: history_stats
    name: "Rainsensor flips/h"
    entity_id: binary_sensor.rain_gauge_rain_gauge_status
    state: "on"
    type: count
    start: "{{ now() - timedelta(hours=1) }}"
    end: "{{ now() }}"

You paste this piece of code into the configuration.yaml file.
It counts how many times the binary sensor has changed its status to ‘on’ (which means the ESP32 has connected) within the last hour.

  - sensor:
      - name: Rainfall [h]
        state_class: total_increasing
        unique_id: rainfall_h
        unit_of_measurement: mm
        icon: mdi:weather-pouring
        state: >-
          {% set count = states('sensor.rainsensor_flips_h') | int(0) %}
          {% set mm = count * 0.631356 %}
          {% if count >= 0 %}
            {{ mm|round(1, 'floor') }}
          {% endif %}

        availability: "{{ (states('sensor.rainsensor_flips_h') not in 
 ('unknown', 'unavailable')) }}"

In this template, we convert the number calculated by the “Rainsensor flips/h” sensor into a specific value expressed in “mm”.

Now, from the dashboard, you can see the “Rainfall [h]” sensor, which shows how much rainfall has occurred in the last hour.

You can display this information in any way you prefer, whether it’s on a chart or simply as a numerical value. You can also use this information, for example, to automate watering your lawn.

Additionally, I similarly calculate the average rainfall sum over a day and a month. But I won’t explain it further since the principle is identical to the previous case.

How to use OTA?

The remaining issue is updating the firmware.

Typically, this is done Over the Air (OTA) on a previously programmed device. In our case, the problem is that the device spends the majority of its time in Deep Sleep mode, making it impossible to connect to it at any time.

We could wait for a stroke of luck and try to catch the 5-second window when the ESP32 is active after tilting the cup. But let’s be realistic, that’s not a great idea.

To make things easier, let’s create a “helper” that prevents the device from entering sleep mode once it has been awakened. This way, we can easily update the firmware while the device is active, without worrying about the short window of opportunity.

Go to: Settings -> Devices & Services -> Helpers, and then click on CREATE HELPER button.

From the available list, let’s choose “Toggle“. Now, let’s name it the same way we did during the configuration in ESPHome. If you used your file, the name will be “rain_gauge_deep_sleep“.

When this switch is turned on, our Rain Gauge will connect to Home Assistant every time it wakes up, send battery status information, and then go back to sleep.

However, if the switch is turned off, the Rain Gauge will remain active for as long as we want. This allows us to calmly and leisurely program OTA the device.

If we turn the switch back on, the device will immediately go into Deep Sleep mode again.


Thanks for sticking to the end! 🙂

If you have any questions or suggestions, feel free to contact me at

Scroll to Top

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.