The Planted Tank Forum banner

Hunter X's Arduino Whole Tank Controller (52K Warning)

55K views 226 replies 23 participants last post by  HunterX 
#1 · (Edited)
Hello Everyone!

Parts list on post #13
Code on post #156

A few months back I stumbled across an Arduino thread. I can't remember what exactly led me there but I remember a thread about Auto Dosing FERTS sparked my interest. While I was back home, I'm currently deployed to Kuwait, I was growing tired of dosing FERTS every morning before leaving for work. Not so much the mixing of FERTS and playing in the tank, rather having to get up 20 mins early for work was getting old. Naturally, the ability to dose FERTS automatically pecked my interest immediately. Like many I started researching and learning how the "Arduino" worked. As I researched it I noticed several individuals were using it to control parts of their tanks. I hadn't noticed a design that controlled all of aspects of the tank. With that being said, I decided I wanted to try and build one that would control all my filters, heaters, FERT dosing, power heads, CO2, lights and feeding. If had the ability to plumb an ATO I would have it control that as well. (currently renting my house). Seeing how I wanted to control so many components, the Arduino Mega was the only board that would have enough inputs/outputs to meet the requirement.

1ST Step: Fortunately for me, the Army saw fit in its wisdom to have a "Wood Shop" on post so Soldiers could have something to do during their down time. That is where I started. I originally planned to have the controller box fit under my stand but after I completed the box, I decided it turned out locking too nice to be hid under the stand. So it will sit beside the stand.



The stand was designed to hold three bottles of premixed FERTS under it. MACRO, MICRO and FE. I figured that would cover the FERT requirements. Below you can see the 3 dosing pumps as well as how they are wired. I choose to use bricks with quick disconnects to keep the wire as neat and tidy as I could.





Next to the DC motor controller are the three variable DC controllers. I stacked them to create more room in the box. They are used to reduce the 12 volts from the 12v AC to DC converter to 9v for the Arduino, 5v for the relay board, and then 5v for the Sensor Shield. I will run 12 volts straight from the AC to DC converter to power the 12v for the DC motor controller.

Speaking of power, the below pictures shows how I take the 120v coming into the box to the distro blocks and then to the relay board so I have the ability to turn on and off each of the 120 plugs. Each of the fist tank components will plug into the 120v sockets (not the lights).





I loved the idea of everything being on a schedule which is controlled by the Arudino but what if I wanted to shut down just one pump to clean or one of my power heads. I didn't like the idea of having to always unplug it or power down the whole Arduino in order to do that. I decided to have a button that I could press to shut down the corresponding item/relay so I could perform whatever maintenance I needed to do. So across the front you see 8 green buttons. The buttons light up when the corresponding component is power up. The LED within the button works independently of the button. Remember the Arduino controls the relays automatically so if the programming has that component "on" the corresponding button will light up. But if I push the button the automatic control will be interrupted and the corresponding relay will shot off. Ok but what if I want to shut everything down do I press all the green buttons or unplug the Arduino? NAH that is what the two red buttons below the LCD do. If I bush the red button on the left it shuts down everything except for the lights. So guess what the red button on the right does? Give up? It shuts the lights off. With all the buttons all you have to do is release them and the automatic control will take back over.

I still have to get the AC to DC converter installed, it was on back order for three months. I also have to get the DIN connectors installed so the controllers for the LED can plug into the back along with temp probe. The heater is turned off and on by the arduino based on the TEMP of the tank. So as long as the probe doesn't fail it doesn't matter if the heater gets stuck on, the Arduion will just kill the power to heat when it gets to the HIGH Temp Limit. I have to get the DIN for the autofeeder installed to. So yeah I'm about 85%. Below are a few more pictures. I would love any insight or comments you may have!







 
See less See more
9
#73 ·
Hey everyone, I got the replacement nano router, and finally got it up and running. It is working really well! I put together a quick little sketch with Xively's service to read a DS18B20 temp sensor. You can check out the feed here: https://xively.com/feeds/835109294

I gotta say, I'm pretty impressed. The nano router was super easy to setup, and Xively has tons of examples so it was easy to put together a working test. Being able to graph and access my data from anywhere is really cool. I read that you can supposedly turn things on and off through Xively, but I haven't figured that out yet.

Hunter, I know you were pretty excited to hear my thoughts on the nano, and so far its obviously a big thumbs up, but I just wanted to make sure you know that you also need an ethernet shield, or another way to connect the arduino to ethernet (something like this will work too: http://www.robotshop.com/en/w5100-ethernet-network-module.html). I didn't make that clear when I first mentioned it. If you already have one, then perfect, get the router. I was lucky enough to get it on sale for like $15. If you don't already have one check out Adafruit's wifi shield for $40:
http://www.adafruit.com/products/1491

They also have a low cost breakout board for $35:
http://www.adafruit.com/products/1469

If I didn't have the ethernet shield already, I would go for one of those. However, I don't know if they ship to APOs.
 
#77 ·
Hey everyone, I got the replacement nano router, and finally got it up and running. It is working really well! I put together a quick little sketch with Xively's service to read a DS18B20 temp sensor. You can check out the feed here: https://xively.com/feeds/835109294

I gotta say, I'm pretty impressed. The nano router was super easy to setup, and Xively has tons of examples so it was easy to put together a working test. Being able to graph and access my data from anywhere is really cool. I read that you can supposedly turn things on and off through Xively, but I haven't figured that out yet.

Hunter, I know you were pretty excited to hear my thoughts on the nano, and so far its obviously a big thumbs up, but I just wanted to make sure you know that you also need an ethernet shield, or another way to connect the arduino to ethernet (something like this will work too: http://www.robotshop.com/en/w5100-ethernet-network-module.html). I didn't make that clear when I first mentioned it. If you already have one, then perfect, get the router. I was lucky enough to get it on sale for like $15. If you don't already have one check out Adafruit's wifi shield for $40:
http://www.adafruit.com/products/1491

They also have a low cost breakout board for $35:
http://www.adafruit.com/products/1469

If I didn't have the ethernet shield already, I would go for one of those. However, I don't know if they ship to APOs.
I love it brother. I especially liked how the Xively sight tracks that data in a graph format as well. I went ahead and ordered the breakout board. I can['t wait to get it up and running. Thank you very much for taking the time to research and then post your findings. Also for posting where to purchase the parts I needed. Very Cool!
 
#74 ·
Here's the sketch if anyone wants to put together something similar to test out. It's based off of Xively's wifi tutorial, so there's instructions in there for connecting a wifi shield, too, if anyone has one.

Code:
/*
##Xively WiFi Sensor Tutorial##
This sketch is designed to take sensors (from photocell) and upload the values to Xively
at consistant intervals. At the same time it gets a setable value from Xively to adjust the brigthness
of an LED. This sketch is reusable and can be adapted for use with many different sensors.
Derived from Xively Ardino Sensor Client by Sam Mulube.
 
By Calum Barnes 3-4-2013
BSD 3-Clause License - [http://opensource.org/licenses/BSD-3-Clause]
Copyright (c) 2013 Calum Barnes
*/
#include <SPI.h>
#include <WiFi.h>
#include <Ethernet.h>
#include <OneWire.h>
#include <DallasTemperature.h>
#include <HttpClient.h>
#include <Xively.h>

//Uncomment this section if using ethernet shield
// MAC address for your Ethernet shield
byte mac[] = { 0x90, 0xA2, 0xDA, 0x0D, 0x26, 0x4E };
IPAddress ip(192,168,0,10); //<<< ENTER YOUR IP ADDRESS HERE!!!

//This line may not be neccessary, comment out if having trouble
EthernetServer server(80);

/*Uncomment this section if using WiFi shield 
char ssid[] = "SSID_HERE"; //  your network SSID (name) 
char pass[] = "PASS_HERE";    // your network password (use for WPA, or use as key for WEP)
int keyIndex = 0;            // your network key Index number (needed only for WEP)
 
int status = WL_IDLE_STATUS;
 */
 
#define ONE_WIRE_BUS 7
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
 

 
// Your Xively key to let you upload data
char xivelyKey[] = "8Ot0JKtOfqg7yLaG9QhNN5s3HSqbPl9WnO7c4wrBptpiq6ER";
//your xively feed ID
#define xivelyFeed 835109294
//datastreams
char sensorID[] = "Temperature";
//char ledID[] = "LED_CHANNEL";

 
// Define the strings for our datastream IDs
XivelyDatastream datastreams[] = {
  XivelyDatastream(sensorID, strlen(sensorID), DATASTREAM_FLOAT),
};
// Finally, wrap the datastreams into a feed
XivelyFeed feed(xivelyFeed, datastreams, 1 /* number of datastreams */);
 
EthernetClient client;
XivelyClient xivelyclient(client);
 
 /*Uncomment this section if using WiFi Shield
void printWifiStatus() {
  // print the SSID of the network you're attached to:
  Serial.print("SSID: ");
  Serial.println(WiFi.SSID());
 
  // print your WiFi shield's IP address:
  IPAddress ip = WiFi.localIP();
  Serial.print("IP Address: ");
  Serial.println(ip);
 
  // print the received signal strength:
  long rssi = WiFi.RSSI();
  Serial.print("signal strength (RSSI):");
  Serial.print(rssi);
  Serial.println(" dBm \n");
}
*/




void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
  ///Ethernet connection setup
  Ethernet.begin(mac, ip);
  server.begin();
  
  
  
  
  Serial.println("Starting single datastream upload to Xively...");
  Serial.println();
 
 /*Uncomment this section if using WiFi Shield
  // attempt to connect to Wifi network:
  while ( status != WL_CONNECTED) { 
    Serial.print("Attempting to connect to SSID: ");
    Serial.println(ssid);
    status = WiFi.begin(ssid, keyIndex, pass);
    // wait 10 seconds for connection:
    delay(10000);
  } 
  Serial.println("Connected to wifi");
  printWifiStatus();
}
 */
 }
void loop() {
  sensors.requestTemperatures();//send command to retrieve temperature
  
///////////////////////////////////////////////////////
  //read sensor values
  int sensorValue = sensors.getTempFByIndex(0);
  datastreams[0].setFloat(sensorValue);
  
  /*
  //print the sensor valye
  Serial.print("Read sensor value ");
  Serial.println(datastreams[0].getFloat());
 */
  //send value to xively
  Serial.println("Uploading it to Xively");
  int ret = xivelyclient.put(feed, xivelyKey);

  
  
  //delay between calls
  delay(1000);
}
 
#78 ·
It response to the great advice, I got the diodes installed to prevent any voltage spikes while any of the motors are "coasting"! Cycled them through a few times and everything appears to be working as expected. I did have to move put the controls for the motors on a different set of outputs. I need the outputs the motors were connected to for the new wifi breakout board. Thanks to BigD I'm now anxiously waiting for another shipment. :) Thanks BigD!
 
#82 ·
No problem, man, glad I could help. I've had an autodoser based off of Shift's work that I have been wanting to expand to an all out controller. I had already done a bunch of research into components but never really did anything. This thread has really inspired me to finally get moving on it, so thank you Hunter. This stuff is a lot of fun, and this collective work is really what arduino is all about. It will be fun to see what all of us here put together.

Scapegoat, you said you need to find some code to program the GPIO pins. Are you using something other than Arduino? Raspberry Pi or Beaglebone?
 
#81 ·
I haven't started any programming yet. Still research the handful of node gpio libraries out there. it's looking like I'll need to figure a mix between python and javascript to get mine to work.

i'm trying to plan the programming out a bit more in order to have an easier time of adding/removing/moving things around. such as a way to register components to gpio pins; with probably a plain text config file.

I might get started on some preliminary stuff to cut my teeth on tonight. something simple... turning an LED on/off lol. that'll go far though, controlling a gpio pin. pretty much the basis for everything.
 
#83 ·
Hey. Yeah, I'm using a raspberry pi. I wanted a gui from the start. And being a web developer I've decided to go with nodejs to program the controller. More specifically meteorjs, a websocket based framework. So I'll end up with a web based guide accessible on my home network that'll be used to set my timing and view data like temps.

And not really find. There are a handful of node gpio libraries out there, it's just deciding on which one
 
#86 ·
bigd,

No the one I have does have a line conditioner built into but not a fuse. It would be a great idea to have a fuse built into the high voltage side. I would also recommend a power switch built into the power entry module. They were on back order when I ordered mine so I bought the plain version.
 
#87 ·
BOX Plan

I was asked what the dimensions of the box were so I thought I would upload the plan in case anyone else was curious. It's drawn to scale so I knew everything would fit. It did change some but that is to be expected.
 

Attachments

#88 ·
It might not be a bad idea to add a second fan into the box on the opposite side, so one is blowing in and the other is blowing out. With just one blowing out you will have negative pressure, and essentially create a vacuum inside the case. You could also relieve this issue just by drilling some holes into the case, but if it does get really hot in there a second fan would really help.
 
#89 · (Edited)
Sorry I didn't make that more clear. Yes you have to have some way for air to get into the box when the fan is on. For me, I made sure the lid doesn't seal all the way. There is about a 1/16 gap along the back edge to allow air in. Too be honest, the box just doesn't get that hot. My room stays about 63F and the box stays at about 74F. The fan doesn't even kick on until it gets 78. So in my current conditions, it hardly ever comes on.
 
#93 ·
Looks like i'll be sharing some things sometime this weekend.

I believe I have all my major components except for that FET board. I've been using a 12v AC adapter for prototyping. I've currently got the Pi set up to read, and display, temps from probes, on a web page hosted by itself.

Further, I am capable of turning 6 of the 8 outlets on and off via that same web page. Last night I started work on the scheduling code. I'll be able to set the schedule from the GUI, and save it. Right now it'll accept military time in the following format 00:00. on and off each day. I may change that to just be a daily on/off, instead of doing it by the day. that'll be easier, and makes more sense since it's really just lights on/off and co2 on/off.

The next part of that, though, is having the option to set the outlet based on a data from probes. in this version, just the temp.

I'm debating on wiring up 2 of the pins to control 2 outlets each, so all 8 are controlled as 4x1 and 2x2, or keeping 2 always on. I need to consider what i have need for. I do have 2 heaters.

Once the FET board comes in i'll handle the pumps and their schedules. I've just been using a breadboard on my desk to do this, but i'm thinking it's time to slice off a piece of plywood to mount everything to in order to produce a full prototype that doesn't have to be put away in parts each night. (2 year olds and electronics laying around don't mix)

I've some ideas for way down the line, but i have a second Pi for developing more things. I need to go through and clean out code and move some code off to their own files for easier maintenance. this was a lot easier than I thought.

Many thanks to Hunter for providing a beautifully laid out controller that was easy to follow. Much inspiration was had by all!
 
#94 · (Edited)
I just bought a 120 gallon tank for $450. Let me know if you think I got a good deal on it.

http://springfield.craigslist.org/for/4292091129.html

Scape,

That is awesome. I haven't dealt wit the Pi's at all but it sounds like you're all over it. I wish I was able to control my controller from the net as well. I have to travel all the time because of the Army and that would be a very convenient feature to have. I can't wait to see what you have come up with!
 
#95 ·
I just bought a 120 gallon tank for $450. Let me know if you think I got a good deal on it.

http://springfield.craigslist.org/for/4292091129.html

Scape,

That is awesome. I haven't dealt wit the Pi's at all but it sounds like you're all over it. I wish I was able to control mine controller from the net as well. I have to travel all the time because of the Army and that would be a very convenient feature to have. I can't wait to see what you have come up with!
It's been a bit of a trial and error... it isn't secure enough to be accessed outside the network. It wouldn't be difficult to add in some authentication, which i plan to do. My biggest concern is that I've been unable to run it outside of being root user; which is a huge security hole for this project. the GPIO pins require root access, and i've tried some plugins that are supposed to give me that access. I might have to just change ownership of the gpio files to the pi user.

I do want to, eventually, make it live so folks can go to the url and see parameters. that would be even more useful if you're away and you've got a number of probes set up.

Once things started coming together, making things happen was easy. figuring out the best way to make the system configurable outside code is a little more difficult.

I've got a database that is getting pinged every minute for outlets w/ an on or off of the current time. The temperature sensor file, on the other hand, is being read every second. though, I figure I can probably change that to once a minute as well.

I want to get a pH probe hooked up soon to graph co2 introduction and pH drop, to control when the co2 gets turned on/off while the lights are on.

I think the end product will be neat. using some libraries out there, it'd been a rather trivial project, so I'm excited to share.
 
#97 ·
Good stuff everyone. I'm so glad to see all the input everyone is providing.

I have never used the Pi or read anything about it. Anyone care to detail the major differences between the Pi and Arduino?
 
#103 ·
My controller is "online" Here is the link if you want to check it out.

https://xively.com/feeds/76312450

I am concerned about the code used to connect the CC3000 (WiFi board) to the net. It has several "while" commands in it. This causes the whole project to become unresponsive while the CC3000 is connecting and doing it's thing. Even worse, if the CC3000 is trying to connect and a connection is not available, it will hang in the "while" loop causing the controller to be rendered useless. It could be a major issue if the controller hangs while the heater is on. The heater would stay on and boil the tank. Same scenario with any number of the connected devices.

Any one out there willing to take a look at the code and see if I could substitute the "while" commands with something else? Below is a link to the code I used from Adafruit's website.

http://learn.adafruit.com/adafruit-cc3000-wifi-and-xively/arduino-sketch
 
#109 ·
These are really 2 very different issues.
The problems with unresponsive UI is not really something you can do much about unless it at some point waits for anything that is not going to happen. Allowing the code to do something else while waiting would require the use of software interrupts, something I don't think the Arduino supports.
The danger of 'hanging' during connection is a major problem if not handled. The easiest way to address that would be to store the time ( millis() ) at which the wait begins, and then break the loop if more than X seconds have passed.
 
#105 ·
Hey guys, what are your thoughts about keeping the ends of the fertilizer tubes in the tank, as opposed to having them hang over the edge and dripping in? When they just drip in you have to clean the ends of the tubes every day or so, while leaving them in the water will eliminate this problem. My only concern with this is slowly leaching excessive fertilizer into the tank throughout the day. Do you think this extra amount of fertilizer wouldnt be a concern as long as I'm doing weekly water changes?
 
#108 ·
I haven't thought that far ahead yet. You really shouldn't get too much FERT leaking into the tank from the line though. The pumps are designed to keep the from happening. Granted as the wear out you may get some leaking into your tank. Like you stated though, I wouldn't expect it to be enough to cause a concern.

Hunter, maybe during the while loop add in a counter, and if it attempts to connect and fails three times exit the while loop so it continues to control the tank normally. Or instead of a counter, only give it 60 seconds to connect, if that fails exit the loop.
I was thinking along the same line. I have posted the issue on the Arduino page hoping someone may be able to come up with something to fix that issue. Wish me luck!
 
#111 ·
Below is one small piece of the code that is a problem. If the DHCP doesn't connect, it just keeps delaying (100) over and over until it does again causing the hole system to just wait until it connects. There has to be a better way. LOL. I do need to dig into the cc3000.checkDHCP() function to understand what all it is doing. Haven't got that far yet.

Code:
while (!cc3000.checkDHCP())  
{    
delay(100); 
}
 
#112 ·
Yeah, that is pretty dodgy in most real-life cases.
What you could do is something like this:
Code:
long startTime = millis();
while (!cc3000.checkDHCP())  
{    
delay(100);
if(millis() > startTime + 10000 ) { break; }
}
This will 'give up' checking for a DHCP server after 10 seconds. You will probably have to do some additional error handling after this code if something relies on the Internet connection to work, but I hope you get the idea. :)
 
