#include <Wire.h>
#include <LiquidCrystal_I2C.h>
#include "RTClib.h"
#include "HX711.h"
#include <WiFi.h>
#include <PubSubClient.h>
#include <ESP32Servo.h>
LiquidCrystal_I2C lcd(0x27, 20, 4);
RTC_DS1307 rtc;
int status = WL_IDLE_STATUS;
#define WIFI_SSD "Wokwi-GUEST"
#define WIFI_PASSWORD ""
const char* mqtt_server = "broker.hivemq.com";
const char* topic_publish_ph = "TA1/publish/nilaiPH";
const char* topic_subscribe_rataph = "TA1/subscribe/rataPH";
const char* mqtt_client_name = "ESP32_client";
WiFiClient espClient;
PubSubClient client(espClient);
//pengaturan pin push button
#define pin_pb_on 4
#define pin_pb_dolomit 2
#define pin_pb_kuras 15
//pengaturan pin LED
#define led_merah 26
#define led_hijau 27
#define led_biru 14
//pengaturan pin Sensor pH
#define adc_ph 34
float rataPH = 0;
float rataPHs[3];
int currentIndex = 0;
//pengaturan pin water level
#define pin_wl_atas 32
#define pin_wl_bawah 35
//pengaturan pin relay
#define relay_kuras 19
#define relay_isi 18
#define relay_motor 5
//pengaturan pin servo
Servo s1;
Servo s2;
#define servo1 12
#define servo2 13
//pengaturan pin loadcell
#define LOADCELL_DOUT_PIN 25
#define LOADCELL_SCK_PIN 33
HX711 scale;
float calibration_factor = -1365;
//variabel berat
bool stat_berat = false;
int aturBerat = 100;
//variable status Dolomit
bool stat_perluDolomit = false;
int stat1 = 0;
//variable servo
bool stat_servoTank = false;
bool stat_servoTuang = false;
//variable status Ganti Air
bool stat_perluGantiAir = false;
bool btsAtas = false;
bool btsBawah = false;
bool stat_valveKuras = false;
bool stat_valveIsi = false;
int stat2 = 0;
//variable status RTC
bool checkedToday1 = false;
bool checkedToday2 = false;
//pengecekan koneksi wifi
void connect_wifi(){
Serial.println("Connecting to AP...");
WiFi.begin(WIFI_SSD, WIFI_PASSWORD);
while (WiFi.status() != WL_CONNECTED){
Serial.print(".");
delay(500);
}
Serial.println("");
Serial.println("Connected to AP...");
}
//pengaturan subscribe ke topik MQTT
void reconnect(){
while(!client.connected()){
Serial.println("Connecting to MQTT...");
if (client.connect(mqtt_client_name)){
Serial.println("Terhubung");
client.subscribe(topic_subscribe_rataph);
}
else{
Serial.print("gagal, rc=");
Serial.print(client.state());
Serial.println("Mencoba lagi dalam 5 detik");
delay(5000);
}
}
}
//pengaturan pesan masuk (callback) dari topik yang di subscribe
void callback(char* topic, byte* payload, unsigned int length){
String topik = (String(topic));
Serial.println(topik);
String topik1 = "TA1/subscribe/rataPH";
if (topik == topik1){
String input1 = "";
for (int i = 0; i < length; i++) {
input1 = input1 + (char)payload[i];
Serial.println((char)payload[i]);
}
rataPH = input1.toFloat();
Serial.println(input1);
Serial.println(rataPH);
}
}
//pengaturan logika nyala LED indikator
void kontrolLED(int x){
if (x < 6){
digitalWrite(led_merah, HIGH);
} else{
digitalWrite(led_merah, LOW);
}
if (x >= 6 && x < 9){
digitalWrite(led_hijau, HIGH);
} else{
digitalWrite(led_hijau, LOW);
}
if (x > 9){
digitalWrite(led_biru, HIGH);
} else{
digitalWrite(led_biru, LOW);
}
}
void setup() {
Serial.begin(115200);
//setup WiFi dan MQTT
connect_wifi();
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
reconnect();
Serial.println("Sistem siap terhubung ke MQTT Broker");
//pengaturan modepin
pinMode(pin_pb_on, INPUT_PULLUP);
pinMode(pin_pb_dolomit, INPUT_PULLUP);
pinMode(pin_pb_kuras, INPUT_PULLUP);
pinMode(led_merah, OUTPUT);
pinMode(led_hijau, OUTPUT);
pinMode(led_biru, OUTPUT);
pinMode(adc_ph, INPUT);
pinMode(pin_wl_atas, INPUT_PULLUP);
pinMode(pin_wl_bawah, INPUT_PULLUP);
pinMode(relay_kuras, OUTPUT);
pinMode(relay_isi, OUTPUT);
pinMode(relay_motor, OUTPUT);
//pengaturan pin servo
s1.attach(servo1);
s2.attach(servo2);
//pengaturan awal lcd
lcd.begin(20,4);
lcd.init ();
lcd.backlight ();
//pengaturan awal RTC
while (!Serial);
Serial.begin(57600);
if (! rtc.begin()) {
Serial.println("Couldn't find RTC");
while (1);
}
if (! rtc.isrunning()) {
Serial.println("RTC is NOT running!");
// following line sets the RTC to the date & time this sketch was compiled
// rtc.adjust(DateTime(F(__DATE__), F(__TIME__)));
// This line sets the RTC with an explicit date & time, for example to set
// November 20, 2023 at 3am you would call:
// rtc.adjust(DateTime(2023, 11, 20, 15, 20, 30));
}
//pengaturan HX711 Load Cell
scale.begin(LOADCELL_DOUT_PIN, LOADCELL_SCK_PIN);
scale.set_scale();
scale.tare();
long zero_factor = scale.read_average();
rataPH = 7;
digitalWrite(relay_kuras, LOW);
digitalWrite(relay_isi, LOW);
digitalWrite(relay_motor, LOW);
}
void loop() {
//pengaturan RTC
DateTime now = rtc.now();
//Input nilai
float nilaiPH = analogRead(adc_ph);
//mengirimkan nilai ph ke topik topic_publish_ph setiap jam berdasarkan kondisional RTC
static int lastSentHour = -1;
if (now.minute() == 0 && now.hour() != lastSentHour){
String nilaiPHString = String(nilaiPH,2);
if (client.connected()){
client.publish(topic_publish_ph, nilaiPHString.c_str());
Serial.println("Mengirim nilai pH ke " + String(topic_publish_ph) + ": " + nilaiPHString);
lastSentHour = now.hour();
}else {
Serial.println("Koneksi MQTT hilang saat mencoba mengirimkan data.");
}
Serial.print("LastSentHour = ");
Serial.println(lastSentHour);
}
// Reset checkedToday setiap hari setelah pukul 16:10
if (now.hour() == 16 && now.minute() == 11) {
checkedToday1 = false;
checkedToday2 = false;
}
static bool checkedToday2 = false;
if (now.hour() == 16 && now.minute() == 10 && checkedToday2 == false) {
if (rataPH < 6){
stat_perluDolomit = true;
}
}
// Memasukkan nilai rataPH ke dalam array rataPHs pada pukul 16:10 setiap hari
static bool checkedToday1 = false;
if (now.hour() == 16 && now.minute() == 10 && checkedToday1 == false) {
rataPHs[currentIndex] = rataPH;
currentIndex = (currentIndex + 1) % 3;
checkedToday1 = true;
bool allNonZero = true;
for (int i = 0; i < 3; i++) {
if (rataPHs[i] == 0) {
allNonZero = false;
break;
}
}
if (allNonZero) {
stat_perluGantiAir = (rataPHs[0] < 6 && rataPHs[1] < 6 && rataPHs[2] < 6);
if (stat_perluGantiAir){
Serial.println("Status x aktif karena semua data < 1500");
currentIndex = 0;
memset(rataPHs, 0, sizeof(rataPHs)); // Reset semua data array ke 0
return; // Keluar dari loop untuk mencegah penambahan currentIndex
}
}
Serial.print("Status X = ");
Serial.println(stat_perluGantiAir);
Serial.print("checkedToday1 = ");
Serial.println(checkedToday1);
}
//===================================================================================
//pengaturan variable proses penambahan dolomit
//pengukuran berat dolomit
unsigned int weight = scale.get_units();
float berat = float(weight)/2100.00*5000.00;
if (berat == aturBerat){
stat_berat = true;
} else{
stat_berat = false;
}
if(stat_perluDolomit){
stat1 = 1;
}
//pengaturan servo domolit tank
if (stat1 == 1 && stat_berat == false){
stat_servoTank = true;
} else if (stat1 == 1 && stat_berat == true) {
stat_servoTank = false;
stat1 = 2;
}
//pengaturan servo penuang dolomit
if (stat1 == 2 && berat != 0 && stat_servoTank == false){
stat_servoTuang = true;
} else if (stat1 == 2 && berat == 0 && stat_servoTank == false){
stat_servoTuang = false;
stat1 = 3;
}
//kondisional kembali semula yaitu stat_perluDolomit = false;
if (stat_perluDolomit && stat1 == 3){
stat_perluDolomit = false;
stat1 = 0;
}
//implementasi
if (stat_servoTank){
digitalWrite(relay_motor, HIGH);
s1.write(90);
} else{
digitalWrite(relay_motor, LOW);
s1.write(0);
}
if (stat_servoTuang){
s2.write(180);
} else{
s2.write(0);
}
//===================================================================================
//pengaturan variable proses pergantian air kolam
if(stat_perluGantiAir){
stat2 = 1;
}
// waterAtas
if(digitalRead(pin_wl_atas) == LOW){
btsAtas = true;
}
else{
btsAtas = false;
}
// waterBawah
if(digitalRead(pin_wl_bawah) == LOW){
btsBawah = true;
}
else{
btsBawah = false;
}
//pengaturan valve kuras
if (stat2 == 1 && btsBawah){
stat_valveKuras = true;
} else if (stat2 == 1 && ~btsBawah){
stat_valveKuras = false;
stat2 = 2;
}
//pengaturan valve isi
if (stat2 == 2 && (~btsAtas || ~btsBawah)){
stat_valveIsi = true;
} else if(stat2 == 2 && (btsAtas && btsBawah)){
stat_valveIsi = false;
stat2 = 3;
}
//kondisional kembali semula yaitu stat_perluGantiAir = false;
if(stat_perluGantiAir == true && status == 3){
stat_perluGantiAir = false;
status = 0;
}
//implementasi
digitalWrite(relay_kuras, stat_valveKuras);
digitalWrite(relay_isi, stat_valveIsi);
}