Change value of millis

So I've searched on this forum for how to change the value of millis. The threads seam to be full of people saying this is a bad idea and why do you want to do this. To be fair people never have a good answer as to why they want to do it. I want to do it simply so I can test my code doesn't break when millis rolls over. So basically fast forward it to 2^32-10000 and watch it rollover.

Thanks

What board / processor?

MikeKulls:
So I've searched on this forum for how to change the value of millis. The threads seam to be full of people saying this is a bad idea and why do you want to do this. To be fair people never have a good answer as to why they want to do it. I want to do it simply so I can test my code doesn't break when millis rolls over. So basically fast forward it to 2^32-10 and watch it rollover.

Thanks

Or you can simulate millis() in code instead of trying to change the firmware.

unsigned long MillisSim(unsigned long startPeriod=0x07FFFFFF)
{
   static unsigned long start = startPeriod;
   start += millis();
   return start;
}

Change all instances of millis() to micros() and let 'er rip.

gfvalvo:
What board / processor?

Sorry, I was going to include that but forgot for some reason. It's a Wemos d1 mini pro running ESP8266

arduino_new:
Or you can simulate millis() in code instead of trying to change the firmware.

unsigned long MillisSim(unsigned long startPeriod=0x07FFFFFF)

{
  static unsigned long start = startPeriod;
  start += millis();
  return start;
}

That's not a bad idea, although has the issue that I need to change it everywhere of course. This is likely to be my solution if I can't directly change it

MikeKulls:
That’s not a bad idea, although has the issue that I need to change it everywhere of course. This is likely to be my solution if I can’t directly change it

Search and replace to the rescue.

arduino_new:
Search and replace to the rescue.

Well I tried and it definitely worked. I found my code didn't handle rollovers correctly. So it's done the job I wanted.

MikeKulls:
Well I tried and it definitely worked. I found my code didn’t handle rollovers correctly. So it’s done the job I wanted.

Normally, one should subtract and not add.

Do:

if (millis() - prevTime >= INTERVAL)

Don’t:

if (prevTime + INTERVAL <= millis())

You should have posted your code here. You would soon have found out whether there was a problem :slight_smile:

Testing it yourself with millis() is easy as long as the program is structured correctly. Read millis() just once at the start of loop() and put the value in a variable and use that throughout the program as a common point of reference rather than reading millis() again. To test the rollover, add the appropriate offset to the "millis()" variable

UKHeliBob:
You should have posted your code here. You would soon have found out whether there was a problem :slight_smile:

Testing it yourself with millis() is easy as long as the program is structured correctly. Read millis() just once at the start of loop() and put the value in a variable and use that throughout the program as a common point of reference rather than reading millis() again. To test the rollover, add the appropriate offset to the “millis()” variable

I avoided that for good reason :slight_smile:

There’s a few complexities in my code in that it gets the unix time from an NTP server once per hour and uses millis to calculate the unix time during the hour. It then captures data at rounded 10 second intervals. I’ve fixed the code for millis rollover now, not sure about NTP which should roll over in 2036. Maybe I’ll worry about that then :slight_smile:

I see nothing in your reply that precludes reading millis() just once per iteration of loop(), if that was what you were alluding to, unless your code prevents the rapid repetition of the loop() function which, in my opinion, should not be the case

MikeKulls:
That’s not a bad idea, although has the issue that I need to change it everywhere of course.

Why?

You only need to do it one in a short program to verify the behaviour of millis(). Once you are satisfied then just use millis() in the normal way.

If you always test millis() using subtraction there will be no problem when it rolls over - like this

if (millis() - startTime >= desiredInterval) {

…R

Robin2:
Why?

You only need to do it one in a short program to verify the behaviour of millis(). Once you are satisfied then just use millis() in the normal way.

If you always test millis() using subtraction there will be no problem when it rolls over - like this

if (millis() - startTime >= desiredInterval) {

...R

I guess but I really need to test the complete code. I've certainly learnt that bugs can popup when you really don't think it's possible. That being said changing millis in a few places isn't a huge deal, I was just pointing out it's slightly more work than directly manipulating millis.

MikeKulls:
I guess but I really need to test the complete code.

IMHO that is not the best approach - it is accepting that weird code would be OK if it passes the test rather than reviewing the code to check that everywhere millis() appears it is used correctly.

...R

MikeKulls:
So I've searched on this forum for how to change the value of millis. The threads seam to be full of people saying this is a bad idea and why do you want to do this. To be fair people never have a good answer as to why they want to do it. I want to do it simply so I can test my code doesn't break when millis rolls over. So basically fast forward it to 2^32-10000 and watch it rollover.

Thanks

Write a myMillis() function to do this, and change your code to use this - you can program any functionality you like then without modifying the system.

However if you are using other libraries whose source you don't want to monkey with then you'll have to go into the Arduino runtime code yourself and temporarily edit it. This is open source so you have all the sources for this exact sort of reason. Once you've seen how millis() work you can figure how to change it - its not a secret!

MikeKulls:
Sorry, I was going to include that but forgot for some reason. It's a Wemos d1 mini pro running ESP8266

That's unfortunate. What you want to do is pretty simple on an uno-type board. Just go into wiring.c and change the starting time for milllis:

volatile unsigned long timer0_millis = 0;

However, on an ESP8266, the key function is declared in user_interface.h:

uint32 system_get_time(void);

But, this is only the function prototype. The actually implementation appears to be part of the pre-compiled ESP core. So, looks like hacking millis on this platform will be more difficult.

gfvalvo:
But, this is only the function prototype. The actually implementation appears to be part of the pre-compiled ESP core. So, looks like hacking millis on this platform will be more difficult.

Thinking about it some more, it may not be that hard. Look for the millis() function in core_esp8266_wiring.c:

unsigned long ICACHE_RAM_ATTR millis()
{
  union {
     uint64_t  q;     // Accumulator, 64-bit, little endian
     uint32_t  a[2];  // ..........., 32-bit  segments
  } acc;
  acc.a[1] = 0;       // Zero high-acc
  
  // Get usec system time, usec overflow counter
  uint32_t  m = system_get_time();
  uint32_t  c = micros_overflow_count +
                   ((m < micros_at_last_overflow_tick) ? 1 : 0);

  // (a) Init. low-acc with high-word of 1st product. The right-shift
  //     falls on a byte boundary, hence is relatively quick.
  
  acc.q  = ( (uint64_t)( m * (uint64_t)MAGIC_1E3_wLO ) >> 32 );

  // (b) Offset sum, low-acc
  acc.q += ( m * (uint64_t)MAGIC_1E3_wHI );

  // (c) Offset sum, low-acc
  acc.q += ( c * (uint64_t)MAGIC_1E3_wLO );

  // (d) Truncated sum, high-acc
  acc.a[1] += (uint32_t)( c * (uint64_t)MAGIC_1E3_wHI );

  return ( acc.a[1] );  // Extract result, high-acc

} //millis

Change the return statement to:

return ( acc.a[1] + OFFSET);  // Extract result, high-acc

Where OFFSET is the 32-but value you want as the starting point for millis().

Note: I haven’t tried this yet, but it seems workable.

Robin2:
IMHO that is not the best approach - it is accepting that weird code would be OK if it passes the test rather than reviewing the code to check that everywhere millis() appears it is used correctly.

...R

So not testing the complete code is somehow the best approach?