I want to try and use an array for the first time in my code.
Objective: Log a single temperature value to an array based on a change of state from each of 3 different digital inputs. If any of the digital inputs change state I want to reference that one temperature input. Retrieve the values in the array when I do a webpage client request. Reset the array or overwrite the existing data values to avoid memory shortage issues.
My UNO WiFi Rev 2 is at about 85% on memory used for the ino files. So I want to avoid any
logging that will come close to maxing out the available memory storage. Since it is only 1 temp input I hope to have some latitude with this array. The temp value will be input approximately every hour or so typically; based on a call for heat from my home heating system and whether my boiler is currently locked out or not. The idea being I want to monitor the temperature fluctuations of my storage tank water as a function of the boiler coming On and going back Off.
Is there a need to reset/clear the array periodically or does the new data just overwrite anything exisiting in that array cell?
I am thinking it is just a simple matter to reference the array element values with the client print command for a webpage display.
how long do you require to keep the sensor readings e.g. a month would be 672
you could use a circular buffer which would maintain readings for a fixed time
what have you got connected to the UNO?
if the UNO does not have sufficient memory (2KBytes of SRAM) move to a Mega 8KBytes of SRAM or even better a ESP32 with 520KBytes of SRAM or save data to a SD card
a common approach is to overwrite earlier values in an array when it is filled.
an index variable keeps track of the next location to write to and a size variables keeps track of the # of value written. the index is set to zero when it reaches the size of the array
reading the array, if the size < size of the array, report values from the zeroth location up to the current index. if size == max, report the # of values values starting the current index value
This will seem like a stupid question perhaps but if I have a timer that periodically
clears/overwrites the array elements with a "0" value is there a noticeable reduction in the memory being used? And is there a register I can access to display remaining memory to ensure the processor is not going to face a shortage?
Maybe you could create a local server, using python code, which there are several examples available, you can 'upload' the values to the server to be handled any way you want. It is really not that difficult.
or
You could set up a web server on the uno and connect to it with your browser from another device and capture the values.
or
Just do a serial print each time the value changes and capture that with serial terminal.
or
or get an sd card shield and save the values to a file.
no - as @sterretje stated array sizes are fixed
on more powerful processor you could use a std::vector but not on a UNO
you would need to timestamp the data when it is saved
you need to design a data structure to hold a data sample
then knowing how many cells you have and how often you save a sample you can work out array size
in post 1 you mention Retrieve the values in the array when I do a webpage client request.
how do you plan to implement the internet connection?
constexpr size_t dataCountPerCycle = 3; // 3 data points to represent a boiler On/Off cycle
struct BoilerData {
int16_t temperatures[dataCountPerCycle];
};
Then you would decide how many cycles you want to be able to maintain in memory
constexpr size_t maxCycleCount = 50;
And create an array of BoilerData with maxCycleCount entries for the recordings
BoilerData historicalData[maxCycleCount];
This array will have a fixed size, known and reported at compile time and your code will just have to store stuff in there (so you’ll need to maintain an index of where you record the next data)
When the array is full (the index would have reached maxCycleCount) then you need to decide what to do. The typical implementation is to throw away the oldest entry.
So I am thinking by what you said that the ino file sizes are irrelevant to the data storage requirements of an array since the array values are maintained in the RAM area...
I like hearing this because if it just returns back to the 1st cell to dump the next value into
that would probably work ok.
I have to start playing with these arrays in the IDE and writing stuff to the serial monitor first.
I was a little surprised there was not much in the IDE Examples pertaining to array applications.
I realize they are kind of ordinary in the programmer world but I lean on code examples to get a thorough understanding of how things work.
One temperature input that will be sent to the array when any one of three digital
inputs changes state.
So if I had an array that accomodated 15 - 18 cells that would give me the readings
for a day as typically the boiler cycles about 4 - 5 times a day when it gets below 50 dgf.
And if I did a tim eo f sday or elaped time reset of the values, say at midnight, that would be
ok. I do not need to record these for a permanent log/record.
In maintaining the index I will need to have some sort of increment counter that advances
the "index value" by one each time another temp value is sent into the array?
So:
temp1 goes into array[0]
increment index value to 1 (starts with 0]
(20 mins later) temp2 goes into array(1]
increment index to 2
(15 more mins) temp3 goes into array[2]
increment index to 3
and so on and so on...
when index value gets to "maxCycleCount" revert index back to "0"....
I am gonna have to bail on trying to use structures with an array inside and all that stuff.
I was unable to get a compile probably due to my misunderstanding of the purpose of "dataCountPerCycle" when defining "temperatures" as an integer.
The other issue was the "historicalData" attachment to "BoilerData" in defining the limit of the array with "maxCycleCount."
I understand things better since doing more reading on the subject and should be able to get a simple array working. Although the use of a structure has advantages over a simple array.
If you could not find examples on how to deal with an array of structures and implement a "circular array" here is something for you to study.
I simplified the structure to make it obvious you record 3 data points (called tempSensor1, tempSensor2, tempSensor3) in the structure.
The code basically defines the structure and an array with maxCycleCount entries (10 in the example) and you have a couple functions
one for adding a new entry in the array
one for printing an entry
one for printing the full array
the main loop just adds an entry with 3 random values and print the full array.
The secret sauce is to know what to print. Until the array is not full, you just need to print from index 0 (the start of the array) to the last entry that was filled to see all the samples but once the array has been entirely filled in then as we overwrite the oldest entry when recording a new one, the start of the array (the oldest value) is no longer at position 0 but it evolves.
have a look at the code and run the simulation and let me know if you have questions.
click to see the code
struct BoilerData {
int16_t tempSensor1;
int16_t tempSensor2;
int16_t tempSensor3;
};
constexpr size_t maxCycleCount = 10;
BoilerData historicalData[maxCycleCount];
size_t nextHistoricalEntry = 0;
bool HistoricalDataIsFull = false;
void addHistoricalData(int16_t sensor1, int16_t sensor2, int16_t sensor3) {
historicalData[nextHistoricalEntry].tempSensor1 = sensor1;
historicalData[nextHistoricalEntry].tempSensor2 = sensor2;
historicalData[nextHistoricalEntry].tempSensor3 = sensor3;
nextHistoricalEntry = (nextHistoricalEntry + 1) % maxCycleCount; // Move to the next index, cycling if necessary
if (nextHistoricalEntry == 0) HistoricalDataIsFull = true; // If nextHistoricalEntry is back to 0, then we kknow the array is full
}
void printHistoricalDataAtIndex(size_t idx) {
Serial.print(idx + 1); // for display start numbering at 1 (in C++ arrays starts at index 0)
Serial.write('\t');
Serial.print(historicalData[idx].tempSensor1);
Serial.write('\t');
Serial.print(historicalData[idx].tempSensor2);
Serial.write('\t');
Serial.println(historicalData[idx].tempSensor3);
}
void printHistoricalData() {
Serial.println(F("\n-----------------------------"));
// print the number of valid samples
if (HistoricalDataIsFull) Serial.print(maxCycleCount);
else Serial.print(nextHistoricalEntry);
Serial.println(F(" Historical Data sample(s)"));
Serial.println(F("-----------------------------"));
Serial.println(F("#\tsensor1\tsensor2\tsensor3"));
if (HistoricalDataIsFull) {
// the array has been fill up so we have to go through all entries
// because we use a circular buffer, the oldest entry is no longer at index 0
// but it is at nextHistoricalEntry (the one we should overwrite)
// so this is our start index, and we want to print maxCycleCount entries
// we use modulo so that our index stays within the bounds of our array
for (size_t i = nextHistoricalEntry; i < nextHistoricalEntry + maxCycleCount; ++i) {
printHistoricalDataAtIndex(i % maxCycleCount); // Ensure the index stays within bounds
}
} else {
for (size_t i = 0; i < nextHistoricalEntry; ++i) {
// the array is not full yet, so we print samples from index 0 to nextHistoricalEntry (not included)
printHistoricalDataAtIndex(i);
}
}
}
void setup() {
Serial.begin(115200);
}
void loop() {
addHistoricalData(random(200), random(200), random(200)); // add a new random entrty
printHistoricalData();
delay(1000);
}
What is the range and precision of the temperatures measured?
(which sensor do you use?)
You could "double" the storage by compressing the values into an uint8_t
That will give you 256 unique values.
If the range is limited e.g. to 10-70 degrees Celsius.
you could store the values in an uint8_t with 0.25 degree C precision.
If the range is limited e.g. to -20 - 100 degrees Celsius.
you could store the values in an uint8_t with 0.5 degree C precision.
Or in Fahrenheit you could do -30 - 212 F in an uint8_t with 1 degree F precision.
If temperatures fluctuate minimal you could apply run length compression.
Run length: instead of 23 23 23 23 23 24 25 25 25 you store 5, 23, 1, 24, 3, 25
A hardware option might be to use an FRAM for the data.
Advantage is that data is stored non-volatile and is kept over restarts.
A 32 K FRAM could store ~16000 measurements
(enough for ~10 days a measurement per minute)
I appreciate your not giving up on me. I did manage to get a simple array working with a digital input prompting another value loaded into the array.
I will definitely check out that link and code you offered as the structure approach has piqued my interest as it seems more adaptable should I choose to add other data types like a timestamp.
One area I am yet to tackle is incorporating this array data into part of my boiler webpage display. That may be cumbersome to do but I am up to the challenge.
I have used circular saws a lot over the years but never a circular buffer. That link was very interesting and some form of that is what I will probably end up using.