├── LICENSE.txt
├── README.md
├── RingBufCPP.h
├── RingBufHelpers.h
├── examples
└── Interrupts
│ └── Interrupts.ino
├── examples_no_arduino
└── test.cpp
├── keywords.txt
└── library.properties
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 D. Aaron Wisner
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Embedded RingBufCPP
2 |
3 | This is a simple ring (FIFO) buffer queuing library for embedded platforms, such as Arduino's. This library is based on a previous one I wrote, only now in C++ instead of C. It uses C++'s templating, so no more weird pointer casting, and deep copies of objects are supported. It has concurrency protection built in, so feel free to do operations
4 | on the buffer inside of ISR's. All memory is statically allocated at compile time, so no heap memory is used. It can buffer any fixed size object (ints, floats, structs, objects, etc...).
5 |
6 | ## FAQ's
7 |
8 | - I only have a C compiler for my platform
9 | - No worries, try the vanilla C version of the library.
10 |
11 |
12 |
13 | ## Use Cases
14 |
15 | A ring buffer is used when passing asynchronous io between two threads. In the case of the Arduino, it is very useful for buffering data in an interrupt routine that is later processed in your `void loop()`.
16 |
17 | ## Supported Platforms
18 | The library currently supports:
19 | - AVR
20 | - ESP8266
21 | - Any other platform (just implement the `RB_ATOMIC_START` and `RB_ATOMIC_END` macros)
22 |
23 | ## Install
24 |
25 |
26 | This library is now available in the Arduino Library Manager, directly in the IDE. Go to `Sketch > Include Library > Manage Libraries` and search for `RingBufCPP`. Then `#include ` in your sketch.
27 |
28 |
29 | To manually install this library, download this file as a zip, and extract the resulting folder into your Arduino Libraries folder. [Installing an Arduino Library](https://www.arduino.cc/en/Guide/Libraries).
30 |
31 | ## Examples
32 |
33 | Look at the examples folder for several examples.
34 |
35 | ## Contributing
36 |
37 | If you find this Arduino library helpful, click the Star button, and you will make my day.
38 |
39 | Feel free to improve this library. Fork it, make your changes, then submit a pull request!
40 |
41 | ## API
42 |
43 |
44 | ### Constructor
45 |
46 | ```c++
47 | RingBufCPP();
48 | ```
49 |
50 | Creates a new RingBuf object that can buffer up to MaxElements of type Type.
51 |
52 |
53 | ## Methods
54 |
55 |
56 | ### add()
57 |
58 | ```c++
59 | bool add(const Type &obj, bool overwrite=false)
60 |
61 | ```
62 |
63 | Append an element to the buffer.
64 | If there is already MaxElements in the buffer,
65 | the oldest element will either be overwritten (when overwrite is true) or
66 | this add will have no effect (when overwrite is false).
67 | Return false on a full buffer.
68 |
69 | ### peek()
70 |
71 | ```c++
72 | Type *peek(uint16_t num);
73 | ```
74 |
75 | Peek at the num'th element in the buffer. Returns a pointer to the location of the num'th element. If num is out of bounds or the num'th element is empty, a NULL pointer is returned. Note that this gives you direct memory access to the location of the num'th element in the buffer, allowing you to directly edit elements in the buffer. Note that while all of RingBuf's public methods are atomic (including this one), directly using the pointer returned from this method is not safe. If there is a possibility an interrupt could fire and remove/modify the item pointed to by the returned pointer, disable interrupts first with `noInterrupts()`, do whatever you need to do with the pointer, then you can re-enable interrupts by calling `interrupts()`.
76 |
77 | ### pull()
78 |
79 | ```c++
80 | bool pull(Type *dest);
81 | ```
82 |
83 | Pull the first element out of the buffer. The first element is copied into the location pointed to by dest. Returns false if the buffer is empty, otherwise returns true on success.
84 |
85 |
86 | ### numElements()
87 | ```c++
88 | size_t numElements();
89 | ```
90 |
91 | Returns number of elements in the buffer.
92 |
93 | ### isFull()
94 | ```c++
95 | bool isFull();
96 | ```
97 |
98 | Returns true if buffer is full, otherwise false.
99 |
100 |
101 | ### isEmpty()
102 |
103 | ```c++
104 | bool isEmpty();
105 | ```
106 |
107 | Returns true if buffer is empty, false otherwise.
108 |
109 | ## License
110 |
111 | This library is open-source, and licensed under the [MIT license](http://opensource.org/licenses/MIT). Do whatever you like with it, but contributions are appreciated.
112 |
--------------------------------------------------------------------------------
/RingBufCPP.h:
--------------------------------------------------------------------------------
1 | #ifndef EM_RINGBUF_CPP_H
2 | #define EM_RINGBUF_CPP_H
3 |
4 | #include "RingBufHelpers.h"
5 |
6 | template
7 | class RingBufCPP
8 | {
9 | public:
10 |
11 | RingBufCPP()
12 | {
13 | RB_ATOMIC_START
14 | {
15 | _numElements = 0;
16 |
17 | _head = 0;
18 | }
19 | RB_ATOMIC_END
20 | }
21 |
22 | /**
23 | * Add element obj to the buffer.
24 | *
25 | * If there is already MaxElements in the buffer,
26 | * the oldest element will either be overwritten (when overwrite is true) or
27 | * this add will have no effect (when overwrite is false).
28 | *
29 | * Return: true if there was room in the buffer to add this element
30 | */
31 | bool add(const Type &obj, bool overwrite=false)
32 | {
33 | bool full = false;
34 | RB_ATOMIC_START
35 | {
36 | full = isFull();
37 | if (!full || overwrite) {
38 | _buf[_head] = obj;
39 | _head = (_head + 1)%MaxElements;
40 | _numElements = full ? _numElements : (_numElements + 1);
41 | }
42 | }
43 | RB_ATOMIC_END
44 |
45 | return !full;
46 | }
47 |
48 |
49 | /**
50 | * Remove last element from buffer, and copy it to dest
51 | * Return: true on success
52 | */
53 | bool pull(Type *dest)
54 | {
55 | bool ret = false;
56 | size_t tail;
57 |
58 | RB_ATOMIC_START
59 | {
60 | if (!isEmpty()) {
61 | tail = getTail();
62 | *dest = _buf[tail];
63 | _numElements--;
64 |
65 | ret = true;
66 | }
67 | }
68 | RB_ATOMIC_END
69 |
70 | return ret;
71 | }
72 |
73 |
74 | /**
75 | * Peek at num'th element in the buffer
76 | * Return: a pointer to the num'th element
77 | */
78 | Type* peek(size_t num)
79 | {
80 | Type *ret = NULL;
81 |
82 | RB_ATOMIC_START
83 | {
84 | if (num < _numElements) //make sure not out of bounds
85 | ret = &_buf[(getTail() + num)%MaxElements];
86 | }
87 | RB_ATOMIC_END
88 |
89 | return ret;
90 | }
91 |
92 |
93 | /**
94 | * Return: true if buffer is full
95 | */
96 | bool isFull() const
97 | {
98 | bool ret;
99 |
100 | RB_ATOMIC_START
101 | {
102 | ret = _numElements >= MaxElements;
103 | }
104 | RB_ATOMIC_END
105 |
106 | return ret;
107 | }
108 |
109 |
110 | /**
111 | * Return: number of elements in buffer
112 | */
113 | size_t numElements() const
114 | {
115 | size_t ret;
116 |
117 | RB_ATOMIC_START
118 | {
119 | ret = _numElements;
120 | }
121 | RB_ATOMIC_END
122 |
123 | return ret;
124 | }
125 |
126 |
127 | /**
128 | * Return: true if buffer is empty
129 | */
130 | bool isEmpty() const
131 | {
132 | bool ret;
133 |
134 | RB_ATOMIC_START
135 | {
136 | ret = !_numElements;
137 | }
138 | RB_ATOMIC_END
139 |
140 | return ret;
141 | }
142 |
143 | protected:
144 | /**
145 | * Calculates the index in the array of the oldest element
146 | * Return: index in array of element
147 | */
148 | size_t getTail() const
149 | {
150 | return (_head + (MaxElements - _numElements))%MaxElements;
151 | }
152 |
153 |
154 | // underlying array
155 | Type _buf[MaxElements];
156 |
157 | size_t _head;
158 | size_t _numElements;
159 | private:
160 |
161 | };
162 |
163 | #endif
164 |
--------------------------------------------------------------------------------
/RingBufHelpers.h:
--------------------------------------------------------------------------------
1 | #ifndef EM_RINGBUF_HELPERS_CPP_H
2 | #define EM_RINGBUF_HELPERS_CPP_H
3 |
4 | // TODO fix this
5 | #ifndef NULL
6 | #define NULL (void *)(0)
7 | #endif
8 |
9 | #ifdef ARDUINO
10 | #include
11 | #else
12 | #include
13 | #endif
14 |
15 | #ifdef ARDUINO
16 |
17 | #if defined(ARDUINO_ARCH_AVR)
18 | #include
19 | #define RB_ATOMIC_START ATOMIC_BLOCK(ATOMIC_RESTORESTATE) {
20 | #define RB_ATOMIC_END }
21 |
22 |
23 | #elif defined(ARDUINO_ARCH_ESP8266)
24 | #ifndef __STRINGIFY
25 | #define __STRINGIFY(a) #a
26 | #endif
27 |
28 | #ifndef xt_rsil
29 | #define xt_rsil(level) (__extension__({uint32_t state; __asm__ __volatile__("rsil %0," __STRINGIFY(level) : "=a" (state)); state;}))
30 | #endif
31 |
32 | #ifndef xt_wsr_ps
33 | #define xt_wsr_ps(state) __asm__ __volatile__("wsr %0,ps; isync" :: "a" (state) : "memory")
34 | #endif
35 |
36 | #define RB_ATOMIC_START do { uint32_t _savedIS = xt_rsil(15) ;
37 | #define RB_ATOMIC_END xt_wsr_ps(_savedIS); } while(0);
38 |
39 | #else
40 | #define RB_ATOMIC_START {
41 | #define RB_ATOMIC_END }
42 | #warning "This library only fully supports AVR and ESP8266 Boards."
43 | #warning "Operations on the buffer in ISRs are not safe!"
44 | #endif
45 |
46 | #else
47 | #define RB_ATOMIC_START {
48 | #define RB_ATOMIC_END }
49 | #warning "Operations on the buffer in ISRs are not safe!"
50 | #warning "Implement RB_ATOMIC_START and RB_ATOMIC_END macros for safe ISR operation!"
51 | #endif
52 |
53 | #endif
54 |
--------------------------------------------------------------------------------
/examples/Interrupts/Interrupts.ino:
--------------------------------------------------------------------------------
1 | /*
2 | RingBufInterruptsExample.ino - Interrupt example, to demonstrate the power of a ring buffer with asynchronous io
3 | Created by D. Aaron Wisner (daw268@cornell.edu)
4 | January 17, 2015.
5 | Released into the public domain.
6 |
7 | Trigger an interrupt by toggling EVENT_PIN, this will push an Event struct into the buffer
8 | Print the contents of the buffer by sending anything over Serial Monitor
9 | */
10 | #include
11 |
12 | // MUST BE AN INTTERUPT COMPATIBLE PIN
13 | #define EVENT_PIN 3
14 | #define MAX_NUM_ELEMENTS 10
15 |
16 | struct Event
17 | {
18 | int index;
19 | unsigned char pinState;
20 | unsigned long timestamp;
21 | };
22 |
23 | // Declare as volatile, since modofied in ISR
24 | volatile unsigned int index = 0;
25 |
26 | // Stack allocate the buffer to hold event structs
27 | RingBufCPP buf;
28 |
29 |
30 | void setup() {
31 | Serial.begin(9600);
32 |
33 | // Attach the interrupt service routine
34 | attachInterrupt(digitalPinToInterrupt(EVENT_PIN), isr_event, CHANGE);
35 |
36 |
37 | Serial.println("Toggle the pin state of pin EVENT_PIN, to fire an interrupt");
38 | Serial.println("Send anything over serial to dump the contents of the buffer");
39 | Serial.println();
40 | }
41 |
42 | void loop() {
43 | // Print the number of elements
44 | Serial.print("There are: ");
45 | Serial.print(buf.numElements());
46 | Serial.println(" element(s) in the ring buffer");
47 |
48 | // Check if empty
49 | if (buf.isEmpty())
50 | {
51 | Serial.println("The ring buffer is currently empty");
52 | }
53 | else if (buf.isFull())
54 | {
55 | Serial.println("The ring buffer is currently full");
56 | }
57 |
58 | // If any message sent through serial monitor, dump buffer
59 | if (Serial.available() > 0)
60 | {
61 | // Print contents of buffer
62 | print_buf_contents();
63 |
64 | //empty serial buffer
65 | while (Serial.read() > 0);
66 | }
67 |
68 | Serial.println();
69 |
70 | delay(2000);
71 | }
72 |
73 |
74 | // This interrupt serve routine function is called whenever an interrupt fires on EVENT_PIN
75 | // An Event Struct will be appended to the buffer
76 | void isr_event()
77 | {
78 | struct Event e;
79 | e.index = index++;
80 | e.pinState = digitalRead(EVENT_PIN);
81 | e.timestamp = millis();
82 | // Add it to the buffer
83 | buf.add(e);
84 | }
85 |
86 | // Print the buffer's contents then empty it
87 | void print_buf_contents()
88 | {
89 | struct Event e;
90 |
91 | Serial.println("\n______Dumping contents of ring buffer_______");
92 |
93 | // Keep looping until pull() returns NULL
94 | while (buf.pull(&e))
95 | {
96 | //
97 | Serial.print("Event index: ");
98 | Serial.println(e.index);
99 |
100 | Serial.print("Pin state: ");
101 | Serial.println(e.pinState);
102 |
103 | Serial.print("Timestamp (ms): ");
104 | Serial.println(e.timestamp);
105 |
106 | Serial.println();
107 | }
108 |
109 | Serial.println("______Done dumping contents_______");
110 |
111 | }
112 |
--------------------------------------------------------------------------------
/examples_no_arduino/test.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "RingBuf.h"
3 | #include
4 | #include
5 |
6 | int main()
7 | {
8 | RingBufCPP q;
9 | int tmp = 12;
10 | //q.add(tmp);
11 |
12 | for (uint16_t i=0; i< 100; i++) {
13 | int tmp = random();
14 |
15 | if (q.add(tmp)) {
16 | printf("Added %d\n", tmp);
17 | }
18 | else {
19 | q.pull(&tmp);
20 | break;
21 | }
22 | }
23 |
24 | while(!q.isEmpty()) {
25 | int pulled;
26 | q.pull(&pulled);
27 | printf("Got %d\n", pulled);
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/keywords.txt:
--------------------------------------------------------------------------------
1 | RingBufCPP KEYWORD1
2 |
3 | isFull KEYWORD2
4 | isEmpty KEYWORD2
5 | numElements KEYWORD2
6 | add KEYWORD2
7 | peek KEYWORD2
8 | pull KEYWORD2
9 |
--------------------------------------------------------------------------------
/library.properties:
--------------------------------------------------------------------------------
1 | name=RingBufCPP
2 | version=1.3
3 | author=D. Aaron Wisner (daw268@cornell.edu)
4 | maintainer=D. Aaron Wisner (daw268@cornell.edu)
5 | sentence=A C++ library for buffering items into a ring (circular/FIFO) buffer
6 | paragraph=This library is perfect for capturing pin states, timestamps, etc.. during an ISR. Then in void loop(), the buffer can be asynchronously processed whenever your program has free time.
7 | category=Data Storage
8 | url=https://github.com/wizard97/Embedded_RingBuf_CPP
9 | architectures=avr,esp8266,samd
10 |
--------------------------------------------------------------------------------