#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <ESP32Servo.h>
// ============================================================
// PROJECT CONFIGURATION
// ============================================================
const char* projectName = "E-WASTE MONITOR";
// ============================================================
// PIN DEFINITIONS
// ============================================================
// Relay / vibration motor
const int motorRelayPin = 18;
// LEDs
const int greenLedPin = 27;
const int redLedPin = 32;
// Buzzer
const int buzzerPin = 26;
// Ultrasonic sensor
const int ultrasonicTrigPin = 25;
const int ultrasonicEchoPin = 33;
// DHT22
const int dhtPin = 4;
const int dhtType = DHT22;
// Servo
const int servoPin = 13;
// I2C LCD
const int lcdSdaPin = 21;
const int lcdSclPin = 22;
// ============================================================
// LCD CONFIGURATION
// ============================================================
const int lcdAddress = 0x27;
const int lcdColumns = 16;
const int lcdRows = 2;
LiquidCrystal_I2C lcd(
lcdAddress,
lcdColumns,
lcdRows
);
// ============================================================
// SENSOR OBJECTS
// ============================================================
DHT dht(dhtPin, dhtType);
Servo ultrasonicServo;
// ============================================================
// SERVO SCAN ANGLES
// ============================================================
const int servoAngleOne = 0;
const int servoAngleTwo = 30;
const int servoAngleThree = 90;
// ============================================================
// DISTANCE OFFSETS / CALIBRATION
// ============================================================
/*
Corrected distance = Raw distance + Offset
Start all offsets at 0.0 and calibrate them using
the actual physical bin.
*/
const float distanceOffsetAtZero = 0.0;
const float distanceOffsetAtThirty = 0.0;
const float distanceOffsetAtNinety = 0.0;
// ============================================================
// TIMING CONFIGURATION
// ============================================================
// Time for vibration motor to stabilize e-waste
const unsigned long stabilizationTime = 5000;
// Time for servo to reach requested position
const unsigned long servoMoveDelay = 800;
// Delay between ultrasonic readings
const unsigned long ultrasonicDelay = 100;
// Main measurement interval
const unsigned long measurementInterval = 10000;
// ============================================================
// BIN CONFIGURATION
// ============================================================
/*
Usable vertical height of the bin.
Example:
Empty bin = approximately 30 cm
Full bin = approximately 5 cm
Larger distance = less e-waste
Smaller distance = more e-waste
*/
const float binHeightCm = 30.0;
// Fill level thresholds
const float lowLevelPercentage = 40.0;
const float mediumLevelPercentage = 75.0;
// ============================================================
// ULTRASONIC CONFIGURATION
// ============================================================
const int ultrasonicSamples = 5;
const unsigned long ultrasonicTimeout = 30000;
// ============================================================
// RELAY CONFIGURATION
// ============================================================
// true = LOW activates relay
// false = HIGH activates relay
const bool relayActiveLow = true;
// ============================================================
// LEVEL ENUMERATION
// ============================================================
enum BinLevel {
LEVEL_LOW,
LEVEL_MEDIUM,
LEVEL_FULL,
LEVEL_UNKNOWN
};
// ============================================================
// SENSOR DATA STRUCTURE
// ============================================================
struct MeasurementData {
float rawDistanceAtZero;
float rawDistanceAtThirty;
float rawDistanceAtNinety;
float distanceAtZero;
float distanceAtThirty;
float distanceAtNinety;
float averageDistance;
float fillPercentage;
float temperature;
float humidity;
BinLevel binLevel;
};
// ============================================================
// GLOBAL VARIABLES
// ============================================================
MeasurementData measurementData;
unsigned long lastMeasurementTime = 0;
// ============================================================
// FUNCTION: setRelay
// ============================================================
void setRelay(bool state) {
/*
Controls the vibration motor relay.
*/
if (relayActiveLow) {
digitalWrite(
motorRelayPin,
state ? LOW : HIGH
);
} else {
digitalWrite(
motorRelayPin,
state ? HIGH : LOW
);
}
}
// ============================================================
// FUNCTION: showStartupMessage
// ============================================================
void showStartupMessage() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("E-WASTE MONITOR");
lcd.setCursor(0, 1);
lcd.print("Initializing...");
delay(2000);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Smart Waste Bin");
lcd.setCursor(0, 1);
lcd.print("System Ready");
delay(2000);
}
// ============================================================
// FUNCTION: activateBuzzer
// ============================================================
void activateBuzzer(
int beepCount,
int beepDuration
) {
for (
int i = 0;
i < beepCount;
i++
) {
digitalWrite(
buzzerPin,
HIGH
);
delay(beepDuration);
digitalWrite(
buzzerPin,
LOW
);
delay(beepDuration);
}
}
// ============================================================
// FUNCTION: readUltrasonicDistance
// ============================================================
float readUltrasonicDistance() {
digitalWrite(
ultrasonicTrigPin,
LOW
);
delayMicroseconds(3);
digitalWrite(
ultrasonicTrigPin,
HIGH
);
delayMicroseconds(10);
digitalWrite(
ultrasonicTrigPin,
LOW
);
unsigned long duration = pulseIn(
ultrasonicEchoPin,
HIGH,
ultrasonicTimeout
);
if (duration == 0) {
return -1.0;
}
float distance =
duration * 0.0343 / 2.0;
if (
distance <= 0 ||
distance > 400
) {
return -1.0;
}
return distance;
}
// ============================================================
// FUNCTION: getStableDistance
// ============================================================
float getStableDistance() {
float totalDistance = 0.0;
int validReadings = 0;
for (
int i = 0;
i < ultrasonicSamples;
i++
) {
float distance =
readUltrasonicDistance();
if (distance > 0) {
totalDistance += distance;
validReadings++;
}
delay(ultrasonicDelay);
}
if (validReadings == 0) {
return -1.0;
}
return totalDistance / validReadings;
}
// ============================================================
// FUNCTION: getAngleOffset
// ============================================================
float getAngleOffset(int angle) {
if (angle == servoAngleOne) {
return distanceOffsetAtZero;
}
if (angle == servoAngleTwo) {
return distanceOffsetAtThirty;
}
if (angle == servoAngleThree) {
return distanceOffsetAtNinety;
}
return 0.0;
}
// ============================================================
// FUNCTION: moveServoAndMeasure
// ============================================================
float moveServoAndMeasure(
int angle,
float &rawDistance
) {
ultrasonicServo.write(angle);
delay(servoMoveDelay);
rawDistance =
getStableDistance();
if (rawDistance < 0) {
return -1.0;
}
float offset =
getAngleOffset(angle);
float correctedDistance =
rawDistance + offset;
if (correctedDistance < 0) {
correctedDistance = 0;
}
if (correctedDistance > 400) {
correctedDistance = 400;
}
return correctedDistance;
}
// ============================================================
// FUNCTION: calculateAverageDistance
// ============================================================
float calculateAverageDistance(
float distanceOne,
float distanceTwo,
float distanceThree
) {
float total = 0.0;
int count = 0;
if (distanceOne > 0) {
total += distanceOne;
count++;
}
if (distanceTwo > 0) {
total += distanceTwo;
count++;
}
if (distanceThree > 0) {
total += distanceThree;
count++;
}
if (count == 0) {
return -1.0;
}
return total / count;
}
// ============================================================
// FUNCTION: calculateFillPercentage
// ============================================================
float calculateFillPercentage(
float averageDistance
) {
if (averageDistance < 0) {
return -1.0;
}
float emptySpace =
averageDistance;
float filledHeight =
binHeightCm - emptySpace;
if (filledHeight < 0) {
filledHeight = 0;
}
if (filledHeight > binHeightCm) {
filledHeight = binHeightCm;
}
float fillPercentage =
(filledHeight / binHeightCm) * 100.0;
return fillPercentage;
}
// ============================================================
// FUNCTION: determineBinLevel
// ============================================================
BinLevel determineBinLevel(
float fillPercentage
) {
if (fillPercentage < 0) {
return LEVEL_UNKNOWN;
}
if (
fillPercentage <
lowLevelPercentage
) {
return LEVEL_LOW;
}
if (
fillPercentage <
mediumLevelPercentage
) {
return LEVEL_MEDIUM;
}
return LEVEL_FULL;
}
// ============================================================
// FUNCTION: getLevelName
// ============================================================
const char* getLevelName(
BinLevel level
) {
switch (level) {
case LEVEL_LOW:
return "LOW";
case LEVEL_MEDIUM:
return "MEDIUM";
case LEVEL_FULL:
return "FULL";
default:
return "ERROR";
}
}
// ============================================================
// FUNCTION: getOutputName
// ============================================================
const char* getOutputName(
BinLevel level
) {
switch (level) {
case LEVEL_LOW:
return "GREEN LED";
case LEVEL_MEDIUM:
return "GREEN+RED LED";
case LEVEL_FULL:
return "RED LED + ALERT";
default:
return "SENSOR ERROR";
}
}
// ============================================================
// FUNCTION: readDhtSensor
// ============================================================
void readDhtSensor() {
float humidity =
dht.readHumidity();
float temperature =
dht.readTemperature();
if (
isnan(humidity) ||
isnan(temperature)
) {
measurementData.humidity = -1;
measurementData.temperature = -1;
Serial.println(
"ENV: T=ERR | H=ERR"
);
return;
}
measurementData.humidity =
humidity;
measurementData.temperature =
temperature;
// Compact one-line environment output
Serial.print("ENV: T=");
Serial.print(
temperature,
1
);
Serial.print("C | H=");
Serial.print(
humidity,
1
);
Serial.println("%");
}
// ============================================================
// FUNCTION: stabilizeWaste
// ============================================================
void stabilizeWaste() {
/*
Displays stabilization time in seconds
and activates the vibration motor.
*/
unsigned long stabilizationSeconds =
stabilizationTime / 1000;
Serial.print(
"STABILIZING WASTE... "
);
Serial.print(
stabilizationSeconds
);
Serial.println(" sec");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Stabilizing");
lcd.setCursor(0, 1);
lcd.print("Waste...");
setRelay(true);
unsigned long startTime =
millis();
while (
millis() - startTime <
stabilizationTime
) {
delay(100);
}
setRelay(false);
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Stabilization");
lcd.setCursor(0, 1);
lcd.print("Completed");
delay(1000);
}
// ============================================================
// FUNCTION: performWasteMeasurement
// ============================================================
void performWasteMeasurement() {
Serial.println(
"BIN SCAN START"
);
// ----------------------------------------------------------
// 0 DEGREE
// ----------------------------------------------------------
measurementData.distanceAtZero =
moveServoAndMeasure(
servoAngleOne,
measurementData.rawDistanceAtZero
);
// ----------------------------------------------------------
// 30 DEGREE
// ----------------------------------------------------------
measurementData.distanceAtThirty =
moveServoAndMeasure(
servoAngleTwo,
measurementData.rawDistanceAtThirty
);
// ----------------------------------------------------------
// 90 DEGREE
// ----------------------------------------------------------
measurementData.distanceAtNinety =
moveServoAndMeasure(
servoAngleThree,
measurementData.rawDistanceAtNinety
);
// ----------------------------------------------------------
// CALCULATE AVERAGE
// ----------------------------------------------------------
measurementData.averageDistance =
calculateAverageDistance(
measurementData.distanceAtZero,
measurementData.distanceAtThirty,
measurementData.distanceAtNinety
);
// ----------------------------------------------------------
// CALCULATE FILL
// ----------------------------------------------------------
measurementData.fillPercentage =
calculateFillPercentage(
measurementData.averageDistance
);
// ----------------------------------------------------------
// DETERMINE LEVEL
// ----------------------------------------------------------
measurementData.binLevel =
determineBinLevel(
measurementData.fillPercentage
);
// ----------------------------------------------------------
// COMPACT DISTANCE OUTPUT
// ----------------------------------------------------------
Serial.print("DIST:A0=");
if (
measurementData.distanceAtZero >= 0
) {
Serial.print(
measurementData.distanceAtZero,
2
);
} else {
Serial.print("ERR");
}
Serial.print(" | A30=");
if (
measurementData.distanceAtThirty >= 0
) {
Serial.print(
measurementData.distanceAtThirty,
2
);
} else {
Serial.print("ERR");
}
Serial.print(" | A90=");
if (
measurementData.distanceAtNinety >= 0
) {
Serial.print(
measurementData.distanceAtNinety,
2
);
} else {
Serial.print("ERR");
}
Serial.println("cm");
// ----------------------------------------------------------
// COMPACT RESULT OUTPUT
// ----------------------------------------------------------
Serial.print("AVG:");
if (
measurementData.averageDistance >= 0
) {
Serial.print(
measurementData.averageDistance,
2
);
Serial.print("cm");
} else {
Serial.print("ERR");
}
Serial.print(" | FILL:");
if (
measurementData.fillPercentage >= 0
) {
Serial.print(
measurementData.fillPercentage,
2
);
Serial.print("%");
} else {
Serial.print("ERR");
}
Serial.print(" | STATUS:");
Serial.print(
getLevelName(
measurementData.binLevel
)
);
Serial.print(" | OUTPUT:");
Serial.println(
getOutputName(
measurementData.binLevel
)
);
}
// ============================================================
// FUNCTION: updateIndicators
// ============================================================
void updateIndicators() {
digitalWrite(
greenLedPin,
LOW
);
digitalWrite(
redLedPin,
LOW
);
digitalWrite(
buzzerPin,
LOW
);
switch (
measurementData.binLevel
) {
// --------------------------------------------------------
// LOW
// --------------------------------------------------------
case LEVEL_LOW:
digitalWrite(
greenLedPin,
HIGH
);
break;
// --------------------------------------------------------
// MEDIUM
// --------------------------------------------------------
case LEVEL_MEDIUM:
digitalWrite(
greenLedPin,
HIGH
);
digitalWrite(
redLedPin,
HIGH
);
activateBuzzer(
1,
150
);
break;
// --------------------------------------------------------
// FULL
// --------------------------------------------------------
case LEVEL_FULL:
digitalWrite(
redLedPin,
HIGH
);
activateBuzzer(
3,
200
);
break;
// --------------------------------------------------------
// SENSOR ERROR
// --------------------------------------------------------
default:
digitalWrite(
redLedPin,
HIGH
);
activateBuzzer(
2,
100
);
break;
}
}
// ============================================================
// FUNCTION: displayLevel
// ============================================================
void displayLevel() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("LEVEL:");
lcd.print(
getLevelName(
measurementData.binLevel
)
);
lcd.setCursor(0, 1);
if (
measurementData.fillPercentage >= 0
) {
lcd.print("Fill:");
lcd.print(
(int)measurementData.fillPercentage
);
lcd.print("%");
} else {
lcd.print("Sensor Error");
}
delay(2000);
}
// ============================================================
// FUNCTION: displayEnvironment
// ============================================================
void displayEnvironment() {
lcd.clear();
lcd.setCursor(0, 0);
if (
measurementData.temperature >= 0
) {
lcd.print("Temp:");
lcd.print(
measurementData.temperature,
1
);
lcd.print((char)223);
lcd.print("C");
} else {
lcd.print("Temp:ERROR");
}
lcd.setCursor(0, 1);
if (
measurementData.humidity >= 0
) {
lcd.print("Hum:");
lcd.print(
measurementData.humidity,
0
);
lcd.print("%");
} else {
lcd.print("Hum:ERROR");
}
delay(2000);
}
// ============================================================
// FUNCTION: displayDistances
// ============================================================
void displayDistances() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("0:");
lcd.print(
measurementData.distanceAtZero,
1
);
lcd.print(" 30:");
lcd.print(
measurementData.distanceAtThirty,
1
);
lcd.setCursor(0, 1);
lcd.print("90:");
lcd.print(
measurementData.distanceAtNinety,
1
);
lcd.print("cm");
delay(2000);
}
// ============================================================
// FUNCTION: displayMainStatus
// ============================================================
void displayMainStatus() {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("E-WASTE:");
lcd.print(
getLevelName(
measurementData.binLevel
)
);
lcd.setCursor(0, 1);
lcd.print("F:");
if (
measurementData.fillPercentage >= 0
) {
lcd.print(
(int)measurementData.fillPercentage
);
lcd.print("%");
} else {
lcd.print("ERR");
}
lcd.print(" T:");
if (
measurementData.temperature >= 0
) {
lcd.print(
(int)measurementData.temperature
);
lcd.print("C");
} else {
lcd.print("--");
}
delay(2000);
}
// ============================================================
// FUNCTION: runMeasurementCycle
// ============================================================
void runMeasurementCycle() {
// ----------------------------------------------------------
// HEADER
// ----------------------------------------------------------
Serial.println();
Serial.println(
"================================"
);
Serial.println(
"E-WASTE BIN MONITORING CYCLE"
);
Serial.println(
"================================"
);
// ----------------------------------------------------------
// 1. ENVIRONMENT
// ----------------------------------------------------------
readDhtSensor();
// ----------------------------------------------------------
// 2. STABILIZE WASTE
// ----------------------------------------------------------
stabilizeWaste();
// ----------------------------------------------------------
// 3. BIN SCAN
// ----------------------------------------------------------
performWasteMeasurement();
// ----------------------------------------------------------
// 4. OUTPUTS
// ----------------------------------------------------------
updateIndicators();
// ----------------------------------------------------------
// 5. LCD
// ----------------------------------------------------------
displayLevel();
displayEnvironment();
displayDistances();
displayMainStatus();
}
// ============================================================
// SETUP
// ============================================================
void setup() {
Serial.begin(115200);
delay(1000);
// ----------------------------------------------------------
// INITIAL STARTUP MESSAGE
// ----------------------------------------------------------
Serial.println();
Serial.println(
"================================"
);
Serial.println(
"SMART E-WASTE MONITORING SYSTEM"
);
Serial.println(
"================================"
);
// ----------------------------------------------------------
// GPIO CONFIGURATION
// ----------------------------------------------------------
pinMode(
motorRelayPin,
OUTPUT
);
pinMode(
greenLedPin,
OUTPUT
);
pinMode(
redLedPin,
OUTPUT
);
pinMode(
buzzerPin,
OUTPUT
);
pinMode(
ultrasonicTrigPin,
OUTPUT
);
pinMode(
ultrasonicEchoPin,
INPUT
);
// ----------------------------------------------------------
// INITIAL OUTPUT STATES
// ----------------------------------------------------------
setRelay(false);
digitalWrite(
greenLedPin,
LOW
);
digitalWrite(
redLedPin,
LOW
);
digitalWrite(
buzzerPin,
LOW
);
digitalWrite(
ultrasonicTrigPin,
LOW
);
// ----------------------------------------------------------
// I2C
// ----------------------------------------------------------
Wire.begin(
lcdSdaPin,
lcdSclPin
);
// ----------------------------------------------------------
// LCD
// ----------------------------------------------------------
lcd.init();
lcd.backlight();
// ----------------------------------------------------------
// DHT
// ----------------------------------------------------------
dht.begin();
// ----------------------------------------------------------
// SERVO
// ----------------------------------------------------------
ultrasonicServo.setPeriodHertz(50);
ultrasonicServo.attach(
servoPin,
500,
2400
);
ultrasonicServo.write(
servoAngleOne
);
delay(1000);
// ----------------------------------------------------------
// STARTUP LCD
// ----------------------------------------------------------
showStartupMessage();
Serial.println(
"System initialization completed."
);
// ----------------------------------------------------------
// RUN FIRST CYCLE IMMEDIATELY
// ----------------------------------------------------------
lastMeasurementTime =
millis() - measurementInterval;
}
// ============================================================
// MAIN LOOP
// ============================================================
void loop() {
unsigned long currentTime =
millis();
if (
currentTime -
lastMeasurementTime >=
measurementInterval
) {
lastMeasurementTime =
currentTime;
runMeasurementCycle();
}
}