How to create a real-time water level monitoring system using Arduino, Python

Introduction

Nowadays, when people are more aware of the state of our Planet, the control of the water level is important for various purposes in our life beginning from the irrigation and ending with the regulation in the houses. In this tutorial, you will learn the steps to follow in order to design an Arduino-based water level monitoring system that uses WebSocket technology offered by the Tornado Python framework. At the end of this tutorial, you will have all the knowledge that you need to put up such a system because with this type of system, you can easily monitor the water levels without having to be physically present at the location.

Prerequisites

Before we dive into the implementation, here are a few prerequisites you should have:

  • Understand or have prior working knowledge of the Arduino programming environment.
  • Familiarity with Python programming.
  • The preliminary elements of web site design involve possession of HTML, CSS, and JavaScript.
  • In this circuit, a microcontroller Arduino Uno or similar board can be used.
  • Water level sensor.
  • Buzzer (optional for alerting).
  • An Arduino board and an Arduino IDE compatible programming software such as Arduino Software (IDE) from Arduino.cc and a Python IDE for programming Arduino boards with the Python programming language.

Setting Up the Hardware

  • Take a wire and link it to the water level sensor at the analog port of the Arduino board known as A0.
  • Insert a buzzer on digital pin number 8 (this is done for alarming).
  • Make sure the Arduino board is connected to your computer through a USB cable connection.

Arduino Code:

// Arduino Code for Water Level Sensor Monitoring

const int waterSensorPin = A0; // Analog pin connected to the water level sensor
const int wetThreshold = 70;   // Define threshold for wet condition
const int dryThreshold = 30;   // Define threshold for dry condition
const int buzzerPin = 8;       // Digital pin connected to the buzzer

void setup() {
  Serial.begin(9600);         // Initialize serial communication
  pinMode(buzzerPin, OUTPUT); // Set the buzzer pin as an output
}

void loop() {
  int totalValue = 0;
  int numReadings = 10;

  // Take multiple readings to get an average value
  for (int i = 0; i < numReadings; i++) {
    totalValue += analogRead(waterSensorPin);
    delay(50); // Small delay between readings
  }

  int averageValue = totalValue / numReadings;

  // Calibrate the sensor readings
  int minSensorValue = 300; // Value when the sensor is completely out of the water
  int maxSensorValue = 700; // Value when the sensor is fully submerged

  // Map the sensor value to a percentage based on calibrated min and max values
  int waterLevel = map(averageValue, minSensorValue, maxSensorValue, 0, 100);
  waterLevel = constrain(waterLevel, 0, 100); // Ensure the value is within 0-100%

  // Determine if the plant is wet or dry based on thresholds
  String status;
  if (waterLevel >= wetThreshold) {
    status = "Wet";
    digitalWrite(buzzerPin, LOW); // Turn off buzzer if water level is adequate
  } else if (waterLevel <= dryThreshold) {
    status = "Dry";
    digitalWrite(buzzerPin, HIGH); // Turn on buzzer if water level is low
  } else {
    status = "Moist"; // Optional: add a 'Moist' condition between dry and wet
    digitalWrite(buzzerPin, LOW); // Turn off buzzer if water level is adequate
  }

  Serial.println(status); // Send the status of the water plant
  delay(500); // Delay before next reading
}

Python Code (Server Side):

# Python code using Tornado framework with WebSocket communication for real-time water level sensor monitoring

import tornado.ioloop
import tornado.web
import tornado.websocket
import serial

# Arduino serial port
arduino_port = 'COM5'

# Global variable to store WebSocket clients
clients = []

# Define the Tornado WebSocket handler
class WSHandler(tornado.websocket.WebSocketHandler):
    def open(self):
        clients.append(self)

    def on_message(self, message):
        pass

    def on_close(self):
        clients.remove(self)

# Define the Tornado request handler for the main page
class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.render("index.html")

# Read data from serial port and broadcast to clients
def read_serial():
    try:
        ser_bytes = ser.readline().decode().strip()  # Read data from serial port
        if ser_bytes:  # Check if data is received
            for client in clients:
                client.write_message(ser_bytes)  # Broadcast data to all clients
    except Exception as e:
        print("Error reading serial data:", e)

