#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <Adafruit_BMP085.h>
#define DHTPIN 4 // Digital GPIO4 (DHT22)
#define DHTTYPE DHT22
#define ONE_WIRE_BUS 18 // DS18B20 Soil Temperature
#define SOIL_MOIST_PIN 35 // Potentiometer simulating Soil Moisture
#define MQ2_PIN 34 // MQ-2 Gas Sensor Analog Pin
#define RELAY_PUMP_PIN 26 // Water Pump Relay Output
// Calibration values for Soil Moisture mapping
#define DRY_SOIL_RAW 3200
#define WET_SOIL_RAW 1400
// Wi-Fi & MQTT Credentials
const char* ssid = "Wokwi-GUEST";
const char* password = "";
const char* mqtt_server = "broker.hivemq.com";
const int mqtt_port = 1883;
// MQTT Topics
const char* topic_telemetry = "wokwi/agri/telemetry";
const char* topic_control = "wokwi/agri/control";
DHT dht(DHTPIN, DHTTYPE);
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature ds18b20(&oneWire);
Adafruit_BMP085 bmp;
LiquidCrystal_I2C lcd(0x27, 20, 4);
WiFiClient espClient;
PubSubClient client(espClient);
// Global State Variables
bool manualPumpOverride = false;
bool pumpState = false;
float airTemp = 0.0, airHum = 0.0, soilTemp = 0.0, pressure = 0.0;
int soilMoist = 0, gasRaw = 0;
unsigned long lastSensorReadTime = 0;
unsigned long lastFastSensorReadTime = 0;
unsigned long lastLCDUpdateTime = 0;
const unsigned long SENSOR_INTERVAL = 1000; // Read environment sensors (DHT22/BMP) every 1s
const unsigned long FAST_SENSOR_INTERVAL = 50; // Read Potentiometer & Gas ADC every 50ms for instant response
const unsigned long LCD_INTERVAL = 100; // Refresh LCD 10 times per second (100ms) for real-time display
void updateLCD(); // Forward declaration for instant trigger
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 Received [");
Serial.print(topic);
Serial.print("]: ");
Serial.println(message);
if (message == "PUMP_ON") {
manualPumpOverride = true;
pumpState = true;
digitalWrite(RELAY_PUMP_PIN, HIGH);
Serial.println("Pump manually forced ON");
} else if (message == "PUMP_OFF") {
manualPumpOverride = true;
pumpState = false;
digitalWrite(RELAY_PUMP_PIN, LOW);
Serial.println("Pump manually forced OFF");
} else if (message == "PUMP_AUTO") {
manualPumpOverride = false;
Serial.println("Pump returned to AUTO Mode");
}
// INSTANT LCD UPDATE on MQTT command reception (0ms delay)
updateLCD();
}
void reconnectMQTT() {
while (!client.connected()) {
Serial.print("Connecting to HiveMQ Broker...");
String clientId = "ESP32AgriHub-" + String(random(0, 0xffff), HEX);
if (client.connect(clientId.c_str())) {
Serial.println(" Connected!");
client.subscribe(topic_control);
client.publish(topic_telemetry, "{\"status\":\"ONLINE\"}");
} else {
Serial.print(" Failed (rc=");
Serial.print(client.state());
Serial.println("). Retrying in 2 seconds...");
vTaskDelay(2000 / portTICK_PERIOD_MS);
}
}
}
// Ultra-fast reading for interactive analog sensors (potentiometer & gas)
void readFastSensors() {
// Read Soil Moisture
int rawSoil = analogRead(SOIL_MOIST_PIN);
soilMoist = map(rawSoil, DRY_SOIL_RAW, WET_SOIL_RAW, 0, 100);
soilMoist = constrain(soilMoist, 0, 100);
// Read MQ-2 Gas
gasRaw = analogRead(MQ2_PIN);
// Auto Irrigation Logic
if (!manualPumpOverride) {
if (soilMoist < 30) {
digitalWrite(RELAY_PUMP_PIN, HIGH);
pumpState = true;
} else {
digitalWrite(RELAY_PUMP_PIN, LOW);
pumpState = false;
}
}
}
// Periodic reading for environment sensors & MQTT publishing
void readSlowSensors() {
// Read DHT22
float t = dht.readTemperature();
float h = dht.readHumidity();
if (!isnan(t)) airTemp = t;
if (!isnan(h)) airHum = h;
// Read DS18B20 Soil Temp (Non-blocking)
soilTemp = ds18b20.getTempCByIndex(0);
ds18b20.requestTemperatures(); // Trigger next conversion asynchronously
// Read Barometric Pressure
pressure = bmp.readPressure() / 100.0F; // Pa to hPa
// Publish JSON Payload to MQTT
String payload = "{";
payload += "\"airTemp\":" + String(airTemp, 1) + ",";
payload += "\"airHum\":" + String(airHum, 1) + ",";
payload += "\"soilTemp\":" + String(soilTemp, 1) + ",";
payload += "\"soilMoist\":" + String(soilMoist) + ",";
payload += "\"pressure\":" + String(pressure, 1) + ",";
payload += "\"gasRaw\":" + String(gasRaw) + ",";
payload += "\"pump\":" + String(pumpState ? "true" : "false") + ",";
payload += "\"mode\":\"" + String(manualPumpOverride ? "MANUAL" : "AUTO") + "\"";
payload += "}";
client.publish(topic_telemetry, payload.c_str());
}
void updateLCD() {
lcd.setCursor(0, 0);
lcd.print("Air: ");
lcd.print(airTemp, 1);
lcd.print((char)223);
lcd.print("C Hum:");
lcd.print((int)airHum);
lcd.print("% ");
lcd.setCursor(0, 1);
lcd.print("Soil:");
lcd.print(soilTemp, 1);
lcd.print((char)223);
lcd.print("C Moist:");
lcd.print(soilMoist);
lcd.print("% ");
lcd.setCursor(0, 2);
lcd.print("Pres:");
lcd.print((int)pressure);
lcd.print("hPa Gas:");
lcd.print(gasRaw);
lcd.print(" ");
lcd.setCursor(0, 3);
lcd.print("Pump:");
lcd.print(pumpState ? "ON " : "OFF");
lcd.print(" Mode:");
lcd.print(manualPumpOverride ? "MANUAL " : "AUTO ");
}
void setup() {
Serial.begin(115200);
pinMode(RELAY_PUMP_PIN, OUTPUT);
digitalWrite(RELAY_PUMP_PIN, LOW);
// Initialize LCD
lcd.init();
lcd.backlight();
lcd.setCursor(0, 0);
lcd.print("AGRI TECH HUB V2.0");
lcd.setCursor(0, 1);
lcd.print("Connecting WiFi...");
// Initialize Sensors
dht.begin();
ds18b20.begin();
// CRITICAL FIX: Disable blocking delay on DS18B20 reads!
ds18b20.setWaitForConversion(false);
ds18b20.requestTemperatures(); // Start initial conversion
if (!bmp.begin()) {
Serial.println("BMP180 Sensor Error!");
}
// Connect Wi-Fi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
vTaskDelay(250 / portTICK_PERIOD_MS);
Serial.print(".");
}
Serial.println("\nWiFi Connected!");
lcd.setCursor(0, 2);
lcd.print("WiFi Connected!");
lcd.setCursor(0, 3);
lcd.print("Connecting MQTT...");
client.setServer(mqtt_server, mqtt_port);
client.setCallback(mqttCallback);
reconnectMQTT();
lcd.clear();
}
void loop() {
if (!client.connected()) {
reconnectMQTT();
}
client.loop();
unsigned long currentMillis = millis();
// 1. Instant 50ms read for potentiometer & gas slider interactions
if (currentMillis - lastFastSensorReadTime >= FAST_SENSOR_INTERVAL) {
lastFastSensorReadTime = currentMillis;
readFastSensors();
}
// 2. 1000ms read for environment sensors & MQTT publishing
if (currentMillis - lastSensorReadTime >= SENSOR_INTERVAL) {
lastSensorReadTime = currentMillis;
readSlowSensors();
}
// 3. Super-fast 100ms LCD refresh rate (10 FPS)
if (currentMillis - lastLCDUpdateTime >= LCD_INTERVAL) {
lastLCDUpdateTime = currentMillis;
updateLCD();
}
// Yield time back to Wokwi WebAssembly Engine
vTaskDelay(5 / portTICK_PERIOD_MS);
}Loading
bmp180
bmp180