Main menu

Aquarium Control

UPD. During the exploitation of aquarium i set separate power supply for the stepper motor, a feeding schedule and reboot once a day.

UPD2. Added another engine for mixing feed. Food become caked around the drill and begins to crumble much less food per unit time.

------

Some time ago Andrew gave us aquarium guppies and one catfish.
After some time, the aquarium's population has grown, the plants appeared, general, formed a new wonderful world.
The system is extremely useful regularity: turn on / off lights, feed the fish at the same time.
Then the idea to automate the aquarium.
That's what came out of it:



Functionality:
- Web interface
- Enable / disable the filter
- On / off pump
- Automatic shutdown of the filter and pump
- On / off lighting
- Auto power on / automatic shutdown lighting on schedule
- On / off feeders
- Auto power on / automatic shutdown feeders on schedule
- Temperature control
- Auto power on / automatic shutdown cooling
- Time synchronization
- LCD display with touch screen (not implemented)

I took Arduino Uno + ethernet shield.
After some time had to change to Arduino Mega 2560 because Uno has not enough memory.

The final components:
- Arduino Mega 2560 R3
- Ethernet Shield
- Digital temperature sensor DS18B20 interface c Dallas 1-Wire, waterproof
- 2x Stepper motor 5V (28BYJ48) + driver (ULN2003)
- Four channel 5V Relay Module for Arduino
- 2x Power supply 5V
- Power supply 12V
- Fan 80mm from the base unit
- A plastic box, plastic pieces
- Drill 8mm, 12cm
- Dowel 6mm

I made my case based on: http://rukobludov.net/%D0%B0%D0%B2%D1%82%D0%BE%D0%BC%D0%B0%D1%82%D0%B8%D0%B7%D0%B8%D1%80%D0%BE%D0%B2%D0%B0%D0%BD%D0%BD%D0%B0%D1%8F-%D0%BA%D0%BE%D1%80%D0%BC%D1%83%D1%88%D0%BA%D0%B0-%D0%B4%D0%BB%D1%8F-%D1%80%D1%8B%D0%B1/

Chunks of the code i composed from the source code from various sources. I apologize to the authors that do not pointed out them here.

Pins 1-4 are connected to the relay pins 5-8
DS18B20: red wire - + 5V, black - the GND, yellow - pin 9. Between the black and yellow wire resistor 4,7kOm.
Pins 1-4 stepper motor drivers are connected to the pins 40-43
Pins 1-4 second stepper motor driver (mixing) are connected to the pins 44-47

5V power supply with usb connector, power supply 5V, 12V power supply, a plug with a wire connected to surge protector.
The relay 1,2,4 connected one wire 220 which is connected to the central clamp.
From sockets 1,2 1,2 wire are connected to the relay in the normally closed state, that is 220V devices have power when no power from the arduino to the relay.
The first power outlet is connected filter, the second - the pump.
The power outlet 3 is connected lighting.
From the power outlet 3 and 4 relay wire is connected to the terminal in the normally opened stat, the default lighting is turned off.
The cooler is connected to the relay 3. 12v wire is connected to the terminal in the normally opened state.

Photos:

IMAG2006

IMAG2009

IMAG2019

IMAG2013

IMAG2014

IMAG2015

After UPD:
IMAG2499

IMAG2501

IMAG2502

ToDo
- Add the possibility to enter the time on / off of lighting
- Add the possibility to enter the time on / off of feeder
- Add a touch screen for displaying information and management

Sketch:

#include <Stepper.h>
#include <SD.h>
#include <OneWire.h>
#include <SPI.h>
#include <Ethernet.h>
#include <EthernetUdp.h>
//#include <EEPROM.h>

#define stepsPerMotorRevolution 32
#define stepsPerOutputRevolution 32 * 64  //2048  
#define motorSpeed 800
#define motor2Speed 200
#define motorPin1 40
#define motorPin2 41
#define motorPin3 42
#define motorPin4 43
#define motor2Pin1 44
#define motor2Pin2 45
#define motor2Pin3 46
#define motor2Pin4 47
#define dsPin 9 // DS18 Pin
#define ethPin 10
#define SSPin 53
#define SDPin 4
#define filterPin 5
#define airPin 6
#define coolerPin 7
#define lightPin 8

Stepper stepper(stepsPerMotorRevolution, motorPin1, motorPin2, motorPin3, motorPin4);
Stepper stepper2(stepsPerMotorRevolution, motor2Pin1, motor2Pin2, motor2Pin3, motor2Pin4);
OneWire  ds(dsPin);  //
float celsius, fahrenheit;
int randNumber;

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0x16 }; // the media access control (ethernet hardware) address for the shield
byte ip[] = { 192, 168, 18, 116 }; //the IP address for the shield
byte gateway[] = { 192, 168, 18, 1 };
byte subnet[] = { 255, 255, 255, 0 };
EthernetServer server(80);

//IPAddress timeServer1(88, 147, 254, 235);
//IPAddress timeServer2(91, 226, 136, 155);
//IPAddress timeServer3(88, 147, 254, 234);
IPAddress timeServer(192, 168, 18, 1);

