Smart Parking System

Master: AAC2
Student: Popa-Artene Andrei

Project Overview. Introduction

This research project aims to investigate the potential of utilizing advanced technologies, specifically the ESP32 module, to design and implement an intelligent parking system. The proposed system features a fixed number of parking spaces and utilizes a servomotor to simulate a parking barrier that can operate in two distinct modes, depending on the direction of a vehicle's approach. The manual mode enables an operator to manually raise and lower the barrier via the use of two buttons, while the automatic mode employs proximity sensors to automatically raise the barrier as a vehicle approach.

Furthermore, the proposed parking system features a ventilation subsystem that simulates environmental conditions within the parking lot via the use of temperature and humidity sensors, as well as a motor that controls a fan. The ventilation subsystem is designed to operate in response to temperature and humidity levels that exceed pre-defined thresholds, in order to ensure optimal environmental conditions within the parking lot.

To facilitate monitoring and management of the system, a web-based interface is implemented that allows real-time monitoring of environmental conditions within the parking lot, and the ability to determine the need for ventilation based on sensor data. Through the implementation of this research project, the feasibility of utilizing advanced technologies such as the ESP32 module to create a more efficient and user-friendly parking system was evaluated.

Project Architecture

Hardware Architecture

The proposed system architecture from a hardware perspective includes a selection of electronic components that were chosen based on their functional capabilities and cost-effectiveness.

  • The core component of the system is the ESP32 microcontroller, developed and manufactured by Espressif Systems, a Chinese company based in Shanghai. The ESP32 series is a low-cost, low-power system-on-a-chip that integrates Wi-Fi and dual-mode Bluetooth capabilities, making it an ideal choice for this application;

  • The system includes two breadboards, which are commonly used in the prototyping of electronic circuits. They provide a reusable and non-destructive platform for building and testing circuit designs;

  • The system also includes two HC-SR04 ultrasonic sensors, which provide non-contact measurement capabilities in the range of 2cm to 700cm, with a ranging accuracy of 3mm. These sensors are used to detect the presence of vehicles and trigger the movement of the barrier, allowing entry or exit from the parking lot;

  • The movement of the barrier is simulated using a MicroServo 9G SG90 servo-motor, which operates based on pulse-width modulation (PWM) control signals. The servo-motor is able to maintain a specific angle based on the width of the PWM signal input, thus providing precise and reliable movement of the barrier;

  • In order to monitor the environmental conditions inside the parking lot, the system also includes a DHT11 humidity and temperature sensor. This sensor is used to detect the humidity and temperature inside the parking lot, which can be used to control the ventilation system. *

  • The system also includes a DC motor with a fan, which simulates the ventilation of the parking lot;

  • Furthermore, the system includes two LEDs (green, red) which are used to indicate the status of the barrier, with the red LED indicating that the barrier is down and the green LED indicating that the barrier is up;

  • Finally, the system includes two push buttons with which the manual operation of the barrier can be simulated.;


The simulated diagram of the hardware architecture is highlighted in the picture below, where the wiring of the system is detailed and can be optimally analyzed.
From an electrical point of view, the hardware architecture was configured and displayed below for a better understanding of how the system works and how can it be implemented from scratch.

In the end, the physically hardware architecture fully configured on the breadboard can be observed in the picture below where the barrier between the ultrasonic sensors can be also noticed. A full view of the parking lot is illustrated in the picture below. The difference between these two pictures is that in this case the whole parking lot can be observed and, implicitely, the placement of the system on the table.

Software Architecture

In regards to the software architecture implemented for this project, an important aspect to consider is the structure of the file system. As depicted in the diagram below, a comprehensive examination of each file will be conducted in this chapter, in order to thoroughly explain the configuration of each hardware component and its role in the overall system behavior. The file “project_IoT.ino” represents the configuration that is uploaded onto the ESP32 microcontroller. In the initial segment of code, the inclusion of the necessary libraries is essential to achieve the intended outcomes.

  • The AsyncTCP.h and ESPAsyncWebServer.h libraries were used to facilitate communication with the web interface designed for this project;
  • The DHT.h library is used for the Humidity and Temperature sensor;
  • The ESP32Servo.h is used for the SG90 Servomotor;
  • The WiFi.h library serves to establish a connection to the desired WiFi network to which the ESP32 board will connect;
  • The SPIFFS.h library is used to mount the file system of the ESP32 board.
#include <AsyncTCP.h>
#include <ESP32Servo.h>
#include <DHT.h>
#include <SPIFFS.h>
#include <WiFi.h>
#include <ESPAsyncWebServer.h>

This snippet of code declares several variables and constants that are used in the program. The Servo object “myservo” is created to control a servo motor. The trigPin1, echoPin1, trigPin2, and echoPin2 constants represent the pin numbers that are used for the trigger and echo pins of two ultrasonic sensors. The led1 and led2 constants represent the pin numbers of two LEDs. The button1 and button2 constants represent the pin numbers of two buttons. The DHT_PIN constant represents the pin number of the DHT11 sensor, DC_Motor and servoPin constants represents the pin number of the DC Motor and Servo Motor respectively. The ssid and password constants are used to store the WiFi network's name and password. The “state” variable is used to keep track of the state of the trigger button from the web interface and the “servoPos” variable is used to store the position of the servo motor. The AsyncWebServer object “server” is created and set to listen on port 80. The DHT object “dht” is created and connected to the DHT11 sensor on pin DHT_PIN.

Servo myservo;

const int trigPin1 = 17;
const int echoPin1 = 16;
const int trigPin2 = 5;
const int echoPin2 = 18;
const int led1 = 14;
const int led2 = 27;
const int button1 = 15;
const int button2 = 2;
const int DHT_PIN = 21;
const int DC_Motor = 12;
const int servoPin = 13;
const char* ssid = "Alex";
const char* password = "Robertandrei";
int state = 0;
int servoPos = 0;
AsyncWebServer server(80);

DHT dht(DHT_PIN, DHT11);

The next snippet of code is the setup function, which is executed once when the program starts. The Serial.begin(115200) line initializes serial communication at a baud rate of 115200. The next four lines are used to allocate timers for the PWM functionality. The myservo.setPeriodHertz(50) sets the frequency of the PWM used to control the servo to 50Hz, and myservo.attach(servoPin, 500, 2400) attaches the servo to the pin specified by the servoPin constant and sets the pulse width range. Next lines set the specified pins as input or output. The dht.begin() line starts communication with the DHT11 sensor.

void setup() {
  Serial.begin(115200);
  ESP32PWM::allocateTimer(0);
  ESP32PWM::allocateTimer(1);
  ESP32PWM::allocateTimer(2);
  ESP32PWM::allocateTimer(3);
  myservo.setPeriodHertz(50);    
  myservo.attach(servoPin, 500, 2400); 
  pinMode(trigPin1, OUTPUT);
  pinMode(echoPin1, INPUT);
  pinMode(trigPin2, OUTPUT);
  pinMode(echoPin2, INPUT);
  pinMode(led1, OUTPUT);
  pinMode(led2, OUTPUT);
  pinMode(button1, INPUT_PULLUP);
  pinMode(button2, INPUT_PULLUP);
  pinMode(DHT_PIN, INPUT);
  pinMode(DC_Motor, OUTPUT);
  dht.begin();
 

This code initializes the SPIFFS (SPI Flash File System) on the ESP32 board. If an error has occurred, the program will print an error message and the function will return, effectively exiting the setup. If the SPIFFS is successfully mounted, the program will continue.

  // Initialize SPIFFS
  if(!SPIFFS.begin()){
    Serial.println("An Error has occurred while mounting SPIFFS");
    return;
  }

This snippet is used to establish a WiFi connection on the ESP32. The WiFi.begin(ssid, password) line attempts to connect to the WiFi network specified by the ssid and password constants. The while loop will keep executing until the ESP32 successfully connects to the WiFi network. Once the ESP32 is connected to the network, the loop will exit. The Serial.println(WiFi.localIP()) line will print the ESP32's local IP address to the serial port. This is useful for debugging, as it allows the user to check if the ESP32 has connected to the correct network and to see the IP address assigned to the ESP32 by the router.

  // Connect to Wi-Fi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(1000);
    Serial.println("Connecting to WiFi..");
  }

  // Print ESP32 Local IP Address
  Serial.println(WiFi.localIP());

