top of page
Writer's pictureRamesh G

Node MCU Based Water Level Controller with EEPROM

Updated: Dec 29, 2022


In this tutorial, I 'll Published how to controlling Water level using web server over WiFi using Node MCU, Ultrasonic, OLED display and multiple web pages. In this project Automatic and semi manual mode water level controller through assigned web page and Level graphical display using fusion charts and level graph using high charts in separate web pages is advantage of the project.


Components Required

To make this project you need the following components:

Node MCU ESP8266 Development Board - 1 no

HC-SR04 Ultrasonic sensor - 1 no

0.96 OLED Display 4wire Module - 1 no

3.3V single Channel Relay Module - 1 no

4k7 Resistor - 1 no

Push Button - 1 no

Jumper wires


Circuit Diagram

Ultrasonic:

The HC-SR04 ultrasonic module is a module that can provide non-contact measurement within the range of 2cm to 400cm with ranging accuracy that can reach 3mm. It works on the principle of echolocation.

The ultrasonic sensor as a trigger and an echo pin. The arduino provides a high signal of 10microseconds to this pin. After the HC-SR04 is triggered, it sends out eight 40Khz sound waves to the surface of the water. On getting to the surface of the water, the wave is echoed back to the sensor and the ESP8266 reads the echo pin to determine time spent between triggering and receiving of the echo. Since we know that the speed of sound is around 340m/s then we can calculate the distance using; Distance = (time/2)*speed of sound.



Here's a nice example of a modern, flat style slider using pips. There are no labels so this kind of slider would be purely visual without much help to the user for knowing their real chosen value. In this example we make use of the .ui-slider-pip-inrange class to create the nice "filled in" effect on the pips.


The default way to use the plugin is to call the pips method on an initialized slider. This will add the markers along the slider, and place the min/max values to the beginning/end of the slider:

In this Project jQuery UI Slider Pips added in the Web page to adjust the Value between two points 0 to 100.

  • 1st Point for water Level Lower Threshold value adjustment slider to Control Motor ON.

  • 2nd Point for water Level Higher Threshold value adjustment slider to Control Motor OFF.


Fusion Charts

Fusion Charts helps you build beautiful dashboards for your web & mobile projects. With extensive documentation, cross-browser support, and a consistent API, it is easier than ever to add interactive and responsive charts. From simple charts like line, column, and pie to domain-specific charts like heat maps, radar, and stock chart


Cylinder Fill

The cylinder gauge is represented by a vertical cylinder, whose fill level is defined by the data value you plot. You can use it to report inventory levels, fuel levels, etc. The cylinder gauge is a real-time chart, which can update its data after intervals you specify, without any page refreshes. What makes a cylinder gauge different from other gauges is that this gauge can only be rendered with one fill color. You cannot create a cylinder gauge with color ranges.

The cylinder is the main component in a cylinder chart. You can understand the value being illustrated by looking at the percentage of cylinder filled.

Use the following attributes to create a simple cylinder gauge:


  • Specify the type using the type attribute. To render Cylinder gauge, set cylinder.

  • Set the container object using renderAt attribute.

  • Specify the dimension of the chart using width and height attributes.

  • Set the type of data (JSON/XML) you want to pass to the chart object using dataFormat attribute.

  • Use the lowerLimit attribute to specify the lower limit, or the minimum value, of the gauge scale.

  • Use the upperLimit attribute to specify the upper limit, or the maximum value, of the gauge scale.

  • Use the lowerLimitDisplay attribute to specify the label to be displayed with the lower limit value on the gauge scale.

  • Use the upperLimitDisplay attribute to specify the label to be displayed with the upper limit value on the gauge scale.

  • Use the numberSuffix attribute to specify the character(s) to be appended to the end of a number.


Bulp Gauge

Bulb gauge is used to indicate a specific dataset by utilizing a circle that indicates whether the monitored data is within defined limits, and if it is, then which limit does it belong to. Colors for the bulb can be selected to suit the application such as green for satisfactory, yellow for caution, and red for alarm.


High Charts

High Charts make it easy for developers to create charts for web and mobile platforms.

For Javascript, Angular, React, VueJS, iOS, R, .NET, Python, and more.


Line Charts

The line chart is represented by a series of data points connected with a spline. Line charts are most often used to visualize data that changes over time.



Arduino Code

#include <ESP8266WiFi.h>

#include <HCSR04.h>

#include <ESP8266WebServer.h>

#include "index.h";

#include <DNSServer.h>

#include <WiFiManager.h>

