Project tutorial
Rainometer

Rainometer © GPL3+

Has your phone ever told you that it rained and you didn't water your plants and then it actually didn't and your plants died? Never Again!

  • 2,021 views
  • 0 comments
  • 6 respects

Components and supplies

Necessary tools and machines

3drag
3D Printer (generic)
09507 01
Soldering iron (generic)

Apps and online services

Ide web
Arduino IDE
Python Idle
Only necessary if you want to edit the python program.
Dp image kit 02
Amazon Alexa Alexa Skills Kit

About this project

Rainometer Introduction

Has the weather station ever told you that there was plenty of rain and you thought oh sweet I don't have to water all of my plants or my grass! SWEET! And then,

Don't you hate that feeling!

Or, you invite a bunch of friends over to play outside.

When your friends get to your house and you go to your yard to play, you realize that it rained more than your weather app said it had overnight.

With Rainometer, the rain gauge that talks with Alexa, you won't have to feel that feeling ever again! And you won't even have to go outside to check it, you simply say "Alexa, ask Rainometer how many centimeters has it rained in the past 6 hours?" or "Alexa, ask Rainometer how many centimeters did it rain on Friday" Alexa will tell you exactly how much it rained down to the quarter of a centimeter!

Finished Product Example

Hardware

  • Arduino uno

The Arduino will read data from the hall effect sensor and talk to a python program over a serial port.

  • Amazon Alexa Echo Dot

The Amazon Alexa Echo Dot can recognize speech, and skills for the echo dot are easy to make. In our project, the echo dot will take user voice input, then respond with the response generated by a python program.

  • Raspberry Pi

Any Raspberry pi will work. The Raspberry pi talks to both the Arduino over serial and Alexa over the internet. You will need to make sure that your Raspberry Pi is connected to the internet either over Ethernet or WiFi.

  • Hall effect Sensor

A Hall effect sensor is a transducer that varies its output voltage in response to a magnetic field. Hall effect sensors can sense how close they are to magnets. In this project, when the rain gauge is tipped one way, the hall effect sensor sends a positive signal because the magnet is closer to it.

How it was Built

Building Rainometer was a fun process. First, the CAD was designed in Autodesk Fusion 360. Here's a video showing it being designed.

Next, the hall effect sensor was wired up and plugged into the Arduino. Schematics can be found in the Schematic section.

Finally, all of the code was written and uploaded to the Raspberry Pi and Arduino. The code for this project can be found below.

Building Rainometer was a great way to learn new skills for working with Arduino, Raspberry Pi, and Alexa. The end result is also useful and fun.

VUI Diagram

Here's how Rainometer works:

Build Instructions

To build your own Rainometer, first gather the materials. You will need a Raspberry Pi, an Arduino, and a hall effect sensor. You will also need to 3d print the plastic parts that can be found in the CAD section. Wire up the hall effect sensor as shown below.

Glue a magnet into the hole in the 3d printed rocker piece. Glue your hall effect sensor underneath the magnet.

Code Setup

Next, you will need to set up the code. On your Raspberry Pi, make sure you have python installed, then run the following commands.

sudo apt-get install python-pip
sudo apt-get install python-serial
sudo pip install Flask flask-ask

The next step is to download ngrok. Ngrok allows you to connect strait to your Alexa skill over the internet.

Go to the following page and download the latest version of ngrok:

https://ngrok.com/

Find the directory when ngrok is installed and run it using the following command:

./ngrok http 5000 

