Dynamic allocating of an array of objects

Hello!

I have a problem with the subject. I’ve been already searching through google and this forum, but haven’t really managed yet, although I think I am on the right track…

I’m sorry for the long code snippets, but I was a bit unsure what to leave out. The dynamic allocation is naturally almost in the end of the second code…

Here’s a code which I got up running nicely, after carefully reading this article: Arduino the Object Way

const unsigned FADE_INTERVAL_MS = 10;                         // some parameters for timing..
const unsigned SENSE_INTERVAL_MS = 10;



class Sensor {
    const int sensorScale = 4;
    const int accTh = 10;
    const int velTh = 200;
    const byte inputPin;
    int value;
    unsigned long PREV_SENSE_MS = 0;

  protected:
    virtual void lightOn(int value) = 0;
    virtual void lightDim(int value) = 0;

  public:
    Sensor(byte attachTo) :                                   
      inputPin(attachTo)                                      // initialize pin for sensor
    {
    }

    void setup() {
      value = readValue();                                    // read the sensors output for the first time
    }

    void loop() {
      int preValue = value;
      int delta;

      if (millis() - PREV_SENSE_MS >= SENSE_INTERVAL_MS) {    // do not read the sensor on every round or the delta never rises
        PREV_SENSE_MS += SENSE_INTERVAL_MS;
        value = readValue();
      }

      delta = value - preValue;                               // delta is the "acceleration" of the sensor

      if (delta > accTh && value >= velTh) {                  // on fast changes turn the LED on immediately
        lightOn(value);
      } else if (value < velTh) {                             // fade out completely, if below threshold
        if (value >= 0) lightDim(0);
      } else if (abs(delta) < accTh && value >= velTh) {      // otherwise follow the sensor value
        lightDim(value);
      }
    }

    int readValue() {
      int state = analogRead(inputPin) / 4;                   // scale the raw value roghly
      return state;
    }
};



class Led {
    const byte outPin;
    int brightness;
    unsigned long PREV_FADE_MS = millis();
    byte fadecycle = 0;

  public:
    Led(byte attachTo) :
      outPin(attachTo)                                        // initialize pin for LED
    {
    }

    void setup() {
      pinMode(outPin, OUTPUT);                                // define pin
      analogWrite(outPin, 0);                                 // turn the LED off
      brightness = 0;
    }

    void loop()
    {
      if (millis() - PREV_FADE_MS >= FADE_INTERVAL_MS) {      // makes fadig visible
        PREV_FADE_MS += FADE_INTERVAL_MS;
        fadecycle = 1;
      } else {
        fadecycle = 0;
      }
    }

    void lightOn(int intensity) {                             // immediate full
      brightness = intensity;
      analogWrite(outPin, brightness);
    }

    void lightDim(int intensity) {                            // dimming the LED
      if (brightness < intensity && fadecycle == 1) {
        brightness++;
      } else if (brightness > intensity && fadecycle == 1) {
        brightness--;
      } else if (brightness == intensity) {
        brightness = intensity;
      }
      analogWrite(outPin, brightness);
    }
};



class LedControlSensor: public Sensor {
    Led &led;                                               // initialize a reference to led

  public:
    LedControlSensor(byte attachToPin, Led &attachToLed) :
      Sensor(attachToPin),                                  // initialize a sensor
      led(attachToLed)                                      // define corresponding led for a sensor
    {
    }
  protected:
    void lightOn(int intensity) {
      led.lightOn(intensity);
    }

    void lightDim(int intensity) {
      led.lightDim(intensity);
    }
};



const byte ledCount = 3;

Led leds[] = {
  Led(11),
  Led(9),
  Led(10)
};

LedControlSensor sensors[] = {
  LedControlSensor(A0, leds[0]),                
  LedControlSensor(A1, leds[1]),               
  LedControlSensor(A2, leds[2])                 
};



void setup() {
  Serial.begin(9600);                                       //
  for (byte i = 0; i < ledCount; i++) {
    sensors[i].setup();
    leds[i].setup();
  }
  pinMode(13, OUTPUT);                                      // for debugging...
  digitalWrite(13, 0);                                      //
}

void loop() {
  for (byte i = 0; i < ledCount; i++) {
    sensors[i].loop();
    leds[i].loop();
  }
}

I am quite happy with this, and I dare to say I uderstand 99% of it… :slight_smile:

Here’s the same code, but I wondered if I could do this with dynamic allocation (just in case if I ever run into situation where I want to use loads of sensors or leds or actually anything…) The code compiles, yet it doesn’t do anything. I think it must be something with theses pointers pointing to god-knows-where, but here I lost the track myself… As source I used mainly the article mentioned before and this discussion on the forum: https://forum.arduino.cc/index.php?topic=590953.0

const unsigned FADE_INTERVAL_MS = 10;
const unsigned SENSE_INTERVAL_MS = 10;

class Runnable {
  public:
    virtual void setup() = 0;
    virtual void loop() = 0;
};

class Sensor: public Runnable {
    const int sensorScale = 4;
    const int accTh = 10;
    const int velTh = 150;
    const byte inputPin;
    int value;
    unsigned long PREV_SENSE_MS = 0;

  protected:
    virtual void lightOn(int value) = 0;
    virtual void lightDim(int value) = 0;

  public:
    Sensor(byte attachTo) :                                   
      inputPin(attachTo)                                      // initialize pin for sensor
    {
    }

    void setup() {
      value = readValue();                                    // read the sensors output for the first time
    }

    void loop() {
      //
    }

    int readValue() {
      //
    }
};



class Led: public Runnable {
    const byte outPin;
    int brightness;
    unsigned long PREV_FADE_MS = millis();
    byte fadecycle = 0;

