Rami1
Published © GPL3+

Irrigation system

Measures soil moisture and lighting. watering plants when humidity is low and no sun through valve control. log data to SD card.

IntermediateShowcase (no instructions)3,506
Irrigation system

Things used in this project

Hardware components

Arduino 101
Arduino 101
×1
Solenoid Valve ZE-4F180 NC DC 12V
×1
Soil Moisture Sensor YL-69
×1
Light Intensity Sensor Module 5528 Photo Resistor
×1
1.8" Serial SPI 128x160 Color TFT LCD Module Display (Driver IC ST7735)
×1
DS18B20 1-Wire Digital Temperature Sensor
×1
QR30E DC 12V 4.2W 240L/H Flow Rate Waterproof Brushless Pump
×1

Software apps and online services

Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)

Story

Read more

Schematics

Irrigation system

code and schematic

irrigationschematic_schem_VWoRF2Aysz.png

Code

IrrigationSystem.ino

Arduino
/*
  Irrigation system for plants home use
  by Rami.  eventsur@gmail.com

  components:
    Arduino or Genuino 101
    Solenoid Valve ZE-4F180 NC DC 12V
    Soil Moisture Sensor YL-69
    Light Intensity Sensor Module 5528 Photo Resistor
    1.8" Serial SPI 128x160 Color TFT LCD Module Display (Driver IC ST7735)
    DS18B20 1-Wire Digital Temperature Sensor
    QR30E DC 12V 4.2W 240L/H Flow Rate Waterproof Brushless Pump

  Moisture soil reading :
    1000 ~1023 dry soil
    901 ~999 humid soil
    0 ~900 in water

  Light reading :
    no Light 0 - 214
    light 214 - 1023

  Measures soil moisture and lighting,
  watering plants when humidity is low and no sun through valve control.
  Measures and records irrigation time and moisture and lighting values to SD card
*/
#include <CurieTime.h>
#include <CurieBLE.h>
#include <ArduinoJson.h>  // version 6.16.1
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <TFT.h>  // Arduino LCD library
#include <SD.h>

// pin definition for sensors
#define moistureSensorPin A0 // input from moisture sensor
#define lightSensorPin A1    // input from Light sensor
#define Valve_Output1_PIN 7 // select the pin for the solenoid valve control

// pin definition for the tft display
#define cs   10
#define dc   9
#define rst  8
/*
  The circuit:
  SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)
*/
// pin definition for the SD card
#define SDchipSelect 4

// pin definition for buttons control
#define sensorButton 0
#define modeButton 5
#define okButton 6

// controls the pump motor speed using Pulse Width Modulation
// duty cycle: between 0 (always off) and 255 (always on).
// due to power supply limitation DutyCycleOn set to 160
#define pumpPwmPin 3
#define pumpPwmDutyCycleOn 255
#define pumpPwmDutyCycleOff 0

boolean serialOut = true; // use for printing lcd display output message to terminal for debug

// create an instance of TFT library
TFT TFTscreen = TFT(cs, dc, rst);

// Data wire is plugged into port (pin) 2 on the Arduino
#define ONE_WIRE_BUS 2
// Setup a oneWire instance to communicate with any OneWire devices (not just Maxim/Dallas temperature ICs)
OneWire oneWire(ONE_WIRE_BUS);
// Pass our oneWire reference to Dallas Temperature.
DallasTemperature sensors(&oneWire);
int numberOfDevices; // Number of temperature devices found
DeviceAddress tempDeviceAddress; // We'll use this variable to store a found device address

// irrigation timeing
unsigned long startTime = 0, stopTime = 0, elapseTime = 0;
unsigned long timeZone = 10800 ; // 10800 utc +3 hours in israel +2 7200

// watering Solenoid Valve stat
boolean ValveOutput1Stat = false;

// use for display clear old clock value
char clockPrintout[20] = "";

struct Config {
  unsigned long lastWateringDate;       // manual Mode
  unsigned long sensorLastWateringDate; // Sensor Mode
  int moistureWateringThreshhold;       // Sensor mode, moisture value threshhold
  int lightWateringThreshhold;          // Sensor mode, light value threshhold
  unsigned long wateringTime;           // Sensor mode, watering time and rest watering time in sec.
  unsigned long schWateringTime;        // schedule mode, long watering time in sec.
  unsigned long schLastWateringDate;    // schedule mode, Last Watering Date in sec.
  unsigned long schWateringFrequency;   // schedule mode, time between Watering in sec.
  float flowRate;                       // L/s
  float waterReservoirState;            // waterReservoirSize -  elapseTime * flowRate
  byte defaultMode;                     // set the default working mode
};

const char *configFileName = "/config.txt";  // <- SD library uses 8.3 filenames
Config config;                         // <- global configuration object

byte showSensorSelect = 0;
byte modeSelect = 0;
byte okSelect = 0;

BLEService irrigationService("19B10000"); // BLE Irrigation Service

