Eco Breathe: Smart-Plant Based Air Purification System

by diyarathaur in Circuits > Arduino

395 Views, 0 Favorites, 0 Comments

Eco Breathe: Smart-Plant Based Air Purification System

ecobreathe.jpg

The escalating issue of air pollution in India has become an increasingly dangerous concern for its citizens. Recent studies have established a concerning link between air pollution and deteriorating mental health. Many Indian cities consistently secure unenviable positions among the world's most polluted urban centers, with New Delhi at the forefront. Shockingly, air pollution exacts a devastating toll, claiming the lives of two million individuals each year. In response to this mounting problem, our Eco Breathe project provides an innovative and sustainable solution. In this period of rising concern regarding air pollution, we firmly believe in leveraging the power of nature. Our project seamlessly integrates the air-purifying capabilities of plants with state-of-the-art technology, forming a distinctive system that not only purifies the air but also delivers real-time monitoring and control.

At the heart of the Eco Breathe system lies a humble yet powerful natural air purifier – the snake plant. Known for its air-purifying qualities, the snake plant is an excellent choice for this innovative project. Its leaves are adept at filtering out toxins and improving air quality. Inside the pot, activated carbon acts as a catalyst to purify the air that passes through the snake plant's roots. This pot is equipped with an array of bottles, serving as a duct to channel air through the activated carbon. A fan, driven by suction, propels the air through this purification process. Our system relies on two essential sensors, the ENS160 for monitoring the Air Quality Index and the PMS5003 for tracking atmospheric dust particles, crucial in providing precise data on the actual Air Quality of the Atmosphere. Additionally, a capacitive soil moisture sensor gauges soil moisture levels, prompting a water container to irrigate the plant if moisture is low. The water level indicator notifies users via email when a refill is needed, thanks to the IFTTT platform.

All of these sensors are seamlessly controlled by the Arduino Nano 33 IoT Board, which communicates wirelessly with a Raspberry Pi, enabling real-time data presentation to users or plant owners.

Supplies

Screenshot 2023-10-21 001212.png
firebase.jpg

Hardware Components:

  • Arduino Nano 33Iot Board
  • ENS160 AQI Sensor
  • PMS5003 Sensor
  • Capacitive Soil Moisture Sensor
  • Water Level Indicator Sensor
  • Pump
  • Raspberry Pi 4 Model
  • Relay

Software Requirements:

  • Real-Time Database

Circuit Diagram

Screenshot 2023-10-17 161517.png

All the sensors used in this project are connected to a PCB, which is controlled by an Arduino Nano 33 IoT board. The Arduino board processes data from the air quality sensors, soil moisture sensors, and water level indicators. The soil moisture sensor monitors the plant's water needs, ensuring it receives the right amount of hydration. Simultaneously, the water level indicator in the container measures the water level to prevent overwatering or dehydration of the plant.

In terms of the connections, we establish a vital link between the sensors and the Arduino Nano 33 IoT Board. The ENS160, PMS5003, and Water Level Indicator draw their power from a 5V input supplied by the Arduino Nano. Meanwhile, the Capacitive soil moisture sensor operates on a 3V power source. All these sensors are grounded via common ground connections (Gnd pins).

It's worth noting that the PMS5003 relies on TX and RX pins, signifying its dependence on serial communication. Additionally, the ENS160 employs UART Communication, functioning seamlessly through the SDA and SCL pins, offering a versatile approach to sensor integration within our system.


Caution: Applying the incorrect voltage may result in sensor malfunction and potential damage. It's essential to double-check and verify the power input for each component to guarantee proper functionality.

Testing Hardware Connections


Individual Sensor Testing:

  • Each component and sensor, such as the soil moisture sensor, AQI sensor (ENS160), pump, water level indicator, and PMS5003 sensor, undergoes thorough individual testing.


Soil Moisture Sensor Calibration:

  • The soil moisture sensor is thoroughly tested using the Arduino IDE. After collecting initial readings, threshold values are defined in the code. These values determine when the pump is activated to hydrate the soil when it becomes dry and when it's deactivated when the soil maintains adequate moisture levels.


Water Level Indicator Assessment:

  • The water level indicator is tested to ensure it can accurately determine whether the water container has sufficient water to sustain the plant's well-being.


