Last Updated: 29/04/2024

Arduino FIFO (First In First Out) Buffer

Components/How To >> Arduino FIFO (First In First Out) Buffer

What is a FIFO (First in First Out) Buffer?

NOTE: A second video has been added with three more examples for buttons, sensors and interrupts. 29/04/2024

A FIFO buffer sometimes called a Circular or Ring buffer is a way of storing data in a way that the first biit of data that arrives is also the first to leave.

FIFO buffers are useful for dealing with surges of data that need to be passed on to another process.

An example would be a recent project that read a number of sensors that then needed the data transmitted. as there are limits on the transmit speed the data needed to be stored in the order it arrived and then sent on.

As well as situations like the one mentioned, FIFO buffers are also very useful for storing data that come from an Interrupt routines. A way of storing the data ready to be picked up and processed by the programs main loop. This prevents the interrupt routine becoming slowed down by excess code.

The second video gives some more examples of using the FIFO buffer with buttons, Sensors, Interruots and LED's.
Code examples to go with the video are examples 4-6

Circuit Diagram

For this example no circuit is required. This was tested on an Arduino Uno but any board that can use the Arduino IDE and can use the Serial Monitor will work.

The sketch is set to use the Serial monitor at 9600bps

How if Works

For this method we have 3 elements.

An Array: This is used to store the data
Write Pointer: A variable that stores the location of the next element of the array to be written to.
Read Pointer: A variable that stores the location of the next element to be read from.

In the simlified example below we see the data array with the pointer to the next location to write to in Blue while the Read pointer in red follows behind.

FIFO Buffer diagram

In the simple example above we then need to deal with what happens when the Write (and later the Read) we need to write some code that moves the pointer back to the start of the buffer again.

PROBLEMS:

Although it is not a problem for the incoming data to arrive at a variety of speeds and quantities it is important that no matter how large the buffer is the output is slightly faster that the average input speed. Without this the Write position will catch up with the Read pointer and data will be overwritten before it is processed.

The FIFO buffer is a fixed size so some testing may be required to get the correct buffer size.

The bigger the array the more the Global Variables use up Dynamic Memory. On some boards such as the Arduino UNO it does not take much to use up all of this type of memory. NOTE: Dynamic memeory is NOT the same as the memeory (Flash Memory) used to store the sketch on the Arduino.

 

Example 1: Basic FIFO buffer example


Click to Download code: fifoV1.ino

This sketch explains shows the basics of a FIFO buffer. The buffer is slightly prefilled in the setup function to help show the code working in the Serial monitor.

The code has been written in such a way as to slow down the Read and Writes so that it's possible to follow the code on the Serial Monitor.

IMPORTANT NOTE: This example uses the serial monitor to show what is happening, when building real projects comment out ALL Serial.print() calls to increase the speed the code runs at.

 

 
/*  fifoV1
     22/12/23
     This should run on any board as it does not require any pins
     Just the Serial monitor.

    A simple sketch to show the basics of using a fifo buffer

    FIFO  First in First Out


*/
int readPosition;
int writePosition;
int fifoBuffer[100];// (20%)

unsigned long currentMillis;
unsigned long writeMillis;
unsigned long readMillis;
int randomWriteTiming;

void setup() {
  Serial.begin(9600);
  Serial.println("fifoV1");

  //this loop fills the first 30 items in the fifo buffer to prefill with some info.
  for (int q = 0; q < 30; q++) {
    fifoBuffer[writePosition] = q;
    writePosition++;
  }

}

void loop() {
  currentMillis = millis();

  if (currentMillis - readMillis >= 1000) { //1 second spacing for read
    readMillis = currentMillis;
    if (readPosition != writePosition) {
      Serial.print("R ");
      Serial.println(fifoBuffer[readPosition]);
      readPosition++;
      if (readPosition > 99) {
        readPosition = 0;
      }
    }
  }

  if (currentMillis - writeMillis >= randomWriteTiming) {
    writeMillis = currentMillis;
    randomWriteTiming = random(500, 2000); //generate random times to write between 0.5 and 2 seconds
    fifoBuffer[writePosition] = writePosition;
    
    Serial.print("W ");
    Serial.println(fifoBuffer[writePosition]);
    if (writePosition > 99) {
      writePosition = 0;
    }
    writePosition++;
  }

}

Example 2: Memory Usage and an ideal buffer size


Click to Download code: fifoV2.ino

This sketch shows the effects of buffer size. By uncommenting the various lines defining the fifoBuffer[10000] etc the effects on dynamic memory can be seen on compile size.

This sketch also shows an advatage in writing code if the buffer is 256 elements long as the pointers can by bytes that overun back to Zero after 255 saving on a small amount of code.

The code has been written in such a way as to slow down the Read and Writes so that it's possible to follow the code on the Serial Monitor.

