Confusion on calculations

I am having trouble understanding why my calculations are not working as I would expect. I am a newbie to C++.

If I run the code as written below using the calculation
tempInterval = 1000 * 60; , I get

4294961760
4294961760

If I comment out the line and just use the declaration:

unsigned long tempInterval = 60000000;

I get:
60000000
60000000

There is something basic I am missing here and any help will be appreciated.

unsigned long tempInterval = 60000000;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(9600);
 
tempInterval = 1000 * 60;
   Serial.println(tempInterval);

}

void loop() {
  // put your main code here, to run repeatedly:

}

Try this:

tempInterval = 1000.0 * 60;

No. do what gfvalvo says later in this thread:

tempInterval = 1000UL * 60;
tempInterval = 1000UL * 60UL;

Edit:
On most Arduinos int type is 16-bits. If you don't specify a type for integers, C/C++ selects int type for calcualtions if the values will fit in an int.

1000 = 0x03E8
60 = 0x003C
0x03E8 * 0x0060 = 0xEA60
0xEA60 is negative when treated as a signed 16-bit value
The sign bit is extended into the upper 32 bits giving 0xFFFFEA60
0xFFFFEA60 converted to unsigned 32-bit = 4294961760

Don't resort to floating point as SteveMann suggests. Just add the UL (Unsigned Long) type specifier, then the compiler will use 32-but unsigned values and arithmetic.

SteveMann:
Try

tempInterval = 1000.0 * 60;

Nope.

tempInterval = 1000UL * 60;

Explanation: With 8-bit AVR processors, the compiler will perform signed, 16-bit arithmetic by default. 60000 can't fit in a signed, 16 bit result. Adding 'UL' to one of the constants will force it to do 32-bit arithmetic.

Many thanks for the quick responses!

Now I tried:
unsigned long tempInterval = (1000UL * 60 * 60 * 24 * 90);

and got

3481032704
but the number should be 7776 *10^6

So I still taxing the processor and/or doing something wrong.

I want to set a time in milliseconds for an interval to increment a counter. The math above should be 90 days, according to my logic. I also want to divide that by 30, but one step at a time.

Still stuck, but very appreciative.

The largest number that can be held in an unsigned, 32-bit variable is 2^32-1.

normd:
the number should be 7776 *10^6

You have a four byte unsigned integer. Largest number is 2^32-1 so your number is too big to fit, hence the funky result. If you were to divide by 30, it will fit, but only if you don't do that division last.

Edit: Apparently there's an echo in here :wink:

Time periods of 1 day. Count 90 of them.

Chances of being anywhere near accurate using the CPU clock over those timescales - nil.

The Arduino millis() timer overflows after about 57 days, for the reasons given above.

Use a battery-backed external Real Time Clock (RTC) module to keep track of time, even if the power cycles. The DS3231 is inexpensive and works very well.

