Things used in this project

Hardware components:
SparkFun 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

Contact

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

Space Invaders
Intermediate
  • 14
  • 2

A classic arcade game built using two TI microcontrollers.

Self Balancing Robot Using Mpu6050 Accelerometer
Intermediate
  • 284
  • 8

Protip

this is self balancing robot on two wheels using mpu6050 accelerometer based on arduino..

Controlling 16x2 Character Display with Arduino
Intermediate
  • 32
  • 2

Protip

This project shows you how to control 16x2 character display with an Arduino. Enjoy!

Node-Red and MQTT for Your IoT Projects
Intermediate
  • 151
  • 3

Work in progress

The idea is to create an environment running an MQTT Server, Mosquitto, and Node-Red, and start developing projects with connected sensors.

Reducing Arduino Power Consumption
Intermediate
  • 1,221
  • 18

Protip

One the most important feature of portable electronics should be long battery life. We can reduce the current drawn by several ways.

RING PONG
Intermediate
  • 2,203
  • 8

A simple Ping Pong game played on a NeoPixel Ring with Arduino.

Sign up / LoginProjectsCommunitiesTopicsContestsLiveAppsBetaFree StoreBlog