Environmental Testing for AQI and PMS5003:

  • The AQI and PMS5003 sensors are evaluated in different environmental conditions to verify their reliability in capturing air quality data.


Integration and Functionality Test:

  • After the individual sensor and component tests, all components are assembled, and a comprehensive test is performed. This test ensures that the sensors collectively operate cohesively without relying on default random values.

Test Code for ENS160 AQI Sensor

/*!
 * @file getMeasureData.ino
 * @brief Get the sensor data by polling (use 3.3V main controller for Fermion version)
 * @details Configure the sensor power mode and parameters (for compensating the calibrated temperature and relative humidity in gas measurement)
 * @copyright Copyright (c) 2010 DFRobot Co.Ltd (http://www.dfrobot.com)
 * @license The MIT License (MIT)
 * @author [qsjhyy](yihuan.huang@dfrobot.com)
 * @version V1.0
 * @date 2021-10-26
 * @url https://github.com/DFRobot/DFRobot_ENS160
 */
#include <DFRobot_ENS160.h>

#define I2C_COMMUNICATION //I2C communication. Comment out this line of code if you want to use SPI communication.

#ifdef I2C_COMMUNICATION
 /**
  *  For Fermion version, the default I2C address is 0x53, connect SDO pin to GND and I2C address will be 0x52
  */
 DFRobot_ENS160_I2C ENS160(&Wire, /*I2CAddr*/ 0x53);
#else
 /**
  * Set up digital pin according to the on-board pin connected with SPI chip-select pin
  * csPin Available Pins. For example: ESP32&ESP8266(D3), m0(6)
  */
 uint8_t csPin = D3;
 DFRobot_ENS160_SPI ENS160(&SPI, csPin);
#endif


void setup(void)
{
 Serial.begin(115200);

 // Init the sensor
 while( NO_ERR != ENS160.begin() ){
  Serial.println("Communication with device failed, please check connection");
  delay(3000);
 }
 Serial.println("Begin ok!");

 /**
  * Set power mode
  * mode Configurable power mode:
  *  ENS160_SLEEP_MODE: DEEP SLEEP mode (low power standby)
  *  ENS160_IDLE_MODE: IDLE mode (low-power)
  *  ENS160_STANDARD_MODE: STANDARD Gas Sensing Modes
  */
 ENS160.setPWRMode(ENS160_STANDARD_MODE);

 /**
  * Users write ambient temperature and relative humidity into ENS160 for calibration and compensation of the measured gas data.
  * ambientTemp Compensate the current ambient temperature, float type, unit: C
  * relativeHumidity Compensate the current ambient temperature, float type, unit: %rH
  */
 ENS160.setTempAndHum(/*temperature=*/25.0, /*humidity=*/50.0);

}

void loop()
{
 /**
  * Get the sensor operating status
  * Return value: 0-Normal operation, 
  *     1-Warm-Up phase, first 3 minutes after power-on.
  *     2-Initial Start-Up phase, first full hour of operation after initial power-on. Only once in the sensor’s lifetime.
  * note: Note that the status will only be stored in the non-volatile memory after an initial 24h of continuous
  *    operation. If unpowered before conclusion of said period, the ENS160 will resume "Initial Start-up" mode
  *    after re-powering.
  */
 uint8_t Status = ENS160.getENS160Status();
 Serial.print("Sensor operating status : ");
 Serial.println(Status);

 /**
  * Get the air quality index
  * Return value: 1-Excellent, 2-Good, 3-Moderate, 4-Poor, 5-Unhealthy
  */
 uint8_t AQI = ENS160.getAQI();
 Serial.print("Air quality index : ");
 Serial.println(AQI);

 /**
  * Get TVOC concentration
  * Return value range: 0–65000, unit: ppb
  */
 uint16_t TVOC = ENS160.getTVOC();
 Serial.print("Concentration of total volatile organic compounds : ");
 Serial.print(TVOC);
 Serial.println(" ppb");

 /**
  * Get CO2 equivalent concentration calculated according to the detected data of VOCs and hydrogen (eCO2 – Equivalent CO2)
  * Return value range: 400–65000, unit: ppm
  * Five levels: Excellent(400 - 600), Good(600 - 800), Moderate(800 - 1000), 
  *        Poor(1000 - 1500), Unhealthy(> 1500)
  */
 uint16_t ECO2 = ENS160.getECO2();
 Serial.print("Carbon dioxide equivalent concentration : ");
 Serial.print(ECO2);
 Serial.println(" ppm");

 Serial.println();
 delay(1000);
}


