//****Virtual Pin Assignments****
// V0 - Blynk Version - labeled value
// V1 - ESP Version - labeled value
// V2 - Millis Uptime - labeled value
// V3 - Wifi RSSI - labeled value
// V4 - Volume - labeled value
// V8 - Terminal - terminal
// V9 - Percent - level V widget -- value 1 -10
//V10 - Numeric Widget - Level Alert
//V11 - Button Widget to cancel alert or turn alert (on or off) --button
//V12 - Distance - height from sensor to water level -- labeled value
//#define BLYNK_PRINT Serial // Comment this out to disable prints and save space
#include <ESP8266WiFi.h>
#include <BlynkSimpleEsp8266.h>
#include <ESP8266mDNS.h> // For OTA w/ ESP8266
#include <WiFiUdp.h> // For OTA
#include <ArduinoOTA.h> // For OTA
char auth[] = "yourAuthCode";
char ssid[] = "changeto your ssid";
char pass[] = "yourpassword";
BlynkTimer timer;
WidgetTerminal terminal(V8);
#define echoPin 14// Echo Pin
#define trigPin 12 // Trigger Pin
#define filterSamples 3 // filterSamples should be an odd number, no smaller than 3
int maximumRange = 450;
int minimumRange = 20;
int alertLevelCounter = 0; //Counter starts at zero
int volume;
int volume1; // for smoothing algorithum
int percent;
int percent1; // for smoothing algorithum
int levelAlert;
int wateralert = 0;
int sensSmoothArray1 [filterSamples]; // array for holding raw sensor values for sensor1
int smoothDistance; // variables for sensor1 data
double alertInMin = 0.5; //Alert time following alert water level being reached (0.5 = 30secs)
const int alertDuration = 2; //Alert sent every 2 minutes
const int waterTankDia = 80; //Water Tank Diameter (cm)
const int waterTankHt = 100; //ie. Sensor Height (cm)
long jarak,jarak1;
long duration, distance, distance1; // Duration used to calculate distance
BLYNK_CONNECTED() { // runs once at device startup, once connected to server.
Blynk.syncVirtual(V10);
Blynk.syncVirtual(V11);
}
void MeasureCm() {
// The following trigPin/echoPin cycle is used to determine the
// distance of the nearest object by bouncing soundwaves off of it.
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
//Calculate the distance (in cm) based on the speed of sound.
distance = duration/58.2;
if(distance >= 20 ){
volume = ((waterTankHt*(PI*(sq(waterTankDia/2))))-(distance*(PI*(sq(waterTankDia/2)))))/1000;
//percent = ((float)volume/9000)*100;
//jarak = 1-(distance/100);
//volume = ((0.4 * 0.4 * (1-(distance/100))) *(22/7))*1000;
percent = (100 - distance);
//percent = 100 - (distance/3);
}
else{
if(distance < 20 ){
volume = 450;
percent = 100;
}
}
Blynk.virtualWrite(V9, percent); // virtual pin
Blynk.virtualWrite(V12, distance); // virtual pin
Blynk.virtualWrite(V4, volume); // virtual pin
}
void UpTime() {
Blynk.virtualWrite(2, (millis() / 1000));
Blynk.virtualWrite(3, WiFi.RSSI());
//terminal.println(levelAlert);
}
BLYNK_WRITE(V10) {
levelAlert = param.asInt();
terminal.print("Water Level Alert: ");
terminal.println(levelAlert);
terminal.println();
terminal.flush();
}
BLYNK_WRITE(V11) {
int alertOff = param.asInt();
if(alertOff == 0) {
resetWaterLevelAlert();
wateralert = 1;
terminal.print("Level Alert is OFF");
terminal.println();
terminal.flush();
}
else {
resetWaterLevelAlert();
wateralert = 0;
terminal.print("Level Alert is ON");
terminal.println();
terminal.flush();
}
}
void checkWaterLevel() {
if(volume1 < levelAlert && wateralert == 0){
alertLevelCounter += 1;
}
if(alertLevelCounter > alertInMin * 60 && wateralert == 0){
Blynk.notify("Water Tank Level Alert"); // send push notification to blynk app
wateralert = 1;
resetWaterLevelAlert();
}
}
void resetWaterLevelAlert() {
alertLevelCounter = 0;
wateralert = 0;
}
void MeasureCmForSmoothing() {
/* The following trigPin/echoPin cycle is used to determine the
distance of the nearest object by bouncing soundwaves off of it. */
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
duration = pulseIn(echoPin, HIGH);
//Calculate the distance (in cm) based on the speed of sound.
distance1 = duration/58.2; //for smoothing algorithim
smoothDistance = digitalSmooth(distance1, sensSmoothArray1);
{
if(distance1 >= 20 ) {
volume1 = ((waterTankHt*(PI*(sq(waterTankDia/2))))-(smoothDistance*(PI*(sq(waterTankDia/2)))))/1000;
//volume1 = ((((0.4 * 0.4) * (1-(smoothDistance/100))) *(22/7))*1000);
percent1 = (100 - smoothDistance);
terminal.print("Uptime (secs): ");
terminal.println((millis() / 1000));
terminal.print("Distance: ");
terminal.println(distance1);
terminal.print("Smoothed Distance: ");
terminal.println(smoothDistance);
terminal.println();
terminal.print("percent1: ");
terminal.println(percent1);
terminal.flush();
//terminal.print("Percent: ");
// terminal.println(percent1);
}
else{
if(distance1 < 20 ){
volume1 = 450;
percent1 = 100;
}
}
}
}
void UploadMeasureCmForSmoothing() {
Blynk.virtualWrite(V12, smoothDistance); // virtual pin
Blynk.virtualWrite(V4, volume1); // virtual pin
Blynk.virtualWrite(V9, percent1); // virtual pin
terminal.print("Uploaded Volume: ");
terminal.println(volume1);
terminal.print("Uploaded Smoothed Distance: ");
terminal.println(smoothDistance);
terminal.println();
terminal.flush();
}
void setup() {
Serial.begin(115200);
Blynk.begin(auth, ssid, pass, "blynk-cloud.com", 8080);
ArduinoOTA.setHostname("Water_Tank"); // For OTA - Use your own device identifying name
ArduinoOTA.begin(); // For OTA
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
terminal.clear();
timer.setTimeout(100, [](){
timer.setInterval(5000L, UpTime);
});
timer.setTimeout(400, [](){
timer.setInterval(300000L, UploadMeasureCmForSmoothing); //get a measurement straight up if the device was reset for some reason
});
timer.setTimeout(600, [](){
timer.setTimer(1000L, MeasureCm, 1);
});
timer.setTimeout(500, [](){
timer.setInterval(1000L, checkWaterLevel);
});
timer.setTimeout(700, [](){
timer.setInterval(15000L, MeasureCmForSmoothing);
});
}
void loop() {
Blynk.run(); // Initiates Blynk
ArduinoOTA.handle(); // For OTA
timer.run(); // Initiates SimpleTimer
}
int digitalSmooth(int rawIn, int *sensSmoothArray){ // "int *sensSmoothArray" passes an array to the function - the asterisk indicates the array name is a pointer
int j, k, temp, top, bottom;
long total;
static int i;
// static int raw[filterSamples];
static int sorted[filterSamples];
boolean done;
i = (i + 1) % filterSamples; // increment counter and roll over if necc. - % (modulo operator) rolls over variable
sensSmoothArray[i] = rawIn; // input new data into the oldest slot
// Serial.print("raw = ");
for (j=0; j<filterSamples; j++){ // transfer data array into anther array for sorting and averaging
sorted[j] = sensSmoothArray[j];
}
done = 0; // flag to know when we're done sorting
while(done != 1){ // simple swap sort, sorts numbers from lowest to highest
done = 1;
for(j = 0; j < (filterSamples - 1); j++){
if(sorted[j] > sorted[j + 1]){ // numbers are out of order - swap
temp = sorted[j + 1];
sorted [j+1] = sorted[j] ;
sorted [j] = temp;
done = 0;
}
}
}
/*
for (j = 0; j < (filterSamples); j++){ // print the array to debug
Serial.print(sorted[j]);
Serial.print(" ");
}
Serial.println();
*/
// throw out top and bottom 15% of samples - limit to throw out at least one from top and bottom
// bottom = max(((filterSamples * 15) / 100), 1);
// top = min((((filterSamples * 85) / 100) + 1 ), (filterSamples - 1)); // the + 1 is to make up for asymmetry caused by integer rounding
bottom = max(((filterSamples * 20) / 100), 1);
top = min((((filterSamples * 80) / 100) + 1 ), (filterSamples - 1)); // the + 1 is to make up for asymmetry caused by integer rounding
k = 0;
total = 0;
for( j = bottom; j< top; j++){
total += sorted[j]; // total remaining indices
k++;
//Serial.print(sorted[j]);
//Serial.print(" ");
//terminal.print(sorted[j]);
//terminal.println(" ");
}
//terminal.print("average: ");
//terminal.println(total/k);
//terminal.flush();
//Serial.println();
//Serial.print("average = ");
//Serial.println(total/k);
return total / k; // divide by number of samples
}