Things used in this project

Hardware components:
Sparkfun Photon Redboard
Using this version of Photon because of Arduino form factor and fits in Arduino cases! (You can use any particle photon form factor)
×1
51gzz5eu9pl. sx425
HC-SR04
Suggested link (need 1 sensor), but cheaper to buy a 3 or 5 pack from your favorite online source.
×1
Arduino Compatible "Mega" Large Enclosure
Suggested Link (need Mega compatible case to fit both Photon and Sensor). This one is easy to modify!
×1
Female to Male Jumper Connectors
Suggested Link (need any female connectors to make jumpers).
×1
5V Power
Suggested Link
×1
Software apps and online services:
Ubidots
Ubidots
Q8wtlimqnp04fzhtr9v5
IFTTT Maker service
Hand tools and fabrication machines:
Tool to modify plastic enclosure
Use metal filer or 3/8'' drill bit

Schematics

Sump Monitor V2
Schematic changes Pinout to trigger D2, echo D6
sumppumpmonitorv2_ZzkIryioN0.fzz

Code

SumpMonitorV2C/C++
Copy and Paste into Particle.IO IDE
// This #include statement was automatically added by the Particle IDE.
#include <HttpClient.h>
#include "application.h"

//#define TOKEN "paste_ubidots_token_here"

// GET TOKEN ID AND VARIABLE ID FROM YOUR UBIDOTS ACCOUNT

//String SUMP_WATER_LEVEL   = "paste_ubidots_variable_here";  // Sump Water Level Variable
//String SUMP_FREQUENCY     = "paste_ubidots_variable_here";  // Sump Frequency of discharge in hours
//String SUMP_CYCLES        = "paste_ubidots_variable_here";  // Sump Cycles
//String SUMP_GALLONS       = "paste_ubidots_variable_here";  // Sump Gallons Discharged
//String SUMP_HEARTBEAT     = "paste_ubidots_variable_here";  // Sump Monitor Status
//String ADC_POSITION       = "paste_ubidots_variable_here";  // Analog Position if you have one


// HTTP Client Variables
String resultstrdata;
char resultstr[64];    // used to buffer sprintf(x) function
HttpClient http;

http_header_t headers[] = {
    { "Content-Type", "application/json" },
    { "X-Auth-Token" , TOKEN },
    { NULL, NULL } // NOTE: Always terminate headers will NULL
};

http_request_t request;
http_response_t response;

/*
 ******************************************************************************
 *  Copyright (c) 2015 Particle Industries, Inc.  All rights reserved.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
 *
 * -----------------------------------
 * HC-SR04 Ping / Range finder wiring:
 * -----------------------------------
 * Particle - HC-SR04
 *      GND - GND
 *      VIN - VCC
 *       D2 - TRIG
 *       D6 - ECHO
 */

//int buffer[4]; // buffer needed for sprintf() function

unsigned int data_samples, temp_val;       // used for buffering sonic sensor and getting average of 60 samples
unsigned int analogvalue, adc_voltage;     // if using position sensor then buffering not needed

unsigned int heartbeat_counter;            // used to count every sample sent to ubidots
unsigned int dischargefreq_counter;        // used to count time between cycles
unsigned int cycle_counter;                // used to count number of cycles
unsigned int cycle_latch;                  // used to flag a complete discharge
unsigned int current_value;                // water level taken
unsigned int last_hour_value;              // water level taken last hour

unsigned int seconds_counter, minutes_counter, hours_counter; // used to count time for main loop
unsigned int inches, centimeters, seconds, minutes;
unsigned int flag_1sec, flag_1min, flag_60min;

#define SERIAL_BAUDRATE 115200     // 115200 kbit/s
#define HEARTBEAT_COUNT_LIMIT 15   // 15 min
#define MONITOR_TIMEOUT_LIMIT 1439 // 1439 min (24 hours)
#define SUMP_BASIN_VOLUME 14       // 14 gal. per cycle (based on volume of water level)
#define SUMP_BASIN_HEIGHT 54       // 54 cm
#define LATCH_LOWER_LIMIT 12       // 12 cm
#define LATCH_UPPER_LIMIT 30       // 30 cm

Timer Time_millisecs( 1000, CountSeconds); // 1 second timer interrupt to run the main loop

void setup() {
    init_system();             // initialize variables and pin IO
    Time_millisecs.start();    // start timer
}