The next part of the code is setting up different routes for handling different HTTP requests on the AsyncWebServer. Each route is associated with a callback function that is executed when a request is made to that route.

  • The first route ”/” is set to handle HTTP GET requests and sends the contents of the file “index.html” located in the SPIFFS file system as a response to the client.
  • The second route ”/humidity” is set to handle HTTP GET requests and sends the humidity value read from the DHT11 sensor as a plain text response to the client.
  • The third route ”/temperature” is set to handle HTTP GET requests and sends the temperature value read from the DHT11 sensor as a plain text response to the client.
  • The forth route ”/turnOn” is set to handle HTTP GET requests and it sets the state variable to 1.
  • The fifth route ”/turnOff” is set to handle HTTP GET requests, it sets the state variable to 0.

At the end, the server is started by calling the server.begin() method.

 
  // Route for root / web page
  server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send(SPIFFS, "/index.html");
  });
  server.on("/humidity", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", String(dht.readHumidity()).c_str());
  });
  server.on("/temperature", HTTP_GET, [](AsyncWebServerRequest *request){
    request->send_P(200, "text/plain", String(dht.readTemperature()).c_str());
  });

  server.on("/turnOn", HTTP_GET, [](AsyncWebServerRequest *request){
    digitalWrite(DC_Motor, HIGH);
    state = 1;
    request->send(200, "text/plain", "OK");
  });

  server.on("/turnOff", HTTP_GET, [](AsyncWebServerRequest *request){
    state = 0;
    digitalWrite(DC_Motor, LOW);
    request->send(200, "text/plain", "OK");
  });
 
  // Start server
  server.begin();
  digitalWrite(led2, HIGH);
}

The next snippet of code starts the main loop function that is executed repeatedly after the setup function. It starts by declaring four integer variables duration1, distance1, duration2, distance2 which are used to store the duration and distance measurements from the two ultrasonic sensors. The int button1State = digitalRead(button1); and int button2State = digitalRead(button2); lines read the state of the button1 and button2 and store it in the corresponding variables. The int state1 = digitalRead(DC_Motor) line reads the state of the DC Motor and store it in the state1 variable.

