Static Variables in Functions with LEDs

I haven’t been working on arduino and c ++ for very long, but I invest a lot of time and learn a lot every day. I always stumble across an basic understanding of principles in functions related to static variables.

I use 1 WS2812 LED strip with 81 LEDs connected to pin 1. The strip is divided in 3 Parts with 27 LEDs each, called “strip” in the Code. The LEDs are controlled by the <FastLED.h> library.
I work with a Teensy 3.2 in in 24MHz and “Faster” Mode.

I want to make a kind of larsonscanner that I can run on a specific section of the led strip. The input of the function is the starting point (part number) and the length (over how many parts).

If int c & int i are static, the animation works perfectly, but only if the function is only used once. If I use the function several times, the start and end positions start to shift illogically. Is that because a static variable space is reserved in memory, which is then taken over by another function call?

If int c & int i are not static, then the animation does not work, but each instance of the function has a led at the beginning and at the end of the respective positioning that lights up, but always overwrites itself with the start value, what makes sense to me.

This link helped me a little, but didn’t bring me to the solution of my problem:

What other ways are there that i don’t know yet to solve this problem?

Thanks and greetings JPTHA

#include <FastLED.h>

// LED LIGHTING SETUP
#define NUM_LEDS_PER_STRIP  81
#define NUM_LEDS_PER_PART 27
#define NUM_PARTS 3
#define NUM_LEDS NUM_LEDS_PER_PART * NUM_PARTS

// STRIP SETUP
#define BRIGHTNESS  255
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

void setup() {
  // LED LIGHTING SETUP
  delay(300);

  FastLED.addLeds<LED_TYPE, 1, COLOR_ORDER>(leds, 0, NUM_LEDS_PER_PART * 3);
  FastLED.setBrightness(BRIGHTNESS);

  // CLEAR LEDS
  fill_solid(leds, NUM_LEDS, CRGB::Black);
  FastLED.show();
}

void loop() {

  // LarsonScanner(strip, striplength)

  LarsonScanner(0, 1);
  //  LarsonScanner(1, 1);
  //  LarsonScanner(2, 1);
}

void LarsonScanner(byte strip, byte striplength) {

  static unsigned long previousMillis = 0;
  unsigned long interval = 32;

  static int c = (strip * NUM_LEDS_PER_PART);
  static int i = ((strip * NUM_LEDS_PER_PART) + ((NUM_LEDS_PER_PART * striplength) - 1));

  unsigned long currentMillis = millis();
  if ((unsigned long)(currentMillis - previousMillis) >= interval) {
    leds[c] = CHSV(0, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 80);
    if (++c > ((strip * NUM_LEDS_PER_PART) + ((NUM_LEDS_PER_PART * striplength) - 1))) c = (strip * NUM_LEDS_PER_PART);

    leds[i] = CHSV(160, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 80);
    if (--i < (strip * NUM_LEDS_PER_PART)) i = ((strip * NUM_LEDS_PER_PART) + ((NUM_LEDS_PER_PART * striplength) - 1));

    previousMillis = currentMillis;
  }
}
  if ((unsigned long)(currentMillis - previousMillis) >= interval) {

There is no reason for the cast. Both variables are unsigned long, so the result will also be unsigned long.

It does not make sense to have c and i static, since the values on any given call depend on the input arguments, not whatever value they had last time the function ended.

It is unclear what your problem is, but making c and i static is NOT the solution. Instead of hand-waving, tell us exactly which LEDs come on when c and i are properly not static.

Nope. Static just means space for the variable is on the heap, not on the stack. Perhaps that might have an effect if you are calling LarsonScanner recursively. But as you only present a code snipped we cannot say. Where is fadeToBlackBy?

@PaulS
Yes, this cast does not make sense.
I call the function 3 times with c and i not static.

LarsonScanner(0, 1); // LED 1 is Red and LED 27 is Blue.
LarsonScanner(1, 1); // LED 28 is Red and LED 54 is Blue.
LarsonScanner(2, 1); // LED 55 is Red and LED 81 is Blue.

The LEDs flicker irregularly.

@pcbbc
This is a reduced version of the full code. fadeToBlackBy comes right after FastLED.show. Worked for me in other applications. Is this wrong in this case?

#include <FastLED.h>

// LED LIGHTING SETUP
#define NUM_LEDS_PER_STRIP  81
#define NUM_LEDS_PER_PART 27
#define NUM_PARTS 3
#define NUM_LEDS NUM_LEDS_PER_PART * NUM_PARTS

// STRIP SETUP
#define BRIGHTNESS  255
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

void setup() {
  // LED LIGHTING SETUP
  delay(300);
  
  FastLED.addLeds<LED_TYPE, 1, COLOR_ORDER>(leds, 0, NUM_LEDS_PER_PART * 3);
  FastLED.setBrightness(BRIGHTNESS);

  // CLEAR LEDS
  fill_solid(leds, NUM_LEDS, CRGB::Black);
  FastLED.show();
}

void loop() {

  LarsonScanner(0, 1);
  LarsonScanner(1, 1);
  LarsonScanner(2, 1);

}

void LarsonScanner(byte strip, byte striplength) {

  static unsigned long previousMillis = 0;
  unsigned long interval = 32;

  int c = (strip * NUM_LEDS_PER_PART);
  int i = ((strip * NUM_LEDS_PER_PART) + ((NUM_LEDS_PER_PART * striplength) - 1));

  unsigned long currentMillis = millis();
  if ((currentMillis - previousMillis) >= interval) {
    leds[c] = CHSV(0, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 80);
    if (++c > ((strip * NUM_LEDS_PER_PART) + ((NUM_LEDS_PER_PART * striplength) - 1))) c = (strip * NUM_LEDS_PER_PART);

    leds[i] = CHSV(160, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 80);
    if (--i < (strip * NUM_LEDS_PER_PART)) i = ((strip * NUM_LEDS_PER_PART) + ((NUM_LEDS_PER_PART * striplength) - 1));

    previousMillis = currentMillis;
  }
}
void LarsonScanner(byte strip, byte striplength) {

  static unsigned long previousMillis = 0;
  unsigned long interval = 32;

  int c = (strip * NUM_LEDS_PER_PART);
  int i = ((strip * NUM_LEDS_PER_PART) + ((NUM_LEDS_PER_PART * striplength) - 1));

  unsigned long currentMillis = millis();
  if ((currentMillis - previousMillis) >= interval) {

Most of the time when you call this function, it does nothing. The time that it is called is NOT more than interval milliseconds after the last time it was called, so nothing gets done. Does that seem right to you?

I understood this so that whenever the interval time is reached, a run is calculated and another led is switched on. So this made sense for me until now. But apparently I'm wrong ...

So is it an incorrect implementation of the blink without delay concept?

So is it an incorrect implementation of the blink without delay concept?

That's for you to decide. It looks, though, because you call the function three times on each pass through loop, that each call should have a separate last-time-something-happened variable.

Only you can decide if the code is doing what you want.

@PaulS, thank you for your inputs! And unfortunately the code doesn’t do what I want.

I did 2 days of research based on your inputs and did various millis and function tutorials. But unfortunately I’m still not further.

I want a function that generates a running light (larson scanner) at the desired position on a LED-strip. I want to be able to define the starting point (strip) and the length of the running light (striplength) in the function parameters. The function must be callable several times with different input parameters, so that a total of about 9 running lights can run in parallel with different numbers of leds and do not block each other.

The following code does the animations correctly, but this undynamic way does not work for me. I’m sure there is a much cleaner way that is more scalable… I don’t want to use global variables for c, i, x & y, I want to enter the position and length of the respective chase using the function parameters. And I don’t want to have to make a copy of the larsonScanner function for every chaser.

#include <FastLED.h>

// LED LIGHTING SETUP
#define NUM_LEDS_PER_STRIP  22
#define NUM_LEDS_PER_PART 11
#define NUM_PARTS 2
#define NUM_LEDS NUM_LEDS_PER_PART * NUM_PARTS

// STRIP SETUP
#define BRIGHTNESS  255
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

unsigned long previousMillisA = 0;
unsigned long previousMillisB = 0;
unsigned long interval = 100;

int c = 0;
int i = 10;

int x = 11;
int y = 21;

void setup() {
  // LED LIGHTING SETUP
  delay(300);

  FastLED.addLeds<LED_TYPE, 10, COLOR_ORDER>(leds, 0, NUM_LEDS_PER_PART * 3);
  FastLED.setBrightness(BRIGHTNESS);

  // CLEAR LEDS
  fill_solid(leds, NUM_LEDS, CRGB::Black);
  FastLED.show();
}

void loop() {
  LarsonScannerA(0, 1);
  LarsonScannerB(1, 1);
}

void LarsonScannerA(byte strip, byte striplength) {

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillisA >= interval) {
    previousMillisA = currentMillis;

    leds[c] = CHSV(0, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 80);
    if (++c > ((strip * NUM_LEDS_PER_PART) + ((NUM_LEDS_PER_PART * striplength) - 1))) c = (strip * NUM_LEDS_PER_PART);

    leds[i] = CHSV(160, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 80);
    if (--i < (strip * NUM_LEDS_PER_PART)) i = ((strip * NUM_LEDS_PER_PART) + ((NUM_LEDS_PER_PART * striplength) - 1));
  }
}

void LarsonScannerB(byte strip, byte striplength) {

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillisB >= interval) {
    previousMillisB = currentMillis;

    leds[x] = CHSV(40, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 80);
    if (++x > ((strip * NUM_LEDS_PER_PART) + ((NUM_LEDS_PER_PART * striplength) - 1))) x = (strip * NUM_LEDS_PER_PART);

    leds[y] = CHSV(200, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 80);
    if (--y < (strip * NUM_LEDS_PER_PART)) y = ((strip * NUM_LEDS_PER_PART) + ((NUM_LEDS_PER_PART * striplength) - 1));
  }
}

If someone can give me a tip in which direction I can continue searching I would be very grateful.

pcbbc:
Nope. Static just means space for the variable is on the heap, not on the stack.

No, static is in the data segment or BSS, usually below the heap.
Heap is for dynamically-allocated variables.

As PaulS said, each running light needs its own time variable. Define three and pass the appropriate one to the scanner function by reference when you call it.

There are better ways. Structs and arrays spring to mind but the above should allow you to make progress.

Thank you for your input wildbill! Structs are unfortunately far from my level of knowledge. So I tried to pass global previousMillis-Versions by reference, but I’m not sure if I have implemented it correct.

And I still have the problem mentioned by PaulS: "Most of the time when you call this function, it does nothing. The time that it is called is NOT more than interval milliseconds after the last time it was called, so nothing gets done. "

Serial Print’s the same I see on the Strip.
c = 0 (-> Red)
i = 6 (-> Blue)
c = 7 (-> Red)
i = 13 (-> Blue)
c = 14 (-> Red)
i = 20 (-> Blue)

The LEDs flicker but the chase animation does not work.

I know this topic was discussed intensively in various treads, I have read “Using millis() for timing. A beginners guide”, “Demonstration code for several things at the same time” and many other threads on the subject, but I simply cannot adapt those solutions to my application…

#include <FastLED.h>

// LED LIGHTING SETUP
#define NUM_LEDS_PER_STRIP  22
#define NUM_LEDS_PER_PART 7
#define NUM_PARTS 3
#define NUM_LEDS NUM_LEDS_PER_PART * NUM_PARTS

// STRIP SETUP
#define BRIGHTNESS  255
#define LED_TYPE    WS2812
#define COLOR_ORDER GRB
CRGB leds[NUM_LEDS];

unsigned long previousMillis0 = 0;
unsigned long previousMillis1 = 0;
unsigned long previousMillis2 = 0;
unsigned long interval = 300;

void setup() {
  // LED LIGHTING SETUP
  delay(300);

  FastLED.addLeds<LED_TYPE, 10, COLOR_ORDER>(leds, 0, NUM_LEDS_PER_PART * 3);
  FastLED.setBrightness(BRIGHTNESS);

  // CLEAR LEDS
  fill_solid(leds, NUM_LEDS, CRGB::Black);
  FastLED.show();

  Serial.begin(9600);
}

void loop() {
  LarsonScanner(0, 1, previousMillis0);
  LarsonScanner(1, 1, previousMillis1);
  LarsonScanner(2, 1, previousMillis2);
}

void LarsonScanner(byte strip, byte striplength, unsigned long &previousMillis) {

  int c = (strip * NUM_LEDS_PER_PART);
  int i = ((strip * NUM_LEDS_PER_PART) + ((NUM_LEDS_PER_PART * striplength) - 1));

  Serial.print("c = ");
  Serial.println(c);
  Serial.print("i = ");
  Serial.println(i);

  unsigned long currentMillis = millis();
  if (currentMillis - previousMillis >= interval) {
    previousMillis = currentMillis;

    leds[c] = CHSV(0, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 80);
    c++;
    if (c > ((strip * NUM_LEDS_PER_PART) + ((NUM_LEDS_PER_PART * striplength) - 1))) c = (strip * NUM_LEDS_PER_PART);

    leds[i] = CHSV(160, 255, 255);
    FastLED.show();
    fadeToBlackBy( leds, NUM_LEDS, 80);
    i--;
    if (i < (strip * NUM_LEDS_PER_PART)) i = ((strip * NUM_LEDS_PER_PART) + ((NUM_LEDS_PER_PART * striplength) - 1));
  }
}

What confuses me so much is that the animation works as soon as i do c & i global and use the function only once…