Software Setup

Screenshot 2023-10-21 112318.png

Probing further, the next step is to set up the Real-time firebase which we are using in order to show the live data in real-time on GUI. The first step is to create an account on the Firebase. Once logged in, create a new Firebase project by navigating to the Firebase Console. Click on the "Add Project" button and give your project a name. This project will serve as the container for real-time data.

Additionally, Firebase offers a Realtime Database feature that's ideal for storing and retrieving data in real-time. After creating the project navigate to the Database section and click on "Create Database". Users can choose either "production" or "test" mode depending upon security preferences.

In order to connect the project made by the user to the real-time database, it is essential to add Firebase SDK to the user application. Firebase supports various platforms, including web, Android, iOS, and more. Most importantly, is to configure Firebase by initializing it with the project's API key and other essential credentials. This ensures the application finally communicates with the Firebase Realtime Database.

Note: During Testing, Ensure Authentication and Host Key Integration in Your Code to Enable Connection and Real-Time Data Retrieval.

Integration of Hardware With Software Setup

After successfully connecting all the sensors to the Arduino Nano on the PCB board, the next crucial step is to validate the functionality of the integrated code. This involves uploading the integrated code into the Arduino Nano using the Arduino IDE. The primary objective is to ensure that the sensors provide precise and accurate values in accordance with project requirements. These values are initially displayed on the Serial Monitor for analysis and verification.

External Voltage Supply: Following the sensor testing phase, the PCB receives an external voltage supply of 12V and 5A. This robust power supply is essential for the stable and consistent operation of the entire system.

To facilitate the seamless transmission of sensor data to the cloud, a fundamental library, <ArduinoHttpClient.h>, is employed. This library empowers the client (Arduino Nano) to effectively send the collected data to Firebase. By harnessing this library, real-time data updates are coordinated on Firebase.

Developing the GUI(Graphical User Interface)

Screenshot 2023-10-22 161849.png
Screenshot 2023-10-22 161923.png

The graphical user interface (GUI) of the EcoBreathe system is implemented using the versatile Tkinter software. This user-friendly interface enhances user experience, providing access to the system's features and functions. The EcoBreathe GUI is thoughtfully designed to offer convenience, security, and personalization, and it includes a login page to ensure data privacy and system access.

Upon successful login, users gain access to a trio of essential options:

1. Show Live Data:

  • This option empowers users to closely monitor real-time air quality, soil moisture, and water levels. It offers a comprehensive snapshot of the system's current performance, delivering timely insights.

2. Previous Data:

  • Users can delve into historical data, enabling them to track changes in air quality, soil moisture, and water levels over time. This historical perspective aids in making informed decisions and adjustments.

3. Pump Options:

  • A key feature of the EcoBreathe system, users have the flexibility to manage the pump's operation. This can be done either automatically or manually, allowing for precise control of the plant's watering requirements. This level of control ensures the well-being of the plant while maintaining an efficient and sustainable approach.

Wireless Communication Between Raspberry Pi & Arduino Nano

Screenshot 2023-10-22 161953.png
Screenshot 2023-10-22 162017.png

After, the GUI is fully developed and operational, the next phase involves fetching real-time data using the Raspberry Pi. This critical step brings the project to life, providing users with dynamic and up-to-the-minute information about air quality, soil moisture, and water levels.

To enable this seamless flow of real-time data, the initial and fundamental requirement is to embed Firebase credentials into the Python code running on the Raspberry Pi. These credentials serve as the authentication gateway, ensuring a secure connection with the Firebase platform.

Once authenticated, the Raspberry Pi retrieves the live data. Upon a user's request, specifically when the "Live Data" button is pressed within the GUI, the Raspberry Pi communicates with Firebase, fetching the most current information. This information is then thoughtfully presented within the GUI, affording users immediate access to vital data on air quality, soil moisture, and water levels.

Precision Control with "Pump Options":

