FastLED -> declare array of object pointers (references), use inside object call

Hi,

I´m trying to use the FastLED libary with multiple pins. Normally, it´s simple:

.ino

#include <FastLED.h>

#define NUM_LEDS_S1 16
#define DATA_PIN_S1 6

#define NUM_LEDS_S2 24
#define DATA_PIN_S2 7

[...]


CRGB leds1[NUM_LEDS_S1];
CRGB leds2[NUM_LEDS_S2];

I now have created .h and .cpp file, and would like to create an object for every stripe on every pin.

Function.h

#include "config.h"

#include <FastLED.h>

#include "arduino.h"

class Function {
public:
Function(const byte ueb);

void begin();
}

Function.cpp

#include "config.h"

#include "Function.h"
Function::Function(const byte ueb)
{
 CRGB leds[ueb];
}

void Function::begin()
{
 Serial.begin(115200);
 FastLED.addLeds<WS2812B, DATA_PIN_S1, GRB>(leds, NUM_LEDS_S1);
 fill_solid(leds, NUM_LEDS_S1, CRGB(0, 0, 0));
 FastLED.setBrightness(255);
 FastLED.show();
 Serial.println("begin");
}

.ino:

const byte a = NUM_LEDS_S1;

Functin Stripe1(a);

void setup()
{
 Stripe1.begin();
}

void loop()
{
}

config.h

#define NUM_LEDS_S1 16
#define DATA_PIN_S1 6

#define NUM_LEDS_S2 8
#define DATA_PIN_S2 7

This way VS17 underlines "ueb" in cpp file and says cannot be used as a constant value. However, it compiles without errors.
Calling begin then says

Funktionen.cpp: In member function void Function::begin()
 
Function.cpp: 37:45: error: 'leds' was not declared in this scope
   FastLED.addLeds<WS2812B, DATA_PIN_S1, GRB>(leds, NUM_LEDS_S1)

Whole code is a bit longer btw, I cuttet off the unnecessary part :wink:

Function::Function(const byte ueb)
{
	CRGB leds[ueb];
}

Creates an array of CRGB object which IMMEDIATELY goes out of scope once the function ends and ceases to exist. Hardly useful.

You will need to (choose one):

  1. have a member variable that is a pointer to a CRGB and points to the first element of an array that is created in the sketch and another member variable that holds the total number of elements in that array.

  2. Have a member array that is sized for the longest strip you might ever have and a member variable that tells how many slots of that array you actually used.

  3. A member pointer and malloc memory for it in the constructor. This is NOT a solution for the beginner.

Delta_G:

Function::Function(const byte ueb)

{
CRGB leds[ueb];
}




Creates an array of CRGB object which IMMEDIATELY goes out of scope once the function ends and ceases to exist. Hardly useful. 

You will need to (choose one):

1. have a member variable that is a pointer to a CRGB and points to the first element of an array that is created in the sketch and another member variable that holds the total number of elements in that array.

2. Have a member array that is sized for the longest strip you might ever have and a member variable that tells how many slots of that array you actually used.

3. A member pointer and malloc memory for it in the constructor. This is NOT a solution for the beginner.

Wow - okay, this is two steps ahead of my knowledge^^.

As far as I understand, C++ has no garbage collector - why does the array cease to exist?

If I get 1) right,
byte arrSize = sizeof(ueb);
would hold the length of the array.
Next problem is to declare a variable which could hold leds[0]..

Inso:
As far as I understand, C++ has no garbage collector - why does the array cease to exist?

It's a local variable on the stack. It's removed (actually the stack pointer is adjusted) when the function exits.

Inso:
Wow - okay, this is two steps ahead of my knowledge^^.

As far as I understand, C++ has no garbage collector - why does the array cease to exist?

Google "C++ scope" and do some reading.

If I get 1) right,
byte arrSize = sizeof(ueb);
would hold the length of the array.

ueb is a byte. sizeof a byte would be 1.

