// Install Library
// https://www.arduino.cc/reference/en/libraries/liquidcrystal-i2c/
// https://docs.wokwi.com/parts/wokwi-lcd1602#attributes
// https://docs.arduino.cc/learn/communication/wire/#arduino-i2c-pins
// Arduino I2C Pins for Arduino UNO: SDA/A4 SCL/A5
// Improvement: Create a toggle button to switch type of
// gate to exit instead of entry rather than using extra sensors
#include <LiquidCrystal_I2C.h>
#include <Servo.h>
#define ULTRASONIC_SENSOR_PIN_TRIG 2
#define ULTRASONIC_SENSOR_PIN_ECHO 3
#define PIN_SERVO 4
#define BUTTON_PIN 5
// Set the LCD address to 0x27 for a 16 chars and 2 line display
LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo servo;
unsigned long gateClosedStartTime = 0;
unsigned long gateClosedTime = 0;
int slotsAvailable = 1000;
int nextParkingSpace = 1;
bool isDoorOpen = false;
bool isExitGate = false;
void setup() {
Serial.begin(9600);
pinMode(ULTRASONIC_SENSOR_PIN_TRIG, OUTPUT);
pinMode(ULTRASONIC_SENSOR_PIN_ECHO, INPUT);
servo.attach(PIN_SERVO);
changeGateAngle(0);
pinMode(BUTTON_PIN, INPUT_PULLUP);
pinMode(LED_BUILTIN, OUTPUT);
lcd.init(); // initialize the lcd
lcd.backlight();
printAvailableSlotsOnLCD(slotsAvailable, isExitGate);
}
double readDistanceInCM(int triggerPin, int echoPin)
{
digitalWrite(triggerPin, HIGH);
delayMicroseconds(10);
digitalWrite(triggerPin, LOW);
return pulseIn(echoPin, HIGH) * (1.0f/58);
}
String firstLineCache = "";
String secondLineCache = "";
void lazyPrint(String firstLine, String secondLine) {
if(firstLineCache != firstLine || secondLineCache != secondLine)
{
lcd.clear();
}
if(firstLineCache != firstLine) {
lcd.setCursor(0, 0);
lcd.print(firstLine);
firstLineCache = firstLine;
}
if(secondLineCache != secondLine) {
lcd.setCursor(0, 1);
lcd.print(secondLine);
secondLineCache = secondLine;
}
}
void printWelcomeOnLCD(int parkingSpace) {
lazyPrint("--> Welcome! <--", String("Go to space: " + String(parkingSpace)));
}
void printEntryGateOnLCD() {
lazyPrint(">> Entry Gate <<", "");
delay(1000);
}
void printExitGateOnLCD() {
lazyPrint(">> Exit Gate <<", "");
delay(1000);
}
void printAvailableSlotsOnLCD(int avaiableSlots, bool isGateTypeExit) {
lazyPrint("Space Left", String(String(avaiableSlots) + " " + (isGateTypeExit ? "Exit" : "Entry")));
}
void changeGateAngle(int angle) {
servo.write(angle);
delay(15);
}
void openGate() {
changeGateAngle(90);
calculateGateClosedTime();
}
void closeGate() {
changeGateAngle(0);
gateClosedStartTime = millis();
}
void printGateClosedTimeOnLCD(unsigned long timeInSeconds) {
lazyPrint("Gate Closed For", String(String(timeInSeconds) + " seconds"));
delay(300);
}
void calculateGateClosedTime() {
if (gateClosedStartTime != 0) {
gateClosedTime = (millis() - gateClosedStartTime) / 1000;
Serial.print("Gate was closed for ");
Serial.print(gateClosedTime);
Serial.println(" seconds.");
printGateClosedTimeOnLCD(gateClosedTime);
gateClosedStartTime = 0; // Reset the start time
}
}
void loop() {
if (digitalRead(BUTTON_PIN) == LOW) {
isExitGate = !isExitGate;
if(isExitGate)
{
Serial.println("Gate Type: Exit");
digitalWrite(LED_BUILTIN, HIGH);
printExitGateOnLCD();
printAvailableSlotsOnLCD(slotsAvailable, isExitGate);
}
else {
Serial.println("Gate Type: Entry");
digitalWrite(LED_BUILTIN, LOW);
printEntryGateOnLCD();
printAvailableSlotsOnLCD(slotsAvailable, isExitGate);
}
}
// in centi meter
float distance = readDistanceInCM(ULTRASONIC_SENSOR_PIN_TRIG, ULTRASONIC_SENSOR_PIN_ECHO);
float threshold = 20;
bool isCarDetected = distance < threshold;
bool isEmptySlotAvailable = slotsAvailable > 0;
bool isEntryGate = !isExitGate;
if((isCarDetected && ( (isEntryGate && isEmptySlotAvailable) || isExitGate) ))
{
Serial.println("Open Gate");
openGate();
if(isEntryGate)
{
printWelcomeOnLCD(nextParkingSpace);
}
isDoorOpen = true;
}
else if(isDoorOpen){
Serial.println("Close Gate");
delay(3000); // delay so the gate does not close immediately after car enters.
closeGate();
if(isEntryGate)
{
nextParkingSpace++; // Increment to the next parking space
slotsAvailable -= 1;
}
else {
nextParkingSpace--;
slotsAvailable += 1;
}
isDoorOpen = false;
Serial.println("Slots: " + slotsAvailable);
printAvailableSlotsOnLCD(slotsAvailable, isExitGate);
}
Serial.print("Distance in CM: ");
Serial.println(distance);
}