Array of objects in Arduino/c, add, remove and iterate

Hello,

is there any library or function for using Arrays in Arduino? I need a way to create dynamic array of objects (array) with functions to add and remove element, and a way to iterate through array.

Something like array_push() and foreach loop in PHP.

Currently I have 3 variables (1 integer and 2 arrays) :

int duration = 0;
unsigned long started_time[] = {0L,0L,0L,0L,0L,0L,0L,0L,0L,0L};
int status[] = {0,0,0,0,0,0,0,0,0,0}; // 0 = off, 1 = on

I need to create array of objects containing those 3 variables, as I will have 0+ objects like this, with options to add or remove from array. Bellow you can see pseudo code of what I need to accomplish in Arduino/C: 

$actives = array();
$element1 = array();
$element1["duration"] = 0;
$element1["started_time"] = {0L,0L,0L,0L,0L,0L,0L,0L,0L,0L};
$element1["status"] = 0,0,0,0,0,0,0,0,0,0}
array_push($actives,$element1);
$element = array();
$element2["duration"] = 0;
$element2["started_time"] = {0L,0L,0L,0L,0L,0L,0L,0L,0L,0L};
$element2["status"] = 0,0,0,0,0,0,0,0,0,0}
array_push($actives,$element2);

foreach ($actives as $el) {
  echo $el["duration"];
  foreach ($el["started_time"] as $st) {
    echo $st;
  }
}

Is this possible to do in Arduino? I am new in Arduino/C world :slight_smile:

Unless you can find a third party array library on github, you will have to code your dynamic array yourself from scratch.

Be aware that extensive use of the heap (dynamic memory allocation) is not recommended for Arduino sketches....due to memory fragmentation.

It is better to just declare an array large enough for your needs and just maintain its current size within the total capacity.

you could use a container from the STL

Which Arduino? Surely not an Uno/Nano/Mini/328-chip with 2048 bytes of RAM total?

If you make objects all the same size from a base class then you can make a buffer array and manage the objects in that.

C++ Container Classes ALL have bad behavior when it comes to small RAM environments. Get at least a Mega board (8K with direct-address external-RAM capability) if you're not just piddling.

GoForSmoke:
Which Arduino? Surely not an Uno/Nano/Mini/328-chip with 2048 bytes of RAM total?

If you make objects all the same size from a base class then you can make a buffer array and manage the objects in that.

C++ Container Classes ALL have bad behavior when it comes to small RAM environments. Get at least a Mega board (8K with direct-address external-RAM capability) if you're not just piddling.

External RAM for a Mega? Do you mean so that it extends the space for global variables seamlessly? How?

why not use a Raspberry Pi and C++ with the STL?

boylesg:
External RAM for a Mega? Do you mean so that it extends the space for global variables seamlessly? How?

I have a Rugged Circuits Quadram board for my Mega (an LCD is plugged in now though) with 512KB as 8 banks. The Mega internal RAM gets purposed as stack-only in the low 8K addresses and the top 56KB of each switchable bank as heap.

The RAM board ran $25 + $3 shipping, that was a while ago. QuadRAM — Rugged CircuitsRugged Arduino

They make 12V-tolerant "Ruggeduinos", 24V IO shield, etc.

The datasheet for the 2560 chip is for a line of similar AVRs (IIRC, 4 chips) that have the same feature but less internal RAM, etc.


If you can get by with only 16KB then ATmega1284P DIP chips are less than $6 each less than a year ago. You can breadboard one and get it bootloaded and blinking in very short time by following Nick Gammon's tutorial which does provide software to detect your chip (is that a xxxP-AU or PU, etc, gets the sig number right) and to bootload the chip with fuses set to speed and clock options you want.
While the full walkthrough of every option available for both chips covered is big, the bit of it any one way takes can be done in under an hour even if you move real slow.

======================================================

The thing is that the project may run on a smaller chip using small-environment code methods.

First of all, thank you guys for such a detailed explanaitions. I use MEGA, and currently 25% of dynamic memory is occupied. so there shouldn't be any issues regarding RAM.

It would be fine for me to declare maximum size of 20 for example, so it is not problem. But how to create array of object? add/remove and iterate. Any link or part of code would be useful.

proximus2:
It would be fine for me to declare maximum size of 20 for example, so it is not problem. But how to create array of object? add/remove and iterate. Any link or part of code would be useful.

  • create a static array of pointers to the instances.
  • add to the array each time a constructor is called

for example:

