Table of Contents

Smart room monitoring and control system

Author: Claudiu-Catalin Pumnea - SRIC

Short Description of the project

The “Smart room monitoring and control system” is an innovative project that focuses on collecting environment parameters and leveraging them to control the lights and outlets of a kitchen. The system utilizes a range of sensors including a water level detection sensor, a temperature and humidity sensor, and a gas sensor. Additionally, a control module equipped with two relays is employed for managing the lights and outlets.

To provide users with real-time data and control functionality, a web application has been developed. The web app features intuitive graphs displaying the humidity and temperature readings, allowing users to visualize the environmental conditions in the kitchen. Furthermore, the app provides the capability to control the status of the relays, enabling users to remotely manage the lights and outlets for enhanced convenience and energy efficiency.


Hardware

To develop this project, the following components were utilized:

Hardware setup


Software

Technologies used in the project:

These technologies form the foundation of the project, combining front-end (HTML, CSS, React), back-end ( Nest.js), and database (PostgreSQL) components to create a comprehensive web application with enhanced functionality and a reliable data storage system.

Code

Used Libraries

#include <DHT.h>
#include <string.h>
#include <HTTPClient.h>
#include <WiFi.h>
#include <ArduinoJson.h>

Constants and global variables

#define LIGHT_RELAY_PIN 18
#define CURRENT_OUTLET_RELAY_PIN 4

//Water level sensor
#define WATER_LEVEL_SENSOR_PIN 34

//Gas sensor
#define GAS_SENSOR_MQ2_PIN 35

//Temperature sensor
#define DHTPIN 19
#define DHTTYPE DHT11
DHT dht(DHTPIN, DHTTYPE); //Initialize dht

//Room and apartment constants
const char* roomType = "Kitchen";
const int apartmentNumber = 49;

//Initialize Docs
DynamicJsonDocument getInitializeDoc(2048);
DynamicJsonDocument getRelayStatusDoc(2048);

////Wifi credentials
const char* ssid = "yourWifiNameHere";
const char* password = "yourWifiPasswordHere";


//Initialize wifiClient and httpClient
WiFiClient client;
HTTPClient http;

//Flags if relays exists

bool currentOutletRelayExists = true;
bool lightRelayExists = true;

//Counter for sending the post message
int counter = 0;

//Declare gas_value and wate_level_value
float gas_value ;
float water_Level_value ;

Setup

void setup() {

Serial.begin(9600);
setupWiFi();
initializePins();
initializeBoard();

}

setupWiFi function

void setupWiFi() {
  WiFi.begin(ssid, password);
  Serial.print("\n Connecting to Wifi");
  
  pinMode(2, OUTPUT);
  while ( WiFi.status() != WL_CONNECTED) {
    Serial.print(".");
    digitalWrite(2, HIGH);
    delay(250);
    digitalWrite(2, LOW);
    delay(250);
  }

  digitalWrite(2, HIGH);
  Serial.println("\n Connected to the WiFi network");
  Serial.print("\n IP address: ");
  Serial.print(WiFi.localIP());
  
}

initializePins function

void initializePins() {

  pinMode(LIGHT_RELAY_PIN, OUTPUT);
  pinMode(CURRENT_OUTLET_RELAY_PIN, OUTPUT);
  pinMode(GAS_SENSOR_MQ2_PIN, INPUT);

  //Setting the initial state of the relays
  digitalWrite(LIGHT_RELAY_PIN, HIGH);
  digitalWrite(CURRENT_OUTLET_RELAY_PIN, HIGH);
  dht.begin();

}

initializeBoard function

void initializeBoard() {

  http.begin(client, "http://myIp:3050/apartments/initializeApartmentAndRoom?apartmentNumber=" + String(apartmentNumber) + "&roomType=" + roomType + "&currentOutletRelayExists=" + (currentOutletRelayExists ? "true" : "false") + "&lightRelayExists=" + (lightRelayExists ? "true" : "false"));

  int httpResponseCode = http.GET();


  String httpResponse = http.getString();

  deserializeJson(getInitializeDoc, httpResponse);
  delay(50);

  // Free resources
  http.end();

}

The code is designed to be robust and flexible. Upon loading the code onto the board, it requires the apartment number and room type for database identification. When the board connects to the internet, it sends a GET request to initialize variables. If the apartment number is not found, a new apartment is registered in the database. The same validation and creation mechanism is used for room initialization. During connection, relay presence and types are marked in the database using boolean indicators sent by the ESP32. For existing apartments and rooms, their relevant information is sent and the microcontroller automatically associates received data and IDs from the server.

Void loop

