pcnz22
Published © GPL3+

Uno+WiFi Hydroponics Controller

A simple cheap hydroponics controller with remote monitoring capability.

BeginnerWork in progress309
Uno+WiFi Hydroponics Controller

Things used in this project

Hardware components

Arduino UNO Wifi Rev.3
×1
8 Channel Relay Controller
×1
Ultrasonic Sensor - HC-SR04 (Generic)
Ultrasonic Sensor - HC-SR04 (Generic)
×1
Duinotech-1-3-inch-monochrome-oled-display
×1
RTC module DS1307
×1
DHT 22 arduino-compatible-temperature-and-humidity-sensor-module
×1
Arduino Compatible DC Voltage Regulator
×1

Software apps and online services

Firebase
Google Firebase

Story

Read more

Code

Hydroponics_core

Arduino
Collects sensor input, sets event times (using external RTC) and controls the digital outputs to the relay board.
Displays system status on OLED.
Prepares status words for transmission onto serial bus.
#include <RTClib.h>
#include <DHT_U.h>
#include <U8glib.h>
#include <NewPing.h>

RTC_DS1307 rtc;                                //Real Time Clock.
DHT_Unified dht(12, DHT22);                    //Temperature & Humidity Sensor
U8GLIB_SH1106_128X64 u8g(13, 11, 10, 9);       //OLED Display.
NewPing sonar(7, 8, 300);                      //Ultra-Sonic Transducer.

int Pump = 2, CircFan = 3, TankFan = 4, LED = 5, CoolFan = 6; //Digital ports to 12VDC relay board.
int LoadVolts = A2, PanelVolts = A3;           // select the input pin for the potentiometer
uint32_t delayMS;                              //DHT sensor delay.
String dH[4] = {"", "<", ">", ","};            //Data Null,Header,Terminators,Seperator.
String dM[10];                                 //Number of data messages to be placed on serial bus.

void setup() {

  pinMode(Pump, OUTPUT), pinMode(LED, OUTPUT), pinMode(CircFan, OUTPUT);
  pinMode(CoolFan, OUTPUT), pinMode(TankFan, OUTPUT), pinMode(7, INPUT);

  Serial.begin(115200);
  rtc.begin();
  //rtc.adjust(DateTime(22, 05, 25, 10 , 14, 00));
  dht.begin();
  u8g.setRot180();
  sensor_t sensor;
  dht.temperature().getSensor(&sensor);
  dht.humidity().getSensor(&sensor);
  delayMS = sensor.min_delay / 1000;
}

