Hydroponic Tower Project

I am working a hydroponic tower project and I have run into an issue in finalizing the last step.

The system comprises of two float sensors, 12v submersable pump, Arduino Nano and a LED light strip.

The goal is to have the upper and lower float sensors to determine if the water level is at a sufficient level, provide the respective LED color and turn the pump on and off for a designated timeframe.

The issue that I am having is that I can make each section of the code work independently but when I try to combine all into one program, it doesn't work exactly the way it was planned. The pump turns on and off based on the test times but the LEDs do not match the water levels based on the position of the float sensors. I'm sure the issue could be in the way the IF statements are structured and I'm sure there is a more professional way of correcting the issue so I am reaching out for your support. The code is below for your viewing. Please go easy on me as I am still learning as I am going.

#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
const byte TFS = 7;
const byte BFS = 2;
const byte relay = A5;
byte RelayState;
#define LED_PIN    6
#define LED_COUNT 17
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
unsigned long previousMillis = 0;

unsigned long intervalOn = 4 * 1000; // 4 seconds on
unsigned long intervalOff = 10 * 1000; // 10 seconds off
unsigned long interval = intervalOn; // start with pump on

boolean status = false; //true = on; false = off

void setup() {
  //Serial.begin(9600);
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
  clock_prescale_set(clock_div_1);
#endif
  pinMode (TFS, INPUT_PULLUP);
  pinMode (BFS, INPUT_PULLUP);
  pinMode(relay, OUTPUT);
  strip.begin();
  strip.show();
  strip.setBrightness(150); // Set BRIGHTNESS to about 1/5 (max = 255)

}

void loop() {

  unsigned long currentMillis = millis();

  if (!status) {
    if (currentMillis - previousMillis >= intervalOff) {
      previousMillis = currentMillis;
      status = true;
    }
    else {
      status = false;
    }
  }
  else {
    if (currentMillis - previousMillis >= intervalOn) {
      previousMillis = currentMillis;
      status = false;
    }
    else {
      status = true;
    }
  }
  //Serial.println("Status: " + String(status));

  if (digitalRead (BFS) == HIGH && digitalRead (TFS) == HIGH) {
    if (status) {
      RelayState = HIGH;
      colorWipe(strip.Color(0,   255,   0), 50); // Green
    }
  }

  else if (digitalRead (BFS) == HIGH && digitalRead (TFS) == LOW) {
    if (status) {
      RelayState = HIGH;
      colorWipe(strip.Color(0,   0,   255), 50); // Blue
    }
  }
  else {
    RelayState = LOW;
    colorWipe(strip.Color(255,   0,   0), 50); // red

  }

  digitalWrite(relay, RelayState);
}

void colorWipe(uint32_t color, int wait) {
  for (int i = 0; i < strip.numPixels(); i++) { // For each pixel in strip...
    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip.show();                          //  Update strip to match
    //    delay(wait);                           //  Pause for a moment
  }
}

What does the line of code do for you ?


Always show us a good schematic of your proposed circuit.
Show us a good image of your ‘actual’ wiring.
Give links to components.


A State Machine is your friend.


This looks odd :thinking:

Adafruit_NeoPixel strip = Adafruit_NeoPixel(17, PIN, NEO_GRB + NEO_KHZ800);

The logic from the real world did not make it into the sketch.
You may remove the if-statements with the RelayState. That part of the code has to be written again.
Try to relax, and think what it should do without thinking in code.
Then try to write that in code.

This does not really do something:

interval - intervalOn;

Where is the Serial.begin() ?

Thank you for the reply, sorry for the late response. I have updated the sketch along with some of the information that was requested in terms to links to the parts that are being used. I have reached out for some additional help and this is where we are currently. The system is working as planned with the exception of the timing. The LED lights (color) match up to the float sensors respective position. The pump cuts on when the bottom float sensor moves to the high position but does not cut off per the defined timing. The only time it cuts off is when the bottom float sensor moves to the low positon.

Please show the sketch in a new post, to keep the flow of the discussion going sequentially.
Can you tell what it should do ?

A millis-timer runs often on its own in the main part of the loop() function. It can be started and stopped in a other part of the loop(). The decision to start the millis-timer is in a different part of the sketch then the millis-timer itself.

Here is an example to start a millis-timer (it turns itself off): millis_single_delay.ino.
The first half of the loop() turns the millis-timer on, and the second half of the loop() is the millis-timer.

A millis-timer with different times for "on" and "off" is still just one millis-timer, see: millis_different_on_and_off_times.ino

If you use the green buttons of my "Fun with millis", then you can try my examples directly in Wokwi.

Let’s see your complete current sketch too.

What are the LEDS supposed to do and what do they do instead?

Have you tried serial printing the pin states to make sure they are doing the correct thing?

Do the thingies connected to the pins need debounce?

An easy way to temporarily make this issue moot is to run the loop slower than any bouncing by placing

 delay(30); 

at the top.

It won't have much effect on the other timing which is very longer.