#include <SPI.h>

#include <Wire.h>

#include <Adafruit_GFX.h>

#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // OLED display width, in pixels

#define SCREEN_HEIGHT 64 // OLED display height, in pixels

#define OLED_RESET -1 // Reset pin # (or -1 if sharing Arduino reset pin)

#define SCREEN_ADDRESS 0x3C ///< See datasheet for Address; 0x3D for 128x64, 0x3C for 128x32

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);


#include <EEPROM.h>

int addr = 0;

int distance;

#define buttonPin D0

int buttonState = 0;

float TankHeight;

int Motor_Pinvalue;

const int Motor_Pin = D7;

String data;

String lowset;

String highset;

UltraSonicDistanceSensor distanceSensor(D5,D6); //D5 trig, D6=echo

int waterLevelLowerThreshold=20;

int waterLevelUpperThreshold=75;

float liters = 0;

int waterLevelDownCount=0,waterLevelUpCount=0;


WiFiClient client;

ESP8266WebServer server(80);


void handleRoot() {

String s = MAIN_page; //Read HTML contents

server.send(200, "text/html", s); //Send web page

}


void handleLevelRequest(){

server.send(200,"text",String(liters));

}

void handleDistanceRequest(){

server.send(200,"text",String(distanceSensor.measureDistanceCm()));

}


void handleNotFound(){

String message = "File Not Found\n\n";

server.send(404, "text/plain", message);

}


void handleStatus(){

Serial.print("Motor:");

Serial.print(digitalRead(Motor_Pin));//MOTOR Off/On

server.send(200,"text",String(digitalRead(Motor_Pin)));

}


void handleRangeSetting(){

waterLevelLowerThreshold=(server.arg(0)).toInt();

waterLevelUpperThreshold=(server.arg(1)).toInt();

Serial.print("Low Level ");

Serial.print(waterLevelLowerThreshold);

Serial.print(":");

Serial.print("High Level ");

Serial.println(waterLevelUpperThreshold);

server.send(200, "text/plain", "range");

}


void measure_Volume()

{

distance = distanceSensor.measureDistanceCm();

// volume=(MAX_HEIGHT-heightInch)/28;//MAX_HEIGHT-distance will give actual height,

liters = ((1-(distance/TankHeight))*100);

Serial.println("Level:");

Serial.println(liters);


if(liters<=waterLevelLowerThreshold)

waterLevelDownCount++;

else waterLevelDownCount=0;


if(liters>=waterLevelUpperThreshold)

waterLevelUpCount++;

else waterLevelUpCount=0;



if(waterLevelDownCount==3)

{//TURN ON RELAY

Serial.println("motor turned on");

digitalWrite(Motor_Pin,HIGH);//Relay is active LOW

}

if(waterLevelUpCount==3)

{//TURN OFF RELAY

Serial.println("motor turned off");

digitalWrite(Motor_Pin,LOW);//Relay is active LOW

}

}


void handleMotor_Pinon() {

digitalWrite(Motor_Pin,HIGH); //Motor_Pin is connected in reverse

server.send(200, "text/html", "ON"); //Send ADC value only to client ajax request

}


void handleMotor_Pinoff() {

digitalWrite(Motor_Pin,LOW); //Motor_Pin off

server.send(200, "text/html", "OFF"); //Send ADC value only to client ajax request

}



void runPeriodicFunc()

{

static const unsigned long REFRESH_INTERVAL1 = 1000; // 2.1sec

static unsigned long lastRefreshTime1 = 0;

if(millis() - lastRefreshTime1 >= REFRESH_INTERVAL1)

{

measure_Volume();

handleDisplay();

lastRefreshTime1 = millis();

}

}



