Project showcase
Automated Aeroponic System Wifi Remoted

Automated Aeroponic System Wifi Remoted © GPL3+

I started from no idea what is arduino at the end of February to this project end April. First robotic project. During lockdown. Selfmade.

  • 1,345 views
  • 2 comments
  • 12 respects

Components and supplies

Necessary tools and machines

Hy gluegun
Hot glue gun (generic)
09507 01
Soldering iron (generic)
26w6260 40
Multitool, Screwdriver
70y8518 40
Drill / Driver, Cordless
Manual saw

About this project

Aeroponic Automated System Remote

Introduction

Hi, I'm a chemical engineering student. Until I started this project I had no idea how to code. I started with blink... two months ago... thanks to the lockdown. I can't guarantee that my code is written according to the rules. But I've tested it for hours, days, it works.

I challenge myself in many ways, in this project, I did it for myself; it's not a school project or anything else. I started from scratch. I used a lot of lines of code from other sources, but in the end, I started writing my own. I'm not in a good position to teach someone because I'm a beginner. But I think that since I've read tons of projects and trained for over 450 hours reading, coding and failing tons of times, maybe this one can help people. Besides, knowledge dies when it's not shared.

What the system can do?

The system i've made currently can record, set and monitor through WebBrowser these differents parameters:

  • Water temperature
  • Air temperature (inside the greenhouse)
  • Air temperature inside the electronical box
  • Humidity level
  • TDS (Nutrient )
  • Time Trigger for Light
  • Time Trigger for Watering On/Off Cycle
  • All the datas can be stored in SDcard
  • Water level with AutoRefill (Sensor +pump+relay)*
  • Humidity with mistmaker and fan (Mistmaker + fan+relay)*

Legend for* [Will be added soon]

WebServer Video Demostration

https://streamable.com/w97i19

This is the link of WebServer access by Iphone8 on 4g connexion.

Description of the content of the video:

First I check the error page if log are false (that was made for 20 April )

Then I push a button to see the confirmation message

After that I check no blank formular and ranged value and submit to see response of the arduino the value come from it after refresh

  • The home page contain some casual widget
  • It's fully optimised for SmartPhone Browser
  • You can Monitor data collected by sensor
  • You can switch on/off light and pump manualy
  • You can set time trigger for light and watering
  • You can set Water temperature and TDS value

Thanks you for reading hope this will inspire people like I was reading tons of projects!

Made during the lockdown period March-April 2020

For every suggestion, questions, or optimisation idea comments or send me email.

Alexandros Pantelidis

Code

MEGA-ESP_v08_propre.inoArduino
My first coding project
int getMedianNum(int bArray[], int iFilterLen)
{
  int bTab[iFilterLen];
  for (byte i = 0; i < iFilterLen; i++)
    bTab[i] = bArray[i];
  int i, j, bTemp;
  for (j = 0; j < iFilterLen - 1; j++)
  {
    for (i = 0; i < iFilterLen - j - 1; i++)
    {
      if (bTab[i] > bTab[i + 1])
      {
        bTemp = bTab[i];
        bTab[i] = bTab[i + 1];
        bTab[i + 1] = bTemp;
      }
    }
  }
  if ((iFilterLen & 1) > 0)
    bTemp = bTab[(iFilterLen - 1) / 2];
  else
    bTemp = (bTab[iFilterLen / 2] + bTab[iFilterLen / 2 - 1]) / 2;
  return bTemp;
}

byte Heart[8] = {
  0b00000,
  0b01010,
  0b11111,
  0b11111,
  0b01110,
  0b00100,
  0b00000,
  0b00000
};



//=========================================debut Muoton"Black Sheep" Alexandros Pantelidis Avril 2020=================================
byte tete1[8] =
{
  0b00011,
  0b00100,
  0b01010,
  0b10000,
  0b11111,
  0b00011,
  0b00011,
  0b00010
};

byte tete2[8] =
{
  0b11000,
  0b11100,
  0b10001,
  0b11110,
  0b10001,
  0b11000,
  0b11111,
  0b10001
};

