#include <Wire.h> // I2C communication library
#include <LiquidCrystal_I2C.h> // I2C LCD library
#include <DHT.h> // DHT temperature/humidity sensor library
#include <ESP32Servo.h> // Servo control for ESP32
#include <ESP32PWM.h> // PWM allocation for ESP32 servos
#include <WiFi.h> // Wi-Fi connectivity
#include <PubSubClient.h> // MQTT client library
// Wi-Fi & MQTT Configuration
// Wi-Fi network name and password (Wokwi-GUEST is open)
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// Public HiveMQ broker (no authentication)
const char* mqtt_server = "broker.hivemq.com";
WiFiClient espClient; // TCP client for MQTT
PubSubClient client(espClient); // MQTT client instance
// Hardware Pin Definitions
// I²C LCD at address 0x27, 16 cols × 2 rows
LiquidCrystal_I2C lcd(0x27, 16, 2);
// DHT22 temp/humidity sensor on GPIO 15
#define DHTPIN 15
#define DHTTYPE DHT22
DHT dht(DHTPIN, DHTTYPE);
// Ultrasonic HC-SR04 for trash bin (Bin A)
const int trigPin = 27; // Trigger pin
const int echoPin = 26; // Echo pin
// Compost bin sensors & actuator (Bin B)
const int moistPin = 34; // Soil-moisture potentiometer wiper
const int gasPin = 35; // MQ-2 gas sensor analog output
const int servoPin = 25; // Servo control pin
const int buzzerPin = 2; // Piezo buzzer pin
Servo aerator; // Servo object for mixing
// RGB LED for trash status indication (common cathode)
const int redPin = 14;
const int greenPin = 12;
const int bluePin = 13; // blue channel unused (optional)
// Thresholds & State Tracking
const int DIST_FULL = 10; // cm threshold for “bin full”
const int MOIST_LOW = 300; // analog < MOIST_LOW → too dry
const int GAS_HIGH = 1000; // analog > GAS_HIGH → gas alert
const float TEMP_LOW = 10.0; // °C lower bound
const float TEMP_HIGH = 50.0; // °C upper bound
int okCycles = 0; // consecutive “healthy” compost loops
const int READY_THRESHOLD = 5; // loops of OK before “READY” state
// setup(): Runs once at boot
void setup() {
Serial.begin(115200); // initialize serial monitor
// Initialize Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print("."); // waiting indicator
}
Serial.println("\nWiFi connected");
// Initialize MQTT broker connection
client.setServer(mqtt_server, 1883);
// Initialize LCD
lcd.init(); // turn on LCD
lcd.backlight(); // enable backlight
// Initialize DHT22 sensor
dht.begin();
// Prepare servo PWM timers (ESP32-specific)
ESP32PWM::allocateTimer(0);
ESP32PWM::allocateTimer(1);
ESP32PWM::allocateTimer(2);
ESP32PWM::allocateTimer(3);
aerator.attach(servoPin); // attach servo to pin
// Configure GPIO modes
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
pinMode(buzzerPin, OUTPUT);
pinMode(redPin, OUTPUT);
pinMode(greenPin, OUTPUT);
pinMode(bluePin, OUTPUT);
}
// loop(): Runs repeatedly
void loop() {
// Ensure MQTT connection
if (!client.connected()) {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect("ESP32Client")) {
Serial.println("connected");
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
delay(2000);
}
}
}
client.loop(); // keep MQTT alive
// Bin A: Ultrasonic distance measurement
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
long duration = pulseIn(echoPin, HIGH);
float distance = duration * 0.034 / 2.0; // convert to cm
bool binFull = (distance < DIST_FULL); // fullness flag
// Bin B: Read compost sensors ---
float temp = dht.readTemperature(); // °C
int moisture = analogRead(moistPin); // 0–4095
int gas = analogRead(gasPin); // 0–4095
// Debug print for gas
Serial.print("Gas Value: ");
Serial.println(gas);
// Determine compost status flags
bool tooDry = (moisture < MOIST_LOW);
bool gasAlert = (gas > GAS_HIGH);
bool tooHot = (temp > TEMP_HIGH);
bool tooCold = (temp < TEMP_LOW);
// Update LCD display
lcd.clear();
// Line 1: Trash bin status
lcd.setCursor(0, 0);
if (binFull) {
lcd.print("TRASH: FULL");
setLED(255, 0, 0); // red LED
} else {
lcd.print("TRASH: OK ");
setLED(0, 255, 0); // green LED
}
// Line 2: Compost bin status with “ready” logic
lcd.setCursor(0, 1);
if (tooDry || gasAlert || tooHot || tooCold) {
// Any bad condition → mix required
lcd.print("COMP: MIX ");
aerator.write(90); // rotate servo
tone(buzzerPin, 1000); // beep
delay(1000);
aerator.write(0);
noTone(buzzerPin);
okCycles = 0; // reset ready counter
} else {
// All good → increment healthy cycle count
okCycles++;
if (okCycles >= READY_THRESHOLD) {
lcd.print("COMP: READY"); // compost fully matured
} else {
lcd.print("COMP: OK "); // compost healthy but not ready
}
}
// Serial JSON logging for debug or MQTT
Serial.printf(
"{\"trash_cm\":%.1f,\"temp\":%.1f,\"moisture\":%d,\"gas\":%d,"
"\"binFull\":%d,\"okCycles\":%d}\n",
distance, temp, moisture, gas, binFull, okCycles
);
// Publish to MQTT topic
String payload = String("{\"trash_cm\":") + distance +
",\"temp\":" + temp +
",\"moisture\":" + moisture +
",\"gas\":" + gas +
",\"compost_status\":\"" +
(tooDry||gasAlert||tooHot||tooCold ? "MIX"
: (okCycles>=READY_THRESHOLD ? "READY" : "OK")) +
"\",\"trash_full\":" + binFull + "}";
client.publish("smartbin/status", payload.c_str());
delay(3000); // wait 3 seconds before next loop
}
// setLED(): Control RGB LED
void setLED(int r, int g, int b) {
analogWrite(redPin, r); // red intensity
analogWrite(greenPin, g); // green intensity
analogWrite(bluePin, b); // blue intensity (unused)
}