#include <WiFi.h>
#include <PubSubClient.h>
#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include <DHT.h>
#include <HardwareSerial.h>
// Pin Definitions
#define DHTPIN 4 // GPIO4 connected to DHT22
#define DHTTYPE DHT22 // Specify the sensor type
#define LED_PIN 25 // GPIO25 for LED (Simulating watering)
#define POT_PIN 34 // GPIO34 connected to potentiometer wiper (simulating soil moisture sensor)
#define NPK_RX_PIN 16 // GPIO16 for NPK sensor RX
#define NPK_TX_PIN 17 // GPIO17 for NPK sensor TX
// Create DHT and LCD objects
DHT dht(DHTPIN, DHTTYPE);
LiquidCrystal_I2C lcd(0x27, 16, 2); // 0x27 is the I2C address, 16x2 LCD screen
// Create HardwareSerial object for NPK communication
HardwareSerial npkSerial(1);
// MQTT Configuration
const char* ssid = "Wokwi-GUEST"; // Replace with your WiFi SSID
const char* password = ""; // Replace with your WiFi password
const char* mqttServer = "broker.hivemq.com"; // MQTT Broker URL
const int mqttPort = 1883; // MQTT Broker Port
const char* temperatureTopic = "agriculture/temperature";
const char* humidityTopic = "agriculture/humidity";
const char* moistureTopic = "agriculture/moisture";
const char* wateringTopic = "agriculture/watering";
const char* npkTopic = "agriculture/npk";
const char* nitrogenTopic = "agriculture/nitrogen";
const char* phosphorusTopic = "agriculture/phosphorus";
const char* potassiumTopic = "agriculture/potassium";
WiFiClient wifiClient;
PubSubClient client(wifiClient);
// Variables for NPK sensor data
uint8_t nitrogen = 0;
uint8_t phosphorus = 0;
uint8_t potassium = 0;
// Variables for scrolling and watering simulation
unsigned long previousMillis = 0;
unsigned long scrollInterval = 5000; // Interval for text scroll (in milliseconds) set to 5 seconds
unsigned long wateringMillis = 0; // Track watering duration
bool watering = false;
String tempStr = "";
String humidityStr = "";
String moistureStr = "";
String wateringStr = "";
unsigned long lastPublishTime = 0;
unsigned long publishInterval = 10000; // Publish every 10 seconds
void setup() {
// Initialize Serial Monitor
Serial.begin(115200);
Serial.println("Starting DHT22, Soil Moisture, NPK Sensor, and LCD Test");
// Initialize WiFi
WiFi.begin(ssid, password);
unsigned long startAttemptTime = millis();
while (WiFi.status() != WL_CONNECTED) {
if (millis() - startAttemptTime > 10000) { // 10 seconds timeout
Serial.println("Failed to connect to WiFi, restarting...");
ESP.restart(); // Restart the ESP32
}
delay(500);
Serial.print(".");
}
Serial.println("Connected to WiFi");
// Set up MQTT
client.setServer(mqttServer, mqttPort);
// Initialize NPK sensor communication
npkSerial.begin(15200, SERIAL_8N1, NPK_RX_PIN, NPK_TX_PIN);
// Initialize the DHT sensor
dht.begin();
// Initialize the LED pin
pinMode(LED_PIN, OUTPUT);
digitalWrite(LED_PIN, LOW); // LED off initially
// Initialize potentiometer pin
pinMode(POT_PIN, INPUT);
// Initialize LCD
lcd.init(); // Initialize the LCD
lcd.backlight(); // Turn on the LCD backlight
// Initial message on LCD
lcd.setCursor(0, 0); // Start at top left
lcd.print("Starting...");
delay(2000); // Delay for startup
lcd.clear(); // Clear the screen after startup
}
uint8_t getNPKValue(uint8_t command) {
uint8_t response = 0xFF; // Default error value
npkSerial.write(command); // Send the command
delay(100); // Wait for response
if (npkSerial.available()) {
response = npkSerial.read(); // Read the response
}
return response;
}
void loop() {
unsigned long currentMillis = millis();
// Ensure MQTT connection is alive
if (!client.connected()) {
reconnectMQTT();
}
client.loop(); // Keeps the connection to the broker alive
// Read temperature and humidity
float temperature = dht.readTemperature();
float humidity = dht.readHumidity();
// Read soil moisture (potentiometer value)
int moistureValue = analogRead(POT_PIN); // Values between 0 and 4095
// Read NPK sensor data
nitrogen = getNPKValue(1); // Request Nitrogen value
phosphorus = getNPKValue(3); // Request Phosphorus value
potassium = getNPKValue(5); // Request Potassium value
// Check if the reading failed
if (isnan(temperature) || isnan(humidity)) {
lcd.clear();
lcd.print("Sensor Error!");
Serial.println("Sensor Error!"); // Print to serial monitor as well
delay(2000); // Wait before retrying
return;
}
// Update temperature, humidity, and moisture strings
tempStr = "Temp: " + String(temperature) + " C";
humidityStr = "Humidity: " + String(humidity) + "%";
moistureStr = "Moisture: " + String(moistureValue); // Soil moisture value from potentiometer
wateringStr = "Watering: " + String(watering ? "On" : "Off");
// Control LED based on temperature or moisture condition
if ((temperature > 30 || moistureValue < 2000) && !watering) { // If temperature > 30 or moisture < 1000
digitalWrite(LED_PIN, HIGH); // Turn on LED (simulating watering)
watering = true; // Mark watering as active
wateringMillis = millis(); // Track watering start time
Serial.println("Watering On"); // Print to serial monitor
}
// Check if watering duration has exceeded 15 seconds
if (watering && millis() - wateringMillis >= 15000) { // Water for 15 seconds
digitalWrite(LED_PIN, LOW); // Turn off LED
watering = false; // Mark watering as inactive
Serial.println("Watering Off"); // Print to serial monitor
}
// Print the same information to Serial Monitor
Serial.print("Temperature: ");
Serial.print(temperature);
Serial.print(" C, Humidity: ");
Serial.print(humidity);
Serial.print(" %, Moisture: ");
Serial.print(moistureValue);
Serial.print(", Watering: ");
Serial.print(watering ? "On" : "Off");
Serial.print(", NPK values: ");
Serial.print("N=" + String(nitrogen) + " ");
Serial.print("P=" + String(phosphorus) + " ");
Serial.println("K=" + String(potassium));
// Check if any NPK value is below 30 and display "Add NPK fertilizer" message
if (nitrogen < 30 || phosphorus < 30 || potassium < 30) {
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Add NPK fertilizer");
Serial.println("Add NPK fertilizer"); // Display message in serial monitor
delay(3000); // Display message for 3 seconds
}
// Publish data to MQTT topics (limit publish to every 10 seconds)
if (currentMillis - lastPublishTime >= publishInterval) {
lastPublishTime = currentMillis;
client.publish(temperatureTopic, String(temperature).c_str());
client.publish(humidityTopic, String(humidity).c_str());
client.publish(moistureTopic, String(moistureValue).c_str());
client.publish(wateringTopic, watering ? "true" : "false"); // Using true/false for clarity
// Publish NPK values as separate messages
client.publish(nitrogenTopic, String(nitrogen).c_str());
client.publish(phosphorusTopic, String(phosphorus).c_str());
client.publish(potassiumTopic, String(potassium).c_str());
}
// Scroll the values every 5 seconds on the LCD
if (millis() - previousMillis >= scrollInterval) {
previousMillis = millis();
// Show temperature, humidity, soil moisture, and watering status with some delay
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(tempStr); // Show temperature at the top of screen
delay(3000); // Show temperature for 3 seconds
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(humidityStr); // Show humidity
delay(3000); // Show humidity for 3 seconds
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(moistureStr); // Show moisture level
delay(3000); // Show moisture for 3 seconds
lcd.clear();
lcd.setCursor(0, 0);
lcd.print(wateringStr); // Show watering status
delay(3000); // Show watering status for 3 seconds
// Display NPK values one below the other
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("N: " + String(nitrogen));
lcd.setCursor(0, 1);
lcd.print("P: " + String(phosphorus));
delay(3000); // Show NPK values for 3 seconds
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("K: " + String(potassium)); // Show Potassium value
delay(3000); // Show Potassium for 3 seconds
// Add a longer delay (500ms) to give a smoother transition between data displays
delay(500); // Adjust this delay as needed for smoother transition between screens
}
}
void reconnectMQTT() {
while (!client.connected()) {
Serial.print("Attempting MQTT connection...");
if (client.connect("ESP32_Client")) {
Serial.println("connected");
client.subscribe(temperatureTopic);
client.subscribe(humidityTopic);
client.subscribe(moistureTopic);
client.subscribe(wateringTopic);
client.subscribe(npkTopic);
} else {
Serial.print("failed, rc=");
Serial.print(client.state());
Serial.println(" try again in 5 seconds");
delay(5000);
}
}
}