// BLE Irrigation moisture light tempeture sensors level and ValveOutput1Stat Characteristic
// custom 128-bit UUID,
// remote clients will be able to get notifications if this characteristic changes
BLEUnsignedCharCharacteristic moistureCharacteristic("19B10001", BLERead | BLENotify);
BLEUnsignedCharCharacteristic lightCharacteristic("19B10002", BLERead | BLENotify);
BLEFloatCharacteristic tempetureCharacteristic("19B10003", BLERead | BLENotify);
BLECharCharacteristic ValveOutput1StatCharacteristic("19B10004", BLERead | BLENotify);


void setup() {
  // initialize serial communication at 9600 bits per second:
  Serial.begin(9600);
  while (!Serial) {
    // wait for serial port to connect. Needed for native USB port only
    delay (5000);
    break;
  }

  startTime = now();
  stopTime = now();

  setupDisplay();
  setupSDcard();
  oneWireSetup();

  // declare the inputs and outputs:
  pinMode(moistureSensorPin, INPUT);
  pinMode(lightSensorPin, INPUT);
  pinMode(Valve_Output1_PIN, OUTPUT);
  pinMode(pumpPwmPin, OUTPUT);

  // watering Off
  digitalWrite(Valve_Output1_PIN, LOW);         // stop the solenoid
  analogWrite(pumpPwmPin, pumpPwmDutyCycleOff); // stop the pump

  sdConfig();
  buttonSetup();
  bleSetup();

  modeSelect = config.defaultMode;
}


void loop() {
  clockDisplay(getStrTime(now()), 0, 110, 2);
  buttonMenu();
  bleConnect();
}


// bluetooth le setup
void bleSetup() {
  // begin initialization
  BLE.begin();

  // Set a local name for the BLE device
  // This name will appear in advertising packets
  // and can be used by remote devices to identify this BLE device
  // The name can be changed but maybe be truncated based on space left in advertisement packet
  BLE.setLocalName("Irrigation");
  // BLE.setDeviceName("IrrigationSys"); // GENUINO 101-E860
  // BLE.setAppearance(384);
  BLE.setAdvertisedService(irrigationService);  // add the service UUID

  // add the sensors level characteristic
  irrigationService.addCharacteristic(moistureCharacteristic);
  irrigationService.addCharacteristic(lightCharacteristic);
  irrigationService.addCharacteristic(tempetureCharacteristic);
  irrigationService.addCharacteristic(ValveOutput1StatCharacteristic);

  BLE.addService(irrigationService);   // Add the BLE irrigation service
  moistureCharacteristic.setValue(getMoisture()); // initial value for this characteristic
  lightCharacteristic.setValue(getLightValue()); // initial value for this characteristic
  tempetureCharacteristic.setValue(getTemperatures()); // initial value for this characteristic
  ValveOutput1StatCharacteristic.setValue(ValveOutput1Stat); // initial value for this characteristic

  // Start advertising BLE.  It will start continuously transmitting BLE
  // advertising packets and will be visible to remote BLE central devices
  // until it receives a new connection

  // start advertising
  BLE.advertise();

  Serial.println("Bluetooth device active, waiting for connections...");
}


void bleConnect() {
  // listen for BLE peripherals to connect:
  BLEDevice central = BLE.central();

  // if a central is connected to peripheral:
  if (central) {
    Serial.print("Connected to central: ");
    // print the central's MAC address:
    Serial.println(central.address());

    // while the central is still connected to peripheral:
    if (central.connected()) {
      // set the moisture value for the characeristic:
      moistureCharacteristic.setValue(getMoisture());
      lightCharacteristic.setValue(getLightValue());
      tempetureCharacteristic.setValue(getTemperatures());
      ValveOutput1StatCharacteristic.setValue(ValveOutput1Stat);

    } else  {
      // when the central disconnects, print it out:
      Serial.print(F("Disconnected from central: "));
      Serial.println(central.address());
    }

  }
}


void clockDisplay(String PrintOut, int xPos, int yPos, int fontSize) {
  // char array to print to the screen
  char sensorPrintout[20];
  String sensorVal = PrintOut;

  // erase the text you just wrote
  TFTscreen.stroke(0, 0, 0);
  TFTscreen.text(clockPrintout, xPos, yPos);

  // convert the reading to a char array
  sensorVal.toCharArray(sensorPrintout, 20);

  // set the font color
  TFTscreen.stroke(255, 255, 0);
  // set the font size very large for the loop
  TFTscreen.setTextSize(fontSize);
  // print the sensor value
  TFTscreen.text(sensorPrintout, xPos, yPos);
  // save current time for clear display and write the new time
  sensorVal.toCharArray(clockPrintout, 20);
}


