Skip to content

Commit 21cbd8f

Browse files
committed
Initial commit
0 parents  commit 21cbd8f

File tree

5 files changed

+391
-0
lines changed

5 files changed

+391
-0
lines changed

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
my_secret.h
2+
*.bin
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,315 @@
1+
#include "settings.h"
2+
//#include "secret.h" // <<--- UNCOMMENT this before you use and change values on config.h tab
3+
#include "my_secret.h" // <<--- COMMENT-OUT or REMOVE this line before you use. This is my personal settings.
4+
#include <PubSubClient.h>
5+
#include <ESP8266WiFi.h>
6+
#include <ArduinoOTA.h>
7+
#include <ArduinoJson.h>
8+
#include <BlynkSimpleEsp8266.h>
9+
#include <ESP8266HTTPClient.h>
10+
#include <ESP8266httpUpdate.h>
11+
12+
void callback(char * topic, byte * payload, unsigned int length);
13+
BlynkTimer timer; //To call a method for every certain millis
14+
15+
WiFiClient wifiClient;
16+
PubSubClient client(mqtt_server, mqttPort, callback, wifiClient);
17+
18+
int sensSmoothArray1[FILTERSAMPLES]; // array for holding raw sensor values for sensor1
19+
int sensSmoothArray2[FILTERSAMPLES]; // array for holding raw sensor values for sensor2
20+
21+
int rawData1, smoothData1; // variables for sensor1 data
22+
int rawData2, smoothData2; // variables for sensor2 data
23+
24+
int long count = 0; //to ignore first few filtered data (power fail, device reset)
25+
26+
int volume; //used to calculate volume of water tank
27+
int percent; //used to calculate percent of water present in water tank
28+
float distanceinFeet; //used to calculate distance in feet
29+
float waterFillLevel; // to calculate at what height water was present in tank
30+
int distance;
31+
long duration, originalDistanceinCm; // Duration used to calculate distance
32+
33+
char const * waterTankSensor = "/house/watersump/";
34+
char const * firmwareUpdateTopic = "/house/watersump/firmware/";
35+
36+
void setup() {
37+
ArduinoOTA.setHostname(OTA_HOSTNAME); // A name given to your ESP8266 module when discovering it as a port in ARDUINO IDE
38+
ArduinoOTA.begin(); // OTA initialization
39+
40+
Serial.begin(115200); // Start the serial line for debugging
41+
delay(100);
42+
43+
pinMode(TRIGPIN, OUTPUT);
44+
pinMode(ECHOPIN, INPUT);
45+
46+
WiFi.begin(ssid, password); // Start wifi subsystem
47+
reconnectWifi(); // Attempt to connect to the WIFI network and then connect to the MQTT server
48+
delay(2000); // Wait a bit before starting the main loop
49+
50+
timer.setInterval(1000L, MeasureCm); // Setup a function to be called every 1sec
51+
}
52+
53+
void loop() {
54+
timer.run();
55+
if (WiFi.status() != WL_CONNECTED) { // Reconnect if connection is lost
56+
reconnectWifi();
57+
} else if (!client.connected()) {
58+
reconnectMQTT();
59+
} else {
60+
client.loop(); // Maintain MQTT connection
61+
delay(10); // MUST delay to allow ESP8266 WIFI functions to run
62+
ArduinoOTA.handle();
63+
}
64+
}
65+
66+
void MeasureCm() {
67+
digitalWrite(TRIGPIN, LOW); // The following trigPin/echoPin cycle is used to determine the
68+
delayMicroseconds(2); // distance of the nearest object by bouncing soundwaves off of it.
69+
digitalWrite(TRIGPIN, HIGH);
70+
delayMicroseconds(10);
71+
digitalWrite(TRIGPIN, LOW);
72+
duration = pulseIn(ECHOPIN, HIGH);
73+
74+
originalDistanceinCm = duration / 58.2; //We get water level distance from top in cm. Calculate the distance (in cm) based on the speed of sound. Distance in centimeters = Time / 58
75+
76+
smoothData1 = digitalSmooth(originalDistanceinCm, sensSmoothArray1); // every sensor you use with digitalSmooth needs its own array
77+
78+
count++; // we are using this counter to ignore first few filter data
79+
80+
// Serial.println(" ");
81+
// Serial.print("Ignore Counter for Smooth Filter: ");
82+
// Serial.print(count);
83+
// Serial.println(" ");
84+
85+
if (count > FILTERSAMPLES) {
86+
count = FILTERSAMPLES; // I thought of not to increase counter when it's satisfies our condition
87+
distance = smoothData1; // we assign fitered data only after ignoring first few samples so we can avoid water level starting from 0 cm
88+
// Serial.print("Original Distance: ");
89+
// Serial.print(originalDistanceinCm);
90+
// Serial.println(" cm");
91+
// Serial.print("Smooth Distance: ");
92+
// Serial.print(distance);
93+
// Serial.println(" cm");
94+
} else {
95+
distance = originalDistanceinCm;
96+
}
97+
98+
distanceinFeet = (distance / 30.48); //We get water level distance from top in feet. Divide the length value by 30.48 to convert cm to feet.
99+
waterFillLevel = (WATER_TANK_HEIGHT_IN_CM - distance); //total water tank hight - fill water level hight
100+
101+
{
102+
if (distance >= MINIMUMRANGE && distance <= WATER_TANK_HEIGHT_IN_CM) { // here we are finding tank volume
103+
volume = ((WATER_TANK_LENGTH_IN_CM * waterFillLevel * WATER_TANK_WIDTH_IN_CM) / 1000); // Filled Volume = Length * Width * Fill Height (Liquid Height) and divide by 1000 because we are passing vlaues in cm so to convert to liters we need to divide
104+
percent = ((float) volume / FULLTANK) * 100;
105+
}
106+
107+
if (distance >= WATER_TANK_HEIGHT_IN_CM) { //we don't want to display negative values
108+
volume = 0;
109+
percent = 0;
110+
}
111+
112+
if (distance < MINIMUMRANGE) { //we don't want to display wrong data
113+
volume = FULLTANK;
114+
percent = 100;
115+
}
116+
}
117+
118+
// Serial.println(" ");
119+
// Serial.print("Percent of Water in Tank: ");
120+
// Serial.print(percent);
121+
// Serial.println("%");
122+
// Serial.print("Liters of Water in Sump: ");
123+
// Serial.print(volume);
124+
// Serial.println(" Liters");
125+
// Serial.print("Distance in cm to Reach Water Level: ");
126+
// Serial.print(distance);
127+
// Serial.println(" cm");
128+
// Serial.print("Distance in Feet to Reach Water Level: ");
129+
// Serial.print(distanceinFeet);
130+
// Serial.println(" Feet");
131+
publishData(volume, percent, distance, distanceinFeet, BUILD_NUMBER);
132+
}
133+
134+
135+
void publishData(int p_volume, int p_percent, int p_distanceInCm, float p_distanceinFeet, int p_buildNumber) { // function called to publish the temperature and the humidity
136+
StaticJsonDocument < 200 > jsonDocument; // create a JSON object
137+
jsonDocument["volume"] = (String) p_volume; // INFO: the data must be converted into a string; a problem occurs when using floats...
138+
jsonDocument["percentage"] = (String) p_percent;
139+
jsonDocument["distanceInCm"] = (String) p_distanceInCm;
140+
jsonDocument["distanceInFeet"] = (String) p_distanceinFeet;
141+
jsonDocument["buildNumber"] = (String) p_buildNumber;
142+
char data[200];
143+
serializeJson(jsonDocument, data);
144+
client.publish(waterTankSensor, data, true); //Publishing data to MQTT server as Json
145+
yield();
146+
}
147+
148+
149+
int digitalSmooth(int rawIn, int * sensSmoothArray) { //Data Filtering: "int *sensSmoothArray" passes an array to the function - the asterisk indicates the array name is a pointer
150+
int j, k, temp, top, bottom;
151+
long total;
152+
static int i;
153+
static int sorted[FILTERSAMPLES];
154+
boolean done;
155+
156+
i = (i + 1) % FILTERSAMPLES; // increment counter and roll over if necc. - % (modulo operator) rolls over variable
157+
sensSmoothArray[i] = rawIn; // input new data into the oldest slot
158+
159+
for (j = 0; j < FILTERSAMPLES; j++) { // transfer data array into anther array for sorting and averaging
160+
sorted[j] = sensSmoothArray[j];
161+
}
162+
163+
done = 0; // flag to know when we're done sorting
164+
while (done != 1) { // simple swap sort, sorts numbers from lowest to highest
165+
done = 1;
166+
for (j = 0; j < (FILTERSAMPLES - 1); j++) {
167+
if (sorted[j] > sorted[j + 1]) { // numbers are out of order - swap
168+
temp = sorted[j + 1];
169+
sorted[j + 1] = sorted[j];
170+
sorted[j] = temp;
171+
done = 0;
172+
}
173+
}
174+
}
175+
176+
177+
bottom = max(((FILTERSAMPLES * 15) / 100), 1); // throw out top and bottom 15% of samples - limit to throw out at least one from top and bottom
178+
top = min((((FILTERSAMPLES * 85) / 100) + 1), (FILTERSAMPLES - 1)); // the + 1 is to make up for asymmetry caused by integer rounding
179+
k = 0;
180+
total = 0;
181+
for (j = bottom; j < top; j++) {
182+
total += sorted[j]; // total remaining indices
183+
k++;
184+
}
185+
return total / k; // divide by number of samples
186+
}
187+
188+
void reconnectWifi() {
189+
Serial.print("");
190+
Serial.print("Wifi status = ");
191+
Serial.print(WiFi.status());
192+
if (WiFi.status() != WL_CONNECTED) { // Attempt to connect to the wifi if connection is lost
193+
Serial.print("Connecting to ");
194+
Serial.println(ssid);
195+
196+
while (WiFi.status() != WL_CONNECTED) { // Loop while we wait for connection
197+
delay(500);
198+
}
199+
200+
Serial.println("");
201+
Serial.println("WiFi connected");
202+
reconnectMQTT();
203+
Serial.println("IP address: ");
204+
Serial.println(WiFi.localIP());
205+
}
206+
}
207+
208+
void reconnectMQTT() {
209+
delay(1000);
210+
if (WiFi.status() == WL_CONNECTED) { // Make sure we are connected to WIFI before attemping to reconnect to MQTT
211+
while (!client.connected()) { // Loop until we're reconnected to the MQTT server
212+
Serial.print("Attempting MQTT connection...");
213+
String clientName; // Generate client name based on MAC address and last 8 bits of microsecond counter
214+
clientName += "esp8266-";
215+
uint8_t mac[6];
216+
WiFi.macAddress(mac);
217+
clientName += macToStr(mac);
218+
219+
if (client.connect("ESP8266Client", mqttUser, mqttPassword)) { // Delete "mqtt_username", and "mqtt_password" here if you are not using any
220+
Serial.print("\tMQTT Connected");
221+
client.subscribe(firmwareUpdateTopic); // If connected, subscribe to the topic(s) we want to be notified about
222+
client.subscribe(waterTankSensor);
223+
client.publish("/house/watersump/Confirmfirmware/", "0"); // Sending message to MQTT server to turn off MQTT firmware upgrade button if its on
224+
} else {
225+
Serial.println("\tFailed.");
226+
abort();
227+
}
228+
}
229+
} else {
230+
Serial.println("Wifi is not connected");
231+
}
232+
}
233+
234+
void callback(char * topic, byte * payload, unsigned int length) {
235+
String topicStr = topic; // Convert topic to string to make it easier to work with
236+
Serial.println("Callback update.");
237+
Serial.print("Topic: ");
238+
Serial.println(topicStr); // Note: the "topic" value gets overwritten everytime it receives confirmation (callback) message from MQTT
239+
240+
if (topicStr == "/house/watersump/firmware/") {
241+
Serial.print("Firmware update calling ");
242+
if (payload[0] == '1') { // Turn the switch on if the payload is '1' and publish to the MQTT server a confirmation message
243+
Serial.print("Firmware switch on ");
244+
checkforupdate();
245+
} else if (payload[0] == '0') { // Turn the switch off if the payload is '0' and publish to the MQTT server a confirmation message
246+
Serial.print("Firmware switch off ");
247+
}
248+
} else {
249+
publishData(volume, percent, distance, distanceinFeet, BUILD_NUMBER);
250+
}
251+
}
252+
253+
void update_started() {
254+
Serial.println("CALLBACK: HTTP update process started");
255+
}
256+
257+
void update_finished() {
258+
Serial.println("CALLBACK: HTTP update process finished");
259+
}
260+
261+
void update_progress(int cur, int total) {
262+
Serial.printf("CALLBACK: HTTP update process at %d of %d bytes...\n", cur, total);
263+
}
264+
265+
void update_error(int err) {
266+
Serial.printf("CALLBACK: HTTP update fatal error code %d\n", err);
267+
}
268+
269+
void checkforupdate() {
270+
Serial.println("OTA Update Request Received");
271+
Serial.print("Firmware URL: ");
272+
Serial.println(FIRMWARE_URL);
273+
274+
HTTPClient httpClient;
275+
httpClient.begin(FIRMWARE_URL);
276+
int httpCode = httpClient.GET();
277+
278+
if (httpCode == 200) {
279+
Serial.println("Update file found, starting update");
280+
// ESPhttpUpdate.onStart(update_started); // Add optional callback notifiers if necessary
281+
// ESPhttpUpdate.onEnd(update_finished);
282+
// ESPhttpUpdate.onProgress(update_progress);
283+
// ESPhttpUpdate.onError(update_error);
284+
t_httpUpdate_return ret = ESPhttpUpdate.update(FIRMWARE_URL);
285+
286+
switch (ret) {
287+
case HTTP_UPDATE_FAILED:
288+
Serial.println("[update] Update failed.");
289+
break;
290+
case HTTP_UPDATE_NO_UPDATES:
291+
Serial.println("[update] Update no Update.");
292+
break;
293+
case HTTP_UPDATE_OK:
294+
Serial.println("[update] Update ok."); // May not called we reboot the ESP
295+
break;
296+
}
297+
} else {
298+
client.publish("/house/switch/Confirmfirmware/", "0"); // Sending message to MQTT server to turn off MQTT firmware upgrade button if its on
299+
Serial.print("Firmware check failed, got HTTP response code ");
300+
Serial.println(httpCode);
301+
}
302+
httpClient.end();
303+
}
304+
305+
String macToStr(const uint8_t * mac) { // Generate unique name from MAC addr
306+
String result;
307+
for (int i = 0; i < 6; ++i) {
308+
result += String(mac[i], 16);
309+
310+
if (i < 5) {
311+
result += ':';
312+
}
313+
}
314+
return result;
315+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
////////////////////////////////////////////////////////////
2+
// Secret Configuration //
3+
//////////////////////////////////////////////////////////
4+
5+
6+
/***************************************************
7+
WiFi Settings
8+
**************************************************/
9+
10+
#define WIFI_SSID "Your WiFi Network Name"
11+
#define WIFI_PASS "Your Wifi Password"
12+
13+
14+
/***************************************************
15+
MQTT Server Settings
16+
**************************************************/
17+
const char* mqtt_server = "xxx.xxx.xx.xxx"; //MQTT server ip address
18+
const int mqttPort = xxxx; //MQTT broker port
19+
20+
const char* mqttUser = "Your MQTT Username"; //MQTT broker username
21+
const char* mqttPassword = "Your MQTT Password"; //MQTT broker user password
22+
23+
24+
/***************************************************
25+
Firmware URL
26+
**************************************************/
27+
#define FIRMWARE_URL "http://www.example.com/Home-Assistant-Water-Tank-Level-Indicator.ino.generic.bin" // Enter the firmware URL location. Only http Protocol is supported. https not supported. Used for FOTA
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
2+
/////////////////////////////////////////////////////////////////
3+
// Settings //
4+
/////////////////////////////////////////////////////////////////
5+
6+
int BUILD_NUMBER = 0; // Build number is something like change some variables in code and building binary file.
7+
8+
9+
/***************************************************
10+
* Hardware Settings
11+
**************************************************/
12+
13+
#define ECHOPIN 14 // Echo Pin D5
14+
#define TRIGPIN 12 // Trigger Pin D6
15+
16+
17+
/***************************************************
18+
* Variable Settings
19+
**************************************************/
20+
21+
#define FILTERSAMPLES 21 // filterSamples should be an odd number, no smaller than 3
22+
23+
//Our rectangular tank capacity in liters; 39,600 Liters full tank capacity
24+
//(length x width x height)/1000(to convert to ltrs) = (472*290*274)/1000=37505120/1000 = 37505.12 liters; I just used 37,000 Ltrs
25+
26+
#define FULLTANK 37000 //in liters
27+
#define MINIMUMRANGE 20 // sensor needs minimum 20 cm distance to work
28+
29+
30+
#define WATER_TANK_LENGTH_IN_CM 472 // Water tank lenght in cm
31+
#define WATER_TANK_WIDTH_IN_CM 290 // water tank width in cm
32+
33+
//Tank original height(depth) is: 310cm. Sensor takes 2 inches (5.08 cm) of height and sensor need minimum 20cm distance to work.
34+
// And I keep 10cm keep extra margin.
35+
//So from our total tank height we need reduce from extra space left in tank that is, (sensor height + sensor minimum distance)
36+
//Waste tank height = 5.08cm + 20cm + 10cm = 35.08cm. New tank height = original tank height - waste tank height.
37+
//New tank height = 310cm - 35.08cm = 274.92cm; I consider 274cm.
38+
39+
#define WATER_TANK_HEIGHT_IN_CM 274 // 274cm is after reducing 20cm + 5.08cm +10cm height from original tank height to work sensor
40+
41+
/***************************************************
42+
*
43+
* Server Settings
44+
**************************************************/
45+
46+
#define OTA_HOSTNAME "Water Level Indicator"
47+

README.md

Whitespace-only changes.

0 commit comments

Comments
 (0)