const long timeZoneOffset = 10800L;
const int NTP_PACKET_SIZE = 48; // NTP time stamp is in the first 48 bytes of the message
byte packetBuffer[ NTP_PACKET_SIZE]; //buffer to hold incoming and outgoing packets
// A UDP instance to let us send and receive packets over UDP
EthernetUDP Udp;
unsigned int localPort = 8888;      // local port to listen for UDP packets
boolean NTPSync = false;

const long NTPIntv = 3600000;
const long NTPRepIntv = 60000;
const long filterIntv = 600000;    // Max delay in Filter
const long airIntv = 600000;    // Max delay in Air
const long lightIntv = 60000;    // interval to checking light
const long del = 100000;

long currentTime = 0;
long NTPSyncTime = 0;
long epoch = 0;
long prvLight = 0; // Last check time. Light
long prvNTPSync = 0; // Last NTP sync time
//long prvNTPsend = 0; // Last NTP sync time
long filterSwOffTime = 0; // When filter switch off
long airSwOffTime = 0; // When air switch off
const long softResetIntv = 86400000;

Sd2Card card;
SdVolume volume;
SdFile root;
File myFile;
String buttonsNames[9] = {};
char Filename[15] = "epoch.txt";

int upLightTime = 8;      // switch on (hour)
int downLightTime = 18;     // switch off (hour)
int isNight = 0;    // in switch on at night
boolean lightTimer = true;

boolean feederTimer = true;
boolean feederMustStop = true;
boolean feederON = false;
boolean feeder2Timer = true;
boolean feeder2MustStop = true;
boolean feeder2ON = false;

int feederOnTime[2] = {9, 12};    // time to switch on feeder
int feederOnIntv = 5; // period in minutes
int feeder2OnTime[2] = {8, 11};    // time to switch on feeder
int feeder2OnIntv = 10; // period in minutes

int minTemp = 23; // Switch off Cooler
int maxTemp = 26; // Switch on Cooler

//The number of outputs going to be switched.
int outputQuantity = 4;  //when added to outputLowest result should not exceed 10

//The lowest output pin we are starting from
int outputLowest = 5;    //Should be between 2 to 9

// Variable declaration
int outp = 0;
boolean initialPrint = true;
boolean reading = false;
boolean readInput[10]; //Create a boolean array for the maximum ammount.

void setup()
{
  randomSeed(analogRead(0));
  //Set pins as Outputs
  for (int var = outputLowest; var < outputLowest + outputQuantity; var++)  {
    pinMode(var, OUTPUT);
    digitalWrite(var, 1);
  }
  pinMode(ethPin, OUTPUT);
  pinMode(SSPin, OUTPUT);
  digitalWrite(ethPin, HIGH);
  //digitalWrite(SDPin, HIGH);
  //digitalWrite(ethPin, LOW);

  Serial.begin(9600);
    
  //SD.begin(SDPin);
  if (!SD.begin(SDPin)) {
    Serial.println("SD initialization failed!");
    //return;
  }
  if (!volume.init(card)) {
    Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
    //return;
  }  
  Serial.println("\nFiles found on the card (name, date and size in bytes): ");
  root.openRoot(volume);
  root.ls(LS_R | LS_DATE | LS_SIZE);  
  //myFile = SD.open("epoch.txt", FILE_WRITE);

  // print the type and size of the first FAT-type volume
  //uint32_t volumesize;
  //Serial.print("\nVolume type is FAT");
  //Serial.println(volume.fatType(), DEC);
  //Serial.println();

  //volumesize = volume.blocksPerCluster();    // clusters are collections of blocks
  //volumesize *= volume.clusterCount();       // we'll have a lot of clusters
  //volumesize *= 512;                            // SD card blocks are always 512 bytes
  //Serial.print("Volume size (bytes): ");
  //Serial.println(volumesize);
  //Serial.print("Volume size (Kbytes): ");
  //volumesize /= 1024;
  //Serial.println(volumesize);
  //Serial.print("Volume size (Mbytes): ");
  //volumesize /= 1024;
  //Serial.println(volumesize);

  buttonsNames[5] = "Filter";
  buttonsNames[6] = "Air";
  buttonsNames[7] = "Cooler";
  buttonsNames[8] = "Lamp";

  currentTime = millis();
  if (SD.exists(Filename)) {
    myFile = SD.open(Filename);
    if (myFile) {
      while (myFile.available()) {
        epoch = myFile.read();
        Serial.print("Epoch: ");
        Serial.println(epoch);
      }      
      myFile.close();
    }
  } else {
    myFile = SD.open(Filename,FILE_WRITE);
    myFile.close();
    if (SD.exists(Filename)) {
      Serial.print(Filename);
      Serial.println(" created");
    } else {
      Serial.print(Filename);
      Serial.println(" did not create");      
    }
  }
  //epoch = EEPROM.read(301);

  Ethernet.begin(mac, ip, gateway, subnet);
  server.begin();  
  Udp.begin(localPort);

  EthernetReset();

  stepper.setSpeed(motorSpeed); // set the speed of the motor
  stepper2.setSpeed(motor2Speed); // set the speed of the motor

  Serial.println(F("Setup ready!"));
}

#define BUFSIZ 100 // Buffer for filename
void loop()
{
  currentTime = millis();

  if (currentTime % del == 0 ) {
    Serial.print("currentTime: ");
    Serial.print(currentTime);
    Serial.println(" ");         
  }
 
  if (currentTime > softResetIntv) softReset();

  if (currentTime < prvNTPSync ) prvNTPSync = currentTime; // 00:00:00
  if (currentTime < prvLight ) prvLight = currentTime; // 00:00:00

  if ((currentTime - prvNTPSync >= NTPRepIntv) && (NTPSync == false || currentTime - NTPSyncTime >= NTPIntv)) {
    //if (NTPSync == false && currentTime - NTPSyncTime >= NTPIntv) {
    //EthernetReset();
    //}
    Serial.print("currentTime: ");
    Serial.print(currentTime);
    Serial.println(" ");            
    Serial.print("prvNTPSync: ");
    Serial.print(prvNTPSync);
    Serial.println(" ");        
    //Serial.print("currentTime - prvNTPSync: ");
    //Serial.print((currentTime - prvNTPSync));
    //Serial.println(" ");        
    //Serial.print("NTPSync: ");
    //Serial.print(NTPSync);
    //Serial.println(" ");        
    Serial.print("NTPSyncTime: ");
    Serial.print(NTPSyncTime);
    Serial.println(" ");        
    //Serial.print("currentTime - NTPSyncTime: ");
    //Serial.print((currentTime - NTPSyncTime));    
    //Serial.println(" ");    
    //Serial.print("NTPIntv: ");    
    //Serial.print(NTPRepIntv);    
    //Serial.println(" ");            
    //randNumber = random(1,4);
    //Serial.println(randNumber);
    //if(randNumber == 1){sendNTPpacket(timeServer1);} // send an NTP packet to a time server
    //if(randNumber == 2){sendNTPpacket(timeServer2);} // send an NTP packet to a time server
    //if(randNumber == 3){sendNTPpacket(timeServer3);} // send an NTP packet to a time server
    prvNTPSync = currentTime;    
    sendNTPpacket(timeServer);
  }

  readOutputStatuses(); //Refresh the reading of outputs
  syncTime();
  checkTemp();
  checkForClient(); // listen for incoming clients, and process requests.
  switchCooler();
  switchFeeder(1);
  if (lightTimer == true) {
    if (currentTime - prvLight > lightIntv) {
      prvLight = currentTime;
      switchLight();
    }
  }
}

void checkTemp() {

  byte i;
  byte type_s;
  byte data[12];
  byte addr[8];
  if ( !ds.search(addr)) {
    //Serial.println("No more addresses.");
    //Serial.println();
    ds.reset_search();
    //delay(250);
    return;
  }

  if (OneWire::crc8(addr, 7) != addr[7]) {
    //Serial.println("CRC is not valid!");
    return;
  }
  Serial.println();

  switch (addr[0]) {
    case 0x10:
      //Serial.println("  Chip = DS18S20");
      type_s = 1;
      break;
    case 0x28:
      //Serial.println("  Chip = DS18B20");
      type_s = 0;
      break;
    case 0x22:
      //Serial.println("  Chip = DS1822");
      type_s = 0;
      break;
    default:
      //Serial.println("Device is not a DS18x20 family device.");
      return;
  }

  ds.reset();
  ds.select(addr);
  ds.write(0x44, 1);
  //delay(1000);

  ds.reset();
  ds.select(addr);
  ds.write(0xBE);

  for ( i = 0; i < 9; i++) {
    data[i] = ds.read();
  }

  int16_t raw = (data[1] << 8) | data[0];
  if (type_s) {
    raw = raw << 3;
    if (data[7] == 0x10) {
      raw = (raw & 0xFFF0) + 12 - data[6];
    }
  }
  else {
    byte cfg = (data[4] & 0x60);
    if (cfg == 0x00) raw = raw & ~7;
    else if (cfg == 0x20) raw = raw & ~3;
    else if (cfg == 0x40) raw = raw & ~1;
  }
  celsius =  (float)raw / 16.0;
  fahrenheit = celsius * 1.8 + 32.0;
}