void buttonMenu() {
  bool cls = false;
  bool clsTxt = false;

  // get control button stat
  bool sensorButtonVal = digitalRead(sensorButton);
  bool modeButtonVal = digitalRead(modeButton);
  bool okButtonVal = digitalRead(okButton);

  int moistureSensorVal = getMoisture();
  int lightSensorVal = getLightValue();
  float tempC = getTemperatures();

  serialCommandInterface();

  // in force show water state for filling indicator
  if (config.waterReservoirState <= 0) {
    showSensorSelect = 3;
    cls = false;
    clsTxt = true;
  }

  if ((sensorButtonVal == LOW) ) {
    showSensorSelect += 1;
    cls = true;
    clsTxt = true;
  }

  if ((modeButtonVal == LOW) ) {
    modeSelect += 1;
    cls = true;
    clsTxt = true;
    if (modeSelect > 3)  modeSelect = 0;
  }

  if ((okButtonVal == LOW) ) {
    okSelect += 1;
  }

  serialOut = false;
  if (ValveOutput1Stat) {
    sensorDisplay("Watering ON", 0, 55, 2, 2, 0, cls, false, serialOut);
  } else {
    sensorDisplay("Watering OFF", 0, 55, 2, 4, 0, cls, false, serialOut);
  }
  serialOut = true;

  // switch info between sensors display
  switch (showSensorSelect) {
    case 0:
      serialOut = false;
      if (cls)  {
        sensorDisplay("humidity\n" + String(moistureSensorVal) + " %", 0, 0, 3, 4, 0, cls, true, serialOut);
        cls = false;
      } else if (moistureSensorVal == getMoisture()) {
        sensorDisplay("humidity\n" + String(moistureSensorVal) + " %", 0, 0, 3, 4, 0, false, false, serialOut);
      } else if (moistureSensorVal != getMoisture()) {
        sensorDisplay("humidity\n" + String(moistureSensorVal) + " %", 0, 0, 3, 4, 0, true, true, serialOut);
      }
      serialOut = true;
      break;
    case 1:
      serialOut = false;
      if (cls) {
        sensorDisplay("light\n" + String(lightSensorVal) + " %", 0, 0, 3, 5, 0, cls, true, serialOut);
        cls = false;
      } else if (lightSensorVal == getLightValue()) {
        sensorDisplay("light\n" + String(lightSensorVal) + " %", 0, 0, 3, 5, 0, false, false, serialOut);
      } else if (lightSensorVal != getLightValue()) {
        sensorDisplay("light\n" + String(lightSensorVal) + " %", 0, 0, 3, 5, 0, true, false, serialOut);
      }
      serialOut = true;
      break;
    case 2:
      serialOut = false;
      if (cls) {
        sensorDisplay("temp\n" + String(tempC), 0, 0, 3, 3, 0, cls, true, serialOut);
        cls = false;
      } else if (tempC == getTemperatures()) {
        sensorDisplay("temp\n" + String(tempC), 0, 0, 3, 3, 0, false, false, serialOut);
      } else if (tempC != getTemperatures()) {
        sensorDisplay("temp\n" + String(tempC), 0, 0, 3, 3, 0, true, false, serialOut);
      }
      serialOut = true;
      break;
    case 3:
      serialOut = false;
      sensorDisplay("waterReservoirState\n" + String(config.waterReservoirState), 0, 0, 3, 6, 0, false, false, serialOut);
      serialOut = true;
      break;
    default:
      showSensorSelect = 0;
      break;
  }

  // switch working mode
  switch (modeSelect) {
    case 0: // sensor mode and reservoir refilled init
      serialOut = false;
      sensorDisplay("Sensor Mode", 0, 80, 2, 4, 0, cls, clsTxt, serialOut);
      serialOut = true;
      cls = false;
      sensorMode();
      tankRefilled(); //reservoir refilled init by ok button press
      break;
    case 1:
      serialOut = false;
      sensorDisplay("Schedule Mode", 0, 80, 2, 7, 0, cls, clsTxt, serialOut);
      serialOut = true;
      cls = false;
      scheduleMode();
      tankRefilled();  //reservoir refilled init by ok button press
      break;
    case 2:
      serialOut = false;
      sensorDisplay("Manual Mode", 0, 80, 2, 7, 0, cls, clsTxt, serialOut);
      serialOut = true;
      cls = false;
      manualMode();
      tankRefilled(); //reservoir refilled init by ok button press
      break;
    case 3:  // setup mode
      serialOut = false;
      sensorDisplay("Setup Mode", 0, 80, 2, 7, 0, cls, clsTxt, serialOut);
      serialOut = true;
      cls = false;
      if (okSelect) {  // load config from sd json config.txt
        sensorDisplay("Load config values from file", 0, 80, 2, 4, 1000, true, true, serialOut);
        loadConfiguration(configFileName, config);
        printConfigValues();
        // Dump Irrigation activity file to consol
        Serial.println("Print Irrigation activity file...");
        printFile("datalog.csv");
        okSelect = 0;
      }
      break;
    default:
      modeSelect = config.defaultMode;
      break;
  }
}