void loop() {

//Saving the needed ids from backend
const char* lightRelayId = getInitializeDoc["electricalRelays"]["lightRelayId"];
const char* currentOutletRelayId = getInitializeDoc["electricalRelays"]["currentOutletRelayId"];
const char* apartmentId = getInitializeDoc["apartmentId"];
const char* roomId = getInitializeDoc["roomId"];

//Initialize Relays status
bool lightRelayIsOn;
bool currentOutletRelayIsOn;

gas_value = readAndCheckGas();
water_Level_value = readWaterLevel();

//Getting relays status
  if(water_Level_value < 30 ){
    getRelayStatus(lightRelayId, currentOutletRelayId);

    //Changing the relay status booleans
    lightRelayIsOn = getRelayStatusDoc["lightRelayIsOn"] == 1 ?  true : false;
    currentOutletRelayIsOn = getRelayStatusDoc["currentOutletRelayIsOn"] == 1 ? true : false;

    setRelayStatus(lightRelayIsOn, currentOutletRelayIsOn);
  } else {
    setRelayStatus(false,false);
  }

  //Read humidity
  float h = dht.readHumidity();
  // Read temperature as Celsius (the default)
  float t = dht.readTemperature();

  // Check if any reads failed and exit early (to try again).
  if (isnan(h) || isnan(t)) {

    Serial.println(F("\n Failed to read from DHT sensor!"));
    return;

  }
Serial.println(counter);
  if (counter == 0) {
    postRequestTemperatureHumidity( h,  t, String("roomInputs"), roomId);
    counter = 240;
  }
  else {
    counter -= 1; 
  }
}

In the loop function of the microcontroller code, there is a continuous reading of the gas level and water level. If the water level exceeds 30%, the relay status is automatically set to LOW. This precautionary measure is taken to prevent potential short circuit problems in case of rising water levels. On the other hand, if the water level is below 30%, a request is made to the database through the backend. This request aims to retrieve the current status of the relays. After the request is completed, the setRelayStatus function is utilized to update the relay status accordingly. Since the loop function runs continuously, making a request to the database for the relay status provides real-time updates and makes the application feel responsive. As the request is made within a short time interval, the system stays synchronized with the actual relay status.

readGasLevel and readWaterLevel functions

float readGasLevel() {

  float gas_value = analogRead(GAS_SENSOR_MQ2_PIN);
  gas_value = gas_value * 100 / 4095;
  delay(50);

  return gas_value;
}

float readWaterLevel() {

  float water_Level_value = analogRead(WATER_LEVEL_SENSOR_PIN);
  water_Level_value = water_Level_value * 100 / 4095;
  delay(50);
  return water_Level_value;

}

getRelayStatus function

void getRelayStatus(const char * lightRelayId, const char * currentOutletRelayId) {
  http.begin(client, "http://192.168.137.241:3050/electricalRelay/statusRelays?lightRelayId=" + String(lightRelayId) +  "&currentOutletRelayId=" + String(currentOutletRelayId));

  int httpResponseCode = http.GET();

  String httpResponse = http.getString();
  
  deserializeJson(getRelayStatusDoc, httpResponse);
}

setRelayStatus function

void setRelayStatus(bool lightRelayIsOn, bool currentOutletRelayIsOn){

  digitalWrite(LIGHT_RELAY_PIN, lightRelayIsOn);
  digitalWrite(CURRENT_OUTLET_RELAY_PIN, currentOutletRelayIsOn);

}

postRequestTemperatureHumidity

void postRequestTemperatureHumidity(int h, int t, String route, const char * roomId) {

  http.begin(client, "http://192.168.137.241:3050/" + route);
  http.addHeader("Content-Type", "application/json");

  DynamicJsonDocument doc(2048);
  doc["humidity"] = String(h);
  doc["temperature"] = String(t);
  doc["roomId"] = String(roomId);

  String requestBody;
  serializeJson(doc, requestBody);
  Serial.print(requestBody);
  int httpResponseCode = http.POST(requestBody);
  String httpResponse = http.getString();
  Serial.print("\n HTTP Response: ");
  Serial.println(httpResponse);
  Serial.print("\n HTTP Response code: ");
  Serial.println(httpResponseCode);

  // Free resources
  http.end();
}

This function is executed every minute, thanks to the counter variable that has been created. This enables the system to store data at regular intervals of one minute. By capturing and storing data periodically, the system can maintain a time-stamped record of various parameters or measurements.


Web app short description

Database

- User Table:

  1. Apartment Table:
  1. Room Table:
  1. Electric Relay Table:
  1. Room Inputs Table:

Application walkthrough

First time when you will open the app you will be redirected to the login page:

After a successful login, users are directed to the monitoring and control page. The page features two dropdown menus, one for selecting the apartments and another for choosing the rooms. This allows users to manage multiple apartments and select specific rooms within those apartments. For the purpose of the demo and screenshots, a single room (the kitchen) is being used.

On the page, users can find two graphs, one displaying the temperature and the other showing the humidity data. These graphs provide visual representation of the collected data. Users can customize the plotted data by selecting a start and end date using the date and time pickers. This allows them to view data recorded within a specific time period.

In addition to monitoring the data, the page also provides switches for controlling the relays individually. This functionality enables users to control the electrical components such as lights or outlets associated with each room.

Demo

In the demo, the main page of the web app will be showcased, featuring the date pickers that allow users to select a specific time range for data visualization. To illustrate the control system, a light bulb will be connected to the relay by connecting the positive terminal of the outlet to the positive terminal of the bulb. We will be able to switch the relay on or off, thereby controlling the power supply to the light bulb in real-time.

Links:

Bibliography