void checkForClient() {

  char clientline[BUFSIZ];
  char *filename;
  int index = 0;

  EthernetClient client = server.available();
  if (client) {

    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    boolean sentHeader = false;

    while (client.connected()) {
      if (client.available()) {

        if (!sentHeader) {

          // send a standard http response header
          client.println(F("HTTP/1.1 200 OK"));
          client.println(F("Content-Type: text/html"));
          client.println(F("Connnection: close"));
          client.println();

          client.println(F("<!DOCTYPE HTML>"));
          client.println(F("<head>"));

          // add page title
          client.println(F("<title>Aquarium's Control</title>"));
          client.println(F("<meta name=\"description\" content=\"Aquarium's Control\"/>"));
          client.println(F("<meta content=\"text/html; charset=UTF-8\" http-equiv=\"content-type\">"));

          // add a meta refresh tag, so the browser pulls again every 10 seconds:
          client.println(F("<meta http-equiv=\"refresh\" content=\"20; url=/\">"));

          // add other browser configuration
          client.println(F("<meta name=\"apple-mobile-web-app-capable\" content=\"yes\">"));
          client.println(F("<meta name=\"apple-mobile-web-app-status-bar-style\" content=\"default\">"));
          client.println(F("<meta name=\"viewport\" content=\"width=device-width, user-scalable=no\"/>"));

          //inserting the styles data, usually found in CSS files.
          client.println(F("<style type=\"text/css\">"));
          client.println(F(""));

          //This will set how the page will look graphically
          client.println(F("html { height:100%; }"));
          client.println(F("body {"));
          client.println(F("    height: 100%;"));
          client.println(F("    margin: 0;"));
          client.println(F("    font-family: helvetica, sans-serif;"));
          client.println(F("    -webkit-text-size-adjust: none;"));
          client.println(F("   }"));
          client.println(F(""));
          client.println(F("body {"));
          client.println(F("    -webkit-background-size: 100% 21px;"));
          client.println(F("    background-color: #c5ccd3;"));
          client.println(F("    background-image:"));
          client.println(F("    -webkit-gradient(linear, left top, right top,"));
          client.println(F("    color-stop(.75, transparent),"));
          client.println(F("    color-stop(.75, rgba(255,255,255,.1)) );"));
          client.println(F("    -webkit-background-size: 7px;"));
          client.println(F("   }"));
          client.println(F(""));
          client.println(F(".button { width:150px; }"));
          client.println(F(""));
          client.println(F(".view {"));
          client.println(F("    min-height: 100%;"));
          client.println(F("    overflow: auto;"));
          client.println(F("   }"));
          client.println(F(""));
          client.println(F(".header-wrapper {"));
          client.println(F("    height: 44px;"));
          client.println(F("    font-weight: bold;"));
          client.println(F("    text-shadow: rgba(0,0,0,0.7) 0 -1px 0;"));
          client.println(F("    border-top: solid 1px rgba(255,255,255,0.6);"));
          client.println(F("    border-bottom: solid 1px rgba(0,0,0,0.6);"));
          client.println(F("    color: #fff;"));
          client.println(F("    background-color: #8195af;"));
          client.println(F("    background-image:"));
          client.println(F("    -webkit-gradient(linear, left top, left bottom,"));
          client.println(F("    from(rgba(255,255,255,.4)),"));
          client.println(F("    to(rgba(255,255,255,.05)) ),"));
          client.println(F("    -webkit-gradient(linear, left top, left bottom,"));
          client.println(F("    from(transparent),"));
          client.println(F("    to(rgba(0,0,64,.1)) );"));
          client.println(F("    background-repeat: no-repeat;"));
          client.println(F("    background-position: top left, bottom left;"));
          client.println(F("    -webkit-background-size: 100% 21px, 100% 22px;"));
          client.println(F("    -webkit-box-sizing: border-box;"));
          client.println(F("   }"));
          client.println(F(""));
          client.println(F(".header-wrapper h1 {"));
          client.println(F("    text-align: center;"));
          client.println(F("    font-size: 20px;"));
          client.println(F("    line-height: 44px;"));
          client.println(F("    margin: 0;"));
          client.println(F("   }"));
          client.println(F(""));
          client.println(F(".group-wrapper {"));
          client.println(F("    margin: 9px;"));
          client.println(F("    }"));
          client.println(F(""));
          client.println(F(".group-wrapper h2 {"));
          client.println(F("    text-align: center;"));
          client.println(F("    color: #4c566c;"));
          client.println(F("    font-size: 17px;"));
          client.println(F("    line-height: 0.8;"));
          client.println(F("    font-weight: bold;"));
          client.println(F("    text-shadow: #fff 0 1px 0;"));
          client.println(F("    margin: 20px 10px 12px;"));
          client.println(F("   }"));
          client.println(F(""));
          client.println(F(".group-wrapper h3 {"));
          client.println(F("    color: #4c566c;"));
          client.println(F("    font-size: 12px;"));
          client.println(F("    line-height: 1;"));
          client.println(F("    font-weight: bold;"));
          client.println(F("    text-shadow: #fff 0 1px 0;"));
          client.println(F("    margin: 20px 10px 12px;"));
          client.println(F("   }"));
          client.println(F(""));
          client.println(F(".group-wrapper table {"));
          client.println(F("    background-color: #fff;"));
          client.println(F("    -webkit-border-radius: 10px;"));

          client.println(F("    -moz-border-radius: 10px;"));
          client.println(F("    -khtml-border-radius: 10px;"));
          client.println(F("    border-radius: 10px;"));

          client.println(F("    font-size: 17px;"));
          client.println(F("    line-height: 20px;"));
          client.println(F("    margin: 9px 0 20px;"));
          client.println(F("    border: solid 1px #a9abae;"));
          client.println(F("    padding: 11px 3px 12px 3px;"));
          client.println(F("    margin-left:auto;"));
          client.println(F("    margin-right:auto;"));

          client.println(F("    -moz-transform :scale(1);")); //Code for Mozilla Firefox
          client.println(F("    -moz-transform-origin: 0 0;"));



          client.println(F("   }"));
          client.println(F(""));


          //how the green (ON) LED will look
          client.println(F(".green-circle {"));
          client.println(F("    display: block;"));
          client.println(F("    height: 23px;"));
          client.println(F("    width: 23px;"));
          client.println(F("    background-color: #0f0;"));
          //client.println(F("    background-color: rgba(60, 132, 198, 0.8);"));
          client.println(F("    -moz-border-radius: 11px;"));
          client.println(F("    -webkit-border-radius: 11px;"));
          client.println(F("    -khtml-border-radius: 11px;"));
          client.println(F("    border-radius: 11px;"));
          client.println(F("    margin-left: 1px;"));

          client.println(F("    background-image: -webkit-gradient(linear, 0% 0%, 0% 90%, from(rgba(46, 184, 0, 0.8)), to(rgba(148, 255, 112, .9)));@"));
          client.println(F("    border: 2px solid #ccc;"));
          client.println(F("    -webkit-box-shadow: rgba(11, 140, 27, 0.5) 0px 10px 16px;"));
          client.println(F("    -moz-box-shadow: rgba(11, 140, 27, 0.5) 0px 10px 16px; /* FF 3.5+ */"));
          client.println(F("    box-shadow: rgba(11, 140, 27, 0.5) 0px 10px 16px; /* FF 3.5+ */"));

          client.println(F("    }"));
          client.println(F(""));

          //how the black (off)LED will look
          client.println(F(".black-circle {"));
          client.println(F("    display: block;"));
          client.println(F("    height: 23px;"));
          client.println(F("    width: 23px;"));
          client.println(F("    background-color: #040;"));
          client.println(F("    -moz-border-radius: 11px;"));
          client.println(F("    -webkit-border-radius: 11px;"));
          client.println(F("    -khtml-border-radius: 11px;"));
          client.println(F("    border-radius: 11px;"));
          client.println(F("    margin-left: 1px;"));
          client.println(F("    -webkit-box-shadow: rgba(11, 140, 27, 0.5) 0px 10px 16px;"));
          client.println(F("    -moz-box-shadow: rgba(11, 140, 27, 0.5) 0px 10px 16px; /* FF 3.5+ */"));
          client.println(F("    box-shadow: rgba(11, 140, 27, 0.5) 0px 10px 16px; /* FF 3.5+ */"));
          client.println(F("    }"));
          client.println(F(""));

          //this will add the glare to both of the LEDs
          client.println(F("   .glare {"));
          client.println(F("      position: relative;"));
          client.println(F("      top: 1;"));
          client.println(F("      left: 5px;"));
          client.println(F("      -webkit-border-radius: 10px;"));
          client.println(F("      -moz-border-radius: 10px;"));
          client.println(F("      -khtml-border-radius: 10px;"));
          client.println(F("      border-radius: 10px;"));
          client.println(F("      height: 1px;"));
          client.println(F("      width: 13px;"));
          client.println(F("      padding: 5px 0;"));
          client.println(F("      background-color: rgba(200, 200, 200, 0.25);"));
          client.println(F("      background-image: -webkit-gradient(linear, 0% 0%, 0% 95%, from(rgba(255, 255, 255, 0.7)), to(rgba(255, 255, 255, 0)));"));
          client.println(F("    }"));
          client.println(F(""));


          //and finally this is the end of the style data and header
          client.println(F("</style>"));
          client.println(F("</head>"));

          //now printing the page itself
          client.println(F("<body>"));
          client.println(F("<div class=\"view\">"));
          client.println(F("    <div class=\"header-wrapper\">"));
          client.println(F("      <h1>Aquarium's Control</h1>"));
          client.println(F("    </div>"));
          client.println(F("<div class=\"group-wrapper\">"));
          client.print(F("<h2>Time: "));
          client.print(((epoch + currentTime / 1000 - NTPSyncTime / 1000)  % 86400L) / 3600);
          client.print(F(":"));
          client.print(((epoch + currentTime / 1000 - NTPSyncTime / 1000)  % 3600) / 60);
          client.print(F(":"));
          client.print((epoch + currentTime / 1000 - NTPSyncTime / 1000) % 60);
          client.println(F("</h2>"));
          client.print(F("<h2>NTP last sync: "));
          client.print(((epoch + NTPSyncTime / 1000)  % 86400L) / 3600);
          client.print(F(":"));
          client.print(((epoch + NTPSyncTime / 1000)  % 3600) / 60);
          client.print(F(":"));
          client.print((epoch + NTPSyncTime / 1000) % 60);
          client.println(F("</h2>"));
          client.print(F("<h2>Temperature: "));
          client.print(celsius);
          client.print(F("C</h2>"));
          client.println(F("    </div>"));
          client.println();
          //This is for the arduino to construct the page on the fly.
          sentHeader = true;
        }

        char c = client.read();

        if (reading && c == ' ') {
          reading = false;
        }

        if (c == '?') {
          reading = true; //found the ?, begin reading the info
        }

        if (reading) {
          if (c == 'A') {
            lightTimer = true;
          }
          if (c == 'B') {
            lightTimer = false;
            //Serial.println("false");
          }
          if (c == 'C') {
            feederTimer = true;
          }
          if (c == 'D') {
            feederTimer = false;
          }
          if (c == 'E') {
            feederMustStop = false;
            feederON = true;
          }
          if (c == 'F') {
            feederMustStop = true;
            feederON = false;
          }
          if (c == 'G') {
            outp = 0;
          }
          if (c == 'H') {
            outp = 1;
          }
          if (c == 'J') {
            feeder2Timer = true;
          }
          if (c == 'K') {
            feeder2Timer = false;
          }
          if (c == 'L') {
            feeder2MustStop = false;
            feeder2ON = true;
          }
          if (c == 'M') {
            feeder2MustStop = true;
            feeder2ON = false;
          }
          Serial.print(c);   //print the value of c to serial communication
          //Serial.print(outp);
          //Serial.print('\n');

          switch (c) {
            case '2':
              //add code here to trigger on 2
              triggerPin(2, client, outp);
              break;
            case '3':
              //add code here to trigger on 3
              triggerPin(3, client, outp);
              break;
            case '4':
              //add code here to trigger on 4
              triggerPin(4, client, outp);
              break;
            case '5':
              //add code here to trigger on 5
              triggerPin(5, client, outp);
              //printHtml(client);
              break;
            case '6':
              //add code here to trigger on 6
              triggerPin(6, client, outp);
              break;
            case '7':
              //add code here to trigger on 7
              triggerPin(7, client, outp);
              break;
            case '8':
              //add code here to trigger on 8
              triggerPin(8, client, outp);
              break;
            case '9':
              //add code here to trigger on 9
              triggerPin(9, client, outp);
              break;
          }
        }
        if (c == '\n' && currentLineIsBlank) {
          readOutputStatuses(); //Refresh the reading of outputs
          printHtmlButtons(client);
          Serial.println("");
          break;
        }
      }
    }

    client.println(F("</div>\n</div>\n</body>\n</html>"));

    delay(1); // give the web browser time to receive the data
    client.stop(); // close the connection:

  }

}