byte tete3[8] =
{
  0b00000,
  0b00000,
  0b10011,
  0b01110,
  0b10001,
  0b00100,
  0b10001,
  0b01110
};

byte tete4[8] =
{
  0b00000,
  0b00000,
  0b00000,
  0b10000,
  0b01000,
  0b01000,
  0b11100,
  0b10110
};

byte bas1[8] =
{
  0b00010,
  0b00010,
  0b00011,
  0b00001,
  0b00001,
  0b00000,
  0b00000,
  0b00000
};

byte bas2[8] =
{
  0b01011,
  0b01100,
  0b00111,
  0b01000,
  0b11111,
  0b01001,
  0b01001,
  0b01001
};

byte bas3[8] =
{
  0b00011,
  0b11001,
  0b00000,
  0b10100,
  0b11111,
  0b00010,
  0b00010,
  0b00010
};

byte bas4[8] =
{
  0b10001,
  0b01001,
  0b00101,
  0b10011,
  0b11110,
  0b00100,
  0b00100,
  0b00100
};
//=========================================Fin mouton=================================
//=========================================Librarie utilisées=========================
#include <EEPROM.h>
#include <DHT.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <SD.h>
#include <LiquidCrystal_I2C.h>
#include  <virtuabotixRTC.h>  //Library used
#include <ResponsiveAnalogRead.h>
//=========================================Pin definie=================================
#define RELAY1 24
#define POMPES 22
#define LAMPES 26
#define moteurA_1 38
#define moteurA_2 40
#define moteurB_1 36
#define moteurB_2 34
#define DS1302_GND_PIN 33
#define DS1302_VCC_PIN 31
#define Tbox A2
#define DHTPIN 32     // what pin we're connected to
#define DHTTYPE DHT22   // DHT 22  (AM2302)
#define TdsSensorPin A8
#define VREF 5.0 // analog reference voltage(Volt) of the ADC
#define SCOUNT 3 // sum of sample point
#define ONE_WIRE_BUS 7
//=========================================Constantes utiilisées============================
unsigned long currentMillis1 = 0;
unsigned long currentMillis4 = 0;
unsigned long previousMillis1 = 0;
unsigned long previousMillis = 0;
unsigned long previousMillis3 = 0;
const long interval = 10000;
unsigned long periodtds = 3600000; // 1h
const long nettoyage = 10000;
const int chipSelect = 49;
unsigned long startMillis1;
unsigned long startMillis;
String message = "";
bool messageReady = false;
String message1 = "";
bool messageReady1 = false;


int analogBuffer1[SCOUNT]; // store the analog value in the array, read from ADC
int analogBufferTemp1[SCOUNT];
int analogBufferIndex1 = 0, copyIndex1 = 0;

int analogBuffer[SCOUNT];  
int analogBufferTemp[SCOUNT];
int analogBufferIndex = 0, copyIndex = 0;
float averageVoltage = 0 , tdsValue = 0;

int analogBuffer2[SCOUNT]; 
int analogBufferTemp2[SCOUNT];
int analogBufferIndex2 = 0, copyIndex2 = 0;
float averageVoltage2 = 0;

int val;
int addr = 0;

const byte numChars = 64;
char receivedChars[numChars];
char tempChars[numChars];
char messageFromPC[numChars] = {0};
int integerFromPC = 0;
float floatFromPC = 0.0;
boolean newData = false;

int ledstate;
int pompestate;

int int1;
int int2;
int int3;
int int4;
int int5;
int int6;
int int7;

int minteau ;
int maxteau ;
int timearro;

float  temperature;
float    humi;
float  temp;
float   tbox;
int tdsval;

int OnHour;
int OnMin;           //set hour and minutes for timer

int OffHour;
int OffMin;

int valeur_brute;

bool freshtds = false;
bool cleanup = false;
bool perioff = true;
//=========================================Fonction declaré============================

LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display
DHT dht(DHTPIN, DHTTYPE); //// Initialize DHT sensor for normal 16mhz Arduino
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
ResponsiveAnalogRead analog(Tbox, true);
virtuabotixRTC myRTC(35, 37, 39); //If you change the wiring change the pins here also
//=========================================Fonction demmarage ============================
void LedState() {

  val = EEPROM.read(addr);
  if (val == 1)
  {
    digitalWrite(LAMPES, HIGH);

  }
  if (val == 2)
  {
    digitalWrite(LAMPES, LOW);
  }
  maxteau = EEPROM.read(2);
  minteau = EEPROM.read(3);
  OnHour = EEPROM.read(4);

  OnMin = EEPROM.read(5);
  OffHour = EEPROM.read(6);
  OffMin = EEPROM.read(7);
  tdsval = 10 * EEPROM.read(8);

  timearro = EEPROM.read(9);

  Serial.println(maxteau);
  Serial.println(minteau);
  Serial.println(OnHour);
  Serial.println(OnMin);
  Serial.println(OffHour);
  Serial.println(OffMin);
  Serial.println(tdsval);
  Serial.println(timearro);

}

//=========================================Executé au demmarage============================
void setup() {
  Serial.begin(9600);
  delay(1);
  Serial1.begin(115200);
  delay(1);
  Serial.print("Initializing SD card...");
  delay(1);

  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    while (1);
  }
  Serial.println("card initialized.");
  dht.begin();
  sensors.begin();
  pinMode(TdsSensorPin, INPUT);
  pinMode(RELAY1, OUTPUT);
  pinMode(POMPES, OUTPUT);
  pinMode(LAMPES, OUTPUT);
  pinMode(Tbox, INPUT);
  pinMode(moteurA_1, OUTPUT);
  pinMode(moteurB_1, OUTPUT);
  pinMode(moteurA_2, OUTPUT);
  pinMode(moteurB_2, OUTPUT);
  digitalWrite(POMPES, HIGH);
  digitalWrite(moteurA_1, LOW);
  digitalWrite(moteurA_2, LOW);
  digitalWrite(moteurB_1, LOW);
  digitalWrite(moteurB_2, LOW);
  digitalWrite(DS1302_GND_PIN, LOW);
  pinMode(DS1302_GND_PIN, OUTPUT);
  digitalWrite(DS1302_VCC_PIN, HIGH);
  pinMode(DS1302_VCC_PIN, OUTPUT);

  LedState();
  startMillis1 = millis();
  startMillis = millis();
  lcd.init();
  lcd.createChar(0, Heart);
  lcd.createChar(1, tete1);
  lcd.createChar(2, tete2);
  lcd.createChar(3, tete3);
  lcd.createChar(4, tete4);
  lcd.createChar(5, bas1);
  lcd.createChar(6, bas2);
  lcd.createChar(7, bas3);
  lcd.createChar(8, bas4);
  lcd.backlight();
  lcd.setCursor(0, 0);

  lcd.print("Alex's Hydro Box");
  lcd.setCursor(3, 1);
  lcd.print("Bienvenue ^^");
  delay(5000);
  lcd.clear();
  //myRTC.setDS1302Time(0, 8, 19, 2, 7, 4, 2020);

}


//=========================================Set cycle arrosage /on/off============================


void setpompe() {
  timearro = EEPROM.read(9);

  currentMillis1 = millis();  //get the current "time" (actually the number of milliseconds since the program started)
  if (currentMillis1 - startMillis >= (1000 * 60 * timearro) ) {
    digitalWrite(POMPES, !digitalRead(POMPES));  //if so, change the state of the LED.  Uses a neat trick to change the state
    startMillis = currentMillis1;  //IMPORTANT to save the start time of the current LED state.
  }
}

void recvWithStartEndMarkers() {
  static boolean recvInProgress = false;
  static byte ndx = 0;
  char startMarker = '<';
  char endMarker = '>';
  char rc;

  while (Serial1.available() > 0 && newData == false) {
    rc = Serial1.read();

    if (recvInProgress == true) {
      if (rc != endMarker) {
        receivedChars[ndx] = rc;
        ndx++;
        if (ndx >= numChars) {
          ndx = numChars - 1;
        }
      }
      else {
        receivedChars[ndx] = '\0'; // terminate the string
        recvInProgress = false;
        ndx = 0;
        newData = true;
      }
    }

    else if (rc == startMarker) {
      recvInProgress = true;
    }
  }
}