IMPORTANT NOTE: This example uses the serial monitor to show what is happening, when building real projects comment out ALL Serial.print() calls to increase the speed the code runs at.

 

 
/*  fifoV2
     22/12/23
     This should run on any board as it does not require any pins
     Just the Serial monitor.

     FIFO  First in First Out

     This sketch looks in to what could be seen as the perfect size of fifo buffer


*/
byte readPosition;
byte writePosition;
//int fifoBuffer[100000];//Too Large
//int fifoBuffer[10000];//Too Large (987%)
//int fifoBuffer[1000];//Too Large (108%)
int fifoBuffer[256];// (35%) OK
//int fifoBuffer[100];// (20%) OK

unsigned long currentMillis;
unsigned long writeMillis;
unsigned long readMillis;
int randomWriteTiming;

void setup() {
  Serial.begin(9600);
  Serial.println("fifoV2");

  //this loop fills the first 30 items in the fifo buffer to prefill with some info.
  for (int q = 0; q < 30; q++) {
    fifoBuffer[writePosition] = q;
    writePosition++;
  }

}

void loop() {
  currentMillis = millis();

  if (currentMillis - readMillis >= 1000) { //1 second spacing for read
    readMillis = currentMillis;
    if (readPosition != writePosition) {
      Serial.print("R ");
      Serial.println(fifoBuffer[readPosition]);
      readPosition++;
      /* Not required any more, using a byte and a 256 element array
       *  The line readPosition++; leads to
       *  253
       *  254
       *  255
       *  0....goes back to Zero as the byte value has been exceeded
       *  1
       *  2
       *  
      if (readPosition > 99) {
        readPosition = 0;
      }
      */
    }
  }

  if (currentMillis - writeMillis >= randomWriteTiming) {
    writeMillis = currentMillis;
    randomWriteTiming = random(500, 2000); //generate random times to write between 0.5 and 2 seconds
    fifoBuffer[writePosition] = writePosition;
    
    Serial.print("W ");
    Serial.println(fifoBuffer[writePosition]);
    /*
    if (writePosition > 99) {
      writePosition = 0;
    }
    */
    writePosition++;
  }

}

Example 3: FIFO buffer using 2 arrays


Click to Download code: fifoV3.ino

This final sketch shows two different arrays being controlled as if 2 types of variable are being passed to the system and then being moved on.

 

 
/*  fifoV3
     22/12/23
     This should run on any board as it does not require any pins
     Just the Serial monitor.

     FIFO  First in First Out

     This sketch controls 2 different arrays.
     Although I could have used a 2 dimensional integer array it would have used
     more memeory than the seperate ineteger and byte arrays.


*/
byte readPosition;
byte writePosition;
byte byteSwap;

// (48%) OK
int fifoBuffer[256];
byte fifoByteBuffer[256];

unsigned long currentMillis;
unsigned long writeMillis;
unsigned long readMillis;
int randomWriteTiming;

void setup() {
  Serial.begin(9600);
  Serial.println("fifoV3");

  //this loop fills the first 30 items in the fifo buffer to prefill with some info.
  for (int q = 0; q < 30; q++) {
    fifoBuffer[writePosition] = q;
    if(byteSwap > 0){
      byteSwap = 0;  
    }else{
      byteSwap = 1;  
    }
    fifoByteBuffer[writePosition] = byteSwap;
    writePosition++;
  }

}

void loop() {
  currentMillis = millis();

  if (currentMillis - readMillis >= 1000) { //1 second spacing for read
    readMillis = currentMillis;
    if (readPosition != writePosition) {
      Serial.print("R ");
      Serial.print(fifoBuffer[readPosition]);
      Serial.print(":");
      Serial.println(fifoByteBuffer[readPosition]);
      readPosition++;
      /* Not required any more, using a byte and a 256 element array
       *  The line readPosition++; leads to
       *  253
       *  254
       *  255
       *  0....goes back to Zero as the byte value has been exceeded
       *  1
       *  2
       *  
      if (readPosition > 99) {
        readPosition = 0;
      }
      */
    }
  }

  if (currentMillis - writeMillis >= randomWriteTiming) {
    writeMillis = currentMillis;
    randomWriteTiming = random(500, 2000); //generate random times to write between 0.5 and 2 seconds
    fifoBuffer[writePosition] = writePosition;
    if(byteSwap > 0){
      byteSwap = 0;  
    }else{
      byteSwap = 1;  
    }
    fifoByteBuffer[writePosition] = byteSwap;
    
    Serial.print("W ");
    Serial.print(fifoBuffer[writePosition]);
    Serial.print(":");
    Serial.println(fifoByteBuffer[writePosition]);
    writePosition++;
  }

}