void loop() {
  int duration1, distance1;
  int duration2, distance2;
  int button1State = digitalRead(button1);
  int button2State = digitalRead(button2);
  int state1 = digitalRead(DC_Motor);

The following code is used for measuring the distance using two ultrasonic sensors. The distance is calculated by sending out a sound pulse, measuring the time it takes for the echo to return, and then using that time to calculate the distance.

  // Ultrasound sensor 1
  digitalWrite(trigPin1, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin1, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin1, LOW);
  duration1 = pulseIn(echoPin1, HIGH);
  distance1 = (duration1/2) / 29.1;

  // Ultrasound sensor 2
  digitalWrite(trigPin2, LOW);
  delayMicroseconds(2);
  digitalWrite(trigPin2, HIGH);
  delayMicroseconds(10);
  digitalWrite(trigPin2, LOW);
  duration2 = pulseIn(echoPin2, HIGH);
  distance2 = (duration2/2) / 29.1;

With the following code, the program checks if the distance measured by the ultrasound sensors (distance1 and distance2) is less than 5. If the distance is less than 5, it performs a series of actions: it sets a servo motor (myservo) to a position of 90 degrees, turns on one LED (led1) and turns off another LED (led2), waits for 5 seconds, sets the servo motor back to 0 degrees, turns off the previously turned on LED (led1) and turns on the previously turned off LED (led2).

  // Check if first ultrasound sensor is triggered
  if (distance1 < 5) {
    myservo.write(90);
    digitalWrite(led1, HIGH);
    digitalWrite(led2, LOW);
    delay(5000);
    myservo.write(0);
    digitalWrite(led1, LOW);
    digitalWrite(led2, HIGH);
  }

  // Check if second ultrasound sensor is triggered
  if (distance2 < 5) {
    myservo.write(90);
    digitalWrite(led1, HIGH);
    digitalWrite(led2, LOW);
    delay(5000);
    myservo.write(0);
    digitalWrite(led1, LOW);
    digitalWrite(led2, HIGH);
  }

This code is checking the state of two buttons, and perform an action depending on the state of the button.

The first if statement checks if the button1 is pressed by checking if the button1State variable is LOW, if it is, then it performs the following actions:

  • The servo motor is set to an angle of 90 degrees by calling myservo.write(90).
  • The led1 is turned on by calling digitalWrite(led1, HIGH)
  • The led2 is turned off by calling digitalWrite(led2, LOW)

The second if statement checks if the button2 is pressed by checking if the button2State variable is LOW, if it is, then it performs the reverse actions described above.

  // Check if button 1 is pressed
  if (button1State == LOW) {
    myservo.write(90);
    digitalWrite(led1, HIGH);
    digitalWrite(led2, LOW);
     delay(5000);
  }

  // Check if button 2 is pressed
  if (button2State == LOW) {
    myservo.write(0);
    digitalWrite(led1, LOW);
    digitalWrite(led2, HIGH);
     delay(5000);
  }

This code is reading the humidity and temperature from a DHT11 sensor, and then checking if the values read are valid. If they are not, it will print an error message. Then it checks if the humidity is greater than 80 or the temperature is greater than 40 or the state variable is equal to 1, if any of these conditions are true, it turns on the DC motor by setting its pin to high. If none of these conditions are true, it turns off the DC motor by setting its pin to low.

  // Read humidity and temperature
  float h = dht.readHumidity();
  float t = dht.readTemperature();
  if (isnan(h) || isnan(t)) {
       Serial.println("Error: Failed to read from DHT sensor!");
    } else {
        //
    }

  // Control DC motor
  if (h > 80 || t > 40 || state == 1) {
    digitalWrite(DC_Motor, HIGH);
  } else {
    digitalWrite(DC_Motor, LOW);
  }
}

In this project, the ESP32 microcontroller serves as the central control unit for the operation of all sensors and motors involved. This is demonstrated also in the demo video. Additionally, the file “index.html” was uploaded to the microcontroller's internal memory in order to facilitate the display of a user-friendly web interface, as depicted in the picture below. Through this interface, users can easily monitor the temporal evolution of humidity and temperature and make a decision to activate the ventilation system via the button provided at the top of the page.

The flash on the ESP32 internal memory of the “index.html” file was realized with a plugin called ESP32FS, documented in the resource chapter of this project.


The above code is the HTML document that contains the structure, layout, and styling of the web page depicted above. The web page is used to display two Highchart graph and a switch button. The switch button is used to turn on and off the ventilation system, when the switch is on the button is green, and when it's off it's red. The page uses a stylesheet to define the layout and design of the page elements such as the switch, the labels and the charts. The JavaScript code is used to create the charts and to handle the switch button “onchange” event.

