I would like to write a program where in one hour check water sensor and if sensor output is less than I set, then turn on water pump for 3 second. I hope for your help!
The way you can diagnose problems with code is to insert Serial.print at various points and see if you get prints to the serial monitor when you expect them. If not then you know where to look for problems.
A millis-timer can be started behind a if-statement, but the millis-timer itself should run on its own in the loop(). You can not start and run a millis-timer a few levels deep in if-statements.
A Finite State Machine (FSM) or "state machine" solves that problem, since there are no levels under levels, the code is flat. Every state hat its own piece of code.
Hanging around in a certain state with millis() is shown in a very nice way by @UKHeliBob.
try
const unsigned long wateringTime = 60UL * 60* 1000; // note the UL ul also works
otherwise the right hand side is calculated in int and does not give the correct large number
Also see How to write Timers and Delays in Arduino
and Multi-tasking in Arduino for how to structure your code when using millis delays
Hello
Beside the usage of a state machine, the implementation of a timer function is recommended.
The timer function has two possible designs.
A free running timer to catch the hourly event.
A event controlled timer to controll the run time of the pump.
// https://forum.arduino.cc/t/function-with-millis/874612
// https://www.arduino.cc/reference/en/
//---------- Declarations
const int heartBeat = LED_BUILTIN;
const unsigned long oneHour = 3600000UL;
//const unsigned long oneHour = 3600UL; //test
unsigned long oneHourMillis;
const unsigned long threeSeconds = 3000UL;
unsigned long threeSecondsMillis;
const byte PumpPinOn = 2;
int waterLimitSensor = 33;
//---------- Functions
enum {Off, On};
void makePump(int state) {
Serial.print("the pump has been switched ");
Serial.println(state ? "on" : "off");
digitalWrite(PumpPinOn, state);
}
void setup() { // put your setup code here, to run once:
Serial.begin(9600);
pinMode (heartBeat, OUTPUT);
pinMode (PumpPinOn, OUTPUT);
oneHourMillis = oneHour;
while(!millis());
}
void loop() { // put your main code here, to run repeatedly:
unsigned long currentTime = millis();
digitalWrite(heartBeat, currentTime / 500 % 2);
// one hour timer
if (currentTime - oneHourMillis >= oneHour) {
oneHourMillis = currentTime;
Serial.println("\ncheck sensor");
if (waterLimitSensor < 35) {
makePump(On);
threeSecondsMillis = currentTime; // start 3 seconds timer
}
}
// 3 seconds timer
if (currentTime - threeSecondsMillis >= threeSeconds && threeSecondsMillis) {
threeSecondsMillis = 0;
makePump(Off);
}
}
Now you have to arrange th connection pins and the correct sensor readings.
What do you think?
I think: 'There we go again, it is a a "O, jolly me" aproach'. See also here.
You use a unsigned long to store the millis() value and you use the same variable as a boolean to turn on and off the timer. After 50 days, millis() could be zero. This is not about the chance that it might go wrong, this about providing good and clean code on a forum to show new users how to do it right.
Please don't put something bad in the code by trying to be smart
I'm not even talking about the while(!millis());. Oops, I just did.
@paulpaulson your use of multiple if statements in your code, together with the fact that each { and } is not on its own line to make the dependant code blocks obvious serves to reinforce the reason why I prefer to use state machines implemented using switch/case as illustrated earlier in this topic
There is, of course, nothing intrinsically wrong with if/else, just that code using it can become difficult to follow and maintain
Hello,
many thanks for your well remarks.
This was one solution of n-solutions and n runs to infinite.
Another solution is to bring the timer function within a struct for timer values and tasks, maybe with a switch/case instruction.
And what did Serial.print tell you? Was "Water limit:" ever less than 35? Even if it is, you are going to turn the pump off a few microseconds later because you don't set 'prevMillisFunction' before testing if the interval is over.