Make millis() accurate regardless of code (or alternative to it)

I'm trying to program a ~1h led light show with an Arduino Mega. It handles a combination of addressable led strips and regular ones that I'm trying to sync with a video, so the maximum timing error is something like 100ms per hour.
The syncing is done by flashing a white square under a photoresistor taped on the screen and store the value of millis() of that point.
I'm now trying to program the first minutes of the show, but I'm probably doing something bad because the routine ends before the video does, suggesting that millis() is skipping some milliseconds. If I just program it to wait till the end the timing is perfect, so there's something in the code that makes it skip some milliseconds. Note that the code is nowhere near optimized, I'm still trying out different ways to animate the lights before doing the whole thing.

counter = 1;
k = 0;
while (millis() - beginningTime <= framesToMillis(2625)){
    FillLEDsFromPalette(1, k, 255);
    k--;
    mirror();
    FastLED.show();
    if (millis() - beginningTime >= 469*counter){
        counter++;
        setwhiteledpins(0 + counter%2, 0);
        setwhiteledpins(1 - counter%2, 128);
        setrgbledpins(0 + counter%2, 10, 0, 125);
        setrgbledpins(1 - counter%2, 20, 0, 250);
        if (counter%2 == 0){
            SetupPinkPalette();
        } else {
            SetupOrangePalette();
        }
    }
}
turnOff();

Why is millis() not keeping accurate timing? What kind of code makes it this much wrong? In my example, the animation should run for 105 sec but by the end millis() is about 2 sec behind.
Also if the best solution involves adding an external module to the board or something like that I would prefer it over having to program with too many constraints.
Thanks!

Do you use interrupts anywhere? Could something be disabling interrupts for more than a millisecond at a time?

framesToMillis(2625)

What are you using for the frame rate? NTSC is 29.97 frames per second or 33.3667 milliseconds per frame. If you are using integers (33 milliseconds per frame) then you will be off by a millisecond every three frames.

johnwasser:
Do you use interrupts anywhere? Could something be disabling interrupts for more than a millisecond at a time?

As in NeoPixels.

johnwasser:
Do you use interrupts anywhere? Could something be disabling interrupts for more than a millisecond at a time?

I don't use them explicitly (I didn't even know what they were before searching the problem up), maybe FastLED.h uses them. But if I understand correctly using them should make my program end later not sooner...

Almost any code that controls NeoPixel-type addressable LEDs must disable interrupts to meet the device timing requirements. The only exception that I'm aware of is DMA-based solutions.

johnwasser:

framesToMillis(2625)

What are you using for the frame rate? NTSC is 29.97 frames per second or 33.3667 milliseconds per frame. If you are using integers (33 milliseconds per frame) then you will be off by a millisecond every three frames.

I'm using 25fps, so that function just multiplies the input by 40.

gfvalvo:
Almost any code that controls NeoPixel-type addressable LEDs must disable interrupts to meet the device timing requirements. The only exception that I'm aware of is DMA-based solutions.

I've now tried removing FastLED.show(); and replacing it with a delay(15); (approximately the time it takes to execute) and the timing is now spot on so it looks like that fast led is the culprit.
Any ideas on other ways to keep track of the time that are immune to interrupts?

I had the same problem and solved it by simply watching a free-running counter.

It's a kludge I call fakeMilcros(), I never did figure out how to make it run as micros or millis, but I can say it is accurate and is not affected by interrupts. Good for neopixels…

The approach is sound, I was just too lazy to link the fakeMilcros to any particular real time increment like a millisecond or microsecond as report and used in delay() and delayMicroseconds().

I offer it as a point of departure. I use this as is in my animations of smart LEDs even if they don't mess with millis();

Demo below for UNO at least.

a7

/* fake milcros demo */

void setup() {
  // put your setup code here, to run once:
	Serial.begin(9600);
	Serial.println("hello fake micros world!");

	pinMode(LED_BUILTIN, OUTPUT);

	timerOneInit();
}


/* voo doo. who do? you do! */
void timerOneInit()
{
 noInterrupts();

  TCCR1A = 0;
  TCCR1B = 0;

  TCNT1  = 0;
  TCCR1B = TCCR1B & B11111000 | B00000100;    // set timer 1 divisor to ???

interrupts();
}

unsigned int fakeMilcros()
{
	return (TCNT1);
}

# define TICK 20000

unsigned int timeTo = 0;

void loop() {
	static int counter = 0;
	unsigned int timeTemp;

	Serial.print("looping ");
	Serial.println(counter++);

	digitalWrite(LED_BUILTIN, LOW);
	while ((timeTemp = fakeMilcros()) - timeTo < TICK)
		;
	timeTo = timeTemp;
	digitalWrite(LED_BUILTIN, HIGH);
}
1 Like

FastLED attempts to correct the millis() count for the time in which interrupts are disabled, but as you have observed that is not highly accurate. You may want to look at using a teensy board, some of those support driving LEDs using DMA (direct memory access). Alternately, you could used a 2nd arduino to control the timing and have it trigger the LED sequences at the appropriate times.

Don't forget that any board that uses a resonator vs. a crystal for system timing (such as the Mega) will not keep very accurate millis() time. You should confirm that with a simple test sketch that only tracks millis() time, does nothing else.

Thanks everyone for your answers and suggestions, tomorrow I'll do some testing. I'll make an hour-long test and see what works out best between @alto777 's code

alto777:
I offer it as a point of departure. I use this as is in my animations of smart LEDs even if they don't mess with millis();

and the second Arduino for the timing.

david_2018:
Alternately, you could used a 2nd arduino to control the timing and have it trigger the LED sequences at the appropriate times.

I have an Arduino UNO laying around so I'm going to try with that and see how precise its resonator is.

aarg:
Don't forget that any board that uses a resonator vs. a crystal for system timing (such as the Mega) will not keep very accurate millis() time. You should confirm that with a simple test sketch that only tracks millis() time, does nothing else.

If I understand correctly via this approach I could hook up an output pin on the second Arduino with an input of the main one, so each time I need to switch animation I just raise or lower the voltage on the pin based on timings from the second Arduino. Right?

Toss a (R)eal (T)ime (C)lock board into the mix. Every second or so reset your light show timer (keyframe) and you should be good to go.

-jim lee

This topic was automatically closed 120 days after the last reply. New replies are no longer allowed.