This

  if (digitalRead (BFS) == HIGH && digitalRead (TFS) == HIGH) {
    if (status) {
      RelayState = HIGH;
      colorWipe(strip.Color(0,   255,   0), 50); // Green
    }
  }

Makes me question the roll of status, as you don't do anything if the status is flase, that is to say it doesn't control the relay - once the relay goes on, nothing turns it off until the TFS and BFS change.

The code is doing exactly what you wrote (I know, so annoying!) rather than doing what you want. Or think.

I recommend you describe the desired behaviour better, and draw a simple flowchart - I don't think your if/else statements are separating your logical cases correctly.

It might get a bit verbose and repetitive, but you could write four if statements covering the four possible TFS/BFS conditions, and within each use status to do (or undo) something.

It would make the logic way clearer. And possible point up your mis-thinking.

I threw in the wokwi and placed an alternate to the LED strip so you can see what is going on, please take this link and play with it to see it doing what you wrote, and to help you describe what you want:


Aren't we all. :expressionless:

a7

The idea was to have the following:

When the water level is: LOW (top float sensor(TFS) is high and bottom float sensor(BFS) Low, LED Strip will turn red

High TFS is low and BFS is high. LED is green

Half way TFS is high and BFS is high, LED is Blue, pump on for 5 minutes and off for 45 minutes. In the sketch I have 4 seconds and 10 seconds for testing purposes.

Here's what I mean, I had a few so I rejiggered your code - it should do the same as the way you put it: TBC, your code, not your description.

Half way TFS is high and BFS is high, LED is Blue, pump on for 5 minutes and off for 45 minutes. In the sketch I have 4 seconds and 10 seconds for testing purposes.

Your section that runs in that case does not respect status and turn the pump on and off.

I cut the times down to 1 and 3 seconds. 4 and 10 still too long.

L8R

a7

Good Morning! I had a few minutes to look at the simulation provided by A7 this morning before work. One thing I noticed in the simulation is that the relay does not flash to the 1/3 second sequence. Maybe I’m confusing everyone with the bowl of spaghetti that I am putting out there for you guys and I am asking you to untangle it and make straight lines so I thank you for your patience.

The sketch seems to be giving the right feedback for the LED Strip based on the position of the float sensors. The issue seems to be in turning on and off the relay that controls the water pump. At the moment, it just turns on and there is nothing telling it to turn off.

Below is the process flow:

TFS=Top Float Sensor
BFS=Bottom Float Sensor

TFS LOW/BFS HIGH

  • LED strip is Green
  • Relay goes High
  • 1/3 second sequence activated

TFS HIGH/BFS HIGH

  • LED strip is Blue
  • Relay goes High
  • 1/3 second sequence activated

TFS HIGH/BFS LOW

  • LED Strip is Red
  • Relay goes LOW

TFS LOW/BFS LOW

  • LED Strip is Red
  • Relay goes Low
#include <Adafruit_NeoPixel.h>
#ifdef __AVR__
#include <avr/power.h> // Required for 16 MHz Adafruit Trinket
#endif
const byte TFS = 7;
const byte BFS = 2;
const byte statusLED = 10;
const byte relay = A5;
byte RelayState;
#define LED_PIN    6
#define LED_COUNT 17
Adafruit_NeoPixel strip(LED_COUNT, LED_PIN, NEO_GRB + NEO_KHZ800);
unsigned long previousMillis = 0;

unsigned long intervalOn = 1 * 1000; // 1 seconds on  life too short!
unsigned long intervalOff = 3 * 1000; // 3 seconds off
unsigned long interval = intervalOn; // start with pump on

boolean status = false; //true = on; false = off

void setup() {
  Serial.begin(9600);
#if defined(__AVR_ATtiny85__) && (F_CPU == 16000000)
  clock_prescale_set(clock_div_1);
#endif
  pinMode (TFS, INPUT_PULLUP);
  pinMode (BFS, INPUT_PULLUP);
  pinMode(relay, OUTPUT);
  pinMode(statusLED, OUTPUT);

  strip.begin();
  strip.show();
  strip.setBrightness(150); // Set BRIGHTNESS to about 1/5 (max = 255)
}

void loop() {
  unsigned long currentMillis = millis();

  delay(50);
  if (!status) {
    if (currentMillis - previousMillis >= intervalOff) {
      previousMillis = currentMillis;
      status = true;
    }
    else {
      status = false;
    }
  }
  else {
    if (currentMillis - previousMillis >= intervalOn) {
      previousMillis = currentMillis;
      status = false;
    }
    else {
      status = true;
    }
  }

  digitalWrite(statusLED, status ? HIGH : LOW);

  if (digitalRead (BFS) == HIGH && digitalRead (TFS) == HIGH) {
    if (status) {
      RelayState = HIGH;
      colorWipe(strip.Color(0,   255,   0), 50); // Green
    }
  }

  if (digitalRead (BFS) == HIGH && digitalRead (TFS) == LOW) {
    if (status) {
      RelayState = HIGH;
      colorWipe(strip.Color(0,   0,   255), 50); // Blue
    }
  }

  if (digitalRead (BFS) == LOW && digitalRead (TFS) == HIGH) {
    RelayState = LOW;
    colorWipe(strip.Color(255,   0,   0), 50); // red
  }

  if (digitalRead (BFS) == LOW && digitalRead (TFS) == LOW) {
    RelayState = LOW;
    colorWipe(strip.Color(255,   0,   0), 50); // red
  }

  digitalWrite(relay, RelayState);
}

void colorWipe(uint32_t color, int wait) {
  static unsigned long lastSaidColor;

  if (color != lastSaidColor) {

    Serial.print("strip ");
    switch (color) {
      case 0xff0000 :
      Serial.println("RED");
      break;
      
      case 0x00ff00 :
      Serial.println("GREEN");
      break;
      
      case 0x0000ff :
      Serial.println("BLUE");
      break;
    
    }

    lastSaidColor = color;
  }
}

void colorWipeX(uint32_t color, int wait) {
  for (int i = 0; i < strip.numPixels(); i++) { // For each pixel in strip...
    strip.setPixelColor(i, color);         //  Set pixel's color (in RAM)
    strip.show();                          //  Update strip to match
    //    delay(wait);                           //  Pause for a moment
  }
}

Because… there is nothing telling it to turn off.

This

  if (digitalRead (BFS) == HIGH && digitalRead (TFS) == HIGH) {
    if (status) {
      RelayState = HIGH;
      colorWipe(strip.Color(0,   255,   0), 50); // Green
    }
  }

Only turns the pump on. Once it is on, it stays on. You have to explicitly turn off the pump when you want it off.

  if (digitalRead (BFS) == HIGH && digitalRead (TFS) == HIGH) {
    colorWipe(strip.Color(0,   255,   0), 50); // Green
    if (status) {
      RelayState = HIGH;
    }
    else RelayState = LOW;
  }

I also moved the colorWipe() call so that case will change the strip color immediately the float sensors are seen HIGH/HIGH. The pump will not come on until status goes true, which may take awhile if you catch it during the false phase of its periodic toggling.

a7

You can do the pump control without the Arduino just using the float contacts and a relay to latch in the low level sense - when low level detected , latch that signal , AND NOT high level:
pump on .
when high level detected pump off , latch broken .
The interlocking so formed will make a more reliable system that using an Arduino ( less parts ).

If my idea of what's going on in your real world is correct, you have set yourself up for the pump going on and off as the BFS goes HIGH and LOW.

This may make the pump unhappy.

Typically in a control system some hysteresis is coded in or otherwise provided.

As @hammy has just suggested, typically the pump is turned on when the water goes over the top sensor and is only turned off when it falls below the bottom sensor.

This provides for having the pump be on or off as appropriate during the time when the water is between too full and too empty.

And automagically means you won't be frequently turning the pump on and off as the level deviates only a small amount about the bottom sensor.

The hysteresis is exactly the distance between the two sensors, which Imma guess means the pump would be happier, running down the level, then waiting for it to recover before turning on again.

You can still use the Arduino, but again @hammy is correct, this would just take a fairly simple circuit even if you did include your idea of duty-cycling the pump.

I use software wherever, it is easier for me and remains the ultimate flexibility for reporting, modifying and enhancing as well as inclusion in a larger context. Should that day come.

a7

When I started micros we’re a bit new and wouldn’t be trusted to look after such a system !!
All sequence control systems were pretty much done with relays , some times a lot of them !
PLC’s came along later on , the ladder logic used enabled the relay guys to move over as the principals were the same.

It occurs to me that another kind of engineer would cook up a srsly old school mechanical solution.

See sump pumps and probably your fluch toilet. Very hard to argue with decades of success.

I totally agree that reliability and even redundant solutions are important. It pains me to know that so many ill-designed modern products die of "control board failures". And service is a board level swap.

a7

Yep a ball cock as used in the Thunder box is a good solution

Some confusion here, if the water level is halfway, one switch should be open and the other closed unless one switch is normally open and the other normally closed. Are the switches the same type? Post a link to the float switches.

The float switches are reed switches. One them is inverted from the other(see pictures in post1). They should read opposite of each other.

[> Blockquote](Anndason 6 Pieces Plastic PP Float Switch Fish Tank Liquid Water Level Sensor,Model: DP5200 https://a.co/d/3RwzLDN)

@taymar16 okay, you just clipped the grass under my feet.

I assume there are three significant levels of liquid.

1 - Too empty

2 - In between and

3 - Too full

There are four possible combinations of the sensor outputs

TFS / BFS
a - HIGH / HIGH
b - HIGH / LOW
c - LOW / HIGH and
d - LOW / LOW

help us by matching conditions 1, 2 and 3 with the sensor combinations a, b, c and d. One letter shouldn't show up as it is impossible for a tank to be both too full and too empty.

I thought I had it, and it makes no difference to the advice I have been giving.

I get no mileage out of examining your pictures and drawings wrt how the sensors function or what the sense of a given sensor is.

a7