The "Pump Options" feature grants users a crucial degree of control over the watering mechanism. Users can choose between two modes: automatic or manual.

  • Automatic Mode: When a user selects the automatic mode, the system reverts to real-time data posting on Firebase. The pump's operation is synchronized with threshold values. When these values indicate that the soil requires hydration, the pump springs the water into the pot, ensuring that the plant's needs are met. If the threshold values suggest adequate soil moisture, the pump remains in a dormant state. This dynamic and intelligent process ensures optimal plant care while conserving water resources.
  • Manual Mode: Opting for the manual mode provides users with direct control over the pump. The pump can be toggled between "on" and "off" states, allowing users to manage the watering process in a hands-on manner. As users make their selections, Firebase diligently records and communicates the status of the pump, and sends the command to the Arduino Nano, ensuring that the chosen state is accurately executed.

The Code for Integrated Sensors on Arduino

#include <DFRobot_ENS160.h>
#include "Firebase_Arduino_WiFiNINA.h"
#include <Wire.h>
#include "PMS.h"
#include <WiFiNINA.h>
#include <ArduinoHttpClient.h>
// #include <Arduino.h>

DFRobot_ENS160_I2C ENS160(&Wire, 0x53); // Change the I2C address if needed
PMS pms(Serial1);
PMS::DATA data;

const int soil_moisture = A2;
const int indicator = 9;
const int relay = 2;

#define DATABASE_URL " "
#define DATABASE_SECRET " "

#define WIFI_SSID " "
#define WIFI_PASSWORD ""

WiFiClient client;

// IFTTT Webhook settings
char  HOST_NAME[] = "maker.ifttt.com";
String PATH1  = "/trigger/water_level/with/key/YOUR_KEY";
String queryString = "?value1=57&value2=25";

FirebaseData fbdo;

bool waterLevelLowFlag = false;

void setup(void) {
 Serial.begin(9600);
 Serial1.begin(9600);
  
 pinMode(soil_moisture, INPUT);
 pinMode(relay, OUTPUT);
 pinMode(indicator, INPUT);
 // digitalWrite(relay, LOW);

 while (NO_ERR != ENS160.begin()) {
  Serial.println("Communication with device failed, please check connection");
  delay(3000);
 }
 Serial.println("Begin ok!");
 ENS160.setPWRMode(ENS160_STANDARD_MODE);
 ENS160.setTempAndHum(25.0, 50.0);

 // Connect to Wi-Fi and Firebase
 Serial.print("Connecting to Wi-Fi");
 int status = WL_IDLE_STATUS;
 while (status != WL_CONNECTED) {
  status = WiFi.begin(WIFI_SSID, WIFI_PASSWORD);
  Serial.print(".");
  delay(100);
 }
 Serial.println();
 Serial.print("Connected with IP: ");
 Serial.println(WiFi.localIP());
 Serial.println();
 Firebase.begin(DATABASE_URL, DATABASE_SECRET, WIFI_SSID, WIFI_PASSWORD);
 Firebase.reconnectWiFi(true);
}

void water_ifttt()
{
 if (client.connect(HOST_NAME, 80)) {
  Serial.println("Connected to server");
 }
 else {
  Serial.println("Connection Failed");
 }

 client.println("GET " + PATH1 + queryString + " HTTP/1.1");
 client.println("Host: " + String(HOST_NAME));
 client.println("Connection: close");
 client.println();

 while (client.connected()) {
  if (client.available()) {
   char c = client.read();
   Serial.print(c);
  }
 }

 client.stop();
 Serial.println();
 Serial.println("disconnected");
}

void loop() {
 if (pms.read(data)) {

  Serial.print("PM 1.0 (ug/m3): ");
  Serial.println(data.PM_AE_UG_1_0);
  Serial.print("PM 2.5 (ug/m3): ");
  Serial.println(data.PM_AE_UG_2_5);

  Serial.print("PM 10.0 (ug/m3): ");
  Serial.println(data.PM_AE_UG_10_0);

  uint8_t Status = ENS160.getENS160Status();
  Serial.print("Sensor operating status : ");
  Serial.println(Status);

  uint8_t AQI = ENS160.getAQI();
  Serial.print("Air quality index : ");
  Serial.println(AQI);

  uint16_t TVOC = ENS160.getTVOC();
  Serial.print("Concentration of total volatile organic compounds : ");
  Serial.print(TVOC);
  Serial.println(" ppb");

  uint16_t ECO2 = ENS160.getECO2();
  Serial.print("Carbon dioxide equivalent concentration : ");
  Serial.print(ECO2);
  Serial.println(" ppm");

  int moist = analogRead(soil_moisture);
  int level = digitalRead(indicator); // Use digitalRead for digital inputs

  if (level == LOW && !waterLevelLowFlag) {
   water_ifttt();
   waterLevelLowFlag = true; // Set the flag to true to prevent repeated triggers
  } else if (level == HIGH) {
   waterLevelLowFlag = false; // Reset the flag when water level goes back to high
  }
  Serial.println(level);
  Serial.println(moist);
  Firebase.getString(fbdo, "/sensing/mode");
  String mode = fbdo.stringData(); // Store mode in a variable for reuse
  Firebase.getString(fbdo, "/sensing/pump");
  String pump = fbdo.stringData(); // Store pump in a variable for reuse

  if (mode == "Automatic") {
   if (moist > 450 && level == HIGH) {
    digitalWrite(relay, HIGH);
   }

   if (moist < 450 || level == LOW) {
    digitalWrite(relay, LOW);
   }
   if (moist < 450 && level == LOW) {
    digitalWrite(relay, LOW);
   }
  }

  if (mode == "Manual") {
   if (pump == "on") {
    digitalWrite(relay, HIGH);
   } else if (pump == "off") {
    digitalWrite(relay, LOW);
   }
  }


  // Send data to Firebase
  String path = "/sensor_data"; // Change this path to your desired Firebase path
  String jsonStr = "{\"AQI\":" + String(AQI) + ",\"TVOC\":" + String(TVOC) + ",\"ECO2\":" + String(ECO2) + ",\"PMS1_0\":" + String(data.PM_AE_UG_1_0) + ",\"PMS2_5\":" + String(data.PM_AE_UG_2_5) + ",\"PMS10\":" + String(data.PM_AE_UG_10_0) + ",\"soil_moist\":" + String(moist) + ",\"water_level\":" + String(level) + "}";

  if (Firebase.setJSON(fbdo, path, jsonStr)) {
   Serial.println("Data sent to Firebase");
  } else {
   Serial.println("Error sending data to Firebase");
   Serial.println("Error reason: " + fbdo.errorReason());
  }

  Serial.println();
  delay(1000); // Adjust the delay as needed
 }
}

The Code for Raspberry Pi GUI

import tkinter
from tkinter import messagebox
import pyrebase
import time as sleep

config = {}

firebase = pyrebase.initialize_app(config)
database = firebase.database()

sensing_enabled = True

# Create the main window
window = tkinter.Tk()
window.title("Login form")
window.geometry('800x600')
window.configure(bg='#333333')


def retrieve_and_display_live_data():
  aqi_value = database.child("sensor_data/AQI").get().val()
  eco2_value = database.child("sensor_data/ECO2").get().val()
  tvoc_value = database.child("sensor_data/TVOC").get().val()
  pm1_value = database.child("sensor_data/PMS1_0").get().val()
  pm2_value = database.child("sensor_data/PMS2_5").get().val()
  pm10_value = database.child("sensor_data/PMS10").get().val()
  moist_value = database.child("sensor_data/soil_moist").get().val()
  water_value = database.child("sensor_data/water_level").get().val()

  aqi_label.config(text=f"AQI: {aqi_value}")
  eco2_label.config(text=f"CO2: {eco2_value}")
  tvoc_label.config(text=f"TVOC: {tvoc_value}")
  pm1_label.config(text=f"PM1.0: {pm1_value}")
  pm2_label.config(text=f"PM2.5: {pm2_value}")
  pm10_label.config(text=f"PM10.0: {pm10_value}")
  moist_label.config(text=f"Moisture: {moist_value}")
  water_label.config(text=f"Water Level: {water_value}")

  window.after(1000, retrieve_and_display_live_data)


def pump_options():
  if pump_var.get() == 1: # If "Automate" is selected
    messagebox.showinfo("Pump", "Pump will be operated automatically.")
    database.child("sensing/mode/").set("Automatic")
    # Remove the sensing button when in automatic mode
    sensing_button.grid_forget()
  elif pump_var.get() == 2: # If "Manual" is selected
    messagebox.showinfo("Pump", "Pump will be operated manually.")
    database.child("sensing/mode/").set("Manual")
    # Show the sensing button
    sensing_button.grid(row=3, column=0, pady=10, padx=20)
  else:
    messagebox.showwarning("Pump", "Please select an option for the pump.")


