687 lines
12 KiB
C++
687 lines
12 KiB
C++
#include <WiFi.h>
|
|
#include <HTTPClient.h>
|
|
#include <WiFiClientSecure.h>
|
|
#include <Wire.h>
|
|
#include "RTClib.h"
|
|
#include "time.h"
|
|
#include <Preferences.h>
|
|
|
|
// =====================================
|
|
// OBJECT
|
|
// =====================================
|
|
Preferences prefs;
|
|
RTC_DS3231 rtc;
|
|
WiFiClientSecure client;
|
|
|
|
// =====================================
|
|
// WIFI
|
|
// =====================================
|
|
const char* ssid = "Galaxy A16 2CF8";
|
|
const char* password = "bismillah";
|
|
|
|
// =====================================
|
|
// FIREBASE
|
|
// =====================================
|
|
String baseURL =
|
|
"https://smartcabai-default-rtdb.firebaseio.com/";
|
|
|
|
// =====================================
|
|
// PIN
|
|
// =====================================
|
|
#define SENSOR_PIN 34
|
|
#define RELAY_AIR 26
|
|
#define RELAY_PESTISIDA 27
|
|
|
|
// =====================================
|
|
// KALIBRASI SENSOR
|
|
// =====================================
|
|
int nilaiKering = 3000;
|
|
int nilaiBasah = 2200;
|
|
|
|
int batasKering = 60;
|
|
|
|
// =====================================
|
|
// VARIABEL SISTEM
|
|
// =====================================
|
|
String mode = "AUTO";
|
|
|
|
String airStatus = "OFF";
|
|
String pestStatus = "OFF";
|
|
|
|
bool pestisidaNyala = false;
|
|
|
|
unsigned long waktuMulaiPestisida = 0;
|
|
unsigned long lastSemprotUnix = 0;
|
|
|
|
unsigned long lastFirebase = 0;
|
|
unsigned long lastGetMode = 0;
|
|
unsigned long lastManualGet = 0;
|
|
|
|
unsigned long firebaseCooldown = 0;
|
|
|
|
bool firebaseError = false;
|
|
|
|
// =====================================
|
|
// CONNECT WIFI
|
|
// =====================================
|
|
void connectWiFi() {
|
|
|
|
WiFi.mode(WIFI_STA);
|
|
|
|
WiFi.setSleep(false);
|
|
|
|
WiFi.setAutoReconnect(true);
|
|
|
|
WiFi.persistent(true);
|
|
|
|
WiFi.config(
|
|
INADDR_NONE,
|
|
INADDR_NONE,
|
|
INADDR_NONE,
|
|
IPAddress(8,8,8,8),
|
|
IPAddress(8,8,4,4)
|
|
);
|
|
|
|
WiFi.begin(ssid, password);
|
|
|
|
Serial.print("Connecting WiFi");
|
|
|
|
int timeout = 0;
|
|
|
|
while (WiFi.status() != WL_CONNECTED &&
|
|
timeout < 20) {
|
|
|
|
delay(500);
|
|
|
|
Serial.print(".");
|
|
|
|
timeout++;
|
|
}
|
|
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
|
|
Serial.println("\nWiFi Connected");
|
|
|
|
Serial.print("IP : ");
|
|
|
|
Serial.println(WiFi.localIP());
|
|
|
|
} else {
|
|
|
|
Serial.println("\nOFFLINE MODE");
|
|
}
|
|
}
|
|
|
|
// =====================================
|
|
// FIREBASE GET
|
|
// =====================================
|
|
String getFirebase(String path) {
|
|
|
|
if (WiFi.status() != WL_CONNECTED) {
|
|
return "";
|
|
}
|
|
|
|
HTTPClient http;
|
|
|
|
String url = baseURL + path + ".json";
|
|
|
|
http.begin(client, url);
|
|
|
|
http.setTimeout(10000);
|
|
|
|
int httpCode = http.GET();
|
|
|
|
String hasil = "";
|
|
|
|
if (httpCode > 0) {
|
|
|
|
hasil = http.getString();
|
|
|
|
hasil.replace("\"", "");
|
|
|
|
hasil.trim();
|
|
|
|
} else {
|
|
|
|
firebaseError = true;
|
|
|
|
firebaseCooldown = millis();
|
|
}
|
|
|
|
http.end();
|
|
|
|
delay(100);
|
|
|
|
return hasil;
|
|
}
|
|
|
|
// =====================================
|
|
// FIREBASE PUT
|
|
// =====================================
|
|
// =====================================
|
|
// FIREBASE PUT
|
|
// =====================================
|
|
void putFirebase(String path,
|
|
String value,
|
|
bool isString = false) {
|
|
|
|
if (WiFi.status() != WL_CONNECTED) {
|
|
return;
|
|
}
|
|
|
|
HTTPClient http;
|
|
|
|
String url = baseURL + path + ".json";
|
|
|
|
http.begin(client, url);
|
|
|
|
http.setTimeout(5000);
|
|
|
|
http.addHeader(
|
|
"Content-Type",
|
|
"application/json"
|
|
);
|
|
|
|
String payload;
|
|
|
|
if (isString) {
|
|
|
|
payload = "\"" + value + "\"";
|
|
|
|
} else {
|
|
|
|
payload = value;
|
|
}
|
|
|
|
int httpCode =
|
|
http.PUT(payload);
|
|
|
|
if (httpCode <= 0) {
|
|
|
|
firebaseError = true;
|
|
|
|
firebaseCooldown = millis();
|
|
}
|
|
|
|
http.end();
|
|
}
|
|
// =====================================
|
|
// GET UNIX RTC
|
|
// =====================================
|
|
unsigned long getUnixTimeRTC() {
|
|
|
|
DateTime now = rtc.now();
|
|
|
|
Serial.print("Tanggal RTC : ");
|
|
Serial.print(now.day());
|
|
Serial.print("/");
|
|
Serial.print(now.month());
|
|
Serial.print("/");
|
|
Serial.print(now.year());
|
|
|
|
Serial.print(" ");
|
|
|
|
Serial.print(now.hour());
|
|
Serial.print(":");
|
|
Serial.print(now.minute());
|
|
Serial.print(":");
|
|
Serial.println(now.second());
|
|
|
|
Serial.print("NOW UNIX : ");
|
|
Serial.println(now.unixtime());
|
|
|
|
Serial.print("LAST UNIX : ");
|
|
Serial.println(lastSemprotUnix);
|
|
|
|
return now.unixtime();
|
|
}
|
|
|
|
// =====================================
|
|
// SETUP
|
|
// =====================================
|
|
void setup() {
|
|
|
|
Serial.begin(115200);
|
|
|
|
prefs.begin("spray", false);
|
|
|
|
client.setInsecure();
|
|
|
|
// =================================
|
|
// RELAY
|
|
// =================================
|
|
pinMode(RELAY_AIR, OUTPUT);
|
|
|
|
pinMode(RELAY_PESTISIDA, OUTPUT);
|
|
|
|
digitalWrite(RELAY_AIR, HIGH);
|
|
|
|
digitalWrite(RELAY_PESTISIDA, HIGH);
|
|
|
|
// =================================
|
|
// RTC
|
|
// =================================
|
|
Wire.begin(21, 22);
|
|
|
|
if (!rtc.begin()) {
|
|
|
|
Serial.println("RTC ERROR");
|
|
|
|
while (1);
|
|
}
|
|
|
|
// =================================
|
|
// WIFI
|
|
// =================================
|
|
connectWiFi();
|
|
|
|
// =================================
|
|
// SYNC RTC DARI INTERNET
|
|
// =================================
|
|
configTime(7 * 3600,
|
|
0,
|
|
"pool.ntp.org");
|
|
if (rtc.lostPower()) {
|
|
|
|
struct tm timeinfo;
|
|
|
|
if (getLocalTime(&timeinfo)) {
|
|
|
|
rtc.adjust(DateTime(
|
|
timeinfo.tm_year + 1900,
|
|
timeinfo.tm_mon + 1,
|
|
timeinfo.tm_mday,
|
|
timeinfo.tm_hour,
|
|
timeinfo.tm_min,
|
|
timeinfo.tm_sec
|
|
));
|
|
|
|
Serial.println("RTC SYNC BERHASIL");
|
|
|
|
} else {
|
|
|
|
Serial.println("GAGAL SYNC RTC");
|
|
}
|
|
}
|
|
|
|
// =================================
|
|
// AMBIL DATA LAST SEMPROT
|
|
// =================================
|
|
String lastData =
|
|
getFirebase("last_semprot");
|
|
|
|
if (lastData != "" &&
|
|
lastData != "null" &&
|
|
lastData != "0") {
|
|
|
|
// dari firebase
|
|
unsigned long temp = strtoul(
|
|
lastData.c_str(),
|
|
NULL,
|
|
10
|
|
);
|
|
|
|
if(temp > 0) {
|
|
lastSemprotUnix = temp;
|
|
}
|
|
|
|
// simpan lokal
|
|
prefs.putULong(
|
|
"lastUnix",
|
|
lastSemprotUnix
|
|
);
|
|
|
|
Serial.println(
|
|
"DATA DARI FIREBASE"
|
|
);
|
|
|
|
} else {
|
|
|
|
// dari memori lokal
|
|
lastSemprotUnix =
|
|
prefs.getULong(
|
|
"lastUnix",
|
|
0
|
|
);
|
|
|
|
Serial.println(
|
|
"DATA DARI MEMORI LOKAL"
|
|
);
|
|
|
|
// jika belum ada data
|
|
if (lastSemprotUnix == 0) {
|
|
|
|
lastSemprotUnix =
|
|
rtc.now().unixtime();
|
|
|
|
Serial.print("LAST SEMPROT BARU: ");
|
|
Serial.println(lastSemprotUnix);
|
|
|
|
prefs.putULong(
|
|
"lastUnix",
|
|
lastSemprotUnix
|
|
);
|
|
|
|
Serial.println(
|
|
"DATA BARU DIBUAT"
|
|
);
|
|
}
|
|
}
|
|
|
|
Serial.println("SYSTEM READY");
|
|
}
|
|
|
|
// =====================================
|
|
// LOOP
|
|
// =====================================
|
|
void loop() {
|
|
|
|
if(WiFi.status()!=WL_CONNECTED) {
|
|
WiFi.reconnect();
|
|
}
|
|
|
|
// =================================
|
|
// FIREBASE COOLDOWN
|
|
// =================================
|
|
if (firebaseError &&
|
|
millis() - firebaseCooldown < 10000) {
|
|
|
|
delay(1000);
|
|
|
|
return;
|
|
}
|
|
|
|
firebaseError = false;
|
|
|
|
// =================================
|
|
// STATUS WIFI
|
|
// =================================
|
|
bool wifiConnected =
|
|
(WiFi.status() == WL_CONNECTED);
|
|
|
|
if (!wifiConnected) {
|
|
|
|
mode = "AUTO";
|
|
}
|
|
|
|
// =================================
|
|
// AMBIL MODE DARI FIREBASE
|
|
// =================================
|
|
if (wifiConnected &&
|
|
millis() - lastGetMode >= 10000) {
|
|
|
|
String firebaseMode =
|
|
getFirebase("mode");
|
|
|
|
if (firebaseMode != "") {
|
|
|
|
mode = firebaseMode;
|
|
|
|
mode.trim();
|
|
}
|
|
|
|
lastGetMode = millis();
|
|
}
|
|
|
|
// =================================
|
|
// BACA SENSOR
|
|
// =================================
|
|
int adc = analogRead(SENSOR_PIN);
|
|
|
|
float kelembapan =
|
|
((float)(nilaiKering - adc) /
|
|
(nilaiKering - nilaiBasah))
|
|
* 100;
|
|
|
|
kelembapan =
|
|
constrain(
|
|
kelembapan,
|
|
0,
|
|
100
|
|
);
|
|
|
|
// =================================
|
|
// RTC
|
|
// =================================
|
|
DateTime now = rtc.now();
|
|
|
|
|
|
int jam = now.hour();
|
|
|
|
int menit = now.minute();
|
|
|
|
// =================================
|
|
// HITUNG COUNTDOWN
|
|
// =================================
|
|
unsigned long nowUnix =
|
|
now.unixtime();
|
|
|
|
unsigned long intervalDetik =
|
|
120UL * 3600UL;
|
|
|
|
unsigned long elapsedDetik =
|
|
nowUnix - lastSemprotUnix;
|
|
|
|
long sisaDetik;
|
|
|
|
if (elapsedDetik >= intervalDetik) {
|
|
sisaDetik = 0;
|
|
} else {
|
|
sisaDetik = intervalDetik - elapsedDetik;
|
|
}
|
|
|
|
// =================================
|
|
// FORMAT COUNTDOWN
|
|
// =================================
|
|
int hariSisa = sisaDetik / 86400;
|
|
int jamSisa = (sisaDetik % 86400) / 3600;
|
|
int menitSisa = (sisaDetik % 3600) / 60;
|
|
int detikSisa = sisaDetik % 60;
|
|
|
|
String countdown;
|
|
|
|
if (sisaDetik > 0) {
|
|
countdown =
|
|
String(hariSisa) + " Hari " +
|
|
String(jamSisa) + " Jam " +
|
|
String(menitSisa) + " Menit " +
|
|
String(detikSisa) + " Detik";
|
|
} else {
|
|
countdown = "Siap Semprot";
|
|
}
|
|
|
|
// =================================
|
|
// KIRIM FIREBASE
|
|
// =================================
|
|
if (wifiConnected &&
|
|
millis() - lastFirebase >= 1000) {
|
|
|
|
putFirebase(
|
|
"kelembapan",
|
|
String(kelembapan, 1)
|
|
);
|
|
|
|
putFirebase(
|
|
"jeda_penyemprotan",
|
|
countdown,
|
|
true
|
|
);
|
|
|
|
lastFirebase = millis();
|
|
}
|
|
|
|
// =================================
|
|
// SERIAL MONITOR
|
|
// =================================
|
|
Serial.print("Mode : ");
|
|
|
|
Serial.print(mode);
|
|
|
|
Serial.print(" ADC: ");
|
|
|
|
Serial.print(adc);
|
|
|
|
Serial.print(
|
|
" || Kelembapan : "
|
|
);
|
|
|
|
Serial.print(
|
|
kelembapan,
|
|
1
|
|
);
|
|
|
|
Serial.print("%");
|
|
|
|
Serial.print(
|
|
" ||Jeda Penyemprotan : "
|
|
);
|
|
|
|
Serial.print(countdown);
|
|
Serial.print(" || ");
|
|
|
|
if (wifiConnected) {
|
|
|
|
Serial.println("ONLINE");
|
|
|
|
} else {
|
|
|
|
Serial.println("OFFLINE");
|
|
}
|
|
|
|
// =================================
|
|
// MODE AUTO
|
|
// =================================
|
|
if (mode == "AUTO") {
|
|
|
|
// =============================
|
|
// POMPA AIR
|
|
// =============================
|
|
if (kelembapan <
|
|
batasKering) {
|
|
|
|
digitalWrite(
|
|
RELAY_AIR,
|
|
LOW
|
|
);
|
|
|
|
} else {
|
|
|
|
digitalWrite(
|
|
RELAY_AIR,
|
|
HIGH
|
|
);
|
|
}
|
|
|
|
// =============================
|
|
// PESTISIDA OTOMATIS
|
|
// =============================
|
|
if (sisaDetik <= 0 &&
|
|
!pestisidaNyala &&
|
|
WiFi.status()==WL_CONNECTED) {
|
|
|
|
Serial.println("PESTISIDA MULAI");
|
|
|
|
digitalWrite(
|
|
RELAY_PESTISIDA,
|
|
LOW
|
|
);
|
|
|
|
pestisidaNyala = true;
|
|
|
|
waktuMulaiPestisida =
|
|
millis();
|
|
}
|
|
|
|
// =============================
|
|
// MATIKAN PESTISIDA
|
|
// =============================
|
|
if (pestisidaNyala &&
|
|
millis() -
|
|
waktuMulaiPestisida
|
|
>= 7000) {
|
|
|
|
Serial.println("PESTISIDA SELESAI");
|
|
|
|
digitalWrite(
|
|
RELAY_PESTISIDA,
|
|
HIGH
|
|
);
|
|
|
|
pestisidaNyala = false;
|
|
|
|
lastSemprotUnix =
|
|
rtc.now().unixtime();
|
|
|
|
// simpan firebase
|
|
|
|
putFirebase(
|
|
"last_semprot",
|
|
String(lastSemprotUnix)
|
|
);
|
|
|
|
// simpan lokal
|
|
prefs.putULong(
|
|
"lastUnix",
|
|
lastSemprotUnix
|
|
);
|
|
}
|
|
}
|
|
|
|
// =================================
|
|
// MODE MANUAL
|
|
// =================================
|
|
else if (mode == "MANUAL" &&
|
|
wifiConnected) {
|
|
|
|
// =============================
|
|
// AMBIL STATUS MANUAL
|
|
// =============================
|
|
if (millis() -
|
|
lastManualGet >= 5000) {
|
|
|
|
airStatus =
|
|
getFirebase("pompa_air");
|
|
|
|
pestStatus =
|
|
getFirebase(
|
|
"pompa_pestisida"
|
|
);
|
|
|
|
lastManualGet =
|
|
millis();
|
|
}
|
|
|
|
// =============================
|
|
// POMPA AIR
|
|
// =============================
|
|
if (airStatus == "ON") {
|
|
|
|
digitalWrite(
|
|
RELAY_AIR,
|
|
LOW
|
|
);
|
|
|
|
} else {
|
|
|
|
digitalWrite(
|
|
RELAY_AIR,
|
|
HIGH
|
|
);
|
|
}
|
|
// =============================
|
|
// POMPA PESTISIDA
|
|
// =============================
|
|
if (pestStatus == "ON") {
|
|
digitalWrite(
|
|
RELAY_PESTISIDA,
|
|
LOW
|
|
);
|
|
} else {
|
|
digitalWrite(
|
|
RELAY_PESTISIDA,
|
|
HIGH
|
|
);
|
|
}
|
|
}
|
|
delay(3000);
|
|
} |