Millis() and rollover tutorial

Can you explain why the difference between two unsigned short is not greater than or equal to zero? In the code which is posted above, why is the condition (b - a > 0) false?

What do you mean?

in your example the result of (b-a) and (d-c) are both greater than zero.

google "C or C++ usual arithmetic conversions"

No. If you execute this code, the output is Choice 2 only. Please take a moment to read my earlier comment.

Thanks
Dheeraj

Thanks for posting this. I was thinking of doing something similar, because many times i see people using millis(), and often wonder thinking based on their code snippet they are not considering the effects of the rollover.

which compiler are you using?

Tried it on

  1. gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.4)
  2. Apple LLVM version 9.0.0 (clang-900.0.38)
    Target: x86_64-apple-darwin16.7.0
  3. ARM GCC 541
  4. Particle cloud compiler

All results were same.

This is C integer promotion from short to int. The assumption built-in to the language is that doing arithmetic on int and uint is faster than doing it on the smaller types.

This stack overflow has some good info and pointers:

The code as written for millis() using unsigned int’s is correct, but might not be correct if millis() returned a type smaller than int. Since it returns unsigned int, all is well.

5 Likes

why 1000? so for all the negative numbers will be 1000? what would happen in the second, third…loop (all will be negative, so all will be 1000)? so it would not reach the interval time?

I want to run my code for 12 months, will it work?

#define publish_cycle 30000 
unsigned long lastPublish = 0;

int led = D7;
double reading = 0.0;
double mvolts = 0.0;
double tempC = 0.0;
double tempF = 0.0;
boolean flag = false;

double readingl=0.0;

void setup() {
    pinMode(led,OUTPUT);
    Particle.keepAlive(120);
    Particle.function("led", ledStatus);

}

void loop() {
    unsigned long now = millis();
    reading = analogRead(A0);
    mvolts = (reading * 3300.0)/4095.0;
    mvolts = mvolts -500.0;
    tempC = mvolts/10.0;
    tempF = (tempC*1.8) + 32.0;
    readingl = analogRead(A1);
    
   //to IFTTT
    if ( tempC > 26.0 && flag == false){
    Particle.publish("toohot");
    flag = true;
    }
    if (tempC < 24.0 && flag == true){
    Particle.publish("tempnormal");
    flag = false;
    }

    
    if ((unsigned long)(now - lastPublish) > publish_cycle) {
    //to LOSANT
    Particle.publish("temp-reading", String(tempC), PRIVATE) ;
    Particle.publish("light-intensity",String(readingl), PRIVATE) ;
    delay(500);
    //to ThingSpeak
    Particle.publish("temperature", "{ \"1\": \"" + String(tempC) + "\"," + "\"2\": \"" + String(readingl) + "\"}", PRIVATE);
    lastPublish=now;
    }
    

}





int ledStatus(String command){
    if(command.equalsIgnoreCase("on")){
        digitalWrite(led,HIGH);
        return 1;
    }
    else if (command.equalsIgnoreCase("off")){
        digitalWrite(led,LOW);
        return 0;
    }
    else{
        return -1;
    }
    
}

This question could also have been asked in your own thread instead of just dropping your code there without further comment - particularly not answering any of the questions asked.
That's not really appreciated by people trying to help in their little spare time

BTW, as the name suggests unsigned variable types (as uint32_t or unsigned long returned by millis()) cannot become negative and 1000 is just an exemplary number.

3 Likes

@rickkas7: Is the rollover of millis() an issue while using elapsedMillis.h (and f.e. elapsedMillisDemo.cpp) from the library? if so, please add a note to the .cpp or change the code of .h please, a great help for newbies, else they are wondering why things stop running after a certain time, and turn away from particle. for purposes like home automation it’s a must-have to run w/o reset for more than 49 days…

@mf2105, elapsedMillis() uses millis() and will used unsigned long values internally so there should be no rollover problems. Are you having a specific issue?

1 Like

