View Full Version : Arduino Aquarium Controller
joopie
11/07/2013, 02:13 PM
I thought i would share what i have going so far for my aquarium. A few months back i purchased a 90 gallon tank with built in over flow. Since then i have been working on building the stand, canopy, and sump. Most of it is complete, i have been working with arduino and web server to setup the automation and control processes of the tank. Here is what i have so far...
http://www.rareglitch.net/overview.htm
Lets not try to break it or guess the password =)
So currently i have:
-automated control for the heater, with set point of 77.80 decreasing, resets at 78.20.
-auto top off via a solenoid and ultrasonic range finder.
-automated lights that get brighter/dimmer depending on the schedule i have set.
-cooling fans for LEDs will kick on at 85 and off at 80.
-ph probe
i am able to track and trend whatever i want on the web site.i am also able to control every process after inputting a password.
A quick explanation of how it works:
Inputs are provided to the arduino which processes the data and provides any outputs(relays,solenoids). The inputs/outputs are also sent to the web server via HTML get requests. The server process any incoming data with a php script and stores the data in a mysql database. The overview page pulls the data from the database and is displayed in tables and charts(charts from highcarts.com)
If anyone would like any more info or would like to see some code then let me know! i have had blast designing and building everything and cant wait to start filling up the tank/sump.
adamgoldberg
11/11/2013, 09:36 AM
Oh! Oh! More info, please!!
wow looks great would you share the code that makes all that happen for the website and the arduino
joopie
11/18/2013, 04:14 PM
here is the arduino code... i have added a blocked=) to areas that i thought may compromise security of my fish tank. there are still plenty of ideas i'd like to add/fix, but here is what i have so far.
#include <OneWire.h>
#include <DallasTemperature.h>
#include <SPI.h>
#include <Ethernet.h>
#include <HttpClient.h>
#include <Time.h>
#include <EthernetUdp.h>
#include <NewPing.h>
//PINS
#define PIN_TEMP 3 //Temp Probes 01,02,03+
#define PIN_RY_HEATER 22 //Relay for heater
#define PIN_RY_FAN_LEFT 24 //Relay for left fan
#define PIN_RY_FAN_RIGHT 26 //Relay for right fan
#define PIN_RY_LEVEL 28 //level relay
#define PIN_RY_DRAIN 30 //drain relay
#define PIN_RY_AUX 32 //aux relay
#define PIN_LED_WHITE 9 //LED_WHITE
#define PIN_LED_BLUE 8 //LED_BLUE
#define TRIGGER_PIN 11
#define ECHO_PIN 10
#define MAX_DISTANCE 200
#define amt 30
NewPing sonar(TRIGGER_PIN, ECHO_PIN, MAX_DISTANCE);
const int chipSelect = 4;
byte mac[] = {
0xDA, 0xAD, 0xBF, 0xEF, 0xFE, 0xED };
byte ip[] = {
192,168,1,120};
OneWire oneWire(PIN_TEMP);
DallasTemperature sensors(&oneWire);
DeviceAddress Probe01 = {
0x28, 0x48, 0xA1, 0x5C, 0x04, 0x00, 0x00, 0x60 };
DeviceAddress Probe02 = {
0x28, 0xB3, 0x6B, 0x5C, 0x04, 0x00, 0x00, 0x75 };
DeviceAddress Probe03 = {
0x28, 0xF3, 0x14, 0x98, 0x04, 0x00, 0x00, 0x35 };
DeviceAddress Probe04 = {
0x28, 0x30, 0xE8, 0x1D, 0x05, 0x00, 0x00, 0x87 };
DeviceAddress Probe05 = {
0x28, 0x8E, 0xB2, 0xEF, 0x04, 0x00, 0x00, 0x66 };
DeviceAddress Probe06 = {
0x28, 0xAA, 0x1D, 0xEF, 0x04, 0x00, 0x00, 0x50 };
DeviceAddress Probe07 = {
0x28, 0x6D, 0x64, 0xEF, 0x04, 0x00, 0x00, 0xD4 };
//Time
IPAddress timeServer(132, 163, 4, 101); // time-a.timefreq.bldrdoc.gov
const int timeZone = -4;
EthernetUDP Udp;
unsigned int localPort = blocked=);
unsigned long CUR_MILLIS;
//String webServer = "blocked=)";
EthernetClient client;
//Alarms
boolean ALARM_LEVEL_HIGH = false;
boolean ALARM_LEVEL_LOW = false;
boolean ALARM_PH_HIGH = false;
boolean ALARM_PH_LOW = false;
boolean ALARM_TEMP_AVG_HIGH = false;
boolean ALARM_TEMP_AVG_LOW = false;
//Input Vars
float IN_TEMP_HEATER; //probe 1
float IN_TEMP_RETURN; //probe 2
float IN_TEMP_OVERFLOW; //probe 3
float IN_TEMP_ROOM; //probe 4
float IN_TEMP_TANK; //probe 7
float IN_LED_TEMP_LEFT; //probe 5
float IN_LED_TEMP_RIGHT; //probe 6
float IN_LED_BLUE = 5;
float IN_LED_WHITE = 5;
float IN_LEVEL_SUMP; //Sump water level
float IN_LEVEL_PH;
float TEMP_AVG;
int IN_HEATER_MODE = 2; // 0 - OFF, 1 - ON, 2 - AUTO
int IN_LED_FAN_MODE = 2; // 0 - OFF, 1 - ON, 2 - AUTO
int IN_LED_MODE = 2; // 0 - OFF, 1 - ON, 2 - AUTO
int IN_LEVEL_MODE = 2; //0 - OFF, 1 - ON, 2 - AUTO
int IN_AUX_MODE = 0; //0 - OFF, 1 - ON
int IN_LEVEL_DRAIN_MODE = 0; //0 - OFF, 1 - ON
int IN_LEVEL_PH_MODE = 1; //0 - STOP, 1 - CON,2-TEMP, 3 - CAL4, 4 - CAL7, 5 - CAL10, ...
int IN_LEVEL_PH_MODE_L = 0;
//OUTPUT Vars
boolean OUT_HEATER = false;
boolean OUT_LEFT_FAN = false;
boolean OUT_RIGHT_FAN = false;
boolean OUT_LEVEL = false;
//boolean OUT_LEVEL_DRAIN = false;
int OUT_LED_WHITE;
int OUT_LED_BLUE;
float OUT_LEVEL_SUMP;
//Setpoints
float SP_TEMP_RETURN = 77.80;
float SPR_TEMP_RETURN = 78.20;
float SP_TEMP_LED = 85.00;
float SPR_TEMP_LED = 80.00;
float SP_LEVEL = 11.00;
float SPR_LEVEL = 12.00;
//Ranges in minutes
int LED_WHITE_START = 720;
int LED_WHITE_END = 1200;
int LED_BLUE_START = 600;
int LED_BLUE_END = 1320;
int LED_MID = 960;
//led ranges
int BLUE_UP = 150;
int BLUE_LW = 19;
int WHITE_UP = 200;
int WHITE_LW = 19;
//sump ranges
float SUMP_RNG = 24;
String readString;
//frame delays
long PREV_MILLIS = 0;
long PREV_MILLIS1 = 0;
long PREV_MILLIS2 = 0;
long IN_DELAY = 1000; // 500ms framerate
long XMIT_DELAY = 5000; // 3s framerate
long REC_DELAY = 10000; // 10s framerate
boolean keep;
//ph stuff
#define avgSize 20
float phavg[avgSize];
int phRear=0;
float LVLavg[avgSize];
int LVLRear=0;
void QueueEnter(float queue[],float item,int *rear){
if(*rear < avgSize)
{
queue[*rear]=item;
*rear++;
}else{
*rear = 0;
}
}
float getAVG(float queue[]){
float avg;
int count = 0;
for(int i = 0;i < avgSize;i++){
if(queue[i] != 0){
avg += queue[i];
count++;
}
}
return avg / count;
}
void setup()
{
Serial.begin(38400);
Serial.println("Initializing...");
Serial.print("PH Probe... ");
Serial.println(startPH());
Serial.print("Temperature Sensors... ");
Serial.println(startTemp());
Serial.print("Ethernet... ");
Serial.println(startEthernet());
Serial.print("Input/Outputs... ");
Serial.println(startPins());
Serial.print("Time Server... ");
Serial.print(startTime());
Serial.print(" ");
Serial.println(getTime());
Serial.print("Setting up Relays... ");
Serial.println(startPins());
Config("Password",blocked=));
Config("Heater_Mode",IN_HEATER_MODE);
Config("LED_Mode",IN_LED_MODE);
Config("LED_Fan_Mode",IN_LED_FAN_MODE);
Config("Level_Mode",IN_LEVEL_MODE);
Config("Level_PH_Mode",IN_LEVEL_PH_MODE);
Config("Level_Drain_Mode",IN_LEVEL_DRAIN_MODE);
Config("AUX_Mode",IN_AUX_MODE);
}
void loop()
{
CUR_MILLIS = millis();
if (CUR_MILLIS - PREV_MILLIS > IN_DELAY){
PREV_MILLIS = CUR_MILLIS;
Inputs();
Process();
Outputs();
}
if (CUR_MILLIS - PREV_MILLIS1 > XMIT_DELAY){
PREV_MILLIS1 = CUR_MILLIS;
XMIT();
}
if (CUR_MILLIS - PREV_MILLIS2 > REC_DELAY){
PREV_MILLIS2 = CUR_MILLIS;
RX();
}
}
String getTime()
{
String theTime;
theTime.concat(month());
theTime.concat("/");
theTime.concat(day());
theTime.concat("/");
theTime.concat(year());
theTime.concat(" ");
theTime.concat(hour() - 1);
theTime.concat(":");
theTime.concat(minute());
theTime.concat(":");
theTime.concat(second());
return theTime;
}
void XMIT(){
Trender("TEMP_RETURN",IN_TEMP_RETURN);
Trender("TEMP_HEATER",IN_TEMP_HEATER);
Trender("TEMP_OVERFLOW",IN_TEMP_OVERFLOW);
Trender("TEMP_TANK",IN_TEMP_TANK);
Trender("TEMP_ROOM",IN_TEMP_ROOM);
Trender("TEMP_AOUT_HEATER",OUT_HEATER?1.0f:0.0f);
Trender("LED_ABLUE",map(OUT_LED_BLUE,BLUE_LW,BLUE_UP,0,100)); //grab in var to output in %
Trender("LED_AWHITE",map(OUT_LED_WHITE,WHITE_LW,WHITE_UP,0,100)); //grab in var to output in %
Trender("LED_XTEMP_LEFT",IN_LED_TEMP_LEFT);
Trender("LED_XTEMP_RIGHT",IN_LED_TEMP_RIGHT);
Trender("LED_FAN_LEFT",OUT_LEFT_FAN);
Trender("LED_FAN_RIGHT",OUT_RIGHT_FAN);
Trender("LEVEL_SUMP",IN_LEVEL_SUMP);
Trender("LEVEL_FILL",OUT_LEVEL);
Trender("LEVEL_XPH",IN_LEVEL_PH);
Trender("LEVEL_DRAIN",IN_LEVEL_DRAIN_MODE);
//Trender("LEVEL_AUX_MODE",IN_AUX_MODE);
Trender("ALARM_LEVEL_HIGH",ALARM_LEVEL_HIGH);
Trender("ALARM_LEVEL_LOW",ALARM_LEVEL_LOW);
Trender("ALARM_PH_HIGH",ALARM_PH_HIGH);
Trender("ALARM_PH_LOW",ALARM_PH_LOW);
Trender("ALARM_TEMP_AVG_HIGH",ALARM_TEMP_AVG_HIGH);
Trender("ALARM_TEMP_AVG_LOW",ALARM_TEMP_AVG_LOW);
}
void Trender(String table,float value){
if (client.connect("blocked=)", blocked=)))
{
client.print("GET /blocked=)");
client.print(table);
client.print("&value=");
client.println(value);
}
client.flush();
client.stop();
client.flush();
}
void RX(){
if (client.connect("blocked=)",blocked=)))
{
client.println("GET /blocked=)");
client.println();
}
if (client) {
while (client.connected()) {
if (client.available()) {
char c = client.read();
if (c == '!') {
client.stop();
}
readString += c;
}
}
parseString(readString);
readString = "";
}
client.flush();
client.stop();
client.flush();
}
void parseString(String str){
keep = true;
int offset = 0;
while(keep){
int start = str.indexOf('#',offset) + 1;
int mid = str.indexOf('@',offset);
int finish = str.indexOf('$',offset);
int endloop = str.indexOf('!');
offset = finish + 1;
String name = str.substring(start,mid);
float value = strTofloat(str.length(),str.substring(mid + 1,finish));
setInput(name,value);
if(offset == endloop){
str = "";
keep = false;
}
}
}
void setInput(String name,float value){
if(name == "Heater_Mode"){
IN_HEATER_MODE = (int)(value);
}
else if(name == "LED_Mode"){
IN_LED_MODE = (int)(value);
}
else if(name == "LED_Fan_Mode"){
IN_LED_FAN_MODE = (int)(value);
}
else if(name == "LED_Blue"){
IN_LED_BLUE = (int)(value);
}
else if(name == "LED_White"){
IN_LED_WHITE = (int)(value);
}
else if(name == "Password"){
}
else if(name == "Level_Mode"){
IN_LEVEL_MODE = (int)(value);
}
else if(name == "Level_PH_Mode"){
IN_LEVEL_PH_MODE = (int)(value);
}
else if(name == "Level_Drain_Mode"){
IN_LEVEL_DRAIN_MODE = (int)(value);
}
else if(name == "AUX_Mode"){
IN_AUX_MODE = (int)(value);
}
else{
Serial.println("INVALID ARGS PASSED TO SETINPUT");
keep = false;
}
}
void Config(String setting,float value){
if (client.connect("blocked=)",blocked=)))
{
client.print("GET /blocked=)");
client.print(setting);
client.print("&value=");
client.println(value);
}
client.flush();
client.stop();
client.flush();
}
boolean startTime()
{
Udp.begin(localPort);
setSyncProvider(getNtpTime);
return true;
}
boolean startPH(){
Serial3.begin(38400);
delay(2000);
return true;
}
boolean startPins()
{
pinMode(PIN_RY_HEATER,OUTPUT);
digitalWrite(PIN_RY_HEATER,HIGH); //off
pinMode(PIN_RY_FAN_LEFT,OUTPUT);
digitalWrite(PIN_RY_FAN_LEFT,HIGH); //off
pinMode(PIN_RY_FAN_RIGHT,OUTPUT);
digitalWrite(PIN_RY_FAN_RIGHT,HIGH); //off
pinMode(PIN_RY_LEVEL,OUTPUT);
digitalWrite(PIN_RY_LEVEL,HIGH); //off
pinMode(PIN_LED_WHITE,OUTPUT);
pinMode(PIN_LED_BLUE,OUTPUT);
pinMode(PIN_RY_DRAIN,OUTPUT);
digitalWrite(PIN_RY_DRAIN,HIGH); //off
pinMode(PIN_RY_AUX,OUTPUT);
digitalWrite(PIN_RY_AUX,HIGH); //off
return true;
}
boolean startTemp()
{
sensors.begin();
sensors.setResolution(Probe01, 12);
sensors.setResolution(Probe02, 12);
sensors.setResolution(Probe03, 12);
sensors.setResolution(Probe04, 12);
sensors.setResolution(Probe05, 12);//left led
sensors.setResolution(Probe06, 12);//right led
sensors.setResolution(Probe07, 12);//tank
if (sensors.getDeviceCount() > 0){
return sensors.getDeviceCount();
}
else{
return false;
}
}
boolean startEthernet(){
Ethernet.begin(mac,ip);
server.begin();
return true;
}
void Alarms(){
if(IN_LEVEL_SUMP > 15){
ALARM_LEVEL_HIGH = true;
}else{
ALARM_LEVEL_HIGH = false;
}
if(IN_LEVEL_SUMP < 8){
ALARM_LEVEL_LOW = true;
}else{
ALARM_LEVEL_LOW = false;
}
if(IN_LEVEL_PH > 9){
ALARM_PH_HIGH = true;
}else{
ALARM_PH_HIGH = false;
}
if(IN_LEVEL_PH < 7.6){
ALARM_PH_LOW = true;
}else{
ALARM_PH_LOW = false;
}
if(TEMP_AVG > 82){
ALARM_TEMP_AVG_HIGH = true;
}else{
ALARM_TEMP_AVG_HIGH = false;
}
if(TEMP_AVG < 76){
ALARM_TEMP_AVG_LOW = true;
}else{
ALARM_TEMP_AVG_LOW = false;
}
}
void Inputs()
{
sensors.requestTemperatures();
IN_TEMP_HEATER = getTemp(IN_TEMP_HEATER,Probe01);
IN_TEMP_RETURN = getTemp(IN_TEMP_RETURN,Probe02);
IN_TEMP_OVERFLOW = getTemp(IN_TEMP_OVERFLOW,Probe03);
IN_TEMP_ROOM = getTemp(IN_TEMP_ROOM,Probe04);
IN_LED_TEMP_LEFT = getTemp(IN_LED_TEMP_LEFT,Probe05);
IN_LED_TEMP_RIGHT = getTemp(IN_LED_TEMP_RIGHT,Probe06);
IN_TEMP_TANK = getTemp(IN_TEMP_TANK,Probe07);
IN_LEVEL_SUMP = readLevel();
IN_LEVEL_PH = PHProcess(IN_LEVEL_PH_MODE);
}
void Process()
{
OUT_HEATER = Controller(IN_TEMP_RETURN,SP_TEMP_RETURN,SPR_TEMP_RETURN,true,IN_HEATER_MODE,OUT_HEATER); //Heater controller
OUT_LEFT_FAN = Controller(IN_LED_TEMP_LEFT,SP_TEMP_LED,SPR_TEMP_LED,false,IN_LED_FAN_MODE,OUT_LEFT_FAN); //Left LED Fan
OUT_RIGHT_FAN = Controller(IN_LED_TEMP_RIGHT,SP_TEMP_LED,SPR_TEMP_LED,false,IN_LED_FAN_MODE,OUT_RIGHT_FAN); //Right LED Fan
OUT_LED_WHITE = LEDController(map(IN_LED_WHITE,0,100,WHITE_LW,WHITE_UP),IN_LED_MODE,"white"); // white led controller
OUT_LED_BLUE = LEDController(map(IN_LED_BLUE,0,100,BLUE_LW,BLUE_UP),IN_LED_MODE,"blue"); //blue led controller
OUT_LEVEL = Controller(IN_LEVEL_SUMP,SP_LEVEL,SPR_LEVEL,true,IN_LEVEL_MODE,OUT_LEVEL); //Heater controller
TEMP_AVG = (IN_TEMP_RETURN + IN_TEMP_OVERFLOW + IN_TEMP_TANK) / 3;
Alarms();
}
float PHProcess(int mode){ //0 - STOP, 1 - CON,2-TEMP, 3 - CAL4, 4 - CAL7, 5 - CAL10, 6 - CALD4,7 - CALD7,8 - CALD10
if(mode != IN_LEVEL_PH_MODE_L){
switch(mode){
case 0:
Serial3.print("E\r\r");
delay(1000);
break;
case 1:
Serial3.print("C\r\r");
delay(1000);
break;
case 2:
Serial3.print(toC(IN_TEMP_RETURN));
Serial3.print("\r\r");
delay(1000);
break;
case 3: //ph 4
Serial3.print("F\r\r");
Config("LEVEL_PH_MODE",6);
break;
case 4: //ph 7
Serial3.print("S\r\r");
Config("LEVEL_PH_MODE",7);
break;
case 5: //ph 10
Serial3.print("T\r\r");
Config("LEVEL_PH_MODE",8);
break;
}
}
IN_LEVEL_PH_MODE_L = mode;
return readPH();
}
int LEDController(int IN_LED,int mode,String color){
if (mode == 0){ //Off
return 0;
}
else if(mode == 1){ //On
return IN_LED;
}
else{ //auto
int minNow = ((hour() - 1) * 60) + minute();
if(minNow <= LED_BLUE_START){ //0-10
return 0;
}
else if(minNow >= LED_BLUE_END){//22-24
return 0;
}
else if(minNow >= LED_BLUE_START && minNow <= LED_WHITE_START && color == "blue"){ //10-12 blue only
return map(minNow,LED_BLUE_START,LED_WHITE_START,BLUE_LW,BLUE_UP);
}
else if(minNow >= LED_BLUE_START && minNow <= LED_WHITE_START && color == "white"){ //10-12 white
return 0;
}
else if(minNow >= LED_WHITE_START && minNow <= LED_MID && color == "blue"){ //12-16 blue
return map(minNow,LED_WHITE_START,LED_MID,BLUE_UP,BLUE_LW);
}
else if(minNow >= LED_WHITE_START && minNow <= LED_MID && color == "white"){ //12-16 white
return map(minNow,LED_WHITE_START,LED_MID,WHITE_LW,WHITE_UP);
}
else if(minNow >= LED_MID && minNow <= LED_WHITE_END && color == "blue"){ //16-20 blue
return map(minNow,LED_MID,LED_WHITE_END,BLUE_LW,BLUE_UP);
}
else if(minNow >= LED_MID && minNow <= LED_WHITE_END && color == "white"){ //16-29 white
return map(minNow,LED_MID,LED_WHITE_END,WHITE_UP,WHITE_LW);
}
else if(minNow >= LED_WHITE_END && minNow <= LED_BLUE_END && color == "blue"){ // 20-22 blue
return map(minNow,LED_WHITE_END,LED_BLUE_END,BLUE_UP,BLUE_LW);
}
else if(minNow >= LED_WHITE_END && minNow <= LED_BLUE_END && color == "white"){ // 20-22 blue
return 0;
}
else{
return 0;
}
}
}
boolean Controller(float input,float sp,float rst,boolean dec,int mode,boolean output){
if (mode == 0){ //Off
return false;
}
else if(mode == 1){ //On
return true;
}
else{ //Auto
if(dec){
if(input < sp){
return true;
}
else if(input > rst){
return false;
}
else{
return output;
}
}
else{
if(input > sp){
return true;
}
else if(input < rst){
return false;
}
else{
return output;
}
}
}
}
void Outputs(){
digitalWrite(PIN_RY_HEATER,!OUT_HEATER); //Invert output for relay
digitalWrite(PIN_RY_FAN_LEFT,!OUT_LEFT_FAN);
digitalWrite(PIN_RY_FAN_RIGHT,!OUT_RIGHT_FAN);
digitalWrite(PIN_RY_LEVEL,!OUT_LEVEL);
digitalWrite(PIN_RY_DRAIN,!IN_LEVEL_DRAIN_MODE);
digitalWrite(PIN_RY_AUX,!IN_AUX_MODE);
analogWrite(PIN_LED_WHITE,OUT_LED_WHITE);
analogWrite(PIN_LED_BLUE,OUT_LED_BLUE);
}
float getTemp(float last,DeviceAddress deviceAddress)
{
float temp = sensors.getTempF(deviceAddress);
if(temp < 0){
return last;
}
else{
return temp;
}
}
float readLevel(){
float average;
float samples[amt];
for (int i=0; i< amt; i++) {
float uS = sonar.ping();
float dist = uS / US_ROUNDTRIP_IN;
samples[i] = dist;
delay(30);
}
average = 0;
for (int i=0; i< amt; i++) {
average += samples[i];
}
average /= amt;
QueueEnter(LVLavg,SUMP_RNG - average,&LVLRear);
return getAVG(LVLavg);
}
float lastPH;
float readPH(){
String PHSTR = "";
while(Serial3.available()){
char inchar = (char)Serial3.read();
PHSTR += inchar;
if(inchar == '\r') {
float phCheck = strTofloat(PHSTR.length(),PHSTR);
if(phCheck > 14 || phCheck < 0){
return lastPH;
}
else{
float phCorrected;
if(phCheck >= 7){
phCorrected = mapfloat(phCheck,7.00,10.20,7.00,10.00);
}
else{
phCorrected = mapfloat(phCheck,3.70,7.00,4.00,6.99);
}
lastPH = phCorrected;
QueueEnter(phavg,phCorrected,&phRear);
Serial.println("");
Serial.println(phCorrected);
Serial.println(getAVG(phavg));
return getAVG(phavg);
}
}
}
}
float mapfloat(float x, float in_min, float in_max, float out_min, float out_max)
{
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}
float strTofloat(int chars,String str){
char floatbuf[chars];
str.toCharArray(floatbuf,sizeof(floatbuf));
float value = atof(floatbuf);
}
float toC(float temp){
return (temp - 32) * (5/9);
}
/*-------- NTP code ----------*/
const int NTP_PACKET_SIZE = 48; // NTP time is in the first 48 bytes of message
byte packetBuffer[NTP_PACKET_SIZE]; //buffer to hold incoming & outgoing packets
time_t getNtpTime()
{
while (Udp.parsePacket() > 0) ; // discard any previously received packets
// Serial.println("Transmit NTP Request");
sendNTPpacket(timeServer);
uint32_t beginWait = millis();
while (millis() - beginWait < 1500) {
int size = Udp.parsePacket();
if (size >= NTP_PACKET_SIZE) {
// Serial.println("Receive NTP Response");
Udp.read(packetBuffer, NTP_PACKET_SIZE); // read packet into the buffer
unsigned long secsSince1900;
// convert four bytes starting at location 40 to a long integer
secsSince1900 = (unsigned long)packetBuffer[40] << 24;
secsSince1900 |= (unsigned long)packetBuffer[41] << 16;
secsSince1900 |= (unsigned long)packetBuffer[42] << 8;
secsSince1900 |= (unsigned long)packetBuffer[43];
return secsSince1900 - 2208988800UL + timeZone * SECS_PER_HOUR;
}
}
Serial.println("No NTP Response :-(");
return 0; // return 0 if unable to get the time
}
// send an NTP request to the time server at the given address
void sendNTPpacket(IPAddress &address)
{
// 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;
// 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();
}
would you please share the html files?? or email them to me I would like to start using this very soon
you replaced your local ip and password with blocked=) did you also replace a FQDN also with blocked=)
joopie
11/19/2013, 09:54 PM
i don't believe i replaced a FQDN with blocked... I block out some URLs for the get requests. Showing the way i have the get request set up for the trender function would allow anyone to insert data into my data tables. I'm sure what i have going is not very secure but seems to be ok so far... I am going to add interlocks so that if someone was able to gain access they wouldn't be able to do much. I just hard code it so that the sump level will never drain below x inches, or fill over x inches. same with heater, and lighting. So the main page is generated from a couple of php files and javascript for the charts.AJAX makes server calls to update the server without refreshing. here are some example links of how i get the tables:
http://www.rareglitch.net/getTables.php?tables=TEMP_%
http://www.rareglitch.net/getTables.php?tables=LED_%
http://www.rareglitch.net/getTables.php?tables=LEVEL_%
if you guys know of,or find any vulnerabilities id hope you would let me know =). i hope my code will help, and i will try to answer any questions. I am no web developer, i just took the time to learn and understand what to do and piece it all together. as far as charting please see http://www.highcharts.com/. i used the highstock for plotting my points. It needs a json output to load the points. here is the json output of the temp_return table: http://www.rareglitch.net/get_json.php?&table=TEMP_RETURN
php file to generate the tables and format the table names and data to be more appealing:
<?php
include_once('connect.php');
$tables = $_GET['tables'];
$query = "SHOW TABLES LIKE '".$tables."'";
$result = mysql_query($query,$connect);
$out = "<table border='.5'>";
$out .= "<tr bgcolor='#9ACDFD'>";
$out .= "<td>Last Update:</td>";
$first = mysql_fetch_row($result);
$result = mysql_query($query,$connect);
while($row = mysql_fetch_row($result)){
$out .= "<td bgcolor='".getColor($row[0])."'>".getName($row[0])."</td>";
}
$out .= "</tr>";
$out .= "<tr>";
$dateQRY = "SELECT Date FROM ".$first[0]." ORDER BY Date DESC LIMIT 1";
$dateResult = mysql_query($dateQRY,$connect);
$out .= "<td>" .mysql_fetch_array($dateResult)[0] . "</td>";
$query = "SHOW TABLES LIKE '".$tables."'";
$result = mysql_query($query,$connect);
while($row = mysql_fetch_row($result)){
$iquery = "SELECT Value FROM ".$row[0]." ORDER BY Date DESC LIMIT 1";
$iresult = mysql_query($iquery,$connect);
$out .= "<td>" . formatData($row[0],mysql_fetch_array($iresult)[0]) . "</td>";
}
$out .= "</tr>";
$out .= "</table>";
echo $out;
function getColor($tablename){
switch($tablename){
case "TEMP_AOUT_HEATER":
return "red";
break;
case "TEMP_HEATER":
return "royalblue";
break;
case "TEMP_OVERFLOW":
return "green";
break;
case "TEMP_RETURN":
return "yellow";
break;
case "TEMP_ROOM":
return "cyan";
break;
case "LED_ABLUE":
return "royalblue";
break;
case "LED_XTEMP_LEFT":
return "lightgray";
break;
case "LED_XTEMP_RIGHT":
return "lightgray";
break;
case "TEMP_TANK":
return "pink";
break;
default:
return "white";
break;
}
}
function getName($tablename){
switch($tablename){
case "TEMP_AOUT_HEATER":
return "Heater Status";
break;
case "TEMP_HEATER":
return "Heater Temp";
break;
case "TEMP_OVERFLOW":
return "Overflow Temp";
break;
case "TEMP_RETURN":
return "Return Temp";
break;
case "TEMP_ROOM":
return "Room Temp";
break;
case "LED_ABLUE":
return "Blue LEDs";
break;
case "LED_AWHITE":
return "White LEDs";
break;
case "LED_XTEMP_LEFT":
return "Left LEDs";
break;
case "LED_XTEMP_RIGHT":
return "Right LEDs";
break;
case "TEMP_TANK":
return "Tank Temp";
break;
case "LED_FAN_LEFT":
return "Left Fan";
break;
case "LED_FAN_RIGHT":
return "Right Fan";
break;
case "LEVEL_FILL":
return "Fill Status";
break;
case "LEVEL_SUMP":
return "Sump Level";
break;
case "LEVEL_XPH":
return "PH Level";
break;
case "LEVEL_DRAIN":
return "Drain Status";
break;
case "ALARM_LEVEL_HIGH":
return "Level High Alarm";
break;
case "ALARM_LEVEL_LOW":
return "Level Low Alarm";
break;
case "ALARM_PH_HIGH":
return "PH High";
break;
case "ALARM_PH_LOW":
return "PH Low";
break;
case "ALARM_TEMP_AVG_HIGH":
return "AVG Temp High";
break;
case "ALARM_TEMP_AVG_LOW":
return "AVG Temp Low";
break;
default:
return $tablename;
break;
}
}
function formatData($table,$data){
switch($table){
case "LED_FAN_RIGHT":
case "LED_FAN_LEFT":
case "LEVEL_FILL":
case "TEMP_AOUT_HEATER":
case "LEVEL_DRAIN":
switch($data){
case 1:
return "On";
break;
case 0:
return "Off";
break;
default:
return $data;
break;
}
break;
case "LED_AWHITE":
case "LED_ABLUE":
if($data <= 0){
return "Off";
break;
}else{
return $data . "%";
break;
}
case "ALARM_LEVEL_HIGH":
case "ALARM_LEVEL_LOW":
case "ALARM_PH_HIGH":
case "ALARM_PH_LOW":
case "ALARM_TEMP_AVG_HIGH":
case "ALARM_TEMP_AVG_LOW":
switch($data){
case 1:
return "<center>ALARM</center>";
break;
case 0:
return "<center>GOOD</center>";
break;
default:
return $data;
break;
}
default:
return $data;
break;
}
}
connect.php
<?php
//CONNECT.PHP
$hostname = "blocked";
$database = "blocked";
$username = "blocked";
$password = "blocked";
if(!$connect = mysql_connect($hostname,$username,$password,$database)){
die('FAILED TO CONNECT' . mysql_error());
}
$bool = mysql_select_db($database,$connect);
if(!$bool){
die('FAILED DB SELECT' . mysql_error());
}
?>
json file to convert mysql table into json for charting:
<?php
include_once('connect.php');
$table = $_GET['table'];
if($table == "blocked"){
$qry = "SELECT * FROM ".$table;
$result = mysql_query($qry,$connect);
while($row = mysql_fetch_array($result)){
if($row['Setting'] == "blocked"){
}else{
echo "#" . $row['Setting'];
echo "@" . $row['Value'] . "$";
}
}
echo "!";
}else{
$shorten = false;
$count = 3;
$start = 0;
$qry = "SELECT * FROM ".$table." ORDER BY Date ASC";
$result = mysql_query($qry,$connect);
if(mysql_num_rows($result) > 100000 & mysql_num_rows($result) < 200000){
$shorten = true;
$count = 5;
}elseif(mysql_num_rows($result) > 200000){
$shorten = true;
$count = 10;
}else{
$shorten = false;
}
while($row = mysql_fetch_array($result)){
$dates = strtotime($row['Date']) * 1000;
switch($table){
case "TEMP_AOUT_HEATER":
$values = ((float)$row['Value'] * 5) + 85.00;
break;
case "LED_FAN_LEFT":
case "LED_FAN_RIGHT":
$values = ((float)$row['Value'] * 5) + 80.00;
break;
case "LEVEL_FILL":
$values = ((float)$row['Value'] * 5) + 5.00;
break;
case "LED_ABlUE":
case "LED_AWHITE":
$values = ((float)$row['Value'] * .1) + 0.00;
default:
$values = (float)$row['Value'];
break;
}
if($shorten){
if($start == $count){
$array[] = array($dates,$values);
$start = 0;
}else{
$start++;
}
}else{
$array[] = array($dates,$values);
}
}
echo json_encode($array);
}
?>
overview site:
<!DOCTYPE HTML>
<html>
<head>
<meta ***********="Content-Type" content="text/html; charset=utf-8">
<link rel="icon" type="icon" href="1_fish.ico"/>
<link rel="shortcut icon" type="icon" href="1_fish.ico"/>
<title>Fish Tank</title>
</head>
<body bgcolor="grey">
******** type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.2/jquery.min.js"></script>
******** type="text/javascript">
<!--
//Browser Support Code
if(screen.width <=699){
document.location = "http://www.rareglitch.net/lite.htm";
}
function ajaxAlarm(){
var ajaxRequest; // The variable that makes Ajax possible!
try{
// Opera 8.0+, Firefox, Safari
ajaxRequest = new XMLHttpRequest();
} catch (e){
// Internet Explorer Browsers
try{
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try{
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e){
// Something went wrong
alert("Your browser broke!");
return false;
}
}
}
// Create a function that will receive data sent from the server
ajaxRequest.onreadystatechange = function(){
if(ajaxRequest.readyState == 4){
var ajaxDisplay = document.getElementById('ajaxAlarm');
ajaxDisplay.innerHTML = ajaxRequest.responseText;
}
}
ajaxRequest.open("GET", "getTables.php?&tables=ALARM_%", true);
ajaxRequest.send(null);
}
function ajaxLED(){
var ajaxRequest; // The variable that makes Ajax possible!
try{
// Opera 8.0+, Firefox, Safari
ajaxRequest = new XMLHttpRequest();
} catch (e){
// Internet Explorer Browsers
try{
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try{
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e){
// Something went wrong
alert("Your browser broke!");
return false;
}
}
}
// Create a function that will receive data sent from the server
ajaxRequest.onreadystatechange = function(){
if(ajaxRequest.readyState == 4){
var ajaxDisplay = document.getElementById('ajaxLED');
ajaxDisplay.innerHTML = ajaxRequest.responseText;
}
}
ajaxRequest.open("GET", "getTables.php?tables=LED_%", true);
ajaxRequest.send(null);
}
function ajaxTemp(){
var ajaxRequest; // The variable that makes Ajax possible!
try{
// Opera 8.0+, Firefox, Safari
ajaxRequest = new XMLHttpRequest();
} catch (e){
// Internet Explorer Browsers
try{
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try{
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e){
// Something went wrong
alert("Your browser broke!");
return false;
}
}
}
// Create a function that will receive data sent from the server
ajaxRequest.onreadystatechange = function(){
if(ajaxRequest.readyState == 4){
var ajaxDisplay = document.getElementById('ajaxTemp');
ajaxDisplay.innerHTML = ajaxRequest.responseText;
}
}
ajaxRequest.open("GET", "getTables.php?tables=TEMP_%", true);
ajaxRequest.send(null);
}
function ajaxLevel(){
var ajaxRequest; // The variable that makes Ajax possible!
try{
// Opera 8.0+, Firefox, Safari
ajaxRequest = new XMLHttpRequest();
} catch (e){
// Internet Explorer Browsers
try{
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try{
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e){
// Something went wrong
alert("Your browser broke!");
return false;
}
}
}
// Create a function that will receive data sent from the server
ajaxRequest.onreadystatechange = function(){
if(ajaxRequest.readyState == 4){
var ajaxDisplay = document.getElementById('ajaxLevel');
ajaxDisplay.innerHTML = ajaxRequest.responseText;
}
}
ajaxRequest.open("GET", "getTables.php?tables=LEVEL_%", true);
ajaxRequest.send(null);
}
function ajaxCFG(password){
var ajaxRequest; // The variable that makes Ajax possible!
try{
// Opera 8.0+, Firefox, Safari
ajaxRequest = new XMLHttpRequest();
} catch (e){
// Internet Explorer Browsers
try{
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try{
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e){
// Something went wrong
alert("Your browser broke!");
return false;
}
}
}
// Create a function that will receive data sent from the server
ajaxRequest.onreadystatechange = function(){
if(ajaxRequest.readyState == 4){
var ajaxDisplay = document.getElementById('ajaxCFG');
ajaxDisplay.innerHTML = ajaxRequest.responseText;
}
}
//var pass = document.getElementById("pass").value;
ajaxRequest.open("GET", "blocked, true);
ajaxRequest.send();
}
function updateConfig(name,value){
var ajaxRequest; // The variable that makes Ajax possible!
try{
// Opera 8.0+, Firefox, Safari
ajaxRequest = new XMLHttpRequest();
} catch (e){
// Internet Explorer Browsers
try{
ajaxRequest = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try{
ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e){
// Something went wrong
alert("Your browser broke!");
return false;
}
}
}
// Create a function that will receive data sent from the server
//ajaxRequest.onreadystatechange = function(){
// if(ajaxRequest.readyState == 4){
// var ajaxDisplay = document.getElementById('ajaxCFG');
// ajaxDisplay.innerHTML = ajaxRequest.responseText;
// }
//}
ajaxRequest.open("GET","blocked, true);
ajaxRequest.send(null);
}
ajaxAlarm();
ajaxLevel();
ajaxTemp();
ajaxLED();
ajaxCFG(document.getElementById("pass"));
//window.setInterval("loadChart()",3000);
window.setInterval("ajaxTemp()",5000);
window.setInterval("ajaxLED()",5000);
window.setInterval("ajaxLevel()",5000);
window.setInterval("ajaxAlarm()",5000);
window.setInterval("ajaxCFG(document.getElementById('pass').value)",5000);
</script>
<table border="0" width="100%">
<tr align="center"><td><div id ='ajaxAlarm'></div></td></tr>
</table>
<table border="0" width="100%">
<tr align="center"><td><div id ='ajaxTemp'></div></td><td><div id ='ajaxLED'></div></td></tr>
<tr align="center"><td><iframe src="chart_Temp.htm" frameborder="0" width="100%" height="320"></iframe></td><td><iframe src="chart_LED.htm" frameborder="0" width="100%" height="320"></iframe></td></tr>
<tr align="center"><td valign="bottom"><form>Password:<input type="password" id="pass"></form></td><td><div id ='ajaxLevel'></div></td></tr>
<tr align="center"><td valign="top"><div id ='ajaxCFG'></div></td><td><iframe src="chart_Level.htm" frameborder="0" width="100%" height="320"></iframe></td></tr></table>
</div> </body>
</html>
I all ready have a machine setup with wamp for internal use so that should work
Anytime your ready I can be
Joopie, thanks for sharing your code and actual internet view of your program! It has given me a lot of ideas for my new project!
gernby
12/06/2013, 02:06 PM
Wow, this is awesome! Thanks for sharing!
Glancing at your code, it seems that you are using an analog output instead of PWM. Is that correct? If so, what is the max output voltage, and what drivers are you using with it? Also, which Arduino board are you using?
joopie
12/06/2013, 05:50 PM
I'm using an arduino mega. The analogwrite is outputing a 0-5v pwm, see http://arduino.cc/en/Reference/analogWrite
im using a transistor and seperate 10v power source to up it to 0-10v pwm.
here is a good example:http://arduino-for-beginners.blogspot.com/2011/04/controlling-12v-fan-speed-with-pwm.html
the led drivers i have are the Mean Well ELN-60-48P dimmable driver:
http://www.rapidled.com/mean-well-eln-60-48p-dimmable-driver/
they are nice but they cut out at ~15%, so i recommend going with the Mean Well ELN-60-48D dimmable driver that cut out around 5%.
gernby
12/06/2013, 08:24 PM
Good to know. I'm planning to use the LDD-L drivers, which allow a 0~2.5V analog input for dimming OR a 0~2.6+V PWM input. When using the analog dimming mode, the LED's dim range is something like 20%-100%, so thinking about using PWM for the high power LED strings, and analog for the low power LED strings.
joopie
12/14/2013, 01:33 AM
Updated site so that each column header is a link to that data points chart.Charts with a lot of data points may take a awhile to load.
http://www.rareglitch.net/overview.htm
Also check out my "mobile" version of the site(no charts).
http://www.rareglitch.net/lite.htm
wow that is very nice took a few minutes to load but I did like it
gernby
12/18/2013, 04:43 PM
wow that is very nice took a few minutes to load but I did like it
:iagree: I kept getting popups about slow script performance. Is there a way to pre-process the data so that it loads quicker?
joopie
12/18/2013, 08:30 PM
I'm sure there is a better way but this works well for me since i'm usually loading the site off the lan.
joopie
12/31/2013, 09:17 PM
So i got a new toy for Christmas, you can see it in action at http://www.rareglitch.net/overview.htm or http://www.rareglitch.net/fishcam.htm
May not work for older IE browsers. recommend chrome or firefox.
dre7606
12/31/2013, 10:13 PM
joopie can you provide a parts list? This I love the added video feed!
reefwiser
01/01/2014, 07:45 AM
Looks like your fish cam is knocked out of view. I have to straight web cams at work all the time for monitoring production in a factory.:)
joopie
01/01/2014, 10:04 AM
yeah seems to reset after awhile. i think i fixed it, now it should look at the tank when it boots up.
reefwiser
01/01/2014, 01:09 PM
Love the wrasse swimming up down the one side.:)
joopie
01/05/2014, 01:49 PM
So the camera thing is cool but the wife doesn't like random people looking into our fish tank, and possibly whatever is reflected off the glass lol. so that wont be on there anymore.
joopie
01/05/2014, 02:06 PM
joopie can you provide a parts list? This I love the added video feed!
Arduino Mega:
https://www.sparkfun.com/products/11061
Ethernet Shield to talk with database and time keeping:
http://yourduino.com/sunshop2/index.php?l=product_detail&p=291
Temperature Sensors DS18b20(7 in use):
http://www.amazon.com/DS18b20-Waterproof-Temperature-Sensors-Transducer/dp/B00CC7TGKO/ref=sr_1_8?ie=UTF8&qid=1388951534&sr=8-8&keywords=ds+temperature+sensor
LED 90g Dimmable Solderless Kit:
http://www.rapidled.com/standard-solderless-90g-tank-dimmable-kit/
Opto-Isolated 2 Channel Relays for fans,heater and water change pump:
http://yourduino.com/sunshop2/index.php?l=product_detail&p=218
Power strip with above relays mounted inside to control outlets:
http://www.amazon.com/Technical-Pro-PS9U-Supply-Charging/dp/B0057RL6DQ/ref=sr_1_16?ie=UTF8&qid=1388951725&sr=8-16&keywords=dj+power+strip
Solenoids for auto top off, draining, water changes:
http://www.adafruit.com/products/996
I do have some flow meters installed but not in use:
http://www.adafruit.com/products/828
Ultrasonic range finder for sump level:
http://yourduino.com/sunshop2/index.php?l=product_detail&p=115
A PC for web server running Ubuntu and LAMPP. I also use this computers power supply to power the relays and solenoids.
I think that pretty much covers the arduino electronics.
dre7606
01/06/2014, 08:28 PM
Thanks Joopie!!
tidalwave1
01/06/2014, 09:11 PM
Joopie I am going to order rapid LEDs and I was thinking about building an arduino controller. Can I use the 1-10v drivers with an arduino controller or do I have to order the pwm drivers? The reason i am asking is because I was going to use the analog dimmers in the beginning and build the controller later.
joopie
01/07/2014, 03:44 PM
Joopie I am going to order rapid LEDs and I was thinking about building an arduino controller. Can I use the 1-10v drivers with an arduino controller or do I have to order the pwm drivers? The reason i am asking is because I was going to use the analog dimmers in the beginning and build the controller later.
yep you sure can do a 0-10v output, just google for some examples.I would have gone with the analog dimmers if i saw the 15% cutoff on the pwm ones before i bought the kit.
Yunghov
01/21/2014, 09:49 PM
Hi there, new to the DIY fish projects and ardiuno and this forum. Couple ?s if you don't mind. Y use the mega over the uno, as I have the uno. Can I use my uno. Is it possible to provide a wiring diagram because I want to use the temp sensor control led lights white and blue and provide a storm if that is possible. Thanks in advance.
gam3ovr
01/29/2014, 07:26 PM
Very cool stuff - wish I had the know-how to mess with this!
dre7606
02/07/2014, 12:43 AM
Joopie i have a couple of questions. Is the arduino sending all it data to the mysql database and then the website is retrieving it data from the database and then the refresh is doing another database query to pull the latest data? Also are you able to manually control the lights once you are on the password protected part of your site. Thanks for sharing.
joopie
02/07/2014, 05:57 AM
yep that's exactly whats going on. Once i put the password in, the slots with "Disabled" will show a combo box/number box that lets me select what i want it to do. i can set the % brightness for blue/white LEDs then put LED mode to On and it will set them to that.
clay12340
02/07/2014, 01:54 PM
Could you explain how the ultrasonic module is determining water height in your sump? Also are you using it in a way that provides any sort of overfill protection should it fail?
This looks really interesting.
joopie
02/07/2014, 03:40 PM
the ultra sonic measurement returns the distance from the water height back to the module. So i subtract that from the distance of the module from the floor. it will kick on at 11 inches and off at 12. i actually soaked it in salt water last night so it was outputing ~27 inches. i have some stuff coded in were it will not allow stuff to happen when there is an alarm condition. Since it was over my alarm set point of 15 inches the auto fill will not turn on. if it wanted to fail low, i have one of these installed that works great! http://www.amazon.com/Kerick-Valve-M252-Float-Mount/dp/B0077RAUTA/ref=sr_1_2?ie=UTF8&qid=1391808864&sr=8-2&keywords=float+valve
it will stop the fill process around ~16 inches.
now that the module is all dried out it seems to be working fine lol.
clay12340
02/08/2014, 05:56 AM
Thanks, so it is just up above your sump measuring the distance to the water's surface? What happens if you get a pipe in the way or something of that sort? Just sets off your water is too high alarm and shuts down the ATO?
This looks like a really fun project. I've spent the last decade coding whatever I'm told for work, so I've not actually had a fun project for myself in a long time. I may have to give this a whirl.
dre7606
02/08/2014, 02:09 PM
I think I understand how you are sending the data to the mysql database. What I am having issues understanding is how you can change the LED brightness from the website. Can you expand on that a little?
Again thanks for sharing!
joopie
02/08/2014, 05:48 PM
Thanks, so it is just up above your sump measuring the distance to the water's surface? What happens if you get a pipe in the way or something of that sort? Just sets off your water is too high alarm and shuts down the ATO?
This looks like a really fun project. I've spent the last decade coding whatever I'm told for work, so I've not actually had a fun project for myself in a long time. I may have to give this a whirl.
Yeah its on the top of my sump in the open area, so it has a clear shot all the way down if there was no water in it. If something gets in the way it just depends on how far. I have it only working within 6-15 inches, if the arduino gets a value outside of this range it will not turn on the ATO. I have yet to program an "automated" water change but right now i turn my level mode to off so the ATO will be disabled. Then i turn drain mode to on and it'll drain down to 6 inches(for a 30 gallon water change). I then have the water change mode which is my fill pump from a trash can that i use to mix salt water. So i just sit at my computer to do my water change once i have the salt mixed lol.
joopie
02/08/2014, 05:59 PM
I think I understand how you are sending the data to the mysql database. What I am having issues understanding is how you can change the LED brightness from the website. Can you expand on that a little?
Again thanks for sharing!
This part took me awhile to figure out how to do it. The arduino can read a string from a website. So when things are set on the web site they are updated in the database. The arduino calls this website: http://www.rareglitch.net/get_json.php?&table=CONFIG
which builds a custom string from the database for the arduino to parse. You can see below that RX() will call the website to get the json string thats buffered in readString. When readString is done it goes to parseString(String str) to separate the command and value. Once a command, value pair is separated it gets passed to setInput to assign the variable.
void RX(){
if (client.connect(ip, port))
{
client.println("GET /get_json.php?&table=CONFIG");
client.println();
}
if (client) {
while (client.connected()) {
if (client.available()) {
char c = client.read();
if (c == '!') {
client.stop();
}
readString += c;
}
}
parseString(readString);
readString = "";
}
client.flush();
client.stop();
client.flush();
}
void parseString(String str){
keep = true;
int offset = 0;
while(keep){
int start = str.indexOf('#',offset) + 1;
int mid = str.indexOf('@',offset);
int finish = str.indexOf('$',offset);
int endloop = str.indexOf('!');
offset = finish + 1;
String name = str.substring(start,mid);
float value = strTofloat(str.length(),str.substring(mid + 1,finish));
setInput(name,value);
if(offset == endloop){
str = "";
keep = false;
}
}
}
void setInput(String name,float value){
if(name == "Heater_Mode"){
IN_HEATER_MODE = (int)(value);
}
else if(name == "LED_Mode"){
IN_LED_MODE = (int)(value);
}
else if(name == "LED_Fan_Mode"){
IN_LED_FAN_MODE = (int)(value);
}
else if(name == "LED_Blue"){
IN_LED_BLUE = (int)(value);
}
else if(name == "LED_White"){
IN_LED_WHITE = (int)(value);
}
else if(name == "Password"){
}
else if(name == "Level_Mode"){
IN_LEVEL_MODE = (int)(value);
}
else if(name == "Level_PH_Mode"){
IN_LEVEL_PH_MODE = (int)(value);
}
else if(name == "Level_Drain_Mode"){
IN_LEVEL_DRAIN_MODE = (int)(value);
}
else if(name == "AUX_Mode"){
IN_AUX_MODE = (int)(value);
}
else if(name == "Arduino_Mode"){
IN_ARDUINO_MODE = (int)(value);
}
else{
Serial.println("INVALID ARGS PASSED TO SETINPUT");
keep = false;
}
}
joopie
02/08/2014, 06:26 PM
Here is what it looks like when i put the password in. All the "Mode" settings have an On Off or Auto. PH Mode has continuous, off or calibration modes.<br>
http://rareglitch.net/cfg.PNG
dre7606
02/10/2014, 06:56 AM
Joopie, thank you so much for sharing!
I have not for got I want to try to build this and make it work I have all the parts just need to get the website part set up to run on my website and domain
joopie
05/21/2014, 09:08 AM
I am replacing my current solenoids with these: http://www.wicvalve.com/
They are excellent and a good price. You can even select your coil voltage! I will be building a salt water mixing station next. My plan is to only dump a bag of salt in and it will do the rest. I will also experiment with a better web site GUI.
cant wait to see your new Gui and mixing station
joopie
05/29/2014, 04:32 PM
I have been working on the web page mostly. Some of the physical parts are built. This wont be using an arduino but a beaglebone black instead.
Since nothing is really working yet i made a simulated site to show how it'll work. Click on parts and turn them on or off. I will prob make another thread with a complete explanation of everything once its near complete. Let me know what you guys think! After this project i think ill be updating the display tanks controller and web site.
http://www.rareglitch.net/SWMS/index_sim.html
ElmoC
06/12/2014, 05:11 PM
Was wandering around the site and came across your thread. Really nice stuff. Been playing around some with an arduino so was interested to see how you had done things.
However, the thing that caught my attention and wanted to ask about is those solenoids you gave a link to. Is it safe to use brass on the aquarium's plumbing? Since brass is a copper/zinc alloy, I would be worried about copper getting into the system. Is this not an issue with brass?
joopie
06/12/2014, 05:29 PM
yes it is an issue, that's why i have upgraded my solenoids to the plastic WIC valve solenoids. Like this one: http://www.wicvalve.com/1-2-Inch-Plastic-Electric-Air-Gas-Water-Solenoid-Valve-NC-2PCG-1-2-D.htm
joopie
07/23/2014, 08:08 PM
Not much to report yet. I have made some progress on the beaglebone code with python but have been busy with other stuff so haven't had much time to complete this project. Should be able to resume work on it soon.
Sonicboom
07/31/2014, 09:41 AM
Hi Joopie, so glad i found this. I have been reading all i can on the arduino and was about to start my controller project. do you know if the new Adruino Tre can serve as an upgrade to the Mega?
joopie
08/01/2014, 06:30 PM
Hi Joopie, so glad i found this. I have been reading all i can on the arduino and was about to start my controller project. do you know if the new Adruino Tre can serve as an upgrade to the Mega?
Definitely looks like an upgrade. I just took a look at the specs and saw the new web IDE it uses. Looks awesome! My new project is using a beaglebone black, which i love since i can host everything from it(web server,database,IO,controller). According to the article they developed the Tre from arduino and beaglebone. If you are familiar with the arduino platform i would hold out for the Tre, otherwise check out beaglebone. The beaglebone is not as well documented as arduino but if you know linux then you should be able to figure it out. Once i finish this project i plan on converting over the fish tank controller to the beaglebone(or maybe a Tre =) ) Then ill host the web server and database off of it and can remove the PC under the stand. That'll lower the power bill slightly.
I use SSH to remote into my beaglebone from any location so i can work on it. I am able to access the files from my network and can just program that way as well. Here is a screen shot of my SSH session.
http://www.rareglitch.net/share/pic.png
Sonicboom
08/01/2014, 06:40 PM
Awesome, thanks for confirming. I will hold out for the tre. I already have a synology Nas which I used to use for hosting website and running the sql dB for them. Now to compile the shopping list for the aquarium controller. We may be working on these at the same time depending on when it releases and when you finish your current project...lol
joopie
08/03/2014, 01:45 PM
So the SWMS is setup and running, i can manually control it via the website right now.The level is kinda messed up right now due to reflections of the ultrasonic. Here is a view of the linux console controlling it, and the actual setup. site: www.rareglitch.net/SWMS (http://www.rareglitch.net/SWMS)
http://www.rareglitch.net/SWMS/swmsbbb.PNG
http://www.rareglitch.net/SWMS/swms.JPG
keith20
08/17/2014, 03:41 PM
joopie, I really like what you've done with the controller. I particularly like being able to view the tanks parameters remotely. I would feel better about monitoring my tank with that capability. I thought there were about 33 pages to this. I only see 3 now. Did I miss something? Is there any way to get a complete copy of the source code you used in this project?
joopie
08/22/2014, 06:49 PM
joopie, I really like what you've done with the controller. I particularly like being able to view the tanks parameters remotely. I would feel better about monitoring my tank with that capability. I thought there were about 33 pages to this. I only see 3 now. Did I miss something? Is there any way to get a complete copy of the source code you used in this project?
I'm sorry, you'll have to scavenge the thread for the pieces you need. I wanted it to be more of a take what you need thing, and not provide a full package so people will build there own system. Maybe once i switch everything over to the beaglebone i'll package it all up and post it. I'll be glad to answer any questions or help out with coding.
yajur
08/30/2014, 12:42 PM
Can you provide the circuit diagram for multiple temperature sensor ?
joopie
08/30/2014, 02:30 PM
Can you provide the circuit diagram for multiple temperature sensor ?
The DS18B20 uses One Wire. Each probe is addressable so you can connect all the signal wires together with a 4.7k resister to the supply side.
http://www.milesburton.com/?title=Dallas_Temperature_Control_Library
http://www.pjrc.com/teensy/td_libs_OneWire.html
http://www.amazon.com/Vktech-DS18b20-Waterproof-Temperature-Transmitter/dp/B00CHEZ250/ref=sr_1_1?ie=UTF8&qid=1409430521&sr=8-1&keywords=ds18b20
fire_fr
08/31/2014, 03:57 AM
Hello
I love your work.
I want to know if you can post the new code of the ardiuno and website please.
Thank you
ps : excuse me for my english.
joopie on the first 2 pages are those all the scripts I would need to run a setup like you have now or is there some missing??
joopie
10/21/2014, 02:44 PM
Hello
I love your work.
I want to know if you can post the new code of the ardiuno and website please.
Thank you
ps : excuse me for my english.
The new code for the mixing station is written in python and ran on a beaglebone black. I don't plan on sharing any of the mixing station stuff until its complete.
joopie
10/21/2014, 02:51 PM
joopie on the first 2 pages are those all the scripts I would need to run a setup like you have now or is there some missing??
I did leave out the PHP code that stores the information into the database that was sent from the arduino Trender function. I will release a package of the arduino side of stuff soon as i switch everything over.
joopie are you still working on this I would like to see the rest of the code to make your old system work
corale
03/19/2015, 04:33 AM
if you guys know of,or find any vulnerabilities id hope you would let me know =). i hope my code will help, and i will try to answer any questions. I am no web developer, i just took the time to learn and understand what to do and piece it all together. as far as charting please see http://www.highcharts.com/. i used the highstock for plotting my points. It needs a json output to load the points. here is the json output of the temp_return table: http://www.rareglitch.net/get_json.php?&table=TEMP_RETURN
Joopie, if you don't like coding, you can also use the JSON converter at https://json-csv.com to convert to Excel and use the charts etc. in there.
joopie are you still active on this thread??? haven't seen you login for a while now
vBulletin® v3.8.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.