Soil

By Paul , 8 September 2025
// Smart Plant Watering - Complete Version for Arduino Nano with OLED
#include <DHT.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

// Display settings - Use 32px height to save memory
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 32
#define OLED_RESET -1
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

// Pin definitions
#define DHT_PIN 2
#define DHT_TYPE DHT22
#define SOIL_PIN A2
#define WATER_PIN A3
#define PUMP_PIN 7
#define BUZZ_PIN 8

DHT dht(DHT_PIN, DHT_TYPE);

// Thresholds - adjust these!
const int DRY_THRESH = 300;
const int WATER_THRESH = 100;
const unsigned long PUMP_TIME = 3000;
const unsigned long WAIT_TIME = 300000; // 5 min

// Variables
unsigned long lastWater = 0;
unsigned long lastRead = 0;
unsigned long lastDisplay = 0;
bool pumpOn = false;
bool displayAvailable = false;
float temp, humid;
int soil, water, soilPct;

void setup() {
  Serial.begin(9600);
  dht.begin();
  pinMode(PUMP_PIN, OUTPUT);
  pinMode(BUZZ_PIN, OUTPUT);
  digitalWrite(PUMP_PIN, HIGH); // Set HIGH for active-low relay (OFF position)
  
  // Try to initialize display
  if(display.begin(SSD1306_SWITCHCAPVCC, 0x3C)) {
    displayAvailable = true;
    display.clearDisplay();
    display.setTextSize(1);
    display.setTextColor(SSD1306_WHITE);
    display.setCursor(0,0);
    display.println(F("Plant Watering"));
    display.println(F("System Ready"));
    display.println(F("Starting..."));
    display.display();
    Serial.println("Display initialized");
  } else {
    displayAvailable = false;
    Serial.println("Display failed - continuing without");
  }
  
  Serial.println("Plant Watering Ready");
  Serial.println("Commands: t=test, c=calibrate, s=status");
  Serial.println("Format: Moisture%,Temp,Humidity,Water,Pump,Threshold");
  
  tone(BUZZ_PIN, 1000, 200);
  delay(300);
  tone(BUZZ_PIN, 1500, 200);
  
  delay(2000); // Give DHT time to stabilize
}

void loop() {
  // Read sensors every 2 seconds
  if (millis() - lastRead >= 2000) {
    readSensors();
    lastRead = millis();
  }

  // Handle serial commands
  if (Serial.available()) {
    char cmd = Serial.read();
    if (cmd == 't') testPump();
    if (cmd == 'c') calibrate();
    if (cmd == 's') status();
  }

  // Check watering
  checkWater();

  // Update display every 3 seconds
  if (displayAvailable && (millis() - lastDisplay >= 3000)) {
    updateDisplay();
    lastDisplay = millis();
  }

  // Send plotter data every 5 seconds
  static unsigned long lastPlot = 0;
  if (millis() - lastPlot >= 5000) {
    sendData();
    lastPlot = millis();
  }

  delay(100);
}

void readSensors() {
  soil = analogRead(SOIL_PIN);
  water = analogRead(WATER_PIN);
  temp = dht.readTemperature();
  humid = dht.readHumidity();
  
  soilPct = map(soil, 1023, 0, 0, 100);
  soilPct = constrain(soilPct, 0, 100);
  
  if (isnan(temp)) temp = 0;
  if (isnan(humid)) humid = 0;
}

void updateDisplay() {
  if (!displayAvailable) return;
  
  display.clearDisplay();
  display.setCursor(0,0);
  display.setTextSize(1);
  
  // Line 1: Soil moisture (larger text)
  display.setTextSize(2);
  display.print(F("Soil:"));
  display.print(soilPct);
  display.println(F("%"));
  
  // Line 2: Temperature and humidity
  display.setTextSize(1);
  display.print(F("T:"));
  display.print((int)temp);
  display.print(F("C H:"));
  display.print((int)humid);
  display.print(F("%"));
  
  // Line 3: Status indicator
  if (pumpOn) {
    display.print(F(" PUMP"));
  } else if (water <= WATER_THRESH) {
    display.print(F(" LOW H2O"));
  } else {
    display.print(F(" OK"));
  }
  
  display.display();
}

void checkWater() {
  bool dry = (soil < DRY_THRESH);
  bool timeOK = (millis() - lastWater > WAIT_TIME);
  bool hasWater = (water > WATER_THRESH);

  // Start watering
  if (dry && timeOK && hasWater && !pumpOn) {
    Serial.print("WATERING - Soil was ");
    Serial.print(soilPct);
    Serial.println("%");
    digitalWrite(PUMP_PIN, LOW);  // Changed to LOW for active-low relay
    pumpOn = true;
    lastWater = millis();
    tone(BUZZ_PIN, 800, 200);
    
    // Update display immediately
    if (displayAvailable) {
      display.clearDisplay();
      display.setCursor(0,0);
      display.setTextSize(2);
      display.println(F("WATERING"));
      display.setTextSize(1);
      display.print(F("Soil was: "));
      display.print(soilPct);
      display.println(F("%"));
      display.display();
    }
  }

  // Stop watering
  if (pumpOn && (millis() - lastWater > PUMP_TIME)) {
    Serial.println("Watering complete");
    digitalWrite(PUMP_PIN, HIGH);  // Changed to HIGH for active-low relay (OFF)
    pumpOn = false;
    tone(BUZZ_PIN, 1200, 200);
  }

  // Low water alert
  if (!hasWater && (millis() % 15000 < 100)) {
    Serial.println("LOW WATER!");
    tone(BUZZ_PIN, 2000, 100);
    
    // Flash low water warning on display
    if (displayAvailable) {
      display.clearDisplay();
      display.setCursor(0,0);
      display.setTextSize(2);
      display.println(F("LOW"));
      display.println(F("WATER!"));
      display.display();
      delay(1000);
    }
  }
}

