While button is held, output another pin as high- up to a maximum time length

I have an interesting project that I cannot find another example of. I have been searching and practicing on and off all week.

The goal of this project is to turn on and off a fertilizer pump on a planter, so that fertilizer is only applied while seed is being dropped instead of constantly on. An NPN NO proximity sensor will be attached to the plastic rotating seed plate so that it will read an attached piece of metal. The sensor when active will allow path to ground, which is connected to 5V in series with a resistor (see diagram). The code will then output 5V from pin 13 to the 5V relay, which will close the 12V circuit to allow the fertilizer pump to activate.

The tricky part of this is that there needs to be a safety time-out. If you are planting and happen to stop planting while the proximity sensor is active I do not want the fertilizer pump to run indefinitely. Once the proximity sensor activates it should output HIGH to the 5V relay for up to a maximum time length (~2seconds). If the sensor de-activates before the maximum time length is reached, the 5V relay should immediately go back to LOW.

This is the code I have so far;

const int Relay =  13;  //SET PIN 13 AS A CONSTANT FOR RELAY ACTIVATION
const int ProxPin = 10;    //SET PIN 10 AS A CONSTANT FOR PROXIMITY SWITCH
unsigned long startMillis;  //some global variables available anywhere in the program
unsigned long currentMillis;
const unsigned long period = 6000;  //milliseconds

void setup() {
  pinMode(Relay, OUTPUT); //SET THE RELAY PIN AS AN OUTPUT
  pinMode (ProxPin, INPUT); //SET THE PROXIMITY SWITCH AS AN INPUT (NPN NO)

  startMillis=millis(); //initial start time
}

void loop() {
  if (digitalRead(ProxPin) == HIGH) {
    
    currentMillis = millis(); //get the current "time" (milliseconds since the program started)
    if (currentMillis - startMillis >= period) //test whether the period has elapsed
    {
      digitalWrite(Relay, HIGH);
    } 
    else
      digitalWrite(Relay, LOW);
    
  }
      else
      digitalWrite(Relay, LOW);
}

The problem I see is that when the code is looped it seems to be resetting currentmillis=millis(), when in reality I need to loop to a spot just below this and incrementally check if enough time has passed. I do not know how to do this when I only want to write currentmillis “if” the proxpin is high (so I can’t put it in general setup).

Does anyone have any advice or has seen something similar? I can find code that is ran for a set time after a button press but none that requires the button to be held and also turns off the code if the button is released.

Thanks in advance - (yes I am new to arduino)

FERTILIZER CONTROLLER simplified.pdf (13.6 KB)

Go through your logic very carefully.

As the code currently stands, you read out the time only when (digitalRead(ProxPin) == HIGH).

If you want all decisions to be based on the most current time, read it just after the start of the loop function.

Usually, people reset startMillis periodically. Otherwise this is a one shot program. In your case, it might make sense to reset startMillis either when the button is released, or pressed, depending on what you want to accomplish. Study the "blink without delay" program example to see what I mean.

The first thing to get down is sensing when [color=blue]proxPin[/color] changes . In the IDE refer to: file/examples/digital/state change detection.

Next, set the timer up as free-running, that is, it increments continuously. When timer is done, that is, reaches preset, use that condition to shut off the pump.

Finally, reset the timer - [color=blue]currentMillis = millis()[/color] - when the state of [color=blue]proxPinChanged[/color] goes high. [color=blue]proxPinChanged[/color] will be high for only one pass through the program each time the sensor is triggered.

So, as long as pulses are arriving at a certain rate the timer will never time out, keeping the pump on. If pulses cease the pump will shut off after X time.

jremington:
Go through your logic very carefully.

As the code currently stands, you read out the time only when (digitalRead(ProxPin) == HIGH).

If you want all decisions to be based on the most current time, read it just after the start of the loop function.

The first decision is whether or not the proxpin is HIGH or not- if it is then its time to activate the relay and start the timer. If its not then the relay shall remain off.

I think this is where I'm confused. I'm expecting to need a sub-loop within the main loop, which will run when the proxpin is HIGH.

The sub-loop would turn on the relay for up to a certain time and then turn the relay off. The sub-loop would then have to stay put until the proxpin goes LOW, in which case the program continues in the main loop.

jremington:
Usually, people reset startMillis periodically. Otherwise this is a one shot program. In your case, it might make sense to reset startMillis either when the button is released, or pressed, depending on what you want to accomplish. Study the "blink without delay" program example to see what I mean.

This is a good point. I based my code off the "blink without delay" program, but cannot understand how to modify it to include the prox on/off and timer requirements.

dougp:
The first thing to get down is sensing when [color=blue]proxPin[/color] changes . In the IDE refer to: file/examples/digital/state change detection.

Next, set the timer up as free-running, that is, it increments continuously. When timer is done, that is, reaches preset, use that condition to shut off the pump.

Finally, reset the timer - [color=blue]currentMillis = millis()[/color] - when the state of [color=blue]proxPinChanged[/color] goes high. [color=blue]proxPinChanged[/color] will be high for only one pass through the program each time the sensor is triggered.

So, as long as pulses are arriving at a certain rate the timer will never time out, keeping the pump on. If pulses cease the pump will shut off after X time.

These are good points. I had not heard of the state change detection before but it looks promising. I will play with it. Thanks for the response

I'm expecting to need a sub-loop within the main loop, which will run when the proxpin is HIGH.

No, you do not want this.

Simply record the time when proxpin goes HIGH, and in the main loop, time other actions on that basis.