Millis() / Unsigned long subtraction not working

Hey, I recently tried this test program to have a function set off after a given time in another program:

unsigned long TimeSinceStart;
const unsigned long ResetTimePumpe = 82800000; //23 Stunden Betriebszeit (in Millisekunden)
const unsigned long BreakTimePumpe = 1800000; //30min Pausenzeit (in Millisekunden)
unsigned long Differenz;

void setup() {
  // put your setup code here, to run once:
Serial.begin(115200);   
}

void loop() {
TimeSinceStart = millis();   

  // put your main code here, to run repeatedly:
Serial.print(TimeSinceStart);
Serial.print("   ");
Differenz = TimeSinceStart - ResetTimePumpe;
Serial.println(Differenz);
}

When watching the "Differenz" variable in the monitor the value didnt go from -82800000 to 0, but started at a very high number and continued to grow. I thought unsigned longs where 2^63 - 1 in both positive and negative directions. So why would it give me a positive value on "Differenz"?

Thanks for all help

Welcome to the forum

unsigned long Differenz;

Differenz is an unsigned variable so how could it ever go negative ?

2 Likes

AFAIK, there are no Arduino boards where a "long" is 64bits; they're only 32bits.

And "unsigned" means never negative, so you get from 0 to 2^32-1

It's unclear whether Serial.print() handles "unsigned" differently, though.

since these are unsigned, they won't be negative

Differenz starts at a large value because ResetTimePumpe is a large value

an unsigned long has a value from 0 to 2^64 -1

1 Like

First - the range of long is 2^32 rather than 2^63.
And second - I'm wondered, why did you thought that unsigned varibale can be negative? :)))

Thanks for the super quick response

So this version should work?

unsigned long TimeSinceStart;
const unsigned long ResetTimePumpe = 82800000; //23 Stunden Betriebszeit (in Millisekunden)
const unsigned long BreakTimePumpe = 1800000; //30min Pausenzeit (in Millisekunden)
long Differenz;

void setup() {
  // put your setup code here, to run once:
Serial.begin(115200);   
}

void loop() {
TimeSinceStart = millis();   

  // put your main code here, to run repeatedly:
Serial.print(TimeSinceStart);
Serial.print("   ");
Differenz = TimeSinceStart - ResetTimePumpe;
Serial.println(Differenz);

if(TimeSinceStart - ResetTimePumpe >= 0){

  //execute Funktion every 23h

  TimeSinceStart = TimeSinceStart - ResetTimePumpe
}

This condition will always true because the both variables are unsigned

1 Like

please be more precise on what's mean "work" for you in the case

oups, wanted to make ResetTimePumpe const long

executing funktion in "IF" every 23h

what are you trying to do?

Is not good decision.
A good idea is never to mix signed and unsigned variables in the same expression.

1 Like
static unsigned long time_of_last_run = 0;

if (millis() - time_of_last_run > 23ul*3600*1000)

// do the thing
....
time_of_last_run = millis();

}

So now it just executes IF as soon as the time passes 23h and then it resets the TimeSinceStart? Thanks for the help, didnt work much with millis previously

unsigned long TimeSinceStart;
const unsigned long ResetTimePumpe = 82800000; //23 Stunden Betriebszeit (in Millisekunden)
const unsigned long BreakTimePumpe = 1800000; //30min Pausenzeit (in Millisekunden)
long Differenz;

void setup() {
  // put your setup code here, to run once:
Serial.begin(115200);   
}

void loop() {
TimeSinceStart = millis();   

  // put your main code here, to run repeatedly:
Serial.print(TimeSinceStart);
Serial.print("   ");
Differenz = TimeSinceStart - ResetTimePumpe;
Serial.println(Differenz);

if(TimeSinceStart > ResetTimePumpe){

  //execute Funktion every 23h

  TimeSinceStart = TimeSinceStart - ResetTimePumpe;
}
}

Do you mean your code in post #14?
It looks like a complete nonsense for me.

See my example in #13

Pump-On  0
Pump-Off 2000
Pump-On  6000
Pump-Off 8000
Pump-On  12000
Pump-Off 14000
Pump-On  18000
const unsigned long MsecPumpOn  = 2000;
const unsigned long MsecPumpOff = 4000;
      unsigned long msecPeriod;
      unsigned long msec0;

bool pumpOn;

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

    if (msec - msec0 >= msecPeriod)  {
        msec0 += msecPeriod;

        if (pumpOn) 
            msecPeriod = MsecPumpOff;
        else
            msecPeriod = MsecPumpOn;

        pumpOn = ! pumpOn;
        Serial.print   (pumpOn ? "Pump-On  " : "Pump-Off ");
        Serial.println (msec);
    }
}


void setup ()
{
    Serial.begin (115200);
}
1 Like

ty will use smth like that, uses way less unnecessary space, what does the "ul" in "23ul36001000" stand for?
And this version should work even after millis() sets back to 0

an AI answer:

In Arduino (and C/C++), the suffix "ul" stands for unsigned long. It specifies the constant as an unsigned 32-bit integer (a non-negative integer with a larger range than a standard int).

Explanation:

  • "ul" is a suffix you add to a numeric literal to tell the compiler to treat it as an unsigned long.
  • "23ul" ensures that the number 23 is explicitly an unsigned long value.
  • This is important for calculations involving large numbers or to prevent overflow or unexpected behavior when doing arithmetic.

Example:

unsigned long delayTime = 23ul * 3600 * 1000;

Without the "ul" suffix, the literals might be treated as int, which could lead to issues if the resulting product exceeds the maximum value of a signed int (usually 32767 or 65535 depending on system).

Summary:
"ul" stands for unsigned long literal suffix, used to specify the constant as an unsigned 32-bit integer.

1 Like

If you want to do a comparison between two unsigned values, do this:

unsigned long a;
unsigned long b;
// Give them values, then...
if ((long) (b - a) < 0) {
  // b is < a
} else {
  // b is >= a
}

This works even if one or both a and b might wrap around, such as they’re taken from millis() (wraps around after a bit over 7 weeks of continuous running) or micros() (wraps around in about 70 minutes).

Not that I’ve ever noticed, and I’ve been fiddling with some millis()/micros() based timing code for the last few days, thought the Nano R4 was generating bad code. No, subtle error in my own code. :sweat_smile: