Ring buffer is very common and useful data structure, excellent for storing asynchronous data from multiple sources in embedded devices.

// Simple prove-of-concept ring buffer
// Author: Adam Orcholski, www.tath.eu
#pragma once

// types
typedef struct ring_buffer_s
{
    unsigned char * data;   // pointer to external buffer
    int size;               // size of external buffer
    int head;               // index of free space in external buffer
    int tail;               // index of last item to be read
    int count;              // hold number of items stored but not read
} ring_buffer_t;

// interface
void RING_Init(ring_buffer_t * const buff_state, unsigned char * in_buffer, int buffer_size_in_bytes);
void RING_Reset(ring_buffer_t * const buff_state);      // note: does not clear external buffer
int  RING_IsEmpty(ring_buffer_t * const buff_state);    // return non-zero if buffer is empty
int  RING_IsFull(ring_buffer_t * const buff_state);     // return non-zero if buffer is full
int  RING_AddItem(ring_buffer_t * const buff_state, unsigned char new_item);            // return non-zero if buffer is full
int  RING_GetItem(ring_buffer_t * const buff_state, unsigned char * const out_item);    // return non-zero if buffer is empty
// Simple prove-of-concept ring buffer
// Author: Adam Orcholski, www.tath.eu

#include <ringbuffer.h>
#include <assert.h>


// function definitions
void RING_Init(ring_buffer_t * const buff_state, unsigned char * in_buffer, int buffer_size_in_bytes)
{
    assert(0 != buff_state);
    assert(0 != in_buffer);
    assert(buffer_size_in_bytes > 0);

    buff_state->data = in_buffer;
    buff_state->size = buffer_size_in_bytes;
    buff_state->head = 0;
    buff_state->tail = 0;
    buff_state->count = 0;
}

void RING_Reset(ring_buffer_t * const buff_state)
{
    assert(0 != buff_state);

    buff_state->head = 0;
    buff_state->tail = 0;
    buff_state->count = 0;
}

// return non-zero value if buffer is empty
int  RING_IsEmpty(ring_buffer_t * const buff_state)
{
    int fBufferIsEmpty = 0;

    assert(0 != buff_state);

    if (0 == buff_state->count)
    {
        fBufferIsEmpty = 1;
    }

    return fBufferIsEmpty;
}

// return non-zero value if buffer is full
int  RING_IsFull(ring_buffer_t * const buff_state)
{
    int fBufferIsFull = 0;

    assert(0 != buff_state);

    if (buff_state->count >= buff_state->size)
    {
        fBufferIsFull = 1;
    }

    return fBufferIsFull;
}

// return non-zero value if buffer is full
int  RING_AddItem(ring_buffer_t * const buff_state, unsigned char new_item)
{
    int fBufferIsFull = 0;

    assert(0 != buff_state);

    fBufferIsFull = RING_IsFull(buff_state);

    if (!fBufferIsFull)
    {
        buff_state->data[buff_state->head] = new_item;
        buff_state->head++;
        buff_state->count++;

        if (buff_state->head >= buff_state->size)
        {
            buff_state->head = 0;
        }
    }


    return fBufferIsFull;
}

// return non-zero value if buffer is empty
int  RING_GetItem(ring_buffer_t * const buff_state, unsigned char * const out_item)
{
    int fBufferIsEmpty = 0;

    assert(0 != buff_state);
    assert(0 != out_item);

    fBufferIsEmpty = RING_IsEmpty(buff_state);

    if (!fBufferIsEmpty)
    {
        *out_item = buff_state->data[buff_state->tail];
        buff_state->tail++;
        buff_state->count--;

        if (buff_state->tail >= buff_state->size)
        {
            buff_state->tail = 0;
        }
    }

    return fBufferIsEmpty;
}

and example/test compiled in Microsoft Visual Studio:

// Simple prove-of-concept ring buffer example
// Author: Adam Orcholski, www.tath.eu

#include <stdio.h>
#include <ringbuffer.h>

#define BUFF_SIZE 4
unsigned char bufffeeer[BUFF_SIZE];

ring_buffer_t buff;

int main()
{
    RING_Init(&buff, bufffeeer, sizeof(bufffeeer));

    char c = 0;

    while (c != 'q')
    {
        static char write_char = 'a';
        c = getchar();
        
        int ret = 0;

        if (c == 'w')
        {
            ret = RING_AddItem(&buff, write_char);
            if (ret)
            {
                printf("buffer full\r\n");
            }
            else
            {
                printf("written char = %c\r\n", write_char);
            }
            write_char++;
            if (write_char == 'z') { write_char = 'a'; }
        }
        else if (c == 's')
        {
            char out;
            ret = RING_GetItem(&buff, &out);
            if (ret)
            {
                printf("buffer empty\r\n");
            }
            else
            {
                printf("read char = %c\r\n", out);
            }
        }
    }

    return 0;
}

Run example, and press W key to add item to buffer and S key to read last item. Press Q to quit.