using functionPtr = void(*)(void);
constexpr uint8_t MAX_BUTTON_COUNT = 5;
constexpr uint32_t DEBOUNCE_TIME = 50;

class ButtonPressAction{
  public:
    ButtonPressAction(functionPtr action);
    void bindToPin(uint8_t buttonPin);
    void bindToPin(uint8_t buttonPin, int mode);
    static void update(void);
  private:
    uint8_t pin;
    uint32_t lastPressMillis;
    bool lastState;
    functionPtr callback;
    static ButtonPressAction* instances[MAX_BUTTON_COUNT];
    static size_t instanceCount;
};

size_t ButtonPressAction::instanceCount = 0;
ButtonPressAction* ButtonPressAction::instances[MAX_BUTTON_COUNT] = {nullptr};

ButtonPressAction::ButtonPressAction(functionPtr action){
  callback = action;
}

void ButtonPressAction::bindToPin(uint8_t buttonPin){
  bindToPin(buttonPin, INPUT_PULLUP);
}

void ButtonPressAction::bindToPin(uint8_t buttonPin, int mode){
  pin = buttonPin;
  pinMode(pin, mode);
  instances[instanceCount++] = this;
}

void ButtonPressAction::update(void){
  for (size_t count = 0; count < instanceCount; count++)
  {
    bool currentState = digitalRead(instances[count]->pin) == 0? true: false;
    if (instances[count]->lastState != currentState and millis() - instances[count]->lastPressMillis > DEBOUNCE_TIME)
    {
      if(currentState == true)
      {
        if (instances[count]->callback)
        {
          instances[count]->callback();
        }
      }
      instances[count]->lastState = currentState;
      instances[count]->lastPressMillis = millis();
    }
  }
}

constexpr uint8_t LED_PIN = 13;

void pinTwoAction(void);
void pinOneAction(void);

ButtonPressAction buttonOne(pinOneAction);
ButtonPressAction buttonTwo(pinTwoAction);

bool sensorReadings = false;

void setup() {
  pinMode(LED_PIN, OUTPUT);
  buttonOne.bindToPin(2, INPUT_PULLUP);
  buttonTwo.bindToPin(3, INPUT_PULLUP);
}

void loop() {
  ButtonPressAction::update();
}

void pinOneAction(void){
  Serial.println(F("button one pressed"));
}

void pinTwoAction(void)
{
  sensorReadings = !sensorReadings;
  digitalWrite(LED_PIN, sensorReadings? HIGH : LOW);
}

GoForSmoke:
I have a Rugged Circuits Quadram board for my Mega (an LCD is plugged in now though) with 512KB as 8 banks. The Mega internal RAM gets purposed as stack-only in the low 8K addresses and the top 56KB of each switchable bank as heap.

The RAM board ran $25 + $3 shipping, that was a while ago. QuadRAM — Rugged CircuitsRugged Arduino

They make 12V-tolerant "Ruggeduinos", 24V IO shield, etc.

The datasheet for the 2560 chip is for a line of similar AVRs (IIRC, 4 chips) that have the same feature but less internal RAM, etc.


If you can get by with only 16KB then ATmega1284P DIP chips are less than $6 each less than a year ago. You can breadboard one and get it bootloaded and blinking in very short time by following Nick Gammon's tutorial which does provide software to detect your chip (is that a xxxP-AU or PU, etc, gets the sig number right) and to bootload the chip with fuses set to speed and clock options you want.
While the full walkthrough of every option available for both chips covered is big, the bit of it any one way takes can be done in under an hour even if you move real slow.

======================================================

The thing is that the project may run on a smaller chip using small-environment code methods.

Except that it appears as though it is uses up most of the digital pins 22 - 54

It uses the whole back-end header, IIRC. The RAM is direct 16-bit address, 8 bits data to the CPU plus 3 bits for which bank of 64K to use. It's gonna take pins for control signals, but that's the price of fast parallel data.

Use struct to declare your containers (“typedef” may be unnecessary):

typedef struct MYELEMENT {
  int duration, status[10];
  unsigned long started_time[10];
} MYELEMENT;

Now you can declare a simple array with the data type:

#define NUM_ELEMENTS 10
MYELEMENT MYELEMENTS[NUM_ELEMENTS];

If “duration” is 0 then the element is considered unused / empty. Here is how to find an empty element:

int findEmpty() {
  for (int i = 0; i < NUM_ELEMENTS; i++) if (MYELEMENTS[i].duration == 0) return i;
  return -1; //Return -1 if no empty element was found
}

Should be pretty easy to get on from there :slight_smile: