Using dynamic allocation

Hello Guys.

I already saw a lot of examples in the foruns, and after try all things that I consider to be used in my codes, I'm writing here to ask for help. Sorry if my english is not soo good.

I'm trying to use an Arduino Leonardo to control some stepper motors. I'm using Adafruit featherwings bords as drivers. When I try to declare all my feathers with static allocation I have a problem of SRAM memory. I think that the solution for my case should be change the Arduino board for another with more memory. But I'm trying to use dynamic allocation of memory.

I'm using the void display_freeram() to show me how much I have of free memory after the loop. Also in this code I'm working from 0x60 untin 0x90, but in my full project I will have more than this and that is why my Leonardo can't storage all the feathers declaration.
I use the new operator to call the AdafruitMotorShield method and the delete operator to free the memory, but somehow the memory is becaming small and small in each interaction of my lood section.

Bellow is my code:

#include <Adafruit_MotorShield.h>

void setup() {
   Serial.begin(9600);
}

void loop() {
  for (int k=0x60; k<=0x69;k++){
    Serial.print("k = ");
    Serial.println(k);
    mover(k);
  }
  Serial.print("After: ");
  display_freeram();
}

void mover(uint8_t k){
    Adafruit_MotorShield *AFMS = new Adafruit_MotorShield(k);
    AFMS->begin();
    Adafruit_StepperMotor *M1 = AFMS->getStepper(200, 1);
    Adafruit_StepperMotor *M2 = AFMS->getStepper(200, 2);
    M1->setSpeed(1000);
    M2->setSpeed(1000);
    M1->step(10, FORWARD, DOUBLE);
    M2->step(10, FORWARD, DOUBLE);
    M1->step(10, BACKWARD, DOUBLE);
    M2->step(10, BACKWARD, DOUBLE);
  
  delete AFMS;
}

void display_freeram() {
  Serial.print(F("- SRAM left: "));
  Serial.println(freeRam());
}

int freeRam() {
  extern int __heap_start,*__brkval;
  int v;
  return (int)&v - (__brkval == 0 ? (int)&__heap_start : (int) __brkval);  
}

I think the following guidelines apply here.

and

[edit]

It also seems that the method Adafruit_MotorShield::begin() calls an other method (Adafruit_MS_PWMServoDriver::begin()), which allocates some resources that cannot be released (no destructor or end() method seem to be present). So I presume that this method is supposed to be called only once (in setup()).

1 Like

Thank you for your answer @jfjlaros.

I read your answer in C++ Core Guidelines. A think that R11 is not my case because I am using new and delete in sequence and this is not cause the problem related. The R5 five recomendation I tried to do but with the Leonardo Board that I have it's impossible.

I had looked inside Adafruit_MotorShield::begin() method and I saw that he called another method, but I thought that there is one destructor in it. I believed that change this library will be very hard for me now but I will try or try other solution.

If you have another tip for me I'll be very pleased.

Thank's a lot.

It still applies. Manual memory management, including naked new and delete, has no place in high-level code, even if you make sure to always call delete. Experience teaches us that people forget, or they do not consider all code paths, or they do not consider the propagation of exceptions, etc.

Unless you're running into stack size limitations, there should be no observable difference between the heap-allocated and stack-allocated version, and you should prefer the stack-allocated one.

It may very well be that the class in question is not meant to be instantiated multiple times, as pointed out by @jfjlaros, but that's a separate issue, unrelated to whether the instance has automatic storage duration or whether it is allocated dynamically.

Sorry, but I don't understand what you mean by "you're running into stack size limitations"? It's about something physical or related with the code?
I'll will have to call Adafruit_MotorShield method 28 times and declare 56 Steppers in the end. Whem I call this 28 times in my original code I have the following issue:

Sketch uses 21072 bytes (73%) of program storage space. Maximum is 28672 bytes.
Global variables use 2565 bytes (100%) of dynamic memory, leaving -5 bytes for local variables. Maximum is 2560 bytes.
Not enough memory; see https://support.arduino.cc/hc/en-us/articles/360013825179 for tips on reducing your footprint.
data section exceeds available space in board

Compilation error: data section exceeds available space in board

Is that what you mean by "running into stack size limitations"? I can clearly see that I don't have enough memory by static declaring my variables and that was why I'm tring to do this dynamicly.

