Back to IoT Blog
Smart Cities 20 min read

Smart Parking Lot with Ultrasonic Sensors

Build an IoT-enabled parking management system. Detect parking space occupancy using ultrasonic sensors, guide drivers to available spots, and provide real-time data via mobile app and Azure IoT Hub.

System Overview

Smart parking systems reduce traffic congestion and driver frustration by guiding vehicles directly to available spots. Our system uses ultrasonic sensors to detect occupancy and provides real-time data to drivers via a mobile app.

Key Benefits:
  • Reduce parking search time by 40-60%
  • Lower carbon emissions from circling vehicles
  • Improve customer experience
  • Enable dynamic pricing based on demand
  • Collect parking usage analytics
  • Support reservation systems

System Architecture

[Ultrasonic Sensors] → [ESP32 Nodes] → [WiFi] → [Azure IoT Hub]
                                                    ↓
                                          [Azure Functions]
                                                    ↓
                                          [Cosmos DB]
                                                    ↓
                                          [Mobile App]

Hardware Components

Per Parking Space Node

Gateway/Concentrator

HC-SR04 Specifications

Parameter Value
Operating Voltage 5V DC
Range 2cm - 400cm
Accuracy Âą3mm
Beam Angle 15 degrees
Current Draw 15mA (active), <50ΞA (sleep)

Sensor Installation

Mounting Guidelines

Distance Calibration

// Typical distances for parking detection
Empty space distance: 250cm (sensor height)
Occupied space distance: 100-150cm (car height)
Threshold: 180cm (midpoint)

if (distance < threshold) {
  status = OCCUPIED;
} else {
  status = AVAILABLE;
}

Wiring Diagram

ESP32          HC-SR04
│
├── 5V    →    VCC
├── GND   →    GND
├── GPIO5 →    Trig
└── GPIO18 →   Echo

ESP32          LED (via transistor)
│
├── GPIO23 →   Base (via 1k resistor)
├── LED +   →   Collector
└── LED -   →   GND
Outdoor Installation Tips:
  • Use UV-resistant enclosures
  • Add conformal coating to PCBs
  • Include desiccant packs to prevent condensation
  • Mount sensors under protective covers
  • Use stainless steel hardware

ESP32 Firmware

Complete firmware for parking space monitoring:

#include <WiFi.h>
#include <AzureIoTHub.h>
#include <ArduinoJson.h>

// WiFi credentials
const char* ssid = "PARKING_WIFI";
const char* password = "your_password";

// Azure IoT Hub connection string
static const char* connectionString = "HostName=your-hub.azure-devices.net;DeviceId=parking-space-001;SharedAccessKey=your-key";

// Pins
#define TRIG_PIN 5
#define ECHO_PIN 18
#define LED_GREEN 23
#define LED_RED 22

// Parking space configuration
const int SPACE_ID = 1;
const int DISTANCE_THRESHOLD = 180; // cm

void setup() {
  Serial.begin(115200);
  
  // Initialize pins
  pinMode(TRIG_PIN, OUTPUT);
  pinMode(ECHO_PIN, INPUT);
  pinMode(LED_GREEN, OUTPUT);
  pinMode(LED_RED, OUTPUT);
  
  // Connect to WiFi
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }
  Serial.println("WiFi connected");
  
  // Initialize Azure IoT
  IoTHubClient_Init(connectionString);
}

void loop() {
  // Measure distance
  int distance = measureDistance();
  
  // Determine occupancy
  bool occupied = distance < DISTANCE_THRESHOLD;
  
  // Update LED indicators
  if (occupied) {
    digitalWrite(LED_RED, HIGH);
    digitalWrite(LED_GREEN, LOW);
  } else {
    digitalWrite(LED_RED, LOW);
    digitalWrite(LED_GREEN, HIGH);
  }
  
  // Send to Azure IoT Hub
  sendTelemetry(distance, occupied);
  
  // Sleep for 30 seconds
  esp_sleep_enable_timer_wakeup(30000000);
  esp_deep_sleep_start();
}

int measureDistance() {
  // Send 10Ξs pulse
  digitalWrite(TRIG_PIN, LOW);
  delayMicroseconds(2);
  digitalWrite(TRIG_PIN, HIGH);
  delayMicroseconds(10);
  digitalWrite(TRIG_PIN, LOW);
  
  // Read echo pulse duration
  long duration = pulseIn(ECHO_PIN, HIGH, 30000); // 30ms timeout
  
  // Convert to distance (cm)
  int distance = duration * 0.034 / 2;
  
  // Filter invalid readings
  if (distance == 0 || distance > 400) {
    return 999; // Invalid reading
  }
  
  return distance;
}

