// SmartBridgeX: Wokwi simulation for a bridge safety monitor.
// This code reads from a few sensors to check for overload, cracks, and environmental stress.
// Libraries
#include <DHT.h>
#include <Wire.h>
#include <Adafruit_MPU6050.h>
#include <Adafruit_Sensor.h>
#include <math.h>
// Pin setup for all our sensors
// Potentiometer to act like a weight sensor
const int WEIGHT_PIN = 34;
const float MAX_BRIDGE_LOAD_TONS = 100.0; // Our bridge's max designed load
const float OVERLOAD_THRESHOLD_TONS = 80.0; // Alert at 80% of max load
// Ultrasonic sensor to check for cracks or joint gaps
const int TRIG_PIN = 5;
const int ECHO_PIN = 18;
const float CRACK_ALERT_THRESHOLD_CM = 15.0; // Alert if a gap is over 15cm
// Temp/Humidity sensor
const int DHT_PIN = 4;
#define DHTTYPE DHT22 // Using DHT22
DHT dht(DHT_PIN, DHTTYPE);
const float TEMP_STRESS_THRESHOLD_C = 40.0; // Alert if it gets too hot
Adafruit_MPU6050 mpu;
// ----- Configuration -----
float vibrationAxisThreshold = 1.5; // Max allowed spike in any axis (g units)
float tiltThreshold = 10.0; // Max tilt allowed in degrees
float gravityBaseline = 9.8; // Baseline Z acceleration (g)
// This runs once when the ESP32 starts up
void setup() {
// Start the serial monitor so we can see the output
Serial.begin(115200);
Serial.println("--- SmartBridgeX System Online ---");
// Setup the ultrasonic sensor pins
pinMode(TRIG_PIN, OUTPUT);
pinMode(ECHO_PIN, INPUT);
// Get the DHT sensor ready
dht.begin();
if (!mpu.begin()) {
Serial.println("Failed to find MPU6050 chip");
while (1) delay(10);
}
Serial.println("MPU6050 Initialized.");
mpu.setAccelerometerRange(MPU6050_RANGE_8_G);
mpu.setGyroRange(MPU6050_RANGE_500_DEG);
mpu.setFilterBandwidth(MPU6050_BAND_21_HZ);
pinMode(32, OUTPUT); // Tilt warning
pinMode(33, OUTPUT); // Vibration warning
pinMode(25, OUTPUT); // Temperature warning
pinMode(26, OUTPUT); // Weight warning
pinMode(27, OUTPUT); // Crack warning
delay(100);
}
// This part runs over and over again
void loop() {
Serial.println("\n------------------------------------");
// Just a simple timestamp to see when the reading was taken
Serial.println("Reading at " + String(millis() / 1000) + " seconds");
// Read the simulated weight from the potentiometer
int weightValueRaw = analogRead(WEIGHT_PIN);
// Convert the pot's 0-4095 reading to a 0-100 ton scale for our bridge
float currentLoadTons = map(weightValueRaw, 0, 4095, 0, (int)MAX_BRIDGE_LOAD_TONS);
Serial.print("[LOAD] ");
Serial.print(currentLoadTons);
Serial.println(" tons");
// Check for overload
if (currentLoadTons > OVERLOAD_THRESHOLD_TONS) {
Serial.println(" >> ALERT: Bridge Overload!");
digitalWrite(26, HIGH);
} else {
digitalWrite(26, LOW);
}
// Use the ultrasonic sensor to measure a gap
// Send out a sound pulse
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// Listen for the echo and measure the time it took
long duration = pulseIn(ECHO_PIN, HIGH);
// Calculate distance from time
float gapDistanceCm = duration * 0.034 / 2;
Serial.print("[GAP] ");
Serial.print(gapDistanceCm);
Serial.println(" cm");
// Check if the gap is too wide
if (gapDistanceCm > CRACK_ALERT_THRESHOLD_CM) {
Serial.println(" >> ALERT: Structural gap is too wide!");
digitalWrite(27, HIGH);
} else {
digitalWrite(27, LOW);
}
// Read the temperature and humidity
float humidity = dht.readHumidity();
float temperature = dht.readTemperature();
// Sometimes the sensor fails, so check for that
if (isnan(humidity) || isnan(temperature)) {
Serial.println("[ENV] Couldn't read from DHT sensor");
} else {
Serial.print("[ENV] Temp: ");
Serial.print(temperature);
Serial.print("C, Humidity: ");
Serial.print(humidity);
Serial.println("%");
// Check for extreme heat
if (temperature > TEMP_STRESS_THRESHOLD_C) {
Serial.println(" >> ALERT: High temperature stress!");
digitalWrite(25, HIGH);
} else {
digitalWrite(25, LOW);
}
}
sensors_event_t acc, gyro, temp;
mpu.getEvent(&acc, &gyro, &temp);
float ax = acc.acceleration.x;
float ay = acc.acceleration.y;
float az = acc.acceleration.z;
// Approximate tilt angle using accelerometer
float tiltX = atan2(ay, az) * 180 / PI;
float tiltY = atan2(ax, az) * 180 / PI;
// Print current readings
Serial.print("Tilt X: ");
Serial.print(tiltX);
Serial.print("°, Y: ");
Serial.print(tiltY);
Serial.print("° | Acc X: ");
Serial.print(ax);
Serial.print(" Y: ");
Serial.print(ay);
Serial.print(" Z: ");
Serial.println(az);
// Check tilt threshold
if (abs(tiltX) > tiltThreshold || abs(tiltY) > tiltThreshold) {
Serial.println("⚠️ WARNING: Bridge Tilt Detected!");
digitalWrite(32, HIGH);
} else {
digitalWrite(32, LOW);
}
// Check vibration per-axis
Serial.println(abs(az - gravityBaseline));
bool excessiveVibration =
abs(ax) > vibrationAxisThreshold ||
abs(ay) > vibrationAxisThreshold ||
abs(az - gravityBaseline) > vibrationAxisThreshold;
if (excessiveVibration) {
Serial.println("💥 WARNING: Excessive Bridge Vibration Detected!");
digitalWrite(33, HIGH);
} else {
digitalWrite(33, LOW);
}
// Wait 2 seconds before the next loop
delay(2000);
}