Dynamic Array Options

Hi,

I'm planning some code that will create an array of values, then the array will be analysed, descriptive statistics will be appended to a log.

What I have currently is an outline and just picks up on state change.

int R1_Pin = A1; 
int R1_ADC; 

int POWER_STATE;
int last_POWER_STATE;

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

 if (R1_ADC > 1) {   
    POWER_STATE = 1;
  }   else {
        POWER_STATE = 0;
      }

  if (POWER_STATE == 1) { 
    while (POWER_STATE == last_POWER_STATE) {  
  }}

// Part 1 - Create my array
// Part 2 - Descriptive statistics
// Part 3 - Append to log

In reverse Parts 2 and 3 I don't need advice on, it is just the array and managing it.

@jremington published some code that takes a sample (hardwritten) array of 4 numbers, and outputs mean and standard deviation. That was useful and I can easily adapt it and deal with the appending to logs.

That leaves me with the creation of the array, and control of it that I am thinking about my strategy and reading up on the options for.

a) The sample rate / resolution shall be 1 second.
b) The value incoming to the array is specified in the code above - R1_ADC
c) The size of the array will be dynamic but I know that it will be around 5 minutes (300 values).
d) I don't want to do any analysis on the array until it is all gathered.

It is these dynamic aspects of array building I need to try and understand more to write the code.

Thanks

working on microcontrollers with small aounts of RAM it is wise to avoid dynamic memory allocation otherwise you can fragment the statck and run out of memory.
Why not define a global static array such as

  int data[1000];

you can always check for array overrun before adding elements
if you wish to analyse the data as it is acquired you can use a ring buffer

2 Likes

If you're using an ESP or ARM-based "Arduino" board, then check out std::vector.

1 Like

Hi @horace

Thanks for your feedback.

As the time series is second resolution and it will run for about six minutes I want to gather all of the values then analyse them, then write their summary descriptives to the log, then only the log will be taking up space.

I think in terms of enhancing my understanding it would be useful to understand both ways, possibly with the dynamic array having some protection on it to stop it being a problem. For example if R1_ADC was to stick "ON" for whatever reason then perhaps we could also set up a maximum number of values, based on second TSV then for me giving it twenty minutes or 1200 samples maximum would be more than double expected anyway.

Can you feed back on how to do both? It is important for me in terms of the learning because the load profile it is gathering information on fluctuates so I am going to try and use a "complete on" full set of second values to characterise that in the logs. For example in the statistics I will break them down into equally numerous subsets and compare the subsets over time and under different operating conditions.

what kinda Arduino board?

This

Why not define a global static array such as…

From @horace makes the most sense. If you ever need the memory, it will always have been available to you, so why not just carve out a def big enough array right at the strat?

a7

2 Likes

while loops are very dangerous as they fail if the test condition can not change inside the loop.
Usually, code becomes easier wihout nested loops. Just use the loop() function in a flat way and avoid blocking

2 Likes

i think you misunderstand the term "dynamic memory".

i think you want a capture buffer that is post processed. it can then be used again for another scan

2 Likes

you can use the c_function_malloc or as @gfvalvo suggests std::vector (however, I have used vector extensively but not on microcomputers)

1 Like

Hi @gcjr

I think I was starting off saying dynamic array then @horace mentioned "avoid dynamic memory". My use of that wording was based on point (c) in my original post, as the sample size varies every time, so dynamic in terms of my language use in the first post relates to the unknown size.

The way you explain it saying "a capture buffer that is post processed" that can be used again is exactly what I am trying to do in terms of philosophy.

Hi @gfvalvo, and @alto777, thanks for your messages, the board is Nano BLE33 Sense, nRF528x Mbed.

@michael_x, thanks for your comment.

Then, wow more responses whilst I'm writing, thanks @horace - OK, so we have c_function_malloc, and @gfvalvo std::vector, cool, and @alto777 mentioned defining a "global static array", and @gcjr "a capture buffer that is post processed".

I need to go and read some more about these options, but @gcjr I think you hit the nail on the head in terms of defining the strategy or approach in plain English terms. If you can define that any further with recommended reading or link to an example(s) that would be much appreciated.

Best wishes,

with 256KB of SRAM you have plenty of memory (for a microcomputer)
you could double buffer be filling one buffer with new data while processing the other or a ring buffer or dynamic memory

1 Like

Interesting @horace, thank you.
OK I will be able to work something out I think.
But @gcjr if you have any further pointers in terms of clarification of the approach you suggested please let me know.

Best wishes,

as @horace sugested, define a buffer, array large enough for your needs. presumably it has an indexed initialized to zero and incremented after each sample is captured.

it's unclear what triggers your capturing of samples, perhaps some I/O pin changing state

when the Nth sample is captured, post process the samples in the buffer and do with them as needed. at that point the index is reset to zero and the code is ready to repeat.

1 Like

