Smart Farming Monitoring System
Smart Farming Monitoring System adalah sebuah sistem pertanian pintar berbasis Internet of Things (IoT) yang digunakan untuk memantau kondisi lingkungan dan media tanam secara otomatis. Sistem ini menggunakan ESP32 DevKit V1 sebagai pusat pengendali yang menerima data dari berbagai sensor, mengolah data tersebut, kemudian menampilkan hasilnya melalui layar TFT ILI9341 dan aplikasi Blynk IoT.
Selain memantau kondisi tanaman, sistem ini juga dapat mengendalikan perangkat elektronik seperti:
Pompa air.
Kipas pendingin.
Sistem penyiraman.
Perangkat tambahan lainnya.
Tujuan utamanya adalah membantu petani mengetahui kondisi tanaman secara real-time, mengurangi pemantauan manual, serta meningkatkan efisiensi penggunaan air dan perawatan tanaman.
1. Skema
2. Program Arduino IDE
/************************************************************
SMART FARMING MONITORING SYSTEM
ESP32 DevKit V1 + TFT ILI9341 SPI + Blynk IoT
SENSOR:
- Soil Moisture analog
- DHT11
- TDS analog (rumus contoh DFRobot Gravity TDS)
- pH analog (kalibrasi dua titik sederhana)
OUTPUT:
- TFT ILI9341
- Relay 4 Channel
- Blynk IoT
PIN:
TFT: SCK18, MISO19, MOSI23, CS5, DC21, RST22
Soil AO: GPIO34
DHT DATA: GPIO4
TDS AO: GPIO35
pH AO: GPIO32
Relay IN1..IN4: GPIO16,17,25,26
************************************************************/
// ================ BLYNK CONFIGURATION ====================
#define BLYNK_TEMPLATE_ID "ISI_TEMPLATE_ID"
#define BLYNK_TEMPLATE_NAME "SMART FARMING"
#define BLYNK_AUTH_TOKEN "ISI_AUTH_TOKEN"
#define BLYNK_PRINT Serial
#include <WiFi.h>
#include <BlynkSimpleEsp32.h>
#include <Wire.h>
#include <SPI.h>
#include <Adafruit_GFX.h>
#include <Adafruit_ILI9341.h>
#include <DHT.h>
#include <math.h>
// ================ WIFI ==================================
char ssid[] = "NAMA_WIFI";
char pass[] = "PASSWORD_WIFI";
// TFT ILI9341 SPI pins
#define TFT_SCK 18
#define TFT_MISO 19
#define TFT_MOSI 23
#define TFT_CS 5
#define TFT_DC 21
#define TFT_RST 22
// ================ PIN ===================================
#define SOIL_PIN 34
#define DHT_PIN 4
#define DHT_TYPE DHT11
#define TDS_PIN 35
#define PH_PIN 32
#define RELAY1 16
#define RELAY2 17
#define RELAY3 25
#define RELAY4 26
// Sesuaikan dengan modul relay.
// Mayoritas modul relay 5V 4-channel aktif LOW.
#define RELAY_ON LOW
#define RELAY_OFF HIGH
// ================ OBJECT ================================
Adafruit_ILI9341 tft(TFT_CS, TFT_DC, TFT_RST);
DHT dht(DHT_PIN, DHT_TYPE);
BlynkTimer timer;
// ================ SENSOR CALIBRATION ====================
// Ganti berdasarkan nilai ADC aktual saat tanah kering/basah.
int soilDry = 3000;
int soilWet = 1200;
// Rasio pemulihan tegangan jika memakai pembagi tegangan.
// Contoh: resistor 10K dari AO ke titik ADC dan 20K
// dari titik ADC ke GND -> rasio = (10K+20K)/20K = 1.5.
// Jika tidak memakai pembagi, isi 1.0 HANYA jika AO terbukti
// tidak pernah melebihi 3.3V.
float TDS_DIVIDER_RATIO = 1.5;
float PH_DIVIDER_RATIO = 1.5;
// Isi dari hasil pengukuran pada larutan buffer pH.
// Nilai berikut hanya contoh, bukan nilai universal.
float voltageAtPH7 = 2.50;
float voltageAtPH4 = 3.00;
// ================ VARIABLES =============================
float soilPercent = 0.0;
float temperatureC = 25.0;
float humidityPct = 0.0;
float tdsVoltage = 0.0;
float tdsPPM = 0.0;
float phVoltage = 0.0;
float phValue = 7.0;
bool dhtValid = false;
int relayStatus[4] = {0, 0, 0, 0};
const int relayPins[4] = {RELAY1, RELAY2, RELAY3, RELAY4};
// ================ RELAY CONTROL =========================
void setRelay(uint8_t channel, int state) {
if (channel > 3) return;
relayStatus[channel] = state ? 1 : 0;
digitalWrite(
relayPins[channel],
relayStatus[channel] ? RELAY_ON : RELAY_OFF
);
Serial.printf("Relay %u: %s\n",
channel + 1,
relayStatus[channel] ? "ON" : "OFF");
}
BLYNK_WRITE(V5) { setRelay(0, param.asInt()); }
BLYNK_WRITE(V6) { setRelay(1, param.asInt()); }
BLYNK_WRITE(V7) { setRelay(2, param.asInt()); }
BLYNK_WRITE(V8) { setRelay(3, param.asInt()); }
BLYNK_CONNECTED() {
Blynk.syncVirtual(V5, V6, V7, V8);
}
// ================ ADC AVERAGING =========================
uint32_t readMilliVoltsAverage(uint8_t pin, uint8_t samples = 10) {
uint32_t total = 0;
for (uint8_t i = 0; i < samples; i++) {
total += analogReadMilliVolts(pin);
delay(3);
}
return total / samples;
}
int readRawAverage(uint8_t pin, uint8_t samples = 10) {
uint32_t total = 0;
for (uint8_t i = 0; i < samples; i++) {
total += analogRead(pin);
delay(3);
}
return total / samples;
}
// ================ SOIL MOISTURE =========================
void readSoil() {
int soilRaw = readRawAverage(SOIL_PIN);
if (soilDry != soilWet) {
// Rumus ini mengasumsikan ADC lebih rendah saat tanah basah.
soilPercent = (float)(soilDry - soilRaw) * 100.0f /
(float)(soilDry - soilWet);
soilPercent = constrain(soilPercent, 0.0f, 100.0f);
}
Serial.printf("Soil ADC: %d | Moisture: %.1f%%\n",
soilRaw, soilPercent);
}
// ================ DHT11 =================================
void readDHT() {
float t = dht.readTemperature();
float h = dht.readHumidity();
if (!isnan(t) && !isnan(h)) {
temperatureC = t;
humidityPct = h;
dhtValid = true;
} else {
dhtValid = false;
Serial.println("DHT11 read error");
}
Serial.printf("Temperature: %.1f C | Humidity: %.1f%%\n",
temperatureC, humidityPct);
}
// ================ TDS ===================================
void readTDS() {
float adcVoltage = readMilliVoltsAverage(TDS_PIN) / 1000.0f;
tdsVoltage = adcVoltage * TDS_DIVIDER_RATIO;
// Kompensasi suhu; gunakan 25 C jika DHT11 gagal dibaca.
float tempForCompensation = dhtValid ? temperatureC : 25.0f;
float compensationCoefficient =
1.0f + 0.02f * (tempForCompensation - 25.0f);
if (compensationCoefficient < 0.1f) {
compensationCoefficient = 1.0f;
}
float compensatedVoltage = tdsVoltage / compensationCoefficient;
// Persamaan contoh untuk modul DFRobot Gravity Analog TDS.
// Tidak otomatis cocok untuk semua modul TDS Meter V1.0.
float ppm =
(133.42f * powf(compensatedVoltage, 3) -
255.86f * powf(compensatedVoltage, 2) +
857.39f * compensatedVoltage) * 0.5f;
tdsPPM = (ppm < 0.0f) ? 0.0f : ppm;
Serial.printf("TDS ADC voltage: %.3f V | TDS: %.1f ppm\n",
adcVoltage, tdsPPM);
}
// ================ pH ====================================
void readPH() {
float adcVoltage = readMilliVoltsAverage(PH_PIN) / 1000.0f;
phVoltage = adcVoltage * PH_DIVIDER_RATIO;
// Interpolasi linear dua titik. Kalibrasikan tegangan aktual
// menggunakan larutan buffer pH 7.00 dan pH 4.00.
if (fabsf(voltageAtPH4 - voltageAtPH7) > 0.001f) {
phValue = 7.0f + (phVoltage - voltageAtPH7) *
(4.0f - 7.0f) /
(voltageAtPH4 - voltageAtPH7);
}
phValue = constrain(phValue, 0.0f, 14.0f);
Serial.printf("pH ADC voltage: %.3f V | pH: %.2f\n",
adcVoltage, phValue);
}
// ================ READ ALL SENSORS ======================
void readAllSensors() {
readSoil();
readDHT();
readTDS();
readPH();
Serial.println("--------------------------------");
}
// ================ BLYNK UPDATE ==========================
void sendToBlynk() {
if (!Blynk.connected()) return;
Blynk.virtualWrite(V0, soilPercent);
if (dhtValid) {
Blynk.virtualWrite(V1, temperatureC);
Blynk.virtualWrite(V2, humidityPct);
}
Blynk.virtualWrite(V3, tdsPPM);
Blynk.virtualWrite(V4, phValue);
}
// ================ TFT DASHBOARD =========================
// Adafruit_GFX tidak menyediakan drawCentreString seperti TFT_eSPI.
// Fungsi ini menghitung lebar teks lalu menempatkannya di tengah.
void drawCenteredText(const String &text, int16_t centerX, int16_t y,
uint8_t textSize, uint16_t color,
uint16_t background) {
int16_t x1, y1;
uint16_t w, h;
tft.setTextSize(textSize);
tft.getTextBounds(text, 0, y, &x1, &y1, &w, &h);
tft.setTextColor(color, background);
tft.setCursor(centerX - (int16_t)w / 2, y);
tft.print(text);
}
void drawDashboard() {
tft.fillScreen(ILI9341_BLACK);
// Header
tft.fillRect(0, 0, 320, 30, ILI9341_BLUE);
drawCenteredText("SMART FARMING", 160, 8, 2,
ILI9341_WHITE, ILI9341_BLUE);
// Soil
tft.drawRoundRect(5, 36, 150, 58, 6, ILI9341_GREEN);
tft.setTextColor(ILI9341_GREEN, ILI9341_BLACK);
tft.setTextSize(2);
tft.setCursor(15, 43);
tft.print("SOIL");
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setTextSize(3);
tft.setCursor(15, 65);
tft.print(String(soilPercent, 1) + "%");
// Temperature
tft.drawRoundRect(165, 36, 150, 58, 6, ILI9341_RED);
tft.setTextColor(ILI9341_RED, ILI9341_BLACK);
tft.setTextSize(2);
tft.setCursor(175, 43);
tft.print("TEMP");
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setTextSize(3);
tft.setCursor(175, 65);
tft.print(dhtValid ? String(temperatureC, 1) + " C" : "-- C");
// Humidity
tft.drawRoundRect(5, 101, 150, 58, 6, ILI9341_CYAN);
tft.setTextColor(ILI9341_CYAN, ILI9341_BLACK);
tft.setTextSize(2);
tft.setCursor(15, 108);
tft.print("HUMIDITY");
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setTextSize(3);
tft.setCursor(15, 130);
tft.print(dhtValid ? String(humidityPct, 1) + "%" : "--");
// TDS
tft.drawRoundRect(165, 101, 150, 58, 6, ILI9341_YELLOW);
tft.setTextColor(ILI9341_YELLOW, ILI9341_BLACK);
tft.setTextSize(2);
tft.setCursor(175, 108);
tft.print("TDS");
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setTextSize(3);
tft.setCursor(175, 130);
tft.print(String(tdsPPM, 0) + " ppm");
// pH
tft.drawRoundRect(5, 166, 90, 62, 6, ILI9341_MAGENTA);
tft.setTextColor(ILI9341_MAGENTA, ILI9341_BLACK);
tft.setTextSize(2);
tft.setCursor(15, 173);
tft.print("pH");
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setTextSize(3);
tft.setCursor(15, 198);
tft.print(String(phValue, 2));
// Relay states
tft.drawRoundRect(105, 166, 210, 62, 6, ILI9341_WHITE);
tft.setTextColor(ILI9341_WHITE, ILI9341_BLACK);
tft.setTextSize(2);
tft.setCursor(115, 171);
tft.print("RELAY STATUS");
tft.setTextColor(ILI9341_GREEN, ILI9341_BLACK);
tft.setTextSize(1);
tft.setCursor(115, 194);
tft.print(String("R1:") + (relayStatus[0] ? "ON" : "OFF") +
" R2:" + (relayStatus[1] ? "ON" : "OFF"));
tft.setCursor(115, 210);
tft.print(String("R3:") + (relayStatus[2] ? "ON" : "OFF") +
" R4:" + (relayStatus[3] ? "ON" : "OFF"));
}
// ================ SETUP =================================
void setup() {
Serial.begin(115200);
// ADC configuration
analogReadResolution(12);
analogSetPinAttenuation(SOIL_PIN, ADC_11db);
analogSetPinAttenuation(TDS_PIN, ADC_11db);
analogSetPinAttenuation(PH_PIN, ADC_11db);
// Relay: set output and OFF at startup
for (int i = 0; i < 4; i++) {
digitalWrite(relayPins[i], RELAY_OFF);
pinMode(relayPins[i], OUTPUT);
digitalWrite(relayPins[i], RELAY_OFF);
}
// DHT
dht.begin();
// TFT ILI9341 menggunakan SPI hardware ESP32
SPI.begin(TFT_SCK, TFT_MISO, TFT_MOSI, TFT_CS);
tft.begin();
tft.setRotation(1); // landscape 320 x 240
tft.fillScreen(ILI9341_BLACK);
drawCenteredText("SMART FARMING", 160, 100, 2,
ILI9341_WHITE, ILI9341_BLACK);
drawCenteredText("Initializing...", 160, 130, 2,
ILI9341_WHITE, ILI9341_BLACK);
// Wi-Fi and Blynk
Blynk.begin(BLYNK_AUTH_TOKEN, ssid, pass);
// First sensor read and display
readAllSensors();
drawDashboard();
// Timers
timer.setInterval(2500L, readAllSensors);
timer.setInterval(3000L, drawDashboard);
timer.setInterval(3000L, sendToBlynk);
Serial.println("SMART FARMING SYSTEM READY");
}
// ================ MAIN LOOP ============================
void loop() {
Blynk.run();
timer.run();
}
3. Gambar Aplikasinya



No comments:
Post a Comment