// Smart Garbage Bin (Wokwi) - ESP32
// HC-SR04 (fill level) + POT (simulated gas sensor on ADC35)
// Publishes JSON to Serial and to MQTT if available.
/* Add libraries.txt to Wokwi:
knolleary/PubSubClient@^2.8
*/
#include <WiFi.h>
#include <PubSubClient.h>
// -------- CONFIG ----------
const char* WIFI_SSID = "REPLACE_WITH_SSID";
const char* WIFI_PASS = "REPLACE_WITH_PASSWORD";
const char* MQTT_BROKER = "test.mosquitto.org";
const uint16_t MQTT_PORT = 1883;
const char* DEVICE_ID = "BIN-0001";
#define TRIG_PIN 25
#define ECHO_PIN 26
#define GAS_PIN 35 // ADC input (POT wiper)
#define ALERT_LED 16
const float BIN_DEPTH_CM = 40.0; // distance from sensor to bin bottom when EMPTY
const float FULL_THRESHOLD_PCT = 80.0; // consider full above this percent
const float GAS_THRESHOLD_PCT = 45.0; // consider smell high above this percent
const unsigned long PUBLISH_INTERVAL_MS = 10000UL; // 10 seconds
WiFiClient wifiClient;
PubSubClient mqtt(wifiClient);
unsigned long lastPublish = 0;
// ---------- Helpers ----------
float measureDistanceCm() {
// Trigger 10us pulse
digitalWrite(TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(TRIG_PIN, LOW);
// wait for echo (timeout 30000 us)
unsigned long dur = pulseIn(ECHO_PIN, HIGH, 30000UL);
if (dur == 0) return -1.0; // no echo
float dist = dur / 58.0; // us -> cm (approx)
return dist;
}
float distanceToFillPct(float distCm) {
if (distCm < 0) return -1.0; // invalid
if (distCm >= BIN_DEPTH_CM) return 0.0; // empty
float pct = (1.0 - (distCm / BIN_DEPTH_CM)) * 100.0;
if (pct < 0) pct = 0;
if (pct > 100) pct = 100;
return pct;
}
float readGasPct() {
// ADC returns 0..4095 (12-bit) on ESP32 by default in Arduino core
int raw = analogRead(GAS_PIN); // 0..4095
float pct = (raw / 4095.0) * 100.0; // 0..100%
return pct;
}
void connectWiFiOnce() {
if (WiFi.status() == WL_CONNECTED) return;
Serial.print("Connecting WiFi...");
WiFi.mode(WIFI_STA);
WiFi.begin(WIFI_SSID, WIFI_PASS);
unsigned long start = millis();
while (WiFi.status() != WL_CONNECTED && millis() - start < 20000UL) {
delay(500); Serial.print(".");
}
if (WiFi.status() == WL_CONNECTED) {
Serial.println("\nWiFi OK: " + WiFi.localIP().toString());
} else {
Serial.println("\nWiFi not connected (will retry later).");
}
}
void mqttCallback(char* topic, byte* payload, unsigned int length) {
Serial.print("MQTT Rx: "); Serial.println(topic);
String s;
for (unsigned int i = 0; i < length; i++) s += (char)payload[i];
Serial.println("Payload: " + s);
}
void connectMQTT() {
if (WiFi.status() != WL_CONNECTED) return;
if (mqtt.connected()) return;
mqtt.setServer(MQTT_BROKER, MQTT_PORT);
mqtt.setCallback(mqttCallback);
Serial.print("Connecting MQTT...");
if (mqtt.connect(DEVICE_ID)) {
Serial.println("connected");
String cmdTopic = String("waste/bin/") + DEVICE_ID + "/cmd";
mqtt.subscribe(cmdTopic.c_str());
} else {
Serial.print("failed rc="); Serial.println(mqtt.state());
}
}
void publishTelemetry(float fillPct, float dist, float gasPct, float tempMock, float humMock) {
String payload = "{";
payload += "\"device_id\":\"" + String(DEVICE_ID) + "\"";
payload += ",\"ts\":" + String((unsigned long)(millis() / 1000));
payload += ",\"fill_level_pct\":" + String(fillPct,1);
payload += ",\"distance_cm\":" + String(dist,1);
payload += ",\"gas_pct\":" + String(gasPct,1);
payload += ",\"temp_c\":" + String(tempMock,1);
payload += ",\"humidity_pct\":" + String(humMock,1);
payload += "}";
String topic = "waste/bin/" + String(DEVICE_ID) + "/telemetry";
Serial.println("MQTT Publish -> " + topic + " : " + payload);
// Try MQTT publish if connected
if (WiFi.status() == WL_CONNECTED && mqtt.connected()) {
bool ok = mqtt.publish(topic.c_str(), payload.c_str());
if (!ok) Serial.println("MQTT publish failed");
} else {
Serial.println("MQTT not connected (Serial only).");
}
}
// ---------- Setup / Loop ----------
void setup() {
Serial.begin(115200);
delay(50);
Serial.println("Smart Bin (Wokwi) starting...");
pinMode(TRIG_PIN, OUTPUT); digitalWrite(TRIG_PIN, LOW);
pinMode(ECHO_PIN, INPUT);
pinMode(ALERT_LED, OUTPUT); digitalWrite(ALERT_LED, LOW);
analogReadResolution(12); // ensure 0..4095 (default)
// Attempt WiFi (optional)
connectWiFiOnce();
connectMQTT();
}
void loop() {
// Maintain MQTT
if (WiFi.status() == WL_CONNECTED) {
if (!mqtt.connected()) connectMQTT();
mqtt.loop();
} else {
static unsigned long lastWiFiTry = 0;
if (millis() - lastWiFiTry > 30000UL) { lastWiFiTry = millis(); connectWiFiOnce(); }
}
// Read sensors
float dist = measureDistanceCm();
float fillPct = distanceToFillPct(dist);
float gasPct = readGasPct();
// simple mocked temp/humidity (use real sensors in hardware)
float tempMock = 30.0;
float humMock = 60.0;
// Decide alert
bool full = (fillPct >= FULL_THRESHOLD_PCT && fillPct >= 0);
bool smelly = (gasPct >= GAS_THRESHOLD_PCT);
if (full || smelly) digitalWrite(ALERT_LED, HIGH); else digitalWrite(ALERT_LED, LOW);
// Publish periodically
if (millis() - lastPublish >= PUBLISH_INTERVAL_MS) {
lastPublish = millis();
publishTelemetry(fillPct < 0 ? 0 : fillPct, dist, gasPct, tempMock, humMock);
}
delay(200); // short pause
}