Example 4: Buttons and LED's


Click to Download code: fifoButtonsLedsV1.ino

This final sketch shows two different arrays being controlled as if 2 types of variable are being passed to the system and then being moved on.

 

 
/*  fifoButtonsLedsV1
     29/04/2024

     This ia an example of using buttons to input items into the fifo queue
     That are then processed in the same order with the LEDs

     LED's will flash on and off in the same order that the buttons are pressed.

     The array is 256 elements


*/
byte readPosition;
byte writePosition;
int fifoBuffer[256];

int buttonArray[4] = {7,6,5,4};
unsigned long debounceMillis;
int debounceTimer = 350;//debounce set for 350 milliseconds

int ledArray[4] = {12,11,10,9};

unsigned long currentMillis;

unsigned long readMillis;


void setup() {
  int q;
  Serial.begin(9600);
  Serial.println("fifoButtonsLedsV1");

  for(q=0;q<4;q++){//set all the buttons as inputs and leds as outputs
    pinMode(buttonArray[q],INPUT);
    pinMode(ledArray[q],OUTPUT);
  }
}

void loop() {
  int q;
  currentMillis = millis();

  //This section is just checking for button presses and storiong the info in the fifo buffer as it happens.

  if(currentMillis - debounceMillis >= debounceTimer){//make sure not in debounce time perios to prevent double presses
    for(q=0;q<4;q++){//Work through all the buttons
      if(digitalRead(buttonArray[q]) > 0){//is a button being pressed
        //Yes... set the debounce timer
        debounceMillis = currentMillis; //This prevents multiple presses being recorded
        fifoBuffer[writePosition] = q;//store the position in ther array being saved... you may wish to store the pinNumber or an assocaited value depoending on what you are trying to do.
        writePosition++;//increment the write position so that it is ready for the next input
        Serial.print("B: ");
        Serial.println(q);
      }
    }
  }


  //I have used time as a trigger to read the next item in the buffer.
  //Your projects may trigger the next read based on when the last read had been processed.
  if (currentMillis - readMillis >= 2000) { //2 second spacing for read...just because I'm not fast at pressing buttons
    readMillis = currentMillis;//reset the timer
    if (readPosition != writePosition) {//Only read something if the read and write positions are different
                                        //If the same no new instrcution has been received
      Serial.print("R ");
      Serial.println(fifoBuffer[readPosition]);
      for(q=0;q<4;q++){
        digitalWrite(ledArray[q],LOW);//turn all the LED's OFF as new LED to be lit.
      }
      digitalWrite(ledArray[fifoBuffer[readPosition]],HIGH);//turn on the LED that is next to be processed by the fifo buffer
      

      //Alternate processing may looik like
      // switch(fifoBuffer[readPosition]){
      //   case 0:
      //     //do something...example
      //     for(q=0;q<4;q++){
      //       digitalWrite(ledArray[q],LOW);//turn all the LED's OFF as new LED to be lit.
      //     }
      //     digitalWrite(ledArray[fifoBuffer[readPosition]],HIGH);
      //     break;
      //   case 1:
      //     //do something
      //     break;
      //   case 2:
      //     //do something
      //     break;
      //   case 3:
      //     //do something
      //     break;
      //   default:
      //     Serial.println("Switch unprocessed value");
      //     break;
      // }
      

      readPosition++;//update the readpostion ready for next read
      Serial.print("Rpos: ");
      Serial.print(readPosition);
      Serial.print(" Wpos: ");
      Serial.println(writePosition);
    }
  }

  

}

Example 5: Sensors and LED's


Click to Download code: fifoButtonsLedsV2.ino

This final sketch shows two different arrays being controlled as if 2 types of variable are being passed to the system and then being moved on.

 

 
/*  fifoButtonsLedsV2
     29/04/2024

     Model railway train queue example

    In this example the code simulates a set of sesors rather than buttons.
    The sensors being used are reed switches so a debounce is added.

    For this example imagine 4 tracks reducing to a single track.
    A sensor is on each track and trains will move down the single track in the order that they arrived.

    >>----------------Train--------------\
    >>----------------Train---------------\ -/---------->>------------------------ single track
    >>----------------Train-----------------/
    >>----------------Train----------------/

    As the sensor will be triggered the whole time the train is sitting over it we need to store the sensor state and only update the 
    fifo buffer if the state changes.

    to keep the example simple I hqave created another array called button state, normally for this I would make buttonArray a 2 dimensional array
    with the 2nd element storing the state.

     The array is 256 elements


*/
byte readPosition;
byte writePosition;
int fifoBuffer[256];

int buttonState[4];  //array will store the state of any sensor
int buttonArray[4] = { 7, 6, 5, 4 };
unsigned long debounceMillis;
int debounceTimer = 20;  //debounce set for 20 milliseconds as will only be preventing poor contact bounce