After running the command, look for where it says "Forwarding:" and copy the HTTPS address. (i.e. https://ese18274.ngrok.io)

Set up the Alexa Skill

Now, go to the Alexa developer website and create a new skill

https://developer.amazon.com/edw/home.html#/

For help creating a developer account, go to the following link:

https://developer.amazon.com/docs/custom-skills/register-and-manage-alexa-skills-in-the-developer-portal.html

Once you are on this page, click "Alexa Skills Kit", then "Add a New Skill"

Enter "Rainometer" for both the skill name and the invocation name and click save.

First, click "Interaction Model". Next, launch Skill Builder Beta, click code editor, and paste the code below into the code box.

{
 "languageModel": {
   "intents": [
     {
       "name": "AMAZON.CancelIntent",
       "samples": []
     },
     {
       "name": "AMAZON.HelpIntent",
       "samples": []
     },
     {
       "name": "AMAZON.StopIntent",
       "samples": []
     },
     {
       "name": "AtHourRainIntent",
       "samples": [
         "How much did it rain at {Number} o'clock",
         "How much did it rain at {Number}",
         "How much rain was there at {Number} o'clock",
         "How much rain was there at {Number}"
       ],
       "slots": [
         {
           "name": "Number",
           "type": "AMAZON.NUMBER"
         }
       ]
     },
     {
       "name": "DateRainIntent",
       "samples": [
         "How much did it rain on {DATE}",
         "How much did it rain {DATE}",
         "How much rain was there on {DATE}",
         "How much rain fell on {DATE}"
       ],
       "slots": [
         {
           "name": "DATE",
           "type": "AMAZON.DATE"
         }
       ]
     },
     {
       "name": "LastHoursRainIntent",
       "samples": [
         "How much did it rain in the past {Number} hours",
         "How much rainfall was their in the past {Number} hours",
         "how many centimeters of rainfall was there in the past {Number} hours",
         "How many centimeters of rain was there in the past {Number} hours"
       ],
       "slots": [
         {
           "name": "Number",
           "type": "AMAZON.NUMBER"
         }
       ]
     }
   ],
   "invocationName": "rainometer"
 }
}

Apply Changes and click "build model." Once that is done, move on to configuration.

Select HTTPS as your endpoint. Where it says "default," paste in the HTTPS address that ngrok gave you on the Raspberry Pi (i.e. https://ese18274.ngrok.io)

Click save and go on to SSL Certificate. In SSL Certificate, select "My development endpoint is a sub-domain of a domain that has a wildcard certificate from a certificate authority"

Once you save that, your skill should be done.

Arduino and Raspberry Pi Code

Download the Arduino code and both python codes. To upload the code to your Arduino. Open the code in the Arduino IDE found here:

https://www.arduino.cc/en/Main/Software

Under tools->ports, select your arduino port and click upload.

To setup your Raspberry Pi, run both ArduinoRainGauge.py and AlexaRainGauge.py by double clicking on them. If the Arduino python program gives an error because it can't connect to your Arduino, make sure that your Arduino is plugged into the Raspberry Pi and that the port in the program is the port that the Arduino is plugged into. Once both programs are running, also make sure that your ngrok server is running as explained above.

To test everything, ask Alexa to open rainometer. Your Rainometer is finished! Feel free to ask questions if you cannot get everything setup.

Code

AlexaRainGauge.pyPython
This code runs on the Raspberry Pi, and when the user calls an Alexa Intent, it reads data saved in text files and outputs the response.
'''
This code is for the Alexa + Arduino smart home compotion
This takes data from files created by the ArduinoRainGauge program, which measures rainfall, and
stores it. When the user asks how much rain has fallen on any
given day, it comes up with a response

For this to work with Alexa, install ngrok and run the following command:
./ngrok http 5000
'''

#import modules to talk to Alexa over the internet
import requests, json, datetime
from flask import Flask
from flask_ask import Ask, statement, question



#get the amount it rained in the last X hours. For example, "How much did it rain in the last 4 hours?"
def GetRainInLastHours(hours):
    file = open("rain48Hours", "r")
    rain48Hours = file.readline().split(',')
    file.close()
    totalRain = 0
    for i in range(hours):
        totalRain += float(rain48Hours[i])
    return totalRain

#get the amount of rain at a certain time. For example, "How much did it rain at 3:00?" (military time)
def GetRainAtTime(hour):
    file = open("rainToday", "r")
    rainToday = file.readline().split(',')
    file.close()
    return float(rainToday[hour])

#get the amount of rain for a specific day
def GetRainForDate(day,month,year):
    #if the given date is today, the file won't be save yet, so total the amount so far
    date = datetime.datetime.now()
    if day == date.day and month == date.month and year == date.year:
        totalRain = 0
        file = open("rainToday", "r")
        rainToday = file.readline().split(',')
        file.close()
        for rainInHour in rainToday:
            try:
                totalRain += float(rainInHour)
            except:
                pass
        return totalRain
    else:
        try:
            #open the file and read the number of centemeters of rain
            fileName = str(month) + "-" + str(day) + "-" + str(year)
            file = open(fileName, "r")
            rainAmount = file.readline()
            file.close()
            return float(rainAmount)
            
        #there is no file for the date because the program wasn't running then
        except:
            return 0 #(no data recorded)

#connect to alexa
#the following functions are run when the user asks alexa for a specific intent

app = Flask(__name__)
ask = Ask(app, '/')

#all responces are questions so that the session doesn't end
@ask.launch
def Launch():
    return question("Welcome to rainometer.").reprompt("Say help for a list of things you can say.")

@ask.intent("AMAZON.HelpIntent")
def HelpIntent():
    return question("You can ask rainometer things like how much did it rain in the last five hours? or how much did it rain last Tuesday? If you want to quit, simply say stop")

@ask.intent("AMAZON.StopIntent")
@ask.intent("AMAZON.CancelIntent")
def StopIntent():
    return statement("Thank you for using rainometer.")

@ask.intent("DateRainIntent")
def DateRainIntent(DATE):
    date = datetime.datetime.now()
    response = ""
    #if no date is given, assume today
    if DATE == None:
        rain = GetRainInLastHours(date.hour)
        response = "It has rained " + str(rain) + " centimeters today."

    else:
        year = int(DATE[:4])
        month = int(DATE[5:7])
        day = int(DATE[8:10])
        #if the date is today's date, return rain for today
        if year == date.year and month == date.month and day == date.day:
            rain = GetRainInLastHours(date.hour)
            response = "It has rained " + str(rain) + " centimeters today."
        else:
            rain = GetRainForDate(day,month,year)
            response = "It rained " + str(rain) + " centimeters on " + DATE + "."
    return question(response)

@ask.intent("LastHoursRainIntent")
def LastHoursRainIntent(Number):
    if Number == None: #If they don't specify a number, set it to one
        Number = 1
    rain = GetRainInLastHours(int(Number))
    response = "It has rained " + str(rain) + " centimeters in the last " + str(Number) + " hours."
    return question(response)

@ask.intent("RainAtHourIntent")
def AtHourRainIntent(Number):
    rain = GetRainAtTime(Number)
    response = "It rained " + str(rain) + " centimeters at " + str(Number) + " o'clock."
    return question(response)

#run the flask app which talks to alexa
app.run(debug=True)
AlexaRainGauge.inoArduino
This code runs on the Arudino and sends serial data to the Raspberry Pi when the rain gauge is tipped.
/*
 * This code runs on the Arduino and sends a 'T' character over serial
 * when the rain gauge is tipped.
 */

const int HallEffectSensorPin = A0; //where the hall effect sensor is plugged in

int hallEffectValue; //the current value of the hall effect sensor (depends on location of the magnet on the rain gauge and is updated every loop)

int currentTippingState; //can be either 1 or 0 depending on which way the rain gauge is tipped
long int tippingTimer; //used to keep track of how long the rain gauge has been tipped one way

//compare data from the hall effect sensor and change the tipping state if the rain gauge has tipped
void UpdateTippingState()
{
  //as long as the rain guage remains in the same state, keep reseting the timer
  if(hallEffectValue == currentTippingState){
    tippingTimer = millis();
  }
  
  //if the rain gauge tipped and has been tipped for at least half of a second (to debounce the switch) 
  else if (millis() - tippingTimer > 500){
    
    //set the new state and tell the raspberry pi that the rain gauge has tipped
    currentTippingState = hallEffectValue;
    
    Serial.println('T'); //T stands for tipped
  }
}

//setup everything
void setup() {
  pinMode(HallEffectSensorPin,INPUT_PULLUP);
  Serial.begin(9600);

  //read which way the rain guage is tipped and set the starting tipping state
  currentTippingState = digitalRead(HallEffectSensorPin);
}

//every loop, read data from the hall effect sensor and compare it to previous data
void loop() {
  hallEffectValue = digitalRead(HallEffectSensorPin);
  UpdateTippingState();

  //delay because the program doesn't need to run fast
  delay(100);
  //sent a new line character so that the python program doesn't just keep waiting
  Serial.println("");
}
ArduinoRainGauge.pyPython
This code runs on the Raspberry Pi and reads serial data from the Arduino. It saves the data to text files that can be read by the Alexa rain gauge program.
'''
This program is for the Alexa + Arduino smart home compotition.
It reads data from an Arduino sent over serial and saves the data to text files
'''

#import modules to talk to arduino and store data under correct date
import serial, datetime


#how much it has rained in the last hour
rainLastHour = 0

#how much it has rained each hour of the day
rainToday = [0] * 24

#how much it has rained in the last 24 hours
rain48Hours = [0] * 48

#when data was last saved
lastSavedDay = 0
lastSavedHour = 0

#save data hourly, write data to files daily
def SaveData(time):
    
    global lastSavedDay
    global lastSavedHour
    global rainLastHour
    global rainToday
    global rain48Hours

    #if data has not been recoreded for this hour, record it
    if lastSavedHour != time.hour:
        #save rain for that hour
        rainToday[time.hour] = rainLastHour

        #save the rain in the "last 48 hour" list. Remove the last item and add this to the begining
        #if this is being called because there was a reading from the rain gause
        if(lastSavedHour == -1):
            rain48Hours[0] = rainLastHour
        else:
            del rain48Hours[len(rain48Hours) - 1]
            rain48Hours.insert(0,rainLastHour)

        #write to files
        rain48HoursString = ""
        for hour in rain48Hours:
            rain48HoursString += str(hour)
            rain48HoursString += ","
        file = open("rain48Hours", "w")
        file.write(str(rain48HoursString))
        file.close()
        rainTodayString = ""
        for hour in rainToday:
            rainTodayString += str(hour)
            rainTodayString += ","
        file = open("rainToday", "w")
        file.write(str(rainTodayString))
        file.close()
        
        #reset
        if(lastSavedHour != -1):
            rainLastHour = 0
        lastSavedHour = time.hour   

    
    #if data has not been saved for yesterday, save it
    if lastSavedDay != time.day:

        #add up the total rain for the day
        totalRain = 0
        for amountInHour in rainToday:
            totalRain += amountInHour
        
        #write it to a new text file with the name of the file being the date
        yesterday = time - datetime.timedelta(days=1)
        fileName = str(yesterday.month) + "-" + str(yesterday.day) + "-" + str(yesterday.year)
        file = open(fileName, "w")
        file.write(str(totalRain)) #in centemeters
        file.close()

        #reset
        rainToday = [0] * 24
        rainTodayString = ""
        for hour in rainToday:
            rainTodayString += str(hour)
            rainTodayString += ","
        file = open("rainToday", "w")
        file.write(str(rainTodayString))
        file.close()
        lastSavedDay = time.day

#read data from arduino. If it indicates that the scale has tipped, add to the amount of rain for this hour
def ReadArduino():

    global rainLastHour

    #read from Arduino and check if it sent the signal that the gauge had been tipped
    line = ser.readline()
    if line.startswith("T"):
        print("Rained a bit...")
        rainLastHour += 0.25 #Calculated by dividing the area of the opening by the volume of water that it takes to tip the guage
                                #Actual value is 0.262, but it is rounded for convinience
        #make sure the data is saved
        global lastSavedHour
        lastSavedHour = -1

#connect to the arduino
ser = serial.Serial("/dev/ttyACM0",9600)

#loop that runs consistantly (can be stopped with KeyboardInterupt)
shouldQuit = False
print("Started")

while not shouldQuit:
    try:
        #get the date and time of day
        time = datetime.datetime.now()

        #Get data from the arduino and add to the amount of rain this hour if the rain guage tipped
        ReadArduino()
        
        #every hour, record data. Every day, save data in a new file
        SaveData(time)

    except KeyboardInterrupt:
        shouldQuit = True
        print("quitting...")
        ser.close()

    

Custom parts and enclosures

Full Assembly Picture
A screen shot of what the full assembly looks like.
Rainometer assembly gx6bsaktme
raingauge_part_1_l5DPPQmUax.stl
raingauge_part_2_suEn1IG2D6.stl
raingauge_part_3_Mt5iEfam6D.stl
raingauge_part_4_itkGAmb3LM.stl
Whole Assembly for Fusion 360
If you want to edit the design in Autodesk Fusion 360, download this design of the whole Assembly
rain_gauge_v7_ejkKCguqwL.f3d

Schematics

Scematic
Scematic rt6lch8gtj

Comments

Similar projects you might like

Sputnik Spotlight

Project in progress by Joster

  • 1,257 views
  • 0 comments
  • 4 respects

Enable Alexa Control to your Ceiling Fan

Project tutorial by Jithin Thulase

  • 3,123 views
  • 5 comments
  • 9 respects

(SAL) Sonar&Alexa Lights

Project in progress by Matthew Reed

  • 864 views
  • 0 comments
  • 0 respects

Secure Package Delivery Trunk for Your Front Porch

Project tutorial by Castle Locker

  • 2,968 views
  • 1 comment
  • 19 respects

Smart Smokes

Project tutorial by oleolsonfablab

  • 933 views
  • 0 comments
  • 3 respects

Herb Box Eco System

Project tutorial by Walter Heger

  • 37,028 views
  • 20 comments
  • 239 respects
Add projectSign up / Login