#include <ESP32Servo.h>
#include <LiquidCrystal_I2C.h>
#include <WiFi.h>
#include "RTClib.h"
#include "DHTesp.h"
#include "PubSubClient.h"
#include <freertos/FreeRTOS.h>
#include <freertos/semphr.h>
#define DHT22_PIN 15
#define SERVO_PIN 14
#define BUZZER_PIN 17
#define ULTRASONIC_TRIG_PIN 5
#define ULTRASONIC_ECHO_PIN 18
#define LED_GREEN_PIN 25
#define LED_YELLOW_PIN 26
#define LED_RED_PIN 27
#define LED_RGB_RED_PIN 12
#define LED_RGB_GREEN_PIN 32
#define LED_RGB_BLUE_PIN 33
#define MAX_TEMPERATURE 60.0
#define MIN_HUMIDITY 25.0
const char* WIFI_NAME = "Wokwi-GUEST"; //WiFi SSID
const char* WIFI_PASSWORD = ""; //WiFI Password
const char* MQTT_SERVER = "test.mosquitto.org"; //MQTT Server
const int MQTT_PORT = 1883; //MQTT Port
//schedule
bool hour[24];
bool modeFromNodeRed = false;
bool mode = false; //false: automatic, true: schedule
bool wateringDay[7];
int wateringHour[3];
int duration;
int minHumidity;
int maxHumidity;
bool isWateringAuto = false;
Servo myServo;
int currentAngle = 0; //0 is off, 90 is on
bool servoState = false; //false is off, true is on
LiquidCrystal_I2C lcd(0x27, 20, 4);
uint8_t degreeSymbol[8] = {0x0E, 0x1F, 0x1F, 0x0E, 0x00, 0x00, 0x00, 0x00};
RTC_DS1307 RTC;
//create an instance of the DHTesp library
DHTesp dhtSensor;
//create a WiFi client object
WiFiClient espClient;
//create a PubSubClient object
PubSubClient client(espClient);
long getDistance(){
digitalWrite(ULTRASONIC_TRIG_PIN, LOW);
delayMicroseconds(2);
digitalWrite(ULTRASONIC_TRIG_PIN, HIGH);
delayMicroseconds(10);
digitalWrite(ULTRASONIC_TRIG_PIN, LOW);
long duration = pulseIn(ULTRASONIC_ECHO_PIN, HIGH);
long distanceCm = duration * 0.034 / 2;
return distanceCm;
}
void warningSign(int countSign){
for(int i=0;i<countSign;i++){
//warn by buzzer
tone(BUZZER_PIN, 1000);
delay(500);
noTone(BUZZER_PIN);
delay(1000);
//warn by LED RGB
analogWrite(LED_RGB_RED_PIN, random(0, 256));
analogWrite(LED_RGB_BLUE_PIN, random(0, 256));
analogWrite(LED_RGB_GREEN_PIN, random(0, 256));
}
}
void waterLevelSign(int distanceCm){
if(distanceCm <= 133){
digitalWrite(LED_GREEN_PIN, HIGH);
digitalWrite(LED_YELLOW_PIN, LOW);
digitalWrite(LED_RED_PIN, LOW);
}else if(distanceCm <= 300){
digitalWrite(LED_YELLOW_PIN, HIGH);
digitalWrite(LED_GREEN_PIN, LOW);
digitalWrite(LED_RED_PIN, LOW);
}else{
digitalWrite(LED_RED_PIN, HIGH);
digitalWrite(LED_GREEN_PIN, LOW);
digitalWrite(LED_YELLOW_PIN, LOW);
int countSign = 3;
if(distanceCm >= 400){
countSign = 5;
}
warningSign(countSign);
//turn of LED RGB
analogWrite(LED_RGB_RED_PIN, LOW);
analogWrite(LED_RGB_BLUE_PIN, LOW);
analogWrite(LED_RGB_GREEN_PIN, LOW);
}
}
void showSymbol(uint8_t index, const char* description) {
lcd.write(index); //display the custom symbol
lcd.print(description); //print the symbol description
}
void showTempAndHumidityOnLCD(TempAndHumidity data) {
// TempAndHumidity data = dhtSensor.getTempAndHumidity();
lcd.clear();
lcd.setCursor(0,0);
//display temperature
lcd.print("Temp: " + String(data.temperature, 2));
showSymbol(0, "C");
lcd.setCursor(0,1);
// Display humidity
lcd.print("Humidity: " + String(data.humidity, 1) + "%");
}
void showRealTime() {
DateTime now = RTC.now();
//--Date
lcd.setCursor(0,2); //Defining positon to write from first row,first column.
lcd.print(now.day());
//lcd.setCursor(4,2);
lcd.print("/");
//lcd.setCursor(5,2);
lcd.print(now.month());
//lcd.setCursor(7,2);
lcd.print("/");
//lcd.setCursor(8,2);
lcd.print(now.year());
//lcd.setCursor(11,2);
//lcd.print("water");
//-----------------
//--Time
lcd.setCursor(11,2);
lcd.print(now.hour());
//lcd.setCursor(2,1);
lcd.print(":");
//lcd.setCursor(3,1);
lcd.print(now.minute());
//lcd.setCursor(5,1);
lcd.print(":");
//lcd.setCursor(6,1);
lcd.print(now.second());
}
void wifiConnect(){
delay(10);
Serial.println();
Serial.print("Connecting to SSID: ");
Serial.println(WIFI_NAME);
//connect to the WiFi network
WiFi.begin(WIFI_NAME, WIFI_PASSWORD);
//print a message if WiFi is not connected
while (WiFi.status() != WL_CONNECTED){
delay(500);
Serial.println("Wifi not connected");
}
Serial.println("Wifi connected !"); //print a message if WiFi is connected
Serial.println("Local IP: " + String(WiFi.localIP())); //print the local IP address
// //WiFi.mode(WIFI_STA); //set the WiFi mode to station mode
}
void mqttReconnect(){
while(!client.connected()){
Serial.print("Attempting MQTT connection...");
if(client.connect("22127146")){
Serial.print("Connected to Node-RED by MQTT\n");
client.subscribe("arduino/sensor"); //make sure arduino to subscribe true topic
client.subscribe("arduino/servo"); //make sure arduino to subscribe true topic
client.subscribe("arduino/schedule");
client.subscribe("arduino/automatic");
}else{
Serial.print("Failed when connecting to Node-RED by MQTT, rc=");
Serial.print(client.state());
Serial.println(" Try again in 5 seconds to connect to Node-RED by MQTT");
delay(5000);
}
}
}
void mqttCallback(char* topic, byte* payload, unsigned int length){
Serial.print("Message (from Node-RED by MQTT) arrived [");
Serial.print(topic);
Serial.print("] ");
String message;
for (unsigned int i = 0; i < length; i++) {
message += (char)payload[i];
}
Serial.println(message + "\n");
if (String(topic) == "arduino/servo") {
int servoAngle = message.substring(message.indexOf(":") + 1, message.indexOf("}")).toInt();
if (servoAngle == 90)
{
Serial.println("Turn on watering in command of the web");
turnOnServo();
}
else
{
turnOffServo();
Serial.println("Turn off watering in command of the web");
}
Serial.print("Servo angle set to: ");
Serial.println(servoAngle);
}
if (String(topic) == "arduino/schedule") {
modeFromNodeRed = true;
processMessageForSchedule(message, length);
}
if (String(topic) =="arduino/automatic") {
modeFromNodeRed = false;
}
}
int findIntegerInArray(int arr[], int size, int target) {
for (int i = 0; i < size; i++) {
if (arr[i] == target) {
return i; // Return the index if the integer is found
}
}
return -1; // Return -1 if the integer is not found
}
void processMessageForSchedule(String msg, unsigned int length)
{
int countComma = 0;
String curAtri = "";
bool start = false;
for (int i = 0; i < length; i++)
{
if (msg[i] == ':')
start = true;
if (msg[i] != ',' && start == true && msg[i] != '"' && msg[i] != ':' && msg[i] != '}')
curAtri += msg[i];
if (msg[i] == ',')
{
countComma += 1;
start = false;
if (countComma < 8)
{
if (curAtri == "false")
wateringDay[countComma - 1] = false;
else
wateringDay[countComma - 1] = true;
}
if (countComma < 11)
{
int time = curAtri.toInt();
wateringHour[countComma - 8] = time;
}
if (countComma == 11)
{
int time = curAtri.toInt();
duration = time;
}
if (countComma == 12)
{
int minHu = curAtri.toInt();
minHumidity = minHu;
}
Serial.println(curAtri);
curAtri = "";
}
int maxHu = curAtri.toInt();
maxHumidity = maxHu;
}
Serial.println(wateringDay[0]);
Serial.println(wateringDay[1]);
Serial.println(wateringDay[2]);
Serial.println(wateringDay[3]);
Serial.println(wateringDay[4]);
Serial.println(wateringDay[5]);
Serial.println(wateringDay[6]);
Serial.println(wateringHour[0]);
Serial.println(wateringHour[1]);
Serial.println(wateringHour[2]);
Serial.println(duration);
Serial.println(minHumidity);
Serial.println(maxHumidity);
}
void turnOnServo()
{
if (servoState == false)
{
Serial.println("turn on begins");
for (int i = 0; i <= 90; i++)
{
myServo.write(i);
delay(15);
}
currentAngle = 90;
servoState = true;
}
}
void turnOffServo()
{
if (servoState == true)
{
Serial.println("turn off begins");
for (int i = 90; i >= 0; i--)
{
myServo.write(i);
delay(15);
}
currentAngle = 0;
servoState = false;
}
}
void scheduleWatering()
{
DateTime now = RTC.now();
TempAndHumidity data = dhtSensor.getTempAndHumidity();
int day = now.dayOfTheWeek();
int h = now.hour();
if (day == 0)
day = 6;
else
day = day - 1;
Serial.println("hehe");
if (wateringDay[day] == true && findIntegerInArray(wateringHour, 3, h) != -1
&& data.humidity < maxHumidity && hour[h] == false)
{
hour[h] = true;
Serial.println("Currently watering in schedule mode");
turnOnServo();
delay(duration * 1000);
// turnOffServo();
// Serial.println("Done watering");
}
// else
// {
// //Serial.println("It's time for schedule watering but the humidity is over the max value");
// }
}
void resetWateringInforAfterADay()
{
DateTime now = RTC.now();
if (now.hour() == 23 && now.minute() == 59 && now.second() > 30)
for (int i = 0; i < 24; i++)
hour[i] = false;
}
void automaticWatering()
{
TempAndHumidity data = dhtSensor.getTempAndHumidity();
if (data.temperature > MAX_TEMPERATURE || data.humidity < MIN_HUMIDITY)
{
Serial.println("Currently watering in automatic mode");
turnOnServo();
isWateringAuto = true;
}
if (isWateringAuto == true && data.temperature < MAX_TEMPERATURE && data.humidity > MIN_HUMIDITY)
{
turnOffServo();
Serial.println("Done watering");
isWateringAuto = false;
}
}
void setup() {
// set up ultrasonic
Serial.begin(9600);
pinMode(ULTRASONIC_TRIG_PIN, OUTPUT);
pinMode(ULTRASONIC_ECHO_PIN, INPUT);
// set up LED
pinMode(LED_GREEN_PIN, OUTPUT);
pinMode(LED_YELLOW_PIN, OUTPUT);
pinMode(LED_RED_PIN, OUTPUT);
// set up buzzer
pinMode(BUZZER_PIN, OUTPUT);
// set up servo
myServo.attach(SERVO_PIN);
myServo.write(0);
delay(1000);
//set up the LCD
lcd.init();
//turn on the LCD backlight
lcd.backlight();
lcd.setCursor(0,0);
lcd.setCursor(0,1);
lcd.createChar(0, degreeSymbol);
//set up RTC
RTC.begin();
//set up DTH22 Sensor
dhtSensor.setup(DHT22_PIN, DHTesp::DHT22);
for (int i = 0; i < 24; i++)
hour[i] = false;
//setup wifi
wifiConnect();
client.setServer(MQTT_SERVER, MQTT_PORT);
client.setCallback(mqttCallback);
}
void loop() {
if(!client.connected()){
mqttReconnect();
}
client.loop();
//get water level
int waterDistance = getDistance();
int waterLevel = 400 - waterDistance;
//read temperature and humidity from the DHT22 sensor
TempAndHumidity data = dhtSensor.getTempAndHumidity();
showTempAndHumidityOnLCD(data);
showRealTime();
lcd.setCursor(0,3); //Defining positon to write from first row,first column.
lcd.print("Mode: ");
if (mode == false)
lcd.print("Automatic");
else
lcd.print("Schedule");
//get notification
waterLevelSign(waterDistance);
if(waterDistance >= 400 and data.temperature > MAX_TEMPERATURE and data.humidity < MIN_HUMIDITY){
warningSign(7);
}
if (mode == true)
scheduleWatering();
else
{
automaticWatering();
}
Serial.println("Temp: " + String(data.temperature, 2) + "°C"); //print the temperature value with 2 decimal places
Serial.println("Humidity: " + String(data.humidity, 1) + "%"); //print the humidity value with 1 decimal place
Serial.println("Water Level: " + String(waterLevel) + "cm");
//send data to node-RED by MQTT
char buffer[1000];
sprintf(buffer, "{\"temperature\":%d,\"humidity\":%d, \"waterLevel\":%d}", int(data.temperature), int(data.humidity), waterLevel);
client.publish("arduino/sensor", buffer);
resetWateringInforAfterADay();
Serial.println("--------------------------------------------------------"); //print a separator line
delay(5000);
mode = modeFromNodeRed;
}