Double Loop

Hi!
I'm using my Arduino to send the temperature of my aquarium to a php site and in case of a water leakage it also sends an alarm to the php site and an email.

The problem is that i don't want the arduino to send every minute the temperature, so i added an delay and it only sends the temperature every half hour. But i wanted the alarm to send immediately in case of water detection.

How can I do this since I only have on loop? is it possible?

Classic case of the need for Blink Without Delay

fabmsg:
Hi!
How can I do this since I only have on loop? is it possible?

something like this example:

unsigned long oneMinute = 60000UL;
unsigned long startTime;
int myWaterSensor;

void setup() 
{
  Serial.begin(115200);
}
void loop() 
{
  if (millis() - startTime >= oneMinute)
  {
    // Put your temperature reading/transmiting code here or create a function:
    float myTemp = readTemp(); // for example
    Serial.print("temperature = ");
    Serial.println(myTemp);
    startTime += oneMinute;
  }
  if (myWaterSensor == HIGH) //or however your sensor reports alarm state
  {
    // Sound alarm here
  }
}

float readTemp()
{
  return 77.7;// for example purposes
}
    startTime += oneMinute;

Please do not suggest that people ADD times. Only subtraction involving unsigned long values is guaranteed to work. Besides, if you add a value to start time, aren't you defining an end time? In which case storing end time in a variable called startTime looks pretty stupid.

PaulS:

    startTime += oneMinute;

Only subtraction involving unsigned long values is guaranteed to work..

Why's that Paul? Some subtlety under the hood presumably....

Why's that Paul? Some subtlety under the hood presumably....

Suppose that, instead of rolling over after about 49 days, millis() returned time in seconds, and in a type that rolled over after an hour (so the values are more manageable).

So, millis() will return values from 0 to 60. Lets suppose, then, that now = millis(); results in 58 being stored in now. Further, supposed that we add 10 seconds to now, as in nextTime = now + 10;. What value will be in nextTime, considering that it can hold a value between 0 and 60, and 58 + 10 is not in that range? Well, the answer is 8. So, if you try to compare the value returned by millis() (which lets say just returned 59) to the value in nextTime, you'll see that now (59) is greater than nextTime ( 8 ), so whatever action should be taken next time will be taken.

On the other hand, if you store the value of now in then, and ask if millis() - then >= 10, then when millis returns 59, the answer will be false, because 59 - 58 is 1. Three seconds later, millis() will return 2, and 2 - 58 is 4, which is less than 10, so whatever action should be taken 10 seconds after last time will not be taken.

Subtraction always returns the correct value, as long as now minus then is not more than the rollover amount (49+ days). If you are talking intervals of that length, an RTC is the only way to go.

@PaulS, I think you have got the wrong end of the stick here.

The code startTime += oneMinute; is used in the same way that I would use previousMillis += oneMinutesWorthOfMillis and is perfectly proper.