#120 ·
First of all: If you have not yet put this whole 'send data' section into a function by itself, you should do so now.
I tried to do that but had an issue with the temperature floats. When I declared them globally, the code wouldn't compile. As you know if I can't declare the globally then I can't use them in a function. I'm sure there is an easy fix to this but I haven't dug too deep into it yet.

This section should also be escaped with a timeout like the other one, otherwise you can have an endless loop here as well:
Code:
 while (ip == 0) {
    if (! cc3000.getHostByName(WEBSITE, &ip)) {
      Serial.println(F("Couldn't resolve!"));
    }
    delay(500);
  }

I did just that. Did I do it correctly?

Code:
long startTime2 = millis();
          while (client.connected()) 
            {
              while (client.available()) 
              {
                char c = client.read();
                Serial.print(c);
                if(millis() > startTime2 + 10000 ) 
                   { 
                    break;                       
                   }
              }
            }


I would also move the above delay inside the if() statement, there really is no reason to delay anything if the IP is resolved correctly.
Done!
 
#122 ·
I tried to do that but had an issue with the temperature floats. When I declared them globally, the code wouldn't compile. As you know if I can't declare the globally then I can't use them in a function. I'm sure there is an easy fix to this but I haven't dug too deep into it yet.
Well if the floats simply will not work for some reason, you can always pass them as parameters to the function. That is how you would normally do things elsewhere, global variables are often considered bad design in programming in general.

