In the overview of Accident Aid I described the whole pipeline โ crash detection to emergency alert. This post is the part I spent the most nights on: getting an Arduino Uno to reliably tell the difference between a pothole and a crash using a piezo vibration sensor, and then pushing an SMS out through a GSM module with raw AT commands.
The sensor: a piezo film that turns impact into voltage
The detector is a PZT piezo film sensor (LDT0-028). When the film flexes โ vibration, impact, touch โ it generates a voltage proportional to how hard it moved. The breakout board adds a comparator and a potentiometer so you can adjust sensitivity with a screwdriver, and its wide dynamic range means it registers everything from a finger tap to a collision.
That sensitivity is both the feature and the problem. Mounted on a vehicle, the sensor sees everything: engine idle, closing doors, gravel roads, speed bumps. The entire detection problem is separating that constant noise floor from the one signal that matters.
Reading it: threshold plus averaging, not single spikes
The naive version โ "if reading > threshold, it's a crash" โ fires on every pothole. A single analog spike is noise; a crash is a burst of high readings. So the loop accumulates values above the threshold and only evaluates the average of the burst:
int SENSOR_PIN = A1;
int sum = 0;
int threshold = 450;
float avg = 0;
int i = 0;
void loop()
{
int value = analogRead(SENSOR_PIN);
if (value > threshold) {
sum = sum + value; // accumulate the burst
i++;
} else {
avg = sum / i; // burst ended โ evaluate it
findCrash(avg);
}
delay(1000);
}
void findCrash(float avg)
{
if (avg > threshold) {
SendMessage(ph); // sustained heavy impact: alert
avg = 0; sum = 0; i = 0;
}
}
The logic reads oddly until you see what it's doing: while readings stay above the threshold, it accumulates; the moment they drop back below, the burst is over and the average intensity of the burst decides whether it was a crash. A pothole produces one or two high samples whose average gets dragged down; a collision produces a sustained run of very high samples that keeps the average above the line.
The threshold of 450 wasn't computed โ it was calibrated. We logged readings from normal driving conditions, watched the serial monitor, and moved the line (and the onboard potentiometer) until ordinary abuse stayed quiet and sharp impacts didn't. Every vehicle would need its own calibration pass; that's not a weakness, it's what tuning real sensors looks like.
Sending the alert: GSM and the AT command dance
The GSM module talks over a serial line โ SoftwareSerial on pins 9 and 10, 9600 baud โ and speaks the ancient, wonderful language of AT commands:
void SendMessage(String num)
{
mySerial.println("AT+CMGF=1"); // text mode
delay(1000);
mySerial.println("AT+CMGS=\"+91" + ph + "\""); // recipient
delay(1000);
mySerial.println("Accident detected. Location: " + gpsLink);
delay(100);
mySerial.println((char)26); // CTRL+Z sends it
delay(1000);
}
Three things bit us here, and they'll bite anyone doing GSM on Arduino:
- The delays are load-bearing. The module needs time to process each command. Too fast and it silently drops the message โ no error, just nothing.
(char)26is the send button. SMS text mode ends the message with a literal CTRL+Z byte. Forget it and the module waits forever.- A SIM with no credit fails silently. The AT commands all succeed; no SMS arrives. We lost an afternoon to that before checking the balance.
The GPS module rides alongside, providing coordinates that get embedded in the SMS โ so the message isn't "there was an accident" but "there was an accident here," as a link the recipient can open on a map. The same coordinates flow to the hospital and police web portals.
Power, mounting, and other unglamorous realities
The parts of embedded work no tutorial dwells on:
- Placement changes everything. The same impact reads differently depending on where the film is mounted and how rigidly. We fixed the sensor's position before calibrating, because moving it invalidates the threshold.
- Wiring must survive vibration. A breadboard in a vibrating vehicle is a random-disconnection generator. Solder what you can.
- The device must outlive the crash. Alerting is worthless if the first casualty of the impact is the device's own power. Independent battery backup isn't optional in a real deployment.
Common pitfalls
- Trusting one reading. Average the burst. The difference between "fires on potholes" and "fires on crashes" is that one change.
- Skipping serial-monitor time. Calibration is staring at live values while someone slams a door, drives over gravel, drops a weight. There is no shortcut.
- No delays between AT commands. The GSM module is a slow peripheral pretending to be a modem from 1995. Respect its pace.
- Testing indoors only. The lab bench has no engine vibration, no heat, no weak signal. The field test is the real test.
- Ignoring the unhappy paths. No GSM signal, no GPS fix, empty SIM โ each needs a defined behaviour, even if that behaviour is "retry and blink an LED."
What this taught me
Working at the analog edge โ where physics becomes data โ made me permanently suspicious of clean inputs. Every system I've built since assumes the incoming signal lies a little: user form input, webhook payloads, third-party APIs. The vibration sensor was just the most honest version of the problem. Filtering, thresholds, debouncing, retries โ I met them all here first, at 9600 baud.
FAQ
Why a piezo film instead of an accelerometer?
Does the averaging approach add dangerous delay?
Can this run on something other than Arduino?
How would you harden the SMS path today?
Enjoy hardware-meets-software problems, or need a developer who understands both ends? Get in touch.