I think the overflow is 49.7 days for millis().
(((( (2^32-1 ms)/1000(ms/s) ) /60(sec/min) )/60 (min/hr) )/24 (hr/day) = 49.71 days
(2^32-1)/1000/60/60/24

Can do similar for micros()
(2^32-1)/1000000/60 = 71.58 minutes

But usually the answer is so what?
If (laterTime - earlier) is used for time measurements, the calculations still work out around the rollover due to the way unsigned math works out.
Ex 0x0000000F (post rollover) - 0xFFFFFFF0 (pre rollover) = 0x0000001F as only 32 bits are retained in unsigned long calculations.

It works great in this simple time counter, try it and see.
You can them follow the same logic to add tracking of # of days passed.

Battery back RTC will be more reliable, keeping the time thru any power losses.

unsigned long currentMicros;
unsigned long previousMicros;
unsigned long elapsedTime;

// Initial time to start, adjust as needed.

byte hundredths;
byte tenths;
byte oldTenths;
byte secondsOnes = 0;
byte oldSecondsOnes;
byte secondsTens = 0;
byte minutesOnes = 2;
byte minutesTens = 3;
byte hoursOnes = 4;
byte hoursTens = 0;

void setup() {
  Serial.begin(115200); // make serial monitor match
  Serial.println ("Setup Done");
}

void loop() {
  currentMicros = micros();

  // how long's it been?
  elapsedTime = currentMicros - previousMicros;
  if ( elapsedTime >= 10000UL) { // 0.01 second passed? Update the timers
    previousMicros  = previousMicros + 10000UL;

    hundredths = hundredths + 1; // increment
    if (hundredths >= 10) {
      hundredths = 0; // else rollover and increment next digit
      tenths = tenths + 1;
      if (tenths >= 10) {
        tenths = 0;
        secondsOnes = secondsOnes + 1;
        if (secondsOnes >= 10) {
          secondsOnes = 0;
          secondsTens = secondsTens + 1;
          if (secondsTens >= 6) {
            secondsTens = 0;
            minutesOnes = minutesOnes + 1;
            if (minutesOnes >= 10) {
              minutesOnes = 0;
              minutesTens = minutesTens + 1;
              if (minutesTens >= 6 ) {
                minutesTens = 0;
                hoursOnes = hoursOnes + 1;
                if ((hoursTens == 2) && (hoursOnes == 4)) {
                  hoursOnes = 0;
                  hoursTens = 0;
                }// hours total rollover check
                if (hoursOnes >= 10) {
                  hoursOnes = 0;
                  hoursTens = hoursTens  + 1;
                } // hoursOnes rollover check
              } // minutesTens rollover check
            } // minutesOnes rollover check
          } // secondsTens rollover check
        } // secondsOnes rollover check
      } // tenths rollover check
    } // hundredths rollover check
  }// hundredths passing check

  if (oldTenths != tenths) { // show the elapsed time
    oldTenths = tenths;
    Serial.print(hoursTens);
    Serial.print(hoursOnes);
    Serial.print(":");
    Serial.print(minutesTens);
    Serial.print(minutesOnes);
    Serial.print(":");
    Serial.print(secondsTens);
    Serial.print(secondsOnes);
    Serial.print(".");
    Serial.println (tenths);

  } // end one second check
} // end loop

Example output. No slow % math needed. micros() tracks time much better than millis().
If you Arduino is in a stable environment, then time will not drift much. Varies crystal by crystal.
You can experiment. I find opening that serial monitor 7 or 8 seconds ahead of the time you enter will get it pretty close to 'real time' if you want to monitor it against time.gov or similar.

But usually the answer is so what?

The OP should understand that without extra programming effort, you can't directly use millis() to time a 90 day period.

I think the point has been made that the OP can time a day, and then count the days. Or use a RTC.

Some RTCs support setting and an alarm within the RTC.
Otherwise, one can query the date & time from the RTC, and when the desired date & time is met, or exceeded in the case where one was trying to match a specific time, then take whatever action is desired.

if (date returned == 29 Aug 2020){
   if (time returned >= 12:00:05){ // say the sketch was busy and got back 12:00:06  or 12:00:07, you'd still want to act on it
   // alarm time!
   }
}

jremington:
The OP should understand that without extra programming effort, you can't directly use millis() to time a 90 day period.

Or without much effort, using the Time library because it supports timing from the processor clock, without any programming fuss. As a bonus, the whole snafu of integer overflow is easily avoided by using the user friendly conversion constants that are provided, since they are declared as UL.

I only changed this to represent seconds, not milliseconds:

unsigned long tempInterval = (SECS_PER_DAY * 90);

Because SECS_PER_DAY is already declared as UL, the qualifier is not needed here.

I synced up my bog standard Uno with my timer, will let it run and see how the resonator tracks.

Wow - 38 seconds in just a few hours.

CrossRoads:
Wow - 38 seconds in just a few hours.

Try telling it that there are 10014 microseconds in 0.01 second.

Time test wit 1284P, 16 MHz crystal in generic thru HC49 can, 22 pf cap.


~5 seconds behind after 15 hours. I thought it would be better.