1

I have a class defined as below, it's basically a simple ring buffer, with some methods for getting useful statistics from the data held in the buffer.

enum BalanceMsgType{
  BAL_MSG_NOT = 0,
  BAL_MSG_READING = 1,
  BAL_MSG_SPECIAL = 2,
  BAL_MSG_ERROR = 3
};

typedef struct MeasuredMass{
  float mg;
  unsigned long time;
  bool steady;
  BalanceMsgType type;
};

class RingBuffer{
  private:
  unsigned int _size = 0;
  unsigned int _pos = 0;
  MeasuredMass *_array;
  void LinearRegression(){
    //... left out for simplicity
  }
  public:
  float lin_reg_m = 0;
  float lin_reg_c = 0;
  float lin_reg_r = 0;
  float est_current_mg = 0;
  //methods
  RingBuffer(unsigned int size){
    _size = size; //forgetting to put this line ->div0-> >1hr debugging :(
    _array = new MeasuredMass[_size];
    for (int i = 0; i < _size; i++){
      _array[i].mg = 0.0;
    }
  }
  void Add(MeasuredMass in_element){
    _array[_pos] = in_element;
    _pos = (_pos + 1)%_size;
    LinearRegression();
    est_current_mg = lin_reg_m*(in_element.time+BALANCE_DELAY) + lin_reg_c;
  }
  void ReadInOrder(MeasuredMass *pdata){  //Call with 
                                          //my_buffer.ReadInOrder(data)
    for(int i = 0; i < _size; i++){
      pdata[i]=_array[(_pos+i+1)%_size];
    }
  }
  float Sum(){
    //...
  }
  float Mean(){
    //...
  }
  float StdDev(){
    //...
  }
  bool SteadyEnough(float max_sd){
    //...
  }
};

It compiles fine, and runs nicely in test programs. However, if I include it in larger programs (something like progmem 117404 of 122880 bytes, global vars 13604 of 32768) I get a "Board at COM8 not available" warning when using the Arduino Serial Monitor after uploading to my Fubarino Mini. (Even without actually interacting with those programs beyond creating an instance of the class.) Recent experience tells me this is some kind of crash due to a runtime error.

Fiddling around with the code, I've found that the issue doesn't occur in a version of the library which holds an array of floats instead of my MeasuredMass. That my MeasuredMass struct is obviously much larger than a float suggests to me that this is due to some pointer/array overflowy behaviour.

I've also managed to show that if I alter the class such that it uses a fixed length array rather than a pointer to one, the issue disappears.

i.e. RingBuffer's private member MeasuredMass *_array; becomes MeasuredMass _array[25]; and

RingBuffer(unsigned int size){
  _size = size; //forgetting to put this line -> div0 -> >1hr bugtracking
  _array = new MeasuredMass[_size];
  for (int i = 0; i < _size; i++){
    _array[i].mg = 0.0;
  }
}

becomes

RingBuffer(){
  for (int i = 0; i < _size; i++){
    _array[i].mg = 0.0;
  }
}

I'd rather maintain the ability to set the size of the ring buffer at runtime (in my case it's set based upon the number of seconds' worth of data requested), so can anyone help me in figuring out how I can achieve this without running into the overflow / memory problems I seem to be having?

Thanks

1

The problem is most likely here:

_array = new MeasuredMass[_size];

You're allocating _size * sizeof(MeasuredMass) bytes in the heap. If _size is too big the amount of memory allocated becomes too great and it fails to allocate. You should check if _array is NULL or not after the new. If it's NULL then it failed to allocate.

The problem is that heap allocation is limited to 2kB. This is regardless of the amount of memory you actually have in the chip. This is a limit imposed by the compiler (actually the libraries bundled with the compiler) and is set by a value in the linker script. It's something I've been campaigning to have removed from the libraries for many years, but it's always fallen on deaf ears. So much so that I provide a "fixed" version of the compiler in UECIDE that removes this hard limit.

The best thing you can do is to not use dynamic allocation. Instead have a statically created array which you then pass to the constructor.

MeasuredMass massBuffer[25];
RingBuffer massRing(massBuffer, 25);

and:

RingBuffer(MeasuredMass *buf, unsigned int size){
  _size = size; //forgetting to put this line ->div0-> >1hr debugging :(
  _array = buf;
  for (int i = 0; i < _size; i++){
    _array[i].mg = 0.0;
  }
}
  • Thank you! I was just in the middle of adding a comment to my original question, wondering if allocating a buffer just before calling the constructor and passing it through would work. But you beat me too it. On the compiler hard limit - the more I use the Arduino IDE, the more I find that I don't want to use it anymore... – JRVeale Jun 27 '18 at 14:47
  • You should move over to uecide for chipKIT stuff... You get better support ;) – Majenko Jun 27 '18 at 14:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.