Resetting millis() using software

Hi I am working on a project to illuminate my bicycle during the night. Its something similar to Revolights www.revolights.com

I am planning to implement in the following way, your suggestions are welcome:

The bicycle wheel will have 12 * 2 LEDs per wheel, and at any time 8 Leds (4 Leds on each side of the wheel will be illuminated). The On time of Each LED is dependent on the RPM, which is calculated for every 2 complete wheel rotation. The wheel rotation completion is detected when an external hardware interrupt is triggered by the closure of magnetic reed switch. I am using millis, to calculate the relative position of each LED and use that to specify which LED should be on and which should be off.

The problem is, after some time, the calculations involving millis are vecoming slower as millis() output is growing larger. So how can i reset the millis() once the interrupt is fired on completion of one rotaion?

Thanks.

Can you show your code? The size of the millis value shouldn't normally matter, unless perhaps you're doing multiplications. Normally, you're only interested in differences.

Hi AWOL,

Ill shortly post the code. I am only using the millis() to perform subtraction but I do it in a while condition like:

while( (millis() - lastRevolutionCompleteTime) <= LEDRefreshTime)
{
// Switch OFF LED 4
// Switch ON LED 9
}

and there are 12 such while loops in total and all of them should complete in one Wheel Rotation. I have tried using delay() function to hold and change the LED state but most of the time, i found that the code is not triggering the input correctly all the time. So changed it back to milis().

You can't reset millis() except by resetting the hardware. It is a free running number from 0 to 2^31-1 that takes 49+ days to rollover from FFFF FFFF back to 0.

If your calculations are all done as (later time) - (earlier time) then you do not need to worry about rollover either. Try it: 0000 0040 (millis after rollover) - FFFF FFE0 (millis before rollover) = 0000 0060

How is it possible for your calculations to become slower? The calculations are done on 32-bit numbers all the time.

Please post your code - maybe something more basic needs tweaking.

I could do some calculations and determine what the RPM rate would be for a 26" wheel bicycle tire at 30 miles/hour, but I believe it would still end up being ridiculously slow compared to a 16 MHz processor. circumference in feet = 2*pi*R /12 = 6.8 feet/rotation 60 miles/hr = 88 feet/second, 30 miles/hr = 44 feet/second (44 feet/second) / (6.8 feet/rotation) = 44 feet/second * 1 rotation/6.8 feet = 6.47 rotations/second @ 30 miles/hour. 1/6.47 = 0.155, or 155mS/rotation, plenty of time to do all kinds of math.

You can’t reset millis() except by resetting the hardware

You could; simply reset “timer0_millis”, no hardware involved, but it’d be a pretty dumb thing to do.

That's the first time I'd ever seen that AWOL.

That's the first time I'd ever seen that AWOL.

Me too :stuck_out_tongue_closed_eyes: It has never occurred to me to even look before!

Some of the 'sleep' libraries do that, so obviously it's possible. I assume this thing won't be running long enough for timer overflow to be relevant and I find it unlikely that arithmetic execution time is sufficient to affect the behaviour noticeably. The more likely explanation IMO is that there is a bug which manifests when the result of millis() gets large enough.

Perhaps if you posted the whole code somebody would spot it.

Hi everyone, thanks for your replies. Please find the code i have developed so far, its still in progress:

// For front wheel, the last LED (on pin 15) should be touching ground when reed switch closes and the LEDs are arranged (from pins 4-15) in counter clock wise direction.
// For back wheel, arrange accordingly

volatile byte revolutions;
volatile float milliperrevolution = 0;
volatile float ledRefreshTime = 0;
volatile float relativetime = 0;
volatile float lastRevolutionCompleteTime = 0;


volatile float refreshStart = 0;
volatile float refreshEnd = 0;

volatile boolean interruptHit = false;
unsigned long timeold;