Next problem is to declare a variable which could hold leds[0]..

No, you misunderstand. Define the array in the .ino file just as you would if the class wasn't there. Just like you would have in your first example code in the OP. Then pass a pointer to that array into your class and have a member variable that holds that pointer and another that holds the number of elements in the array.

gfvalvo:
It's a local variable on the stack. It's removed (actually the stack pointer is adjusted) when the function exits.

Think I get it - it is not declared in .h public or private. Omg, yes. And I cannot declare it in .h, because the size is not known at this point. Damn..

Delta_G:
Google "C++ scope" and do some reading.

ueb is a byte. sizeof a byte would be 1.

No, you misunderstand. Define the array in the .ino file just as you would if the class wasn't there. Just like you would have in your first example code in the OP. Then pass a pointer to that array into your class and have a member variable that holds that pointer and another that holds the number of elements in the array.

Ok, now it makes sense, I just tell the object array where the global created array is located in ram, so i can use it within the object without creating it there..

Many tanks guys, I will now read on :smiley:

Me again :frowning:

read a lot about pointers the last days, did research, tested, and was ~sure the problem is solved.

It is not :-\

Simple example:

I declare two int, create a pointer array, put in the pointer to s and t. Then I call a function of another class, using the array:

int s = 5;
int t = 10;

int *testArray[] = { &s, &t };

[...]

aTest::testTheA(*testArray);

On the other class, I create two int values, set the value to the two values I have the pointer of. First is fine. Second is not.

int u = 0;

int v = 0;

void aTest::testTheA(int *testArray)
{
	u = testArray[0];
	v = testArray[1];
	Serial.print("S = ");
	Serial.println(u);
	Serial.print("T = ");
	Serial.println(v);
}

Output is:

S = 5
T = 731

Setting S to 15 also prints S= 15. But changing t to whatever does not change anything, it is always 731.

u = testArray[0];

u is an int. testArray[0] is a pointer to int. You can't assign like this. You need to dereference the pointer.

u = *(testArray[0]);

There's a LOT more wrong here, but that's the crux of your problem.

Delta_G:

u = testArray[0];

u is an int. testArray[0] is a pointer to int. You can't assign like this. You need to dereference the pointer.

[code]u = *(testArray[0]);[/code]

There's a LOT more wrong here, but that's the crux of your problem.

Yeah, seems so -

u = *(testArray[0]);

throws the error "operand of * has to be a pointer".

Maybe in short, if someone has the time:

int *testArray[]

is an array of pointers. I can store pointer inside. ?

&s

is a pointer to the value assigned to s. The memory address. ?

int *testArray[] = { &s, &t };

is an pointer-array which holds two pointers. ?

aTest::testTheA(*testArray);

is a correct way to call a function with a pointer array as parameter ?

aTest::testTheA(*testArray);

is a correct way to call a function with a pointer array as parameter ?

Yes, but then you pass it a pointer to a pointer to an int. An int** if you will. Remember, the array name without any braces is itself a pointer to the first element.

What you want to do here is not to have an array of pointers, but an array of int. Then you want to pass a pointer to that array. Not a pointer to a pointer.

int *testArray[] = { &s, &t };

Should just be:

int testArray[] = { s, t };

Then it will work with the code you had before.

If not then stop posting contrived examples and give me a MCVE so I can play with it too.

throws the error "operand of * has to be a pointer".

Sorry, no compiler handy to check syntax with. I thought that was right.

Delta_G:
If not then stop posting contrived examples and give me a MCVE so I can play with it too.

No problem :slight_smile:

The project: I have a LED sketch running for a couple of WS2812b on several pins.
I want to be able to f.e. set a seperate pin to a color, and also set them all to a color.
Therefor, I create an object for every stripe (here T1 and T2) and also a multiobject, a virtual stripe (here Multi).
The multiobject holds an array of pointers to every stripe, so it can call functions on every stripe, while I have just to call one function.
Calling the function with multi now only works for the first stripe.
I made a new sketch only focusing on two stripes, the multiobject and the set color method:

