Why is this code outside of void setup()?

I am trying to understand the syntax and convention used by my betters and have been studying the code for a test routine used as a sample in the FaseLED library. The question I have is immediately after the void setup() routine, there is some code not within the setup but prior to the void loop().

The segment in question is this:

// List of patterns to cycle through.  Each is defined as a separate function below.
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm };

uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current
uint8_t gHue = 0; // rotating "base color" used by many of the patterns

The program runs as expected but I am trying to learn c++ on the Arduino and don’t understand why this code is not within the setup routine. My understanding is that the board will execute the setup() everytime it is powered up and that the void loop() is the “body” of the program. My assumption is that the code I mention after the setup is run after the setup routine but before the void loop(). Is this accurate? Is this functionally identical to putting that code within the setup() routine?

I am not trying to nit pick the style of the author but trying to understand if this is done for a reason or just a style issue. Thanks in advance.

FYI am an old school programmer who pursued a medical career and programs infrequently for fun/hobby. I was last employed as a programmer in the early 80’s.

Code follows.

#include <FastLED.h>

FASTLED_USING_NAMESPACE

// FastLED "100-lines-of-code" demo reel, showing just a few
// of the kinds of animation patterns you can quickly and easily
// compose using FastLED.
//
// This example also shows one easy way to define multiple
// animations patterns and have them automatically rotate.
//
// -Mark Kriegsman, December 2014

#if defined(FASTLED_VERSION) && (FASTLED_VERSION < 3001000)
#warning "Requires FastLED 3.1 or later; check github for latest code."
#endif

#define DATA_PIN    12
//#define CLK_PIN   4
#define LED_TYPE    WS2812B
#define COLOR_ORDER GRB
#define NUM_LEDS    16
CRGB leds[NUM_LEDS];

#define BRIGHTNESS          96
#define FRAMES_PER_SECOND  120

void setup()
{
	delay(3000); // 3 second delay for recovery

	// tell FastLED about the LED strip configuration
	FastLED.addLeds<LED_TYPE, DATA_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);
	//FastLED.addLeds<LED_TYPE,DATA_PIN,CLK_PIN,COLOR_ORDER>(leds, NUM_LEDS).setCorrection(TypicalLEDStrip);

	// set master brightness control
	FastLED.setBrightness(BRIGHTNESS);
}


// List of patterns to cycle through.  Each is defined as a separate function below.
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm };

uint8_t gCurrentPatternNumber = 0; // Index number of which pattern is current
uint8_t gHue = 0; // rotating "base color" used by many of the patterns

void loop()
{
	// Call the current pattern function once, updating the 'leds' array
	gPatterns[gCurrentPatternNumber]();

	// send the 'leds' array out to the actual LED strip
	FastLED.show();
	// insert a delay to keep the framerate modest
	FastLED.delay(1000 / FRAMES_PER_SECOND);

	// do some periodic updates
	EVERY_N_MILLISECONDS( 20 )
	{
		gHue++;    // slowly cycle the "base color" through the rainbow
	}
	EVERY_N_SECONDS( 10 )
	{
		nextPattern();    // change patterns periodically
	}
}

#define ARRAY_SIZE(A) (sizeof(A) / sizeof((A)[0]))

void nextPattern()
{
	// add one to the current pattern number, and wrap around at the end
	gCurrentPatternNumber = (gCurrentPatternNumber + 1) % ARRAY_SIZE( gPatterns);
}

void rainbow()
{
	// FastLED's built-in rainbow generator
	fill_rainbow( leds, NUM_LEDS, gHue, 7);
}

void rainbowWithGlitter()
{
	// built-in FastLED rainbow, plus some random sparkly glitter
	rainbow();
	addGlitter(80);
}

void addGlitter( fract8 chanceOfGlitter)
{
	if( random8() < chanceOfGlitter)
	{
		leds[ random16(NUM_LEDS) ] += CRGB::White;
	}
}

void confetti()
{
	// random colored speckles that blink in and fade smoothly
	fadeToBlackBy( leds, NUM_LEDS, 10);
	int pos = random16(NUM_LEDS);
	leds[pos] += CHSV( gHue + random8(64), 200, 255);
}

void sinelon()
{
	// a colored dot sweeping back and forth, with fading trails
	fadeToBlackBy( leds, NUM_LEDS, 20);
	int pos = beatsin16(13, 0, NUM_LEDS);
	leds[pos] += CHSV( gHue, 255, 192);
}

void bpm()
{
	// colored stripes pulsing at a defined Beats-Per-Minute (BPM)
	uint8_t BeatsPerMinute = 62;
	CRGBPalette16 palette = PartyColors_p;
	uint8_t beat = beatsin8( BeatsPerMinute, 64, 255);
	for( int i = 0; i < NUM_LEDS; i++)   //9948
	{
		leds[i] = ColorFromPalette(palette, gHue + (i * 2), beat - gHue + (i * 10));
	}
}

void juggle()
{
	// eight colored dots, weaving in and out of sync with each other
	fadeToBlackBy( leds, NUM_LEDS, 20);
	byte dothue = 0;
	for( int i = 0; i < 8; i++)
	{
		leds[beatsin16(i + 7, 0, NUM_LEDS)] |= CHSV(dothue, 200, 255);
		dothue += 32;
	}
}