int ledArray[4] = { 12, 11, 10, 9 };

unsigned long currentMillis;

unsigned long readMillis;


void setup() {
  int q;
  Serial.begin(9600);
  Serial.println("fifoButtonsLedsV2");

  for (q = 0; q < 4; q++) {  //set all the buttons as inputs and leds as outputs
    pinMode(buttonArray[q], INPUT);
    pinMode(ledArray[q], OUTPUT);
  }
}

void loop() {
  int q;
  currentMillis = millis();

  //This section is just checking for button presses and storiong the info in the fifo buffer as it happens.

  if (currentMillis - debounceMillis >= debounceTimer) {  //make sure not in debounce time perios to prevent double presses
    debounceMillis = currentMillis;                       //This is just preventing multiple reads from read switch contacts making/breaking
    for (q = 0; q < 4; q++) {                             //Work through all the buttons
      if (digitalRead(buttonArray[q]) > 0) {              //is a button being pressed
        //Yes.....do I need to add to fifo or has it already been added
        if (buttonState[q] != 1){//If the last state was NOT 1 then this is a new press so item needs to be added to fifo
                                  //If it was already 1 just ignore the item
            buttonState[q] = 1;

            fifoBuffer[writePosition] = q;  //store the position in ther array being saved... you may wish to store the pinNumber or an assocaited value depoending on what you are trying to do.
            writePosition++;                //increment the write position so that it is ready for the next input
            Serial.print("B: ");
            Serial.println(q);
        }


      } else {
        buttonState[q] = 0;  //if nothing sensed just update the buttonState to 0
      }
    }
  }


  //I have used time as a trigger to read the next item in the buffer.
  //Your projects may trigger the next read based on when the last read had been processed.
  //For the railway example it would be when the output track is empty and ready for next train
  if (currentMillis - readMillis >= 2000) {  //2 second spacing for read... Just because I can't press buttons fast enough.
    readMillis = currentMillis;              //reset the timer
    if (readPosition != writePosition) {     //Only read something if the read and write positions are different
                                             //If the same no new instrcution has been received
      Serial.print("R ");
      Serial.println(fifoBuffer[readPosition]);
      for (q = 0; q < 4; q++) {
        digitalWrite(ledArray[q], LOW);  //turn all the LED's OFF as new LED to be lit.
      }
      digitalWrite(ledArray[fifoBuffer[readPosition]], HIGH);  //turn on the LED that is next to be processed by the fifo buffer


      //Alternate processing may looik like
      switch (fifoBuffer[readPosition]) {
        case 0:
          //do something...example
          for (q = 0; q < 4; q++) {
            digitalWrite(ledArray[q], LOW);  //turn all the LED's OFF as new LED to be lit.
          }
          digitalWrite(ledArray[fifoBuffer[readPosition]], HIGH);
          break;
        case 1:
          //do something
          break;
        case 2:
          //do something
          break;
        case 3:
          //do something
          break;
        default:
          Serial.println("Switch unprocessed value");
          break;
      }


      readPosition++;  //update the readpostion ready for next read
      Serial.print("Rpos: ");
      Serial.print(readPosition);
      Serial.print(" Wpos: ");
      Serial.println(writePosition);
    }
  }
}

Example 6: Fifo and interrupts


Click to Download code: fifoInterruptV1

This final sketch shows two different arrays being controlled as if 2 types of variable are being passed to the system and then being moved on.

 

 
/* fifoInterruptV1
   29/04/2024

  This will work out the timings between interrupts on Pin 2
*/

int interruptPin = 2;

byte readPosition;
byte writePosition;
unsigned long fifoBuffer[256];


unsigned long lastTime;

void processInterrupt(){
  fifoBuffer[writePosition] = micros();//Store the current time in microseconds in the buffer
  writePosition++;//increment the write variable ready for next input
}

void setup() {
  Serial.begin(9600);
  Serial.println("fifoInterruptV1");

  attachInterrupt(digitalPinToInterrupt(interruptPin), processInterrupt, RISING);
}

void loop() {
  unsigned long myInterval;
  if (readPosition != writePosition) {//work to be done
    myInterval = fifoBuffer[readPosition] - lastTime;
    lastTime = fifoBuffer[readPosition];
    Serial.println(myInterval);//print out the interval
    readPosition++;
  }

}

Additional Resource Links

Lesson 6: Basic Numeric variables, boolean, byte, int, unsigned int, long, unsigned long 23/07/2021

Lesson 11: Arrays 28/08/2021

Comments


This site has been designed to be child friendly, this means that comments cannot be added to videos or directly to the site.
To add a comment or ask a question please email the address in this image: and use Arduino FIFO (First In First Out) Buffer as a reference.