Robert Allan
Published © GPL3+

Fish Tank Control System

A simple project to achieve timeclock control of Lights, Filter and Aeration, along with remote override via MQTT.

AdvancedFull instructions provided8 hours398
Fish Tank Control System

Things used in this project

Hardware components

ESP8266 ESP-12E
Everything ESP ESP8266 ESP-12E
×1
Capacitor 1000 µF
Capacitor 1000 µF
×1
Wire Cable - By the Foot
OpenBuilds Wire Cable - By the Foot
×1
Double Sided Tape
×1
LM317 DC-DC Linear Converter Module
×1
Resistor 10k ohm
Resistor 10k ohm
×7
Resistor 221 ohm
Resistor 221 ohm
×4
Resistor 1k ohm
Resistor 1k ohm
×1
4n35 Opto Isolator
×2
IRF520 Mosfet
×2
AQH3213 SolidState Relay
×1
PCB Screw Terminals
×1
UK Socket Outlet
×1
UK 13A Plug
×1
3Amp Fuse
×1
0.33A Surface Mount Resettable Fuse, 240V ac/dc
×1
Female Header 8 Position 1 Row (0.1")
Female Header 8 Position 1 Row (0.1")
×1
Prototype PCB Board 4cm x 6cm Double Sided
×1

Software apps and online services

MQTT
MQTT
MQTT Dash Android App
Arduino IDE
Arduino IDE

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Rotary Multi-tool
Multi-Meter

Story

Read more

Schematics

Fishtank Controller Schematic

Schematic fish tank timer control 20180425203109 xk1pahtyev

Code

ESP12 Fishtank Control Version1

C/C++
/*
    ESPHelper.h
    Copyright (c) 2017 ItKindaWorks Inc All right reserved.
    github.com/ItKindaWorks
    This file is part of ESPHelper
    ESPHelper is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.
    ESPHelper is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
    You should have received a copy of the GNU General Public License
    along with ESPHelper.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "ESPHelper.h"

/*
Copyright 2016 German Martin (gmag11@gmail.com). All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are
permitted provided that the following conditions are met :
1. Redistributions of source code must retain the above copyright notice, this list of
conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this list
of conditions and the following disclaimer in the documentation and / or other materials
provided with the distribution.
THIS SOFTWARE IS PROVIDED BY <COPYRIGHT HOLDER> ''AS IS'' AND ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.IN NO EVENT SHALL <COPYRIGHT HOLDER> OR
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
CONSEQUENTIAL DAMAGES(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT(INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
The views and conclusions contained in the software and documentation are those of the
authors and should not be interpreted as representing official policies, either expressed
or implied, of German Martin
*/
/*
 Name:    NtpClientLib
 Created: 17/08/2016
 Author:  Germán Martín (gmag11@gmail.com)
 Maintainer:Germán Martín (gmag11@gmail.com)
 Editor:  http://www.visualmicro.com
 Library to get system sync from a NTP server
*/

#include <NtpClientLib.h>
#include <TimeLib.h>

#define TOPIC "home/fishtank/control"
#define STATUS_TOPIC "home/fishtank/status"

#define LIGHT_PIN 5
#define PUMP_PIN 4
#define BUBBLES_PIN 13

#define NETWORK_HOSTNAME "ESP12FishTank"
#define OTA_PASSWORD "YourOTAPassword" // Create your own unique password for OTA updates

char* relayTopic = TOPIC;
char* statusTopic = STATUS_TOPIC;

char* hostnameStr = NETWORK_HOSTNAME;
char* otaPassword = OTA_PASSWORD;

const int LightPin = LIGHT_PIN;
const int FilterPin = PUMP_PIN;
const int AeratorPin = BUBBLES_PIN;

bool LightSts = false;
bool FilterSts = false;
bool AeratorSts = false;

boolean syncEventTriggered = false; // True if a time even has been triggered
NTPSyncEvent_t ntpEvent; // Last triggered event

int8_t timeZone = 0;

int ActHr = 0;
int ActMin = 0;
int ActDay = 0;

netInfo homeNet = {  .mqttHost = "***.***.***.***",     //Your MQQT Brokers IP Adress
          .mqttUser = "",   //can be blank depending on your broker config
          .mqttPass = "",   //can be blank depending on your broker config
          .mqttPort = 1883,         //default port for MQTT is 1883 - only chance if needed.
          .ssid = "WIFISSID", // Your wifi SSID 
          .pass = "WIFICode"};
          
ESPHelper myESP(&homeNet);


void setup() {
  
  Serial.begin(115200); //start the serial line
  delay(500);

  Serial.println("Starting Up, Please Wait...");

  myESP.OTA_enable();
  myESP.OTA_setPassword(otaPassword);
  myESP.OTA_setHostnameWithVersion(hostnameStr);
  myESP.OTA_begin();

  myESP.addSubscription(relayTopic);

  myESP.begin();
  
  myESP.setMQTTCallback(callback);
  
  NTP.begin("es.pool.ntp.org", timeZone, true);
  NTP.setInterval(3600);
  Serial.print("Got NTP time: ");
  Serial.println(NTP.getTimeDateString(NTP.getLastNTPSync())); 
  pinMode(LightPin, OUTPUT);
  digitalWrite(LightPin, LOW);
  pinMode(FilterPin, OUTPUT);
  digitalWrite(FilterPin, LOW);
  pinMode(AeratorPin, OUTPUT);
  digitalWrite(AeratorPin, LOW);  
  Serial.println("Initialization Finished.");
  delay(100);
  
}

void loop(){
  myESP.loop();  //run the loop() method as often as possible - this keeps the network services running
  time_t t = now();
  ActHr = hour();
  ActMin = minute();
  ActDay = weekday();
  if (ActHr == 21 && ActMin == 45){ // At 2145 perform the below except Saturday Night
    if (AeratorSts == false && ActDay != 7){
      AeratorSts = true;  
    }
    if (LightSts == true){
      LightSts = false;  
    }
    if (FilterSts == false){
      FilterSts = true;  
    }
  }
  if (ActHr == 06 && ActMin == 00){ // At 0600 perform the below
    if (LightSts == false){
      LightSts = true;  
    }
    if (FilterSts == false){
      FilterSts = true;  
    }
  }
  if (ActHr == 07 && ActMin == 00){ // At 0600 perform the below
    if (AeratorSts == true){
      AeratorSts = false;
    }  
  }
  switch (AeratorSts){
    case false:
      digitalWrite(AeratorPin, LOW);
      break;
    case true:
      digitalWrite(AeratorPin, HIGH);
      break;
  }
  switch (LightSts){
    case false:
      digitalWrite(LightPin, LOW);
      break;
    case true:
      digitalWrite(LightPin, HIGH);
      break;
  }
  switch (FilterSts){
    case false:
      digitalWrite(FilterPin, LOW);
      break;
    case true:
      digitalWrite(FilterPin, HIGH);     
      break;
  }
      
  yield();
}


void callback(char* topic, byte* payload, unsigned int length) {
  String topicStr = topic;
  
  if(payload[0] == '0'){ // Payload 0 turn off the lights
    Serial.println("Payload Received 0");
    myESP.publish(statusTopic,"0",true);  
    LightSts = false;

  }
  else if (payload[0] == '1'){ // Payload 1 turn on the lights
    Serial.println("Payload Received 1");
    myESP.publish(statusTopic,"1",true);
    LightSts = true;
   
  }
  else if (payload[0] == '2'){ // Payload 2 turn off the aerator
    Serial.println("Payload Received 2");
    myESP.publish(statusTopic,"2",true);
    AeratorSts = false;
  }
  else if (payload[0] == '3'){ // Payload 3 turn on the aerator
    Serial.println("Payload Received 3");
    myESP.publish(statusTopic,"3",true);
    AeratorSts = true;
  }
  else if (payload[0] == '4'){ // Payload 4 turn off the filter
    Serial.println("Payload Received 4");
    myESP.publish(statusTopic,"4",true);
    FilterSts = false;
  }
  else if (payload[0] == '5'){ // Payload 5 turn on the filter
    Serial.println("Payload Received 5");
    myESP.publish(statusTopic,"5",true);
    FilterSts = true;
  }
  else if (payload[0] == '6'){ // Payload 6 Manual Time Sync
      Serial.println("Payload Received 6");
      myESP.publish(statusTopic,"6",true);
      Serial.print("1. Got NTP time : ");
      Serial.println(NTP.getTimeDateString(NTP.getLastNTPSync()));
      NTP.onNTPSyncEvent([](NTPSyncEvent_t event) {
      ntpEvent = event;
      if (ntpEvent) {
        Serial.print("Time Sync error: ");
        myESP.publish(statusTopic,"611",true); // if 611 published on the status topic then an error occurred
        if (ntpEvent == noResponse)
          Serial.println("NTP server not reachable");
        else if (ntpEvent == invalidAddress)
          Serial.println("Invalid NTP server address");
      }
      else {
      delay(100);
      Serial.print("2. Got NTP time: ");
      Serial.println(NTP.getTimeDateString(NTP.getLastNTPSync()));
      myESP.publish(statusTopic,"601",true); // if 601 published on the status topic then it was successful
      }    
      syncEventTriggered = true;
      });
      delay(3000);
      String xActHr = String(ActHr);   
      String xActMin = String(ActMin);
      String xTime = String(xActHr + xActMin);
      char xTimex[5];
      xTime.toCharArray(xTimex,5);  
      Serial.print("Current Time: ");
      Serial.println(xTimex);
      myESP.publish(statusTopic,xTimex,true); // time published on the status topic
  }
}

Credits

Robert Allan

Robert Allan

6 projects • 5 followers
Maker of things.... some get completed....

Comments