void loop() {

  // Get date & time from RTC.
  char Date[] = "hh:mm:ss";
  DateTime now = rtc.now(); now.toString(Date);
  int nH = now.hour(), nM = now.minute(), nD = now.day();

  // Display Time and static text on OLED.
  u8g.firstPage(); do {
    u8g.setFont(u8g_font_6x10); u8g.setColorIndex(255);
    u8g.drawStr(0, 10, "HYDROPONICS" ); u8g.drawStr(80, 10, (Date)); u8g.drawStr(66, 20, "Level");
    u8g.drawStr(116, 20, "cm"); u8g.drawCircle(119, 23, 1); u8g.drawStr(66, 30, "Temp"); u8g.drawStr(122, 30, "C");
    u8g.drawStr(66, 40, "Hum"); u8g.drawStr(110, 40, "%RH"); u8g.drawStr(66, 50, "Load"); u8g.drawStr(120, 50, "V");
    dM[0] = dH[0] + "Time " + (Date) + dH[3];

    // Hydroponic Gully Pump Timer.
    if ((nH > (7)) && (nH < (22)) && (nM > (0)) && (nM < (5)))
    {
      digitalWrite (Pump, HIGH);
      u8g.drawStr(0, 20, "Pump ON");
      dM[1] = "Gully Pump ON" + dH[3];
    }
    else  {
      digitalWrite (Pump, LOW);
      u8g.drawStr(0, 20, "Pump 00>05");
      dM[1] = "Gully Pump [00-05]" + dH[3];
    }

    // Air Cooling Fans Timer.
    if ((nH > (11)) && (nH < (17)) && (nM > (9)) && (nM < (15)))
    {
      digitalWrite (CoolFan, HIGH);
      u8g.drawStr(0, 30, "Cool ON");
      dM[2] = "Cooling Fan ON" + dH[3];
    }
    else  {
      digitalWrite (CoolFan, LOW);
      u8g.drawStr(0, 30, "Cool 09>15");
      dM[2] = "Cooling Fans [09-15]" + dH[3];
    }

    // Air Cirulation Fans Timer.
    if ((nH > (6)) && (nH < (21)) && (nM > (24)) && (nM < (30)))
    {
      digitalWrite (CircFan, HIGH);
      u8g.drawStr(0, 40, "Circ ON");
      dM[3] = "Circulation Fans ON" + dH[3];
    }
    else  {
      digitalWrite (CircFan, LOW);
      u8g.drawStr(0, 40, "Circ 24>30");
      dM[3] = "Circulation Fans [24-30]" + dH[3];
    }

    // Tank Air Circulation Timer.
    if ((nH > (7)) && (nH < (21)) && (nM > (34)) && (nM < (40)))
    {
      digitalWrite (TankFan, HIGH);
      u8g.drawStr(0, 50, "Tank ON");
      dM[4] = "Tank Fan" + dH[3];
    }
    else  {
      digitalWrite (TankFan, LOW);
      u8g.drawStr(0, 50, "Tank 34>40");
      dM[4] = "Tank Fan [34-40]" + dH[3];
    }

    // LED Panels Light Sensor Activation Timer.
    if ((nH > (19)) && (nH < (20)))
    {
      digitalWrite (LED, HIGH);
      u8g.drawStr(0, 60, "LEDs ON");
      dM[5] = "LED Light Sensor ON" + dH[3];
    }
    else  {
      digitalWrite (LED, LOW);
      u8g.drawStr(0, 60, "LEDs OFF");
      dM[5] = "LED Light Sensor OFF" + dH[3];
    }

    // Read Temperature & Humidity.
    sensors_event_t event;
    char Temp[1], Hum[1];
    dht.temperature().getEvent(&event);
    dtostrf(event.temperature, 0, 1, Temp);
    u8g.drawStr(94, 30, Temp);
    dM[6] = dH[0] + "Temperature " + (Temp) + " °C" + dH[3]; //Display Temperature.

    dht.humidity().getEvent(&event);
    dtostrf(event.relative_humidity, 0, 1, Hum);
    u8g.drawStr(85, 40, Hum);
    dM[7] = dH[0] + "Humidity " + (Hum) + " %RH" + dH[3];//Disply Humidity.

    // Read Nutrient Tank Level.
    char Lbuf[1];
    int uS = sonar.ping(); dtostrf((110 - (uS / US_ROUNDTRIP_CM)), 0, 0, Lbuf);
    int level = (110 - (uS / US_ROUNDTRIP_CM));
    // Reset Ultra-Sonic Transducer in case of error.
    if (uS == 0)
    { u8g.drawStr(70, 20, "Level Error"); dM[8] = dH[0] + "Level Error" + dH[3];
      pinMode(7, OUTPUT); delay(20); digitalWrite(7, LOW); delay(20); pinMode(7, INPUT); delay(20);
    }
    else {
      u8g.drawStr(98, 20, Lbuf);
      dM[8] = dH[0] + "Tank Level " + (Lbuf) + " cm" + dH[3];
    }

    // Read Load Voltage.
    char LoadValue[1], PanelValue[1]; int LoadV, PanelV;
    dtostrf(((analogRead(LoadVolts))* .0049 * 3), 0, 1, LoadValue); u8g.drawStr(92, 50, LoadValue);
    dM[9] = dH[0] + "Load " + (LoadValue) + " VDC" + dH[3]; //Display Load Voltage.

  }
  while ( u8g.nextPage() );
  //Place data on serial bus.
  Serial.print((dH[1]) + (dM[0]) + (dM[1]) + (dM[2]) + (dM[3]) + (dM[4]) + (dM[5]) + (dM[6]) + (dM[7]) + (dM[8]) + (dM[9]) + (dH[2]));

}

Hydroponics WiFi

Arduino
Connect to local WiFi connection.
Connects to Firebase demo database.
Receives and parse serial status data.
#include <Arduino.h>
#include <ESP8266WiFi.h>
#include <Firebase_ESP_Client.h>
#include <WiFiUdp.h>
#include "addons/TokenHelper.h"  // Provide the token generation process info.
#include "addons/RTDBHelper.h"   // Provide the RTDB payload printing info and other helper functions.

#define WIFI_SSID "************"                                       
#define WIFI_PASSWORD "*************"
#define API_KEY "************************"    
#define USER_EMAIL "*****************"                          
#define USER_PASSWORD "****************"
#define DATABASE_URL "https://esp-firebase-demo-cba62-default-rtdb.asia-southeast1.firebasedatabase.app/"  

// Define Firebase objects
FirebaseData fbdo;                  // defines FirebaseData object.
FirebaseAuth auth;                  // defines FirebaseData object needed for authentication.
FirebaseConfig config;              // defines FirebaseData object needed for configuration data.