@peekay123 no, not a specific issue, just about to adapt code from delay() to elapsedMillis() to get rid of delays using mobicle.io (next step maybe Blynk) for my home automation. Tried to read elapsedMillis.h, but was confusing for a beginner like me :thinking:. Thanks for the quick reply.

1 Like

You shouldn't have any problems using the sample from the first post to replace delay.
The concept works perfectly and it's a great tutorial for beginners.

I'm currently writing an application for gardening which will often have long times between function calls, hours or perhaps days depending on the function. I'm trying to decide whether I should be using Software Timers, millis(), or Time.now() in some way to do that.

Reading up on millis(), I am having trouble understanding these two sections which seem to be contrary.

I assume every negative value has a DIFFERENT unsigned equivalent, but not sure what those values would be to understand how comparing them to a value of say 86,400,000 (24hrs in ms) would behave.

Based on the first quoted section, since lastTime is an unsigned long, shouldn't the result of this statement be -1000 which is then converted to some positive number because it's unsigned?

Thanks in advance.

For hours or days, the real-time clock Time.now() is probably the best option.

The examples in the original post are specifically for dealing with how millis wraps after 49 days.

Say the time in lastTime was +4294966796, right before rolling over.

The next time you check, millis() (or the now variable in the original post) is now 500 because it rolled over. The formula now - lastTime would be 500 - 4294966796.

In signed math, that’s a negative number, -4294966296. However, that number is stored in memory as 1000. It really is the same bits in a 4-byte value.

The reason this isn’t ambiguous is that -4294966296 can’t really be represented in a 32-bit signed value because the minimum value is -2147483648.

But when you compare against 1000, it works properly.

3 Likes

Yeah, I assumed I was going to have to use time.now() and store the last run times for each function in EEProm to compare to because a reboot would reset both the millis() and Software Timers.

Just to complete wrapping my brain around the millis() question though I had one more question.

If -4294966296 is represented the same as 1000 when stored as an unsigned long, what would it be if the number was -3294966296? It’s still below the minimum value of -2147483648, but is the result different in a predictable way?

Thanks again.

Now that I think about it, a better way of explaining it is that -4294966296 or -3294966296 requires a 33-bit two’s complement integer. But since integer is only 32 bits, the sign bit is lost, making a positive 32-bit integer.

-3294966296 = 0b100111011100110101100110111101000 (33 bits)

Removing the MSB:
0b00111011100110101100110111101000 = 1000001000 (decimal)

3 Likes

I had to work this out for myself, a while back, for a machine that I never wanted to reset (for purist reasons, really!). Here is a trivial function that other may like to use, which is roll-over safe - like @rickkas7 code:

// return true if reference time is more than threshold mS behind current time.
// unsigned modulo arithmetic handles any wrap-around in this equation.
bool millisExpired(unsigned long mSref, const unsigned long mSthreshold) {
    return (millis() - mSref > mSthreshold);
}

Example:

// Declarations
const unsigned short EVENT_TIME_OUT=1000; //mS
unsigned long eventTimer; 

// Earlier in code, when needing to time event
eventTimer=millis();

... do stuff...
if (millisExpired(eventTimer, EVENT_TIME_OUT)) {
  ... do time-out stuff...
}
2 Likes

[quote=“rickkas7, post:1, topic:20429”]
And this similar recipe, that doesn’t quite work either. It fails in the same way.

unsigned long lastRun = 0;

void setup() {
	Serial.begin(9600);
}

void loop() {
	// Don't do this!
	if (millis() >= (lastRun + 1000)) {
		lastRun = millis();
		Serial.printlnf("%lu", millis());
	}
}

rickkas7, Thank you so much sir for your great tutorials
Regards Kazem

I need to ask: is that code above the same as this code below, or is there some vudu magic that would make it not work properly?

      if ((millis() - lastTime) >= 1000) {

The question is whether declaring the unsigned long now variable changes something or not.
Thanks!