void setup(void){

Serial.begin(115200);

delay(100);

EEPROM.begin(512);

pinMode(Motor_Pin, OUTPUT);


// SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally


if(!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {

Serial.println(F("SSD1306 allocation failed"));

for(;;); // Don't proceed, loop forever

}

display.display();

//WiFi.begin(ssid, password);

//Serial.println("");


WiFiManager wifiManager;

wifiManager.autoConnect("Smart Level");

while (WiFi.status() != WL_CONNECTED) {

delay(500);

Serial.print(".");

}

Serial.print("IP address:");

Serial.println(WiFi.localIP());

display.display();

display.setTextSize(2); // Draw 2X-scale text

display.setTextColor(SSD1306_WHITE);

display.setCursor(0, 17);

for (int i=0; i<=5; i++)

{

display.setCursor(0, 17);

Serial.println("Press & Hold Button ");

display.setCursor(0, 47);

Serial.println(3-i);

Serial.println(" Seconds ");

delay(1000);

buttonState = digitalRead(buttonPin);

if (buttonState == LOW) // Button to GND connection

{

distance = distanceSensor.measureDistanceCm();

TankHeight = distance;

EEPROM.write(addr, TankHeight);

EEPROM.commit();

}

delay(100);

}

{

TankHeight= EEPROM.read(addr);

display.clearDisplay();

display.display();

display.setTextSize(2); // Draw 2X-scale text

display.setTextColor(SSD1306_WHITE);

display.setCursor(0, 1);

Serial.println("Tank Height Scan");

Serial.println(" ");

Serial.println(" cm ... ");

Serial.print("TankHeight : ");

Serial.println(TankHeight);

delay(1000);

display.setCursor(30, 1);

Serial.println("Tank Height Cm:");

Serial.print("TankHeight : ");

Serial.println(TankHeight);

Serial.println("IP :");

Serial.println(WiFi.localIP());

}

server.on("/", handleRoot);

server.on("/level",handleLevelRequest);

server.on("/distance",handleDistanceRequest);

server.on("/configRange",handleRangeSetting);

server.on("/motor_status",handleStatus);

server.on("/Motor_PinOn", handleMotor_Pinon);

server.on("/Motor_PinOff", handleMotor_Pinoff);

server.on("/readdisplay",handleDisplay);

server.onNotFound(handleNotFound);

server.begin();

Serial.println("HTTP server started");

}


void loop(void){

runPeriodicFunc();

server.handleClient();

}

void handleDisplay()

{

// volume=(MAX_HEIGHT-heightInch)/28;//MAX_HEIGHT-distance will give actual height,

display.clearDisplay();

display.setTextSize(1); // Draw 2X-scale text

display.setTextColor(SSD1306_WHITE);

display.setCursor(2, 2);

display.println("IP: ");

display.setCursor(22,2);

display.println(WiFi.localIP());

display.display();

display.setTextSize(3); // Draw 2X-scale text

display.setTextColor(SSD1306_WHITE);

display.setCursor(0, 17);

display.println(String(liters));

display.println(" ");

display.display();

delay(100);

if (digitalRead(Motor_Pin)>=1)

{

Serial.println("Pump Motor : ON ");

}

else {

Serial.println("Pump Motor : OFF ");

}

Motor_Pinvalue = digitalRead(Motor_Pin);

Serial.println(digitalRead(Motor_Pin));

display.setTextSize(1); // Draw 2X-scale text


display.setTextColor(SSD1306_WHITE);

display.setCursor(0, 45);

display.print("L: ");

display.print(waterLevelLowerThreshold);

display.display();

display.setTextSize(1); // Draw 2X-scale text

display.setTextColor(SSD1306_WHITE);

display.setCursor(70, 45);

display.print("H: ");

display.print(waterLevelUpperThreshold);

display.display();

if (digitalRead(Motor_Pin)==LOW){

display.setTextSize(1); // Draw 2X-scale text

display.setTextColor(SSD1306_WHITE);

display.setCursor(0, 55);

display.print("Pump OFF ");

display.display();}

else if (digitalRead(Motor_Pin)==HIGH){

display.setTextSize(1); // Draw 2X-scale text

display.setTextColor(SSD1306_WHITE);

display.setCursor(0, 55);

display.print("Pump ON");

display.display();}

display.setTextSize(1); // Draw 2X-scale text

display.setTextColor(SSD1306_WHITE);

display.setCursor(70, 55);

display.print(TankHeight);

display.display();

String data = "{\"Height\":\""+String(TankHeight)+"\", \"TankLevel\":\""+ String(liters) +"\", \"LowLevelThreshold\":\""+ String(waterLevelLowerThreshold) +"\", \"HighLevelThreshold\":\""+ String(waterLevelUpperThreshold) +"\", \"Motorstatus\":\""+ (digitalRead(Motor_Pin)) +"\"}";

//Serial.println("data:");

// Serial.println(data);

server.send(200, "text/plane", data);

}



Then, upload the code to your NodeMCU board. Make sure you have selected the right board and COM port. Also, make sure you’ve configure your WiFi Credentials using mobile.


After a successful upload, open the Serial Monitor at a baud rate of 115200. Press the “EN/RST” button on the ESP8266 board. Now it should print its IP address



Pay and get index.h file, arduino code and library files.


Demo:






Comentários


bottom of page