home aquaponics - fully controlled aquarium with garden element

hi there !

my plan was to make an ebb / flow aquarium controlled by an arduino mega and then from there go full IoT.
but as a first step I want to control as much as possible just on the mega.

the elements which have to be controlled are:

  • an external sump/garden with lights to simulate ebb and flow
  • heating element
  • aquarium lights
  • aquarium pump

-3 water sensors
-2 ec meters

  • 1 distance sensor to measure water height

  • dht11/22

  • 2 thermistor probes

  • 2 light sensors

  • relays

the time i would like to control via NTP Time via Ethernet Shield

Time-Alarm (4 alarms) to call different functions for the day (Morning, Afternoon, Evening, Night)
Functions Activate Lights and Set Cycle Time of Ebb and Flow Pump

Timer which triggers 3 cycles with different parameters matching Morning Afternoon Evening and Night
1 cycle to check for NTP time every now and then
1 cycle to take sensor readings
1 cycle to activate second pump, depending on daytime this cycle is shorter or longer

interrupts for water/leakage detection via water sensors as well as distance sensor

further ideas for the project are:
ph probe
Thermocouple probe

web control of arduino via rasperry pi / mqtt / node red
alarm messages via email
all sorts of nice graphs and diagrams
webcams

future:
motion detection of fish
fish recognition
fish tracking

So I built a nice frame, set the aquarium up, did all the plumbing with a nice bell siphon, soldered the sensors and made nice acrylic cases for them. after a few tests and improvements everything works fine (except for the thermistor sensors, which react quite funny OO ?? )

As a next step I tested every single element of the device in single sketches and implemented them as they were fit and had no bugs. As you all know - this is where the real trouble starts...

my main issues at the moment are the following points:

  1. thermistor probes need calibration. as they are resistors varying with temperature I think I need a voltage reference to calibrate the excact reading as V = C*I ? - any help is welcome

  2. my main loop runs:

void loop () {
  r.execute();
   unsigned long currentMillis1 = millis();
   // time to update ?
   if ((unsigned long)(currentMillis1 - previousMillisUpdate1) >= intervallUpdate) {
  Scheduler &s = Scheduler::currentScheduler();
  Task &t = s.currentTask();
    digitalClockDisplay();
    previousMillisUpdate1 = currentMillis1;
   }
   Alarm.delay(11);
}

r.execute executes the 3 cycle callback functions:

1 cycle to check for NTP time (every 30 min, should run forever)
1 cycle to take sensor readings (every 20 seconds, should run forever)
1 cycle to activate second pump, depending on daytime this cycle is shorter or longer (hpr cycle - depending of the time of the day switching from high to low )

the ebb and flow cycle code is this:

void abc (){
   Scheduler &s = Scheduler::currentScheduler();
  Task &t = s.currentTask();
    t1.disable();
    r.deleteTask(t1);
    t3.disable();
    r.deleteTask(t3);
    getNtpTime();
    Serial.print("It is ");
    Serial.print(hour());
    printDigits(minute());
    printDigits(second());
    Serial.println("");
    Serial.print("Watering Plants for: ");
    Serial.print (WateringTime / ((unsigned)1000));
    Serial.println ("  Seconds");
    digitalWrite(PumpeA_Relais1, LOW);
    digitalWrite(PumpeK_Relais2, HIGH);
    int x = 0;
do {
   unsigned long currentMillisWatering = millis();
   if ((unsigned long)(currentMillisWatering - previousMillisWatering) >= intervallUpdate) {
   
  digitalClockDisplay();
  UltraSonic();
  x++;    
  previousMillisWatering = currentMillisWatering;
   }
  } 
  while(x < (WateringTime / ((unsigned)1000)));
  
    digitalWrite(PumpeK_Relais2, LOW);
    Serial.println("Watering Plants STOP");
    Serial.print("Watered Plants for: ");
    Serial.print (WateringTime / ((unsigned)1000));
    Serial.println ("  Seconds");
    Serial.print("Waiting: ");
    Serial.print (FlowbackTime / ((unsigned)1000));
    Serial.println ("  Seconds to let Water Flow back");
    int y = 0;
do {
   unsigned long currentMillisafterWatering = millis();
   if ((unsigned long)(currentMillisafterWatering - previousMillisafterWatering) >= intervallUpdate) {
   
  digitalClockDisplay();
  UltraSonic();
  y++;    
  previousMillisafterWatering = currentMillisafterWatering;
   }
  } 
  while(y < (FlowbackTime / (unsigned)1000)); 
    
    Serial.print("Waited: ");
    Serial.print (FlowbackTime / (unsigned)1000);
    Serial.println ("  Seconds to let Water Flow back");
    Serial.println("Turning Aquarium Pump PumpeA_Relais1 ON ");
    digitalWrite(PumpeA_Relais1, HIGH);
    Serial.println("Aquarium Pump PumpeA_Relais1 is ON ");
    Serial.print("Wating for:");
    Serial.print((CycleTime - WateringTime - FlowbackTime)  / (unsigned)1000);
    Serial.println(" Seconds to activate the next Cycle");
    r.addTask(t1);
      t1.enable();
      r.addTask(t3);
      t3.enable();
   }