  public:
    Led(byte attachTo) :
      outPin(attachTo)                                        // initialize pin for LED
    {
    }

    void setup() {
      //
    }

    void loop()
    {
      //
    }

    void lightOn(int intensity) {                           
      //
    }

    void lightDim(int intensity) {                           
     //
    }
};



class LedControlSensor: public Sensor {
    Led &led;                                                 // initialize a reference to led

  public:
    LedControlSensor(byte attachToPin, Led &attachToLed) :
      Sensor(attachToPin),                                    // initialize a sensor
      led(attachToLed)                                        // define corresponding led for a sensor
    {
    }
  protected:
    void lightOn(int intensity) {
      led.lightOn(intensity);
    }

    void lightDim(int intensity) {
      led.lightDim(intensity);
    }
};



const byte ledCount = 3;
const byte runners = ledCount * 2;
byte LED_OUTPINS[] = {11, 9, 10};
byte SENSOR_PINS[] = {A0, A1, A2};



Runnable **runMe;

void setup() {
  Serial.begin(9600);
  pinMode(13, OUTPUT);
  digitalWrite(13, 0);
  byte runnerIndex = 0;

  runMe = new Runnable*[runners];

  for (byte i = 0; i < ledCount; i++) {
    runMe[runnerIndex] = new Led(LED_OUTPINS[i]);
    runnerIndex++;
  }

  for (byte i = 0; i < ledCount; i++) {
    runMe[runnerIndex] = new LedControlSensor(SENSOR_PINS[i], runMe[i]);
    runnerIndex++;
  }

  for (byte i = 0; i < runners; i++) {
    runMe[i]->setup();
  } 
}

void loop() {
  for (byte i = 0; i < ledCount; i++) {
    runMe[i]->loop();
  }
}

I appreciate your help! Also if you have suggestions and intrest in how to improve the code (please explain also why…), I’d love to hear them!

Toni.

With very limited amount of memory available and no mechanism to clean it up an arduino can crash very quickly when Dynamic allocation is used because it causes memory fragmentation. For this reason it is not recommended

Okay, that’s definitely good to know! Just for the pure interest of the C++, how should I write the code to get it working?

Toni.

it would help if you explained what you need help with better rather than pointing to a lengthy article and then some code with changes to the code in the article.

i admit i'm bias. I attended a talk on C++ at Bell Labs where C++ was invented and within the few few sentences the speaker said object oriented code can be done with many language, including assembler but not languages like Fortran or Basic.

contrary to your article, object oriented techniques, while not called that, were in practice a couple decades before the 90s. C++ was in use at Bell Labs when I joined in 1985.

i've had to deal with some C++ code, but apparently not as much as others who disdain it because many C++ programs are unnecessarily complicated because they are poorly written. Tom Cargill wrote a book showing C++ programs and how they are incorrect.

presumably C++ (or any good object oriented language) has significant advantage when used properly in appropriate applications. However, C++'s inventor, Stroustrup said he felt it take 10 years to really understand how to use C++.

re: you title of your post,

malloc() dynamically allocates memory at run time. It can be used to allocate space for variables when the needs are unknown at compile time. C++ has new and delete, but Scott Meyers has written books on their misuse and the causes of memory leaks where memory is allocated but not freed correctly and ultimately causing a run time crash because of the lack of memory.

a common way to use malloc() is to allocate a block of memory. but there is the potential for needing more and realloc() is intended to allocate a larger block of memory and copying the old data to the new space.

another approach it to allocates a structure of data. The structure may be a fixed size array of data plus ancillary information. Such structures can be managed by putting them on a linked list where each structure include a pointer to another structure.

i think a common problem is freeing allocated memory using free(). I think allocated memory in applications such as ours, should never be freed; instead put it aside to be reused instead of using malloc again. with this in mind, there may be a list of structures that are in use and a 2nd list of unused structure that can be used as needed

Hi!

I’m sorry, it might be I was a bit unclear about my aims here, to be honest, I’m not very experienced on coding, yet alone asking good questions about it. Using the right terms is a bit stressful also, since English is not my preferred language.

I’ll try to get my head around that linked link-wiki!

But so, in the end of the second pasted code, I tried to initialize (in this case three) leds to their corresponding sensors, in a dynamic allocating way (at least that’s how I understood it?) compared to the first code which is done less in that way. However I realized, that in every case I have to write the pin numbers by hand (if I want to use specific pins, that is), thus it doesn’t make much sense to do it in a way of the second code. Nevertheless, I’d like to learn to get this working in that manner also…

The first code compiles and works as it is, the second compiles, but doesn’t work (my only guess is that the led and sensor-objects are not binded like they should, something with the references and pointers?).

Toni.

my beef with C++ it is often overly complicated and misused. Even Stroustrup warns readers in his 3rd edition not to use unnecessary features. I think your code examples are examples of just that.

i believe a monitoring module could have a function with sensor and LED arguments to add to the module which are then monitored thru a single call to a function in that module. That module need not have any classes, but could certainly epitomize the concept of object oriented.

runMe[runnerIndex] = new LedControlSensor(SENSOR_PINS[i], runMe[i]);

runMe{i} - on the right hand side - is of type “Runable*” but LedControlSensor() requires type “Led&” as second parameter. To compile you must cast the pointer to the correct type, then dereference it to pass it as a reference.

runMe[runnerIndex] = new LedControlSensor(SENSOR_PINS[i], *(static_cast<Led*>(runMe[i])));

Keep in mind that, runMe{i} now no longer have an owner, so you can’t deallocate it.

Thank you for the reply!

I didn't get it working with that either, but I decided to move on and will try this with something a bit more simplier first! Got to study more about pointers and memory usage. Thank you all for your efforts!

Toni.