void parseData() {

  char * strtokIndx; // this is used by strtok() as an index

  //strtokIndx = strtok(tempChars,",");      // get the first part - the string
  //strcpy(messageFromPC, strtokIndx); // copy it to messageFromPC

  strtokIndx = strtok(tempChars, ","); // this continues where the previous call left off
  integerFromPC = atoi(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  int1 = atoi(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  int2 = atoi(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  int3 = atoi(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  int4 = atoi(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  int5 = atoi(strtokIndx);

  strtokIndx = strtok(NULL, ",");
  int6 = atoi(strtokIndx);


}

//=========================================fonction recu depuis nodemcu============================

void showParsedData() {
  if (integerFromPC  == 1) {
    digitalWrite(LAMPES, HIGH);
    EEPROM.update(addr, 1);
    integerFromPC  = 250;
  }
  if (integerFromPC  == 0) {
    digitalWrite(LAMPES, LOW);
    EEPROM.update(addr, 2);
    integerFromPC  = 250;
  }

  if (integerFromPC  == 4) {
    digitalWrite(POMPES, HIGH);
    integerFromPC  = 250;
  }
  if (integerFromPC  == 5) {
    digitalWrite(POMPES, LOW);
    integerFromPC  = 250;
  }
  if (integerFromPC  == 9 && int1 != 0 ) {


    int  timearro = int1;

    EEPROM.update(9, timearro);
    integerFromPC  = 250;
    int1 = 0;
  }
}

void formulair () {
  if (integerFromPC  == 6 && int1 != 0 && int2 != 0) {
    int maxteau = int1;
    int minteau = int2;
    EEPROM.update(2, maxteau);
    EEPROM.update(3, minteau);
    Serial.println(maxteau);
    Serial.println(minteau);
    integerFromPC  = 250;
    int1 = 0;
    int2 = 0;
  }
}
void formulair1 () {
  if (integerFromPC  == 7 && int3 != 0 ) {
    int OnHour = int3;
    int OnMin = int4;
    int OffHour = int5;
    int OffMin = int6;
    EEPROM.update(4, OnHour);
    EEPROM.update(5, OnMin);
    EEPROM.update(6, OffHour);
    EEPROM.update(7, OffMin);
    Serial.println(OnHour);
    Serial.println(OnMin);
    Serial.println(OffHour);
    Serial.println(OffMin);
    integerFromPC  = 250;
  }
}
void tdsva () {
  if (integerFromPC  == 8 && int1 != 0 ) {
    int tdsval = int1;

    EEPROM.update(8, tdsval);

    Serial.println(tdsval * 10);

    integerFromPC  = 250;
  }
}

void settds() {
  int tdsval = 10 * EEPROM.read(8);   //10* valeur epprom car max 256 impossible de stocker 1000 comme valeur mais 100*10 c'est possible

  unsigned long currentMillis3 = millis();
  if (tdsValue  <= tdsval && tdsValue != 0 && freshtds == false && pompestate == 1 && tdsValue > 150) {
    
  currentMillis4 = millis();  //get the current "time" (actually the number of milliseconds since the program started)
  if (currentMillis4 - startMillis >= 8000 ) {
    digitalWrite(moteurB_1, !digitalRead(moteurB_1));  //if so, change the state of the LED.  Uses a neat trick to change the state
    startMillis1 = currentMillis4;  //IMPORTANT to save the start time of the current LED state.
  
  }
  previousMillis3 = currentMillis3;
    cleanup = true;
  }
  if (tdsValue  >= tdsval && cleanup == true ) {

    digitalWrite(moteurB_1, LOW);
    digitalWrite(moteurB_2, HIGH);

    freshtds = true;
    cleanup = false;
    perioff = false;

  }
  if (currentMillis3 - previousMillis3 > nettoyage && perioff == false ) {

    digitalWrite(moteurB_1, LOW);
    digitalWrite(moteurB_2, LOW);
    perioff = true;
  }

  if (currentMillis3 - previousMillis3 > periodtds && perioff == true) {
    Serial.print("freshtds false");
    freshtds = false;
  }
}
void loop() {

  recvWithStartEndMarkers();
  if (newData == true) {
    strcpy(tempChars, receivedChars);
    // this temporary copy is necessary to protect the original data
    //   because strtok() used in parseData() replaces the commas with \0
    parseData();
    showParsedData();
    newData = false;
  }

  state();
  sensor();
  tdsva();
  setpompe();
  myRTC.updateTime();
  settds();
  formulair();
  formulair1();
  timmer();
  data();
  flcd();
  average();
  setwater();
}

void state() {
  maxteau = EEPROM.read(2);
  minteau = EEPROM.read(3);

  OnHour = EEPROM.read(4);
  OnMin = EEPROM.read(5);
  OffHour = EEPROM.read(6);
  OffMin = EEPROM.read(7);

  tdsval = 10 * EEPROM.read(8);
  timearro = EEPROM.read(9);
  analog.update();
  ledstate = digitalRead(LAMPES);
  pompestate = digitalRead(POMPES);
}

void sensor() {
  sensors.requestTemperatures();
  temperature = sensors.getTempCByIndex(0);
  humi = dht.readHumidity();
  temp = dht.readTemperature();

  valeur_brute = analog.getValue();
  tbox = valeur_brute * (5.0 / 1023.0 * 100.0);
}

void setwater() {
  if (temperature >= maxteau && temperature != -127 && temperature != 0)
  {
    digitalWrite(RELAY1, HIGH);

  }
  else if (temperature <= minteau && temperature != -127 && temperature != 0)
  {
    digitalWrite(RELAY1, LOW);

  }
  else {
    ;
  }
}

void average() {
  static unsigned long analogSampleTimepoint = millis();
  if (millis() - analogSampleTimepoint > 40U) //every 40 milliseconds,read the analog value from the ADC
  {
    analogSampleTimepoint = millis();
    analogBuffer[analogBufferIndex] = analogRead(TdsSensorPin); //read the analog value and store into the buffer
    analogBufferIndex++;
    if (analogBufferIndex == SCOUNT)
      analogBufferIndex = 0;
  }
  static unsigned long printTimepoint = millis();
  if (millis() - printTimepoint > 800U)
  {
    printTimepoint = millis();
    for (copyIndex = 0; copyIndex < SCOUNT; copyIndex++)
      analogBufferTemp[copyIndex] = analogBuffer[copyIndex];
    averageVoltage = getMedianNum(analogBufferTemp, SCOUNT) * (float)VREF / 1024.0; // read the analog value more stable by the median filtering algorithm, and convert to voltage value
    float compensationCoefficient = 1.0 + 0.02 * (temperature - 25.0); //temperature compensation formula: fFinalResult(25^C) = fFinalResult(current)/(1.0+0.02*(fTP-25.0));
    float compensationVolatge = averageVoltage / compensationCoefficient; //temperature compensation
    tdsValue = (133.42 * compensationVolatge * compensationVolatge * compensationVolatge - 255.86 * compensationVolatge * compensationVolatge + 857.39 * compensationVolatge) * 0.5; //convert voltage value to tds value
  }
}



void recorddata() {

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis > 1000) {
    previousMillis = currentMillis;
    //Serial.print("Current Date / Time: ");

    //  Serial.print(myRTC.dayofmonth);             //You can switch between day and month if you're using American system && myRTC.minutes == OnMin
    // Serial.print("/");
    // Serial.print(myRTC.month);
    // Serial.print("/");
    //  Serial.print(myRTC.year);
    //  Serial.print(" ");
    //  Serial.print(myRTC.hours);
    // Serial.print(":");
    //Serial.print(myRTC.minutes);
    //  Serial.print(":");
    // Serial.println(myRTC.seconds);
    // Serial.print("Humidity: ");
    // Serial.print(humi);
    // Serial.println(" %");
    // Serial.print("Temp air: ");
    //Serial.print(temp);
    // Serial.println(" Celsius");
    // Serial.print("Temp H2O ;");
    // Serial.print(temperature);
    //  Serial.println(" Celsius");
    // Serial.print("TDS Value:");
    // Serial.print(tdsValue, 0);
    //  Serial.println("ppm");
    //Serial.print("Temp box: ");
    //  Serial.println(tbox, 1);
    // Serial.println(" Celsius");

    File dataFile = SD.open("datalog1.txt", FILE_WRITE);
    delay(1);
    dataFile.println(temperature);
    delay(1);
    dataFile.close();

  }
}

void timmer() {
  if (myRTC.hours == OnHour && myRTC.minutes == OnMin ) {
    EEPROM.update(addr, 1);
    digitalWrite(LAMPES, HIGH);
    delay(5);
  }
  else if (myRTC.hours == OffHour && myRTC.minutes == OffMin ) {  // add && myRTC.minutes == OnMin   ;    (myRTC.hours == OffHour && myRTC.minutes == OffMin){
    EEPROM.update(addr, 2);
    digitalWrite(LAMPES, LOW);
    delay(5);
  }
}

void data() {
  if (integerFromPC  == 3) {

    Serial1.print("<");
    Serial1.print(ledstate);
    Serial1.print(",");
    Serial1.print(pompestate);
    Serial1.print(",");
    Serial1.print(tdsValue, 0);
    Serial1.print(",");
    Serial1.print(temp, 2);
    Serial1.print(",");
    Serial1.print(temperature, 2);
    Serial1.print(",");
    Serial1.print(humi, 2);
    Serial1.print(",");
    Serial1.print(tbox, 2);
    Serial1.print(",");
    Serial1.print(tdsval);
    Serial1.print(",");
    Serial1.print(OnHour);
    Serial1.print(",");
    Serial1.print(OnMin);
    Serial1.print(",");
    Serial1.print(OffHour);
    Serial1.print(",");
    Serial1.print(OffMin);
    Serial1.print(",");
    Serial1.print(maxteau);
    Serial1.print(",");
    Serial1.print(minteau);
    Serial1.print(",");
    Serial1.print(timearro);
    Serial1.print(">");
    Serial1.println("");
  }
}


void flcd() {
  lcd.setCursor(0, 0);
  lcd.print("T Eau:");
  lcd.print(temperature, 1);
  lcd.print((char)223);
  lcd.setCursor(12, 0);
  lcd.print("Hum:");
  lcd.print(humi, 0);
  lcd.print(" % ");
  lcd.print("AP Grow Box V1");
  lcd.print(" ");
  lcd.write(byte(1));
  lcd.write(byte(2));
  lcd.write(byte(3));
  lcd.write(byte(4));

  lcd.setCursor(0, 1);
  lcd.print("T Air:");
  lcd.print(temp, 1);
  lcd.print((char)223);
  lcd.setCursor(12, 1);
  lcd.print("TDS:");
  lcd.print(tdsValue, 0);
  lcd.print(" ppm ");
  lcd.print("T Box:");
  lcd.print(tbox, 1);
  lcd.print((char)223);
  lcd.print(" ");
  lcd.write(byte(5));
  lcd.write(byte(6));
  lcd.write(byte(7));
  lcd.write(byte(8));
  lcd.scrollDisplayLeft();
}

Schematics

Shemas Mega2560/nodemcu8266 Aeroponic System
Wiring shemas
Shemas hydrobox oo7wen72tz

Comments

Similar projects you might like

Arduino Controlled Smart Hydroponic Modular System

Project in progress by Luis Antonio Martin Nuez

  • 26,755 views
  • 6 comments
  • 119 respects

Plant Watering System

Project showcase by Niko123

  • 9,368 views
  • 3 comments
  • 19 respects

Arduino-ESP WiFi Integration

Project in progress by Turai Botond

  • 13,258 views
  • 11 comments
  • 57 respects

Automated Plant Watering System

Project in progress by Ammar Shahid

  • 10,277 views
  • 3 comments
  • 32 respects

Smart Farm Application with Watering and Fertilizer System

Project in progress by monkeysuit

  • 6,242 views
  • 9 comments
  • 42 respects
Add projectSign up / Login