The Alarms are programmed for:
10:00 setting lights on and activate watering cycle
14:00 lights off and high ebb/flow cycle
16:00 lights on, low ebb flow cycle
21:00 lights off, watering cycle off

here are the alarm functions:

void LightsMorningON () {
  Alarm.delay(11);
   Scheduler &s = Scheduler::currentScheduler();
  Task &t = s.currentTask();
  Serial.print("Good Morning ! It is ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println("");
  Alarm.delay(11);
  Serial.print("Turning Lights ON");
  digitalWrite(PumpeA_Relais1, HIGH);
  Serial.println("setting PumpeA_Relais1: HIGH");
  digitalWrite(PumpeK_Relais2, LOW);
  Serial.println("setting PumpeK_Relais2: LOW");
  digitalWrite(LichtA_Relais3, HIGH);
  Serial.println("setting LichtA_Relais3: HIGH");
  digitalWrite(HeizstabA_Relais4, HIGH);
  Serial.println("setting HeizstabA_Relais4: HIGH");
  Alarm.delay(11);
  t1.enable();
  r.addTask(t1);
  t3.enable();
  r.addTask(t3);
  t4.enable();
  r.addTask(t4);
  t4.setInterval(CycleTime);
Alarm.delay(11);

}

void LightsMorningOFF () {
    Serial.print("Good Afternoon ! It is ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Alarm.delay(11);
   Scheduler &s = Scheduler::currentScheduler();
  Task &t = s.currentTask();
  Serial.print("It is ");
  Serial.println(hour());
  printDigits(minute());
  printDigits(second());
  Alarm.delay(11);
  Serial.print("Turning Lights OFF");
  digitalWrite(LichtA_Relais3, LOW);
  Serial.println("setting LichtA_Relais3: LOW");
  Alarm.delay(11);
  t1.enable();
  r.addTask(t1);
  t3.enable();
  r.addTask(t3);
  t4.enable();
  r.addTask(t4);
  t4.setInterval(CycleTimeHigh);
  Serial.println("Rain: HIGH WATERING CYCLE  IS ON");
Alarm.delay(11);


}

void LightsEveningON () {
  Alarm.delay(11);
   Scheduler &s = Scheduler::currentScheduler();
  Task &t = s.currentTask();
  
  Serial.print("Good Evening! It is ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println("");
  Alarm.delay(11);
  Serial.println("Turning Lights ON");
  digitalWrite(LichtA_Relais3, HIGH);
  Serial.println("setting LichtA_Relais3: HIGH");
  Alarm.delay(11);
  t1.enable();
  r.addTask(t1);
  t3.enable();
  r.addTask(t3);
  t4.enable();
  r.addTask(t4);
  t4.setInterval(CycleTime);
  Serial.println("Rain: HIGH WATERING CYCLE  IS OFF");
Alarm.delay(11);

  

}

void LightsEveningOFF () {
  Alarm.delay(11);
   Scheduler &s = Scheduler::currentScheduler();
  Task &t = s.currentTask();
  Serial.print("Good Night ! It is ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Alarm.delay(11);
  Serial.println("");
  Serial.print("Turning Lights OFF");
  digitalWrite(LichtA_Relais3, LOW);
  Serial.println("setting LichtA_Relais3: LOW");
  t1.enable();
  r.addTask(t1);
  Alarm.delay(11);
  t3.enable();
  r.addTask(t3);
  Alarm.delay(11);
  t4.disable();
  r.deleteTask(t4);
  Alarm.delay(11);
}

after trying many different options I ended up at best with the alarms getting triggered minutes after they should or not at all ...
where it gets really tricky is: whyle a ebb and flow cycle is activated, the alarm should be delayed to let the water flow back

I am helpfull for any ideas or solutions
thank you all and may the fish swim without getting electrocuted :slight_smile:
:o

what I am basically looking for is a way to prioritize the Alarm.repeat functions over my first 2 timer functions (sensor readings, ntp request) but let timer function 3 (ebb flow relay) end its cycle, delaying the alarm function until the cycle function completed

what I am basically looking for is a way to prioritize the Alarm.repeat functions over my first 2 timer functions (sensor readings, ntp request) but let timer function 3 (ebb flow relay) end its cycle, delaying the alarm function until the cycle function completed

also described here
home aquaponics - fully controlled aquarium with garden element - Programming Questions - Arduino Forum (sorry for double posting)

the time i would like to control via NTP Time via Ethernet Shield

Time-Alarm (4 alarms) to call different functions for the day (Morning, Afternoon, Evening, Night)
Functions Activate Lights and Set Cycle Time of Ebb and Flow Pump

Timer which triggers 3 cycles with different parameters matching Morning Afternoon Evening and Night
1 cycle to check for NTP time every now and then
1 cycle to take sensor readings
1 cycle to activate second pump, depending on daytime this cycle is shorter or longer

interrupts for water/leakage detection via water sensors as well as distance sensor

  1. my main loop runs:

Code: [Select]

void loop () {
  r.execute();
   unsigned long currentMillis1 = millis();
   // time to update ?
   if ((unsigned long)(currentMillis1 - previousMillisUpdate1) >= intervallUpdate) {
  Scheduler &s = Scheduler::currentScheduler();
  Task &t = s.currentTask();
    digitalClockDisplay();
    previousMillisUpdate1 = currentMillis1;
   }
   Alarm.delay(11);
}

r.execute executes the 3 cycle callback functions:

1 cycle to check for NTP time (every 30 min, should run forever)
1 cycle to take sensor readings (every 20 seconds, should run forever)
1 cycle to activate second pump, depending on daytime this cycle is shorter or longer (hpr cycle - depending of the time of the day switching from high to low )

the ebb and flow cycle code is this:
Code: [Select]

void abc (){
   Scheduler &s = Scheduler::currentScheduler();
  Task &t = s.currentTask();
    t1.disable();
    r.deleteTask(t1);
    t3.disable();
    r.deleteTask(t3);
    getNtpTime();
    Serial.print("It is ");
    Serial.print(hour());
    printDigits(minute());
    printDigits(second());
    Serial.println("");
    Serial.print("Watering Plants for: ");
    Serial.print (WateringTime / ((unsigned)1000));
    Serial.println ("  Seconds");
    digitalWrite(PumpeA_Relais1, LOW);
    digitalWrite(PumpeK_Relais2, HIGH);
    int x = 0;
do {
   unsigned long currentMillisWatering = millis();
   if ((unsigned long)(currentMillisWatering - previousMillisWatering) >= intervallUpdate) {
   
  digitalClockDisplay();
  UltraSonic();
  x++;    
  previousMillisWatering = currentMillisWatering;
   }
  } 
  while(x < (WateringTime / ((unsigned)1000)));
  
    digitalWrite(PumpeK_Relais2, LOW);
    Serial.println("Watering Plants STOP");
    Serial.print("Watered Plants for: ");
    Serial.print (WateringTime / ((unsigned)1000));
    Serial.println ("  Seconds");
    Serial.print("Waiting: ");
    Serial.print (FlowbackTime / ((unsigned)1000));
    Serial.println ("  Seconds to let Water Flow back");
    int y = 0;
do {
   unsigned long currentMillisafterWatering = millis();
   if ((unsigned long)(currentMillisafterWatering - previousMillisafterWatering) >= intervallUpdate) {
   
  digitalClockDisplay();
  UltraSonic();
  y++;    
  previousMillisafterWatering = currentMillisafterWatering;
   }
  } 
  while(y < (FlowbackTime / (unsigned)1000)); 
    
    Serial.print("Waited: ");
    Serial.print (FlowbackTime / (unsigned)1000);
    Serial.println ("  Seconds to let Water Flow back");
    Serial.println("Turning Aquarium Pump PumpeA_Relais1 ON ");
    digitalWrite(PumpeA_Relais1, HIGH);
    Serial.println("Aquarium Pump PumpeA_Relais1 is ON ");
    Serial.print("Wating for:");
    Serial.print((CycleTime - WateringTime - FlowbackTime)  / (unsigned)1000);
    Serial.println(" Seconds to activate the next Cycle");
    r.addTask(t1);
      t1.enable();
      r.addTask(t3);
      t3.enable();
   }

The Alarms are programmed for:
10:00 setting lights on and activate watering cycle
14:00 lights off and high ebb/flow cycle
16:00 lights on, low ebb flow cycle
21:00 lights off, watering cycle off

here are the alarm functions:

Code: [Select]

void LightsMorningON () {
  Alarm.delay(11);
   Scheduler &s = Scheduler::currentScheduler();
  Task &t = s.currentTask();
  Serial.print("Good Morning ! It is ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println("");
  Alarm.delay(11);
  Serial.print("Turning Lights ON");
  digitalWrite(PumpeA_Relais1, HIGH);
  Serial.println("setting PumpeA_Relais1: HIGH");
  digitalWrite(PumpeK_Relais2, LOW);
  Serial.println("setting PumpeK_Relais2: LOW");
  digitalWrite(LichtA_Relais3, HIGH);
  Serial.println("setting LichtA_Relais3: HIGH");
  digitalWrite(HeizstabA_Relais4, HIGH);
  Serial.println("setting HeizstabA_Relais4: HIGH");
  Alarm.delay(11);
  t1.enable();
  r.addTask(t1);
  t3.enable();
  r.addTask(t3);
  t4.enable();
  r.addTask(t4);
  t4.setInterval(CycleTime);
Alarm.delay(11);

}

void LightsMorningOFF () {
    Serial.print("Good Afternoon ! It is ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Alarm.delay(11);
   Scheduler &s = Scheduler::currentScheduler();
  Task &t = s.currentTask();
  Serial.print("It is ");
  Serial.println(hour());
  printDigits(minute());
  printDigits(second());
  Alarm.delay(11);
  Serial.print("Turning Lights OFF");
  digitalWrite(LichtA_Relais3, LOW);
  Serial.println("setting LichtA_Relais3: LOW");
  Alarm.delay(11);
  t1.enable();
  r.addTask(t1);
  t3.enable();
  r.addTask(t3);
  t4.enable();
  r.addTask(t4);
  t4.setInterval(CycleTimeHigh);
  Serial.println("Rain: HIGH WATERING CYCLE  IS ON");
Alarm.delay(11);


}

void LightsEveningON () {
  Alarm.delay(11);
   Scheduler &s = Scheduler::currentScheduler();
  Task &t = s.currentTask();
  
  Serial.print("Good Evening! It is ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Serial.println("");
  Alarm.delay(11);
  Serial.println("Turning Lights ON");
  digitalWrite(LichtA_Relais3, HIGH);
  Serial.println("setting LichtA_Relais3: HIGH");
  Alarm.delay(11);
  t1.enable();
  r.addTask(t1);
  t3.enable();
  r.addTask(t3);
  t4.enable();
  r.addTask(t4);
  t4.setInterval(CycleTime);
  Serial.println("Rain: HIGH WATERING CYCLE  IS OFF");
Alarm.delay(11);

  

}

void LightsEveningOFF () {
  Alarm.delay(11);
   Scheduler &s = Scheduler::currentScheduler();
  Task &t = s.currentTask();
  Serial.print("Good Night ! It is ");
  Serial.print(hour());
  printDigits(minute());
  printDigits(second());
  Alarm.delay(11);
  Serial.println("");
  Serial.print("Turning Lights OFF");
  digitalWrite(LichtA_Relais3, LOW);
  Serial.println("setting LichtA_Relais3: LOW");
  t1.enable();
  r.addTask(t1);
  Alarm.delay(11);
  t3.enable();
  r.addTask(t3);
  Alarm.delay(11);
  t4.disable();
  r.deleteTask(t4);
  Alarm.delay(11);
}

after trying many different options I ended up at best with the alarms getting triggered minutes after they should or not at all ...
where it gets really tricky is: whyle a ebb and flow cycle is activated, the alarm should be delayed to let the water flow back

I am helpfull for any ideas or solutions
thank you all and may the fish swim without getting electrocuted :slight_smile:
:o

IoT_Enthusiast:
also described here (sorry for double posting)

Don't do that again. Threads merged.

ok never ever again . promised :o :o