<!DOCTYPE HTML><html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://code.highcharts.com/highcharts.js"></script>
  <style>
    body {
      min-width: 310px;
    	max-width: 800px;
    	height: 400px;
      margin: 0 auto;
    }
    h2 {
      font-family: Arial;
      font-size: 2.5rem;
      text-align: center;
    }
    .switch {
      position: relative;
      display: inline-block;
      width: 150px;
      height: 50px;
    }

    .switch input { 
      opacity: 0;
      width: 0;
      height: 0;
    }
    .switch-label {
      margin-right: 10px;
      font-size: 20px;
      font-family: Arial;
    }
    .slider {
      position: absolute;
      cursor: pointer;
      top: 0;
      left: 0;
      right: 0;
      bottom: 0;
      background-color: #ccc;
      -webkit-transition: .4s;
      transition: .4s;
    }

    .slider:before {
      position: absolute;
      content: "";
      height: 42px;
      width: 42px;
      left: 4px;
      bottom: 4px;
      background-color: white;
      -webkit-transition: .4s;
      transition: .4s;
    }

    input:checked + .slider {
      background-color: green;
    }
    input:focus + .slider {
      box-shadow: 0 0 1px green;
    }
    input:checked + .slider:before {
      -webkit-transform: translateX
    (92px);
      -ms-transform: translateX(92px);
      transform: translateX(92px);
    }
    /* Add styles for on and off labels */
    .on-label {
      position: relative;
      left: 0px;
      top: 12px;
      color: white;
      font-size: 20px;
      font-family: Arial;
    }

    .off-label {
      position: relative;
      left: 50px;
      top: 12px;
      color: white;
      font-size: 20px;
      font-family: Arial;
    }
    /* Add styles for when the switch is off */
    input:not(:checked) + .slider {
      background-color: red;
    }
    input :not(:checked) + .slider:before {
      -webkit-transform: translateX(0);
      -ms-transform: translateX(0);
      transform: translateX(0);
    }
    input:not(:checked) + .slider {
      box-shadow: 0 0 1px red;
    }
    input:not(:checked) + .on-label {
      display: none;
    }
    input:checked + .off-label {
      display: none;
    }
    .center{
    display: flex;
    align-items: center;
    justify-content: center;
}
  </style>
</head>
<body>
  <h2>Smart Parking System</h2>
  <div id="container" class="center">
    <span class="switch-label">Turn the ventilation ON/OFF:</span>
    <label class="switch">
      <input type="checkbox" id="switchButton" onchange="togglePin()">
      <span class="slider"></span>
      <span class="on-label">ON</span>
      <span class="off-label">OFF</span>
    </label>
  </div>
  <div id="chart-humid" class="container"></div>
  <div id="chart-temp" class="container"></div>
</body>
<script>
var chartL1 = new Highcharts.Chart({
  chart:{ renderTo : 'chart-humid' },
  title: { text: 'Humidity' },
  series: [{
    showInLegend: false,
    data: []
  }],
  plotOptions: {
    line: { animation: false,
      dataLabels: { enabled: true }
    },
    series: { color: '#059e8a' }
  },
  xAxis: { type: 'datetime',
    dateTimeLabelFormats: { second: '%H:%M:%S' }
  },
  yAxis: {
    title: { text: 'Humidity (%)' }
  },
  credits: { enabled: false }
});
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var x = (new Date()).getTime(),
          y = parseFloat(this.responseText);
      //console.log(this.responseText);
      if(chartL1.series[0].data.length > 40) {
        chartL1.series[0].addPoint([x, y], true, true, true);
      } else {
        chartL1.series[0].addPoint([x, y], true, false, true);
      }
    }
  };
  xhttp.open("GET", "/humidity", true);
  xhttp.send();
}, 3000 ) ;
 