There may be scope for arguing about the name of the variable but even there you are on weak ground when you see that it is used in if (millis() - startTime >= oneMinute) { - which is where the subtraction is essential.

A trip to the optician's perhaps ?

...R

The code startTime += oneMinute; is used in the same way that I would use previousMillis += oneMinutesWorthOfMillis and is perfectly proper.

It's syntactically proper, not logically proper. Adding times could cause overflow.

There may be scope for arguing about the name of the variable but even there you are on weak ground when you see that it is used in if (millis() - startTime >= oneMinute) { - which is where the subtraction is essential.

Adding to start time, and then pretending that you didn't makes for very confusing code. That code will actually trigger the next event after two minutes, not one.

A trip to the optician's perhaps ?

I don't think so.

I think you need to look closer, PaulS

you see:

startTime += oneMinute;

is going to be less than or equal to simply returning millis()...

the math doesn't go into the future... it is catching up with the present, but allowing you to take out the error of a few millis() that may have happened since here:

if (millis() - startTime >= oneMinute)

is going to be less than or equal to simply returning millis()...

I guess we'll have to agree to disagree. First, there is nothing in the code to ever sync things up again if startTime does roll over.

I don't know whether the issue is a just a poor choice for a variable name, or something fundamental I'm missing, but the code can be written to not use addition.

the math doesn't go into the future... it is catching up with the present, but allowing you to take out the error of a few millis() that may have happened since here:

I do not see that. In fact, it look like startTime should be called lastTime, and should simply be assigned the value of millis(). Then, sending the temperature can happen when now (as returned by millis()) minus then (lastTime) is greater than the desired interval.

Paul,

How do you think millis() gets updated? Do you think the interrupt handler does a subtraction?

Here's the analogy for you to consider Paul.

Let's say I have a delivery driver that comes by every hour on the hour and I want to check for a package when he comes. So I start at 1:00 and mark the time. Now when the present time - the start time is greater than 1 hour I need to go check for the package. Right, that's what we're all familiar with. But then I have two options. I can either set the last time I checked for a package to now or I can set it to the time when I really should have checked.

Consider this. The delivery guy comes at 2:00 but I don't check for the package until 2:05. If I set my lastTime to 2:05, I won't see that it is time to check again until 3:05. Let's say I don't actually get out there to check until 3:10, now the next time it will not be until 4:10. If this continues, I'll eventually be missing the delivery guy by 30 or 40 minutes and it might not be acceptable for my package to sit out there that long.

So instead I have another option. I can check for the package at 2:05, but instead of saying I need to check again an hour from now, I'll set my lastTime to the last time that the delivery guy came, that is I'll set lastTime to 2:00. That takes up for my being late.

Now Paul, I want you to get out a pen and paper and add some times together and see that when you are doing the comparison you're right you have to use subtraction. But when you are updating lastTime it is OK to add to it. If it rolls over then so did millis(). All you're doing is setting lastTime (or the misnamed startTime in the OP case) to something that millis() was a few millis ago. So you're not concerned that it rolls over because if it did then that means millis() rolled over too.

But when you are updating lastTime it is OK to add to it. If it rolls over then so did millis().

No. That is where you are wrong.

Considering your package check analogy. Suppose you last checked at 2:55. You add 10 minutes to that, to check again in the future. So, now you wait for your watch to show 2:65. Going to be a long wait.

The problem is that adding to the current time can cause a rollover. So, subtracting then (2:55) from now (3:05), you can see that 10 minutes has elapsed, and go check. Waiting for 2:65 will cause a long wait.

Sorry Paul, when I went with hours and minutes I assumed we were still using the imaginary data type from reply 5.

In that case, if I check at 2:55 and add 10 minutes, the unsigned math rolls over and my new value is 3:05.

If we held time in a byte, cause I like 255.

lastTime = 220 and Interval = 20.

One interation, millis is now let's say 242. The difference is greater than 20 so I check the package. And I add 20 to lastTime so it is now 240. I think we both agree things work in this case. Now let's roll over.

Next iteration. Millis has rolled over, now millis is 7. 7 - 242 is greater than 20 so we do our thing and check the package. Now let's add 20 to lastTime. And we get... 5. It rolls over too.

Now last time is 5. As soon as millis - lastTime > 20 will happen when millis is 25. And now we're away from the rollover and everything works.

I think you are confusing the need to do subtraction always when comparing times with a need to never add time. If we never added time, how would I know what time it will be 10 minutes from now. I have to add time, just not to determine a length of time. The difference is absolute time vs. calculating a length of time.

PaulS:
Considering your package check analogy. Suppose you last checked at 2:55. You add 10 minutes to that, to check again in the future. So, now you wait for your watch to show 2:65. Going to be a long wait.

that is where you are not understanding Paul… you are not setting startTime to be greater than millis(), startTime is not a number incremented beyond the current return of millis()

you are setting startTime essentially to the present time, excluding any additional mS that have transpired due to processing, startTime in that case would be less than millis().

startTime resets when it has become oneMinute old… then it is reset to oneMinute later than the last time it was reset… or about equal to millis()

If we never added time, how would I know what time it will be 10 minutes from now.

Why do you need to know that? We are not trying to schedule something to happen in the future.

OP wants to send temperature every half hour, and an alarm immediately. So, on any given pass though loop, check to see if an alarm happened, and deal with it. Check to see if it's been half an hour. If so, send the temperature. There is no need to know anything about any future time.

PaulS:

If we never added time, how would I know what time it will be 10 minutes from now.

Why do you need to know that? We are not trying to schedule something to happen in the future.

OP wants to send temperature every half hour, and an alarm immediately. So, on any given pass though loop, check to see if an alarm happened, and deal with it. Check to see if it's been half an hour. If so, send the temperature. There is no need to know anything about any future time.

In this case no. I was merely showing that sometimes time does get added.

Maybe instead of focusing on the semantics of that one line, maybe you should have a look at the rest of that post. Specifically where it walks through the condition of rollover so you can convince yourself that it works out.

If you can illustrate the condition where you think it will fail then please do. We may all be missing something, but I really don’t think so.

@PaulS, this (copied from Reply #2) is the standard Blink Without Delay technique for managing time.

  if (millis() - startTime >= oneMinute)
  {
    // Put your temperature reading/transmiting code here or create a function:
    float myTemp = readTemp(); // for example
    Serial.print("temperature = ");
    Serial.println(myTemp);
    startTime += oneMinute;
  }

If you think that example is not the best way to do things, perhaps you can show us how you would do it.

...R

@PaulS, this (copied from Reply #2) is the standard Blink Without Delay technique for managing time.

It is not. The relevant three lines from the BlinkWithoutDelay.ino file are:

  unsigned long currentMillis = millis();
 
  if(currentMillis - previousMillis > interval) {
    // save the last time you blinked the LED 
    previousMillis = currentMillis;

Nowhere is a variable that holds a time added to.