void triggerPin(int pin, EthernetClient client, int outp) {
  //Switching on or off outputs
  if (outp == 1) {
    digitalWrite(pin, HIGH);
  }
  if (outp == 0) {
    digitalWrite(pin, LOW);
  }
  if (pin == lightPin) {
    lightTimer = false;
    Serial.println("false");
  }
  if (pin == filterPin) {
    filterSwOffTime = currentTime;
  }
  if (pin == airPin) {
    airSwOffTime = currentTime;
  }
  //Serial.println(pin);
}


//print the html buttons to switch on/off channels
void printHtmlButtons(EthernetClient client) {

  //Start to create the html table
  client.println("");
  client.println(F("<FORM>"));
  client.println(F("<table border=\"0\" align=\"center\">"));

  //Start printing button by button
  for (int var = outputLowest; var < outputLowest + outputQuantity; var++)  {

    //Print begining of row
    client.print("<tr>\n");

    //Prints the ON Buttons
    client.print(F(" <td><INPUT class='button' id='on' TYPE=\"button\" VALUE=\"ON - "));
    client.print(buttonsNames[var]);
    if (var == filterPin || var == airPin) {
      client.print(F("\" onClick=\"parent.location='/?H"));
    } else {
      client.print(F("\" onClick=\"parent.location='/?G"));
    }
    client.print(var);
    client.print(F("'\"></td>\n"));

    //Prints the OFF Buttons
    client.print(F(" <td><INPUT class='button' id='off' TYPE=\"button\" VALUE=\"OFF - "));
    client.print(buttonsNames[var]);
    if (var == filterPin || var == airPin) {
      client.print(F("\" onClick=\"parent.location='/?G"));
    } else {
      client.print(F("\" onClick=\"parent.location='/?H"));
    }
    client.print(var);
    client.print(F("'\"></td>\n"));

    if (var == filterPin || var == airPin) {
      //Print first part of the Circles or the LEDs
      if (readInput[var] == true) {
        client.print(F(" <td><div class='green-circle'><div class='glare'></div></div></td>\n"));
      } else {
        client.print(F(" <td><div class='black-circle'><div class='glare'></div></div></td>\n"));
      }
    } else {
      //Print first part of the Circles or the LEDs
      if (readInput[var] == false) {
        client.print(F(" <td><div class='green-circle'><div class='glare'></div></div></td>\n"));
      } else {
        client.print(F(" <td><div class='black-circle'><div class='glare'></div></div></td>\n"));
      }
    }

    //Print end of row
    client.print("</tr>\n");

  }

  //Print begining of row
  client.print("<tr>\n");

  //Prints the ON Buttons
  client.print(F(" <td><INPUT class='button' id='on' TYPE=\"button\" VALUE=\"ON - Light Timer"));
  client.print(F("\" onClick=\"parent.location='/?A"));
  client.print(F("'\"></td>\n"));

  //Prints the OFF Buttons
  client.print(F(" <td><INPUT class='button' id='off' TYPE=\"button\" VALUE=\"OFF - Light Timer"));
  client.print(F("\" onClick=\"parent.location='/?B"));
  client.print(F("'\"></td>\n"));

  //Print first part of the Circles or the LEDs
  if (lightTimer == true) {
    client.print(F(" <td><div class='green-circle'><div class='glare'></div></div></td>\n"));
  } else {
    client.print(F(" <td><div class='black-circle'><div class='glare'></div></div></td>\n"));
  }

  //Print end of row
  client.print("</tr>\n");

  //Print begining of row
  client.print("<tr>\n");

  //Prints the ON Buttons
  client.print(F(" <td><INPUT class='button' id='on' TYPE=\"button\" VALUE=\"ON - Feeder"));
  client.print(F("\" onClick=\"parent.location='/?E"));
  client.print(F("'\"></td>\n"));

  //Prints the OFF Buttons
  client.print(F(" <td><INPUT class='button' id='off' TYPE=\"button\" VALUE=\"OFF - Feeder"));
  client.print(F("\" onClick=\"parent.location='/?F"));
  client.print(F("'\"></td>\n"));

  //Print first part of the Circles or the LEDs
  if (feederON == true) {
    client.print(F(" <td><div class='green-circle'><div class='glare'></div></div></td>\n"));
  } else {
    client.print(F(" <td><div class='black-circle'><div class='glare'></div></div></td>\n"));
  }

  //Print end of row
  client.print("</tr>\n");

  //Print begining of row
  client.print("<tr>\n");

  //Prints the ON Buttons
  client.print(F(" <td><INPUT class='button' id='on' TYPE=\"button\" VALUE=\"ON - Feeder Timer"));
  client.print(F("\" onClick=\"parent.location='/?C"));
  client.print(F("'\"></td>\n"));

  //Prints the OFF Buttons
  client.print(F(" <td><INPUT class='button' id='off' TYPE=\"button\" VALUE=\"OFF - Feeder Timer"));
  client.print(F("\" onClick=\"parent.location='/?D"));
  client.print(F("'\"></td>\n"));

  //Print first part of the Circles or the LEDs
  if (feederTimer == true) {
    client.print(F(" <td><div class='green-circle'><div class='glare'></div></div></td>\n"));
  } else {
    client.print(F(" <td><div class='black-circle'><div class='glare'></div></div></td>\n"));
  }

  //Print end of row
  client.print("</tr>\n");

  //Print begining of row
  client.print("<tr>\n");

  //Prints the ON Buttons
  client.print(F(" <td><INPUT class='button' id='on' TYPE=\"button\" VALUE=\"ON - Feeder2"));
  client.print(F("\" onClick=\"parent.location='/?L"));
  client.print(F("'\"></td>\n"));

  //Prints the OFF Buttons
  client.print(F(" <td><INPUT class='button' id='off' TYPE=\"button\" VALUE=\"OFF - Feeder2"));
  client.print(F("\" onClick=\"parent.location='/?M"));
  client.print(F("'\"></td>\n"));

  //Print first part of the Circles or the LEDs
  if (feeder2ON == true) {
    client.print(F(" <td><div class='green-circle'><div class='glare'></div></div></td>\n"));
  } else {
    client.print(F(" <td><div class='black-circle'><div class='glare'></div></div></td>\n"));
  }

  //Print end of row
  client.print("</tr>\n");

  //Print begining of row
  client.print("<tr>\n");

  //Prints the ON Buttons
  client.print(F(" <td><INPUT class='button' id='on' TYPE=\"button\" VALUE=\"ON - Feeder 2 Timer"));
  client.print(F("\" onClick=\"parent.location='/?J"));
  client.print(F("'\"></td>\n"));

  //Prints the OFF Buttons
  client.print(F(" <td><INPUT class='button' id='off' TYPE=\"button\" VALUE=\"OFF - Feeder 2 Timer"));
  client.print(F("\" onClick=\"parent.location='/?K"));
  client.print(F("'\"></td>\n"));

  //Print first part of the Circles or the LEDs
  if (feeder2Timer == true) {
    client.print(F(" <td><div class='green-circle'><div class='glare'></div></div></td>\n"));
  } else {
    client.print(F(" <td><div class='black-circle'><div class='glare'></div></div></td>\n"));
  }

  //Print end of row
  client.print("</tr>\n");  

  //Closing the table and form
  client.println(F("</table>"));
  client.println(F("</FORM>"));

}