void loop() {
    
    if ( flag_1sec == TRUE ) { // 1 second loop
        flag_1sec = FALSE;
        get_signals();
    }
    
    if ( flag_1min == TRUE ) { // 1 minute loop
        flag_1min = FALSE;
        SoftwareInterrupt_1min();
        calc_sump_stats();
    }
    
    if ( flag_60min == TRUE ) { // 1 hour loop
        flag_60min = FALSE;
        SoftwareInterrupt_60min();
        check_sensor_operation();
    }
}

void CountSeconds() {
    
    seconds_counter++;
    flag_1sec = TRUE;
    
    if ( seconds_counter > 59 ) {
        seconds_counter=0;
        flag_1min = TRUE;
        minutes_counter++;
        dischargefreq_counter++;
        
        if ( minutes_counter > 59 ) {
            minutes_counter = 0;
            flag_60min = TRUE;
            hours_counter++;
        }
    }
    
    return;
}

void SoftwareInterrupt_1min(void) {
    
    centimeters = data_samples/60; // simple average filter to deal with noisy sonic sensor
    data_samples = 0;              // get 60 samples and average them, then clear buffer
    //get_ADC();                   // this is a position sensor, no filtering required, just read ADC
    // leave commented if not using position sensor
    send_to_ubidots();
    
    return;
}

void SoftwareInterrupt_60min(void) {
    
    last_hour_value = centimeters;
    //sprintf(resultstr, " %d", last_hour_value);
    //resultstrdata = resultstr;
    //Particle.publish("Sump Monitor", "Latest Hour Water Level was" + resultstrdata + " cm", 60, PRIVATE);
    
    return;
}

void get_signals(void) {
    
    temp_val = ping(D2, D6, 10, false);    // get sonic sensor sample
    
    if ( temp_val > SUMP_BASIN_HEIGHT ) temp_val = SUMP_BASIN_HEIGHT;  // use max limit
    
    data_samples += SUMP_BASIN_HEIGHT-temp_val;    // subtract sensor reading to calc actual water level
    
    return;
}

void calc_sump_stats(void) {
    
    if ( centimeters > LATCH_LOWER_LIMIT ) {
        
        if ( centimeters > LATCH_UPPER_LIMIT ) cycle_latch = TRUE;
        return;
    }
    
    else {
        if (cycle_latch) {
            cycle_counter++;
            
            sprintf(resultstr, "{\"value\":%d}", dischargefreq_counter );
            send_http_post(SUMP_FREQUENCY, resultstr); //USE THIS TO SEND DISCHARGE TIME
            
            sprintf(resultstr, "{\"value\":%d}", cycle_counter);
            send_http_post(SUMP_CYCLES, resultstr); //USE THIS TO SEND CYCLES
            
            sprintf(resultstr, "{\"value\":%d}", cycle_counter*SUMP_BASIN_VOLUME);
            send_http_post(SUMP_GALLONS, resultstr); //USE THIS TO SEND GALLONS
            
            //Particle.publish("Sump Monitor", "Pump has discharged", 60, PRIVATE);
            
            dischargefreq_counter = 0;
            cycle_latch = FALSE;
            return;
        }
        
        return;
    }
}

void check_sensor_operation(void) {
    
    if ( (centimeters == 60) && (last_hour_value == 60) ) {
        
        // compare sensor reading to check if sonic sensor is stuck or faulted
        sprintf(resultstr, "{\"value\":%d}", 0);
        send_http_post(SUMP_WATER_LEVEL, resultstr);
        Particle.publish("Sump Monitor", "System Reset Required", 60, PRIVATE);
        System.reset();
    }
    
    if ( minutes_counter > MONITOR_TIMEOUT_LIMIT ) reset_ubidots_stats();
    
    return;
}

void send_http_post(String VARIABLE_ID, char* result_str) {
    request.port = 80;
    request.hostname = "things.ubidots.com";
    request.path = "/api/v1.6/variables/" + VARIABLE_ID + "/values";
    request.body = result_str;//Sending presence to Ubidots
    http.post(request, response, headers);
}

void send_to_ubidots(void) {
    //sprintf(resultstr, "{\"value\":%d}", adc_voltage);  // format water level to string
    //send_http_post(ADC_POSITION, resultstr);            // send string result to ubidots
    
    sprintf(resultstr, "{\"value\":%d}", centimeters);  // format water level to string
    send_http_post(SUMP_WATER_LEVEL, resultstr);        // send string result to ubidots
    
    if( heartbeat_counter++ > HEARTBEAT_COUNT_LIMIT ) heartbeat_counter = 0;
    
    sprintf(resultstr, "{\"value\":%d}", heartbeat_counter );
    resultstrdata = resultstr;
    send_http_post(SUMP_HEARTBEAT, resultstr);             // send string result to ubidots
}

