Introduction: Shower Monitor Arduino With LCD Display

Welcome! For our Environmental Capstone class (senior thesis) for St. Olaf College we built a monitor that would effectively monitor how much water a shower uses. The idea behind this water monitor is that if you attach a piezo transducer (small microphone) to a shower pipe, you can calibrate the average of the vibrations while the shower is turned on, and from there you can see how much water you're using for that shower. For our project we made four different monitors using the same process. This instructable is written as if you were to make a single water monitor.

Here is all the equipment used for this project. These materials are to make one water monitor:

- One Pelican 1010 case
- One Arduino micro
- One Protoboard (comes in packs of 2)
- One LCD display
- One 9V battery
- One 9V Snap Connector
- Soldering equipment
- JB Weld SteelStik Epoxy
- One small piezo transducer
- Straided wire
- Heat shrink tubing
- Arduino software installed on your computer
- One trim potentiometer (similar to this one)
- Three 4.7 KOhm resistors
- One MOhm resistor
- One 1K resistor
- One opamp (411)
- Access to a drill press/handheld drill
- Silicone sealant
- Leather hole punch
- Shower piping

- cable ties


For most of our equipment, we visited our local RadioShack, Menards, and sparkfun.com. Otherwise we we able to access things from our college's electronics lab.

EDIT: Our final product was enhanced with two 9 volt batteries instead of just one, so that the monitor would run longer. These changes were soldered on to the Arduino.

Step 1: Circuitry

For the circuitry we started by using a large breadboard, then a smaller breadboard, and then finally a small protoboard. Our design comes from this instructable by stacey K. We took the original idea and tried it out on a large breadboard, and then got smaller and smaller until we were able to fit all into the 1" square protoboard.

For the connections from the arduino and the potentiometer to the LCD display we followed this Instructable's directions.

Pin 1 to GND
Pin 2 to 5V
Pin 3 to wiper
Pin 4 to Arduino pin 12
Pin 5 to GND
Pin 6 to Arduino pin 11
Pin 11 to Arduino pin 5
Pin 12 to pin 4
Pin 13 to pin 3
Pin 14 to pin 2
(Connecting an LCD to the Arduino by crocboy)

Instead of using a wiper, we had Pin 3 connect to the potentiometer.

When you are connecting the battery to the protoboard, you'll need to cut off the connector so you can solder the wires directly into the protoboard.

After all of the circuitry was soldered, we soldered the LCD display and the Arduino, as well as the straided wire (that had gone through the case, and was attached to the molded piezo) to the circuitry board.

Step 2: Code

Lots of testing and re-checking happened to make the code what it is right now. Things might work differently for your plumbing systems or if you use different equipment throughout the process. This code worked for our project using the equipment and methods that we used.

The picture is purely for demonstration. Image source http://apcmag.com/arduino-masterclass-part-3-tv-w...

For this specific code we downloaded multiple 'libraries' of how to program an arduino to our computers.

Software for Calibrating the Arduino

Fill 5-gallon bucket with water and read off the number on the screen. Divide this number by 5 and you have your conversion factor (con)

