├── 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 | --------------------------------------------------------------------------------