Heather King
Published © GPL3+

Rainometer

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!

IntermediateFull instructions provided3 hours5,353
Rainometer

Things used in this project

Hardware components

Raspberry Pi 1 Model B
Raspberry Pi 1 Model B
×1
Arduino UNO
Arduino UNO
×1
Echo Dot
Amazon Alexa Echo Dot
×1
Hall Effect Sensor (unipolar, non-latching)
×1
Generic 20mm Round Magnet
×1

Software apps and online services

Arduino IDE
Arduino IDE
Python Idle
Only necessary if you want to edit the python program.
Alexa Skills Kit
Amazon Alexa Alexa Skills Kit

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)
Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Custom parts and enclosures

Full Assembly Picture

A screen shot of what the full assembly looks like.

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

Schematics

Scematic

Code

AlexaRainGauge.py

Python
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.ino

Arduino
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.py

Python
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()

    

Credits

Heather King

Heather King

2 projects • 0 followers

Comments