Translate

Smart Farming Monitoring System

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