ESP32 Advantages:
- Built-in WiFi - No external modules needed!
- Web Dashboard - Beautiful, responsive interface
- RESTful API - JSON endpoints for apps
- Real-time Updates - Auto-refresh every 2 seconds
- 15× Faster - 240MHz dual-core vs 16MHz
- 260× More RAM - 520KB vs 2KB
- IoT Ready - Cloud integration built-in
GATT Server:
- Status Characteristic - Real-time availability
- Slots Characteristic - Individual slot status
- Control Characteristic - Remote gate control
Three Control Buttons:
- SCAN - Scan for nearby BLE devices
- BEACON - Toggle iBeacon mode
- GATE - Manual gate control
Mobile Integration:
Connect via nRF Connect App:
- Download app (iOS/Android)
- Scan for "ESP32_Parking"
- Connect to device
- Explore services & characteristics
- Send "OPEN" command
- Gate opens remotely!
Quick Start:
Step 1: Load in Wokwi
- Go to https://wokwi.com
- Create ESP32 project
- Paste diagram.json
- Paste sketch.ino
Step 2: Run & Test
- Click ▶️ Start
- Blue LED lights = BLE active
- Serial Monitor shows BLE started
- LCD: "Free:3/3 ADV"
Step 3: Control
- SCAN button - Find BLE devices
- BEACON button - Toggle mode
- GATE button - Open gate
- Sensors - Simulate parking
Code:
/*
* 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("╚══════════════════════════╝");
}
Why BLE is Perfect for Parking:
Power Efficiency:
| Technology | Battery Life |
|---|---|
| BLE Beacon | 5+ years |
| WiFi | 2-5 days |
| Classic Bluetooth | 2-4 weeks |
| Cellular | 1-3 months |
Mobile Native:
- No app installation required for beacon detection
- iOS/Android support built-in
- Web Bluetooth for browsers
- Background scanning possible
Diagram.json:
{
"version": 1,
"author": "ESP32 BLE Parking",
"editor": "wokwi",
"parts": [
{ "type": "wokwi-esp32-devkit-v1", "id": "esp32", "top": 0, "left": 0, "attrs": {} },
{
"type": "wokwi-lcd1602",
"id": "lcd1",
"top": -150,
"left": 115.2,
"attrs": { "pins": "i2c" }
},
{
"type": "wokwi-hc-sr04",
"id": "ultrasonic1",
"top": -267.3,
"left": -176.9,
"attrs": { "distance": "400" }
},
{
"type": "wokwi-hc-sr04",
"id": "ultrasonic2",
"top": -296.1,
"left": 24.7,
"attrs": { "distance": "400" }
},
{
"type": "wokwi-hc-sr04",
"id": "ultrasonic3",
"top": -257.7,
"left": 197.5,
"attrs": { "distance": "400" }
},
{ "type": "wokwi-servo", "id": "servo1", "top": 140, "left": -144, "attrs": {} },
{
"type": "wokwi-led",
"id": "led1",
"top": 280,
"left": -140,
"attrs": { "color": "green", "label": "Slot 1" }
},
{
"type": "wokwi-led",
"id": "led2",
"top": 280,
"left": -50,
"attrs": { "color": "green", "label": "Slot 2" }
},
{
"type": "wokwi-led",
"id": "led3",
"top": 280,
"left": 40,
"attrs": { "color": "green", "label": "Slot 3" }
},
{
"type": "wokwi-led",
"id": "led4",
"top": 280,
"left": 130,
"attrs": { "color": "red", "label": "Full" }
},
{
"type": "wokwi-led",
"id": "led5",
"top": 280,
"left": 220,
"attrs": { "color": "blue", "label": "BLE" }
},
{
"type": "wokwi-resistor",
"id": "r1",
"top": 318,
"left": -144.8,
"rotate": 90,
"attrs": { "value": "220" }
},
{
"type": "wokwi-resistor",
"id": "r2",
"top": 318,
"left": -54.8,
"rotate": 90,
"attrs": { "value": "220" }
},
{
"type": "wokwi-resistor",
"id": "r3",
"top": 318,
"left": 35.2,
"rotate": 90,
"attrs": { "value": "220" }
},
{
"type": "wokwi-resistor",
"id": "r4",
"top": 318,
"left": 125.2,
"rotate": 90,
"attrs": { "value": "220" }
},
{
"type": "wokwi-resistor",
"id": "r5",
"top": 318,
"left": 215.2,
"rotate": 90,
"attrs": { "value": "220" }
},
{ "type": "wokwi-buzzer", "id": "bz1", "top": 140, "left": 336, "attrs": {} },
{
"type": "wokwi-pushbutton",
"id": "btn1",
"top": 390,
"left": -115.2,
"attrs": { "color": "green", "label": "SCAN" }
},
{
"type": "wokwi-pushbutton",
"id": "btn2",
"top": 390,
"left": 0,
"attrs": { "color": "blue", "label": "BEACON" }
},
{
"type": "wokwi-pushbutton",
"id": "btn3",
"top": 390,
"left": 115.2,
"attrs": { "color": "red", "label": "GATE" }
}
],
"connections": [
[ "esp32:TX0", "$serialMonitor:RX", "", [] ],
[ "esp32:RX0", "$serialMonitor:TX", "", [] ],
[ "lcd1:GND", "esp32:GND.1", "black", [ "h0" ] ],
[ "lcd1:VCC", "esp32:3V3", "red", [ "h0" ] ],
[ "lcd1:SDA", "esp32:D21", "blue", [ "h0" ] ],
[ "lcd1:SCL", "esp32:D22", "yellow", [ "h0" ] ],
[ "ultrasonic1:VCC", "esp32:VIN", "red", [ "h0" ] ],
[ "ultrasonic1:GND", "esp32:GND.1", "black", [ "h0" ] ],
[ "ultrasonic1:TRIG", "esp32:D13", "orange", [ "h0" ] ],
[ "ultrasonic1:ECHO", "esp32:D12", "purple", [ "h0" ] ],
[ "ultrasonic2:VCC", "esp32:VIN", "red", [ "h0" ] ],
[ "ultrasonic2:GND", "esp32:GND.1", "black", [ "h0" ] ],
[ "ultrasonic2:TRIG", "esp32:D14", "orange", [ "h0" ] ],
[ "ultrasonic2:ECHO", "esp32:D27", "purple", [ "h0" ] ],
[ "ultrasonic3:VCC", "esp32:VIN", "red", [ "h0" ] ],
[ "ultrasonic3:GND", "esp32:GND.1", "black", [ "h0" ] ],
[ "ultrasonic3:TRIG", "esp32:D26", "orange", [ "h0" ] ],
[ "ultrasonic3:ECHO", "esp32:D25", "purple", [ "h0" ] ],
[ "servo1:V+", "esp32:VIN", "red", [ "h0" ] ],
[ "servo1:GND", "esp32:GND.2", "black", [ "h0" ] ],
[ "servo1:SIG", "esp32:D15", "orange", [ "h0" ] ],
[ "led1:A", "r1:1", "green", [ "v0" ] ],
[ "r1:2", "esp32:D16", "green", [ "v0" ] ],
[ "led1:C", "esp32:GND.1", "black", [ "v0" ] ],
[ "led2:A", "r2:1", "green", [ "v0" ] ],
[ "r2:2", "esp32:D17", "green", [ "v0" ] ],
[ "led2:C", "esp32:GND.1", "black", [ "v0" ] ],
[ "led3:A", "r3:1", "green", [ "v0" ] ],
[ "r3:2", "esp32:D18", "green", [ "v0" ] ],
[ "led3:C", "esp32:GND.1", "black", [ "v0" ] ],
[ "led4:A", "r4:1", "red", [ "v0" ] ],
[ "r4:2", "esp32:D19", "red", [ "v0" ] ],
[ "led4:C", "esp32:GND.1", "black", [ "v0" ] ],
[ "led5:A", "r5:1", "blue", [ "v0" ] ],
[ "r5:2", "esp32:D2", "blue", [ "v0" ] ],
[ "led5:C", "esp32:GND.2", "black", [ "v0" ] ],
[ "bz1:1", "esp32:D23", "orange", [ "h0" ] ],
[ "bz1:2", "esp32:GND.2", "black", [ "h0" ] ],
[ "btn1:1.l", "esp32:D32", "green", [ "h0" ] ],
[ "btn1:2.l", "esp32:GND.1", "black", [ "h0" ] ],
[ "btn2:1.l", "esp32:D33", "blue", [ "h0" ] ],
[ "btn2:2.l", "esp32:GND.1", "black", [ "h0" ] ],
[ "btn3:1.l", "esp32:D34", "red", [ "h0" ] ],
[ "btn3:2.l", "esp32:GND.1", "black", [ "h0" ] ]
],
"dependencies": {}
}
Use Cases:
- Shopping Malls - Multi-floor parking with beacon network
- Airports - Long-term parking with payment via BLE
- Smart Cities - Street parking availability
- Residential - Secure access via phone
- Offices - Employee parking management
- Events - Temporary parking with mobile tickets
Comments
Post a Comment