arrayVerify.ino:

// arrayVerify.ino

#include "ledFunction.h"

ledFunction T1;										// create first stripe

ledFunction T2;										// create second stripe

ledFunction *objectPointers[] = { &T1, &T2 };		// create array with pointers to stripes

ledFunction Multi(objectPointers);					// create a virtual stripe to access all

void setup()                                                                      // only for verification
{
	Serial.begin(115200);

	Multi.setColor(255);							// color should be set to T1 and T2
	T1.printColor();								// verification for T1: true : Color code is: 255
	T2.printColor();								// verification for T2: false: Color code is: 0

	T1.setColor(200);								// color should be set to T1 seperate
	T2.setColor(200);								// color should be set to T2 seperate
	T1.printColor();								// verification for T1: true : Color code is: 255
	T2.printColor();								// verification for T2: true : Color code is: 255
}

void loop(){}

ledFunction.h:

// ledFunction.h

#ifndef _LEDFUNCTION_h
#define _LEDFUNCTION_h

#include "arduino.h"

class ledFunction
{
private:

	byte colorValue;			// color value of stripes leds (or of all stripes if multi)

	ledFunction *multiStripe;		// the array used after creating the multistripe object

	boolean isMulti;		// helper to decide if it is a call of multi or singlestripe (used in setColor() )

public:

	ledFunction();							// create single stripe object

	ledFunction(ledFunction **objectPointers);		// create multistripe object

	void setColor(byte color);			// function for single and multi

	void printColor();					// serial print actual value of byte colorValue

};

#endif

ledFunction.cpp:

// ledFunction.cpp

#include "ledFunction.h"

ledFunction::ledFunction()
{
	colorValue = 0;

	isMulti = false;
}

ledFunction::ledFunction(ledFunction **objectPointers)
{
	multiStripe = *objectPointers;

	colorValue = 0;

	isMulti = true;
}

void ledFunction::setColor(byte color)
{
	if (isMulti == true)	// function is called by multi
	{
		multiStripe[0].setColor(color);		// should be same as T1.setColor(byte color)
		multiStripe[1].setColor(color);		// should be same as T2.setColor(byte color)
	}
	else					// function is called by single stripe
	{
		colorValue = color;
	}
}

void ledFunction::printColor()
{
	Serial.print("Color code is: ");
	Serial.println(colorValue);
}

If I can point out anything better or something else, just let me know :slight_smile:

Damn, I have played around for hours, now I just made the array no pointer array and first time I have the correct output, wth..

// arrayVerify.ino line 9

ledFunction objectPointers[] = { &T1, &T2 };		// create array with pointers to stripes

// ledFunction.h line 22

ledFunction(ledFunction *objectPointers);		// create multistripe object

// ledFunction.cpp line 12

ledFunction::ledFunction(ledFunction *objectPointers)

if this is really the solution, I am very sorry for wasting your time. What the..

I assume I do not have to dereference here, just can use it as it is?

Yes. See #9

And again, after three hours of crashes and frustration, I´m back :confused:

With the formentioned sketch, I was able to set my values. No I try to call a sketch with the pointer. Within the sketch, I need to access the variables of the object (f.e. the amount of LEDs). No way.
I have revised the sketch to show in a "simple" example what is wrong.
Again, two objects, T1 and T2, with byte "identifier" 1 and 2. The third object called Multi with "identifier" = 9 and the array of pointers.
Multiobject calls a function. Within, I use first and second array pointer to call another function, just printing the identifier. Both times the identifier is 9 instead of 1 and 2.

The code:

arrayVerify.ino

// arrayVerify.ino

#include "ledFunction.h"

ledFunction T1(1);										// create first stripe

ledFunction T2(2);										// create second stripe

ledFunction objectPointers[] = { &T1, &T2 };		// create array with pointers to stripes