void setup()
{
  Serial.begin(9600);           // set up Serial library at 9600 bps
  
  pinMode(4, OUTPUT);
  pinMode(5, OUTPUT);
  pinMode(6, OUTPUT);
  pinMode(7, OUTPUT);
  pinMode(8, OUTPUT);
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);
  pinMode(14, OUTPUT);
  pinMode(15, OUTPUT);
  attachInterrupt(1, interrupt, RISING);
  revolutions = 0;
}

void loop()
{
  relativetime = millis()-lastRevolutionCompleteTime;
  calRPM();
  if(relativetime<=3000)               // If the rotation time of a bicycle wheel is more than 3 seconds, switch OFF the LEDS
    {       
       if(milliperrevolution!=0)
       {
         refreshLEDState();
         if(interruptHit)  
         {
           interruptHit = false;       //If interrupt is hit early than expected due to increase in speed, quit the rest of refreshLEDState and start again
         }
         else
         {
           interruptHit = true;        //If interrupt is hit later than expected due to decrease in speed, wait till the interrupt occurs and start again
         }
       }
    }
    else
    {
      standbymode();
    }
}
void standbymode()                   
{
  for(int i=4;i<=15;i++)
  {
    digitalWrite(i,LOW);
  }
}

void interrupt()
{
 revolutions++;
 interruptHit = true;
}

void refreshLEDState()
{
  refreshStart = millis();
  while((millis() - refreshStart)<=ledRefreshTime && !interruptHit)            
  {                                                                            //LEDS 4-7 will be ON
    digitalWrite(4,HIGH);       
    digitalWrite(5,HIGH);
    digitalWrite(6,HIGH);
    digitalWrite(7,HIGH);
  }
  refreshEnd = millis();
  
  while((millis() - refreshEnd)<=ledRefreshTime && !interruptHit)             //LEDS 5-8 will be ON
  {
    digitalWrite(4,LOW);
    digitalWrite(8,HIGH);
  }
  refreshEnd = millis();
  while((millis() - refreshEnd)<=ledRefreshTime && !interruptHit)             //LEDS 6-9 will be ON
  {
    digitalWrite(5,LOW);
    digitalWrite(9,HIGH);
  }
  refreshEnd = millis();
  while((millis() - refreshEnd)<=ledRefreshTime && !interruptHit)             //LEDS 7-10 will be ON
  {
    digitalWrite(6,LOW);
    digitalWrite(10,HIGH);
  }
  refreshEnd = millis();
  while((millis() - refreshEnd)<=ledRefreshTime && !interruptHit)             //LEDS 8-11 will be ON
  {
    digitalWrite(7,LOW);
    digitalWrite(11,HIGH);
  }
  refreshEnd = millis();
  while((millis() - refreshEnd)<=ledRefreshTime && !interruptHit)             //LEDS 9-12 will be ON
  {
    digitalWrite(8,LOW);
    digitalWrite(12,HIGH);
  }
  refreshEnd = millis();
  while((millis() - refreshEnd)<=ledRefreshTime && !interruptHit)             //LEDS 10-13 will be ON
  {
    digitalWrite(9,LOW);
    digitalWrite(13,HIGH);
  }
  refreshEnd = millis();
  while((millis() - refreshEnd)<=ledRefreshTime && !interruptHit)             //LEDS 11-14 will be ON
  {
    digitalWrite(10,LOW);
    digitalWrite(14,HIGH);
  }
  refreshEnd = millis();
  while((millis() - refreshEnd)<=ledRefreshTime && !interruptHit)            //LEDS 12-15 will be ON
  {
    digitalWrite(11,LOW);
    digitalWrite(15,HIGH);
  }
  refreshEnd = millis();
  while((millis() - refreshEnd)<=ledRefreshTime && !interruptHit)            //LEDS 13-4 will be ON
  {
    digitalWrite(12,LOW);
    digitalWrite(4,HIGH);
  }
  refreshEnd = millis();
  while((millis() - refreshEnd)<=ledRefreshTime && !interruptHit)            //LEDS 14-5 will be ON
  {
    digitalWrite(13,LOW);
    digitalWrite(5,HIGH);
  }
  refreshEnd = millis();
  while((millis() - refreshEnd)<=ledRefreshTime && !interruptHit)           //LEDS 15-6 will be ON
  {
    digitalWrite(14,LOW);
    digitalWrite(6,HIGH);
  }
}  


