Dusch-o-Meter

Track your water consumption and save money with Dusch-o-meter, the smart device for eco-friendly showers.

BeginnerProtip270
Dusch-o-Meter

Things used in this project

Hardware components

Photon
Particle Photon
×1
Breadboard (generic)
Breadboard (generic)
×1
Jumper wires (generic)
Jumper wires (generic)
×1
Water Flow Sensor
×1

Software apps and online services

Maker service
IFTTT Maker service
ThingSpeak API
ThingSpeak API

Story

Read more

Schematics

Wiring Schematic

Code

dusch-o-meter-driver.ino

C/C++
Copy and Paste
// This #include statement was automatically added by the Particle IDE.
#include <ThingSpeak.h>

#define END_SHOWER_TIMEOUT 5 * 60 // 5 minutes.
#define WATER_LIMIT 150.0 // water limit after which notification is triggered.

const int Output_Pin = D2; // The sensor Pulses are incoming here.

// Global variables for fetching and calculating water flow values from the sensor.
volatile int Pulse_Count = 0;
unsigned int Liter_per_hour;
unsigned long Current_Time, Loop_Time;

double used_water = 0;         // Amount of water used during showering.
unsigned long off_counter = 0; // if no water is flowing increment until specified timeout to detect shower process ending.
bool already_pushed = false;   // bool to detect if data
bool limit_exceeded = false;   // if exceeding water limit detected.

TCPClient client;

// API Keys for ThingsPeak --> here go your own API key and channel.
unsigned long channelNr = 31461;
const char *writeapikey = "LD79EOAAWRVYF04Y";

void setup()
{
    pinMode(Output_Pin, INPUT);
    attachInterrupt(D2, Detect_Rising_Edge, FALLING);

    Current_Time = millis();
    Loop_Time = Current_Time;
    // initialize thingSpeak tcpClient
    ThingSpeak.begin(client);
}

// calculates the amount of water used within 1s in liters
// return: consumed water in liters for 1s.
double calc_used_water_1s(unsigned int act_consumption)
{
    return (double)act_consumption / 3600;
}

// after some minutes with no water flowing publish data to server and reset water counter, reset limit exceeded.
void reset_and_store_data()
{
    ThingSpeak.writeField(channelNr, 1, (int)used_water, writeapikey); // vielleicht benachrichtigung ueber Abschluss.
    Particle.publish("Showering_Data_pushed", String(used_water));
    already_pushed = true;
    limit_exceeded = false;
    used_water = 0;
}

void loop()
{
    Current_Time = millis();
    if (Current_Time >= (Loop_Time + 1000)) // only calculate water flow rate every 1s
    {
        Loop_Time = Current_Time;
        Liter_per_hour = (Pulse_Count * 60 / 6.6); // Pulse_count == anz Pulse/s [Hz],  7.5 constant, 60 scaling to l/h
        // add used water to the amount of total used water.

        // Print the amount of water that was used up until now.
        Particle.publish("Used_water", String(used_water));

        // add used water to session water counter.
        used_water += calc_used_water_1s(Liter_per_hour);

        if (Pulse_Count > 0) // Pulse count only gets reset if Water is really flowing.
        {
            // add used water to the amount of total used water.
            Particle.publish("Rate:", String(Liter_per_hour));
            Pulse_Count = 0;

            // set already_pushed to false to push showering stats to cloud.
            already_pushed = false;
            // Reset off_time when measurement is started.
            off_counter = 0;
        }
        // no water flowing.
        else
        {
            // off counter measures the time in seconds that the shower is not working.
            if (already_pushed == false && off_counter < END_SHOWER_TIMEOUT)
            {
                off_counter++;
                Particle.publish("off_counter", String(off_counter));
            }
            else if (already_pushed == false && off_counter >= END_SHOWER_TIMEOUT)
            {
                // after timeout push used water to cloud and reset variables for next showering session
                reset_and_store_data();
            }
        }
    }

    // Send notification if consumed water is over defined limit (default 150l)
    if (used_water >= WATER_LIMIT && limit_exceeded == false) // good limit 150l
    {
        Particle.publish("Water_limit_reached", String(used_water)); // publish IFTTT message.
        limit_exceeded = true;
    }
}

// ISR when rising edge is detected.
void Detect_Rising_Edge()
{
    Pulse_Count++;
}

Credits

Daniel Fritz

Daniel Fritz

1 project • 2 followers
Raphael Andree

Raphael Andree

0 projects • 2 followers
Janfabian Fabriczek

Janfabian Fabriczek

3 projects • 3 followers
Embedded Software Expert @ Festo SE & Co. KG; Lecturer IoT Ecosystems @ Hochschule Esslingen
Dionysios Satikidis

Dionysios Satikidis

17 projects • 32 followers
Dev. Experience Platforms Guy at Mercedes-Benz Lecturer IoT Ecosystems & Applied AI at Brunel Univ. and Univ. of Applied Sciences Esslingen.

Comments