void init_system(void) {
    
    Serial.begin(SERIAL_BAUDRATE);
    pinMode(A5, INPUT);
    
    analogvalue = 0;
    adc_voltage = 0;
    
    flag_1sec = seconds    = 0;
    flag_1min = minutes    = 0;
    flag_60min             = 0;
    seconds_counter        = 0;
    minutes_counter        = 0;
    hours_counter          = 0;
    heartbeat_counter      = 0;
    dischargefreq_counter  = 0;
    last_hour_value        = 0;
    cycle_latch            = 0;
    cycle_counter          = 0;
    centimeters            = 0;
    temp_val               = 0;
    
    Particle.publish("Sump Monitor V2", "Online", 60, PRIVATE);
    reset_ubidots_stats();
}

void reset_ubidots_stats(void) {
    
    cycle_counter = 0;
    dischargefreq_counter = 0;
    hours_counter = 0;             // don't clear this timer
    
    sprintf(resultstr, "{\"value\":%d}", 0);
    send_http_post(SUMP_CYCLES, resultstr);
    send_http_post(SUMP_HEARTBEAT, resultstr);
    send_http_post(SUMP_FREQUENCY, resultstr);
    send_http_post(SUMP_WATER_LEVEL, resultstr);
    send_http_post(SUMP_GALLONS, resultstr);
    
    //Particle.publish("Sump Monitor", "No Activity for 24 Hours", 60, PRIVATE);
    
    return;
}

void get_ADC(void) {
    
    analogvalue = analogRead(A5);
    adc_voltage = (analogvalue*330)/4095;
    
    return;
    
}

unsigned int ping(pin_t trig_pin, pin_t echo_pin, uint32_t wait, bool info) {
    uint32_t duration, inches, cm;
    static bool init = false;
    if (!init) {
        pinMode(trig_pin, OUTPUT);
        digitalWriteFast(trig_pin, LOW);
        pinMode(echo_pin, INPUT);
        delay(50);
        init = true;
    }
    
    /* Trigger the sensor by sending a HIGH pulse of 10 or more microseconds */
    digitalWriteFast(trig_pin, HIGH);
    delayMicroseconds(10);
    digitalWriteFast(trig_pin, LOW);
    
    duration = pulseIn(echo_pin, HIGH);
    
    /* Convert the time into a distance */
    // Sound travels at 1130 ft/s (73.746 us/inch)
    // or 340 m/s (29 us/cm), out and back so divide by 2
    // Ref: http://www.parallax.com/dl/docs/prod/acc/28015-PING-v1.3.pdf
    inches = duration / 74 / 2;
    cm = duration / 29 / 2;
    
    if (info) { /* Visual Output */
        Serial.printf("%2d:", inches);
        for(int x=0;x<inches;x++) Serial.print("#");
        Serial.println();
    } else { /* Informational Output */
        Serial.printlnf("%6d in / %6d cm / %6d us", inches, cm, duration);
    }
    delay(wait); // slow down the output
    return cm;
}

Credits

0f33c6ac4df6c65712eb5d413a85fd5b
Michael Wilson

Hobby electronics enthusiast

Replications

Did you replicate this project? Share it!

I made one

Love this project? Think it could be improved? Tell us what you think!

Give feedback

Comments

Similar projects you might like

The composting friend
Intermediate
  • 633
  • 34

Full instructions

Our goal is to make composting easier to use and to maintain.

AquaNiner Pet Hydration Monitor
Intermediate
  • 382
  • 14

Full instructions

Using two Photons, IFTTT and ThingSpeak, get notifications for when your furry friend needs water and monitor how much they're drinking.

Hot Shot Hoops
Intermediate
  • 530
  • 13

Full instructions

No need to keep track of the number of baskets you make when shooting hoops. Hot Shot Hoops does the work for you.

Hot Shot Hoops

Team IOT Basketball

Temperature Mirror
Intermediate
  • 378
  • 8

Full instructions

The Temperature Mirror incorporates a digital display to allow its user to readily view time, temperature, humidity, and light.

Temperature Mirror

MEGR3171 Team 49

LightAlert
Intermediate
  • 138
  • 4

Full instructions

As the great Terry Crews once said "that's 49 cents of milk spilt over the table! Somebody's gonna drink that!" But instead... electricity.

Alerts via Pushbullet When Smoke is Detected
Intermediate
  • 312
  • 8

Full instructions

This project uses two Particle Photons and Pushbullet to notify a user when smoke is detected by a smoke detector.

ProjectsCommunitiesContestsLiveAppsBetaFree StoreBlogAdd projectSign up / Login
Respect project
Feedback