void sendData() {
  // Serial Plotter format
  Serial.print(soilPct);
  Serial.print(",");
  Serial.print(temp);
  Serial.print(",");
  Serial.print(humid);
  Serial.print(",");
  Serial.print(water);
  Serial.print(",");
  Serial.print(pumpOn ? 100 : 0);
  Serial.print(",");
  Serial.println(30); // Reference line
}

void testPump() {
  if (water <= WATER_THRESH) {
    Serial.println("No water for test");
    if (displayAvailable) {
      display.clearDisplay();
      display.setCursor(0,0);
      display.println(F("Test Failed:"));
      display.println(F("No Water"));
      display.display();
      delay(2000);
    }
    return;
  }
  
  Serial.println("Testing pump...");
  if (displayAvailable) {
    display.clearDisplay();
    display.setCursor(0,0);
    display.setTextSize(2);
    display.println(F("TESTING"));
    display.setTextSize(1);
    display.println(F("Pump running..."));
    display.display();
  }
  
  digitalWrite(PUMP_PIN, LOW);   // Turn ON pump (LOW for active-low relay)
  delay(2000);
  digitalWrite(PUMP_PIN, HIGH);  // Turn OFF pump (HIGH for active-low relay)
  Serial.println("Test done");
  
  if (displayAvailable) {
    display.clearDisplay();
    display.setCursor(0,0);
    display.println(F("Test Complete"));
    display.display();
    delay(1000);
  }
}

void calibrate() {
  Serial.println("Calibrating...");
  if (displayAvailable) {
    display.clearDisplay();
    display.setCursor(0,0);
    display.println(F("Calibrating..."));
    display.println(F("Please wait"));
    display.display();
  }
  
  int s = 0, w = 0;
  for (int i = 0; i < 5; i++) {
    s += analogRead(SOIL_PIN);
    w += analogRead(WATER_PIN);
    delay(200);
  }
  
  Serial.print("Soil avg: ");
  Serial.print(s/5);
  Serial.print(" Water avg: ");
  Serial.println(w/5);
  
  if (displayAvailable) {
    display.clearDisplay();
    display.setCursor(0,0);
    display.println(F("Calibration:"));
    display.print(F("Soil: "));
    display.println(s/5);
    display.print(F("Water: "));
    display.println(w/5);
    display.display();
    delay(3000);
  }
}

void status() {
  readSensors();
  Serial.print("Soil: ");
  Serial.print(soilPct);
  Serial.print("% Temp: ");
  Serial.print(temp);
  Serial.print("C Humidity: ");
  Serial.print(humid);
  Serial.print("% Water: ");
  Serial.print(water);
  Serial.print(" Pump: ");
  Serial.println(pumpOn ? "ON" : "OFF");
  
  if (displayAvailable) {
    display.clearDisplay();
    display.setCursor(0,0);
    display.setTextSize(1);
    display.println(F("=== STATUS ==="));
    display.print(F("Soil: "));
    display.print(soilPct);
    display.println(F("%"));
    display.print(F("Temp: "));
    display.print((int)temp);
    display.println(F("C"));
    display.print(F("Pump: "));
    display.println(pumpOn ? F("ON") : F("OFF"));
    display.display();
    delay(3000);
  }
}

Comments2

Paul

3 months ago

/home/user/.var/app/cc.arduino.IDE2/cache/ccmLN6Y9.ltrans0.ltrans.o: In function `main':
/home/user/.arduino15/packages/arduino/hardware/avr/1.8.6/cores/arduino/main.cpp:43: undefined reference to `setup'
/home/user/.arduino15/packages/arduino/hardware/avr/1.8.6/cores/arduino/main.cpp:46: undefined reference to `loop'
collect2: error: ld returned 1 exit status
exit status 1

Compilation error: exit status 1

Paul

3 months ago

/home/user/.var/app/cc.arduino.IDE2/cache/ccmLN6Y9.ltrans0.ltrans.o: In function `main':
/home/user/.arduino15/packages/arduino/hardware/avr/1.8.6/cores/arduino/main.cpp:43: undefined reference to `setup'
/home/user/.arduino15/packages/arduino/hardware/avr/1.8.6/cores/arduino/main.cpp:46: undefined reference to `loop'
collect2: error: ld returned 1 exit status
exit status 1

Compilation error: exit status 1