And thank you for the help.

You could ask the authors of the library whether or not this is intended behaviour (perhaps link to this discussion for background information). I believe that the memory leak occurs in the highlighted line below.

When this bug (if it is indeed a bug) has been addressed, you should be able to use the class as follows:

void mover(uint8_t k) {
  Adafruit_MotorShield AFMS(k);
  AFMS.begin();
  Adafruit_StepperMotor *M1 = AFMS.getStepper(200, 1);
  Adafruit_StepperMotor *M2 = AFMS.getStepper(200, 2);
  // ...
}

It sounds like you made them global, rather than giving them automatic storage duration. What I'm referring to is

void mover(uint8_t k){
    Adafruit_MotorShield AFMS {k};
    AFMS.begin();
    Adafruit_StepperMotor *M1 = AFMS.getStepper(200, 1);
    Adafruit_StepperMotor *M2 = AFMS.getStepper(200, 2);
    M1->setSpeed(1000);
    M2->setSpeed(1000);
    M1->step(10, FORWARD, DOUBLE);
    M2->step(10, FORWARD, DOUBLE);
    M1->step(10, BACKWARD, DOUBLE);
    M2->step(10, BACKWARD, DOUBLE);
}

However, as mentioned before, this is not supported by the library you're using, you'll have to write your own, that doesn't have a memory leak.

It only applies to very large objects/arrays. Even if you could make it a variable with automatic storage duration, you might still want to allocate it dynamically so you don't consume too much stack memory (as that could overflow the stack). It is not applicable in this situation.

You are right, I was making them global.

I did this way and made some changes in Adafruit_MotorShield library and now it's working.
I add a close method in Adafruit_MotorShield.cpp file as follow:

bool Adafruit_MotorShield::begin(uint16_t freq, TwoWire *theWire) {
  // init PWM w/_freq
  _pwm = Adafruit_MS_PWMServoDriver(_addr);
  if (!_pwm.begin(theWire))
    return false;
  _freq = freq;
  _pwm.setPWMFreq(_freq); // This is the maximum PWM frequency
  for (uint8_t i = 0; i < 16; i++)
    _pwm.setPWM(i, 0, 0);
  return true;
}

/**************************************************************************/
// close method to delete the _pwm object calling when begin method is called.
// Date August 9, 2023
// Author Eng Samuel Araujo
bool Adafruit_MotorShield::close() {
  // init PWM w/_freq
  if (!_pwm.close()){
    return false;
  }
  return true;
}

and in the Adafruit_MS_PWMServoDriver.cpp file I also add a close method as follow:

bool Adafruit_MS_PWMServoDriver::begin(TwoWire *theWire) {
  if (i2c_dev)
    delete i2c_dev;
  i2c_dev = new Adafruit_I2CDevice(_i2caddr, theWire);
  if (!i2c_dev->begin())
    return false;
  reset();
  
  return true;
}

// close method to delete the i2c_dev object created by the begin method
// Date: August 9, 2023
// Author: Eng Samuel Araujo
bool Adafruit_MS_PWMServoDriver::close() {
  if (i2c_dev){
    delete i2c_dev;
    return true;
  }
  return false;
}

My intention with this two methods is to create a destructor method to i2c_dev object. This object is what I belive to be causing my memory leak problem.

My code now is more simple than before, I only have to call the close method in the end of the loop:

for (int k =0x60; k <= 0x67; k++) {
    Adafruit_MotorShield AFMS(k);
    AFMS.begin();
    Adafruit_StepperMotor *M1 = AFMS.getStepper(200, 1);
    Adafruit_StepperMotor *M2 = AFMS.getStepper(200, 2);
    M1->setSpeed(1000);
    M2->setSpeed(1000);
    M1->step(20, FORWARD, DOUBLE);
    M2->step(20, FORWARD, DOUBLE);
    M1->step(20, BACKWARD, DOUBLE);
    M2->step(20, BACKWARD, DOUBLE);
    AFMS.close();
  }

I'll make some test with my steppers, if I find another issue, I'll call for yours help again.
Thank you for all the pacient and help.

What about:

bool Adafruit_MotorShield::close() {
  return _pwm.close(); 
}

instead?

You could consider filing a pull request as a thank you to the community, at the very least the authors of the library are made aware of the issue this way.

1 Like

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.