circular buffer

Hello friends,

I struggle a bit to create a circularbuffer for my application.

What i want. I continuously measure a pH-value and wanna display them, visual with bars in a xy-diagram.
I want that the current value stands always on the leftest (first) place and and the older value are shifted to the right.

My research showed that a circularbuffer is the right thing. To test it. i first just wanna display the 4 latest values

i store my measured values into an array

plotValues[z]=(sonde_pH.measuredValue);

Now i am stuck. the name of my bars are j0,j1,j2,j3. So the current value should alway be in bar j0.

I need a function that returns the index the following:

0 1 2 3 0
1 ---> 0 ----> 1 ----> 2 ---> 1 and so on
2 3 0 1 2
3 2 3 0 3

Until now, i am not able to solve this problem

Anyone an idea?

thank you

(deleted)

#define LEN 10
int buffer[LEN];
int idx=0;
...
buffer[idx]=what-ever-you-want;
idx=(idx+1)%LEN;

zwieblum:

idx=(idx+1)%LEN;

NO! When the "int" rolls over it may cause problems.

buffer[idx++]=what-ever-you-want;
if (idx >= LEN) idx = 0;

YES!

Heatstroke + brainfart = gibberish :open_mouth:

If you are using an ESP32 the ESP32 freeRTOS extension has what's called Ring Buffer FreeRTOS (Supplemental Features) - ESP32 - — ESP-IDF Programming Guide latest documentation, which works great as a ring buffer.

Danois90:
NO! When the "int" rolls over it may cause problems.

How will it “roll-over” when you are only ever adding 1?
How does your code prevent this?
How does your code correctly wrap the index (in any case other than adding one to the maximum) when it always sets the index to zero?

I agree if you are adding more than one, then there is a possibility of exceeding the range of an int.
I also agree, at least for buffers which are not exact powers of two, then use of the modulo operator may not be the most efficient.

Hello thank you for the answers

Sorry i still dont get it. Maybe i should be more specific.

here is the function, where the values are send to my display to draw the bars

  void writeTextToFront(Initialize sonde)
  {
  plotValues[z]=(sonde_pH.measuredValue); 

for(int t=0; t>=barIt; t++) 
    {
      String str = String("j")+String(t)+String(".val=")+String(int(mapf(plotValues[mapCircularBufferIndex(t, z, BAR_SIZE)],0,14,0,100)));
      Serial2.print(str);
      Serial2.write(0xFF);
      Serial2.write(0xFF);
      Serial2.write(0xFF);
    }
  if(++z >= 3)
  {
    z=0;
  }
}

mapCircularBufferIndex should be th function that gives me the right index back

So j0 should always have th current value.

For example.

j0 = 2.2 (new value = 3.7) j0 = 3.7
j1=3.4 ---> j1=2.2
j2=2.4 j2 = 3.4
j3=3.1 j3 = 2.4 and so on

Is its somehow clear what i mean?

Thank you very much

pcbbc:
How will it “roll-over” when you are only ever adding 1?

The modulo operator doesn't like working with negative numbers. Here is what happens when a mod 10 hits an integer overflow in a char:

123, 3
124, 4
125, 5
126, 6
127, 7
128, -8
129, -7
130, -6
131, -5
132, -4
char val = 100;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  delay(1000);

  for (int iter = 100; iter < 300; iter++) {
    Serial.print(iter);
    Serial.print(", ");
    Serial.print(val % 10);
    Serial.println();
    val++;
  }
}

void loop() {}

The same thing will happen when an int overflows. If you constrain the int, you can:

  void writeTextToFront(Initialize sonde, int offset)
  {
  plotValues[z]=(sonde_pH.measuredValue);

for(int iter=0; iter>=barIt; iter++)

    t = (iter + offset) % barIt; // "circularize the buffer"

    {
      String str = String("j")+String(t)+String(".val=")+String(int(mapf(plotValues[mapCircularBufferIndex(t, z, BAR_SIZE)],0,14,0,100)));
      Serial2.print(str);
      Serial2.write(0xFF);
      Serial2.write(0xFF);
      Serial2.write(0xFF);
    }
  if(++z >= 3)
  {
    z=0;
  }
}

I had to make some assumptions about your code, since you didn't think it important to document it.

@Danois90: Even if would wrap around it's still correct :slight_smile:

Instead of roatating the buffers, why don't you rotate the values of the display?

So as each buffer is filled another variable is used that tells the display where to start from, and you go backward from that value.

zwieblum:
@Danois90: Even if would wrap around it's still correct :slight_smile:

No, as I demonstrated, it would eventually produce negative buffer indexes.

Ok, then mke it "uint" :slight_smile:

No, that won't fix it:

248, 8
249, 9
250, 0
251, 1
252, 2
253, 3
254, 4
255, 5
256, 0
257, 1
258, 2
259, 3
260, 4
261, 5
unsigned char val = 230;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  delay(1000);

  for (int iter = 230; iter < 300; iter++) {
    Serial.print(iter);
    Serial.print(", ");
    Serial.print(val % 10);
    Serial.println();
    val++;
  }
}

void loop() {}

It will only work when the modulus is a factor of the integer modulus. 256/10 is not an integer.

Sorry, but if you write deliberatel wrong code, what do you want to proof with it?

zwieblum:
Sorry, but if you write deliberatel wrong code, what do you want to proof with it?

It proves that your code will fail. For reference, this follows from the comments in reply #3.

I implemented a simple ring buffer.

I assumed that the type of your data is int; if that is not the case, you must change the int in
#define MY_TYPE int
to your data type

I set the buffer size to 4, you can change this modifying
#define BUFFER_SIZE 4

In your code you call

push(sonde_pH.measuredValue)

to insert a value in the buffer, and call

plotMyValues()

to plot all your values; that function in turn will call

plot(value, position)

for every value in the buffer, in LIFO order; insert your code to plot a single element in this function

#define MY_TYPE int
#define BUFFER_SIZE 4

MY_TYPE plotValues[BUFFER_SIZE];

int head = 0; 
int count = 0;

void incIndex(int &index)
{
	if (index + 1 < BUFFER_SIZE)
		index++;
	else
		index = 0;	
}

void decIndex(int &index)
{
	if (index > 0)
		index--;
	else
		index = BUFFER_SIZE-1;	
}

bool isBufferFull()
{
	return count == BUFFER_SIZE;
}

void reset()
{
	head = 0;
	count = 0;
}

void push(MY_TYPE value)
{
	plotValues[head] = value;
	
	incIndex(head);
		
	if (!isBufferFull())
		count++;	
}

void plotMyValues()
{
	int tmp = head;
	int i=0;
	
	while(i < count)
	{
		decIndex(tmp);
	
		MY_TYPE value = plotValues[tmp];
		
		plot(value,i);
		
		i++;
	}
}

//
void plot(MY_TYPE value, int position)
{
	// insert here your code to plot the element at assigned position
}

Sorry, but no again. If you write wrong code, your code is wrong whatever you do.

zwieblum:
Sorry, but no again. If you write wrong code, your code is wrong whatever you do.

I can't write correct code to demonstrate a flaw. The whole point is that it doesn't work. That is standard procedure in cases like this.

Edit - okay, I see the difference. The danger is there, but you've avoided it by constraining the variable - it never exceeds the range of LEN and so never goes negative.

#define LEN 10
int idx;

void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  delay(1000);

  for (unsigned int iter = 32736; iter < 32836; iter++) {
    Serial.print(iter);
    Serial.print(", ");
    Serial.print(idx);
    Serial.println();
    idx=(idx+1)%LEN;
  }
}

void loop() {}

Sometimes people will try something like:

int counter;
...
idx = (counter++)%LEN;

Because that one fails, alarm bells go off anytime anything looks similar.

However, the execution speed and code size overhead for '%' greatly exceed a simple range test and adjustment. It also obfuscates the logic, where the range test and adjustment is self-explanatory.

I have modified my previous post. The modulus operator will work, but I would always avoid it where possible because there is no hardware division available in AVR's. Division is emulated with a series of instructions and when you use modulus you will first have to emulate a division and then perform a multiplication (which btw. may also be emulated) of the result before subtracting that result from a value in order to get the modulus result: Not very effective. A simple and effective one-liner would be:

if (++index >= BUFSIZE) index = 0;

Hello friends.

Thank you for all the answers. unfortunly it seems i am to dumb right now.

Thats the function i made to get my right index

int mapCircularBufferIndex(int index, int bufferBegin, int bufferSize)
{
  index -= bufferBegin;
  if (index >= bufferSize) index = bufferSize;
  return abs(index);
}

when i call the function with:

void writeTextToFront(Initialize sonde)
{
  plotValues[z]=(sonde_pH.measuredValue); 
  for(int t=0; t<=4; t++)
  { 
      Serial.println(mapCircularBufferIndex(t,z,4));
  }

  if(++z >= 4)
  {
    z=0;
  }
  
 }

i get this:

0

1

2

3

4


1

0

1

2

3


2

1

0

1

2


3

2

1

0

1


0

1

2

3

4

What i need that the function returns:

0,1,2,3,4 --> 1,0,4,3,2 --> 2,1,0,4,3 ---> 3,2,1,0,4 ---> 4,3,2,1.0

sorry for my incompetence

thank you