void calRPM()
{
    if (revolutions >= 2)                                     // RPM gets updated for every two complete rotations
    {
      milliperrevolution = (millis() - timeold)/revolutions;
      timeold = millis();
      revolutions = 0;
      ledRefreshTime = milliperrevolution/12;                // We need to illumination for 120 Degrees (4LEDs should be ON) and  
                                                             // for evey ledRefreshTime, One LED turns ON and One LED turns OFF.    
    }
}

I suspect your root problem is using all those float variables where most (all?) should be long variables.

Lefty

Yes, wouldn’t we all have saved ourselves a lot of time if we’d have seen the code sooner?

AWOL: Yes, wouldn't we all have saved ourselves a lot of time if we'd have seen the code sooner?

Sure, but then would you have dug up how to reset millis?

Serendipity, I think. 8)

Besides which you could achieve much the same lighting effect by placing lights near the ends of the forks and thin reflective material on the rims. You wouldn't need timing that way and there would be less spun weight on the wheels which does make a difference to even half-serious riders.

Try to see this thread:

http://www.arduino.cc/cgi-bin/yabb2/YaBB.pl?num=1294117584

I used the code that I showed there (and below) and it is working for months (more than 70 devices…) and no problems at all.

However, that thread also comments the pros and cons of using timer0_millis.

...
#include <Wire.h>
...


    // timer0_millis can change inside the ISR process for timer0
    // 2 options: disable interrupts OR make 2 consecutive readings and
    //		    compare. I am using the second option here:

    unsigned long reading1;
    unsigned long reading2;
    boolean readingOK = false;
    int k=0;
    while (k<1000 && !readingOK) {
	reading1 = timer0_millis;
	reading2 = timer0_millis;
	if (reading1 == reading2) { readingOK = true; }
	k++;
    }

    timer0_millis = 2000 + reading1; // e.g. 2000: time elapsed while sleep
    nint++;
    Serial.print("Going to sleep. Count # " );
    Serial.println(nint );
    Serial.print("Time: millis() " );
    Serial.println(millis() );
    delay(2);		   // wait until the last serial character is send

Hope this helps.

Once Nick Gammon pointed out that if you use unsigned longs with millis() there will never be a problem with roll-over and I had my D'oh! moment, I can't see any reason to bother resetting millis() either.

So the counter-timer stopped during sleep, what's the big deal? It's the differences between the values -while it's running- that are functional while the actual values don't mean bleep.

    unsigned long reading1;
    unsigned long reading2;
    boolean readingOK = false;
    int k=0;
    while (k<1000 && !readingOK) {
	reading1 = timer0_millis;
	reading2 = timer0_millis;
	if (reading1 == reading2) { readingOK = true; }
	k++;
    }

…serves no useful purpose.

    timer0_millis = 2000 + reading1; // e.g. 2000: time elapsed while sleep

…is wrong. Interrupts have to be disabled when reading or writing shared multi-byte data. The previous code snippet protects the read but you failed to protect the write.

The (mostly) correct way to bump millis…

  uint8_t SaveSREG;
  SaveSREG = SREG;
  cli();
  timer0_millis = 2000 + timer0_millis; // e.g. 2000: time elapsed while sleep
  SREG = SaveSREG;

None of the code presented adjusts micros.

instead of millis() you can use a millis() based stopwatch class to have multiple independant timers ... see - http://www.arduino.cc/playground/Code/StopWatchClass -