The code in your first snippet does not seem to be in the program you posted.

However, as far as I can see the code in the snippet is defining global variables - and that is why it is not in any function.

Note that setup() and loop() are functions and the word void the precedes them is there to tell the compiler that the function does not return a value.

...R

Robin2:
The code in your first snippet does not seem to be in the program you posted.

About 1/3 down from the top. Just after setup.

As @Robin2 mentioned, gPatterns is a global variable. The third line of the snippet is the definition.

SimplePatternList is a user defined datatype. Specifically the datatype is an array of pointers to functions that have no parameters and no return value.

I suspect the pointer-to-function syntax is tripping you up.

Hi kd6lor, don t worry, it is correct
As they said to you that are variables
In c and other languages you have to declare them before using

If i say to arduino set the variable gHue to 0 arduino will say me "what are you talking about? Who is gHue?"

This is the reason why we need to declare them before using

You have to study and understand the difference between byte int float an boolean

Have a good learning!

aster94:
Hi kd6lor, don t worry, it is correct
As they said to you that are variables
In c and other languages you have to declare them before using

If i say to arduino set the variable gHue to 0 arduino will say me “what are you talking about? Who is gHue?”

This is the reason why we need to declare them before using

You have to study and understand the difference between byte int float an boolean

Have a good learning!

I first programmed in C in the early 80’s… Syntax and convention are in need of refresh but I do understand scope, just not the scope rules with this compiler.

Followup question. If the variables ( in this example or any other ) are defined in the void setup() code, are the variables global or local?

Paul

found info Void setup() no different from any other routine regarding variable scope.

also in reading the code I ran into something I have never seen before. Putting the name of functions in an array then calling the variable(index) and the routine runs. Wow, that’s cool. What also blows me away is the fact that you increment the index and the routine ends and the one pointed to by the new index runs. No need to kill the first routine? Guess not or I haven’t found that part of the code yet.

PJ

btw, thanks for all the info. This C is a lot more than the C from a long time ago…

kd6lor:
also in reading the code I ran into something I have never seen before. Putting the name of functions in an array then calling the variable(index) and the routine runs.

Actually, pointers to functions have been in the 'C' language since K met R. They are really useful in certain circumstances such as hooking custom callback functions into standard libraries.

What also blows me away is the fact that you increment the index and the routine ends and the one pointed to by the new index runs. No need to kill the first routine?

Not how it works. All processors in the Arduino ecosystem are single-threaded. A function must run to completion and return before any other can run. If it hangs in an infinite loop, no other function will ever run (unless a WDT or other interrupt is able to kick it out of the loop).

btw, thanks for all the info. This C is a lot more than the C from a long time ago...

In a sense you're right, because Arduino is C++. But, good-old C is still in there as an (essentially) unchanged subset.

If the variables ( in this example or any other ) are defined in the void setup() code, are the variables global or local?

Don't use the word "void" in front of function names, it shows your lack of programming knowledge. The word before a function name indicates the type of data it returns (example: unsigned int setup() ), if no data is returned, the word "void" is used.
Your question should have read :

If the variables ( in this example or any other ) are defined in the setup() code, are the variables global or local?

Keep learning and have fun. :slight_smile:

there is some code not within the setup

This might already be clear, but essentially those statements are ok "at global csope" because they're not actually "code" - they are variable initializations that happen at compile time. (not exactly JUST at compile time, in an embedded system, but syntactically speaking...)

// List of patterns to cycle through.  Each is defined as a separate function below.
typedef void (*SimplePatternList[])();
SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm };

Well, you have chose a bit of a doozy for a new programmer.

When I define a function: int foo() { return 9; }

I can call that function: x = foo(); .

The function definition declares an identifier
** **foo** **
. In C++, every identifier has a type, and the type of foo is "pointer to function returning int". It's value will be a memory address in progmem.

Now, I can declare a variable of type "pointer to function returning int" and I can assign foo to it:

int (*bar)() = foo;

The parenthesis around *bar are necessary because pointer indirection - the star operator - has lower precedence than function invocation. It's a bit like how if you want your addition to be performed before you multiplication, you have to put parenthesis around the addition.

After doing this, I can call the foo function by calling bar: int y = bar(); . Technically, a function call is simply a unary operator - you can do it to any pointer to function, either held in a variable (like bar) or an identifier whose value is fixed by the compiler (like foo).

So. The code above says "the type SimplePatternList is an array of pointers to function returning void", and "gPatterns is a SimplePatternList with elements initialised to these values ...".

This means that you can call the functions in gPatterns using an array index. To call confetti, you can go [nobbc]gPatterns[2]();[/nobbc] .

Personal preference only, but I think this is clearer and easier to mentally parse:

typedef void (*SimplePatternList)();
SimplePatternList gPatterns[] = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm };

gfvalvo:
Personal preference only, but I think this is clearer and easier to mentally parse:

typedef void (*SimplePatternList)();

SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm };

Shouldn't call it SimplePatternList anymore since you moved the list part.

Jiggy-Ninja:
Shouldn't call it SimplePatternList anymore since you moved the list part.

Yea, just wanted to reuse OP's variable name. If doing it from scratch, I would have gone with something like:

SimplePatternElement
SimplePatternItem
SimplePatternThing
SimplePattern