// Evaluate the moisture sensor and light values and turn on valves as necessary
void sensorMode() {
  int moistureSensorVal = getMoisture();
  int lightSensorVal = getLightValue();

  // moistureSensorVal 100% fully dry, 0% fully wet
  if ( (moistureSensorVal >= config.moistureWateringThreshhold) &&
       (lightSensorVal <= config.lightWateringThreshhold) &&
       (ValveOutput1Stat == false) &&
       ( (now() - config.sensorLastWateringDate) >= config.wateringTime) &&
       (config.waterReservoirState > 0) )
  {
    wateringOn();
    okSelect = 0;
    do {
      elapseTime = now() - startTime;
      moistureSensorVal = getMoisture();

      // read button state while watering for cancel
      int okButtonVal = digitalRead(okButton);
      if ((okButtonVal == LOW) ) {
        okSelect += 1;
      }

      float WaterReservoirState = getWaterReservoirState( getWateringVolume(elapseTime) );

      if ( (moistureSensorVal <= config.moistureWateringThreshhold)
           || (okSelect > 1)
           || (WaterReservoirState <= 0) )
      {
        okSelect = 0;
        sensorDisplay("Watering canceled", 0, 80, 2, 2, 1000, true, true, serialOut);
        break;
      }

      sensorDisplay("Sensor Mode", 0, 0, 1, 7, 0, false, false, serialOut);
      sensorDisplay("Moisture Thld " + String(config.moistureWateringThreshhold), 0, 15, 1, 7, 0, false, false, serialOut);
      sensorDisplay("Light Thld " + String(config.lightWateringThreshhold), 0, 30, 1, 7, 0, false, false, serialOut);
      sensorDisplay("wateringTime " + String(config.wateringTime), 0, 45, 1, 7, 0, false, false, serialOut);
      sensorDisplay("ElpsTime ", 0, 75, 2, 2, 0, false, false, serialOut);
      sensorDisplay("% Dry is " + String(getMoisture()), 0, 95, 1, 4, 0, false, false, serialOut);
      sensorDisplay(String(elapseTime) + " sec", 100, 75, 2, 2, 500, false, true, serialOut);
      clockDisplay(getStrTime(now()), 0, 110, 2);
      bleConnect();
    } while ( elapseTime <= config.wateringTime );
    wateringOff();
    config.sensorLastWateringDate = now();
    saveConfiguration(configFileName, config);
  }
}


// watering for preset time every periodic preset frequency
// schWateringTime, schWateringFrequency, schLastWateringDate saved to config file
void scheduleMode() {
  bool isRainy = rainIndicator();

  if ( (now() >= (config.schLastWateringDate + config.schWateringFrequency))
       && (ValveOutput1Stat == false)
       && (config.waterReservoirState > 0) )
  {
    okSelect = 0;
    wateringOn();
    do {
      elapseTime = now() - startTime;
      isRainy = rainIndicator();

      int okButtonVal = digitalRead(okButton);
      if ((okButtonVal == LOW) ) {
        okSelect += 1;
      }

      float WaterReservoirState = getWaterReservoirState( getWateringVolume(elapseTime) );

      // cancel watering when rainy or ok button press or water empty
      if ( (isRainy) || (okSelect > 1) || (WaterReservoirState <= 0) )
      {
        okSelect = 0;
        sensorDisplay("Watering canceled", 0, 80, 2, 2, 1000, true, true, serialOut);
        break;
      }
      sensorDisplay("Schedule Mode", 0, 0, 1, 7, 0, false, false, serialOut);
      sensorDisplay("LastWateringDate " + getStrTime(config.schLastWateringDate), 0, 15, 1, 7, 0, false, false, serialOut);
      sensorDisplay("WateringFrequency " + String(config.schWateringFrequency), 0, 30, 1, 7, 0, false, false, serialOut);
      sensorDisplay("wateringTime " + String(config.schWateringTime), 0, 45, 1, 7, 0, false, false, serialOut);
      sensorDisplay("ElpsTime ", 0, 75, 2, 2, 0, false, false, serialOut);
      sensorDisplay("% Dry is " + String(getMoisture()), 0, 95, 1, 4, 0, false, false, serialOut);
      sensorDisplay(String(elapseTime) + " sec", 100, 75, 2, 2, 1000, false, true, serialOut);
      clockDisplay(getStrTime(now()), 0, 110, 2);
      bleConnect();
    } while ( elapseTime <= config.schWateringTime );
    wateringOff();
    config.schLastWateringDate = now();
    saveConfiguration(configFileName, config);
  }
}