void sendTelemetry(int distance, bool occupied) {
  StaticJsonDocument<256> doc;
  doc["space_id"] = SPACE_ID;
  doc["distance"] = distance;
  doc["occupied"] = occupied;
  doc["timestamp"] = millis();
  doc["battery"] = readBattery();
  
  char jsonBuffer[256];
  serializeJson(doc, jsonBuffer);
  
  IoTHubClient_SendEvent(jsonBuffer);
  
  Serial.printf("Space %d: %s (%d cm)\n", 
                SPACE_ID, 
                occupied ? "OCCUPIED" : "AVAILABLE",
                distance);
}

int readBattery() {
  int adc = analogRead(35);
  float voltage = adc * (3.3 / 4095.0) * 2;
  return (int)(voltage * 100);
}

Azure IoT Integration

Set up cloud infrastructure for parking data:

Azure Resources

Azure Function for Processing

using Microsoft.Azure.Devices.Shared;
using Microsoft.Azure.WebJobs;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;

public class ParkingEvent
{
    public int space_id { get; set; }
    public int distance { get; set; }
    public bool occupied { get; set; }
    public long timestamp { get; set; }
    public int battery { get; set; }
}

[FunctionName("ProcessParkingEvent")]
public static async Task Run(
    [EventHubTrigger("parking-events", Connection = "EventHubConnection")] 
    string eventHubMessage,
    [CosmosDB(
        databaseName: "ParkingDB",
        collectionName: "ParkingEvents",
        ConnectionStringSetting = "CosmosDBConnection",
        CreateIfNotExists = true)] 
        IAsyncCollector<ParkingEvent> outputEvents,
    ILogger log)
{
    var parkingEvent = JsonConvert.DeserializeObject<ParkingEvent>(eventHubMessage);
    
    // Store in Cosmos DB
    await outputEvents.AddAsync(parkingEvent);
    
    // Update current state
    await UpdateParkingState(parkingEvent);
    
    // Send real-time update via SignalR
    await SendSignalRUpdate(parkingEvent);
    
    log.LogInformation($"Processed event for space {parkingEvent.space_id}");
}

private static async Task UpdateParkingState(ParkingEvent evt)
{
    // Update latest state document
    var state = new {
        space_id = evt.space_id,
        occupied = evt.occupied,
        last_updated = DateTime.UtcNow
    };
    // Upsert to Cosmos DB
}

IoT Hub Message Routing

// Route parking events to Event Hub
SELECT *
FROM IoTHubMessages
WHERE $level = 'parking'

// Route alerts (low battery) to Service Bus
SELECT *
FROM IoTHubMessages
WHERE battery < 350

Mobile App Development

React Native app for drivers:

Parking Map Component

import React, { useEffect, useState } from 'react';
import { View, Text, StyleSheet } from 'react-native';
import MapView, { Marker } from 'react-native-maps';
import * as signalR from '@microsoft/signalr';

const ParkingMap = () => {
  const [parkingSpaces, setParkingSpaces] = useState([]);
  
  useEffect(() => {
    // Connect to SignalR for real-time updates
    const connection = new signalR.HubConnectionBuilder()
      .withUrl('https://your-api.azurewebsites.net/parkingHub')
      .build();
    
    connection.on('ParkingUpdate', (data) => {
      setParkingSpaces(prev => 
        prev.map(space => 
          space.id === data.space_id 
            ? { ...space, occupied: data.occupied }
            : space
        )
      );
    });
    
    connection.start();
    
    // Fetch initial state
    fetchParkingData();
  }, []);
  
  const fetchParkingData = async () => {
    const response = await fetch('https://your-api.azurewebsites.net/api/parking');
    const data = await response.json();
    setParkingSpaces(data);
  };
  
  return (
    <MapView
      style={styles.map}
      initialRegion={{
        latitude: 37.78825,
        longitude: -122.4324,
        latitudeDelta: 0.01,
        longitudeDelta: 0.01,
      }}
    >
      {parkingSpaces.map(space => (
        <Marker
          key={space.id}
          coordinate={{
            latitude: space.latitude,
            longitude: space.longitude,
          }}
          pinColor={space.occupied ? 'red' : 'green'}
        />
      ))}
    </MapView>
  );
};

Deployment Tips

Power Management

Network Design

Entry Display Board

[Raspberry Pi] → [LED Matrix Display]
       ↓
  [Azure IoT Hub]
       ↓
  Query: SELECT COUNT(*) FROM Spaces WHERE occupied = false
       ↓
  Display: "AVAILABLE: 47 SPACES"
Advanced Features:
  • License plate recognition for reserved spots
  • Dynamic pricing based on occupancy
  • Reservation system with time limits
  • EV charging station integration
  • Handicap spot monitoring
  • Predictive availability (ML forecasting)

Next Steps

Expand your smart parking system:

  • Add camera-based verification
  • Implement payment integration
  • Connect to city-wide parking platform
  • Add EV charging station monitoring
  • Implement predictive analytics for demand