This shows you the differences between two versions of the page.
iothings:proiecte:2022sric:smart-room-monitoring-and-control-system [2023/06/02 02:43] claudiu.pumnea 1 |
iothings:proiecte:2022sric:smart-room-monitoring-and-control-system [2023/06/02 09:11] (current) claudiu.pumnea [Code description] |
||
---|---|---|---|
Line 25: | Line 25: | ||
* Module with Relays: A module equipped with two relays is employed to manage the kitchen's electrical devices. The relays enable control over the lights and outlets, enhancing convenience and energy management. | * Module with Relays: A module equipped with two relays is employed to manage the kitchen's electrical devices. The relays enable control over the lights and outlets, enhancing convenience and energy management. | ||
+ | * EspWROOM32: Microcontroller that integrates Wi-Fi (802.11 b/g) and Bluetooth (dual mode version 4.2) capabilities. It serves as the core of the project, enabling data collection from sensors and transmitting the it to a database through requests. Additionally, the ESP32 is responsible for controlling various components of the system, such as managing lights and outlets in the kitchen. | ||
+ | === Hardware setup === | ||
+ | {{ :iothings:proiecte:2022sric:hardwaresetup.jpeg?300 |}} | ||
+ | ---- | ||
+ | |||
+ | ===== Software ===== | ||
+ | |||
+ | Technologies used in the project: | ||
+ | |||
+ | * HTML5: It the latest version of the Hypertext Markup Language and is used for structuring and presenting the content of web pages. | ||
+ | |||
+ | * CSS (Cascading Style Sheets): CSS is a style sheet language used to describe the visual presentation of HTML documents. It is used to enhance the appearance and layout of web pages. | ||
+ | |||
+ | * TypeScript & TypeORM: TypeScript is a superset of JavaScript that adds static typing and additional features to the language. It enhances code maintainability and provides improved tooling support. TypeORM is an Object-Relational Mapping (ORM) library that simplifies database interactions in TypeScript/JavaScript projects. | ||
+ | |||
+ | * Nest.js: Nest.js is a framework for building server-side applications using TypeScript. It leverages the capabilities of Node.js and provides a modular, scalable, and opinionated structure for building back-end applications. | ||
+ | |||
+ | * PostgreSQL: PostgreSQL is an open-source relational database management system (RDBMS) known for its reliability and robustness. It is used for storing and managing structured data in the project. | ||
+ | |||
+ | * React: A JavaScript library for building user interfaces. React is used for creating dynamic and interactive components on the client-side of the application. | ||
+ | |||
+ | 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 ==== | ||
+ | |||
+ | <code> | ||
+ | #include <DHT.h> | ||
+ | #include <string.h> | ||
+ | #include <HTTPClient.h> | ||
+ | #include <WiFi.h> | ||
+ | #include <ArduinoJson.h> | ||
+ | </code> | ||
+ | |||
+ | ---- | ||
+ | |||
+ | |||
+ | ==== Constants and global variables ==== | ||
+ | |||
+ | <code> | ||
+ | |||
+ | #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 ; | ||
+ | </code> | ||
+ | |||
+ | |||
+ | ---- | ||
+ | |||
+ | ==== Setup ==== | ||
+ | |||
+ | <code> | ||
+ | void setup() { | ||
+ | |||
+ | Serial.begin(9600); | ||
+ | setupWiFi(); | ||
+ | initializePins(); | ||
+ | initializeBoard(); | ||
+ | |||
+ | } | ||
+ | </code> | ||
+ | |||
+ | === setupWiFi function === | ||
+ | |||
+ | <code> | ||
+ | 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()); | ||
+ | | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | |||
+ | === initializePins function === | ||
+ | |||
+ | <code> | ||
+ | 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(); | ||
+ | |||
+ | } | ||
+ | </code> | ||
+ | |||
+ | === initializeBoard function === | ||
+ | |||
+ | <code> | ||
+ | void initializeBoard() { | ||
+ | |||
+ | http.begin(client, "http://myIp:3050/apartments/initializeApartmentAndRoom?apartmentNumber=" + String(apartmentNumber) + "&roomType=" + roomType + "¤tOutletRelayExists=" + (currentOutletRelayExists ? "true" : "false") + "&lightRelayExists=" + (lightRelayExists ? "true" : "false")); | ||
+ | |||
+ | int httpResponseCode = http.GET(); | ||
+ | |||
+ | |||
+ | String httpResponse = http.getString(); | ||
+ | |||
+ | deserializeJson(getInitializeDoc, httpResponse); | ||
+ | delay(50); | ||
+ | |||
+ | // Free resources | ||
+ | http.end(); | ||
+ | |||
+ | } | ||
+ | </code> | ||
+ | |||
+ | 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 ==== | ||
+ | <code> | ||
+ | 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; | ||
+ | } | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | |||
+ | 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 === | ||
+ | <code> | ||
+ | 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; | ||
+ | |||
+ | } | ||
+ | </code> | ||
+ | === getRelayStatus function === | ||
+ | |||
+ | <code> | ||
+ | void getRelayStatus(const char * lightRelayId, const char * currentOutletRelayId) { | ||
+ | http.begin(client, "http://192.168.137.241:3050/electricalRelay/statusRelays?lightRelayId=" + String(lightRelayId) + "¤tOutletRelayId=" + String(currentOutletRelayId)); | ||
+ | |||
+ | int httpResponseCode = http.GET(); | ||
+ | |||
+ | String httpResponse = http.getString(); | ||
+ | | ||
+ | deserializeJson(getRelayStatusDoc, httpResponse); | ||
+ | } | ||
+ | </code> | ||
+ | |||
+ | |||
+ | === setRelayStatus function === | ||
+ | <code> | ||
+ | void setRelayStatus(bool lightRelayIsOn, bool currentOutletRelayIsOn){ | ||
+ | |||
+ | digitalWrite(LIGHT_RELAY_PIN, lightRelayIsOn); | ||
+ | digitalWrite(CURRENT_OUTLET_RELAY_PIN, currentOutletRelayIsOn); | ||
+ | |||
+ | } | ||
+ | |||
+ | </code> | ||
+ | |||
+ | === postRequestTemperatureHumidity === | ||
+ | <code> | ||
+ | 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(); | ||
+ | } | ||
+ | |||
+ | </code> | ||
+ | |||
+ | 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 ===== | ||
+ | |||
+ | {{ :iothings:proiecte:2022sric:databasesystemmonitoringandcontrol.png |}} | ||
+ | |||
+ | - User Table: | ||
+ | * The "user" table contains relevant data about the users of the web application. | ||
+ | * It helps identify the users connected to the web app and determines the data to retrieve from the database. | ||
+ | |||
+ | - Apartment Table: | ||
+ | |||
+ | * It holds relevant information about the apartments associated with each user. | ||
+ | * This table helps organize and manage apartment-related data. | ||
+ | |||
+ | - Room Table: | ||
+ | |||
+ | * It allows users to check individual graphs for each variable in different rooms of the apartment. | ||
+ | * This table stores relevant data about the rooms in the apartment. | ||
+ | * | ||
+ | - Electric Relay Table: | ||
+ | |||
+ | * The "electrical_relay" table is used to control the outputs and lights in the system. | ||
+ | * It has properties like "isOn" to indicate if the relay should be closed or not, and "isUsedFor" to specify whether it is used for outlets or lights. | ||
+ | * This table helps manage and control the electrical components of the system. | ||
+ | - Room Inputs Table: | ||
+ | |||
+ | * The "room_inputs" table is used to store variables related to monitoring the room. | ||
+ | * It holds the data collected from sensors or other inputs for monitoring purposes. | ||
+ | |||
+ | ==== Application walkthrough ===== | ||
+ | |||
+ | First time when you will open the app you will be redirected to the login page: | ||
+ | |||
+ | {{ :iothings:proiecte:2022sric:loginpagemonitoringandcontrolsystem.png |}} | ||
+ | |||
+ | 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. | ||
+ | |||
+ | {{ :iothings:proiecte:2022sric:loginmonitoringandcontrolsystem.png |}} | ||
+ | |||
+ | ===== 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: | ||
+ | * https://www.youtube.com/watch?v=L_2GDmiQ1Do Here the app is presented with the focus on its date pickers feature. | ||
+ | * https://www.youtube.com/watch?v=gNgK9Tk7Zqg&list=PLtRGGOiyGrNN6PvqboIy8p1ljLqFSURG8&index=2 Here, the system control part of the project is showcased. By accident, I connected the bulb to the current outlet relay instead of connecting it to the designated light relay, but you still can see the functionality. | ||
+ | * https://www.youtube.com/watch?v=_KdiClldT98 Here is showed the case where the level of the water is over 30% and the relays are turned off automatically. | ||
+ | |||
+ | |||
+ | ===== Bibliography ==== | ||
+ | |||
+ | * Typeorm: https://typeorm.io/ | ||
+ | * Typescript: https://www.typescriptlang.org/docs/ | ||
+ | * Nest: https://docs.nestjs.com/ | ||
+ | * React: https://reactjs.org/docs/getting-started.html | ||
+ | * esp32: http://esp32.net/, https://ieeexplore.ieee.org/abstract/document/8765944 | ||
+ | * Dht11: https://components101.com/sensors/dht11-temperature-sensor | ||
+ | * MQ2: https://lastminuteengineers.com/mq2-gas-senser-arduino-tutorial/ | ||
+ | * WaterLevelSensor: https://lastminuteengineers.com/water-level-sensor-arduino-tutorial/ | ||
+ | * Relays: https://www.galco.com/comp/prod/relay.htm | ||
+ | * RestApi: https://www.restapitutorial.com/lessons/httpmethods.html | ||