//Reading the Output Statuses
void readOutputStatuses() {
  for (int var = outputLowest; var < outputLowest + outputQuantity; var++)  {
    readInput[var] = digitalRead(var);
  }

}

// send an NTP request to the time server at the given address
unsigned long sendNTPpacket(IPAddress& address)
{
  Serial.println(F("NTPpacket sending"));
  //Ethernet.maintain();
  // all NTP fields have been given values, now
  // you can send a packet requesting a timestamp:
  Udp.beginPacket(address, 123); //NTP requests are to port 123
  Udp.write(packetBuffer, NTP_PACKET_SIZE);
  Udp.endPacket();
  delay(1);
  //Udp.stop();
  Serial.println(F("NTPpacket sended"));
}

void syncTime() {
  if ( Udp.parsePacket() ) {
    Serial.println(F("UDP packet received"));
    // We've received a packet, read the data from it
    Udp.read(packetBuffer, NTP_PACKET_SIZE); // read the packet into the buffer

    //the timestamp starts at byte 40 of the received packet and is four bytes,
    // or two words, long. First, esxtract the two words:

    unsigned long highWord = word(packetBuffer[40], packetBuffer[41]);
    unsigned long lowWord = word(packetBuffer[42], packetBuffer[43]);
    // combine the four bytes (two words) into a long integer
    // this is NTP time (seconds since Jan 1 1900):
    unsigned long secsSince1900 = highWord << 16 | lowWord;
    // now convert NTP time into everyday time:
    // Unix time starts on Jan 1 1970. In seconds, that's 2208988800:
    const unsigned long seventyYears = 2208988800UL;
    // subtract seventy years:
    epoch = secsSince1900 - seventyYears + timeZoneOffset;
    //Serial.println(epoch);    
    NTPSyncTime = currentTime;
    NTPSync = true;    
    Serial.print(F("Time: "));
    Serial.print(((epoch + currentTime / 1000 - NTPSyncTime / 1000)  % 86400L) / 3600);
    Serial.print(F(":"));
    Serial.print(((epoch + currentTime / 1000 - NTPSyncTime / 1000)  % 3600) / 60);
    Serial.print(F(":"));
    Serial.println((epoch + currentTime / 1000 - NTPSyncTime / 1000) % 60);
    EthernetReset();
  }
}

