Introduction: EAL - Coral Feeder Connected
This instructable is a description of my Arduino controlled coral feeder which has taken a step further. It is now able to connect to a network drive (NAS) via wifi and log data to a database. I also made a Windows application making the user able to retrieve the data, and have them represented in an easy readable manner.
As it is now it is only able to log the feedings, but potentially it will log temperature, salinity, and many many more parmeters which is normally measured manually in a reef tank.
When writing this instructable, i assume the reader has looked at my other instructable, and has the basic knowledge of what the coral feeder is.
Step 1: Parts List
- Coral Feeder (see my other instructable).
- 1k Ohm resistor.
- 2k Ohm resistors.
- ESP8266 Wifi Module.
- 3.3V voltage limiter/regulator.
- NAS drive (or computer) with MYSQL database or similar.
- Google (not really a part for the build, but as it's a fairly complex build with somewhat complex software relations, you will run into problems at some point)
Step 2: New Coral Feeder Features
The coral feeder module got a few new features along with the upgrade. I have added some more choices in the menu. You can now get a reading showing your coral feeder IP-address, and have the option to reboot the WiFi module should the coral feeder loose connection to your network for some reason.
During booting and when transmitting data to the NAS-drive, the display will show various informative messages concerning the connectivity. It will tell that it is transmitting and if TCP connection succeeds or errors occur. It will also tell if the feeder has success connecting to your network etc. All display messages can be found in the Arduino code voids: Wifi_Setup() and Transmit().
Note: Depending on your home network IP address class (my coral feeder IP is 10.0.0.48(static)), you will probably need to change som small bits in the Arduino code to make the IP reading right. As i take out a substring with the length of my own IP you should change it to whatever your IP length is. So if your IP is 192.168.0.200 you should change the last number to 88 as your IP is 4 digits longer than mine.
//get connection information from ESP8266 //module and take IP-substring address out of string if (ipRead == true) { IP = (wifi.getLocalIP().c_str()); ipRead = false; } lcd.print("IP: "); lcd.print(IP.substring(75, 84));
Step 3: The Arduino Code
Below you will find the code for the Coral Feeder.
Four libraries is necessary for the code to work and must be downloaded to the Arduino IDE software.
- LiquidCrystal for the LCD
- TimerOne for the timer interrupt making the clock work
- Servo for the servomotor
- ESP8266 for the Wifimodule.
//Prototyping void Clock(); void Feed(); void Wifi_Setup(); void Transmit(); void No_Menu_Default(); //Include library codes: #include LiquidCrystal.h #include TimerOne.h #include Servo.h #include "ESP8266.h" //Definitions for wifi and SQL host #define SSID "MyHomeNetworkName" #define PASSWORD "MyHomeNetworkPassword" #define HOST_NAME "MyNASdriveIPAddress" #define HOST_PORT (80) //NASdrive webserverport --NOT-- SQL port, SQL port goes into PHP script. //Variables declaration int S = 1; int M; int H; int TBF = 0; int Menu_number = 2; int pos_in = 76; int pos_out = 118; int H_feed = 0; unsigned int FPD = 0; bool Feeding_allowed = HIGH; const bool Click = 1; long IdleCounter = 0; long IdleStatic = 120000; String IP; bool ipRead; bool reboot_confirm; char *transmit_data = "GET /feeding.php?value=Booting HTTP/1.1\r\nHost: MyNASdriveIPAddress\r\nConnection: close\r\n\r\n"; //Inputs/Outputs const int Feed_on_off = 33; const int Menu = 32; const int Up = 31; const int Down = 30; const int Shaker = 10; //Define serial ports for ESP8266 Wifi module and set baud communication speed ESP8266 wifi(Serial2); long baud = 115200; //Create servo motor "Feeder" Servo Feeder; //Initialize the LCD library with the numbers of the interface pins LiquidCrystal lcd(28, 26, 5, 4, 3, 2); void setup() { //Open serial comm with "baud" speed Serial2.begin(baud); // set up the LCD's number of columns and rows: lcd.begin(16, 2); // Initialize timer1, and set a ~1 second period, corrected for program read time // and attach the timer service routine interrupt to seperate void (Clock) Timer1.initialize(992275); Timer1.attachInterrupt(Clock); //Buttons declaration pinMode(Feed_on_off, INPUT_PULLUP); pinMode(Menu, INPUT_PULLUP); pinMode(Up, INPUT_PULLUP); pinMode(Down, INPUT_PULLUP); //Shaker motor declaration pinMode(Shaker, OUTPUT); //Servomotor declaration & initialization Feeder.attach(24); Feeder.write(pos_in); //ESP8266 Wifi module startup by jump to void Wifi_Setup(); } void loop() //Main program { //Back to default at 2 min idle if (digitalRead(Menu) == LOW || digitalRead(Up) == LOW || digitalRead(Down) == LOW && Menu_number != 0 ) { IdleStatic = millis() + 120000; } IdleCounter = millis(); if (IdleStatic < IdleCounter && IdleCounter < IdleStatic + 50) { Menu_number = 0; lcd.clear(); } //Enter Menu if (digitalRead(Menu) == LOW) { delay(200); Menu_number = Menu_number + 1; lcd.clear(); } //Reset Menu_number if (Menu_number == 6) { Menu_number = 0; } //Menu switch case switch (Menu_number) { case 1: //Forcefeed { //LCD info lcd.setCursor(0, 0); lcd.print("Menu: Forcefeed "); lcd.setCursor(0, 1); lcd.print("Press Feed "); //Forcefeed if (digitalRead(Feed_on_off) == LOW) { delay(200); transmit_data = "GET /feeding.php?value=Manual_feed HTTP/1.1\r\nHost: MyNASdriveIPAddress\r\nConnection: close\r\n\r\n"; Feed(); } } break; case 2: //Set clock { //LCD info lcd.setCursor(0, 0); lcd.print("Menu: Set clock"); //LCD CLOCK if (H < 10) { lcd.setCursor(6, 1); lcd.print("0"); lcd.setCursor(7, 1); lcd.print(H); } else { lcd.setCursor(6, 1); lcd.print(H); } if (M < 10) { lcd.setCursor(8, 1); lcd.print(":"); lcd.print("0"); lcd.setCursor(10, 1); lcd.print(M); } else { lcd.setCursor(8, 1); lcd.print(":"); lcd.print(M); } //Set clock //Hours if (digitalRead(Up) == LOW) { delay(200); H = H + 1; } //Minutes if (digitalRead(Down) == LOW) { delay(200); M = M + 1; } if (H > 23) { H = 0; } if (M > 59) { M = 0; } } break; case 3: //Feedings per day (FPD) { //LCD info lcd.setCursor(0, 0); lcd.print("Menu: Set FPD"); lcd.setCursor(0, 1); lcd.print("Daily Feeds"); if (FPD < 10) { lcd.setCursor(15, 1); lcd.print(FPD); lcd.setCursor(14, 1); lcd.print(" "); } if (FPD > 9) { lcd.setCursor(14, 1); lcd.print(FPD); } //Adjust FPD up if (digitalRead(Up) == LOW) { delay(200); FPD = FPD + 2; if (FPD == 10) { FPD = 12; } if (FPD == 14) { FPD = 24; } TBF = 24 / FPD; H_feed = H + TBF; if (H_feed >= 24) { H_feed = H_feed - 24; } } //Adjust FPD down if (digitalRead(Down) == LOW) { delay(200); FPD = FPD - 2; if (FPD == 22) { FPD = 12; } if (FPD == 10) { FPD = 8; } TBF = 24 / FPD; H_feed = H + TBF; if (H_feed >= 24) { H_feed = H_feed - 24; } } //Prevent FPD > 24 if (FPD > 24) { FPD = 0; } ipRead = true; } break; case 4: //IP address { //LCD info lcd.setCursor(0, 0); lcd.print("Menu: IP-address"); lcd.setCursor(0, 1); //get connection information from ESP8266 //module and take IP-substring address out of string if (ipRead == true) { IP = (wifi.getLocalIP().c_str()); ipRead = false; } lcd.print("IP: "); lcd.print(IP.substring(75, 84)); } break; case 5: //Reinitialize wifi connection { //LCD info lcd.setCursor(0, 0); lcd.print("Menu: Wifi "); lcd.setCursor(0, 1); lcd.print("Reboot: Press UP"); //Reboot ESP8266 wifi module if (digitalRead(Up) == LOW) { delay(200); lcd.clear(); lcd.setCursor(0, 0); lcd.print(" Confirm Reboot "); lcd.setCursor(0, 1); lcd.print(" Up=Yes Down=No "); reboot_confirm = true; } while (reboot_confirm == true) { if (digitalRead(Up) == LOW) { delay(200); transmit_data = "GET /feeding.php?value=Booting HTTP/1.1\r\nHost: MyNASdriveIPAddress\r\nConnection: close\r\n\r\n"; reboot_confirm = false; Wifi_Setup(); } else if (digitalRead(Down) == LOW) { delay(200); reboot_confirm = false; return; } } } break; default: void No_Menu_Default(); //Daily operation out of menu { //LCD CLOCK lcd.setCursor(0, 0); lcd.print(" "); lcd.setCursor(12, 0); lcd.print(" "); if (H < 10) { lcd.setCursor(4, 0); lcd.print("0"); lcd.setCursor(5, 0); lcd.print(H); } else { lcd.setCursor(4, 0); lcd.print(H); } if (M < 10) { lcd.setCursor(6, 0); lcd.print(":"); lcd.print("0"); lcd.setCursor(8, 0); lcd.print(M); } else { lcd.setCursor(6, 0); lcd.print(":"); lcd.print(M); } if (S < 10) { lcd.setCursor(9, 0); lcd.print(":"); lcd.print("0"); lcd.setCursor(11, 0); lcd.print(S); } else { lcd.setCursor(9, 0); lcd.print(":"); lcd.print(S); } //Feeding on/off toggle if (digitalRead(Feed_on_off) == LOW) { delay(200); Feeding_allowed = !Feeding_allowed; lcd.clear(); } //LCD info if (Feeding_allowed == LOW) { lcd.setCursor(2, 1); lcd.print("Feeding Off"); } else if (FPD == 0) { lcd.setCursor(1, 1); lcd.print("Please set FPD"); } else { lcd.setCursor(0, 1); lcd.print("Feeding at"); if (H_feed < 10) { lcd.setCursor(11, 1); lcd.print("0"); lcd.setCursor(12, 1); lcd.print(H_feed); } else { lcd.setCursor(11, 1); lcd.print(H_feed); } lcd.setCursor(13, 1); lcd.print(":"); lcd.print("00"); } //Feeding conditionals //First feeding cycle after FPD change is reset at H == 10 to bind the cycle to aquarium light schedule //Normal feeding if (H_feed == H && M == 0 && S == 0 && Feeding_allowed == HIGH && FPD > 0 || H == 10 && M == 0 && S == 0 && Feeding_allowed == HIGH && FPD > 0) { transmit_data = "GET /feeding.php?value=Autofeed HTTP/1.1\r\nHost: MyNASdriveIPAddress\r\nConnection: close\r\n\r\n"; Feed(); H_feed = H + TBF; } //Next feed adjustment at feeding off else if (H_feed == H && M == 0 && S == 0 && Feeding_allowed == LOW && FPD > 0 || H == 10 && M == 0 && S == 0 && Feeding_allowed == LOW && FPD > 0) { H_feed = H + TBF; } else { digitalWrite(Shaker, LOW); Feeder.write(pos_in); } if (H_feed >= 24) { H_feed = H_feed - 24; } } break; } } void Wifi_Setup() //Initialization of wifi module { //LCD info lcd.clear(); lcd.setCursor(0, 0); lcd.print(" Initializing "); //Set wifi module to softAP mode if (wifi.setOprToStationSoftAP()) { lcd.setCursor(0, 1); lcd.print("Set to SAP: OK"); } else { lcd.clear(); lcd.setCursor(0, 1); lcd.print("Set to SAP: ERR"); } //Join wifi network if (wifi.joinAP(SSID, PASSWORD)) { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Join AP success "); lcd.setCursor(0, 1); String IP = String(wifi.getLocalIP().c_str()); lcd.print("IP: "); lcd.setCursor(4, 1); lcd.print(IP.substring(75, 84)); } else { lcd.clear(); lcd.setCursor(0, 0); lcd.print("Join AP failure "); } delay(2000); //Set client to single connection lcd.clear(); lcd.setCursor(0, 0); lcd.print(" Initializing "); if (wifi.disableMUX()) { lcd.setCursor(0, 1); lcd.print("Set Single: OK"); } else { lcd.clear(); lcd.setCursor(0, 1); lcd.print("Set Single: ERR"); } delay(2000); if (transmit_data == "GET /feeding.php?value=Booting HTTP/1.1\r\nHost: MyNASdriveIPAddress\r\nConnection: close\r\n\r\n") { Transmit(); } lcd.clear(); } void Transmit() //Transmit data to NASdrive { //LCD info lcd.setCursor(0, 0); lcd.print("Communicating "); lcd.setCursor(0, 1); lcd.print("with server "); delay(1000); //Create TCP connection with NASdrive if (wifi.createTCP(HOST_NAME, HOST_PORT)) { lcd.setCursor(0, 0); lcd.print("Create TCP: OK\r\n"); lcd.setCursor(0, 1); lcd.print("Transmitting "); } else { lcd.setCursor(0, 1); lcd.print("Create TCP: ERR\r\n"); } //Write data to NASdrive //get template "GET /feeding.php?value=XXXXX HTTP/1.1\r\nHost: MyNASdriveIPAddress\r\nConnection: close\r\n\r\n" wifi.send((const uint8_t*)transmit_data, strlen(transmit_data)); wifi.releaseTCP(); delay(1000); lcd.clear(); } void Feed() //Feeding { digitalWrite(Shaker, HIGH); delay(40); digitalWrite(Shaker, LOW); delay(200); digitalWrite(Shaker, HIGH); delay(40); digitalWrite(Shaker, LOW); delay(200); Feeder.write(pos_out); delay(200); Feeder.write(pos_in); delay(200); lcd.begin(16, 2); Transmit(); } void Clock() //System clock { //Clock S = S + Click; if (S > 59) { S = 0; } if (S == 0) { M = M + 1; } if (M > 59) { M = 0; } if (M == 0 && S == 0) { H = H + 1; } if (H > 23) { H = 0; } }
Step 4: The Database
My database is running on a Synology NAS drive which as standard has MariaDB in its package center. MariaDB is a variety of MySQL which will work in this project as well. No other types of databases will work with the windows app or arduino code as there has been implemented specific libraries for MySQL into the codes.
The database can be managed and built in PHPMyAdmin, a free software for this purpose. I have added one database with two tables, one for the actual feeding data, and then a table with dummy temperature data to show how the windows app would be able to read from different tables.
The tables has 3 rows each; Id, Time (time/data stamp) and then a value. When making a table you can either do it manually in PHPMyAdmin or you can write a SQL command which then produces the table. To make it work with the php script, arduino and windows code the table name should be called Feedings (and Temperature for the dummy table).
When making your DB you should remember to check the A.I. (auto increment) in the ID structure editor. You should also make a user for your applications and grant it full access to the database, as otherwise you will not be able to connect from external sources.
Depending on which database and database edition (im using MariaDB 10.0.32) you are using, there will be small variations in the SQL codes necessairy. Please consult the documentation available on the specific database.
Step 5: The PHP Schript
To be able to transmit data from the arduino to the database you will need to make a PHP-script file and paste it into the web-folder on your NAS drive. The arduino then connects to the webserver with GET commands which the webserver running your database then picks up and processes. Attached you will find a textfile with the PHPcode. For making it work with your DB and arduino; fill in your own data in the [variables] (remove the brackets as well), rename the file to feedings.php and copy it into your webfolder on your NASdrive.
Attachments
Step 6: The Windows Application
The windows application is programmed in C# and is made as a windows application form in Microsoft Visual Studio. The full program is attached as ZIP folder.
Besides showing the amount of data you want, the application will scan the data looking for "Booting" which the coral feeder sends to the database when starting up or reconnecting to your network. So if your coral feeder has been disconnected or restarted, the application till notify you, as the data could be incomplete.
The application will also tell you if you have typed the wrong password or username etc, and can read and show all errorcodes sent from your MySQL database if something goes wrong.
Attachments
Step 7: Security Issues
When making a project like this you will need to take some security risks into considerations. In my case the database password, username and other credentials are hardcoded into the PHP-script. This makes the database very vulnerable to SQL injections which can destroy your database in a worst case scenario. So unless you are sure noone other than you and your closes family and friends has access to your network, you should make the arduino pass the credentials to the script instead, making it more safe to use in public(this dosen't make it bulletproof though). When setting up your webstation on your NAS-drive you should make rules that only allow certain IP adresses access to the database. If you do this remember to log onto your router and make sure your arduino has a static IP address.
There are other measures to you can take if afraid of malicious attacks on your database. If you think more preventive measures are necessairy there's plenty of reading material out there on the www.
Step 8: The Wiring
On the picture you will find the wiring schematic. For your specific ESP8266 module please consult the documentation as there are many variations of these.
It is very important that you never supply the ESP8266 with more than 3.3 volt (3.6 volt max) as you will fry it if so. The RX pin should never recieve more than 3.6 volt as well. This is why i had to make a voltage divider, in my case with 1k Ohm resistor and a 2k Ohm resistor on each side and then connect the RX(esp) in the middle. The TX(arduino) should be connected to the low (1k) end and GND to the high (2k)end of the divider. This way the RX pin on the esp will recieve a 3.3v signal as it is supposed to.