// 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;
}
Comments