I am programming an ESP32 in the Arduino framework. For my application, I need to create a buffer which will store information from both the present and the last time it was accessed. Here is what I am attempting to do.
//first buffer
char buffer1[4];
//second buffer
char buffer2[8];
void setup {
//setup
}
//buffer1 values will change with each iteration of loop from external inputs
//buffer2 must store most recent values of buffer1 plus values of buffer1 from when loop last ran
for example:
**loop first iteration**
void loop {
buffer1[0] = {1};
buffer1[1] = {2};
buffer1[2] = {3};
buffer1[3] = {1};
saveold(); //this is the function I'm trying to implement to save values to buffer2 in an element-wise way
}
//value of buffer2 should now be: buffer2 = {1,2,3,1,0,0,0,0}
**loop second iteration**
void loop {
buffer1[0] = {2};
buffer1[1] = {3};
buffer1[2] = {4};
buffer1[3] = {2};
saveold();
}
//value of buffer2 should now be: buffer2 = {2,3,4,2,1,2,3,1}
From what I've been able to understand through searching online, the "saveold" function I'm trying to make should implement some form of memmove for these array operations
I've tried to piece it together, but I always overwrite the value of buffer2 instead of somehow shifting new values in, while retaining the old ones
This is all I've got:
void saveold() {
memmove(&buffer2[0], &buffer1[0], (sizeof(buffer1[0]) * 4));
}
From my understanding, this copies buffer1 starting from index position 0 to buffer2, starting at index position 0, for 4 bytes (where 1 char = 1 byte).
Computer science is not my backround, so perhaps there is some fundamental solution or strategy that I am missing. Any pointers would be appreciated.
You have multiple options to implement saveold()
:
Solution 1
void saveold() {
// "shift" lower half into upper half, saving recent values (actually it's a copy)
buffer2[4] = buffer2[0];
buffer2[5] = buffer2[1];
buffer2[6] = buffer2[2];
buffer2[7] = buffer2[3];
// copy current values
buffer2[0] = buffer[0];
buffer2[1] = buffer[1];
buffer2[2] = buffer[2];
buffer2[3] = buffer[3];
}
Solution 2
void saveold() {
// "shift" lower half into upper half, saving recent values (actually it's a copy)
memcpy(buffer2 + 4, buffer2 + 0, 4 * sizeof buffer2[0]);
// copy current values
memcpy(buffer2 + 0, buffer1, 4 * sizeof buffer1[0]);
}
Some notes
buffer2
is exactly double size of buffer1
.memcpy()
can be used safely if source and destination don't overlap. memmove()
checks for overlaps and reacts accordingly.&buffer1[0]
is the same as buffer1 + 0
. Feel free to use the expression you better understand.sizeof
is an operator, not a function. So sizeof buffer[0]
evaluates to the size of buffer[0]
. A common and most accepted expression to calculate the size of an array dimension is sizeof buffer1 / sizeof buffer1[0]
. You only need parentheses if you evaluate the size of a data type, like sizeof (int)
.Solution 3
The last note leads directly to this improvement of solution 1:
void saveold() {
// "shift" lower half into upper half, saving recent values
size_t size = sizeof buffer2 / sizeof buffer2[0];
for (int i = 0; i < size / 2; ++i) {
buffer2[size / 2 + i] = buffer2[i];
}
// copy current values
for (int i = 0; i < size / 2; ++i) {
buffer2[i] = buffer1[i];
}
}
To apply this knowledge to solution 2 is left as an exercise for you. ;-)
Using memcpy on a microcontroller is almost never the correct solution. In some 95% of the cases where people use memcpy in such systems, they should be using pointer swaps instead. Though in this specific case, the best solution seems to be keeping a single ring buffer - assuming there aren't hardware restrictions such as DMA enforcing a double-buffer design, which nothing in the question indicates.
@Lundin If you need performance and have enough RAM, that's right. The other way around
memcpy()
is OK. As I wrote, there are more possibilities, and it all depends on the requirements and environment. I just picked up the OP where he is.The OP's code as-is already got multiple buffers, so with minimal changes, the correct solution is to do pointer swaps.
Honestly, the pure simplicity of Solution 1 is very appealing. It also worked very well for my application, so I'm going to stick with it. However, I will try to implement a ring buffer as well (now that I know what it is called), for learning purposes. This has been educational!