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:
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:
After UPD:
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");
}