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.
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
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.
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?
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
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.
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.
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
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.
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.
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
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?
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
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.