how to create an array with both members and number of members undefined

Hi all.
The title may be misleading but I couldn't any better way to describe my question in a single line :slight_smile:
I suspect this to be an easy one for most of you but I'm still a noob...
The thing is I'm building a system that enables me to control a servo, record my data and play it back in several ways
(you can see my ongoing progress here :http://arduino.cc/forum/index.php/topic,96056.0.html).

What I want to do (and couldn't find an answer) is not to limit my array to contain a certain amount of members, but to let my actions later on in the program decide that number for me.

for example: once I click "rec" an array is formed and ends when I click again. from now on, until I record again, this is the array and this is the number of its members.

hope I made my question clear and would appreciate some guidance!

As the memory of the Arduino is limited you should know a maximum size of the "macro" in advance.

strategies I: internal memory

  • fix array, maximum size that allocates all free memory
  • linked list that allocates a new node per action, => dynamic in size but has overhead for node admin which is relative high seen the restricted RAM of the Arduino

strategies II: external memory

  • use EEPROM to store macro (intern is rel small, extern EEPROM up to MB),
  • use SD card and store macro as a textfile -

I would go for the SD card as that would allow me to edit macros on my laptop and let the Arduino execute them later. The Arduino should have a menu system to choose the macro. Further you can have a autoexec.mac macro that will execute automatically if no macro is chosen e.g. after some timeout.

Hope this helps

Soffer:
What I want to do (and couldn't find an answer) is not to limit my array to contain a certain amount of members, but to let my actions later on in the program decide that number for me.

The Standard Template Library (STL) will do that. However as robtillaart says, you have overall limitations.

Hey Rob and nick, thank you for your replies.
I will check further.

Rob - I'll use the internal memory since the maximum members will not surpass 500 integers.
Since I'm new at this your explanation seems a bit abstract to me. Could you point to a specific sketch implementing this?

Nick - I'll search for the STL - hoping I'll understand how to implement it.
EDIT: just checked STL - seems way over my head right now... :blush:

Thanks again
:slight_smile:

maximum members will not surpass 500 integers.

What is the range of the integers? If 0..255 you can use uint8_t instead of int

This is a simplified example, it just records analog samples and after 500 it spits them out

uint8_t array[500];
int index =0;

void setup()
{
  Serial.begin(9600);
  Serial.println("array");
}
 
void loop()
{
  // CAPTURE VALUES IN ARRAY (RECORD)
  uint8_t val = analogRead(A0)/4;  // make val 0..255
  array[index] = val;
  index++;
  delay(10);  // remove this for faster output ...

  // IF ARRAY FULL USE THEM (REPLAY) AND RESET
  if (index == 500)
  {
     index = 0;
     for (int i=0; i< 500; i++) 
     {
     Serial.print("\t");
     Serial.print(array[i]);
     if ( i % 8 == 0) Serial.println();
     }
  }
}

Rob - I may have explained badly what I need - what you kindly wrote is already in my code. I'm attaching it here so you can see:

// Controlling a servo position using a potentiometer (variable resistor) 
// with the option of recording the movement, playing it in reverse and in original order.
// Adi Soffer 19.3.12

#include <Servo.h> 
 
   Servo myservo;                                         // create servo object to control a servo 
   char buffer [5];                                         //variable to hold keystrokes
   int potpin = 0;                                          // analog pin used to connect the potentiometer
   int val;                                                       // variable to read the value from the analog pin 
   const int valNumber = 500;                   //variable to contain number of array members
   int incomingByte;                                  //declare variable to hold incoming letter
   int servoPos[valNumber];                    //create array of values for servo position
   const int waitForServo = 15;               //delay time to let servo get to position 
   int recordModeLed = 3;                              // defining pins for ui leds
   int homeModeLed = 4;
   int playModeLed = 5;
   int freeModeLed = 6;
   int freeSwitch = 7;                                 //defining pins for switches
   int recSwitch = 8;
   int homeSwitch = 9;
   int playSwitch = 10;
   

 
  void setup() 
{
     Serial.begin(9600);
     Serial.flush();
     myservo.attach(2);                                                   // attaches the servo on pin 2 to the servo object 
     pinMode (freeModeLed, OUTPUT);                         //defining LED pins as output
     pinMode (recordModeLed, OUTPUT);
     pinMode (homeModeLed, OUTPUT);
     pinMode (playModeLed, OUTPUT);
     pinMode (freeSwitch, INPUT);                              //defining switch pins as input
     pinMode (recSwitch, INPUT);
     pinMode (homeSwitch, INPUT);
     pinMode (playSwitch, INPUT);
     
 }


  void loop ()
{
   
  digitalWrite (freeModeLed, LOW);
  digitalWrite (recordModeLed, LOW);
  digitalWrite (homeModeLed, LOW);
  digitalWrite (playModeLed, LOW);
  
  if (Serial.available( ) > 0) 
 {
       incomingByte = Serial.read ( );
       if (incomingByte == 'f') 
  {
          Serial.println ("Free mode");
          while (incomingByte == 'f') 
    {
          digitalWrite (freeModeLed, HIGH);
          val = analogRead (potpin);
          val = map(val, 0, 1023, 0, 179);
          myservo.write (val);
          delay (waitForServo);
          if (Serial.available ( ) > 0)
      {
            incomingByte = Serial.read ( );
            if (incomingByte == 'r') 
         {
             recordFunction ( );    
         } 
           else if (incomingByte == 'h') 
       {
           reverseFunction ( );
        }   
           else if (incomingByte = 'p') 
        {
             playBackFunction ( ); 
         }    
       }  
     }
   }  
          else if (incomingByte == 'r')                   // checks if input was r
        {                       
           recordFunction ( );
        }
           else if (incomingByte == 'h') 
       {
           reverseFunction ( );
        }
                 else if (incomingByte = 'p') 
        {
           playBackFunction ( );
           } 
        }
      }

void playBackFunction ( ) 
       {
         digitalWrite (freeModeLed, LOW);
          digitalWrite (playModeLed, HIGH);
          Serial.println ("Playback Function");
            for (int i=0;i<(valNumber - 1);i++) 
          {
             myservo.write (servoPos[i]);                    //reads values from array in original order to servo
             delay (waitForServo);
             Serial.println (servoPos[i]);
          }
           digitalWrite (playModeLed, LOW);   
       }

 void reverseFunction ( ) 
      {
            digitalWrite (freeModeLed, LOW);
            digitalWrite (homeModeLed, HIGH);
            Serial.println ("Home function");
            for (int i=(valNumber - 1);i>0;i--) 
          {
             myservo.write (servoPos[i]);                   //reads values from array in reverse to servo
             delay(waitForServo);
             Serial.println (servoPos[i]);
          }
          digitalWrite (homeModeLed, LOW);
       }
     
void recordFunction ( ) 
{
           for (int c=6;c>3;c --)                                     // three sec countdown to recording
           {
             digitalWrite (recordModeLed, HIGH);
             digitalWrite (c, HIGH);
             delay (700);
             digitalWrite (recordModeLed, LOW);
             digitalWrite (c, LOW);
             delay(300);
           }
           digitalWrite (recordModeLed, HIGH);
           Serial.println ("Record function");            // if so - prints record
           for (int i=0;i<(valNumber - 1) ;i++)                //for loop to store declared values of pot  
        {
              val = analogRead(potpin);                             // reads the value of the potentiometer (value between 0 and 1023) 
              val = map(val, 0, 1023, 0, 179);                             // scale it to use it with the servo (value between 0 and 180) 
              myservo.write(val);                                         // sets the servo position according to the scaled value 
              servoPos [i]=val;                                              // stores val in array "servoPos
              delay(waitForServo);                                      // waits for the servo to get there
              Serial.println(val);                                       // print values for checking
         }  
         digitalWrite (recordModeLed, LOW);
}

In my code the "timing" of the recording function is predefined by the index number. What I'm trying to do is to be able to stop the recording when I decide externally - with a key stroke for example - and not just when the predefined 500 cycle ends. Than, if I stopped the cycle, I need the array to have an index number correct.

Did I manage to explain myself this time? I think most of you do this thing I'm trying to figure out off hand, which may explain why my question might seem redundant.

  1. CTRL-T in the IDE does auto formatting.

a refactor of your main loop to give you an idea how it can be done in terms of a minimal state machine
There are missing parts but seeing your code you must be able to fill the gaps, also some parts can be encapsulated in functions.

give it a try...

#define IDLE 0
#define FREE 1
#define RECORD 2
#define PLAYBACK 3
#define STOP 4
#define REWIND 5
#define FASTFORWARD 6

int idx, lastidx;

#include your setup ++

void loop ()
{
digitalWrite (freeModeLed, LOW);
digitalWrite (recordModeLed, LOW);
digitalWrite (homeModeLed, LOW);
digitalWrite (playModeLed, LOW);

// HANDLE IO
if (Serial.available( ) > 0) 
{
  incomingByte = Serial.read ( );

  switch(incomingByte)
  case 'f' : mode = FREE; Serial.println ("Free mode");  break;
  case 'r': mode = RECORD; break;
  case 's' : mode = STOP; break;
  case 'w' : mode = REWIND; break;
  case 'p': mode = PLAYBACK; break;
  case 'z': mode = FASTFORWARD; break;
  case 'i': mode = IDLE; break;
}

switch(mode)
{
  case IDLE:  // do nothing
    break;

  case FREE:
    digitalWrite (freeModeLed, HIGH);
    val = analogRead (potpin);
    val = map(val, 0, 1023, 0, 179);
    myservo.write (val);
    delay (waitForServo);
   break;

  case RECORD:
    val = analogRead (potpin);
    val = map(val, 0, 1023, 0, 179);
    myservo.write (val);
    delay (waitForServo);
    recordarray[idx] = val;
    idx++; 
    lastidx = idx;
    if (idx == maxidx) mode = FREE;
   break;

  case REWIND:
   idx = 0;
   mode = IDLE;
   break;

  case PLAYBACK:
   for (int i=0; i< lastidx; i++)
   {
    myservo.write (val);
    delay (waitForServo);
   }
   break;

  case FASTFORWARD:
   for (int i=0; i< lastidx; i++)
   {
    myservo.write (val);
    delay (waitForServo/4);  // tune this
   }
  break;
}

}

Rob - thank you, I'll try and implement this.
Thank you very much!
:slight_smile:

I'm sorry but there's still something I can't wrap my head around -
If I declare the array at the beginning like so "int recordarray [idx];" I get "array bound is not an integer constant".
If I declare the array in the function itself - off course I get "not declared in this scope".

I'm missing a fundamental thing here, and I feel quite silly...

You're using a variable (idx) to declare your array. The compiler is complaining that it needs the array size to be known at compile time. Use a number or constant instead.

edit: clarity

which defeats my purpose... :frowning:
I'm trying to create an array that changes its index according to external actions.
What am I missing here?

If you declare an array in that fashion, it must have known (constant) size. To create a variable sized array, you have to use malloc. Memory is short on the arduino though, so as suggested earlier, better to allocate the maximum you need up front - you need not use it all.

I felt inside a loop and I think I finally got what your saying:
the fact that I'm declaring array[500], does not conflict with using what Tom wrote:
recordarray[idx] = val;
idx++;
lastidx = idx;
Right?

My error was in assuming that they conflict...
Thank you

Indeed. The array can be 500 elements long and you can still use your idx variable to traverse it. Just ensure you have a check in there to see that idx does not exceed 499.

Thanks.
This basic thing got me confused and now that you've cleared this up for me it will help tremendously with my sketch.
:smiley:

merged your code and my proposal to something that compiles, you have to check if it does what you want :wink:

There is plenty of room for improvement...

//
//    FILE: servoRecorder.pde
//  AUTHOR: Rob Tillaart
//    DATE: 2012-3-24 
//
// PUPROSE: servo recorder
//

#define IDLE 0
#define FREE 1
#define RECORD 2
#define PLAYBACK 3
#define STOP 4
#define REWIND 5
#define FASTFORWARD 6

int mode = IDLE;
int idx, lastidx;
int maxidx = 500;

int recordarray[500];

#include <Servo.h> 

Servo myservo;                      // create servo object to control a servo 
char buffer [5];                        //variable to hold keystrokes
int potpin = 0;                     // analog pin used to connect the potentiometer
int val;                            // variable to read the value from the analog pin 
const int valNumber = 500;          //variable to contain number of array members
int incomingByte;                   //declare variable to hold incoming letter
int servoPos[valNumber];            //create array of values for servo position
const int waitForServo = 15;        //delay time to let servo get to position 
int recordModeLed = 3;              // defining pins for ui leds
int homeModeLed = 4;
int playModeLed = 5;
int freeModeLed = 6;
int freeSwitch = 7;                                 //defining pins for switches
int recSwitch = 8;
int homeSwitch = 9;
int playSwitch = 10;

void setup() 
{
  Serial.begin(9600);
  Serial.flush();
  myservo.attach(2);                                                   // attaches the servo on pin 2 to the servo object 
  pinMode (freeModeLed, OUTPUT);                         //defining LED pins as output
  pinMode (recordModeLed, OUTPUT);
  pinMode (homeModeLed, OUTPUT);
  pinMode (playModeLed, OUTPUT);
  pinMode (freeSwitch, INPUT);                              //defining switch pins as input
  pinMode (recSwitch, INPUT);
  pinMode (homeSwitch, INPUT);
  pinMode (playSwitch, INPUT);
}


void loop ()
{
  digitalWrite (freeModeLed, LOW);
  digitalWrite (recordModeLed, LOW);
  digitalWrite (homeModeLed, LOW);
  digitalWrite (playModeLed, LOW);

  // HANDLE IO
  // read the pins here and set the right mode ...
  if (Serial.available( ) > 0) 
  {
    incomingByte = Serial.read ( );

    switch(incomingByte)
    {
    case 'f': 
      mode = FREE; 
      Serial.println ("Free mode");  
      break;
    case 'r': 
      mode = RECORD; 
      break;
    case 's' : 
      mode = STOP; 
      break;
    case 'w' : 
      mode = REWIND; 
      break;
    case 'p': 
      mode = PLAYBACK; 
      break;
    case 'z': 
      mode = FASTFORWARD; 
      break;
    case 'i': 
      mode = IDLE; 
      break;
    }
  }

  switch(mode)
  {
  case IDLE:  // do nothing
    break;

  case FREE:
    digitalWrite (freeModeLed, HIGH);
    val = analogRead (potpin);
    val = map(val, 0, 1023, 0, 179);    // val = val * 45/256; //val stays just within an unsigned int.  <==> val * 180/1024
    myservo.write (val);
    delay (waitForServo);  //  should be refactored away -> like blink without delay
    break;

  case RECORD:
    val = analogRead (potpin);  // similar code ==> function?
    val = map(val, 0, 1023, 0, 179);
    myservo.write (val);
    delay (waitForServo);
    recordarray[idx] = val;     // recordValue(val); function here?
    idx++; 
    lastidx = idx;
    if (idx == maxidx) mode = FREE;
    break;

  case REWIND:
    idx = 0;
    mode = IDLE;
    break;

  case PLAYBACK:
    for (int i=0; i< lastidx; i++)  // reused in fastforward => function?
    {
      myservo.write (val);
      delay (waitForServo);
    }
    break;

  case FASTFORWARD:
    for (int i=0; i< lastidx; i++)
    {
      myservo.write (val);
      delay (waitForServo/4);  // tune this
    }
    break;
  }
}

Soffer:
If I declare the array at the beginning like so "int recordarray [idx];" I get "array bound is not an integer constant".

I can't say sure for Arduino but something like this usually works in those cases:
int recordarray[(const int) idx];

BTW, you can get serial ram chips that work on SPI bus. They are pretty cheap in the 8k bytes range.

Wow Rob, you've put in so many things that are new to me that even if it works -
It'll take me some time to figure out how the heck it does!
:wink:
Thank you very much for this, it'll help me continue my learning of this great board and it's capabilities!

The most important you should see is that there is a part that handles the IO (serial, could also be the bottons/switches) and sets the operating mode (state).
The second part of the loop() handles the right state one value per iteration of loop().

This 'pattern' can be used in all kind of apps. instead of user IO you could also read sensors and change state according to the value of a sensor.

sample

// READ SENSOR
x = readCO2();

// SET STATE
if (x > 100) state = DOOROPEN;
if (x < 90) state = DOORCLOSED;

// EXECUTE STATE
if (state==DOOROPEN) digitalWrite(doorpin, HIGH);
if (state==DOORCLOSED) digitalWrite(doorpin, LOW);

Statemachines can be very complex but it is also a very powerful technique to solve many projects.

This is advanced and very interesting to me - thanks.
I'll read around site more about this to understand what seems to be a very important technique for my next project as well.
Thank you!