ledFunction Multi(objectPointers);					// create a virtual stripe to access all

void setup()
{
	Serial.begin(115200);

	Multi.example();
}

void loop(){}

ledFunction.h

// ledFunction.h

#ifndef _LEDFUNCTION_h
#define _LEDFUNCTION_h

#include "arduino.h"

class ledFunction
{
private:

	ledFunction *multiStripe;		// the array used after creating the multistripe object

	byte identifier;

public:

	ledFunction(int ident);							// create single stripe object

	ledFunction(ledFunction *objectPointers);		// create multistripe object

	void printIdentifier();

	void example();

};

#endif

// ledFunction.cpp

// ledFunction.cpp

#include "ledFunction.h"

ledFunction::ledFunction(int ident)
{
	identifier = ident;
}

ledFunction::ledFunction(ledFunction *objectPointers)
{
	multiStripe = objectPointers;

	identifier = 9;
}

void ledFunction::example()
{
	multiStripe[0].printIdentifier();
	multiStripe[1].printIdentifier();
}

void ledFunction::printIdentifier()
{
	Serial.print("Identifier is: ");
	Serial.println(identifier);
}

Console:

Opening port
Port open
Identifier is: 9
Identifier is: 9

Got the solution, and will now post my ruinning example here for everyone looking for similar stuff..

I also changed the topic, as the problem "moved a bit" :smiley:

// arrayVerify.ino

#include "ledFunction.h"

ledFunction T1(1);										// create first stripe

ledFunction T2(2);										// create second stripe

ledFunction* objectPointers[] = { &T1, &T2 };		// create array with pointers to stripes

ledFunction Multi(objectPointers);					// create a virtual stripe to access all

void setup()
{
	Serial.begin(115200);

	Multi.example();
}

void loop(){}
// ledFunction.h

#ifndef _LEDFUNCTION_h
#define _LEDFUNCTION_h

#include "arduino.h"

class ledFunction
{
private:

	ledFunction **multiStripe;		// the array used after creating the multistripe object

	byte identifier;

public:

	ledFunction(int ident);							// create single stripe object

	ledFunction(ledFunction **objectPointers);		// create multistripe object

	void printIdentifier();

	void example();

	void setIdent(byte i);

};

#endif
// ledFunction.cpp

#include "ledFunction.h"

ledFunction::ledFunction(int ident)
{
	identifier = ident;
}

ledFunction::ledFunction(ledFunction **objectPointers)
{
	multiStripe = objectPointers;

	identifier = 9;
}

void ledFunction::example()
{
	multiStripe[0]->printIdentifier();
	multiStripe[1]->printIdentifier();
	multiStripe[0]->setIdent(3);
	multiStripe[1]->setIdent(4);
	multiStripe[0]->printIdentifier();
	multiStripe[1]->printIdentifier();
}

void ledFunction::setIdent(byte i)
{
	identifier = i;
}

void ledFunction::printIdentifier()
{
	Serial.print("Identifier is: ");
	Serial.println(identifier);
}

Output:

Opening port
Port open
Identifier is: 1
Identifier is: 2
Identifier is: 3
Identifier is: 4

Seems to me that your 'Multi' object and your T1 / T2 objects are different animals and maybe should be of different classes. I'd consider defining a 'multiLedHandler' class with appropriate friendship so it can access the private members of objects instantiated from the ledFunction class.

Also, your functions for handling multiple stripes are rather inflexible since you hard-coded the number of stripes as 2. I'd pass the 'multiLedHandler' constructor both the array of pointers and the number of elements in the array. That way it could handle an arbitrary number of stripes.

Also, your functions for handling multiple stripes are rather inflexible since you hard-coded the number of stripes as 2. I'd pass the 'multiLedHandler' constructor both the array of pointers and the number of elements in the array. That way it could handle an arbitrary number of stripes.

Yeah, that was my next question. How will this class know how many strips have been connected?

I'm with you that these should be two separate classes.