/**
* esp32_server.ino
* Smart Gardening System - ESP32 Controller with MQTT Communication
*
* This code connects the ESP32 to Wi-Fi ("Wokwi-GUEST") and bridges telemetry
* and control to the Web Dashboard via a public MQTT broker (broker.hivemq.com)
* on port 1883.
*/
#include <WiFi.h>
#include <PubSubClient.h>
// Wi-Fi Credentials for Wokwi simulation
const char* ssid = "Wokwi-GUEST";
const char* password = "";
// MQTT Broker Credentials
const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;
// Unique MQTT topics based on conversation ID to avoid collisions
const char* topic_moisture = "smart_gardening/495d5611/moisture";
const char* topic_pump_state = "smart_gardening/495d5611/pump_state";
const char* topic_pump_cmd = "smart_gardening/495d5611/pump_cmd";
// Hardware Pins
#define MOISTURE_PIN 34 // Analog Input (ADC1)
#define RELAY_PIN 26 // Digital Output for Relay
WiFiClient espClient;
PubSubClient client(espClient);
// Global state variables
int moisturePct = 0;
int pumpState = 0;
unsigned long lastReadTime = 0;
const unsigned long readInterval = 2000; // Read and publish moisture every 2 seconds
/**
* Handles incoming MQTT messages from the Web Dashboard
*/
void mqttCallback(char* topic, byte* payload, unsigned int length) {
String message = "";
for (unsigned int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.print("[MQTT Callback] Message arrived on topic [");
Serial.print(topic);
Serial.print("]: ");
Serial.println(message);
// Check if topic matches the pump command topic
if (String(topic) == topic_pump_cmd) {
if (message == "1" || message == "ON") {
pumpState = 1;
digitalWrite(RELAY_PIN, HIGH); // Turn pump ON (Active High)
Serial.println("[CONTROL] Pump turned ON (Relay HIGH)");
// Publish confirmation state
client.publish(topic_pump_state, "1", true);
} else if (message == "0" || message == "OFF") {
pumpState = 0;
digitalWrite(RELAY_PIN, LOW); // Turn pump OFF (Relay LOW)
Serial.println("[CONTROL] Pump turned OFF (Relay LOW)");
// Publish confirmation state
client.publish(topic_pump_state, "0", true);
}
}
}
/**
* Reconnects to the MQTT broker if the connection is dropped
*/
void reconnectMQTT() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
// Generate a unique random MQTT client ID
String clientId = "ESP32_SmartGardening_";
clientId += String(random(0xffff), HEX);
// Attempt to connect
if (client.connect(clientId.c_str())) {
Serial.println("Connected to MQTT Broker!");
// Subscribe to command topic
client.subscribe(topic_pump_cmd);
Serial.print("Subscribed to: ");
Serial.println(topic_pump_cmd);
// Publish current status immediately upon connection
client.publish(topic_pump_state, pumpState ? "1" : "0", true);
} else {
Serial.print("Failed to connect, state=");
Serial.print(client.state());
Serial.println(" - Retrying in 5 seconds...");
delay(5000);
}
}
}
void setup() {
// Initialize Serial Monitor
Serial.begin(115200);
Serial.println("\n===========================================");
Serial.println("ESP32 Smart Gardening Controller Starting (MQTT)");
Serial.println("===========================================");
// Pin Configuration
pinMode(MOISTURE_PIN, INPUT);
pinMode(RELAY_PIN, OUTPUT);
digitalWrite(RELAY_PIN, LOW); // Default pump OFF
// Connect to Wi-Fi
Serial.print("Connecting to Wi-Fi SSID: ");
Serial.println(ssid);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWi-Fi Connected successfully!");
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
// Setup MQTT client
client.setServer(mqtt_server, mqtt_port);
client.setCallback(mqttCallback);
}
void loop() {
// Ensure we are connected to the MQTT broker
if (!client.connected()) {
reconnectMQTT();
}
client.loop();
// Periodically read sensor and publish to broker
unsigned long currentTime = millis();
if (currentTime - lastReadTime >= readInterval) {
lastReadTime = currentTime;
// Read 12-bit analog value (0 - 4095)
int rawVal = analogRead(MOISTURE_PIN);
// Map to percentage: 0 = Dry (0%), 4095 = Wet (100%)
moisturePct = map(rawVal, 0, 4095, 0, 100);
moisturePct = constrain(moisturePct, 0, 100);
// Convert moisture to string and publish
String moistureStr = String(moisturePct);
client.publish(topic_moisture, moistureStr.c_str(), true);
// Print local debug
Serial.print("[STATUS] Moisture: ");
Serial.print(moisturePct);
Serial.print("% (Raw: ");
Serial.print(rawVal);
Serial.print("), Pump: ");
Serial.println(pumpState ? "ON" : "OFF");
}
}