var chartL2 = new Highcharts.Chart({
  chart:{ renderTo : 'chart-temp' },
  title: { text: 'Temperature' },
  series: [{
    showInLegend: false,
    data: []
  }],
  plotOptions: {
    line: { animation: false,
      dataLabels: { enabled: true }
    },
    series: { color: '#059e8a' }
  },
  xAxis: { type: 'datetime',
    dateTimeLabelFormats: { second: '%H:%M:%S' }
  },
  yAxis: {
    title: { text: 'Temperature (celsius)' }
  },
  credits: { enabled: false }
});
setInterval(function ( ) {
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      var x = (new Date()).getTime(),
          y = parseFloat(this.responseText);
      //console.log(this.responseText);
      if(chartL2.series[0].data.length > 40) {
        chartL2.series[0].addPoint([x, y], true, true, true);
      } else {
        chartL2.series[0].addPoint([x, y], true, false, true);
      }
    }
  };
  xhttp.open("GET", "/temperature", true);
  xhttp.send();
}, 3000 ) ;
function togglePin() {
      var switchButton = document.getElementById("switchButton");
      var xhttp = new XMLHttpRequest();
      if (switchButton.checked) {
        xhttp.open("GET", "/turnOn", true);
      } else {
        xhttp.open("GET", "/turnOff", true);
      }
      xhttp.send();
    }
</script>

</html>

To sum up, a simple yet efficient software architecture was obtained and the whole system's usability was highlighted in the diagram below.

Usability. Demo Video

The way the proposed solution is working was highlighted in the video attached in the link below. There, the full environment and the hardware implementation's placement in the parking lot can be observed. The example displays how the ultrasonic sensors sense the activity which happens in front of them, how the barrier can be moved manually using the buttons or automatically when the ultrasonic sensors detect some movement, the LEDs which indicate the state of the barrier (open/closed) and the web server interface in which the evolution in time of the humidity and temperature can be seen in the graphs and the trigger button of the DC Motor which should be pressed when temperature/humidity exceed a desired treshold.
Link: https://drive.google.com/file/d/1O56uAq17g-68r0betEHjXbiDiJ5rza-4/view?usp=share_link

Future Work & Improvements

This project presents a smart parking system that utilizes an ESP32 microcontroller, two ultrasound sensors, a servomotor, two buttons, a humidity and temperature sensor, two LEDs and a DC motor. The system is capable of detecting the presence of cars at the entrance of the parking lot. However, there is still room for improvement. Future work on this project could include the following:

  • Enhancing the user interface by incorporating a LCD screen to display the temperature, humidity, and the number of cars in the parking lot in real-time. This would provide users with more information about the parking lot conditions and make the system more user-friendly.
  • Integrating a camera and Optical Character Recognition (OCR) algorithm to capture the image of the car's number plate, and using it to keep a record of the cars that entered the parking lot and the time they entered. This would provide more security to the parking lot and make it easier to track cars that have entered the parking lot.
  • Adding a buzzer or LED indicator to alert the user when the parking lot is full. This would help users avoid parking in a lot that is already full.

Overall, the project can be improved by adding more features to make it more user-friendly, efficient and secure.

Conclusions

The implementation of the ESP32 module has proven to be highly effective in providing the necessary intelligence and connectivity capabilities to the parking system, allowing for efficient allocation and management of parking spaces.
The use of a servomotor to simulate a parking barrier, along with the implementation of manual and automatic modes of operation, has resulted in a more secure and user-friendly parking experience for users. The integration of temperature and humidity sensors in the ventilation subsystem has not only ensured a comfortable environment for the users but also helped to reduce energy consumption by only activating the ventilation when needed.
The web-based interface has provided an easy way for the administrator to monitor and manage the system, it could also be further developed to allow users to check the parking space availability in advance or reserve parking spots.
The results of this study have shown that it is possible to develop an intelligent parking system that is not only efficient but also cost-effective by using commercially available modules like the ESP32. This research can be expanded to include more sensors and modules to improve the overall performance and user experience, such as license plate recognition to further automate the parking process.

Resources & References

Pictures sources

Components and their usage

Software tips

iothings/proiecte/2022/smart_parking_system.txt · Last modified: 2023/01/19 22:12 by andrei.popa0805
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0