def toggle_pump_options():
  if pump_frame.winfo_viewable():
    pump_frame.pack_forget()
  else:
    pump_frame.pack(pady=20)


def login():
  username = "techmates"
  password = "12345"
  if username_entry.get() == username and password_entry.get() == password:
    messagebox.showinfo(title="Login Success",
              message="You successfully logged in.")
    create_main_page()
  else:
    messagebox.showerror(title="Error", message="Invalid login.")


def create_main_page():
  # Clear the login page
  frame.pack_forget()

  # Create a new frame for the main page
  global main_frame
  main_frame = tkinter.Frame(bg='#333333')

  # Create the three buttons
  previous_data_button = tkinter.Button(
    main_frame, text="Previous Data", bg="#FF3399", fg="#FFFFFF", font=("Arial", 20), width=30, command=previous_data)
  live_data_button = tkinter.Button(
    main_frame, text="Live Data", bg="#FF3399", fg="#FFFFFF", font=("Arial", 20), width=30, command=live_data)
  pump_button = tkinter.Button(
    main_frame, text="Pump", bg="#FF3399", fg="#FFFFFF", font=("Arial", 20), width=30, command=pump)

  # Placing buttons in the center vertically
  previous_data_button.grid(row=1, column=0, pady=20)
  live_data_button.grid(row=2, column=0, pady=20)
  pump_button.grid(row=3, column=0, pady=20)

  main_frame.grid_rowconfigure(0, weight=1)
  main_frame.grid_rowconfigure(3, weight=1)

  main_frame.pack(expand=True)


def previous_data():
  # Handle the "Previous Data" action
  pass


def live_data():
  # Hide the main frame and show the live data frame
  main_frame.pack_forget()
  open_live_data_page()


def open_live_data_page():
  global live_data_frame
  live_data_frame = tkinter.Frame(bg='#333333')

  # Create labels to display live data
  global aqi_label, eco2_label, tvoc_label, pm1_label, pm2_label, pm10_label, moist_label, water_label
  aqi_label = tkinter.Label(live_data_frame, text="AQI: Loading...",
               bg='#333333', fg="#FFFFFF", font=("Arial", 16))
  eco2_label = tkinter.Label(
    live_data_frame, text="CO2: Loading...", bg='#333333', fg="#FFFFFF", font=("Arial", 16))
  tvoc_label = tkinter.Label(
    live_data_frame, text="TVOC: Loading...", bg='#333333', fg="#FFFFFF", font=("Arial", 16))
  pm1_label = tkinter.Label(live_data_frame, text="PM1.0: Loading...",
               bg='#333333', fg="#FFFFFF", font=("Arial", 16))
  pm2_label = tkinter.Label(
    live_data_frame, text="PM2.5: Loading...", bg='#333333', fg="#FFFFFF", font=("Arial", 16))
  pm10_label = tkinter.Label(
    live_data_frame, text="PM10.0: Loading...", bg='#333333', fg="#FFFFFF", font=("Arial", 16))
  moist_label = tkinter.Label(live_data_frame, text="Moisture: Loading...",
                bg='#333333', fg="#FFFFFF", font=("Arial", 16))
  water_label = tkinter.Label(
    live_data_frame, text="Water Level: Loading...", bg='#333333', fg="#FFFFFF", font=("Arial", 16))
  # Placing labels on the screen
  aqi_label.pack(pady=10)
  eco2_label.pack(pady=10)
  tvoc_label.pack(pady=10)
  pm1_label.pack(pady=10)
  pm2_label.pack(pady=10)
  pm10_label.pack(pady=10)
  moist_label.pack(pady=10)
  water_label.pack(pady=10)

  # Retrieve and display live data
  retrieve_and_display_live_data()

  # Button to close the live data page
  close_button = tkinter.Button(live_data_frame, text="Close", bg="#FF3399", fg="#FFFFFF", font=(
    "Arial", 16), command=close_live_data_page)
  close_button.pack(pady=20)

  # Configure row weights for vertical centering
  live_data_frame.grid_rowconfigure(0, weight=1)
  live_data_frame.grid_rowconfigure(9, weight=1)

  # Expand the frame to fill the available space
  live_data_frame.pack(expand=True)


def close_live_data_page():
  # Hide the live data frame and show the main frame
  live_data_frame.pack_forget()
  create_main_page()


def pump():
  main_frame.pack_forget()
  pump_data_page()