# Configure the Tornado application
def make_app():
    return tornado.web.Application([
        (r"/", MainHandler),
        (r"/ws", WSHandler),
    ], template_path='templates')

if __name__ == "__main__":
    # Open serial connection to Arduino
    ser = serial.Serial(arduino_port, 9600, timeout=1)
    # Start Tornado web application
    app = make_app()
    app.listen(8888)  # Listening on port 8888
    print("Server started at http://localhost:8888")

    # Periodically read serial data and broadcast to clients
    tornado.ioloop.PeriodicCallback(read_serial, 1000).start()

    tornado.ioloop.IOLoop.current().start()

HTML & JavaScript (Client Side): Create templates folder templates/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Water Level Sensor</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.3/css/all.min.css" integrity="sha512-i+6wC4O0QbVIgRx3Nkz4oC5qHpDwhWRVSyR1PdwF7dmMZ0wTt1pIVK6MKmKr+OF3+omBsA0hf1jg7y9hGpy+A==" crossorigin="anonymous" />
    <style>
        body {
            font-family: Arial, sans-serif;
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
            background-color: #f0f0f0;
        }
        .container {
            text-align: center;
        }
        .status-indicator {
            width: 150px;
            height: 150px;
            border-radius: 50%;
            display: flex;
            justify-content: center;
            align-items: center;
            background-color: #fff;
            border: 10px solid #ccc;
            box-shadow: 0 0 20px rgba(0, 0, 0, 0.2);
        }
        .status-text {
            font-size: 24px;
            font-weight: bold;
            color: #333;
        }
        .status-indicator.wet {
            border-color: #4caf50; /* Green border for wet status */
        }
        .status-indicator.dry {
            border-color: #ff0000; /* Red border for dry status */
        }
        .plant-icon {
            font-size: 60px; /* Adjust the size of the icon */
        }
        .warning-message {
            display: none;
            color: #ff0000;
            font-weight: bold;
            margin-top: 10px;
        }
        .warning-visible {
            display: block;
        }
    </style>
    <script>
        var ws = new WebSocket("ws://localhost:8888/ws");

        ws.onmessage = function(event) {
            var status = event.data; // Receive status (wet or dry) from the server
            var statusIndicator = document.getElementById("status-indicator");
            var statusText = document.getElementById("status-text");
            var warningMessage = document.getElementById("warning-message");

            // Update status indicator color and text based on received status
            if (status === 'Wet') {
                statusIndicator.classList.remove('dry');
                statusIndicator.classList.add('wet');
                statusText.innerText = 'Wet';
                warningMessage.classList.remove('warning-visible');
            } else if (status === 'Dry') {
                statusIndicator.classList.remove('wet');
                statusIndicator.classList.add('dry');
                statusText.innerText = 'Dry';
                warningMessage.classList.add('warning-visible');
            }
        };
    </script>
</head>
<body>
    <div class="container">
        <h2> Plant Water Level Status </h2>
        <div id="status-indicator" class="status-indicator">
            <span class="plant-icon"><i class="fas fa-seedling"></i></span>
            <span id="status-text" class="status-text">Waiting for data...</span>
        </div>
        <div id="warning-message" class="warning-message">Warning: Plant needs water!</div>
    </div>
</body>
</html>

Conclusion:
This tutorial reveals an example of the way to design a real-time water level monitoring system through the use of Arduino, Python with Tornado framework, HTML, CSS, and JavaScript. By including the water level sensor that uses an Arduino board on the other hand, you can display the water level and also be notified when the level gets low by use of WebSocket on the web application to detect the level. This project is relevant for demonstrating the use of IoT and web technologies in environmental monitoring, which are critical aspects needed for further enhancements of sustainable actions in the world.

Yes, that must be a very informative and productive tutorial, I always wanted to try such lessons. To expand your knowledge, and get to know more information on similar projects, I would like to invite you to our YouTube channel, and the blog section at our website. We let our audience download tutorials, development projects and other informational materials regarding Arduino, IoT, and web development. You can simply subscribe for membership and get updates on the latest trends and strategies of your field along with other innovative solutions. If you have any suggestions or comments please do not hesitate to share with us. Thank you for your interest and happy learning!

2 thoughts on “How to create a real-time water level monitoring system using Arduino, Python”

Leave a Comment

Your email address will not be published. Required fields are marked *

Shopping Basket