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.
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.
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.