// watering for preset time long and returns to sensor mode
void manualMode() {
  bool isRainy = rainIndicator();

  if ( (ValveOutput1Stat == false)
       && (okSelect > 1)
       && (config.waterReservoirState > 0) )
  {
    okSelect = 0;
    wateringOn();
    do
    {
      elapseTime = now() - startTime;

      int okButtonVal = digitalRead(okButton);
      if ((okButtonVal == LOW) ) {
        okSelect += 1;
      }

      float WaterReservoirState = getWaterReservoirState( getWateringVolume(elapseTime) );
      // stop watering on Ok button true or moisture Threshhold or Water Reservoir empty
      if ( (isRainy) || (okSelect > 1) || (WaterReservoirState <= 0) )
      {
        okSelect = 0;
        sensorDisplay("Watering canceled", 0, 80, 2, 2, 1000, true, true, serialOut);
        break;
      }

      // sensorVal, xPos, yPos, fontSize, fontColorVal, delayVal, cls, clsTxt, serialOut
      sensorDisplay("Manual Mode", 0, 0, 1, 7, 0, false, false, serialOut);
      // sensorDisplay("Watering ON", 0, 55, 2, 2, 0, true, false, serialOut);
      sensorDisplay(String(WaterReservoirState) + " Liter remaining", 0, 75, 2, 2, 1000, false, true, serialOut);
      sensorDisplay(String(elapseTime) + " sec elapsed", 0, 90, 2, 2, 1000, false, true, serialOut);

      isRainy = rainIndicator();
      clockDisplay(getStrTime(now()), 0, 110, 2);
      bleConnect();
    } while (elapseTime <= config.wateringTime);
    wateringOff();
    config.lastWateringDate = now();
    saveConfiguration(configFileName, config);
    modeSelect = config.defaultMode; // return to sensor mode
  }
}


/* cli command
  log -> print log file to terminal
  log.del -> delete log file
  cfg -> print config info to terminal
  time.unixtime -> set time
  moisture.% -> sets moisture Watering Threshhold
*/
void serialCommandInterface() {
  String command = "" ; // for incoming serial data
  String param = "";

  // send data only when you receive data:
  while (Serial.available() > 0) {
    // read the incoming byte:
    command = Serial.readStringUntil('\n');

    param = command.substring(command.indexOf('.') + 1);
    command = command.substring(0, command.indexOf('.'));
    // Serial.println(command);
    // Serial.println(param);

    if ( command.equals("log") ) {
      if (param.equals("del")) {
        delFile("datalog.csv");
      }
      // Dump Irrigation activity file to consol
      Serial.println("Print Irrigation activity file...");
      printFile("datalog.csv");
    } else if (command.equals("cfg")) {
      Serial.println(command);
      printConfigValues();
    } else if (command.equals("time")) {
      cliSetTime(param);
    } else if (command.equals("moisture")) {
      cliSetMoistureTh(param);
    } else  {
      Serial.println("Unknown command");
    }
  }
}


void cliSetMoistureTh(String moistureThreshhold) {

  if ( ! ((moistureThreshhold.equals("moisture"))
          || (moistureThreshhold.equals("moisture."))) )
  {
    config.moistureWateringThreshhold = moistureThreshhold.toInt();
    saveConfiguration(configFileName, config);
    Serial.println("moisture Watering Threshhold is set");
  }
  Serial.println("moisture is " + String(config.moistureWateringThreshhold) );
}


// set system time by sending utc unix time number through serial
// unix time url https://www.unixtimestamp.com/index.php
void cliSetTime(String unixTimeStamp) {
  unsigned long timeZone = 10800 ; // 10800 utc +3 hours in israel +2 7200

  if ( ! (unixTimeStamp.equals("time")) ) {
    setTime(unixTimeStamp.toInt() + timeZone);
    serialOut = true;
    sensorDisplay("time is set", 0, 80, 2, 1, 500, true, true, serialOut);
    serialOut = false;
  }
  Serial.println(getStrTime(now()));
}


int getMoisture() {
  int moistureSensorVal = analogRead(moistureSensorPin);
  return map(moistureSensorVal, 0, 1023, 0, 100);
}


int  getLightValue()  {
  int lightSensorVal = analogRead(lightSensorPin);
  return map(lightSensorVal, 0, 1023, 0, 100);
}


float getTemperatures(void) {
  float tempC; // Temperature
  sensors.requestTemperatures(); // Send the command to get temperatures
  /*
    // Loop through each device, print out temperature data
    for (int i = 0; i < numberOfDevices; i++) {
      // Search the wire for address
      if (sensors.getAddress(tempDeviceAddress, i)) {
        // Output the device ID
        Serial.print("Temperature for device: ");
        Serial.println(i, DEC);
        // Print the data
        tempC = sensors.getTempC(tempDeviceAddress);
      }
    }
  */
  tempC = sensors.getTempC(tempDeviceAddress);
  return tempC;
}


String getStrTime(unsigned long unixTime) {
  String timeStr, dateStr;

  timeStr = print2digits(hour(unixTime)) + ":" + print2digits(minute(unixTime)) + ":" + print2digits(second(unixTime));
  dateStr = String(day(unixTime)) + "/" + String(month(unixTime)) + "/" + String(year(unixTime));
  String dateTimeStr = timeStr + " " + dateStr;
  return dateTimeStr;
}


String print2digits(int number) {
  String numStr;
  if (number >= 0 && number < 10) {
    numStr = "0" + String(number) ;
  }
  else {
    numStr = String(number) ;
  }
  return numStr;
}


