/*
* ESP32 BLE BEACON SMART PARKING SYSTEM
*
* Features:
* - BLE Beacon Broadcasting (iBeacon protocol)
* - BLE Scanner for detecting nearby devices
* - GATT Server for data exchange
* - 3 Parking Slots with Ultrasonic Sensors
* - Real-time BLE advertisement updates
* - Proximity-based access control
*/
#include <BLEDevice.h>
#include <BLEServer.h>
#include <BLEUtils.h>
#include <BLE2902.h>
#include <BLEBeacon.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <ESP32Servo.h>
// BLE UUIDs
#define SERVICE_UUID "4fafc201-1fb5-459e-8fcc-c5c9c331914b"
#define CHAR_STATUS_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a8"
#define CHAR_SLOTS_UUID "beb5483e-36e1-4688-b7f5-ea07361b26a9"
#define CHAR_CONTROL_UUID "beb5483e-36e1-4688-b7f5-ea07361b26aa"
#define BEACON_UUID "e2c56db5-dffb-48d2-b060-d0f5a71096e0"
// Pin Definitions
#define TRIG1 13
#define ECHO1 12
#define TRIG2 14
#define ECHO2 27
#define TRIG3 26
#define ECHO3 25
#define LED_SLOT1 16
#define LED_SLOT2 17
#define LED_SLOT3 18
#define LED_FULL 19
#define LED_BLE 2
#define SERVO_PIN 15
#define BUZZER_PIN 23
#define BTN_SCAN 32
#define BTN_BEACON 33
#define BTN_GATE 34
LiquidCrystal_I2C lcd(0x27, 16, 2);
Servo barrierGate;
BLEServer *pServer = NULL;
BLECharacteristic *pCharStatus = NULL;
BLECharacteristic *pCharSlots = NULL;
BLECharacteristic *pCharControl = NULL;
BLEAdvertising *pAdvertising = NULL;
bool slot1Occupied = false;
bool slot2Occupied = false;
bool slot3Occupied = false;
int availableSlots = 3;
const int totalSlots = 3;
const int THRESHOLD_DISTANCE = 15;
bool gateOpen = false;
unsigned long gateOpenTime = 0;
const unsigned long GATE_AUTO_CLOSE = 3000;
const int GATE_CLOSED = 0;
const int GATE_OPEN = 90;
bool bleConnected = false;
bool bleBeaconMode = true;
int rssiThreshold = -70;
unsigned long totalEntries = 0;
unsigned long bleConnections = 0;
// Forward declarations
void handleBLEEntry();
void closeGate();
void openGate();
void updateBLEStatus();
class ServerCallbacks: public BLEServerCallbacks {
void onConnect(BLEServer* pServer) {
bleConnected = true;
bleConnections++;
digitalWrite(LED_BLE, HIGH);
Serial.println("\n╔═══════════════════════════════════╗");
Serial.println("║ 🔵 BLE CLIENT CONNECTED! ║");
Serial.println("╚═══════════════════════════════════╝");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("BLE CONNECTED!");
digitalWrite(BUZZER_PIN, HIGH);
delay(100);
digitalWrite(BUZZER_PIN, LOW);
delay(1000);
}
void onDisconnect(BLEServer* pServer) {
bleConnected = false;
digitalWrite(LED_BLE, LOW);
Serial.println("║ 🔵 BLE CLIENT DISCONNECTED ║");
delay(500);
pAdvertising->start();
}
};
class ControlCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
String value = pCharacteristic->getValue().c_str();
if (value.length() > 0) {
Serial.print("\n📱 BLE Command: ");
if (value == "OPEN") {
Serial.println("OPEN GATE");
handleBLEEntry();
} else if (value == "CLOSE") {
Serial.println("CLOSE");
closeGate();
} else if (value == "BEEP") {
Serial.println("BEEP");
digitalWrite(BUZZER_PIN, HIGH);
delay(200);
digitalWrite(BUZZER_PIN, LOW);
} else if (value == "STATUS") {
Serial.println("STATUS");
updateBLEStatus();
}
}
}
};
class ScanCallbacks: public BLEAdvertisedDeviceCallbacks {
void onResult(BLEAdvertisedDevice advertisedDevice) {
Serial.print("\nDevice: ");
Serial.print(advertisedDevice.getName().c_str());
Serial.print(" RSSI: ");
Serial.println(advertisedDevice.getRSSI());
if (advertisedDevice.getRSSI() > rssiThreshold) {
Serial.println("✓ In proximity!");
digitalWrite(BUZZER_PIN, HIGH);
delay(100);
digitalWrite(BUZZER_PIN, LOW);
}
}
};
void setup() {
Serial.begin(115200);
Serial.println("\n╔═════════════════════════════════════════╗");
Serial.println("║ ESP32 BLE BEACON PARKING SYSTEM ║");
Serial.println("╚═════════════════════════════════════════╝\n");
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("BLE PARKING");
lcd.setCursor(0, 1);
lcd.print("Starting...");
pinMode(TRIG1, OUTPUT); pinMode(ECHO1, INPUT);
pinMode(TRIG2, OUTPUT); pinMode(ECHO2, INPUT);
pinMode(TRIG3, OUTPUT); pinMode(ECHO3, INPUT);
pinMode(LED_SLOT1, OUTPUT);
pinMode(LED_SLOT2, OUTPUT);
pinMode(LED_SLOT3, OUTPUT);
pinMode(LED_FULL, OUTPUT);
pinMode(LED_BLE, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
pinMode(BTN_SCAN, INPUT_PULLUP);
pinMode(BTN_BEACON, INPUT_PULLUP);
pinMode(BTN_GATE, INPUT_PULLUP);
barrierGate.attach(SERVO_PIN);
barrierGate.write(GATE_CLOSED);
testLEDs();
initializeBLE();
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("SYSTEM READY!");
lcd.setCursor(0, 1);
lcd.print("BLE Active");
delay(2000);
Serial.println("✓ System Ready!");
printHelp();
}
void initializeBLE() {
BLEDevice::init("ESP32_Parking");
pServer = BLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks());
BLEService *pService = pServer->createService(SERVICE_UUID);
pCharStatus = pService->createCharacteristic(
CHAR_STATUS_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY);
pCharStatus->addDescriptor(new BLE2902());
pCharSlots = pService->createCharacteristic(
CHAR_SLOTS_UUID,
BLECharacteristic::PROPERTY_READ |
BLECharacteristic::PROPERTY_NOTIFY);
pCharSlots->addDescriptor(new BLE2902());
pCharControl = pService->createCharacteristic(
CHAR_CONTROL_UUID,
BLECharacteristic::PROPERTY_WRITE);
pCharControl->setCallbacks(new ControlCallbacks());
pService->start();
pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
pAdvertising->setScanResponse(true);
setBeacon();
BLEDevice::startAdvertising();
Serial.println("✓ BLE Started: ESP32_Parking");
Serial.println(" Service: " + String(SERVICE_UUID));
digitalWrite(LED_BLE, HIGH);
delay(500);
digitalWrite(LED_BLE, LOW);
}
void setBeacon() {
BLEBeacon beacon = BLEBeacon();
beacon.setManufacturerId(0x4C00);
BLEUUID bleUUID = BLEUUID(BEACON_UUID);
beacon.setProximityUUID(bleUUID);
beacon.setMajor((availableSlots << 8) | totalSlots);
beacon.setMinor(0x0001);
beacon.setSignalPower(-59);
BLEAdvertisementData advertisementData;
advertisementData.setFlags(0x04);
std::string strServiceData = "";
strServiceData += (char)26;
strServiceData += (char)0xFF;
std::string beaconData = beacon.getData().c_str();
strServiceData += beaconData;
advertisementData.addData(strServiceData.c_str());
pAdvertising->setAdvertisementData(advertisementData);
}
void loop() {
checkParkingSlots();
updateLCD();
updateLEDs();
if (bleConnected) {
updateBLEStatus();
static unsigned long lastBlink = 0;
if (millis() - lastBlink > 500) {
digitalWrite(LED_BLE, !digitalRead(LED_BLE));
lastBlink = millis();
}
}
if (digitalRead(BTN_SCAN) == LOW) {
delay(50);
if (digitalRead(BTN_SCAN) == LOW) {
startBLEScan();
while(digitalRead(BTN_SCAN) == LOW);
}
}
if (digitalRead(BTN_BEACON) == LOW) {
delay(50);
if (digitalRead(BTN_BEACON) == LOW) {
toggleBeaconMode();
while(digitalRead(BTN_BEACON) == LOW);
}
}
if (digitalRead(BTN_GATE) == LOW) {
delay(50);
if (digitalRead(BTN_GATE) == LOW) {
handleEntry();
while(digitalRead(BTN_GATE) == LOW);
}
}
if (gateOpen && (millis() - gateOpenTime > GATE_AUTO_CLOSE)) {
closeGate();
}
static unsigned long lastBeaconUpdate = 0;
if (bleBeaconMode && millis() - lastBeaconUpdate > 5000) {
setBeacon();
lastBeaconUpdate = millis();
}
delay(200);
}
void startBLEScan() {
Serial.println("\n🔍 BLE SCAN STARTED");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("BLE SCANNING...");
BLEScan* pBLEScan = BLEDevice::getScan();
pBLEScan->setAdvertisedDeviceCallbacks(new ScanCallbacks());
pBLEScan->setActiveScan(true);
pBLEScan->setInterval(100);
pBLEScan->setWindow(99);
BLEScanResults* foundDevices = pBLEScan->start(5, false);
Serial.print("Found ");
Serial.print(foundDevices->getCount());
Serial.println(" devices");
lcd.clear();
lcd.print("Found:");
lcd.print(foundDevices->getCount());
delay(2000);
pBLEScan->clearResults();
}
void toggleBeaconMode() {
bleBeaconMode = !bleBeaconMode;
Serial.print("\nBLE Mode: ");
Serial.println(bleBeaconMode ? "BEACON" : "DISCOVERABLE");
lcd.clear();
lcd.print(bleBeaconMode ? "BEACON" : "DISCOVERABLE");
digitalWrite(BUZZER_PIN, HIGH);
delay(100);
digitalWrite(BUZZER_PIN, LOW);
if (bleBeaconMode) setBeacon();
delay(2000);
}
void updateBLEStatus() {
if (!bleConnected) return;
String status = "Available:" + String(availableSlots) + "/" + String(totalSlots);
status += ",Gate:" + String(gateOpen ? "OPEN" : "CLOSED");
pCharStatus->setValue(status.c_str());
pCharStatus->notify();
String slots = "S1:" + String(slot1Occupied ? "1" : "0");
slots += ",S2:" + String(slot2Occupied ? "1" : "0");
slots += ",S3:" + String(slot3Occupied ? "1" : "0");
pCharSlots->setValue(slots.c_str());
pCharSlots->notify();
}
long getDistance(int trigPin, int echoPin) {
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH, 30000);
return duration * 0.034 / 2;
}
void checkParkingSlots() {
bool prev1 = slot1Occupied, prev2 = slot2Occupied, prev3 = slot3Occupied;
slot1Occupied = (getDistance(TRIG1, ECHO1) < THRESHOLD_DISTANCE);
slot2Occupied = (getDistance(TRIG2, ECHO2) < THRESHOLD_DISTANCE);
slot3Occupied = (getDistance(TRIG3, ECHO3) < THRESHOLD_DISTANCE);
if (prev1 != slot1Occupied) Serial.println("Slot 1: " + String(slot1Occupied ? "OCCUPIED" : "FREE"));
if (prev2 != slot2Occupied) Serial.println("Slot 2: " + String(slot2Occupied ? "OCCUPIED" : "FREE"));
if (prev3 != slot3Occupied) Serial.println("Slot 3: " + String(slot3Occupied ? "OCCUPIED" : "FREE"));
availableSlots = !slot1Occupied + !slot2Occupied + !slot3Occupied;
if (availableSlots == 0) alertParkingFull();
}
void updateLCD() {
lcd.clear();
lcd.print("Free:");
lcd.print(availableSlots);
lcd.print("/");
lcd.print(totalSlots);
lcd.print(bleConnected ? " BLE" : " ADV");
lcd.setCursor(0, 1);
lcd.print("S1:");
lcd.print(slot1Occupied ? "X" : "O");
lcd.print(" S2:");
lcd.print(slot2Occupied ? "X" : "O");
lcd.print(" S3:");
lcd.print(slot3Occupied ? "X" : "O");
}
void updateLEDs() {
digitalWrite(LED_SLOT1, !slot1Occupied);
digitalWrite(LED_SLOT2, !slot2Occupied);
digitalWrite(LED_SLOT3, !slot3Occupied);
digitalWrite(LED_FULL, availableSlots == 0);
}
void handleEntry() {
totalEntries++;
if (availableSlots > 0) {
openGate();
lcd.clear();
lcd.print("WELCOME!");
digitalWrite(BUZZER_PIN, HIGH);
delay(200);
digitalWrite(BUZZER_PIN, LOW);
delay(1500);
} else {
lcd.clear();
lcd.print("PARKING FULL");
for (int i = 0; i < 3; i++) {
digitalWrite(BUZZER_PIN, HIGH);
delay(200);
digitalWrite(BUZZER_PIN, LOW);
delay(200);
}
}
}
void handleBLEEntry() {
if (availableSlots > 0) {
openGate();
lcd.clear();
lcd.print("BLE ENTRY");
digitalWrite(BUZZER_PIN, HIGH);
delay(100);
digitalWrite(BUZZER_PIN, LOW);
}
}
void openGate() {
if (!gateOpen) {
barrierGate.write(GATE_OPEN);
gateOpen = true;
gateOpenTime = millis();
}
}
void closeGate() {
if (gateOpen) {
barrierGate.write(GATE_CLOSED);
gateOpen = false;
}
}
void alertParkingFull() {
static unsigned long lastAlert = 0;
if (millis() - lastAlert > 5000) {
for (int i = 0; i < 2; i++) {
digitalWrite(LED_FULL, HIGH);
delay(200);
digitalWrite(LED_FULL, LOW);
delay(200);
}
digitalWrite(LED_FULL, HIGH);
lastAlert = millis();
}
}
void testLEDs() {
digitalWrite(LED_SLOT1, HIGH);
digitalWrite(LED_SLOT2, HIGH);
digitalWrite(LED_SLOT3, HIGH);
digitalWrite(LED_FULL, HIGH);
digitalWrite(LED_BLE, HIGH);
delay(500);
digitalWrite(LED_SLOT1, LOW);
digitalWrite(LED_SLOT2, LOW);
digitalWrite(LED_SLOT3, LOW);
digitalWrite(LED_FULL, LOW);
digitalWrite(LED_BLE, LOW);
}
void printHelp() {
Serial.println("\n╔══════════════════════════╗");
Serial.println("║ BUTTON CONTROLS ║");
Serial.println("║ 🟢 SCAN - BLE scan ║");
Serial.println("║ 🔵 BEACON - Toggle mode ║");
Serial.println("║ 🔴 GATE - Open gate ║");
Serial.println("╚══════════════════════════╝");
}