Hi @gcjr @horace, and others who helped before,

I developed the first part of the code, it works, and it gathers.
I just need to add something that covers IF (i>=ArraySize) but can do that later.
Two small questions if you can help me out that would be great :slight_smile:

One is about architecture and the behaviour of the code. Where in void Populate_Array() I have commented out various references to R1_ADC they don't work, but if I use analogRead(R1_Pin) instead it does. Why is this? It works in part but it is a static and fixed value that becomes locked.

The last question is about handling the transition to analysis. Once the loop is stopped I want to copy it out, scrub off the null values, sort, analyse. Can you recommend the best way to do that? What I had though of is to create a second array called unsigned int Gathered_Array_Values[GatheredArraySize], then to count the nulls in Array_Values to specify GatheredArraySize.

Thanks for your help!

PS: No bashing me over use of Strings, they are only for development and debugging!!!

const unsigned int R1_Pin = A1; // ADC pin reference
const unsigned int ArraySize = 1200; // 1200x 1 second readings 
unsigned int R1_ADC; // ADC variable (0-1023)
unsigned int Array_Values[ArraySize] = {666, 777, 888, 999}; // Array set up with some POST int values
unsigned int i = 0;

boolean Flag_RunOnce_1 = false;
String POWER_STATE;
String last_POWER_STATE;

String Str_LOAD_MEAN_Log = "Averages MEAN during ON States: ";  //later use

void setup() {
Serial.begin(9600); // initiate serial @9600
}

void loop()
{

  /////----- FIRST INITIATE THE R1 TO HAVE SOMETHING TO WORK WITH
  R1_ADC = analogRead(R1_Pin);  

  /////----- SECOND SET CURRENT POWER_STATE AND last_POWER_STATE - PRINT FOR DEBUGGING 
  if (R1_ADC > 1) 
  { 
    POWER_STATE = "ON"; 
    RunOnce_POST_Init();
  } 
    else 
    { POWER_STATE = "OFF";
      RunOnce_POST_Init();
    } 

  /////----- THIRD CHECK FOR POWER STATE CHANGE AND NOTIFY
  if (POWER_STATE != last_POWER_STATE) 
  { 
    if (R1_ADC > 1) 
    {  
      String Str1_POWER_STATE = "Power State Changed To: ";
      Serial.println(Str1_POWER_STATE + POWER_STATE);
      last_POWER_STATE = "ON";
      //Print_Array();
      Populate_Array();
    } else 
      {
        String Str1_POWER_STATE = "Power State Changed To: ";
        Serial.println(Str1_POWER_STATE + POWER_STATE);
        last_POWER_STATE = "OFF";
      }
  }
}

void RunOnce_POST_Init() 
{
  if (Flag_RunOnce_1 == false)
  {
    delay(7500);
    String Str1_POST_POWER_STATE = "POST Power State Initiation: ";
    String Str2_POST_ADC_Reading = ", ADC: ";
    Serial.println(Str1_POST_POWER_STATE + POWER_STATE + Str2_POST_ADC_Reading + R1_ADC);   
    Print_Array();
    Flag_RunOnce_1 = true;
  }
}

void Populate_Array()
{ 
  //R1_ADC = analogRead(R1_Pin);  

    for (unsigned int i=0; i<ArraySize; i++)
    {
      Array_Values[i] = analogRead(R1_Pin);
      //Array_Values[i] = R1_ADC;
      delay(1000); //wait 1 sec
      String Str1_Loop_ADC_Reading = "ADC: ";
      Serial.println(Str1_Loop_ADC_Reading + analogRead(R1_Pin));
      //Serial.println(Str1_Loop_ADC_Reading + R1_ADC);   
      Print_Array();
    }
}  

void Print_Array() 
{
  Serial.print("Array_Values = [");  
    for (unsigned int i=0; i<ArraySize; i++) 
    {
      if (i!=0) 
      {
       Serial.print(", ");
      }
      Serial.print(Array_Values[i]);
    }
  Serial.println("]");
}

in Populate_Array() you assign analogRead(R1_pin) to R1_ADC then use that value in the loop assigning it to the array elements - how can it change - you do not assign it a new value

void Populate_Array()
{ 
   R1_ADC = analogRead(R1_Pin);    // assign R1_ADC here
    for (unsigned int i=0; i<ArraySize; i++)
    {
      Array_Values[i] = R1_ADC;   // use that value here
      delay(1000); //wait 1 sec
      String Str1_Loop_ADC_Reading = "ADC: ";));
      Serial.println(Str1_Loop_ADC_Reading + R1_ADC);   // and here
      Print_Array();
    }
}  

what does scrub off the null values mean?
there are many sort routines in libraries - what type of sort are you looking for?

1 Like

Hi @horace, thanks a lot for your comments.

Let me explain the first question more, because in a round about way I already had exactly what you suggested.