void writeDataToSDcard() {
  // make a string for assembling the data to log:
  String dataString = "";
  // read three sensors and append to the string:
  dataString = getStrTime(now())
               + "," + String(getTemperatures()) + ""
               + "," + String(getMoisture()) + "% moisture"
               + "," + String(getLightValue()) + "% light"
               + "," + getStrTime(startTime)
               + "," + getStrTime(stopTime)
               + "," + String(stopTime - startTime) + " sec"
               + "," + String(modeSelect) + " mode";

  // open the file. note that only one file can be open at a time,
  // so you have to close this one before opening another.
  File dataFile = SD.open("datalog.csv", FILE_WRITE);

  // if the file is available, write to it:
  if (dataFile) {
    dataFile.println(dataString);
    dataFile.close();
    // print to the serial port too:
    Serial.println(dataString);
  }
  else { // if the file isn't open, pop up an error:
    Serial.println("error opening datalog.csv file !");
    setupSDcard();
  }
}


// sensorVal, xPos, yPos, fontSize, fontColorVal, delayVal, cls, clsTxt, serialOut
// print text on display
void sensorDisplay(String  sensorVal, int xPos, int yPos, int fontSize, int fontColorVal,
                   int delayVal, bool cls, bool clsTxt, bool serialOut) {
  // char array to print to the screen
  char sensorPrintout[30];
  byte colorCode[8][3] =
  { // (red, green, blue);
    {0, 0, 0}, //Black 0
    {0, 0, 255}, //Blue 1
    {255, 0, 0}, //Red 2
    {255, 0, 255}, //Magenta    3
    {0, 255, 0}, //Green 4
    {0, 255, 255}, //Cyan 5
    {255, 255, 0}, //Yellow 6
    {255, 255, 255} //White 7
  };

  // clear the screen with a black background
  if (cls) {
    TFTscreen.background(0, 0, 0);
  }

  // convert the reading to a char array
  sensorVal.toCharArray(sensorPrintout, 30);

  // set the font color
  TFTscreen.stroke(colorCode[fontColorVal][2], colorCode[fontColorVal][1], colorCode[fontColorVal][0]);

  // set the font size very large for the loop
  TFTscreen.setTextSize(fontSize);

  // print the sensor value
  TFTscreen.text(sensorPrintout, xPos, yPos);
  if (serialOut) Serial.println(sensorPrintout);

  // wait for a moment
  delay(delayVal);

  // erase the text you just wrote
  if (clsTxt) {
    TFTscreen.stroke(0, 0, 0);
    TFTscreen.text(sensorPrintout, xPos, yPos);
  }
}


void wateringOn() {
  digitalWrite(Valve_Output1_PIN, HIGH);
  analogWrite(pumpPwmPin, pumpPwmDutyCycleOn); // run the pump
  ValveOutput1Stat = true;
  startTime = now();
  if (ValveOutput1Stat) {
    sensorDisplay("Watering ON", 0, 55, 2, 2, 0, true, false, serialOut);
  } else {
    sensorDisplay("Watering OFF", 0, 55, 2, 4, 0, true, false, serialOut);
  }
}


void wateringOff() {
  digitalWrite(Valve_Output1_PIN, LOW);         // stop the solenoid
  analogWrite(pumpPwmPin, pumpPwmDutyCycleOff); // stop the pump
  ValveOutput1Stat = false;
  stopTime = now();
  if (ValveOutput1Stat) {
    sensorDisplay("Watering ON", 0, 55, 2, 2, 0, true, false, serialOut);
  } else {
    sensorDisplay("Watering OFF", 0, 55, 2, 4, 0, true, false, serialOut);
  }
  writeDataToSDcard();
  config.waterReservoirState = getWaterReservoirState( getWateringVolume(elapseTime) );
}


/*
  fluid flow rate = area of the pipe or channelvelocity of the liquid
  Q = Av
  Q = liquid flow rate (m3/s or L/s) liters per second
  A = area of the pipe or channel (m2) area is A = r2
  v = velocity of the liquid (m/s)
*/
float getWateringVolume(unsigned long wateringElapseTime) {
  float wateringVolume = 0;
  wateringVolume = wateringElapseTime * config.flowRate;
  return wateringVolume;
}


float getWaterReservoirState(float wateringVolume) {
  float waterInReservoir = 0;
  waterInReservoir  = config.waterReservoirState - wateringVolume;
  return waterInReservoir;
}


void setupDisplay() {
  char printout[30];

  // Put this line at the beginning of every sketch that uses the GLCD:
  TFTscreen.begin();
  // clear the screen with a black background
  TFTscreen.background(0, 0, 0);
  // write the static text to the screen
  // set the font color to white
  TFTscreen.stroke(255, 255, 255);
  // set the font size
  TFTscreen.setTextSize(2);
  // clear screen
  TFTscreen.fillScreen(0);
  // write the text to the top left corner of the screen
  String headLineStr = String( "Rami's\n Irrigation\n system\n");
  headLineStr.toCharArray(printout, 30);
  // TFTscreen.text(printout, 0, 10);
  // set the font size very large for the loop
  TFTscreen.setTextSize(1);
  sensorDisplay(headLineStr, 0, 0, 1, 7, 0, false, true, serialOut);
}