void EthernetReset() {
    //Udp.stop();
    //Ethernet.begin(mac, ip, gateway, subnet);
    //server.begin();    
    //Udp.begin(localPort);  
    // set all bytes in the buffer to 0
    memset(packetBuffer, 0, NTP_PACKET_SIZE);
    // Initialize values needed to form NTP request
    // (see URL above for details on the packets)
    packetBuffer[0] = 0b11100011;   // LI, Version, Mode
    packetBuffer[1] = 0;     // Stratum, or type of clock
    packetBuffer[2] = 6;     // Polling Interval
    packetBuffer[3] = 0xEC;  // Peer Clock Precision
    // 8 bytes of zero for Root Delay & Root Dispersion
    packetBuffer[12]  = 49;
    packetBuffer[13]  = 0x4E;
    packetBuffer[14]  = 49;
    packetBuffer[15]  = 52;    
}

void switchFilter () {    // Switch ON Filter
  if (currentTime > filterIntv + filterSwOffTime) {
    digitalWrite(filterPin, HIGH);
  }
}

void switchAir () {    // Switch ON Air
  if (currentTime > airIntv + airSwOffTime) {
    digitalWrite(airPin, HIGH);
  }
}

void switchCooler () {    // Switch ON/OFF Cooler
  if (celsius > maxTemp) {
    digitalWrite(coolerPin, LOW);
  }
  if (celsius < minTemp) {
    digitalWrite(coolerPin, HIGH);
  }
}