#include <LiquidCrystal.h>
#include "RunningMedian.h" #define sensor 0 LiquidCrystal lcd(9, 8, 7, 6, 5, 4);
const float lowlim = 3000;  // use this to differentiate between background noise and water flowing
float val =0; // Current reading for analog pin
float ave; // Running average of the wave amplitude used in calculating Median
float RMSAVE = 0; // Running RMS average used in calculating Median
float start = 0;  // Counter for how long the program runs at each calculation
float interval = 0;  // Used in calculating the length of the 6 sec calculation
float RMStot = 0;
RunningMedian Median = RunningMedian(5);
float sum = 0;
boolean flag = 0;
void setup() {
  pinMode(sensor, INPUT);
  pinMode(17, OUTPUT); // Activated onboard LED (yellow)
  lcd.begin(8,2);
  ave = 530; // set average at midpoint
}
// Compute 1000 averages, then print final value to a file.
void loop(){
  start = millis();
for (int j=0; j < 5; j++){
  digitalWrite(17, LOW);  // Has yellow LED light up when calculating
  for (int i=0; i <= 5000; i++){
    val = (float) analogRead(sensor);
    ave = (ave * 0.999) + (val * 0.001); 
    RMSAVE = (RMSAVE * 0.998) + .002*(abs((ave-val)));
    delayMicroseconds (50); //Modulates the sampling rate (now sampling at ~ 1000 Hz?)
}
// Calcuulates the average for the 6-second period
  interval = millis()-start;
  RMStot = interval*RMSAVE;
  Median.add(RMStot);
}  
  digitalWrite(17, HIGH);  // Has yellow LED light up off when resetting
if(Median.getMedian()>lowlim){
  sum = sum + Median.getMedian();
  flag = 0;
  TXLED1;
}
if (Median.getMedian()
//Print output to LCD-display for calibration
  lcd.clear();
  lcd.setCursor(0, 0);
  lcd.print(Median.getMedian());
  lcd.setCursor(0, 1);
  lcd.print(sum);
}



Software for Monitoring process (saves the shower data to EEPROM [volatile memory on Arduino]):

Install the Running Median library from Arduino.cc before uploading this code. Use empirical calibration program to determine "lowlim" and "con" (lowlim is used to seperate background noise from water running in the pipe, and "con" is the conversion factor used to calculate water use):

// include the library code:
#include<EEPROM.h> #include "RunningMedian.h" #include <LiquidCrystal.h> #define sensor 0 // initialize the library with the numbers of the interface pins - this is lines up with how we soldered the Arduino to the LCD LiquidCrystal lcd(9, 8, 7, 6, 5, 4);
const float con = 234862; //Test-value for non-screen unit
const float lowlim = 7000;  // use this to differentiate between background noise and water flowing
RunningMedian Average = RunningMedian(10); //Running out of memory (SRAM) when this is around 19 - leave plenty space, so no more then 10 adviced I think...
RunningMedian Median = RunningMedian(5);
float val =0; // Current reading for analog pin
float ave; // Running average of the wave amplitude used in calculating Median
float RMSAVE = 0; // Running RMS average used in calculating Median
float start = 0;  // Counter for how long the program runs at each calculation
float interval = 0;  // Used in calculating the length of the 6 sec calculation
float RMStot = 0;
float volume = 0;
float sum = 0;
int numshowers = EEPROM.read(0);
int k = numshowers*4; //Use this to update the number of showers as stored in the zero-index EEPROM
float time = 0;
boolean flag = 0; // Flag for storing when shower is turned off
boolean flag2 = 0; // Flag for switching between "H20 used" and "Average use"
void setup() {
  pinMode(sensor, INPUT);
  pinMode(17, OUTPUT); // Activated onboard LED (yellow)
  lcd.begin(8,2);
  ave = 530; // set average at midpoint
  Serial.begin(9600);
// Need to write function to put what's in EEPROM memory into the RunningAverage function
  int l = 0; // Counter for copying from EEPROM to RunningAverage
  int m = 1; // Counter for finding memory-location in EEPROM to RunningAverage
  float TempAve = 0;
  while (l < EEPROM.read(0)) {
    int ReadAve[3];
    ReadAve[0] = EEPROM.read(m)*100;
    ReadAve[1] = EEPROM.read(m+1)*10;
    ReadAve[2] = EEPROM.read(m+2);
    TempAve=ReadAve[0]+ReadAve[1]+ReadAve[2];
    Average.add(TempAve/10);
    m = m + 4;
    l++;
    delay(50);
  }
}
// Compute 1000 averages, then print final value to a file.
void loop(){
  start = millis();
  for (int j=0; j < 5; j++){
    TXLED1; // Green LED light uo when calculating   
  for (int i=0; i <= 5000; i++){
    val = (float) analogRead(sensor);
    ave = (ave * 0.999) + (val * 0.001); 
    RMSAVE = (RMSAVE * 0.998) + .002*(abs((ave-val)));
   delayMicroseconds (50); //Modulates the sampling rate (now sampling at 1000 Hz?)
  }
      // Calcuulates the average for the 6-second period
    interval = millis()-start;
    RMStot = interval*RMSAVE;
    Median.add(RMStot);
  }  
  
  TXLED0;
  if(Median.getMedian()>lowlim){
    sum = sum + Median.getMedian();  //Updates the "sum", amount used (non-converted) if the varlue is over the threshold
    flag = 1;
    time = time+interval/60000; // Updates how long the shower was
    digitalWrite(17, LOW); // Lights up the yellow LED if above threshold
  }
  
  // The following section saves the data from the shower into the EEPROM memory after the Median drops below the threshold, and then resents the "sum". 
  // Total number of showers recorded is stored in the EEPROM memory location 0 [EEPROM.read(0)]
  if (Median.getMedian()



Software retrieving shower data from EEPROM on Arduino:

Upload the program to the Arduino and open serial monitor to read data into serial monitor window

#include<EEPROM.h>
void setup(){ Serial.begin(9600); delay(10000); // Gives you 10 seconds to open serial port after uploading sketch Serial.println("Initializing the Serial port"); }

int numshowers = EEPROM.read(0); //Records number of showers from the first index in the EEEPROM memory int i = 0; ///Counter that will update how many shower have been printed out to the serial port int k = 1; // Counter that shows which memory address in to get the shower number from int gal;

void loop(){ while (i < numshowers) { delay(100); // Keeps serial port from overloading gal = EEPROM.read(k)*100 + EEPROM.read(k+1)*10+EEPROM.read(k+2); Serial.println(gal); Serial.print("Length of Shower: "); Serial.println(EEPROM.read(k+3)); k = k + 4; i++; } Serial.print("Showers taken: "); Serial.println(numshowers); Serial.print("Data points printed: "); Serial.println(i); delay(10000); // Means that the "Showers taken" & "Data points printed" only is repeated every 10 seconds }

Step 3: Cases

For the water monitor's case we ordered Pelican 1010 cases, as they're a perfect size for all of the parts that were used. Once we received them and removed the small carabiner we used a drill press to make a small hole in the backside of the case. This hole will allow for the wires to connect from the piezo transducer to the aruduino, protoboard, and ultimately the LCD display. We measured the width of the cord we used and made a hole a little smaller, just so that there would be less room for moisture to get into the case.

For the rubber inset of the case we used a leather hole punch to punch a small hole in the material. Make sure the holes line up fairly well! The hole in the rubber inset can be smaller as it will be able to expand for the wire to pass through.

Lastly we used some silicone around the hole on the outside of the case where the wire stuck out, just to make sure that absolutely no moisture got into the case. Although the cases aren't under direct water, they're in an environment with plenty of moisture, so insulating the circuitry and wires is key!

We were able to nestle everything (Arduino, Protoboard, Battery, LCD, and wires) into the inside of the case nicely. For two of our models we glued the LCD screen to the top of the case to show consumers how much water they're using during their shower time.

The pictures show the hole we drilled into the case, how we made the hole in the rubber inset, and what the rubber inset looked like after the hole punch was made.

Step 4: Piezo Mold

We looked into working with large piezos, small piezos, and even flexible piezos. Which piezo you decide to work with will change how you test the arduino and ultimately how you use a mold.

We used a small piezo since it was the piezo that gave us the best results. For the epoxy mold we used JB Weld SteelStik. Wear gloves when handling epoxy! We placed a small piece of rubber on a piece of shower piping that we bought, secured it with a rubber band at either side, and placed a small portion of mixed epoxy on the rubber. After we had shaped the epoxy we placed the piezo on top of it and fashioned another small piece of mixed epoxy on top of that to secure the piezo. Make sure the wires aren't entirely covered! The epoxy that we used set very quickly. We let this cure for 24 hours.

When the mold has cured you can remove it from the pipe and rubber. When you have tested the piezo to make sure it works, or if you want to just go for it, solder the piezo's wires to the wires that connect to the LCD, Arduino, and protoboard (Which has already been fixed through the case).

After we had tested the molded piezo to make sure everything was working correctly, we soldered the piezo's wires to the wires that are connected to the arduino/protoboard. After soldering these wires, we used heat shrink wrapping around that connection.

We then molded more epoxy to the piezo and the straided wires so that the connection would receive less damage from outside forces.

The first picture is of the piezo curing to the rubber which is on the piece of pipe, the second picture is a close up of what the piezo looks like after taking it off of the shower piping and rubber.

Step 5: Testing and Implementation

It's important to keep testing throughout your creative process to make sure that everything is functioning accordingly. We had a couple issues with one piezo we used, a few of the op amps for the protoboard, and the code was a continuous work in progress.

For testing we used a five gallon bucket and changed the code based on whether or not the display reading was close to 5 gallons. For the tests we looked at averages of the results and changed the threshold in the programming to reflect that threshold.

For implementation we hooked up four monitors to different showers, and each monitor ran for about 24-30 hours BUT this was with two batteries instead of one. Some of our plans for future development include replacing the 9 volt battery with a polymer lithium ion battery, so that the monitor would be rechargeable and last much longer.

Green Design Contest

Participated in the
Green Design Contest