void setupSDcard() {
  sensorDisplay("Initializing SD card...", 0, 0, 1, 7, 0, false, true, serialOut);
  // see if the card is present and can be initialized:
  if (!SD.begin(SDchipSelect)) {
    sensorDisplay("Card failed, or not present", 0, 10, 1, 7, 0, false, true, serialOut);
    // don't do anything more:
    // while (1);
    loadDefaultConfigValues();
    delay (5000);

  }
  else  {
    sensorDisplay("card initialized.", 0, 10, 1, 7, 0, false, true, serialOut);
  }
}


// tempeture setup
// device 0 with address: 28FFAFD3741604CB
void oneWireSetup(void) {
  // Start up the library
  sensors.begin();
  // Grab a count of devices on the wire
  numberOfDevices = sensors.getDeviceCount();
  // locate devices on the bus
  Serial.print("Locating tempeture sensor devices...");
  Serial.print("Found ");
  Serial.print(numberOfDevices, DEC);
  Serial.println(" devices.");
  // Loop through each device, print out address
  for (int i = 0; i < numberOfDevices; i++) {
    // Search the wire for address
    if (sensors.getAddress(tempDeviceAddress, i)) {
      Serial.print("Found device ");
      Serial.print(i, DEC);
      Serial.print(" with address: ");
      printAddress(tempDeviceAddress);
      Serial.println();
    } else {
      Serial.print("Found ghost device at ");
      Serial.print(i, DEC);
      Serial.print(" but could not detect address. Check power and cabling");
    }
  }
}


// function to print a device address
void printAddress(DeviceAddress deviceAddress) {
  for (uint8_t i = 0; i < 8; i++) {
    if (deviceAddress[i] < 16) Serial.print("0");
    Serial.print(deviceAddress[i], HEX);
  }
}


// json file config.txt format
void sdConfig() {
  // Should load default config if run for the first time
  sensorDisplay("Loading configuration...", 0, 55, 2, 2, 0, true, true, serialOut);
  loadConfiguration(configFileName, config);
  // Dump config file
  sensorDisplay("Print config file...", 0, 55, 2, 2, 0, true, true, serialOut);
  printFile(configFileName);
  printConfigValues();
}


// Loads the configuration from a file
void loadConfiguration(const char *filename, Config & config) {
  // Open file for reading
  File file = SD.open(filename);
  if (!file) {
    sensorDisplay("reading file fail, check sd card !", 0, 55, 2, 2, 0, true, true, serialOut);
    Serial.println(filename);
    setupSDcard();
    return;
  }

  // Allocate a temporary JsonDocument
  // Don't forget to change the capacity to match your requirements.
  // Use arduinojson.org/v6/assistant to compute the capacity.
  StaticJsonDocument<512> doc;
  // Deserialize the JSON document
  DeserializationError error = deserializeJson(doc, file);
  if (error) {
    sensorDisplay("fail,goto default!", 0, 55, 2, 2, 0, true, true, serialOut);
    Serial.println(F("Failed to read file, using default configuration"));
    Serial.println(error.c_str());
    // Load default config values
    loadDefaultConfigValues();
    saveConfiguration(configFileName, config);
  } else {
    // Copy values from the JsonDocument to the Config
    config.lastWateringDate = doc["lastWateringDate"];
    config.sensorLastWateringDate = doc["sensorLastWateringDate"];
    config.moistureWateringThreshhold = doc["moistureWateringThreshhold"];
    config.lightWateringThreshhold = doc["lightWateringThreshhold"];
    config.wateringTime = doc["wateringTime"]; //sec
    config.schWateringTime  = doc["schWateringTime"];
    config.schWateringFrequency = doc["schWateringFrequency"]; // 1 day
    config.schLastWateringDate = doc["schLastWateringDate"];
    config.waterReservoirState = doc["waterReservoirState"]; //liters
    config.flowRate = doc["flowRate"]; // liters/sec 1/3600 240 L/H 1/120 0.01
    config.defaultMode = doc["defaultMode"];

    sensorDisplay("Config load complete", 0, 55, 2, 2, 0, true, true, serialOut);
  }
  // Close the file (Curiously, File's destructor doesn't close the file)
  file.close();
}