def pump_data_page():
  global pump_frame
  pump_frame = tkinter.Frame(bg='#333333')

  pump_label = tkinter.Label(pump_frame, text="Pump Options", font=(
    "Helvetica", 25, "bold"), bg="black", fg="white")

  pump_label.grid(row=0, columnspan=2, pady=10) # Increased padding

  # Create radio buttons for pump options
  global pump_var
  pump_var = tkinter.IntVar()

  # Center-align and style radio buttons
  automate_radio = tkinter.Radiobutton(
    pump_frame, text="Automate", variable=pump_var, value=1, font=("Helvetica", 20), bg="black", fg="white")
  automate_radio.grid(row=1, column=0, pady=10, padx=20, sticky="w")

  manual_radio = tkinter.Radiobutton(
    pump_frame, text="Manual", variable=pump_var, value=2, font=("Helvetica", 20), bg="black", fg="white")
  manual_radio.grid(row=2, column=0, pady=10, padx=20, sticky="w")

  #sensing_enabled = True
  global sensing_button
  sensing_button = tkinter.Button(
    pump_frame, text="Pump: OFF", relief=tkinter.RAISED, command=toggle_data_sensing)

  # Create a button to handle pump options
  pump_button = tkinter.Button(
    pump_frame, text="Submit Pump Options", command=pump_options, font=("Helvetica", 20), bg="blue", fg="white")
  pump_button.grid(row=4, columnspan=2, pady=15)

  close_button1 = tkinter.Button(pump_frame, text="Close", bg="#FF3399", fg="#FFFFFF", font=(
    "Arial", 16), command=close_pump_data_page)
  close_button1.grid(row=5, pady=20)

  pump_frame.pack(expand=True)


def toggle_data_sensing():
  global sensing_enabled
  sensing_enabled = not sensing_enabled
  if sensing_enabled:
    sensing_button.config(text="Pump: ON", relief=tkinter.SUNKEN)
    database.child("sensing/pump/").set("on")
  else:
    sensing_button.config(text="Pump: OFF", relief=tkinter.RAISED)
    database.child("sensing/pump/").set("off")


def close_pump_data_page():
  # Hide the live data frame and show the main frame
  pump_frame.pack_forget()
  create_main_page()


# Create widgets for the login form
frame = tkinter.Frame(bg='#333333')

eco_label = tkinter.Label(
  frame, text="EcoBreathe", bg='#333333', fg="#FF3399", font=("Arial", 30))
page_label = tkinter.Label(
  frame, text="by Team TechMates", bg='#333333', fg="#FFFFFF", font=("Arial", 16))
login_label = tkinter.Label(
  frame, text="Login to Proceed", bg='#333333', fg="#FF3399", font=("Arial", 30))
username_label = tkinter.Label(
  frame, text="Username", bg='#333333', fg="#FFFFFF", font=("Arial", 16))
username_entry = tkinter.Entry(frame, font=("Arial", 16))
password_entry = tkinter.Entry(frame, show="*", font=("Arial", 16))
password_label = tkinter.Label(
  frame, text="Password", bg='#333333', fg="#FFFFFF", font=("Arial", 16))
login_button = tkinter.Button(
  frame, text="Login", bg="#FF3399", fg="#FFFFFF", font=("Arial", 16), command=login)

# Placing widgets on the screen
eco_label.grid(row=0, column=0, columnspan=2, sticky="news", pady=10)
page_label.grid(row=1, column=0, columnspan=2, sticky="news", pady=10)
login_label.grid(row=2, column=0, columnspan=2, sticky="news", pady=20)
username_label.grid(row=3, column=0)
username_entry.grid(row=3, column=1, pady=20)
password_label.grid(row=4, column=0)
password_entry.grid(row=4, column=1, pady=20)
login_button.grid(row=5, column=0, columnspan=2, pady=30, sticky='n')

# Configure row weights for vertical centering
frame.grid_rowconfigure(0, weight=1)
frame.grid_rowconfigure(6, weight=1)

frame.pack(expand=True) # Expand the frame to fill the available space
window.mainloop()

Runing System

plant.jpg
Screenshot 2023-10-22 161953.png
Screenshot 2023-10-24 041453.png

Here, is the link for the working project https://youtu.be/wK9gWis_qhI?si=IZAiRAE3bfszNFN9