Smart Helmet for Accident Detection
ESP32 project: helmet check, alcohol lock, crash detection & OLED display — all in one!
📋 What We're Making
This Smart Helmet system does four jobs at once: it checks if the rider is wearing the helmet (using a PIR sensor), checks if the rider has been drinking using a gas/alcohol sensor (MQ2), and only allows the bike to start (via a relay controlling the ignition) if both checks pass. An MPU6050 accelerometer/gyroscope watches for sudden crashes using a 3-level trigger system and sounds a buzzer alert. And now, a new SSD1306 OLED display shows the live status of everything right on the helmet! 🛵🪖📟
🧰 Parts You'll Need
🔌 The Circuit Diagram
The ESP32 connects to all five sensors/modules at once! The OLED display shares the I2C bus (GPIO 21 & 22) with the MPU6050 — they just need different I2C addresses. ⚡
📐 MPU6050 (Crash Sensor) → ESP32
| MPU6050 Pin | ESP32 Pin |
|---|---|
| VCC | 3V3 |
| GND | GND |
| SDA | GPIO 21 |
| SCL | GPIO 22 |
👤 PIR Motion Sensor (Helmet Worn Check) → ESP32
| PIR Pin | ESP32 Pin |
|---|---|
| VCC | 3V3 |
| GND | GND |
| OUT | GPIO 2 |
🍺 MQ2 Gas/Alcohol Sensor → ESP32
| MQ2 Pin | ESP32 Pin |
|---|---|
| VCC | 3V3 |
| GND | GND |
| AOUT | GPIO 34 (Analog) |
⚡ Relay Module (Ignition Lock) → ESP32
| Relay Pin | ESP32 Pin |
|---|---|
| VCC | 3V3 |
| GND | GND |
| IN | GPIO 12 |
🔔 Buzzer (Crash Alert) → ESP32
| Buzzer Pin | ESP32 Pin |
|---|---|
| Positive (via 100Ω resistor) | GPIO 14 |
| Negative | GND |
📟 SSD1306 OLED Display → ESP32 NEW
| OLED Pin | ESP32 Pin | Notes |
|---|---|---|
| VCC | 3V3 | Use 3.3V — most SSD1306 modules are 3.3V tolerant |
| GND | GND | Common ground |
| SDA | GPIO 21 | Shared with MPU6050 on I2C bus |
| SCL | GPIO 22 | Shared with MPU6050 on I2C bus |
📟 SSD1306 OLED Display NEW
The 128×64 SSD1306 OLED display acts as the helmet's dashboard — showing real-time helmet status, alcohol level, ignition state, and crash alerts without needing to check a phone or serial monitor. Here's what it looks like while the system is running: 👇
🖥️ Normal Riding Mode (Helmet On, Sober)
🚨 Accident Detected Mode
🔒 Locked Mode (Helmet Off or Drunk)
📚 Extra Library to Install
In addition to the existing libraries, you'll need one more for the OLED:
Adafruit MPU6050(already needed)Adafruit Unified Sensor(already needed)Adafruit SSD1306← Add this one!Adafruit GFX Library← Required by SSD1306!
🛠️ Step-by-Step Build
Connect the MPU6050 to the ESP32's I2C pins (SDA=21, SCL=22). This sensor watches for sudden jerks and orientation changes.
Connect the PIR sensor's OUT pin to GPIO 2. When the helmet is worn snugly, the PIR detects the rider's presence (HIGH signal).
Connect the MQ2 sensor's analog output (AOUT) to GPIO 34. This reads a value from 0–4095 — higher means more alcohol detected.
Connect the relay's IN pin to GPIO 12. The relay acts as a switch for the bike's ignition circuit — only closing when it's safe to ride.
Connect the buzzer through a 100Ω resistor to GPIO 14. This sounds an alert when a crash is detected.
Connect the SSD1306 OLED's SDA to GPIO 21 and SCL to GPIO 22 — the same I2C lines as the MPU6050. Connect VCC to 3V3 and GND to GND. Both devices share the bus peacefully using their different I2C addresses.
Power all sensors from the ESP32's 3V3 pin and connect every GND together for a common ground.
Install
Adafruit MPU6050, Adafruit Unified Sensor, Adafruit SSD1306, and Adafruit GFX Library via the Library Manager, then upload the code below.Open the Serial Monitor at 115200 baud. Simultaneously, the OLED display will show live helmet/alcohol/crash status — no PC needed in the field!
💻 The Code
📚 Libraries to Install
Adafruit MPU6050Adafruit Unified SensorAdafruit SSD1306← New!Adafruit GFX Library← New!
🪖 Smart Helmet: Full Code with OLED Display
#include <Wire.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <Adafruit_SSD1306.h>
Adafruit_MPU6050 mpu;
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// Pins
#define PIR_PIN 2 // Helmet detection
#define MQ2_PIN 34 // Alcohol detection (analog)
#define RELAY_PIN 12 // Ignition control
#define BUZZER_PIN 14 // Crash alert
int16_t AcX, AcY, AcZ, Tmp, GyX, GyY, GyZ;
float ax = 0, ay = 0, az = 0, gx = 0, gy = 0, gz = 0;
boolean accident = false;
boolean trigger1 = false;
boolean trigger2 = false;
boolean trigger3 = false;
byte trigger1count = 0;
byte trigger2count = 0;
byte trigger3count = 0;
int angleChange = 0;
void setup() {
Serial.begin(115200);
pinMode(PIR_PIN, INPUT);
pinMode(RELAY_PIN, OUTPUT);
pinMode(BUZZER_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW);
digitalWrite(BUZZER_PIN, LOW);
// Initialize OLED
if (!display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
Serial.println("SSD1306 OLED not found. Check wiring.");
while (1);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(20, 20);
display.println("Smart Helmet");
display.setCursor(25, 36);
display.println("Initializing...");
display.display();
delay(1500);
// Initialize MPU6050
if (!mpu.begin()) {
Serial.println("MPU6050 not found. Check connections.");
display.clearDisplay();
display.setCursor(0, 24);
display.println("MPU6050 ERROR!");
display.display();
while (1);
}
Serial.println("Smart Helmet System Initialized.");
display.clearDisplay();
display.setCursor(20, 24);
display.println("System Ready!");
display.display();
delay(1000);
}
void updateOLED(bool helmetWorn, bool sober, int alcoholLevel,
int amp, bool accidentDetected) {
display.clearDisplay();
// Title bar
display.setTextSize(1);
display.setCursor(25, 0);
display.println("SMART HELMET");
display.drawLine(0, 9, 127, 9, SSD1306_WHITE);
// Helmet status
display.setCursor(0, 12);
display.print("Helmet: ");
if (helmetWorn) {
display.println("WORN [OK]");
} else {
display.println("NOT WORN [!!]");
}
// Alcohol status
display.setCursor(0, 22);
display.print("Alcohol: ");
if (sober) {
display.print("OK (");
display.print(alcoholLevel);
display.println(")");
} else {
display.print("HIGH! (");
display.print(alcoholLevel);
display.println(")");
}
// Ignition status
display.setCursor(0, 32);
display.print("Ignition: ");
if (helmetWorn && sober) {
display.println("ON [SAFE]");
} else {
display.println("LOCKED");
}
// Crash detection row
display.drawLine(0, 42, 127, 42, SSD1306_WHITE);
display.setCursor(0, 45);
display.print("Accel: ");
display.print(amp);
display.print(" Ang: ");
display.println(angleChange);
// Bottom status / accident alert
if (accidentDetected) {
display.fillRect(0, 55, 128, 9, SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK);
display.setCursor(15, 56);
display.print("!! ACCIDENT !!");
display.setTextColor(SSD1306_WHITE);
} else {
display.setCursor(0, 56);
display.println("Monitoring...");
}
display.display();
}
void loop() {
bool helmetWorn = digitalRead(PIR_PIN);
int alcoholLevel = analogRead(MQ2_PIN);
bool sober = alcoholLevel < 3000;
if (helmetWorn && sober) {
digitalWrite(RELAY_PIN, HIGH);
Serial.println("Helmet worn and sober - Ignition ON");
} else {
digitalWrite(RELAY_PIN, LOW);
if (!helmetWorn) Serial.println("Helmet not detected - Ignition locked");
if (!sober) Serial.println("Alcohol detected - Ignition locked");
}
// Crash Detection
mpu_read();
ax = (AcX - 2050) / 16384.00;
ay = (AcY - 77) / 16384.00;
az = (AcZ - 1947) / 16384.00;
gx = (GyX + 270) / 131.07;
gy = (GyY - 351) / 131.07;
gz = (GyZ + 136) / 131.07;
float Raw_Amp = pow(pow(ax, 2) + pow(ay, 2) + pow(az, 2), 0.5);
int Amp = Raw_Amp * 10;
Serial.println(Amp);
if (Amp <= 2 && trigger2 == false) {
trigger1 = true;
Serial.println("Level 1 Trigger");
}
if (trigger1 == true) {
trigger1count++;
if (Amp >= 12) {
trigger2 = true;
Serial.println("Level 2 Trigger");
trigger1 = false; trigger1count = 0;
}
}
if (trigger2 == true) {
trigger2count++;
angleChange = pow(pow(gx, 2) + pow(gy, 2) + pow(gz, 2), 0.5);
Serial.println(angleChange);
if (angleChange >= 30 && angleChange <= 400) {
trigger3 = true; trigger2 = false; trigger2count = 0;
Serial.println(angleChange);
Serial.println("Level 3 Trigger");
}
}
if (trigger3 == true) {
trigger3count++;
if (trigger3count >= 10) {
angleChange = pow(pow(gx, 2) + pow(gy, 2) + pow(gz, 2), 0.5);
Serial.println(angleChange);
if ((angleChange >= 0) && (angleChange <= 10)) {
accident = true; trigger3 = false; trigger3count = 0;
Serial.println(angleChange);
} else {
trigger3 = false; trigger3count = 0;
Serial.println("TRIGGER 3 DEACTIVATED");
}
}
}
if (accident == true) {
Serial.println("ACCIDENT DETECTED");
// Flash OLED alert + buzzer 3 times
for (int i = 0; i < 3; i++) {
updateOLED(helmetWorn, sober, alcoholLevel, Amp, true);
digitalWrite(BUZZER_PIN, HIGH);
delay(300);
digitalWrite(BUZZER_PIN, LOW);
delay(300);
}
accident = false;
Serial.println("Accident Alert!! Send SMS");
delay(2000);
}
if (trigger2count >= 6) {
trigger2 = false; trigger2count = 0;
Serial.println("TRIGGER 2 DEACTIVATED");
}
if (trigger1count >= 6) {
trigger1 = false; trigger1count = 0;
Serial.println("TRIGGER 1 DEACTIVATED");
}
// Update OLED with latest readings
updateOLED(helmetWorn, sober, alcoholLevel, Amp, false);
delay(1000);
}
void mpu_read() {
AcX = Wire.read() << 8 | Wire.read();
AcY = Wire.read() << 8 | Wire.read();
AcZ = Wire.read() << 8 | Wire.read();
Tmp = Wire.read() << 8 | Wire.read();
GyX = Wire.read() << 8 | Wire.read();
GyY = Wire.read() << 8 | Wire.read();
GyZ = Wire.read() << 8 | Wire.read();
}
🖥️ Serial Monitor Output
Here's what you'll see in the Serial Monitor while the system runs. The OLED mirrors this information visually on the helmet itself! 👇
- "Helmet not detected – Ignition locked" → PIR sensor reads LOW, ignition stays off. OLED shows "NOT WORN [!!]"
- "Alcohol detected – Ignition locked" → MQ2 reading is 3000 or higher. OLED shows "HIGH! (xxxx)"
- "1" → Calculated Amplitude (Amp) value from the MPU6050, shown on OLED as "Accel: 1"
- "Level 1 Trigger" → Amp dropped to 2 or below — system watching for a possible crash
- "ACCIDENT DETECTED" → All 3 triggers fired! OLED flashes "!! ACCIDENT !!" and buzzer beeps
🌐 Try It Online with Wokwi (No Hardware Needed!)
Don't have the parts yet? Simulate this entire circuit online for free using Wokwi! The updated diagram.json below includes the SSD1306 OLED wired up and ready to go.
📥 How to Use It
- Go to wokwi.com and create a new ESP32 project
- Paste in the code from above into
sketch.ino - Replace the auto-generated
diagram.jsonwith the one you downloaded - Click the green ▶️ Play button to run the simulation
- Watch the OLED display update live as you interact with sensors!
- Click on sensors to interact — toggle the PIR, drag the gas sensor slider, and tilt the MPU6050!
🧪 Testing & Troubleshooting
🎯 Calibration Tips
- Adjust the
3000threshold inbool sober = alcoholLevel < 3000;based on your MQ2 sensor's baseline readings in clean air. - The crash detection uses three trigger levels — test by gently shaking, then flipping the MPU6050 to simulate a fall, and watch the OLED and Serial Monitor.
- If "ACCIDENT DETECTED" never triggers, try adjusting the threshold values (2, 12, 30–400, 0–10) to match your sensor's real-world readings.
🔧 Quick Fix Table
| Problem 😟 | Solution 😊 |
|---|---|
| "MPU6050 not found" on startup | Check SDA/SCL wiring (21/22) and that VCC is 3.3V |
| "SSD1306 OLED not found" on startup | Check SDA/SCL wiring (21/22), VCC is 3.3V, and I2C address is 0x3C (or try 0x3D) |
| OLED shows garbage / frozen | Check common GND; try power-cycling; ensure SSD1306 library version matches |
| Ignition never turns on | Check PIR is detecting HIGH and MQ2 reading is below threshold |
| Relay clicks randomly | Add a flyback diode and ensure stable power supply |
| Amp value always 0 | Replace raw Wire.read() calls with mpu.getEvent() for real hardware |
| Buzzer doesn't sound | Check resistor value and buzzer polarity on pin 14 |
🚀 Cool Upgrades to Try
- 📡 Add a GPS + GSM module to send the real accident location via SMS (the code already has commented-out SMS lines!)
- 📱 Add a Telegram or WhatsApp alert using Wi-Fi instead of (or alongside) SMS
- 🔆 Add an LED indicator strip showing helmet/alcohol/ignition status at a glance
- 🗣️ Add a small speaker for spoken warnings ("Please wear your helmet")
- 📊 Log all sensor data to an SD card or cloud dashboard for analysis
- 🔋 Add a rechargeable battery pack with solar charging for the helmet unit
- 🌡️ Use the MPU6050's built-in temperature sensor to display the ambient temp on the OLED!
- 🎨 Add icons to the OLED using Adafruit GFX's bitmap drawing functions
Comments
Post a Comment