[SOLVED] Passing object to child class

Hi guys,

I've ran into a problem which I haven't been able to solve yet with my limited C++/Arduino programming knowledge. Here's the situation:

I am using the NeoPixel library from Adafruit to control a bunch of LED's but I have to do so from a specific class in my project.

The problem is that their library requires you to supply arguments with the constructor (it doesn't have an empty constructor). So when I declare the variable in my header file, it gives me compile errors.

This is a very simplified version of my code, but it contains all the parts I need and the ones that are giving errors:

#include <Arduino.h>
#include "Child.h"

#ifndef Parent_h
#define Parent_h

#define NUM_CHILDREN 4
#define NUM_LEDS 3

class Parent
{
  public:
    Parent(unsigned char pin);

  private:
    unsigned char _pin;
    Child _children[NUM_CHILDREN];
};
#endif
#include "Parent.h"

Child _children[NUM_CHILDREN] = { Child() };

Parent::Parent (unsigned char pin, unsigned char leds[NUM_CHILDREN][NUM_LEDS])
{
  _pin = pin;
  pinMode(_pin, OUTPUT);
  
  for (unsigned char i = 0; i < NUM_CHILDREN; i++)
  {
    _children[i].setPin(_pin);
    _children[i].setLeds(leds[i]);
  }
}
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>

#ifndef Child_h
#define Child_h

#define NUM_CHILDREN 4
#define NUM_LEDS 3

class Child
{
  public:
    void setPin(unsigned char pin);
    void setStrip(Adafruit_NeoPixel strip);
    void setLeds(unsigned char leds[NUM_LEDS]);
    void ledOn (unsigned char led);
    void ledOff (unsigned char led);

  private:
    unsigned char _pin;
    unsigned char _leds[NUM_LEDS];
    Adafruit_NeoPixel _strip;
};

#endif
#include "Child.h"

void Child::setPin (unsigned char pin)
{
  _pin = pin;
}

void Child::setStrip (Adafruit_NeoPixel strip)
{
  _strip = strip;
}

void Child::setLeds(unsigned char leds[NUM_LEDS])
{
  for (unsigned char i = 0;i < NUM_LEDS; i++)
  {
    _leds[i][j] = leds[i][j];
  }
}

void Child::ledOn (unsigned char led)
{
  _strip.setPixelColor(led, 255, 255, 255);
}

void Child::ledOff (unsigned char led)
{
  _strip.setPixelColor(led, 0, 0, 0);
}
#include <Parent.h>
#include <Adafruit_NeoPixel.h>

#define DATA_PIN 6
#define NUM_CHILDREN 4
#define NUM_LEDS 3

static unsigned char leds[NUM_CHILDREN][NUM_LEDS] = {
    {3, 4, 5},
    {0, 1, 2},
    {6, 7, 8},
    {9, 10, 11}
};

Parent myParent(DATA_PIN, leds);
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_CHILDREN * NUM_LEDS, DATA_PIN, NEO_GRB + NEO_KHZ800);

void setup ()
{
  strip.begin();
  strip.show();
  myParent.setStrip(strip);
  ...
}

void loop () 
{
}

As you can see I currently have the code in there from something I tried: instantiating the Adafruit_NeoPixel class once and passing that to the Child instances, but that didn't work because the compiler tripped over the variable declarations in the Child header file.

Another thing I tried is creating the Adafruit_NeoPixel instance in the child class itself and not in the main.ino file, but I got the same compiler errors.