// Saves the configuration to a file
void saveConfiguration(const char *filename, const Config & config) {
  // Delete existing file, otherwise the configuration is appended to the file
  SD.remove(filename);

  // Open file for writing
  File file = SD.open(filename, FILE_WRITE);
  if (!file) {
    Serial.println(F("Failed to create file Config.txt...Try again"));
    setupSDcard();
    return;
  }
  // Allocate a temporary JsonDocument
  // Don't forget to change the capacity to match your requirements.
  // Use arduinojson.org/assistant to compute the capacity.
  StaticJsonDocument<512> doc;

  // Set the values in the document
  doc["lastWateringDate"] = config.lastWateringDate;
  doc["sensorLastWateringDate"] = config.sensorLastWateringDate;
  doc["moistureWateringThreshhold"] = config.moistureWateringThreshhold;
  doc["lightWateringThreshhold"] = config.lightWateringThreshhold;
  doc["wateringTime"] = config.wateringTime;

  doc["schWateringTime"] = config.schWateringTime;
  doc["schWateringFrequency"] = config.schWateringFrequency;
  doc["schLastWateringDate"] = config.schLastWateringDate;

  doc["waterReservoirState"] = config.waterReservoirState;
  doc["flowRate"] = config.flowRate;
  doc["defaultMode"] = config.defaultMode;

  // Serialize JSON to file
  if (serializeJson(doc, file) == 0) {
    Serial.println(F("Failed to write to file"));
  }
  // Close the file
  file.close();
}

// DEL file name
void delFile(const char *filename) {
  // Check to see if the file exists:
  Serial.println(filename);
  if (SD.exists(filename)) {
    Serial.println(" Exists.");
    // delete the file:
    Serial.println(" Removing...");
    SD.remove(filename);
  } else {
    Serial.println("Doesn't exist.");
  }
}


// Prints the content of a file to the Serial
void printFile(const char *filename) {

  if (SD.exists(filename)) {
    // Open file for reading
    File file = SD.open(filename);
    if (!file) {
      Serial.println(F("Failed to read file"));
      Serial.println(F(filename));
      Serial.println(F("Try again..."));
      setupSDcard();
      return;
    }
    // Extract each characters by one by one
    while (file.available()) {
      Serial.print((char)file.read());
    }
    Serial.println();
    // Close the file
    file.close();
  } else {
    Serial.println(F(filename));
    Serial.println(" Doesn't exist.");
  }
}


void buttonSetup() {
  //configure pin 3 5 6 as an input and enable the internal pull-up resistor
  pinMode(sensorButton, INPUT_PULLUP);
  pinMode(modeButton, INPUT_PULLUP);
  pinMode(okButton, INPUT_PULLUP);
}


void loadDefaultConfigValues() {
  // load default config values
  config.moistureWateringThreshhold = 25; // 100% fully dry , 0% fully wet
  config.lightWateringThreshhold = 100;  // 100% fully light, 0% fully dark
  config.wateringTime = 60 ;             // manual and sensor mode Duration of watering in seconds
  config.lastWateringDate = now() + timeZone;       // manual mode date of last time watering
  config.sensorLastWateringDate = now() + timeZone; // sensor mode date of last time watering
  // sch. mode
  config.schLastWateringDate = now() + timeZone;   // date of last time watering
  config.schWateringTime = 180;          // Duration of watering in seconds
  config.schWateringFrequency = 86400;  // Watering frequency in seconds, day is 86400 sec
  config.waterReservoirState = 3.3;   // 3.70 liters
  config.flowRate = 0.01;           // liters/sec 1/3600 0.016666667
  config.defaultMode = 1;           // Sensor Mode 0 ,Schedule Mode 1 ,Manual Mode 2
  sensorDisplay("Load complete Default Values !", 0, 55, 2, 2, 0, true, true, serialOut);
}


// logic in case of water reservoir refilled to the full
void tankRefilled() {
  if (okSelect && showSensorSelect == 3) {
    config.waterReservoirState = 3.3;
    saveConfiguration(configFileName, config);
    sensorDisplay("Reservoir refilled", 0, 80, 2, 4, 1000, true, true, serialOut);
    okSelect = 0;
  }
}


void printConfigValues() {

  Serial.println("=====Print Loaded Config Values=====");
  Serial.print("lastWateringDate ");
  Serial.println( getStrTime(config.lastWateringDate) );
  Serial.print("sensorLastWateringDate ");
  Serial.println( getStrTime(config.sensorLastWateringDate) );
  Serial.print("moistureWateringThreshhold ");
  Serial.println(config.moistureWateringThreshhold);
  Serial.print("lightWateringThreshhold ");
  Serial.println(config.lightWateringThreshhold);
  Serial.print("wateringTime ");
  Serial.println(config.wateringTime);
  Serial.print("schWateringTime ");
  Serial.println(config.schWateringTime);
  Serial.print("schWateringFrequency ");
  Serial.println(config.schWateringFrequency);
  Serial.print("schLastWateringDate ");
  Serial.println( getStrTime(config.schLastWateringDate) );
  Serial.print("waterReservoirState ");
  Serial.println(config.waterReservoirState);
  Serial.print("flowRate ");
  Serial.println(config.flowRate);
  Serial.print("defaultMode ");
  Serial.println(config.defaultMode);

  Serial.println();
}


// uses moisture Sensor for indicat rain (return true) to alow stop watering.
// moistureSensorVal 100% fully dry, 0% fully wet
bool rainIndicator() {
  int moistureSensorVal;
  moistureSensorVal = getMoisture();
  if (moistureSensorVal >= 20) return false;
  return true;
}

Irrigation system

code

Credits

Rami1

Rami1

0 projects • 3 followers

Comments