SImple array shifting

Sorry to have ask what I thought would be such a simple question, but Im trying to shift the contents of 5 ‘slot’ array such that I move the contents of 1 into 0, 2 into 1, 3 into 2, 4 into 3 and then overwrite 4 with a new value read from a RTC.

Things cycle, but after refilling the array the array seems to contain all the same time values (the latest one). I just cant see where I’ve gone wrong…

Thanks, this is driving me nuts as I just cant see what I’ve done wrong.

TIA

Here’s my code:

char* Top_Open_Times[5]={"00:00","00:00","00:00","00:00","00:00"}; //define the array of times

//every minute this section runs:

//move each array slot one up a slot save the current time into the 5th Array slot, slot 4. 
                for(int i = 0; i < 4; i++) //count up from 0 to 3 (4 array slots, 0-3)
                {               
                Top_Open_Times[i] = Top_Open_Times[i + 1]; //Move the array contents one slot to the left. 
                //move 1 into 0, 2 into 1, 3 into 2, 4 into 3 
                } 
               readTime(rtc_time);//get the current time
               Top_Open_Times[4] = rtc_time; //save this in slot [4]
 
               for(int i = 0; i < 5; i++) //count up from 0 to 4 (5 array slots, 0-4)
                {                       
               Serial.println (Top_Open_Times[i]);               
                }

Here's what I get:

00:00 00:00 00:00 00:00 22:13

00:00 00:00 00:00 22:13 22:13

00:00 00:00 22:13 22:13 22:13

00:00 22:14 22:14 22:14 22:14

but Im trying to shift the contents of 5 'slot' array such that I move the contents of 1 into 0, 2 into 1, 3 into 2, 4 into 3 and then overwrite 4 with a new value read from a RTC.

Don't do that; it's silly, and a waste of processor cycles. Move the indices, not the contents of the array

OK thanks for the pointer.

I dont understand what it means but it at least gives me something else to try to search for.

I'm in Arduino kindergarten so still learning via the help pages. https://www.arduino.cc/en/Reference/Array

I dont understand what it means

Google “circular buffer”.

Think of a ring with 5 notches in it that you can stick cards in. If there is nothing in a notch, you can put a card there. If there is already a card there, you erase it and write the new data.

The real problem, though, is that you have an array of pointers. You keep changing the data that the pointer points to, and adding the pointer to the array. Of course all elements in the array are going to print the same value, because you have 5 copies of the pointer, each pointing to the same data.

You have to copy the data being pointed to, creating a new pointer (strdup() does that), and store the new pointer in the array. Before you throw away the pointer in position 0, by copying the pointer in position 1, you need to free the memory that the pointer points to (using free()).

Thanks PaulS,

Nice analogy. I had to read your post v-e-r-y s-l-o-w-l-y (and I think my lips were moving… :o ) but it helps me understand the concept of the array a bit better and why shifting the contents isn’t as straightforward as I thought. So after a bit of head scratching, (a lot of googling) I set up what I thought was a circular buffer, I ended up with this code:

            char* Top_Open_Times[5]={"00:00","00:00","00:00","00:00","00:00"};        // empty times 
            int slot = 0; //Top_Open_Times circ buffer current slot    


                readTime(rtc_time);//get the time from the RTC

                Top_Open_Times[slot++] = rtc_time; // place the value in the next slot.
                if (slot == 4) slot = 0; //if we've reached the end of the array, so start at the beginning


//Lets see what we have 
               for(int i = 0; i < 5; i++) //count up from 0 to 4 (5 array slots, 0-4)
                {                       
               Serial.println (Top_Open_Times[i]);               
                }
               Serial.println ("");

//Oh dear. Disappointment

…but it behaves as before.

Im now 6 hours in doing something that I thought was trivial without success, so I need to sleep on it…

int slot = 0; //Top_Open_Times circ buffer current slot    


                readTime(rtc_time);//get the time from the RTC

                Top_Open_Times[slot++] = rtc_time; // place the value in the next slot.

Need context. Post complete code, always. If you just set slot to 0, how will you ever put anything in any slot other than the 0 slot?

                Top_Open_Times[slot++] = rtc_time; // place the value in the next slot.

rtc_time is a pointer. The address of the pointer does not change. You keep making copies of the address. All the copies of the address point to the same memory location.

It's like running a page through a copier, and wondering why the copies are all the same.

Paul hits it on the nose; your array copy is fine, but you're shifting multiple copies of the same thing. (Because in C , quoted strings default to being pointers rather than strings themselves, which is less than obvious, especially the way you've implemented it. things would be easier if you can get the time as an integer representation, and convert it to the printable form "later."

This is a problem crying out for a C++ Object solution. There’s probably a standard STL container that contains too many bells and whistles to be appropriate for an Arduino, but you can try putting this “timelist.h” in the folder with your sketch and using it:

/*
    timeList class maintain a list of N times
    Use it like:
       timeList<5> myTimes;
         :
       myTimes.addTime("00:00");
    You can add a new time with the addTime method.  It will push the
     oldest time (N calls ago) off the end of the list.
       myTimes.getTime(5);
    You can retrieve any of the last N times with the getTime method.
       getTime(5) is the most recently added time, getTime(0) is the oldest
*/

timeQueue.zip (1.41 KB)

Delta_G:

int slot = 0; //Top_Open_Times circ buffer current slot    

readTime(rtc_time);//get the time from the RTC

Top_Open_Times[slot++] = rtc_time; // place the value in the next slot.




Need context. Post complete code, always. If you just set slot to 0, how will you ever put anything in any slot other than the 0 slot?