I did just that. Did I do it correctly?
Yes, that looks just fine. But if you are using elapsedMillis the time keeping should be done a little differently, much like the example at playground.arduino.cc/
 
#127 · (Edited)
Well if the floats simply will not work for some reason, you can always pass them as parameters to the function. That is how you would normally do things elsewhere, global variables are often considered bad design in programming in general.
I took care of that by just pulling all the data again within the function.

Code:
sensors.requestTemperatures();  // Send the command to get temperatures
  float temperature1 = sensors.getTempFByIndex(1); // Tank Temp
  float temperature0 = sensors.getTempFByIndex(0); // Box Temp  
  int temp1 = temperature1;
  int temp0 = temperature0;
Yes, that looks just fine. But if you are using elapsedMillis the time keeping should be done a little differently, much like the example at playground.arduino.cc/
I didn't use elapsedMillis for this just yet. I wanted to make sure it would work. Once I get it working I will use the elapsedMillis syntax. It isn't exiting the while loop in 10 seconds like it should. I'm going to keep playing with it.

I do have a question though. Now that I have the "wifi" stuff set as a function, would it be possible to exit that function, say after 120 seconds, even if its hung in a "WHILE" loop? It's my understand that if it's in the while loop it will not be looping to compare a time statement to exit if the appropriate amount of time has expired. What are you thoughts Ben?

Code:
if (timer0 > interval)
       {
         wifi();
         timer0 = 0;   //resets the time to 0 so the counting starts over 
       }
 
#128 ·
I do have a question though. Now that I have the "wifi" stuff set as a function, would it be possible to exit that function, say after 120 seconds, even if its hung in a "WHILE" loop? It's my understand that if it's in the while loop it will not be looping to compare a time statement to exit if the appropriate amount of time has expired. What are you thoughts Ben?

Code:
if (timer0 > interval)
       {
         wifi();
         timer0 = 0;   //resets the time to 0 so the counting starts over 
       }
Unfortunately that is not going to work. Making it do something that is not part of the currently executed code, like you describe, would require a timer interrupt, and I don't think it is possible to use interrupts to interfere with the execution of other code. You will have to identify where the code could be hanging and then introduce a break condition each of those places. This is strictly single threaded execution, so that kind of 'management' is unfortunately not really possible.
The closest you get is a watchdog timer, but that will reset the device after a set amount of time without interaction..
 
This is an older thread, you may not receive a response, and could be reviving an old thread. Please consider creating a new thread.
Top