void switchFeeder (float num) {    // Switch ON/OFF Feeder
  int hr = (((epoch + currentTime / 1000 - NTPSyncTime / 1000)  % 86400L) / 3600);
  int m = (((epoch + currentTime / 1000 - NTPSyncTime / 1000)  % 3600) / 60);
  int stepsNum = stepsPerOutputRevolution  * num;  
  if ((hr == feederOnTime[0] || hr == feederOnTime[1]) && m >= 0 && m < feederOnIntv) {   // check interval
    feederON = true;
  } else {
    if (feederMustStop == true) {
      feederON = false;
    }
  }
  if (feederON == true) {
    //Serial.println(F("Feeder ON"));
    stepper.step(-stepsNum);
  }
  if ((hr == feeder2OnTime[0] || hr == feeder2OnTime[1]) && m >= 0 && m < feeder2OnIntv) {   // check interval
    feeder2ON = true;
  } else {
    if (feeder2MustStop == true) {
      feeder2ON = false;
    }
  }
  if (feeder2ON == true) {
    //Serial.println(F("Feeder ON"));
    stepper2.step(-stepsNum);
  }  
}


void switchLight () {    // Switch ON/OFF light
  //Serial.println(F("switchLight"));
  int hr = (((epoch + currentTime / 1000 - NTPSyncTime / 1000)  % 86400L) / 3600);
  //Serial.println(hr);
  int isLght = 0;
  if (isNight == 0) {  // if day
    if (hr >= upLightTime && hr < downLightTime) {   // check interval
      isLght = 1;
    } else {
      isLght = 0;
    }
  } else {    // if night
    if (hr - upLightTime >= 0) {
      isLght = 1;
    } else {
      if (hr < downLightTime) {
        isLght = 1;
      } else {
        isLght = 0;
      }
    }
  }
  if ((isLght == 1) && (readInput[lightPin] == 1)) {
    digitalWrite(lightPin, LOW);
  }
  if (isLght == 0 && readInput[lightPin] == 0) {
    digitalWrite(lightPin, HIGH);
  }
}

void softReset() {
  //Serial.print("currentTime: ");
  //Serial.print(currentTime);
  //Serial.println(" ");    
  //Serial.print("softResetIntv: ");
  //Serial.print(softResetIntv);
  //Serial.println(" ");      
  //Serial.println(F("softReset"));
  //EEPROM.write(301, epoch + currentTime / 1000 - NTPSyncTime / 1000);
  //SD.remove(Filename);
  myFile = SD.open(Filename, FILE_WRITE);
  if (myFile) {
    myFile.println(epoch);
    myFile.close();
  } else {
    Serial.println(F("Cannot open file"));
  }
  delay(100);
  asm volatile ("  jmp 0");
}