Apologies, confusing I realise that now.

Here’s a summary what I have:

void setup()
{
const unsigned int reedPin=2;  //the digital pin connected to the Reed sensor's output
char rtc_time[17];             // string to store time from RTC
char* Top_Open_Times[5]={"00:00","00:00","00:00","00:00","00:00"};        // 5 last Top Gate OPEN Times               
int slot = 0; //Top_Open_Times current slot
}


void loop()
{

       if(digitalRead(reedPin) == HIGH){
               
                readTime(rtc_time);//get the time from the RTC function and assign to the variable rtc_time 

                Top_Open_Times[slot++] = rtc_time; // place the value in the next slot.
                if (slot == 4) slot = 0; //if we've reached the end of the array, so re-start at the beginning
 
               for(int i = 0; i < 5; i++) //count up from 0 to 4 (5 array slots, 0-4)
                {                       
               Serial.println (Top_Open_Times[i]);               
                }
               Serial.println ("");
}

The readTime function is not my code its part of the RTC library and returns a formatted string showing HH:MM

rtc_time is a pointer.

Suppose that you have a white board. You want to know what time it is; someone writes the time on the board, and gives you a pointer that points at the white board.

Later, you want to know what time it is again. Someone writes the time on the board, and gives you a pointer that points at the white board.

Later, you want to know what time it is again. Someone writes the time on the board, and gives you a pointer that points at the white board.

Later, you want to know what time it is again. Someone writes the time on the board, and gives you a pointer that points at the white board.

Later, you want to know what time it is again. Someone writes the time on the board, and gives you a pointer that points at the white board.

Now, you have a bunch of pointers, BUT THEY ALL POINT TO THE SAME WHITE BOARD THAT HAS ONE TIME WRITTEN ON IT.

Now, do you see the problem?

You need to COPY the data from the white board to another white board, and point each pointer at a different white board, if you want to keep tack of the different times.

I kinda like westfw’s idea of doing the time conversions later. The circular buffer idea would work well, too. Another alternative is to slide the data in the array. The bad part of this approach is that it does take time to move the data, but it’s simple to understand. Here’s an example:

#define ELEMENTS(x)  (sizeof(x) / sizeof(x[0]))   // macro to get element count of any array

void setup() {
  int rawData[] = {1,2,3,4,5};     // Test data
  int index = 6;
  int elementCount = ELEMENTS(rawData);

  Serial.begin(9600);

  for (int k = 0; k < 20; k++) {
    memmove(rawData, &rawData[1], sizeof(rawData)); // Slide data down 1 element
    rawData[elementCount - 1] = index++;            // Add new data
    ShowData(rawData, ELEMENTS(rawData));           // Show it
  }
}

void ShowData(int n[], int elements)
{
  int i;
  for (i = 0; i < elements; i++) {
    Serial.print(n[i]);
    Serial.print("   ");
  }
  Serial.println();
}

void loop() {
}

westfw:
This is a problem crying out for a C++ Object solution. There’s probably a standard STL container that contains too many bells and whistles to be appropriate for an Arduino, but you can try putting this “timelist.h” in the folder with your sketch and using it:

/*

timeList class maintain a list of N times
    Use it like:
      timeList<5> myTimes;
        :
      myTimes.addTime(“00:00”);
    You can add a new time with the addTime method.  It will push the
    oldest time (N calls ago) off the end of the list.
      myTimes.getTime(5);
    You can retrieve any of the last N times with the getTime method.
      getTime(5) is the most recently added time, getTime(0) is the oldest
*/

This works brilliantly. What’s more I can understand how it works too. Thanks very much !

PaulS: rtc_time is a pointer.

Suppose that you have a white board. You want to know what time it is; someone writes the time on the board, and gives you a pointer that points at the white board............

L Now, do you see the problem?

You need to COPY the data from the white board to another white board, and point each pointer at a different white board, if you want to keep tack of the different times.

Thanks for the explanation, PaulS.

I think I expected rtc_time to be a variable that I would be redefined each time readTime() was called. So I guess I need to assign the result to a new variable each time readTime() is called....

I think I expected rtc_time to be a variable that I would be redefined each time readTime() was called. So I guess I need to assign the result to a new variable each time readTime() is called…

Remember the white board? The variable rtc_time is a pointer. It is like a piece of string leading to the white board where the time is written.

Each time you call the function that returns the pointer, it gives you another piece of string leading to the SAME white board.

You could do something like:

   Top_Open_Times[slot++] = strdup(rtc_time);

What that does is copy the data from the white board that the rtc_time string leads to to another white board and give you a string leading to THAT white board.

When the array is full, you have 5 strings leading to 5 DIFFERENT white boards, NOT 5 strings leading to the SAME white board.

When you are done with a white board, because you have 5 already, you need to free the pointer (until the string) before you get a new piece of string.

Use a 2d array rather than pointers. Note that the strings are six characters long if you include a NUL terminator.

char Top_Open_Times[5][6]={"00:00","00:00","00:00","00:00","00:00"}; //define the array of times

memcpy(Top_Open_Times, Top_Open_Times+1, 
    sizeof(Top_Open_Times) - sizeof(Top_Open_Times[0]);

Destination is the location of element zero, source is location of element 1, number of bytes is the size of the array minus the size of one element.

Job done, for heaven's sake.

econjack -

here it is 3 1/2 years later and i found your comment #12. it solved my problem. you said it is easy to understand, well ... but i understand enough of it to incorporate it into my larger sketch with some modifications and have it work. i am still working on memmove.

I can now make a line graph that continually shifts left showing the last 24 hr data. thanks.