If I use the following code it doesn't work, and R1_ADC becomes constant and never changes.

void Populate_Array()
{ 
  R1_ADC = analogRead(R1_Pin);  

    for (unsigned int i=0; i<ArraySize; i++)
    {
      Array_Values[i] = R1_ADC;
      delay(1000); 
      String Str1_Loop_ADC_Reading = "ADC: ";
      Serial.println(Str1_Loop_ADC_Reading + R1_ADC);   
      Print_Array();
    }
}  

BUT when I use this one it does

void Populate_Array()
{   
    for (unsigned int i=0; i<ArraySize; i++)
    {
      Array_Values[i] = analogRead(R1_Pin);
      delay(1000); 
      String Str1_Loop_ADC_Reading = "ADC: ";
      Serial.println(Str1_Loop_ADC_Reading + analogRead(R1_Pin));
      Print_Array();
    }
}  

RE: Post DAQ Handling >>

I can handle the sorting, stats and analysis part, but it is specifically about dealing with the array. Once the state changes I want to first of all preserve it by moving it out, in the process of which scrub off the zeros, then also leave the original array empty ready for the next time it is in use.

Removing the zeros is just housekeeping for space and analysis and I guess I could loop them out, or use something that copies it as a memory space into another. In my code I already have void Print_Array() which produces an output with the full array, perhaps I can use the same code but exchange Serial.Println with something that is equivalent to updating a variable. That's the question I guess. If it was a variable I could just update it, var = var, but with arrays, is there a best practice for that? I can scrub the empty data at whichever point its not a big deal, before, during, or after preservation.

in your first code analogRead(R1_Pin); is called once before the for() loop and assigned to R1_ADC
within the for() loop R1_ADC does not change and its (fixed) value assigned to all array elements
in the second code analogRead(R1_Pin); is called on every loop and assigned to the array elements hence any changes in the analog value are reflected in the array element values

1 Like

I see @horace yes that makes sense, I used it in the other loop above which was started first.
thanks.

consider the following output and code

  • test the code with a small # of samples and shorter sample period
  • don't understand why you trigger on the analog value being > 1, what prevents it from repeatedly capturing data
  • i use awk to post process lots of things. i've found that having data on separate lines is often easier to process
capture
176
176
182
184
185
197
202
199
193
182
182
175
171
163
151
151
140
136
131
124
// capture analog data

const byte AdcPin = A0;
const byte ButPin = A1;

#define N  20
int buf [N];

#define StartThresh   0
#define SamplePeriod  500

// -----------------------------------------------------------------------------
void
capture ()
{
    Serial.println (__func__);
    for (unsigned n = 0; n < N; n++)  {
        buf [n] = analogRead (AdcPin);
        delay (SamplePeriod);
    }
}

// -----------------------------------------------------------------------------
void
disp ()
{
    for (unsigned n = 0; n < N; n++) 
        Serial.println (buf [n]);
}

// -----------------------------------------------------------------------------
void loop()
{
    if (LOW == digitalRead (ButPin))  {
        capture ();
        disp ();
    }
}

// -----------------------------------------------------------------------------
void setup()
{
    Serial.begin(115200);
    pinMode (ButPin, INPUT_PULLUP);
}

1 Like

Hi @gcjr, thank you

re: R1_ADC > 1, it is part of a bigger program and it's temporarily used. Basically everything in void loop() was already written so I just copied and pasted it then added the single line Populate_Array();

Regarding the downstream analysis Ive been using R and Python PANDAS a bit so I want to learn more about what C++ has to offer and how they differ, that will be the next part of the work once I have the array copying bit sorted. Thanks for the tip on awks I will read about it.

to remove zeros loop thru the array, if a 0 is found move the next element into that location
something like

int d[]={1 ,2, 3, 0, 4, 5, 0, 0, 6, 7, 0, 8 ,9};

int main(void) {
  int size=sizeof(d)/sizeof(int);
  printf("array size %d\n", size);
  for(int i=0; i<size; i++)
     printf("d[%d]=%d ", i, d[i]);
  printf("\n");
  int out=0;
  // remove 0's in array
  for(int i=0; i<size; i++)
    if(d[i] != 0)  d[out++]=d[i];
  size=out;
  printf("zeros removed size %d\n", size);
  for(int i=0; i<size; i++)
     printf("d[%d]=%d ", i, d[i]);
}

a run gives

array size 13
d[0]=1 d[1]=2 d[2]=3 d[3]=0 d[4]=4 d[5]=5 d[6]=0 d[7]=0 d[8]=6 d[9]=7 d[10]=0 d[11]=8 d[12]=9
zeros removed size 9
d[0]=1 d[1]=2 d[2]=3 d[3]=4 d[4]=5 d[5]=6 d[6]=7 d[7]=8 d[8]=9

note that the array length which hold data changes from 13 to 9 elements

1 Like