How to Make a Smart Rain Gauge
2018-10-22 | By Maker.io Staff
License: None Programmers Arduino Raspberry Pi
Rain sensors are very trivial, consisting of a pair of electrodes that send a signal when rain is detected. Determining how much rain has fallen, however, is not as easy. In this project, we will build a smart rain depth gauge.
Things You Will Need
- Arduino Uno
- Copper clad material
- 100R resistor
- 1K resistor
- USB B cable
- PC, Linux, Mac, or Raspberry Pi with Raspbian
- Python 3
Check out the full schematic here.
Adafruit.IO Setup
This month, we learned how to set up and use Adafruit.io, so we now need to take this knowledge and do the following.
- Create an Adafruit.io account.
- Create a new feed called “WaterLevel”.
- Create a dashboard called “WaterLevel”.
- Add a gauge item to our dashboard whose min value is 0 and max value is 100.
Water level feed
Water level dashboard
The main dashboard
We will also need to get our private key so we can send messages to Adafruit.io. You can find your key by either clicking the key icon or by clicking “View AIO Key” in the menu.
How It Works: The Water Sensor
The water sensor in this project is incredibly simple, and it is based on soil sensors. Two prongs are placed inside a rain capture container, and, as the level rises, the resistance between the two probes decreases. One of our probes is connected to 5V through a 100R resistor, while the other probe is directly connected to the base of a transistor. As the resistance between the two probes decreases, the current flowing through the base of the transistor increases, and, therefore, the current flow through the transistor increases. This increase in current flow results in a bigger potential difference across the 1K resistor, and, therefore, the voltage output of the water level sensor increases.
How It Works: The Arduino
The Arduino’s function is to take readings from the water level sensor and then transmit this information to a connected computer using a serial port. But raw readings from the water sensor circuit need to be processed before they can be sent, as the output from the water sensor is not linear. Processing the data is somewhat tricky, as the data shows a somewhat logarithmic output, but trying to convert the readings to a linear pattern using logs proved futile. Instead, a more trivial approach was taken, but the result was surprisingly accurate!
Firstly, raw readings are taken at these water levels: full, half, quarter, and empty. The full and empty levels are then saved to two variables, maxValue and minValue respectively (this is used to determine the range of possible values). Then, the recorded value has the minValue subtracted from it, which creates a 0-adjusted output (i.e., no water = 0). The last step involves converting the 0-adjusted value to a linear scale of 0 to 100. This is achieved by splitting our reading into four comparisons, which check for half, quarter, and eighth ranges. When the value from the sensor falls into one of these categories, we map the reading to a linear scale using the map function. While this is crude, it is effective enough for a rough estimation of the water level. The last stage in the code is to print the linear value to the serial port so our Python program can send the data to Adafruit.io. You may notice that we have two print instructions; the first print function prints the value, while the second print function prints a newline command, which is needed by the Python readline serial function.
const int analogInPin = A0; // Analog input pin that the potentiometer is attached to
const int minValue = 630; // Used for creating a linear scale
const int maxValue = 750;
float value;
float difference = maxValue - minValue;
void setup()
{
// put your setup code here, to run once:
Serial.begin(9600);
}
void loop()
{
// Read the value from the sensor
value = analogRead(analogInPin);
// Zero adjust the result
value = value - minValue;
// Check if the value has fallen below the min value
if(value < 0)
value = 0;
// Convert the reading to a 0 to 100 scale
value = (value * 100) / difference;
// If the reading goes beyond 100 then max it out
if(value >= 100)
value = 100;
// ***********************************************************
// Non-linear to linear simple conversion
// Water level 50% to 100%
if(value > 80)
{
value = map(value, 80, 100, 50, 100);
}
// Water level 25% to 50%
else if(value > 50)
{
value = map(value, 50, 80, 25, 50);
}
// Water level 0% to 25%
else
{
value = map(value, 0, 50, 0, 25);
}
Serial.print(value);
Serial.print("\n");
delay(3000);
}
How It Works: The Python Program
Using IoT services can be tricky at times, but this project perfectly demonstrates why Adafruit.io is arguably one of the best services out there and why Python is a beautiful language!
First, install the adafruit python libraries. Open a terminal window or command prompt and use the following PIP command:
pip install adafruit-io
You will also need the serial library, which can be installed using the following PIP command:
pip install pyserial
The adafruit.io library is incredibly easy, and, for our application, we only need to learn two functions:
- Client(key): This creates an object where the key is our private key.
- .send(feed, value): This sends value to the feed.
The first portion of our program imports the relevant libraries, creates an adafruit.io object called adafruitio, and creates a serial port object called serialPort. The main bulk of the Python code simply reads data from the serial port and then attempts to write this value to adafruit.io using the send command. You may have noticed the typecasting when assigning the waterlevel value to serialstring; this is needed because the serialstring value will be a string and not a float.
from Adafruit_IO import Client
import serial
# Adafruit IO related variables
adafruitKey = "b9aaf9f4054849429c8f32cbf2e3af5a"
adafruitio = Client(adafruitKey)
# Create a serial port object
serialPort = serial.Serial(port = "COM10", baudrate=9600,
bytesize=8, timeout=2, stopbits=serial.STOPBITS_ONE)
# Program variables
waterlevel = 0
serialstring = ""
# Main Program Loop
while(1):
serialstring = serialPort.readline()
try:
print("Reading taken")
waterlevel = float(serialstring)
adafruitio.send('WaterLevel', waterlevel)
print("Sent to Adafruit IO")
except:
pass
from Adafruit_IO import Client
import serial
# Adafruit IO related varibles
adafruitKey = "b9aaf9f4054849429c8f32cbf2e3af5a"
adafruitio = Client(adafruitKey)
# Create a serial port object
serialPort = serial.Serial(port = "COM10", baudrate=9600,
bytesize=8, timeout=2, stopbits=serial.STOPBITS_ONE)
# Program variables
waterlevel = 0
serialstring = ""
# Main Program Loop
while(1):
serialstring = serialPort.readline()
try:
print("Reading taken")
waterlevel = float(serialstring)
adafruitio.send('WaterLevel', waterlevel)
print("Sent to Adafruit IO")
except:
pass
Construction and Testing
This project is very easy to construct using only an Arduino Uno, some wire, and a breadboard. The water level sensor itself was built using three pieces of copper clad material, which are stuck together using super glue. Make sure that the probes are not electrically connected at all. When sticking the cross piece to the two probes, make sure the copper sides all face away from each other.
Ideally, the project should be located near a container that collects rain water, so an enclosure may be helpful. However, long wires could instead be used on the probes. They would rest outside while the rest of the project sits on a windowsill.
Have questions or comments? Continue the conversation on TechForum, DigiKey's online community and technical resource.
Visit TechForum