Another solution I have looked at (though couldn't find anything) is to reference the global strip variable (declared in my main file) from the Child class. I have no idea if this is possible in C++ or what I should look for to accomplish this.

To summarise:
I have to be able to access an Adafruit_NeoPixel instance by either:

  • passing an instance to a Child instance
  • create a new Adafruit_NeoPixel instance inside the Child instance
  • reference a single global instance of Adafruit_NeoPixel

Is there anyone who has some advice as how to solve this issue?

PS: I apologise for the long post, but I wanted to be as thorough as possible.

– Wouter

For me there is a basic error.
If you create an object NeoPixel in main, the Child class not need an internal variable of type NeoPixel but only a pointer to an object of NeoPixel type.
For me _strip in Child class must be a pointer to a NeoPixel type.

If you want a class that encapsulate NeoPixel, you need to declare a pointer to a NeoPixel.
When you use the constructor to create an object Child, you allocate/create an object NeoPixel and assign to _strip.
If you create many Child object every object allocate a new NeoPixel object.

Hi nid69ita,

I looked up some info about pointers and if I understand it correctly, I would have to create a void pointer in my Child class that I can populate with an instance of Adafruit_NeoPixel at runtime.

So my code would look something like this?

#include <Arduino.h>

#ifndef Child_h
#define Child_h

#define NUM_CHILDREN 4
#define NUM_LEDS 3

class Child
{
  public:
    void setPin(unsigned char pin);
    void setStrip(void *strip);
    void setLeds(unsigned char leds[NUM_LEDS]);
    void ledOn (unsigned char led);
    void ledOff (unsigned char led);

  private:
    unsigned char _pin;
    unsigned char _leds[NUM_LEDS];
    void *_strip;
};

#endif
#include "Child.h"

void Child::setStrip (void *strip)
{
  _strip = strip;
}

...
...

Parent myParent(DATA_PIN, leds);
Adafruit_NeoPixel strip = Adafruit_NeoPixel(NUM_CHILDREN * NUM_LEDS, DATA_PIN, NEO_GRB + NEO_KHZ800);

void setup ()
{
  strip.begin();
  strip.show();
  myParent.setStrip(&strip);
}

...

Obviously I'd still need to have a function in the Parent class that passes the strip reference on to the Child instances. I am currently not able to test this, but I was wondering if this the correct way of thinking.

Pointers still confuse me a bit, but if this is how they work I might have a few other parts of my code that I can change to minimise memory usage.

– Wouter

and if I understand it correctly, I would have to create a void pointer in my Child class that I can populate with an instance of Adafruit_NeoPixel at runtime.

NO! Do not use a void pointer when you KNOW the correct type.

Ah, yes... but if I do specify the type the compiler chokes up because the Adafruit_NeoPixel library doesn't have an empty constructor. So how do I get around that?

– Wouter

void is the type, the '*' denotes a pointer. Applied to the Adafruit_NeoPixel type you get:

 Adafruit_NeoPixel*_strip;

AboutWout:
Ah, yes... but if I do specify the type the compiler chokes up because the Adafruit_NeoPixel library doesn't have an empty constructor. So how do I get around that?
– Wouter

The constructor is called only when you create the object. When you declare only a pointer to an NeoPixel object no object is created.
Pseudocode:

NeoPixel * myObj;          // no object created, only a pointer
myObj= new NeoPixel(...);  // create an object using an useful constructor and assign the address to pointer

So if you use a pointer the compiler doesn't try to instantiate the object if you haven't assigned a value to it yet? That is good to know.

nid69ita:
The constructor is called only when you create the object. When you declare only a pointer to an NeoPixel object no object is created.
Pseudocode:

NeoPixel * myObj;          // no object created, only a pointer

myObj= new NeoPixel(...);    // create an object using an useful constructor and assign the address to pointer

Okay, it is becoming clear to me now how pointers work. But just curious: Arduino doesn't have a new operator, right?

I'll test it out and let you know how it turns out.

– Wouter

(Sorry for my English)
All pointers on a system are identical. For a Windows 32 bits a pointer is a long with 32 bits (a word).
The type of a pointer is only to inform the compiler that a pointer will be used to point a certain type of variable/object.
Information useful when you use the pointer to access the memory.

Also for a simple variable type:

char * pChar;      // pointer to a char   only store a pointer
char myChar='X';   // variable allocated and a cell memory to store a char
pChar=&myChar;     // now pChar point myChar

myChar have only one syntax, accessing in read or write the char value.
pChar have two use. You can manipulate in r/w the address (useful in write for an array) but you can also manipulate in r/w what the pointer point. But the compiler check and manipulate the memory according to pointer type.
pChar can manipulate a char, so 1 byte in memory. If pointer is long type (long=32 bits on Arduino) can manipulate a 32 bit (4 bytes).

Arduino doesn't have a new operator, right?

Some versions of the IDE do; some do not.

I don't understand pointer fully (yet), but I've gotten it to work by moving some asterisks and ampersands around.

Adafruit_NeoPixel myStrip = Adafruit_NeoPixel(NUM_DIGITS * NUM_PARTS * NUM_LEDS, DATA_PIN, NEO_GRB + NEO_KHZ800);
Adafruit_NeoPixel * strip = &myStrip;

Parent myParent(DATA_PIN);
setup ()
{
  myParent.setStrip(strip);
}

It took me a while to figure out that to call methods of the Adafruit_NeoPixel object you have to use the arrow notation instead of dot notation.

// _strip.setPixelColor(0, 255, 255, 0);
_strip->setPixelColor(0, 255, 255, 0);
_strip->show();

Thanks for all your help!

– Wouter

AboutWout:

// _strip.setPixelColor(0, 255, 255, 0);

_strip->setPixelColor(0, 255, 255, 0);
_strip->show();

Oh, yes, forgot to tell you !!!
When you work with a pointer to object you need to use -> and not . :grin:

nid69ita:
Oh, yes, forgot to tell you !!!
When you work with a pointer to object you need to use -> and not . :grin:

Where's the fun in having everything handed to you? :stuck_out_tongue:

– Wouter