#include <Wire.h>
#include <LiquidCrystal_I2C.h>
// Initialize the LCD with I2C address 0x27, 16 columns, and 2 rows
LiquidCrystal_I2C lcd(0x27, 16, 2);
// Pin Definitions
const int IR_SENSOR_1 = 2; // Entry Sensor
const int IR_SENSOR_2 = 3; // Exit Sensor
const int BUZZER = 13;
// Physics Configuration
const float DISTANCE_BETWEEN_SENSORS = 0.10; // Distance in meters (e.g., 10cm = 0.10m)
const unsigned long TIMEOUT_MS = 5000; // Reset if car stops halfway (5 seconds)
const float SPEED_LIMIT_KMH = 40.0; // Threshold to trigger buzzer warning
// State Variables
unsigned long startTime = 0;
unsigned long endTime = 0;
bool carDetected = false;
bool timingActive = false;
void setup() {
// Initialize serial communication for debugging
Serial.begin(9600);
// Configure Pin Modes
pinMode(IR_SENSOR_1, INPUT);
pinMode(IR_SENSOR_2, INPUT);
pinMode(BUZZER, OUTPUT);
// Ensure buzzer is off at startup
digitalWrite(BUZZER, LOW);
// Initialize and turn on the LCD backlight
lcd.init();
lcd.backlight();
// Display Startup Splash Screen
lcd.setCursor(0, 0);
lcd.print(" SPEED DETECTOR ");
lcd.setCursor(0, 1);
lcd.print(" INITIALIZING ");
delay(2000);
showDefaultMessage();
}
void loop() {
// Read sensor inputs (Active LOW or HIGH depending on your module. Usually LOW when obstructed)
// Adjust to LOW if your specific IR modules pull the pin LOW upon vehicle detection.
bool sensor1_triggered = (digitalRead(IR_SENSOR_1) == HIGH);
bool sensor2_triggered = (digitalRead(IR_SENSOR_2) == HIGH);
// 1. Car enters the trap zone (triggers Sensor 1)
if (sensor1_triggered && !timingActive && !carDetected) {
startTime = millis();
timingActive = true;
carDetected = true;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("CAR DETECTED... ");
lcd.setCursor(0, 1);
lcd.print("TIMING RUN... ");
}
// 2. Car leaves the trap zone (triggers Sensor 2)
if (sensor2_triggered && timingActive) {
endTime = millis();
timingActive = false;
// Calculate total elapsed duration in seconds
float elapsedSeconds = (endTime - startTime) / 1000.0;
// Safety check to prevent division by zero
if (elapsedSeconds > 0) {
// Speed = Distance / Time (m/s)
float speedMPS = DISTANCE_BETWEEN_SENSORS / elapsedSeconds;
// Convert m/s to km/h
float speedKMH = speedMPS * 3.6;
// Update Display Panel
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("SPEED: ");
lcd.print(speedKMH, 1);
lcd.print(" km/h");
// Speed check for buzzer warning activation
if (speedKMH > SPEED_LIMIT_KMH) {
lcd.setCursor(0, 1);
lcd.print("! SPEED LIMIT !");
// Alert sound cadence
for (int i = 0; i < 3; i++) {
digitalWrite(BUZZER, HIGH);
delay(150);
digitalWrite(BUZZER, LOW);
delay(100);
}
} else {
lcd.setCursor(0, 1);
lcd.print(" SPEED OK ");
}
// Hold speed metrics on screen before reset window opens
delay(4000);
}
// Reset control variables for the next measurement loop
carDetected = false;
showDefaultMessage();
}
// 3. Timeout fallback safeguard
if (timingActive && (millis() - startTime > TIMEOUT_MS)) {
timingActive = false;
carDetected = false;
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" TIMEOUT ERROR ");
lcd.setCursor(0, 1);
lcd.print(" RESETTING... ");
delay(2000);
showDefaultMessage();
}
}
// Helper function to restore default static view
void showDefaultMessage() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(" RADAR ACTIVE ");
lcd.setCursor(0, 2); // Center aligns 'NO CAR FOUND' on row 2
lcd.setCursor(2, 1);
lcd.print("NO CAR FOUND");
}