String uid;                         // Variable to save USER UID
String databasePath;                // Database main path (to be updated in setup with the user UID)
String n0,n1,n2,n3,n4,n5,n6,n7,n8,n9;  // Database child nodes
String parentPath;                  // Parent Node (to be updated in every loop)
const byte numChars = 220;          // Max characters in serial string for parsing.
char receivedChars[numChars];
char tempChars[numChars];           // temporary array for use when parsing
char d1[numChars] = {0}, d2[numChars] = {0}, d3[numChars] = {0}, d4[numChars] = {0}, // variables to hold the parsed data
     d5[numChars] = {0}, d6[numChars] = {0}, d7[numChars] = {0}, d8[numChars] = {0}, d9[numChars] = {0},
     d10[numChars] = {0};
boolean newData = false;

FirebaseJson json;

// Timer variables (send new readings every three minutes)
unsigned long sendDataPrevMillis = 0;
unsigned long timerDelay = 10000;

// Initialize WiFi
void initWiFi() {
  WiFi.begin(WIFI_SSID, WIFI_PASSWORD); Serial.print("Connecting to WiFi ..");
  while (WiFi.status() != WL_CONNECTED) {
    Serial.print('.');
    delay(1000);
  }
  Serial.println(WiFi.localIP()); Serial.println();
}

void setup() {
  Serial.begin(115200);
  initWiFi();
  config.api_key = API_KEY;            // Assign the api key (required)
  auth.user.email = USER_EMAIL;        // Assign the user sign in credentials
  auth.user.password = USER_PASSWORD;
  config.database_url = DATABASE_URL;  // Assign the RTDB URL (required)
  Firebase.reconnectWiFi(true);
  fbdo.setResponseSize(4096);

  // Assign the callback function for the long running token generation task */
  config.token_status_callback = tokenStatusCallback; //see addons/TokenHelper.h

  // Assign the maximum retry of token generation
  config.max_token_generation_retry = 5;

  // Initialize the library with the Firebase authen and config
  Firebase.begin(&config, &auth);

  // Getting the user UID might take a few seconds
  Serial.println("Getting User UID");
  while ((auth.token.uid) == "") {
    Serial.print('.');
    delay(1000);
  }
  // Print user UID
  uid = auth.token.uid.c_str();
  Serial.print("User UID: ");
  Serial.println(uid);

  // Update database path
  databasePath = "/UsersData/" + uid + "/readings";

  // Update database path for data readings.
  n0 = databasePath + "/a"; n1 = databasePath + "/b"; n2 = databasePath + "/c";
  n3 = databasePath + "/d"; n4 = databasePath + "/e"; n5 = databasePath + "/f";
  n6 = databasePath + "/g"; n7 = databasePath + "/h"; n8 = databasePath + "/i";
  n9 = databasePath + "/j"; 
}

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();
    SendData();
    newData = false;
  }
}

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

  while (Serial.available() > 0 && newData == false) {
    rc = Serial.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() {      // split the data into its parts & assign to database child nodes.

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

  strtokIndx = strtok(tempChars, ","); strcpy(d1, strtokIndx);json.set(n0.c_str(), String(d1));
  strtokIndx = strtok(NULL, ","); strcpy(d2, strtokIndx); json.set(n1.c_str(), String(d2));
  strtokIndx = strtok(NULL, ","); strcpy(d3, strtokIndx); json.set(n2.c_str(), String(d3));
  strtokIndx = strtok(NULL, ","); strcpy(d4, strtokIndx); json.set(n3.c_str(), String(d4));
  strtokIndx = strtok(NULL, ","); strcpy(d5, strtokIndx); json.set(n4.c_str(), String(d5));
  strtokIndx = strtok(NULL, ","); strcpy(d6, strtokIndx); json.set(n5.c_str(), String(d6));
  strtokIndx = strtok(NULL, ","); strcpy(d7, strtokIndx); json.set(n6.c_str(), String(d7));
  strtokIndx = strtok(NULL, ","); strcpy(d8, strtokIndx); json.set(n7.c_str(), String(d8));
  strtokIndx = strtok(NULL, ","); strcpy(d9, strtokIndx); json.set(n8.c_str(), String(d9));
  strtokIndx = strtok(NULL, ","); strcpy(d10, strtokIndx); json.set(n9.c_str(), String(d10));
  
}

void SendData() {
  // Send new readings to database
  if (Firebase.ready() && (millis() - sendDataPrevMillis > timerDelay || sendDataPrevMillis == 0)) {
    sendDataPrevMillis = millis();

    parentPath = databasePath + "/";

    Serial.printf("Set json... %s\n", Firebase.RTDB.setJSON(&fbdo, parentPath.c_str(), &json) ? "ok" : fbdo.errorReason().c_str());
  }
}

Credits

pcnz22
0 projects • 0 followers
Contact

Comments

Please log in or sign up to comment.