[chronojump/michrolab: 40/57] Added libraries folder for taking control of versioins used




commit 78be3bed97006b5d47bfc2954062152c85ac2f16
Author: xpadulles <x padulles gmail com>
Date:   Tue May 17 16:55:11 2022 +0200

    Added libraries folder for taking control of versioins used

 arduino/libraries/HX711/HX711.cpp                  |  134 +
 arduino/libraries/HX711/HX711.h                    |   77 +
 arduino/libraries/HX711/LICENSE                    |  339 +++
 arduino/libraries/HX711/README.md                  |   35 +
 .../HX711/examples/HX711Serial/HX711Serial.ino     |   56 +
 .../examples/HX711SerialBegin/HX711SerialBegin.ino |   59 +
 arduino/libraries/HX711/library.json               |   17 +
 arduino/libraries/RF24/CMakeLists.txt              |  167 ++
 arduino/libraries/RF24/COMMON_ISSUES.md            |  100 +
 arduino/libraries/RF24/CONTRIBUTING.md             |   16 +
 arduino/libraries/RF24/Doxyfile                    | 2585 ++++++++++++++++++++
 arduino/libraries/RF24/LICENSE                     |  339 +++
 arduino/libraries/RF24/README.md                   |   16 +
 arduino/libraries/RF24/RF24.cpp                    | 1895 ++++++++++++++
 arduino/libraries/RF24/RF24.h                      | 2258 +++++++++++++++++
 arduino/libraries/RF24/RF24_config.h               |  220 ++
 .../RF24/cmake/AutoConfig_RF24_DRIVER.cmake        |   45 +
 arduino/libraries/RF24/cmake/CPackInfo.cmake       |   75 +
 arduino/libraries/RF24/cmake/Cache.cmake           |   28 +
 .../libraries/RF24/cmake/CompilerWarnings.cmake    |   78 +
 arduino/libraries/RF24/cmake/GetLibInfo.cmake      |   43 +
 .../RF24/cmake/PreventInSourceBuilds.cmake         |   18 +
 arduino/libraries/RF24/cmake/Sanitizers.cmake      |   55 +
 .../RF24/cmake/StandardProjectSettings.cmake       |   33 +
 arduino/libraries/RF24/cmake/StaticAnalyzers.cmake |   38 +
 arduino/libraries/RF24/cmake/detectCPU.cmake       |   78 +
 arduino/libraries/RF24/cmake/pico_sdk_import.cmake |   62 +
 .../libraries/RF24/cmake/toolchains/arm64.cmake    |   33 +
 .../libraries/RF24/cmake/toolchains/armhf.cmake    |   33 +
 .../libraries/RF24/cmake/toolchains/default.cmake  |    1 +
 arduino/libraries/RF24/cmake/toolchains/i686.cmake |   33 +
 .../libraries/RF24/cmake/toolchains/x86_64.cmake   |   33 +
 .../RF24/datasheets/nRF24L01P_datasheet_v1.pdf     |  Bin 0 -> 1063826 bytes
 .../RF24/datasheets/nRF24L01_datasheet_v2.pdf      |  Bin 0 -> 1115693 bytes
 arduino/libraries/RF24/docs/README.md              |    3 +
 arduino/libraries/RF24/docs/arduino.md             |  286 +++
 arduino/libraries/RF24/docs/attiny.md              |   79 +
 arduino/libraries/RF24/docs/atxmega.md             |   55 +
 arduino/libraries/RF24/docs/cross_compile.md       |   98 +
 arduino/libraries/RF24/docs/linux_install.md       |   92 +
 arduino/libraries/RF24/docs/main_page.md           |   82 +
 arduino/libraries/RF24/docs/mraa.md                |   40 +
 arduino/libraries/RF24/docs/pico_sdk.md            |  175 ++
 arduino/libraries/RF24/docs/portability.md         |   38 +
 arduino/libraries/RF24/docs/python_wrapper.md      |  101 +
 arduino/libraries/RF24/docs/rpi_general.md         |   91 +
 arduino/libraries/RF24/docs/sphinx/README.md       |    3 +
 .../libraries/RF24/docs/sphinx/RF24_config_8h.rst  |    7 +
 .../RF24/docs/sphinx/_static/Logo large.png        |  Bin 0 -> 94683 bytes
 .../RF24/docs/sphinx/_static/custom_material.css   |  107 +
 .../RF24/docs/sphinx/_static/new_favicon.ico       |  Bin 0 -> 4286 bytes
 arduino/libraries/RF24/docs/sphinx/classRF24.rst   |  101 +
 arduino/libraries/RF24/docs/sphinx/conf.py         |  151 ++
 arduino/libraries/RF24/docs/sphinx/deprecated.rst  |    5 +
 arduino/libraries/RF24/docs/sphinx/enums.rst       |   12 +
 arduino/libraries/RF24/docs/sphinx/examples.rst    |   58 +
 .../examples/Arduino/AcknowledgementPayloads.rst   |    7 +
 .../sphinx/examples/Arduino/GettingStarted.rst     |    7 +
 .../sphinx/examples/Arduino/InterruptConfigure.rst |    7 +
 .../examples/Arduino/ManualAcknowledgements.rst    |    7 +
 .../sphinx/examples/Arduino/MulticeiverDemo.rst    |    7 +
 .../RF24/docs/sphinx/examples/Arduino/Scanner.rst  |    7 +
 .../docs/sphinx/examples/Arduino/StreamingData.rst |    7 +
 .../examples/Linux/AcknowledgementPayloads.rst     |    7 +
 .../docs/sphinx/examples/Linux/GettingStarted.rst  |    7 +
 .../sphinx/examples/Linux/InterruptConfigure.rst   |    7 +
 .../examples/Linux/ManualAcknowledgements.rst      |    7 +
 .../docs/sphinx/examples/Linux/MulticeiverDemo.rst |    7 +
 .../RF24/docs/sphinx/examples/Linux/Scanner.rst    |    7 +
 .../docs/sphinx/examples/Linux/StreamingData.rst   |    7 +
 .../examples/PicoSDK/AcknowledgementPayloads.rst   |    9 +
 .../sphinx/examples/PicoSDK/GettingStarted.rst     |    9 +
 .../sphinx/examples/PicoSDK/InterruptConfigure.rst |    9 +
 .../examples/PicoSDK/ManualAcknowledgements.rst    |    9 +
 .../sphinx/examples/PicoSDK/MulticeiverDemo.rst    |    9 +
 .../RF24/docs/sphinx/examples/PicoSDK/Scanner.rst  |    9 +
 .../docs/sphinx/examples/PicoSDK/StreamingData.rst |    9 +
 .../docs/sphinx/examples/PicoSDK/default_pins.rst  |    5 +
 .../examples/Python/AcknowledgementPayloads.rst    |    8 +
 .../docs/sphinx/examples/Python/GettingStarted.rst |    8 +
 .../sphinx/examples/Python/InterruptConfigure.rst  |    8 +
 .../examples/Python/ManualAcknowledgements.rst     |    8 +
 .../sphinx/examples/Python/MulticeiverDemo.rst     |    8 +
 .../docs/sphinx/examples/Python/StreamingData.rst  |    8 +
 arduino/libraries/RF24/docs/sphinx/index.rst       |   44 +
 arduino/libraries/RF24/docs/sphinx/make.bat        |   35 +
 .../RF24/docs/sphinx/md_common_issues.rst          |   14 +
 .../libraries/RF24/docs/sphinx/md_contributing.rst |    5 +
 .../libraries/RF24/docs/sphinx/md_docs_arduino.rst |    5 +
 .../libraries/RF24/docs/sphinx/md_docs_attiny.rst  |    6 +
 .../libraries/RF24/docs/sphinx/md_docs_atxmega.rst |    5 +
 .../RF24/docs/sphinx/md_docs_cross_compile.rst     |    7 +
 .../RF24/docs/sphinx/md_docs_linux_install.rst     |    5 +
 .../libraries/RF24/docs/sphinx/md_docs_mraa.rst    |    5 +
 .../RF24/docs/sphinx/md_docs_pico_sdk.rst          |    7 +
 .../RF24/docs/sphinx/md_docs_portability.rst       |    5 +
 .../RF24/docs/sphinx/md_docs_python_wrapper.rst    |    5 +
 .../RF24/docs/sphinx/md_docs_rpi_general.rst       |    5 +
 .../RF24/docs/sphinx/md_docs_using_cmake.rst       |    7 +
 arduino/libraries/RF24/docs/sphinx/modules.rst     |   11 +
 .../RF24/docs/sphinx/modules/Porting_GPIO.rst      |   15 +
 .../RF24/docs/sphinx/modules/Porting_General.rst   |   15 +
 .../RF24/docs/sphinx/modules/Porting_Includes.rst  |   15 +
 .../RF24/docs/sphinx/modules/Porting_SPI.rst       |   15 +
 .../RF24/docs/sphinx/modules/Porting_Timing.rst    |   15 +
 arduino/libraries/RF24/docs/sphinx/nRF24L01_8h.rst |    7 +
 arduino/libraries/RF24/docs/sphinx/pages.rst       |   19 +
 .../libraries/RF24/docs/sphinx/requirements.txt    |    2 +
 arduino/libraries/RF24/docs/using_cmake.md         |  170 ++
 arduino/libraries/RF24/doxygen-custom.css          |  815 ++++++
 .../AcknowledgementPayloads.ino                    |  208 ++
 .../examples/GettingStarted/GettingStarted.ino     |  154 ++
 .../InterruptConfigure/InterruptConfigure.ino      |  346 +++
 .../ManualAcknowledgements.ino                     |  220 ++
 .../examples/MulticeiverDemo/MulticeiverDemo.ino   |  198 ++
 .../RF24/examples/StreamingData/StreamingData.ino  |  185 ++
 .../RF24/examples/examples_formatter.conf          |   31 +
 .../GettingStarted_HandlingFailures.ino            |  217 ++
 .../TransferTimeouts/TransferTimeouts.ino          |  191 ++
 .../old_backups/pingpair_dyn/pingpair_dyn.ino      |  182 ++
 .../old_backups/pingpair_irq/pingpair_irq.ino      |  172 ++
 .../pingpair_multi_dyn/pingpair_multi_dyn.ino      |  263 ++
 .../pingpair_sleepy/pingpair_sleepy.ino            |  226 ++
 .../old_backups/recipes/led_remote/led_remote.ino  |  254 ++
 .../old_backups/recipes/nordic_fob/nordic_fob.ino  |  142 ++
 .../old_backups/recipes/pingpair_maple/main.cpp    |   87 +
 .../recipes/pingpair_maple/pingpair_maple.ino      |  242 ++
 .../RF24/examples/old_backups/recipes/readme.md    |    2 +
 .../examples/rf24_ATTiny/rf24ping85/rf24ping85.ino |  204 ++
 .../timingSearch3pin/timingSearch3pin.ino          |  209 ++
 .../libraries/RF24/examples/scanner/scanner.ino    |  157 ++
 .../libraries/RF24/examples_linux/CMakeLists.txt   |   46 +
 .../RF24/examples_linux/Makefile.examples          |   52 +
 .../examples_linux/acknowledgementPayloads.cpp     |  230 ++
 .../examples_linux/acknowledgement_payloads.py     |  234 ++
 .../RF24/examples_linux/extra/rpi-hub.cpp          |  125 +
 .../RF24/examples_linux/getting_started.py         |  203 ++
 .../RF24/examples_linux/gettingstarted.cpp         |  191 ++
 .../RF24/examples_linux/interruptConfigure.cpp     |  319 +++
 .../RF24/examples_linux/interrupt_configure.py     |  300 +++
 .../gettingstarted_call_response_int.cpp           |  147 ++
 .../gettingstarted_call_response_int2.cpp          |  152 ++
 .../examples_linux/interrupts/pingpair_dyn_int.cpp |  177 ++
 .../interrupts/transfer_interrupt.cpp              |  190 ++
 .../RF24/examples_linux/manualAcknowledgements.cpp |  259 ++
 .../RF24/examples_linux/manual_acknowledgements.py |  254 ++
 .../RF24/examples_linux/multiceiverDemo.cpp        |  273 +++
 .../RF24/examples_linux/multiceiver_demo.py        |  204 ++
 arduino/libraries/RF24/examples_linux/readme.md    |    3 +
 arduino/libraries/RF24/examples_linux/scanner.cpp  |  131 +
 .../RF24/examples_linux/streamingData.cpp          |  291 +++
 .../RF24/examples_linux/streaming_data.py          |  221 ++
 .../libraries/RF24/examples_pico/CMakeLists.txt    |   45 +
 .../RF24/examples_pico/acknowledgementPayloads.cpp |  229 ++
 arduino/libraries/RF24/examples_pico/defaultPins.h |   33 +
 .../RF24/examples_pico/gettingStarted.cpp          |  169 ++
 .../RF24/examples_pico/interruptConfigure.cpp      |  367 +++
 .../RF24/examples_pico/manualAcknowledgements.cpp  |  239 ++
 .../RF24/examples_pico/multiceiverDemo.cpp         |  220 ++
 arduino/libraries/RF24/examples_pico/scanner.cpp   |  123 +
 .../libraries/RF24/examples_pico/streamingData.cpp |  199 ++
 .../libraries/RF24/images/ghetto_sheilding_1.png   |  Bin 0 -> 1376364 bytes
 .../libraries/RF24/images/ghetto_sheilding_2.png   |  Bin 0 -> 1238256 bytes
 arduino/libraries/RF24/images/pinout.jpg           |  Bin 0 -> 40056 bytes
 arduino/libraries/RF24/keywords.txt                |   59 +
 arduino/libraries/RF24/library.json                |   43 +
 arduino/libraries/RF24/library.properties          |    9 +
 arduino/libraries/RF24/nRF24L01.h                  |  128 +
 arduino/libraries/RF24/printf.h                    |   46 +
 .../libraries/RF24/pyRF24/crossunixccompiler.py    |   70 +
 arduino/libraries/RF24/pyRF24/pyRF24.cpp           |  338 +++
 arduino/libraries/RF24/pyRF24/setup.py             |  108 +
 .../RF24/utility/ATTiny/RF24_arch_config.h         |   53 +
 .../RF24/utility/ATXMegaD3/RF24_arch_config.h      |   84 +
 .../RF24/utility/ATXMegaD3/compatibility.c         |   64 +
 .../RF24/utility/ATXMegaD3/compatibility.h         |   49 +
 arduino/libraries/RF24/utility/ATXMegaD3/gpio.cpp  |   46 +
 arduino/libraries/RF24/utility/ATXMegaD3/gpio.h    |   64 +
 .../libraries/RF24/utility/ATXMegaD3/gpio_helper.c |   39 +
 .../libraries/RF24/utility/ATXMegaD3/gpio_helper.h |   86 +
 .../libraries/RF24/utility/ATXMegaD3/includes.h    |   30 +
 arduino/libraries/RF24/utility/ATXMegaD3/spi.cpp   |   64 +
 arduino/libraries/RF24/utility/ATXMegaD3/spi.h     |   88 +
 arduino/libraries/RF24/utility/CMakeLists.txt      |  102 +
 .../RF24/utility/LittleWire/RF24_arch_config.h     |   39 +
 .../libraries/RF24/utility/LittleWire/includes.h   |   30 +
 .../libraries/RF24/utility/MRAA/RF24_arch_config.h |   64 +
 .../libraries/RF24/utility/MRAA/compatibility.cpp  |   44 +
 .../libraries/RF24/utility/MRAA/compatibility.h    |   32 +
 arduino/libraries/RF24/utility/MRAA/gpio.cpp       |   87 +
 arduino/libraries/RF24/utility/MRAA/gpio.h         |   68 +
 arduino/libraries/RF24/utility/MRAA/includes.h     |   10 +
 arduino/libraries/RF24/utility/MRAA/spi.cpp        |   59 +
 arduino/libraries/RF24/utility/MRAA/spi.h          |   66 +
 .../libraries/RF24/utility/RPi/RF24_arch_config.h  |   47 +
 arduino/libraries/RF24/utility/RPi/bcm2835.c       | 2031 +++++++++++++++
 arduino/libraries/RF24/utility/RPi/bcm2835.h       | 2029 +++++++++++++++
 .../libraries/RF24/utility/RPi/compatibility.cpp   |   11 +
 arduino/libraries/RF24/utility/RPi/compatibility.h |   17 +
 arduino/libraries/RF24/utility/RPi/includes.h      |   10 +
 arduino/libraries/RF24/utility/RPi/interrupt.cpp   |  221 ++
 arduino/libraries/RF24/utility/RPi/interrupt.h     |   72 +
 arduino/libraries/RF24/utility/RPi/spi.cpp         |   66 +
 arduino/libraries/RF24/utility/RPi/spi.h           |  100 +
 .../RF24/utility/SPIDEV/RF24_arch_config.h         |   68 +
 .../RF24/utility/SPIDEV/compatibility.cpp          |   50 +
 .../libraries/RF24/utility/SPIDEV/compatibility.h  |   34 +
 arduino/libraries/RF24/utility/SPIDEV/gpio.cpp     |  167 ++
 arduino/libraries/RF24/utility/SPIDEV/gpio.h       |   94 +
 arduino/libraries/RF24/utility/SPIDEV/includes.h   |    9 +
 .../libraries/RF24/utility/SPIDEV/interrupt.cpp    |  221 ++
 arduino/libraries/RF24/utility/SPIDEV/interrupt.h  |   72 +
 arduino/libraries/RF24/utility/SPIDEV/spi.cpp      |  191 ++
 arduino/libraries/RF24/utility/SPIDEV/spi.h        |   93 +
 .../RF24/utility/Teensy/RF24_arch_config.h         |   30 +
 .../RF24/utility/Template/RF24_arch_config.h       |   82 +
 .../RF24/utility/Template/compatibility.h          |   46 +
 arduino/libraries/RF24/utility/Template/gpio.h     |   65 +
 arduino/libraries/RF24/utility/Template/includes.h |   29 +
 arduino/libraries/RF24/utility/Template/spi.h      |   86 +
 arduino/libraries/RF24/utility/rp2/CMakeLists.txt  |   21 +
 .../libraries/RF24/utility/rp2/RF24_arch_config.h  |   62 +
 arduino/libraries/RF24/utility/rp2/gpio.cpp        |   26 +
 arduino/libraries/RF24/utility/rp2/gpio.h          |   57 +
 arduino/libraries/RF24/utility/rp2/spi.cpp         |   52 +
 arduino/libraries/RF24/utility/rp2/spi.h           |   91 +
 .../RF24/utility/wiringPi/RF24_arch_config.h       |   55 +
 arduino/libraries/RF24/utility/wiringPi/includes.h |   29 +
 arduino/libraries/RF24/utility/wiringPi/spi.cpp    |   88 +
 arduino/libraries/RF24/utility/wiringPi/spi.h      |   72 +
 arduino/libraries/SD/README.adoc                   |   26 +
 .../libraries/SD/examples/CardInfo/CardInfo.ino    |  116 +
 .../SD/examples/Datalogger/Datalogger.ino          |   84 +
 .../libraries/SD/examples/DumpFile/DumpFile.ino    |   65 +
 arduino/libraries/SD/examples/Files/Files.ino      |   75 +
 .../examples/NonBlockingWrite/NonBlockingWrite.ino |   91 +
 .../libraries/SD/examples/ReadWrite/ReadWrite.ino  |   79 +
 .../libraries/SD/examples/listfiles/listfiles.ino  |   80 +
 .../SD/extras/codespell-ignore-words-list.txt      |    0
 arduino/libraries/SD/keywords.txt                  |   31 +
 arduino/libraries/SD/library.properties            |    9 +
 arduino/libraries/SD/src/File.cpp                  |  168 ++
 arduino/libraries/SD/src/README.txt                |   13 +
 arduino/libraries/SD/src/SD.cpp                    |  637 +++++
 arduino/libraries/SD/src/SD.h                      |  138 ++
 arduino/libraries/SD/src/utility/FatStructs.h      |  418 ++++
 arduino/libraries/SD/src/utility/Sd2Card.cpp       |  777 ++++++
 arduino/libraries/SD/src/utility/Sd2Card.h         |  273 +++
 arduino/libraries/SD/src/utility/Sd2PinMap.h       |  525 ++++
 arduino/libraries/SD/src/utility/SdFat.h           |  641 +++++
 arduino/libraries/SD/src/utility/SdFatUtil.h       |   77 +
 arduino/libraries/SD/src/utility/SdFatmainpage.h   |  202 ++
 arduino/libraries/SD/src/utility/SdFile.cpp        | 1527 ++++++++++++
 arduino/libraries/SD/src/utility/SdInfo.h          |  232 ++
 arduino/libraries/SD/src/utility/SdVolume.cpp      |  351 +++
 arduino/libraries/elapsedMillis/LICENSE            |   21 +
 arduino/libraries/elapsedMillis/README.md          |   12 +
 arduino/libraries/elapsedMillis/elapsedMillis.h    |  104 +
 .../examples/GettingStarted/GettingStarted.ino     |   41 +
 .../examples/blinkingLeds/blinkingLeds.ino         |   85 +
 .../elapsedMillis/examples/timeouts/timeouts.ino   |  145 ++
 .../examples/timingComparison/timingComparison.ino |  163 ++
 arduino/libraries/elapsedMillis/keywords.txt       |   19 +
 arduino/libraries/elapsedMillis/library.json       |   26 +
 arduino/libraries/elapsedMillis/library.properties |    9 +
 265 files changed, 37395 insertions(+)
---
diff --git a/arduino/libraries/HX711/HX711.cpp b/arduino/libraries/HX711/HX711.cpp
new file mode 100644
index 000000000..c80524e1b
--- /dev/null
+++ b/arduino/libraries/HX711/HX711.cpp
@@ -0,0 +1,134 @@
+#include <Arduino.h>
+#include <HX711.h>
+
+#if ARDUINO_VERSION <= 106
+    // "yield" is not implemented as noop in older Arduino Core releases, so let's define it.
+    // See also: 
https://stackoverflow.com/questions/34497758/what-is-the-secret-of-the-arduino-yieldfunction/34498165#34498165
+    void yield(void) {};
+#endif
+
+HX711::HX711(byte dout, byte pd_sck, byte gain) {
+       begin(dout, pd_sck, gain);
+}
+
+HX711::HX711() {
+}
+
+HX711::~HX711() {
+}
+
+void HX711::begin(byte dout, byte pd_sck, byte gain) {
+       PD_SCK = pd_sck;
+       DOUT = dout;
+
+       pinMode(PD_SCK, OUTPUT);
+       pinMode(DOUT, INPUT);
+
+       set_gain(gain);
+}
+
+bool HX711::is_ready() {
+       return digitalRead(DOUT) == LOW;
+}
+
+void HX711::set_gain(byte gain) {
+       switch (gain) {
+               case 128:               // channel A, gain factor 128
+                       GAIN = 1;
+                       break;
+               case 64:                // channel A, gain factor 64
+                       GAIN = 3;
+                       break;
+               case 32:                // channel B, gain factor 32
+                       GAIN = 2;
+                       break;
+       }
+
+       digitalWrite(PD_SCK, LOW);
+       read();
+}
+
+long HX711::read() {
+       // wait for the chip to become ready
+       while (!is_ready()) {
+               // Will do nothing on Arduino but prevent resets of ESP8266 (Watchdog Issue)
+               yield();
+       }
+
+       unsigned long value = 0;
+       uint8_t data[3] = { 0 };
+       uint8_t filler = 0x00;
+
+       // pulse the clock pin 24 times to read the data
+       data[2] = shiftIn(DOUT, PD_SCK, MSBFIRST);
+       data[1] = shiftIn(DOUT, PD_SCK, MSBFIRST);
+       data[0] = shiftIn(DOUT, PD_SCK, MSBFIRST);
+
+       // set the channel and the gain factor for the next reading using the clock pin
+       for (unsigned int i = 0; i < GAIN; i++) {
+               digitalWrite(PD_SCK, HIGH);
+               digitalWrite(PD_SCK, LOW);
+       }
+
+       // Replicate the most significant bit to pad out a 32-bit signed integer
+       if (data[2] & 0x80) {
+               filler = 0xFF;
+       } else {
+               filler = 0x00;
+       }
+
+       // Construct a 32-bit signed integer
+       value = ( static_cast<unsigned long>(filler) << 24
+                       | static_cast<unsigned long>(data[2]) << 16
+                       | static_cast<unsigned long>(data[1]) << 8
+                       | static_cast<unsigned long>(data[0]) );
+
+       return static_cast<long>(value);
+}
+
+long HX711::read_average(byte times) {
+       long sum = 0;
+       for (byte i = 0; i < times; i++) {
+               sum += read();
+               yield();
+       }
+       return sum / times;
+}
+
+double HX711::get_value(byte times) {
+       return read_average(times) - OFFSET;
+}
+
+float HX711::get_units(byte times) {
+       return get_value(times) / SCALE;
+}
+
+void HX711::tare(byte times) {
+       double sum = read_average(times);
+       set_offset(sum);
+}
+
+void HX711::set_scale(float scale) {
+       SCALE = scale;
+}
+
+float HX711::get_scale() {
+       return SCALE;
+}
+
+void HX711::set_offset(long offset) {
+       OFFSET = offset;
+}
+
+long HX711::get_offset() {
+       return OFFSET;
+}
+
+void HX711::power_down() {
+       digitalWrite(PD_SCK, LOW);
+       digitalWrite(PD_SCK, HIGH);
+}
+
+void HX711::power_up() {
+       digitalWrite(PD_SCK, LOW);
+}
diff --git a/arduino/libraries/HX711/HX711.h b/arduino/libraries/HX711/HX711.h
new file mode 100644
index 000000000..23b2234bc
--- /dev/null
+++ b/arduino/libraries/HX711/HX711.h
@@ -0,0 +1,77 @@
+#ifndef HX711_h
+#define HX711_h
+
+#if ARDUINO >= 100
+#include "Arduino.h"
+#else
+#include "WProgram.h"
+#endif
+
+class HX711
+{
+       private:
+               byte PD_SCK;    // Power Down and Serial Clock Input Pin
+               byte DOUT;              // Serial Data Output Pin
+               byte GAIN;              // amplification factor
+               long OFFSET = 0;        // used for tare weight
+               float SCALE = 1;        // used to return weight in grams, kg, ounces, whatever
+
+       public:
+               // define clock and data pin, channel, and gain factor
+               // channel selection is made by passing the appropriate gain: 128 or 64 for channel A, 32 for 
channel B
+               // gain: 128 or 64 for channel A; channel B works with 32 gain factor only
+               HX711(byte dout, byte pd_sck, byte gain = 128);
+
+               HX711();
+
+               virtual ~HX711();
+
+               // Allows to set the pins and gain later than in the constructor
+               void begin(byte dout, byte pd_sck, byte gain = 128);
+
+               // check if HX711 is ready
+               // from the datasheet: When output data is not ready for retrieval, digital output pin DOUT 
is high. Serial clock
+               // input PD_SCK should be low. When DOUT goes to low, it indicates data is ready for 
retrieval.
+               bool is_ready();
+
+               // set the gain factor; takes effect only after a call to read()
+               // channel A can be set for a 128 or 64 gain; channel B has a fixed 32 gain
+               // depending on the parameter, the channel is also set to either A or B
+               void set_gain(byte gain = 128);
+
+               // waits for the chip to be ready and returns a reading
+               long read();
+
+               // returns an average reading; times = how many times to read
+               long read_average(byte times = 10);
+
+               // returns (read_average() - OFFSET), that is the current value without the tare weight; 
times = how many readings to do
+               double get_value(byte times = 1);
+
+               // returns get_value() divided by SCALE, that is the raw value divided by a value obtained 
via calibration
+               // times = how many readings to do
+               float get_units(byte times = 1);
+
+               // set the OFFSET value for tare weight; times = how many times to read the tare value
+               void tare(byte times = 10);
+
+               // set the SCALE value; this value is used to convert the raw data to "human readable" data 
(measure units)
+               void set_scale(float scale = 1.f);
+
+               // get the current SCALE
+               float get_scale();
+
+               // set OFFSET, the value that's subtracted from the actual reading (tare weight)
+               void set_offset(long offset = 0);
+
+               // get the current OFFSET
+               long get_offset();
+
+               // puts the chip into power down mode
+               void power_down();
+
+               // wakes up the chip after power down mode
+               void power_up();
+};
+
+#endif /* HX711_h */
diff --git a/arduino/libraries/HX711/LICENSE b/arduino/libraries/HX711/LICENSE
new file mode 100644
index 000000000..d7f105139
--- /dev/null
+++ b/arduino/libraries/HX711/LICENSE
@@ -0,0 +1,339 @@
+GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    {description}
+    Copyright (C) {year}  {fullname}
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  {signature of Ty Coon}, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/arduino/libraries/HX711/README.md b/arduino/libraries/HX711/README.md
new file mode 100644
index 000000000..db587f1aa
--- /dev/null
+++ b/arduino/libraries/HX711/README.md
@@ -0,0 +1,35 @@
+HX711
+=====
+
+An Arduino library to interface the Avia Semiconductor HX711 24-Bit Analog-to-Digital Converter (ADC) for 
Weight Scales.
+
+This is my humble attempt at creating an Arduino library for this ADC:
+http://www.dfrobot.com/image/data/SEN0160/hx711_english.pdf
+
+Other libraries exist, including this very good one, which I first used and which is the starting point for 
my library:
+https://github.com/aguegu/ardulibs/tree/master/hx711
+
+# Advantages of this library
+Although other libraries exist, I needed a slightly different approach, so here's how my library is 
different than others:
+
+1. It provides a tare() function, which "resets" the scale to 0. Many other implementations calculate the 
tare weight when the ADC is initialized only. I needed a way to be able to set the tare weight at any time. 
Use case: place an empty container on the scale, call tare() to reset the readings to 0, fill the container 
and get the weight of the content.
+
+2. It provides a power_down() function, to put the ADC into a low power mode. According to the datasheet, 
"When PD_SCK pin changes from low to high and stays at high for longer than 60μs, HX711 enters power down 
mode". Use case: battery powered scales. Accordingly, there is a power_up() function to get the chip out of 
the low power mode.
+
+3. It has a set_gain(byte gain) function that allows you to set the gain factor and select the channel. 
According to the datasheet, "Channel A can be programmed with a gain of 128 or 64, corresponding to a 
full-scale differential input voltage of ±20mV or ±40mV respectively, when a 5V supply is connected to AVDD 
analog power supply pin. Channel B has a fixed gain of 32.". The same function is used to select the channel 
A or channel B, by passing 128 or 64 for channel A, or 32 for channel B as the parameter. The default value 
is 128, which means "channel A with a gain factor of 128", so one can simply call set_gain(). Also, the 
function is called from the constructor.
+
+4. The constructor has an extra parameter "gain" that allows you to set the gain factor and channel. The 
constructor calls the "set_gain" function mentioned above.
+
+5. The "get_value" and "get_units" functions can receive an extra parameter "times", and they will return 
the average of multiple readings instead of a single reading.
+
+# How to Calibrate Your Scale
+
+1. Call set_scale() with no parameter.
+2. Call tare() with no parameter.
+3. Place a known weight on the scale and call get_units(10).
+4. Divide the result in step 3 to your known weight. You should get about the parameter you need to pass to 
set_scale.
+5. Adjust the parameter in step 4 until you get an accurate reading.
+
+# How to use
+See the example in examples/HX711SerialBegin. Please don't use examples/HX711Serial anymore. It is 
deprecated because the pin definition within the constructor
+is not timing safe. (#29)
diff --git a/arduino/libraries/HX711/examples/HX711Serial/HX711Serial.ino 
b/arduino/libraries/HX711/examples/HX711Serial/HX711Serial.ino
new file mode 100644
index 000000000..dbb64368b
--- /dev/null
+++ b/arduino/libraries/HX711/examples/HX711Serial/HX711Serial.ino
@@ -0,0 +1,56 @@
+#include "HX711.h"
+
+// HX711.DOUT  - pin #A1
+// HX711.PD_SCK        - pin #A0
+
+HX711 scale(A1, A0);           // parameter "gain" is ommited; the default value 128 is used by the library
+
+void setup() {
+  Serial.begin(38400);
+  Serial.println("HX711 Demo");
+
+  Serial.println("Before setting up the scale:");
+  Serial.print("read: \t\t");
+  Serial.println(scale.read());                        // print a raw reading from the ADC
+
+  Serial.print("read average: \t\t");
+  Serial.println(scale.read_average(20));      // print the average of 20 readings from the ADC
+
+  Serial.print("get value: \t\t");
+  Serial.println(scale.get_value(5));          // print the average of 5 readings from the ADC minus the 
tare weight (not set yet)
+
+  Serial.print("get units: \t\t");
+  Serial.println(scale.get_units(5), 1);       // print the average of 5 readings from the ADC minus tare 
weight (not set) divided 
+                                               // by the SCALE parameter (not set yet)  
+
+  scale.set_scale(2280.f);                      // this value is obtained by calibrating the scale with 
known weights; see the README for details
+  scale.tare();                                        // reset the scale to 0
+
+  Serial.println("After setting up the scale:");
+
+  Serial.print("read: \t\t");
+  Serial.println(scale.read());                 // print a raw reading from the ADC
+
+  Serial.print("read average: \t\t");
+  Serial.println(scale.read_average(20));       // print the average of 20 readings from the ADC
+
+  Serial.print("get value: \t\t");
+  Serial.println(scale.get_value(5));          // print the average of 5 readings from the ADC minus the 
tare weight, set with tare()
+
+  Serial.print("get units: \t\t");
+  Serial.println(scale.get_units(5), 1);        // print the average of 5 readings from the ADC minus tare 
weight, divided 
+                                               // by the SCALE parameter set with set_scale
+
+  Serial.println("Readings:");
+}
+
+void loop() {
+  Serial.print("one reading:\t");
+  Serial.print(scale.get_units(), 1);
+  Serial.print("\t| average:\t");
+  Serial.println(scale.get_units(10), 1);
+
+  scale.power_down();                          // put the ADC in sleep mode
+  delay(5000);
+  scale.power_up();
+}
\ No newline at end of file
diff --git a/arduino/libraries/HX711/examples/HX711SerialBegin/HX711SerialBegin.ino 
b/arduino/libraries/HX711/examples/HX711SerialBegin/HX711SerialBegin.ino
new file mode 100644
index 000000000..6963afa92
--- /dev/null
+++ b/arduino/libraries/HX711/examples/HX711SerialBegin/HX711SerialBegin.ino
@@ -0,0 +1,59 @@
+#include "HX711.h"
+
+HX711 scale;
+
+void setup() {
+  Serial.begin(38400);
+  Serial.println("HX711 Demo");
+
+  Serial.println("Initializing the scale");
+  // parameter "gain" is ommited; the default value 128 is used by the library
+  // HX711.DOUT        - pin #A1
+  // HX711.PD_SCK      - pin #A0
+  scale.begin(A1, A0);
+
+  Serial.println("Before setting up the scale:");
+  Serial.print("read: \t\t");
+  Serial.println(scale.read());                        // print a raw reading from the ADC
+
+  Serial.print("read average: \t\t");
+  Serial.println(scale.read_average(20));      // print the average of 20 readings from the ADC
+
+  Serial.print("get value: \t\t");
+  Serial.println(scale.get_value(5));          // print the average of 5 readings from the ADC minus the 
tare weight (not set yet)
+
+  Serial.print("get units: \t\t");
+  Serial.println(scale.get_units(5), 1);       // print the average of 5 readings from the ADC minus tare 
weight (not set) divided
+                                               // by the SCALE parameter (not set yet)
+
+  scale.set_scale(2280.f);                      // this value is obtained by calibrating the scale with 
known weights; see the README for details
+  scale.tare();                                        // reset the scale to 0
+
+  Serial.println("After setting up the scale:");
+
+  Serial.print("read: \t\t");
+  Serial.println(scale.read());                 // print a raw reading from the ADC
+
+  Serial.print("read average: \t\t");
+  Serial.println(scale.read_average(20));       // print the average of 20 readings from the ADC
+
+  Serial.print("get value: \t\t");
+  Serial.println(scale.get_value(5));          // print the average of 5 readings from the ADC minus the 
tare weight, set with tare()
+
+  Serial.print("get units: \t\t");
+  Serial.println(scale.get_units(5), 1);        // print the average of 5 readings from the ADC minus tare 
weight, divided
+                                               // by the SCALE parameter set with set_scale
+
+  Serial.println("Readings:");
+}
+
+void loop() {
+  Serial.print("one reading:\t");
+  Serial.print(scale.get_units(), 1);
+  Serial.print("\t| average:\t");
+  Serial.println(scale.get_units(10), 1);
+
+  scale.power_down();                          // put the ADC in sleep mode
+  delay(5000);
+  scale.power_up();
+}
diff --git a/arduino/libraries/HX711/library.json b/arduino/libraries/HX711/library.json
new file mode 100644
index 000000000..d4c945266
--- /dev/null
+++ b/arduino/libraries/HX711/library.json
@@ -0,0 +1,17 @@
+{
+    "name": "HX711",
+    "keywords": "hx711, scale, weight",
+    "description": "An Arduino library to interface the Avia Semiconductor HX711 24-Bit Analog-to-Digital 
Converter (ADC) for Weight Scales.",
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/bogde/HX711.git";
+    },
+    "version": "0.1",
+    "exclude": "tests",
+    "examples": "examples/*/*.ino",
+    "frameworks": "arduino",
+    "platforms": [
+        "atmelavr",
+        "espressif"
+    ]
+}
diff --git a/arduino/libraries/RF24/CMakeLists.txt b/arduino/libraries/RF24/CMakeLists.txt
new file mode 100644
index 000000000..e3414b267
--- /dev/null
+++ b/arduino/libraries/RF24/CMakeLists.txt
@@ -0,0 +1,167 @@
+# Check if we are building a pico-sdk based project
+# (or more exactly: if we just got included in a pico-sdk based project)
+if (PICO_SDK_PATH)
+    # If so, load the relevant CMakeLists-file but don't do anything else
+    include(${CMAKE_CURRENT_LIST_DIR}/utility/rp2/CMakeLists.txt)
+    return()
+endif()
+
+cmake_minimum_required(VERSION 3.15)
+
+# Set the project name to your project name
+project(RF24 C CXX)
+include(${CMAKE_CURRENT_LIST_DIR}/cmake/StandardProjectSettings.cmake)
+include(${CMAKE_CURRENT_LIST_DIR}/cmake/PreventInSourceBuilds.cmake)
+
+# get library info from Arduino IDE's required library.properties file
+include(${CMAKE_CURRENT_LIST_DIR}/cmake/GetLibInfo.cmake)
+
+# allow CMake CLI options to configure RF24_config.h macros
+option(SERIAL_DEBUG "enable/disable debugging output" OFF)
+option(MINIMAL "exclude optional source code to keep compile size compact" OFF)
+
+# Link this 'library' to set the c++ standard / compile-time options requested
+add_library(${LibTargetName}_project_options INTERFACE)
+target_compile_features(${LibTargetName}_project_options INTERFACE cxx_std_17)
+add_compile_options(-Ofast -Wall -pthread)
+
+if(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
+    option(ENABLE_BUILD_WITH_TIME_TRACE "Enable -ftime-trace to generate time tracing .json files on clang" 
OFF)
+    if(ENABLE_BUILD_WITH_TIME_TRACE)
+        add_compile_definitions(${LibTargetName}_project_options INTERFACE -ftime-trace)
+    endif()
+endif()
+
+# Link this 'library' to use the warnings specified in CompilerWarnings.cmake
+add_library(${LibTargetName}_project_warnings INTERFACE)
+
+# enable cache system
+include(${CMAKE_CURRENT_LIST_DIR}/cmake/Cache.cmake)
+
+# standard compiler warnings
+include(${CMAKE_CURRENT_LIST_DIR}/cmake/CompilerWarnings.cmake)
+set_project_warnings(${LibTargetName}_project_warnings)
+
+# sanitizer options if supported by compiler
+include(${CMAKE_CURRENT_LIST_DIR}/cmake/Sanitizers.cmake)
+enable_sanitizers(${LibTargetName}_project_options)
+
+# allow for static analysis options
+include(${CMAKE_CURRENT_LIST_DIR}/cmake/StaticAnalyzers.cmake)
+
+option(BUILD_SHARED_LIBS "Enable compilation of shared libraries" OFF)
+option(ENABLE_TESTING "Enable Test Builds" OFF) # for end-user projects
+option(ENABLE_FUZZING "Enable Fuzzing Builds" OFF) # for end-user projects
+
+if(ENABLE_TESTING)
+    enable_testing()
+    message("Building Tests.")
+    add_subdirectory(test) # directory doesn't exist, so this does nothing.
+endif()
+
+if(ENABLE_FUZZING)
+    message("Building Fuzz Tests, using fuzzing sanitizer https://www.llvm.org/docs/LibFuzzer.html";)
+    add_subdirectory(fuzz_test) # directory doesn't exist, so this does nothing.
+endif()
+
+#####################################
+### Now we actually build the library
+#####################################
+
+# detect the CPU make and type
+include(${CMAKE_CURRENT_LIST_DIR}/cmake/detectCPU.cmake) # sets the variable SOC accordingly
+
+# auto-detect what driver to use
+# auto-detect can be overriden using `cmake .. -D RF24_DRIVER=<supported driver>`
+include(${CMAKE_CURRENT_LIST_DIR}/cmake/AutoConfig_RF24_DRIVER.cmake)
+
+#[[ adding the utility sub-directory will
+    1. set variables RF24_DRIVER, RF24_LINKED_DRIVER, and RF24_DRIVER_SOURCES
+    2. copy the approriate /utility/*/includes.h file to the /utility folder
+    3. set additional install rules according to the RF24_DRIVER specified
+]]
+add_subdirectory(utility)
+
+# setup CPack options
+# package dependencies are resolved correctly only after utility subdirectory is added
+include(${CMAKE_CURRENT_LIST_DIR}/cmake/CPackInfo.cmake)
+
+add_library(${LibTargetName} SHARED
+    RF24.cpp
+    ${RF24_DRIVER_SOURCES}
+)
+
+target_include_directories(${LibTargetName} PUBLIC utility)
+
+set_target_properties(
+    ${LibTargetName}
+    PROPERTIES
+    SOVERSION ${${LibName}_VERSION_MAJOR}
+    VERSION ${${LibName}_VERSION_STRING}
+)
+
+if(NOT "${RF24_LINKED_DRIVER}" STREQUAL "") # linking to a pre-compiled utility driver
+    message(STATUS "Using utility library: ${RF24_LINKED_DRIVER}")
+    target_link_libraries(${LibTargetName} INTERFACE
+        ${LibTargetName}_project_options
+        ${LibTargetName}_project_warnings
+        STATIC RF24_LINKED_DRIVER
+    )
+else() # utility driver is compiled with the library - not linking to a pre-compiled utility driver
+    target_link_libraries(${LibTargetName} INTERFACE
+        ${LibTargetName}_project_options
+        ${LibTargetName}_project_warnings
+    )
+endif()
+
+# assert the appropriate preprocessor macros for RF24_config.h
+if(SERIAL_DEBUG)
+    message(STATUS "SERIAL_DEBUG asserted")
+    target_compile_definitions(${LibTargetName} PUBLIC SERIAL_DEBUG)
+endif()
+if(MINIMAL)
+    message(STATUS "MINIMAL asserted")
+    target_compile_definitions(${LibTargetName} PUBLIC MINIMAL)
+endif()
+# for RF24_POWERUP_DELAY & RF24_SPI_SPEED, let the default be configured in source code
+if(DEFINED RF24_POWERUP_DELAY)
+    message(STATUS "RF24_POWERUP_DELAY set to ${RF24_POWERUP_DELAY}")
+    target_compile_definitions(${LibTargetName} PUBLIC
+        RF24_POWERUP_DELAY=${RF24_POWERUP_DELAY}
+    )
+endif()
+if(DEFINED RF24_SPI_SPEED)
+    message(STATUS "RF24_SPI_SPEED set to ${RF24_SPI_SPEED}")
+    target_compile_definitions(${LibTargetName} PUBLIC
+        RF24_SPI_SPEED=${RF24_SPI_SPEED}
+    )
+endif()
+
+
+#####################################
+### Install rules for root source dir
+### There are separate install rules defined for each utility driver
+### Installing the library requires sudo privileges
+#####################################
+install(TARGETS ${LibTargetName}
+    DESTINATION lib
+)
+
+install(FILES
+        RF24.h
+        nRF24L01.h
+        printf.h
+        RF24_config.h
+    DESTINATION include/RF24
+)
+
+install(FILES
+        utility/includes.h
+    DESTINATION include/RF24/utility
+)
+
+# CMAKE_CROSSCOMPILING is only TRUE when CMAKE_TOOLCHAIN_FILE is specified via CLI
+if("${CMAKE_CROSSCOMPILING}" STREQUAL "FALSE")
+    install(CODE "message(STATUS \"Updating ldconfig\")")
+    install(CODE "execute_process(COMMAND ldconfig)")
+endif()
diff --git a/arduino/libraries/RF24/COMMON_ISSUES.md b/arduino/libraries/RF24/COMMON_ISSUES.md
new file mode 100644
index 000000000..79000fcf5
--- /dev/null
+++ b/arduino/libraries/RF24/COMMON_ISSUES.md
@@ -0,0 +1,100 @@
+# Common Issues
+
+<!-- markdownlint-disable MD031 -->
+
+## Settings that must match
+
+Before you report undesirable behavior, please make sure that the
+following RF24 configurations match on both receiving and transmitting
+nRF24L01 transceivers:
+
+1. `RF24::setAddressWidth()`
+2. `RF24::setChannel()`
+3. `RF24::setDataRate()`
+4. `RF24::setAutoAck()`
+5. `RF24::enableDynamicPayloads()` or `RF24::disableDynamicPayloads()`
+6. `RF24::enableAckPayload()` or `RF24::disableAckPayload()` (requires auto-ack and
+   dynamic payloads features)
+7. `RF24::setPayloadSize()` (only if the dynamic payloads feature is disabled -- it is disabled by default)
+8. `RF24::setCRCLength()` or `RF24::disableCRC()` (the auto-ack feature
+   automatically enables CRC because it is required)
+
+Also, it helps to think of an address as a path (a commonly shared route)
+instead of an identifying device destination. This means that addresses
+have to match for a payload to travel from one transceiver to another.
+However, the pipe numbers assigned with the matching address do not have
+to match. You can think of pipes as parking spots for the packets, while
+all packets' payloads live in a TX or RX FIFO buffer. Remember that the
+TX FIFO buffers and the RX FIFO buffers both have a maximum occupancy of
+3 payloads (regardless of the maximum 32-byte payload size).
+
+## Prohibited usage of write*() in Interrupt Service Routine callbacks
+
+Because the RF24 library uses `millis()` to implement a timeout and `delay()` for mandatory wait times, the 
following functions cannot be used within an ISR callback method:
+
+- `RF24::write()`
+- `RF24::writeBlocking()`
+- `RF24::writeFast()`
+- `RF24::startWrite()`
+- `RF24::txStandBy()` & `RF24::txStandBy(uint32_t, bool)`
+- `RF24::powerUp()`
+- `RF24::startListening()`
+- `RF24::stopListening()`
+
+@see The note in the documentation for `RF24::available()`.
+
+More info about why you can't call `millis()` (or `delay()`) from an ISR callback function is available at 
[the Arduino 
docs](https://www.google.com/url?sa=t&source=web&rct=j&url=https://www.arduino.cc/reference/en/language/functions/external-interrupts/attachinterrupt/&ved=2ahUKEwjMhtSRl5jzAhVUsp4KHWIPCrIQFnoECAoQAQ&usg=AOvVaw1X9H0058Nz7Hck91VIC3bD).
+
+## Here are the most common issues and their solutions
+
+### write() always returns true after setAutoAck(false)
+
+Don't disabled the auto-ack feature. RF24::write() has no reason to doubt
+that the payload was delivered if the auto-ack feature is disabled. We
+recommend you read the docs about RF24::setAutoAck() before disabling the
+auto-ack feature.
+
+### write() returns false when the payload was received
+
+If the settings match on both endpoint transceivers, then this can only
+mean that the receiving nRF24L01 failed to send an acknowledgement (ACK)
+packet back to the transmitting nRF24L01. Usually this is due to
+instability (electric noise) in the power lines (VCC and GND) going to
+the receiving nRF24L01.
+
+If you're not receiving ACK packets correctly/reliably on data rates
+lower than 2MBPS, try adding a big capacitor close to the module/chip.
+Example issues: [#264](https://github.com/nRF24/RF24/issues/264)
+[#211](https://github.com/nRF24/RF24/issues/211).
+
+For reliability, please use Electrolytic or Tantalum capacitors. Ceramic
+capacitors may not be good enough (depending on the manufacturing source).
+
+### Payloads received/sent are inaccurate or printDetails() outputs the unexpected value(s)
+
+This is likely due to the SPI speed being amped up to 10MHz by default. We recommend:
+
+1. Make sure the wires are not loose, and try to avoid using long wires.
+2. If the previous point does not help, then try lowering the SPI speed like so
+   ```cpp
+   RF24 radio(7, 8, 4000000); // set SPI speed to 4MHz instead of default 10MHz
+   ```
+
+In the RF24 library's beginnings, the default value was (prior to 2014) set to 4MHz.
+
+### my PA/LNA module fails to transmit
+
+You may find variants of the nRF24L01 transceiver that are marketed as “nRF24L01+PA+LNA”. These modules are 
distinct in the fact that they come with a detachable (SMA-type) antenna. They employ seperate RFX24C01 IC 
with the antenna for enhanced Power Amplification (PA) and Low Noise Amplification (LNA) features. While they 
boast greater range with the same functionality, they are subject to a couple lesser known (and lesser 
advertised) drawbacks:
+
+1. Stronger power source. Below is a chart of advertised current requirements that many MCU boards’ 3V 
regulators may not be able to provide (after supplying power to internal components).
+   | Specification | Value |
+   |:-------------:|:-----:|
+   | Emission mode current(peak) | 115 mA |
+   | Receive Mode current(peak)  | 45 mA  |
+   | Power-down mode current     | 4.2 µA |
+
+2. Needs shielding from electromagnetic interference. Shielding usually works best when it has a path to 
ground (GND pin), but this connection to the GND pin is not required. It is important that the sheilding does 
not touch any current carrying parts.
+   - Professionals tend to use a faraday cage/mesh to implement electromagnetic shielding, but it can be 
pricey for this scenario.
+   - A quick do-it-yourself solution (as proof-of-concept) would be to wrap the PA/LNA module with 
electrical tape and then wrap foil around the electrical tape (for shielding) while being very careful to not 
let the foil touch any current carrying parts (like the GPIO pins, the antenna mount, and the soldier joints 
for the antenna mount). Observe
+   
[![ghetto_shielding_1.png](https://github.com/nRF24/RF24/blob/master/images/ghetto_sheilding_1.png)](https://github.com/nRF24/RF24/blob/master/images/ghetto_sheilding_1.png)
+   
[![ghetto_shielding_2.png](https://github.com/nRF24/RF24/blob/master/images/ghetto_sheilding_2.png)](https://github.com/nRF24/RF24/blob/master/images/ghetto_sheilding_2.png)
diff --git a/arduino/libraries/RF24/CONTRIBUTING.md b/arduino/libraries/RF24/CONTRIBUTING.md
new file mode 100644
index 000000000..0218e2b4c
--- /dev/null
+++ b/arduino/libraries/RF24/CONTRIBUTING.md
@@ -0,0 +1,16 @@
+# Contributing guidelines
+
+These are the current requirements for getting your code included in RF24:
+
+- Try your best to follow the rest of the code, if you're unsure then [the NASA C style 
guide](https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19950022400.pdf) can help as it's closest to the 
current style.
+
+- Definetly follow [PEP-8](https://www.python.org/dev/peps/pep-0008/) if it's Python code.
+
+- Follow the [Arduino IDE formatting style](https://www.arduino.cc/en/Reference/StyleGuide) for Arduino 
examples
+
+- Add [doxygen-compatible documentation](https://www.doxygen.nl/manual/docblocks.html) to any new functions 
you add, or update existing documentation if you change behaviour
+
+- CMake modules and CMakeLists.txt files should also have a uniform syntax.
+  - Indentation is a mandatory 4 spaces (not a `\t` character).
+  - Closing parenthesis for multi-line commands should have the same indentation as the line that opened the 
parenthesis.
+  - For other useful CMake syntax convention, please see [CMake docs for 
developers](https://cmake.org/cmake/help/v3.20/manual/cmake-developer.7.html) and [this useful best CMake 
practices article](https://gist.github.com/mbinna/c61dbb39bca0e4fb7d1f73b0d66a4fd1). The qiBuild project has 
some [well-reasoned "Dos & Don'ts" 
guideline](http://doc.aldebaran.com/qibuild/hacking/contrib/cmake/coding_guide.html), but beware that the 
nRF24 organization is not related to the qiBuild project in any way.
diff --git a/arduino/libraries/RF24/Doxyfile b/arduino/libraries/RF24/Doxyfile
new file mode 100644
index 000000000..a8cfab286
--- /dev/null
+++ b/arduino/libraries/RF24/Doxyfile
@@ -0,0 +1,2585 @@
+# Doxyfile 1.8.18
+
+# This file describes the settings to be used by the documentation system
+# doxygen (www.doxygen.org) for a project.
+#
+# All text after a double hash (##) is considered a comment and is placed in
+# front of the TAG it is preceding.
+#
+# All text after a single hash (#) is considered a comment and will be ignored.
+# The format is:
+# TAG = value [value, ...]
+# For lists, items can also be appended using:
+# TAG += value [value, ...]
+# Values that contain spaces should be placed between quotes (\" \").
+
+#---------------------------------------------------------------------------
+# Project related configuration options
+#---------------------------------------------------------------------------
+
+# This tag specifies the encoding used for all characters in the configuration
+# file that follow. The default is UTF-8 which is also the encoding used for all
+# text before the first occurrence of this tag. Doxygen uses libiconv (or the
+# iconv built into libc) for the transcoding. See
+# https://www.gnu.org/software/libiconv/ for the list of possible encodings.
+# The default value is: UTF-8.
+
+DOXYFILE_ENCODING      = UTF-8
+
+# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by
+# double-quotes, unless you are using Doxywizard) that should identify the
+# project for which the documentation is generated. This name is used in the
+# title of most generated pages and in a few other places.
+# The default value is: My Project.
+
+PROJECT_NAME           = "Optimized high speed nRF24L01+ driver class documentation"
+
+# The PROJECT_NUMBER tag can be used to enter a project or revision number. This
+# could be handy for archiving the generated documentation or if some version
+# control system is used.
+
+PROJECT_NUMBER         = V1.3.9
+
+# Using the PROJECT_BRIEF tag one can provide an optional one line description
+# for a project that appears at the top of each page and should give viewer a
+# quick idea about the purpose of the project. Keep the description short.
+
+PROJECT_BRIEF          = "TMRh20 2020 - Optimized fork of the nRF24L01+ driver"
+
+# With the PROJECT_LOGO tag one can specify a logo or an icon that is included
+# in the documentation. The maximum height of the logo should not exceed 55
+# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
+# the logo to the output directory.
+
+PROJECT_LOGO           =
+
+# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
+# into which the generated documentation will be written. If a relative path is
+# entered, it will be relative to the location where doxygen was started. If
+# left blank the current directory will be used.
+
+OUTPUT_DIRECTORY       = ./docs
+
+# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub-
+# directories (in 2 levels) under the output directory of each output format and
+# will distribute the generated files over these directories. Enabling this
+# option can be useful when feeding doxygen a huge amount of source files, where
+# putting all generated files in the same directory would otherwise causes
+# performance problems for the file system.
+# The default value is: NO.
+
+CREATE_SUBDIRS         = NO
+
+# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII
+# characters to appear in the names of generated files. If set to NO, non-ASCII
+# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode
+# U+3044.
+# The default value is: NO.
+
+ALLOW_UNICODE_NAMES    = NO
+
+# The OUTPUT_LANGUAGE tag is used to specify the language in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all constant output in the proper language.
+# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese,
+# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States),
+# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian,
+# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages),
+# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian,
+# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian,
+# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish,
+# Ukrainian and Vietnamese.
+# The default value is: English.
+
+OUTPUT_LANGUAGE        = English
+
+# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all
+# documentation generated by doxygen is written. Doxygen will use this
+# information to generate all generated output in the proper direction.
+# Possible values are: None, LTR, RTL and Context.
+# The default value is: None.
+
+OUTPUT_TEXT_DIRECTION  = None
+
+# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member
+# descriptions after the members that are listed in the file and class
+# documentation (similar to Javadoc). Set to NO to disable this.
+# The default value is: YES.
+
+BRIEF_MEMBER_DESC      = YES
+
+# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief
+# description of a member or function before the detailed description
+#
+# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the
+# brief descriptions will be completely suppressed.
+# The default value is: YES.
+
+REPEAT_BRIEF           = YES
+
+# This tag implements a quasi-intelligent brief description abbreviator that is
+# used to form the text in various listings. Each string in this list, if found
+# as the leading text of the brief description, will be stripped from the text
+# and the result, after processing the whole list, is used as the annotated
+# text. Otherwise, the brief description is used as-is. If left blank, the
+# following values are used ($name is automatically replaced with the name of
+# the entity):The $name class, The $name widget, The $name file, is, provides,
+# specifies, contains, represents, a, an and the.
+
+ABBREVIATE_BRIEF       = "The $name class" \
+                         "The $name widget" \
+                         "The $name file" \
+                         is \
+                         provides \
+                         specifies \
+                         contains \
+                         represents \
+                         a \
+                         an \
+                         the
+
+# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then
+# doxygen will generate a detailed section even if there is only a brief
+# description.
+# The default value is: NO.
+
+ALWAYS_DETAILED_SEC    = NO
+
+# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all
+# inherited members of a class in the documentation of that class as if those
+# members were ordinary class members. Constructors, destructors and assignment
+# operators of the base classes will not be shown.
+# The default value is: NO.
+
+INLINE_INHERITED_MEMB  = NO
+
+# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path
+# before files name in the file list and in the header files. If set to NO the
+# shortest path that makes the file name unique will be used
+# The default value is: YES.
+
+FULL_PATH_NAMES        = YES
+
+# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path.
+# Stripping is only done if one of the specified strings matches the left-hand
+# part of the path. The tag can be used to show relative paths in the file list.
+# If left blank the directory from which doxygen is run is used as the path to
+# strip.
+#
+# Note that you can specify absolute paths here, but also relative paths, which
+# will be relative from the directory where doxygen is started.
+# This tag requires that the tag FULL_PATH_NAMES is set to YES.
+
+STRIP_FROM_PATH        =
+
+# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
+# path mentioned in the documentation of a class, which tells the reader which
+# header file to include in order to use a class. If left blank only the name of
+# the header file containing the class definition is used. Otherwise one should
+# specify the list of include paths that are normally passed to the compiler
+# using the -I flag.
+
+STRIP_FROM_INC_PATH    =
+
+# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but
+# less readable) file names. This can be useful is your file systems doesn't
+# support long names like on DOS, Mac, or CD-ROM.
+# The default value is: NO.
+
+SHORT_NAMES            = NO
+
+# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the
+# first line (until the first dot) of a Javadoc-style comment as the brief
+# description. If set to NO, the Javadoc-style will behave just like regular Qt-
+# style comments (thus requiring an explicit @brief command for a brief
+# description.)
+# The default value is: NO.
+
+JAVADOC_AUTOBRIEF      = NO
+
+# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line
+# such as
+# /***************
+# as being the beginning of a Javadoc-style comment "banner". If set to NO, the
+# Javadoc-style will behave just like regular comments and it will not be
+# interpreted by doxygen.
+# The default value is: NO.
+
+JAVADOC_BANNER         = NO
+
+# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first
+# line (until the first dot) of a Qt-style comment as the brief description. If
+# set to NO, the Qt-style will behave just like regular Qt-style comments (thus
+# requiring an explicit \brief command for a brief description.)
+# The default value is: NO.
+
+QT_AUTOBRIEF           = NO
+
+# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a
+# multi-line C++ special comment block (i.e. a block of //! or /// comments) as
+# a brief description. This used to be the default behavior. The new default is
+# to treat a multi-line C++ comment block as a detailed description. Set this
+# tag to YES if you prefer the old behavior instead.
+#
+# Note that setting this tag to YES also means that rational rose comments are
+# not recognized any more.
+# The default value is: NO.
+
+MULTILINE_CPP_IS_BRIEF = NO
+
+# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the
+# documentation from any documented member that it re-implements.
+# The default value is: YES.
+
+INHERIT_DOCS           = YES
+
+# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new
+# page for each member. If set to NO, the documentation of a member will be part
+# of the file/class/namespace that contains it.
+# The default value is: NO.
+
+SEPARATE_MEMBER_PAGES  = NO
+
+# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen
+# uses this value to replace tabs by spaces in code fragments.
+# Minimum value: 1, maximum value: 16, default value: 4.
+
+TAB_SIZE               = 4
+
+# This tag can be used to specify a number of aliases that act as commands in
+# the documentation. An alias has the form:
+# name=value
+# For example adding
+# "sideeffect=@par Side Effects:\n"
+# will allow you to put the command \sideeffect (or @sideeffect) in the
+# documentation, which will result in a user-defined paragraph with heading
+# "Side Effects:". You can put \n's in the value part of an alias to insert
+# newlines (in the resulting output). You can put ^^ in the value part of an
+# alias to insert a newline as if a physical newline was in the original file.
+# When you need a literal { or } or , in the value part of an alias you have to
+# escape them by means of a backslash (\), this can lead to conflicts with the
+# commands \{ and \} for these it is advised to use the version @{ and @} or use
+# a double escape (\\{ and \\})
+
+ALIASES                = "rst=\xmlonly<verbatim>embed:rst:leading-asterisk^^"
+ALIASES               += "endrst=</verbatim>\endxmlonly"
+ALIASES               += "see{1}=@rst .. seealso:: \1@endrst"
+ALIASES               += "important{1}=@rst .. important:: \1@endrst"
+ALIASES               += "hint{1}=@rst.. hint:: \1@endrst"
+ALIASES               += "tip{1}=@rst.. tip:: \1@endrst"
+
+# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources
+# only. Doxygen will then generate output that is more tailored for C. For
+# instance, some of the names that are used will be different. The list of all
+# members will be omitted, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_FOR_C  = NO
+
+# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or
+# Python sources only. Doxygen will then generate output that is more tailored
+# for that language. For instance, namespaces will be presented as packages,
+# qualified scopes will look different, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_JAVA   = NO
+
+# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran
+# sources. Doxygen will then generate output that is tailored for Fortran.
+# The default value is: NO.
+
+OPTIMIZE_FOR_FORTRAN   = NO
+
+# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL
+# sources. Doxygen will then generate output that is tailored for VHDL.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_VHDL   = NO
+
+# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice
+# sources only. Doxygen will then generate output that is more tailored for that
+# language. For instance, namespaces will be presented as modules, types will be
+# separated into more groups, etc.
+# The default value is: NO.
+
+OPTIMIZE_OUTPUT_SLICE  = NO
+
+# Doxygen selects the parser to use depending on the extension of the files it
+# parses. With this tag you can assign which parser to use for a given
+# extension. Doxygen has a built-in mapping, but you can override or extend it
+# using this tag. The format is ext=language, where ext is a file extension, and
+# language is one of the parsers supported by doxygen: IDL, Java, JavaScript,
+# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL,
+# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran:
+# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser
+# tries to guess whether the code is fixed or free formatted code, this is the
+# default for Fortran type files). For instance to make doxygen treat .inc files
+# as Fortran files (default is PHP), and .f files as C (default is Fortran),
+# use: inc=Fortran f=C.
+#
+# Note: For files without extension you can use no_extension as a placeholder.
+#
+# Note that for custom extensions you also need to set FILE_PATTERNS otherwise
+# the files are not read by doxygen.
+
+EXTENSION_MAPPING      = ino=c \
+                         pde=c
+
+# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments
+# according to the Markdown format, which allows for more readable
+# documentation. See https://daringfireball.net/projects/markdown/ for details.
+# The output of markdown processing is further processed by doxygen, so you can
+# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in
+# case of backward compatibilities issues.
+# The default value is: YES.
+
+MARKDOWN_SUPPORT       = YES
+
+# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up
+# to that level are automatically included in the table of contents, even if
+# they do not have an id attribute.
+# Note: This feature currently applies only to Markdown headings.
+# Minimum value: 0, maximum value: 99, default value: 5.
+# This tag requires that the tag MARKDOWN_SUPPORT is set to YES.
+
+TOC_INCLUDE_HEADINGS   = 5
+
+# When enabled doxygen tries to link words that correspond to documented
+# classes, or namespaces to their corresponding documentation. Such a link can
+# be prevented in individual cases by putting a % sign in front of the word or
+# globally by setting AUTOLINK_SUPPORT to NO.
+# The default value is: YES.
+
+AUTOLINK_SUPPORT       = YES
+
+# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want
+# to include (a tag file for) the STL sources as input, then you should set this
+# tag to YES in order to let doxygen match functions declarations and
+# definitions whose arguments contain STL classes (e.g. func(std::string);
+# versus func(std::string) {}). This also make the inheritance and collaboration
+# diagrams that involve STL classes more complete and accurate.
+# The default value is: NO.
+
+BUILTIN_STL_SUPPORT    = NO
+
+# If you use Microsoft's C++/CLI language, you should set this option to YES to
+# enable parsing support.
+# The default value is: NO.
+
+CPP_CLI_SUPPORT        = NO
+
+# Set the SIP_SUPPORT tag to YES if your project consists of sip (see:
+# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen
+# will parse them like normal C++ but will assume all classes use public instead
+# of private inheritance when no explicit protection keyword is present.
+# The default value is: NO.
+
+SIP_SUPPORT            = NO
+
+# For Microsoft's IDL there are propget and propput attributes to indicate
+# getter and setter methods for a property. Setting this option to YES will make
+# doxygen to replace the get and set methods by a property in the documentation.
+# This will only work if the methods are indeed getting or setting a simple
+# type. If this is not the case, or you want to show the methods anyway, you
+# should set this option to NO.
+# The default value is: YES.
+
+IDL_PROPERTY_SUPPORT   = YES
+
+# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC
+# tag is set to YES then doxygen will reuse the documentation of the first
+# member in the group (if any) for the other members of the group. By default
+# all members of a group must be documented explicitly.
+# The default value is: NO.
+
+DISTRIBUTE_GROUP_DOC   = NO
+
+# If one adds a struct or class to a group and this option is enabled, then also
+# any nested class or struct is added to the same group. By default this option
+# is disabled and one has to add nested compounds explicitly via \ingroup.
+# The default value is: NO.
+
+GROUP_NESTED_COMPOUNDS = NO
+
+# Set the SUBGROUPING tag to YES to allow class member groups of the same type
+# (for instance a group of public functions) to be put as a subgroup of that
+# type (e.g. under the Public Functions section). Set it to NO to prevent
+# subgrouping. Alternatively, this can be done per class using the
+# \nosubgrouping command.
+# The default value is: YES.
+
+SUBGROUPING            = YES
+
+# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions
+# are shown inside the group in which they are included (e.g. using \ingroup)
+# instead of on a separate page (for HTML and Man pages) or section (for LaTeX
+# and RTF).
+#
+# Note that this feature does not work in combination with
+# SEPARATE_MEMBER_PAGES.
+# The default value is: NO.
+
+INLINE_GROUPED_CLASSES = NO
+
+# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions
+# with only public data fields or simple typedef fields will be shown inline in
+# the documentation of the scope in which they are defined (i.e. file,
+# namespace, or group documentation), provided this scope is documented. If set
+# to NO, structs, classes, and unions are shown on a separate page (for HTML and
+# Man pages) or section (for LaTeX and RTF).
+# The default value is: NO.
+
+INLINE_SIMPLE_STRUCTS  = NO
+
+# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or
+# enum is documented as struct, union, or enum with the name of the typedef. So
+# typedef struct TypeS {} TypeT, will appear in the documentation as a struct
+# with name TypeT. When disabled the typedef will appear as a member of a file,
+# namespace, or class. And the struct will be named TypeS. This can typically be
+# useful for C code in case the coding convention dictates that all compound
+# types are typedef'ed and only the typedef is referenced, never the tag name.
+# The default value is: NO.
+
+TYPEDEF_HIDES_STRUCT   = NO
+
+# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This
+# cache is used to resolve symbols given their name and scope. Since this can be
+# an expensive process and often the same symbol appears multiple times in the
+# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small
+# doxygen will become slower. If the cache is too large, memory is wasted. The
+# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range
+# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536
+# symbols. At the end of a run doxygen will report the cache usage and suggest
+# the optimal cache size from a speed point of view.
+# Minimum value: 0, maximum value: 9, default value: 0.
+
+LOOKUP_CACHE_SIZE      = 0
+
+#---------------------------------------------------------------------------
+# Build related configuration options
+#---------------------------------------------------------------------------
+
+# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in
+# documentation are documented, even if no documentation was available. Private
+# class members and static file members will be hidden unless the
+# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES.
+# Note: This will also disable the warnings about undocumented members that are
+# normally produced when WARNINGS is set to YES.
+# The default value is: NO.
+
+EXTRACT_ALL            = YES
+
+# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will
+# be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIVATE        = NO
+
+# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual
+# methods of a class will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PRIV_VIRTUAL   = NO
+
+# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal
+# scope will be included in the documentation.
+# The default value is: NO.
+
+EXTRACT_PACKAGE        = NO
+
+# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be
+# included in the documentation.
+# The default value is: NO.
+
+EXTRACT_STATIC         = YES
+
+# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined
+# locally in source files will be included in the documentation. If set to NO,
+# only classes defined in header files are included. Does not have any effect
+# for Java sources.
+# The default value is: YES.
+
+EXTRACT_LOCAL_CLASSES  = YES
+
+# This flag is only useful for Objective-C code. If set to YES, local methods,
+# which are defined in the implementation section but not in the interface are
+# included in the documentation. If set to NO, only methods in the interface are
+# included.
+# The default value is: NO.
+
+EXTRACT_LOCAL_METHODS  = NO
+
+# If this flag is set to YES, the members of anonymous namespaces will be
+# extracted and appear in the documentation as a namespace called
+# 'anonymous_namespace{file}', where file will be replaced with the base name of
+# the file that contains the anonymous namespace. By default anonymous namespace
+# are hidden.
+# The default value is: NO.
+
+EXTRACT_ANON_NSPACES   = NO
+
+# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all
+# undocumented members inside documented classes or files. If set to NO these
+# members will be included in the various overviews, but no documentation
+# section is generated. This option has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_MEMBERS     = NO
+
+# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all
+# undocumented classes that are normally visible in the class hierarchy. If set
+# to NO, these classes will be included in the various overviews. This option
+# has no effect if EXTRACT_ALL is enabled.
+# The default value is: NO.
+
+HIDE_UNDOC_CLASSES     = NO
+
+# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend
+# declarations. If set to NO, these declarations will be included in the
+# documentation.
+# The default value is: NO.
+
+HIDE_FRIEND_COMPOUNDS  = NO
+
+# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any
+# documentation blocks found inside the body of a function. If set to NO, these
+# blocks will be appended to the function's detailed documentation block.
+# The default value is: NO.
+
+HIDE_IN_BODY_DOCS      = NO
+
+# The INTERNAL_DOCS tag determines if documentation that is typed after a
+# \internal command is included. If the tag is set to NO then the documentation
+# will be excluded. Set it to YES to include the internal documentation.
+# The default value is: NO.
+
+INTERNAL_DOCS          = NO
+
+# If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file
+# names in lower-case letters. If set to YES, upper-case letters are also
+# allowed. This is useful if you have classes or files whose names only differ
+# in case and if your file system supports case sensitive file names. Windows
+# (including Cygwin) ands Mac users are advised to set this option to NO.
+# The default value is: system dependent.
+
+CASE_SENSE_NAMES       = YES
+
+# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with
+# their full class and namespace scopes in the documentation. If set to YES, the
+# scope will be hidden.
+# The default value is: NO.
+
+HIDE_SCOPE_NAMES       = NO
+
+# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will
+# append additional text to a page's title, such as Class Reference. If set to
+# YES the compound reference will be hidden.
+# The default value is: NO.
+
+HIDE_COMPOUND_REFERENCE= NO
+
+# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of
+# the files that are included by a file in the documentation of that file.
+# The default value is: YES.
+
+SHOW_INCLUDE_FILES     = YES
+
+# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each
+# grouped member an include statement to the documentation, telling the reader
+# which file to include in order to use the member.
+# The default value is: NO.
+
+SHOW_GROUPED_MEMB_INC  = NO
+
+# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include
+# files with double quotes in the documentation rather than with sharp brackets.
+# The default value is: NO.
+
+FORCE_LOCAL_INCLUDES   = NO
+
+# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the
+# documentation for inline members.
+# The default value is: YES.
+
+INLINE_INFO            = YES
+
+# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the
+# (detailed) documentation of file and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order.
+# The default value is: YES.
+
+SORT_MEMBER_DOCS       = NO
+
+# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief
+# descriptions of file, namespace and class members alphabetically by member
+# name. If set to NO, the members will appear in declaration order. Note that
+# this will also influence the order of the classes in the class list.
+# The default value is: NO.
+
+SORT_BRIEF_DOCS        = NO
+
+# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the
+# (brief and detailed) documentation of class members so that constructors and
+# destructors are listed first. If set to NO the constructors will appear in the
+# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS.
+# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief
+# member documentation.
+# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting
+# detailed member documentation.
+# The default value is: NO.
+
+SORT_MEMBERS_CTORS_1ST = NO
+
+# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy
+# of group names into alphabetical order. If set to NO the group names will
+# appear in their defined order.
+# The default value is: NO.
+
+SORT_GROUP_NAMES       = NO
+
+# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by
+# fully-qualified names, including namespaces. If set to NO, the class list will
+# be sorted only by class name, not including the namespace part.
+# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES.
+# Note: This option applies only to the class list, not to the alphabetical
+# list.
+# The default value is: NO.
+
+SORT_BY_SCOPE_NAME     = YES
+
+# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper
+# type resolution of all parameters of a function it will reject a match between
+# the prototype and the implementation of a member function even if there is
+# only one candidate or it is obvious which candidate to choose by doing a
+# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still
+# accept a match between prototype and implementation in such cases.
+# The default value is: NO.
+
+STRICT_PROTO_MATCHING  = NO
+
+# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo
+# list. This list is created by putting \todo commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TODOLIST      = NO
+
+# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test
+# list. This list is created by putting \test commands in the documentation.
+# The default value is: YES.
+
+GENERATE_TESTLIST      = YES
+
+# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug
+# list. This list is created by putting \bug commands in the documentation.
+# The default value is: YES.
+
+GENERATE_BUGLIST       = YES
+
+# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO)
+# the deprecated list. This list is created by putting \deprecated commands in
+# the documentation.
+# The default value is: YES.
+
+GENERATE_DEPRECATEDLIST= YES
+
+# The ENABLED_SECTIONS tag can be used to enable conditional documentation
+# sections, marked by \if <section_label> ... \endif and \cond <section_label>
+# ... \endcond blocks.
+
+ENABLED_SECTIONS       =
+
+# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the
+# initial value of a variable or macro / define can have for it to appear in the
+# documentation. If the initializer consists of more lines than specified here
+# it will be hidden. Use a value of 0 to hide initializers completely. The
+# appearance of the value of individual variables and macros / defines can be
+# controlled using \showinitializer or \hideinitializer command in the
+# documentation regardless of this setting.
+# Minimum value: 0, maximum value: 10000, default value: 30.
+
+MAX_INITIALIZER_LINES  = 30
+
+# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at
+# the bottom of the documentation of classes and structs. If set to YES, the
+# list will mention the files that were used to generate the documentation.
+# The default value is: YES.
+
+SHOW_USED_FILES        = YES
+
+# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This
+# will remove the Files entry from the Quick Index and from the Folder Tree View
+# (if specified).
+# The default value is: YES.
+
+SHOW_FILES             = YES
+
+# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces
+# page. This will remove the Namespaces entry from the Quick Index and from the
+# Folder Tree View (if specified).
+# The default value is: YES.
+
+SHOW_NAMESPACES        = NO
+
+# The FILE_VERSION_FILTER tag can be used to specify a program or script that
+# doxygen should invoke to get the current version for each file (typically from
+# the version control system). Doxygen will invoke the program by executing (via
+# popen()) the command command input-file, where command is the value of the
+# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided
+# by doxygen. Whatever the program writes to standard output is used as the file
+# version. For an example see the documentation.
+
+FILE_VERSION_FILTER    =
+
+# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed
+# by doxygen. The layout file controls the global structure of the generated
+# output files in an output format independent way. To create the layout file
+# that represents doxygen's defaults, run doxygen with the -l option. You can
+# optionally specify a file name after the option, if omitted DoxygenLayout.xml
+# will be used as the name of the layout file.
+#
+# Note that if you run doxygen from a directory containing a file called
+# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE
+# tag is left empty.
+
+LAYOUT_FILE            =
+
+# The CITE_BIB_FILES tag can be used to specify one or more bib files containing
+# the reference definitions. This must be a list of .bib files. The .bib
+# extension is automatically appended if omitted. This requires the bibtex tool
+# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info.
+# For LaTeX the style of the bibliography can be controlled using
+# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the
+# search path. See also \cite for info how to create references.
+
+CITE_BIB_FILES         =
+
+#---------------------------------------------------------------------------
+# Configuration options related to warning and progress messages
+#---------------------------------------------------------------------------
+
+# The QUIET tag can be used to turn on/off the messages that are generated to
+# standard output by doxygen. If QUIET is set to YES this implies that the
+# messages are off.
+# The default value is: NO.
+
+QUIET                  = NO
+
+# The WARNINGS tag can be used to turn on/off the warning messages that are
+# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES
+# this implies that the warnings are on.
+#
+# Tip: Turn warnings on while writing the documentation.
+# The default value is: YES.
+
+WARNINGS               = YES
+
+# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate
+# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag
+# will automatically be disabled.
+# The default value is: YES.
+
+WARN_IF_UNDOCUMENTED   = YES
+
+# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for
+# potential errors in the documentation, such as not documenting some parameters
+# in a documented function, or documenting parameters that don't exist or using
+# markup commands wrongly.
+# The default value is: YES.
+
+WARN_IF_DOC_ERROR      = YES
+
+# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that
+# are documented, but have no documentation for their parameters or return
+# value. If set to NO, doxygen will only warn about wrong or incomplete
+# parameter documentation, but not about the absence of documentation. If
+# EXTRACT_ALL is set to YES then this flag will automatically be disabled.
+# The default value is: NO.
+
+WARN_NO_PARAMDOC       = NO
+
+# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when
+# a warning is encountered.
+# The default value is: NO.
+
+WARN_AS_ERROR          = YES
+
+# The WARN_FORMAT tag determines the format of the warning messages that doxygen
+# can produce. The string should contain the $file, $line, and $text tags, which
+# will be replaced by the file and line number from which the warning originated
+# and the warning text. Optionally the format may contain $version, which will
+# be replaced by the version of the file (if it could be obtained via
+# FILE_VERSION_FILTER)
+# The default value is: $file:$line: $text.
+
+WARN_FORMAT            = "$file:$line: $text"
+
+# The WARN_LOGFILE tag can be used to specify a file to which warning and error
+# messages should be written. If left blank the output is written to standard
+# error (stderr).
+
+WARN_LOGFILE           =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the input files
+#---------------------------------------------------------------------------
+
+# The INPUT tag is used to specify the files and/or directories that contain
+# documented source files. You may enter file names like myfile.cpp or
+# directories like /usr/src/myproject. Separate the files or directories with
+# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
+# Note: If this tag is empty the current directory is searched.
+
+INPUT                  = ./ \
+                         ./utility/Template \
+                         ./docs
+
+# This tag can be used to specify the character encoding of the source files
+# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
+# libiconv (or the iconv built into libc) for the transcoding. See the libiconv
+# documentation (see: https://www.gnu.org/software/libiconv/) for the list of
+# possible encodings.
+# The default value is: UTF-8.
+
+INPUT_ENCODING         = UTF-8
+
+# If the value of the INPUT tag contains directories, you can use the
+# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and
+# *.h) to filter out the source-files in the directories.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# read by doxygen.
+#
+# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp,
+# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h,
+# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc,
+# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment),
+# *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen
+# C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd,
+# *.vhdl, *.ucf, *.qsf and *.ice.
+
+FILE_PATTERNS          = *.c \
+                         *.cc \
+                         *.cxx \
+                         *.cpp \
+                         *.c++ \
+                         *.java \
+                         *.ii \
+                         *.ixx \
+                         *.ipp \
+                         *.i++ \
+                         *.inl \
+                         *.idl \
+                         *.ddl \
+                         *.odl \
+                         *.h \
+                         *.hh \
+                         *.hxx \
+                         *.hpp \
+                         *.h++ \
+                         *.cs \
+                         *.d \
+                         *.php \
+                         *.php4 \
+                         *.php5 \
+                         *.phtml \
+                         *.inc \
+                         *.m \
+                         *.markdown \
+                         *.md \
+                         *.mm \
+                         *.dox \
+                         *.py \
+                         *.f90 \
+                         *.f \
+                         *.for \
+                         *.tcl \
+                         *.vhd \
+                         *.vhdl \
+                         *.ucf \
+                         *.qsf \
+                         *.as \
+                         *.js
+
+
+# The RECURSIVE tag can be used to specify whether or not subdirectories should
+# be searched for input files as well.
+# The default value is: NO.
+
+RECURSIVE              = NO
+
+# The EXCLUDE tag can be used to specify files and/or directories that should be
+# excluded from the INPUT source files. This way you can easily exclude a
+# subdirectory from a directory tree whose root is specified with the INPUT tag.
+#
+# Note that relative paths are relative to the directory from which doxygen is
+# run.
+
+EXCLUDE                = Makefile \
+                         configure
+
+# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or
+# directories that are symbolic links (a Unix file system feature) are excluded
+# from the input.
+# The default value is: NO.
+
+EXCLUDE_SYMLINKS       = NO
+
+# If the value of the INPUT tag contains directories, you can use the
+# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude
+# certain files from those directories.
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories for example use the pattern */test/*
+
+EXCLUDE_PATTERNS       = bcm2835* \
+                         readme* \
+                         runtest* \
+                         tests* \
+                         README*
+
+# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names
+# (namespaces, classes, functions, etc.) that should be excluded from the
+# output. The symbol name can be a fully qualified name, a word, or if the
+# wildcard * is used, a substring. Examples: ANamespace, AClass,
+# AClass::ANamespace, ANamespace::*Test
+#
+# Note that the wildcards are matched against the file with absolute path, so to
+# exclude all test directories use the pattern */test/*
+
+EXCLUDE_SYMBOLS        = _
+
+# The EXAMPLE_PATH tag can be used to specify one or more files or directories
+# that contain example code fragments that are included (see the \include
+# command).
+
+EXAMPLE_PATH           = examples \
+                         examples_linux
+
+# If the value of the EXAMPLE_PATH tag contains directories, you can use the
+# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and
+# *.h) to filter out the source-files in the directories. If left blank all
+# files are included.
+
+EXAMPLE_PATTERNS       =
+
+# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be
+# searched for input files to be used with the \include or \dontinclude commands
+# irrespective of the value of the RECURSIVE tag.
+# The default value is: NO.
+
+EXAMPLE_RECURSIVE      = YES
+
+# The IMAGE_PATH tag can be used to specify one or more files or directories
+# that contain images that are to be included in the documentation (see the
+# \image command).
+
+IMAGE_PATH             = images
+
+# The INPUT_FILTER tag can be used to specify a program that doxygen should
+# invoke to filter for each input file. Doxygen will invoke the filter program
+# by executing (via popen()) the command:
+#
+# <filter> <input-file>
+#
+# where <filter> is the value of the INPUT_FILTER tag, and <input-file> is the
+# name of an input file. Doxygen will then use the output that the filter
+# program writes to standard output. If FILTER_PATTERNS is specified, this tag
+# will be ignored.
+#
+# Note that the filter must not add or remove lines; it is applied before the
+# code is scanned, but not when the output code is generated. If lines are added
+# or removed, the anchors will not be placed correctly.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+INPUT_FILTER           =
+
+# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
+# basis. Doxygen will compare the file name with each pattern and apply the
+# filter if there is a match. The filters are a list of the form: pattern=filter
+# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how
+# filters are used. If the FILTER_PATTERNS tag is empty or if none of the
+# patterns match the file name, INPUT_FILTER is applied.
+#
+# Note that for custom extensions or not directly supported extensions you also
+# need to set EXTENSION_MAPPING for the extension otherwise the files are not
+# properly processed by doxygen.
+
+FILTER_PATTERNS        =
+
+# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using
+# INPUT_FILTER) will also be used to filter the input files that are used for
+# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES).
+# The default value is: NO.
+
+FILTER_SOURCE_FILES    = NO
+
+# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file
+# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and
+# it is also possible to disable source filtering for a specific pattern using
+# *.ext= (so without naming a filter).
+# This tag requires that the tag FILTER_SOURCE_FILES is set to YES.
+
+FILTER_SOURCE_PATTERNS =
+
+# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that
+# is part of the input, its contents will be placed on the main page
+# (index.html). This can be useful if you have a project on for instance GitHub
+# and want to reuse the introduction page also for the doxygen output.
+
+USE_MDFILE_AS_MAINPAGE = ./docs/main_page.md
+
+#---------------------------------------------------------------------------
+# Configuration options related to source browsing
+#---------------------------------------------------------------------------
+
+# If the SOURCE_BROWSER tag is set to YES then a list of source files will be
+# generated. Documented entities will be cross-referenced with these sources.
+#
+# Note: To get rid of all source code in the generated output, make sure that
+# also VERBATIM_HEADERS is set to NO.
+# The default value is: NO.
+
+SOURCE_BROWSER         = YES
+
+# Setting the INLINE_SOURCES tag to YES will include the body of functions,
+# classes and enums directly into the documentation.
+# The default value is: NO.
+
+INLINE_SOURCES         = NO
+
+# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any
+# special comment blocks from generated source code fragments. Normal C, C++ and
+# Fortran comments will always remain visible.
+# The default value is: YES.
+
+STRIP_CODE_COMMENTS    = YES
+
+# If the REFERENCED_BY_RELATION tag is set to YES then for each documented
+# entity all documented functions referencing it will be listed.
+# The default value is: NO.
+
+REFERENCED_BY_RELATION = NO
+
+# If the REFERENCES_RELATION tag is set to YES then for each documented function
+# all documented entities called/used by that function will be listed.
+# The default value is: NO.
+
+REFERENCES_RELATION    = NO
+
+# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set
+# to YES then the hyperlinks from functions in REFERENCES_RELATION and
+# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will
+# link to the documentation.
+# The default value is: YES.
+
+REFERENCES_LINK_SOURCE = YES
+
+# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the
+# source code will show a tooltip with additional information such as prototype,
+# brief description and links to the definition and documentation. Since this
+# will make the HTML file larger and loading of large files a bit slower, you
+# can opt to disable this feature.
+# The default value is: YES.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+SOURCE_TOOLTIPS        = YES
+
+# If the USE_HTAGS tag is set to YES then the references to source code will
+# point to the HTML generated by the htags(1) tool instead of doxygen built-in
+# source browser. The htags tool is part of GNU's global source tagging system
+# (see https://www.gnu.org/software/global/global.html). You will need version
+# 4.8.6 or higher.
+#
+# To use it do the following:
+# - Install the latest version of global
+# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file
+# - Make sure the INPUT points to the root of the source tree
+# - Run doxygen as normal
+#
+# Doxygen will invoke htags (and that will in turn invoke gtags), so these
+# tools must be available from the command line (i.e. in the search path).
+#
+# The result: instead of the source browser generated by doxygen, the links to
+# source code will now point to the output of htags.
+# The default value is: NO.
+# This tag requires that the tag SOURCE_BROWSER is set to YES.
+
+USE_HTAGS              = NO
+
+# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a
+# verbatim copy of the header file for each class for which an include is
+# specified. Set to NO to disable this.
+# See also: Section \class.
+# The default value is: YES.
+
+VERBATIM_HEADERS       = YES
+
+# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the
+# clang parser (see: http://clang.llvm.org/) for more accurate parsing at the
+# cost of reduced performance. This can be particularly helpful with template
+# rich C++ code for which doxygen's built-in parser lacks the necessary type
+# information.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse_libclang=ON option for CMake.
+# The default value is: NO.
+
+CLANG_ASSISTED_PARSING = NO
+
+# If clang assisted parsing is enabled you can provide the compiler with command
+# line options that you would normally use when invoking the compiler. Note that
+# the include paths will already be set by doxygen for the files and directories
+# specified with INPUT and INCLUDE_PATH.
+# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES.
+
+CLANG_OPTIONS          =
+
+# If clang assisted parsing is enabled you can provide the clang parser with the
+# path to the compilation database (see:
+# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) used when the files
+# were built. This is equivalent to specifying the "-p" option to a clang tool,
+# such as clang-check. These options will then be passed to the parser.
+# Note: The availability of this option depends on whether or not doxygen was
+# generated with the -Duse_libclang=ON option for CMake.
+
+CLANG_DATABASE_PATH    =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the alphabetical class index
+#---------------------------------------------------------------------------
+
+# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all
+# compounds will be generated. Enable this if the project contains a lot of
+# classes, structs, unions or interfaces.
+# The default value is: YES.
+
+ALPHABETICAL_INDEX     = YES
+
+# In case all classes in a project start with a common prefix, all classes will
+# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag
+# can be used to specify a prefix (or a list of prefixes) that should be ignored
+# while generating the index headers.
+# This tag requires that the tag ALPHABETICAL_INDEX is set to YES.
+
+IGNORE_PREFIX          =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the HTML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output
+# The default value is: YES.
+
+GENERATE_HTML          = YES
+
+# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_OUTPUT            = html
+
+# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each
+# generated HTML page (for example: .htm, .php, .asp).
+# The default value is: .html.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FILE_EXTENSION    = .html
+
+# The HTML_HEADER tag can be used to specify a user-defined HTML header file for
+# each generated HTML page. If the tag is left blank doxygen will generate a
+# standard header.
+#
+# To get valid HTML the header file that includes any scripts and style sheets
+# that doxygen needs, which is dependent on the configuration options used (e.g.
+# the setting GENERATE_TREEVIEW). It is highly recommended to start with a
+# default header using
+# doxygen -w html new_header.html new_footer.html new_stylesheet.css
+# YourConfigFile
+# and then modify the file new_header.html. See also section "Doxygen usage"
+# for information on how to generate the default header that doxygen normally
+# uses.
+# Note: The header is subject to change so you typically have to regenerate the
+# default header when upgrading to a newer version of doxygen. For a description
+# of the possible markers and block names see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_HEADER            =
+
+# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each
+# generated HTML page. If the tag is left blank doxygen will generate a standard
+# footer. See HTML_HEADER for more information on how to generate a default
+# footer and what special commands can be used inside the footer. See also
+# section "Doxygen usage" for information on how to generate the default footer
+# that doxygen normally uses.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FOOTER            =
+
+# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style
+# sheet that is used by each HTML page. It can be used to fine-tune the look of
+# the HTML output. If left blank doxygen will generate a default style sheet.
+# See also section "Doxygen usage" for information on how to generate the style
+# sheet that doxygen normally uses.
+# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as
+# it is more robust and this tag (HTML_STYLESHEET) will in the future become
+# obsolete.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_STYLESHEET        =
+
+# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# cascading style sheets that are included after the standard style sheets
+# created by doxygen. Using this option one can overrule certain style aspects.
+# This is preferred over using HTML_STYLESHEET since it does not replace the
+# standard style sheet and is therefore more robust against future updates.
+# Doxygen will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list). For an example see the documentation.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_STYLESHEET  = doxygen-custom.css
+
+# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the HTML output directory. Note
+# that these files will be copied to the base HTML output directory. Use the
+# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these
+# files. In the HTML_STYLESHEET file, use the file name only. Also note that the
+# files will be copied as-is; there are no commands or markers available.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_EXTRA_FILES       =
+
+# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen
+# will adjust the colors in the style sheet and background images according to
+# this color. Hue is specified as an angle on a colorwheel, see
+# https://en.wikipedia.org/wiki/Hue for more information. For instance the value
+# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300
+# purple, and 360 is red again.
+# Minimum value: 0, maximum value: 359, default value: 220.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_HUE    = 220
+
+# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors
+# in the HTML output. For a value of 0 the output will use grayscales only. A
+# value of 255 will produce the most vivid colors.
+# Minimum value: 0, maximum value: 255, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_SAT    = 100
+
+# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the
+# luminance component of the colors in the HTML output. Values below 100
+# gradually make the output lighter, whereas values above 100 make the output
+# darker. The value divided by 100 is the actual gamma applied, so 80 represents
+# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not
+# change the gamma.
+# Minimum value: 40, maximum value: 240, default value: 80.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_COLORSTYLE_GAMMA  = 80
+
+# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML
+# page will contain the date and time when the page was generated. Setting this
+# to YES can help to show when doxygen was last run and thus if the
+# documentation is up to date.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_TIMESTAMP         = YES
+
+# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML
+# documentation will contain a main index with vertical navigation menus that
+# are dynamically created via JavaScript. If disabled, the navigation index will
+# consists of multiple levels of tabs that are statically embedded in every HTML
+# page. Disable this option to support browsers that do not have JavaScript,
+# like the Qt help browser.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_MENUS     = NO
+
+# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML
+# documentation will contain sections that can be hidden and shown after the
+# page has loaded.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_DYNAMIC_SECTIONS  = YES
+
+# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries
+# shown in the various tree structured indices initially; the user can expand
+# and collapse entries dynamically later on. Doxygen will expand the tree to
+# such a level that at most the specified number of entries are visible (unless
+# a fully collapsed tree already exceeds this amount). So setting the number of
+# entries 1 will produce a full collapsed tree by default. 0 is a special value
+# representing an infinite number of entries and will result in a full expanded
+# tree by default.
+# Minimum value: 0, maximum value: 9999, default value: 100.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_INDEX_NUM_ENTRIES = 100
+
+# If the GENERATE_DOCSET tag is set to YES, additional index files will be
+# generated that can be used as input for Apple's Xcode 3 integrated development
+# environment (see: https://developer.apple.com/xcode/), introduced with OSX
+# 10.5 (Leopard). To create a documentation set, doxygen will generate a
+# Makefile in the HTML output directory. Running make will produce the docset in
+# that directory and running make install will install the docset in
+# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at
+# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy
+# genXcode/_index.html for more information.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_DOCSET        = NO
+
+# This tag determines the name of the docset feed. A documentation feed provides
+# an umbrella under which multiple documentation sets from a single provider
+# (such as a company or product suite) can be grouped.
+# The default value is: Doxygen generated docs.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_FEEDNAME        = "Doxygen generated docs"
+
+# This tag specifies a string that should uniquely identify the documentation
+# set bundle. This should be a reverse domain-name style string, e.g.
+# com.mycompany.MyDocSet. Doxygen will append .docset to the name.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_BUNDLE_ID       = org.doxygen.Project
+
+# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify
+# the documentation publisher. This should be a reverse domain-name style
+# string, e.g. com.mycompany.MyDocSet.documentation.
+# The default value is: org.doxygen.Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_ID    = org.doxygen.Publisher
+
+# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher.
+# The default value is: Publisher.
+# This tag requires that the tag GENERATE_DOCSET is set to YES.
+
+DOCSET_PUBLISHER_NAME  = Publisher
+
+# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three
+# additional HTML index files: index.hhp, index.hhc, and index.hhk. The
+# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop
+# (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on
+# Windows.
+#
+# The HTML Help Workshop contains a compiler that can convert all HTML output
+# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML
+# files are now used as the Windows 98 help format, and will replace the old
+# Windows help format (.hlp) on all Windows platforms in the future. Compressed
+# HTML files also contain an index, a table of contents, and you can search for
+# words in the documentation. The HTML workshop also contains a viewer for
+# compressed HTML files.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_HTMLHELP      = NO
+
+# The CHM_FILE tag can be used to specify the file name of the resulting .chm
+# file. You can add a path in front of the file if the result should not be
+# written to the html output directory.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_FILE               =
+
+# The HHC_LOCATION tag can be used to specify the location (absolute path
+# including file name) of the HTML help compiler (hhc.exe). If non-empty,
+# doxygen will try to run the HTML help compiler on the generated index.hhp.
+# The file has to be specified with full path.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+HHC_LOCATION           =
+
+# The GENERATE_CHI flag controls if a separate .chi index file is generated
+# (YES) or that it should be included in the master .chm file (NO).
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+GENERATE_CHI           = NO
+
+# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc)
+# and project file content.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+CHM_INDEX_ENCODING     =
+
+# The BINARY_TOC flag controls whether a binary table of contents is generated
+# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it
+# enables the Previous and Next buttons.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+BINARY_TOC             = NO
+
+# The TOC_EXPAND flag can be set to YES to add extra items for group members to
+# the table of contents of the HTML help documentation and to the tree view.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTMLHELP is set to YES.
+
+TOC_EXPAND             = NO
+
+# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and
+# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that
+# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help
+# (.qch) of the generated HTML documentation.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_QHP           = NO
+
+# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify
+# the file name of the resulting .qch file. The path specified is relative to
+# the HTML output folder.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QCH_FILE               =
+
+# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help
+# Project output. For more information please see Qt Help Project / Namespace
+# (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace).
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_NAMESPACE          = org.doxygen.Project
+
+# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt
+# Help Project output. For more information please see Qt Help Project / Virtual
+# Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-
+# folders).
+# The default value is: doc.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_VIRTUAL_FOLDER     = doc
+
+# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom
+# filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_NAME   =
+
+# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the
+# custom filter to add. For more information please see Qt Help Project / Custom
+# Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-
+# filters).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_CUST_FILTER_ATTRS  =
+
+# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this
+# project's filter section matches. Qt Help Project / Filter Attributes (see:
+# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes).
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHP_SECT_FILTER_ATTRS  =
+
+# The QHG_LOCATION tag can be used to specify the location of Qt's
+# qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the
+# generated .qhp file.
+# This tag requires that the tag GENERATE_QHP is set to YES.
+
+QHG_LOCATION           =
+
+# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be
+# generated, together with the HTML files, they form an Eclipse help plugin. To
+# install this plugin and make it available under the help contents menu in
+# Eclipse, the contents of the directory containing the HTML and XML files needs
+# to be copied into the plugins directory of eclipse. The name of the directory
+# within the plugins directory should be the same as the ECLIPSE_DOC_ID value.
+# After copying Eclipse needs to be restarted before the help appears.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_ECLIPSEHELP   = NO
+
+# A unique identifier for the Eclipse help plugin. When installing the plugin
+# the directory name containing the HTML and XML files should also have this
+# name. Each documentation set should have its own identifier.
+# The default value is: org.doxygen.Project.
+# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES.
+
+ECLIPSE_DOC_ID         = org.doxygen.Project
+
+# If you want full control over the layout of the generated HTML pages it might
+# be necessary to disable the index and replace it with your own. The
+# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top
+# of each HTML page. A value of NO enables the index and the value YES disables
+# it. Since the tabs in the index contain the same information as the navigation
+# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+DISABLE_INDEX          = NO
+
+# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index
+# structure should be generated to display hierarchical information. If the tag
+# value is set to YES, a side panel will be generated containing a tree-like
+# index structure (just like the one that is generated for HTML Help). For this
+# to work a browser that supports JavaScript, DHTML, CSS and frames is required
+# (i.e. any modern browser). Windows users are probably better off using the
+# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can
+# further fine-tune the look of the index. As an example, the default style
+# sheet generated by doxygen has an example that shows how to put an image at
+# the root of the tree instead of the PROJECT_NAME. Since the tree basically has
+# the same information as the tab index, you could consider setting
+# DISABLE_INDEX to YES when enabling this option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+GENERATE_TREEVIEW      = NO
+
+# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that
+# doxygen will group on one line in the generated HTML documentation.
+#
+# Note that a value of 0 will completely suppress the enum values from appearing
+# in the overview section.
+# Minimum value: 0, maximum value: 20, default value: 4.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+ENUM_VALUES_PER_LINE   = 4
+
+# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used
+# to set the initial width (in pixels) of the frame in which the tree is shown.
+# Minimum value: 0, maximum value: 1500, default value: 250.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+TREEVIEW_WIDTH         = 250
+
+# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to
+# external symbols imported via tag files in a separate window.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+EXT_LINKS_IN_WINDOW    = NO
+
+# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg
+# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see
+# https://inkscape.org) to generate formulas as SVG images instead of PNGs for
+# the HTML output. These images will generally look nicer at scaled resolutions.
+# Possible values are: png The default and svg Looks nicer but requires the
+# pdf2svg tool.
+# The default value is: png.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+HTML_FORMULA_FORMAT    = png
+
+# Use this tag to change the font size of LaTeX formulas included as images in
+# the HTML documentation. When you change the font size after a successful
+# doxygen run you need to manually remove any form_*.png images from the HTML
+# output directory to force them to be regenerated.
+# Minimum value: 8, maximum value: 50, default value: 10.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_FONTSIZE       = 10
+
+# Use the FORMULA_TRANSPARENT tag to determine whether or not the images
+# generated for formulas are transparent PNGs. Transparent PNGs are not
+# supported properly for IE 6.0, but are supported on all modern browsers.
+#
+# Note that when changing this option you need to delete any form_*.png files in
+# the HTML output directory before the changes have effect.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+FORMULA_TRANSPARENT    = YES
+
+# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands
+# to create new LaTeX commands to be used in formulas as building blocks. See
+# the section "Including formulas" for details.
+
+FORMULA_MACROFILE      =
+
+# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see
+# https://www.mathjax.org) which uses client side JavaScript for the rendering
+# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX
+# installed or if you want to formulas look prettier in the HTML output. When
+# enabled you may also need to install MathJax separately and configure the path
+# to it using the MATHJAX_RELPATH option.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+USE_MATHJAX            = NO
+
+# When MathJax is enabled you can set the default output format to be used for
+# the MathJax output. See the MathJax site (see:
+# http://docs.mathjax.org/en/latest/output.html) for more details.
+# Possible values are: HTML-CSS (which is slower, but has the best
+# compatibility), NativeMML (i.e. MathML) and SVG.
+# The default value is: HTML-CSS.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_FORMAT         = HTML-CSS
+
+# When MathJax is enabled you need to specify the location relative to the HTML
+# output directory using the MATHJAX_RELPATH option. The destination directory
+# should contain the MathJax.js script. For instance, if the mathjax directory
+# is located at the same level as the HTML output directory, then
+# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax
+# Content Delivery Network so you can quickly see the result without installing
+# MathJax. However, it is strongly recommended to install a local copy of
+# MathJax from https://www.mathjax.org before deployment.
+# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_RELPATH        = http://cdn.mathjax.org/mathjax/latest
+
+# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax
+# extension names that should be enabled during MathJax rendering. For example
+# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_EXTENSIONS     =
+
+# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces
+# of code that will be used on startup of the MathJax code. See the MathJax site
+# (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an
+# example see the documentation.
+# This tag requires that the tag USE_MATHJAX is set to YES.
+
+MATHJAX_CODEFILE       =
+
+# When the SEARCHENGINE tag is enabled doxygen will generate a search box for
+# the HTML output. The underlying search engine uses javascript and DHTML and
+# should work on any modern browser. Note that when using HTML help
+# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET)
+# there is already a search function so this one should typically be disabled.
+# For large projects the javascript based search engine can be slow, then
+# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to
+# search using the keyboard; to jump to the search box use <access key> + S
+# (what the <access key> is depends on the OS and browser, but it is typically
+# <CTRL>, <ALT>/<option>, or both). Inside the search box use the <cursor down
+# key> to jump into the search results window, the results can be navigated
+# using the <cursor keys>. Press <Enter> to select an item or <escape> to cancel
+# the search. The filter options can be selected when the cursor is inside the
+# search box by pressing <Shift>+<cursor down>. Also here use the <cursor keys>
+# to select a filter and <Enter> or <escape> to activate or cancel the filter
+# option.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_HTML is set to YES.
+
+SEARCHENGINE           = YES
+
+# When the SERVER_BASED_SEARCH tag is enabled the search engine will be
+# implemented using a web server instead of a web client using JavaScript. There
+# are two flavors of web server based searching depending on the EXTERNAL_SEARCH
+# setting. When disabled, doxygen will generate a PHP script for searching and
+# an index file used by the script. When EXTERNAL_SEARCH is enabled the indexing
+# and searching needs to be provided by external tools. See the section
+# "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SERVER_BASED_SEARCH    = NO
+
+# When EXTERNAL_SEARCH tag is enabled doxygen will no longer generate the PHP
+# script for searching. Instead the search results are written to an XML file
+# which needs to be processed by an external indexer. Doxygen will invoke an
+# external search engine pointed to by the SEARCHENGINE_URL option to obtain the
+# search results.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/).
+#
+# See the section "External Indexing and Searching" for details.
+# The default value is: NO.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH        = NO
+
+# The SEARCHENGINE_URL should point to a search engine hosted by a web server
+# which will return the search results when EXTERNAL_SEARCH is enabled.
+#
+# Doxygen ships with an example indexer (doxyindexer) and search engine
+# (doxysearch.cgi) which are based on the open source search engine library
+# Xapian (see: https://xapian.org/). See the section "External Indexing and
+# Searching" for details.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHENGINE_URL       =
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the unindexed
+# search data is written to a file for indexing by an external tool. With the
+# SEARCHDATA_FILE tag the name of this file can be specified.
+# The default file is: searchdata.xml.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+SEARCHDATA_FILE        = searchdata.xml
+
+# When SERVER_BASED_SEARCH and EXTERNAL_SEARCH are both enabled the
+# EXTERNAL_SEARCH_ID tag can be used as an identifier for the project. This is
+# useful in combination with EXTRA_SEARCH_MAPPINGS to search through multiple
+# projects and redirect the results back to the right project.
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTERNAL_SEARCH_ID     =
+
+# The EXTRA_SEARCH_MAPPINGS tag can be used to enable searching through doxygen
+# projects other than the one defined by this configuration file, but that are
+# all added to the same external search index. Each project needs to have a
+# unique id set via EXTERNAL_SEARCH_ID. The search mapping then maps the id of
+# to a relative location where the documentation can be found. The format is:
+# EXTRA_SEARCH_MAPPINGS = tagname1=loc1 tagname2=loc2 ...
+# This tag requires that the tag SEARCHENGINE is set to YES.
+
+EXTRA_SEARCH_MAPPINGS  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the LaTeX output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_LATEX tag is set to YES, doxygen will generate LaTeX output.
+# The default value is: YES.
+
+GENERATE_LATEX         = NO
+
+# The LATEX_OUTPUT tag is used to specify where the LaTeX docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: latex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_OUTPUT           = latex
+
+# The LATEX_CMD_NAME tag can be used to specify the LaTeX command name to be
+# invoked.
+#
+# Note that when not enabling USE_PDFLATEX the default is latex when enabling
+# USE_PDFLATEX the default is pdflatex and when in the later case latex is
+# chosen this is overwritten by pdflatex. For specific output languages the
+# default can have been set differently, this depends on the implementation of
+# the output language.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_CMD_NAME         = latex
+
+# The MAKEINDEX_CMD_NAME tag can be used to specify the command name to generate
+# index for LaTeX.
+# Note: This tag is used in the Makefile / make.bat.
+# See also: LATEX_MAKEINDEX_CMD for the part in the generated output file
+# (.tex).
+# The default file is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+MAKEINDEX_CMD_NAME     = makeindex
+
+# The LATEX_MAKEINDEX_CMD tag can be used to specify the command name to
+# generate index for LaTeX. In case there is no backslash (\) as first character
+# it will be automatically added in the LaTeX code.
+# Note: This tag is used in the generated output file (.tex).
+# See also: MAKEINDEX_CMD_NAME for the part in the Makefile / make.bat.
+# The default value is: makeindex.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_MAKEINDEX_CMD    = makeindex
+
+# If the COMPACT_LATEX tag is set to YES, doxygen generates more compact LaTeX
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+COMPACT_LATEX          = NO
+
+# The PAPER_TYPE tag can be used to set the paper type that is used by the
+# printer.
+# Possible values are: a4 (210 x 297 mm), letter (8.5 x 11 inches), legal (8.5 x
+# 14 inches) and executive (7.25 x 10.5 inches).
+# The default value is: a4.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PAPER_TYPE             = a4
+
+# The EXTRA_PACKAGES tag can be used to specify one or more LaTeX package names
+# that should be included in the LaTeX output. The package can be specified just
+# by its name or with the correct syntax as to be used with the LaTeX
+# \usepackage command. To get the times font for instance you can specify :
+# EXTRA_PACKAGES=times or EXTRA_PACKAGES={times}
+# To use the option intlimits with the amsmath package you can specify:
+# EXTRA_PACKAGES=[intlimits]{amsmath}
+# If left blank no extra packages will be included.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+EXTRA_PACKAGES         =
+
+# The LATEX_HEADER tag can be used to specify a personal LaTeX header for the
+# generated LaTeX document. The header should contain everything until the first
+# chapter. If it is left blank doxygen will generate a standard header. See
+# section "Doxygen usage" for information on how to let doxygen write the
+# default header to a separate file.
+#
+# Note: Only use a user-defined header if you know what you are doing! The
+# following commands have a special meaning inside the header: $title,
+# $datetime, $date, $doxygenversion, $projectname, $projectnumber,
+# $projectbrief, $projectlogo. Doxygen will replace $title with the empty
+# string, for the replacement values of the other commands the user is referred
+# to HTML_HEADER.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HEADER           =
+
+# The LATEX_FOOTER tag can be used to specify a personal LaTeX footer for the
+# generated LaTeX document. The footer should contain everything after the last
+# chapter. If it is left blank doxygen will generate a standard footer. See
+# LATEX_HEADER for more information on how to generate a default footer and what
+# special commands can be used inside the footer.
+#
+# Note: Only use a user-defined footer if you know what you are doing!
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_FOOTER           =
+
+# The LATEX_EXTRA_STYLESHEET tag can be used to specify additional user-defined
+# LaTeX style sheets that are included after the standard style sheets created
+# by doxygen. Using this option one can overrule certain style aspects. Doxygen
+# will copy the style sheet files to the output directory.
+# Note: The order of the extra style sheet files is of importance (e.g. the last
+# style sheet in the list overrules the setting of the previous ones in the
+# list).
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_STYLESHEET =
+
+# The LATEX_EXTRA_FILES tag can be used to specify one or more extra images or
+# other source files which should be copied to the LATEX_OUTPUT output
+# directory. Note that the files will be copied as-is; there are no commands or
+# markers available.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EXTRA_FILES      =
+
+# If the PDF_HYPERLINKS tag is set to YES, the LaTeX that is generated is
+# prepared for conversion to PDF (using ps2pdf or pdflatex). The PDF file will
+# contain links (just like the HTML output) instead of page references. This
+# makes the output suitable for online browsing using a PDF viewer.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+PDF_HYPERLINKS         = YES
+
+# If the USE_PDFLATEX tag is set to YES, doxygen will use pdflatex to generate
+# the PDF file directly from the LaTeX files. Set this option to YES, to get a
+# higher quality PDF documentation.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+USE_PDFLATEX           = YES
+
+# If the LATEX_BATCHMODE tag is set to YES, doxygen will add the \batchmode
+# command to the generated LaTeX files. This will instruct LaTeX to keep running
+# if errors occur, instead of asking the user for help. This option is also used
+# when generating formulas in HTML.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BATCHMODE        = NO
+
+# If the LATEX_HIDE_INDICES tag is set to YES then doxygen will not include the
+# index chapters (such as File Index, Compound Index, etc.) in the output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_HIDE_INDICES     = NO
+
+# If the LATEX_SOURCE_CODE tag is set to YES then doxygen will include source
+# code with syntax highlighting in the LaTeX output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_SOURCE_CODE      = NO
+
+# The LATEX_BIB_STYLE tag can be used to specify the style to use for the
+# bibliography, e.g. plainnat, or ieeetr. See
+# https://en.wikipedia.org/wiki/BibTeX and \cite for more info.
+# The default value is: plain.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_BIB_STYLE        = plain
+
+# If the LATEX_TIMESTAMP tag is set to YES then the footer of each generated
+# page will contain the date and time when the page was generated. Setting this
+# to NO can help when comparing the output of multiple runs.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_TIMESTAMP        = NO
+
+# The LATEX_EMOJI_DIRECTORY tag is used to specify the (relative or absolute)
+# path from which the emoji images will be read. If a relative path is entered,
+# it will be relative to the LATEX_OUTPUT directory. If left blank the
+# LATEX_OUTPUT directory will be used.
+# This tag requires that the tag GENERATE_LATEX is set to YES.
+
+LATEX_EMOJI_DIRECTORY  =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the RTF output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_RTF tag is set to YES, doxygen will generate RTF output. The
+# RTF output is optimized for Word 97 and may not look too pretty with other RTF
+# readers/editors.
+# The default value is: NO.
+
+GENERATE_RTF           = NO
+
+# The RTF_OUTPUT tag is used to specify where the RTF docs will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: rtf.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_OUTPUT             = rtf
+
+# If the COMPACT_RTF tag is set to YES, doxygen generates more compact RTF
+# documents. This may be useful for small projects and may help to save some
+# trees in general.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+COMPACT_RTF            = NO
+
+# If the RTF_HYPERLINKS tag is set to YES, the RTF that is generated will
+# contain hyperlink fields. The RTF file will contain links (just like the HTML
+# output) instead of page references. This makes the output suitable for online
+# browsing using Word or some other Word compatible readers that support those
+# fields.
+#
+# Note: WordPad (write) and others do not support links.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_HYPERLINKS         = NO
+
+# Load stylesheet definitions from file. Syntax is similar to doxygen's
+# configuration file, i.e. a series of assignments. You only have to provide
+# replacements, missing definitions are set to their default value.
+#
+# See also section "Doxygen usage" for information on how to generate the
+# default style sheet that doxygen normally uses.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_STYLESHEET_FILE    =
+
+# Set optional variables used in the generation of an RTF document. Syntax is
+# similar to doxygen's configuration file. A template extensions file can be
+# generated using doxygen -e rtf extensionFile.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_EXTENSIONS_FILE    =
+
+# If the RTF_SOURCE_CODE tag is set to YES then doxygen will include source code
+# with syntax highlighting in the RTF output.
+#
+# Note that which sources are shown also depends on other settings such as
+# SOURCE_BROWSER.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_RTF is set to YES.
+
+RTF_SOURCE_CODE        = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the man page output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_MAN tag is set to YES, doxygen will generate man pages for
+# classes and files.
+# The default value is: NO.
+
+GENERATE_MAN           = NO
+
+# The MAN_OUTPUT tag is used to specify where the man pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it. A directory man3 will be created inside the directory specified by
+# MAN_OUTPUT.
+# The default directory is: man.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_OUTPUT             = man
+
+# The MAN_EXTENSION tag determines the extension that is added to the generated
+# man pages. In case the manual section does not start with a number, the number
+# 3 is prepended. The dot (.) at the beginning of the MAN_EXTENSION tag is
+# optional.
+# The default value is: .3.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_EXTENSION          = .3
+
+# The MAN_SUBDIR tag determines the name of the directory created within
+# MAN_OUTPUT in which the man pages are placed. If defaults to man followed by
+# MAN_EXTENSION with the initial . removed.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_SUBDIR             =
+
+# If the MAN_LINKS tag is set to YES and doxygen generates man output, then it
+# will generate one additional man file for each entity documented in the real
+# man page(s). These additional files only source the real man page, but without
+# them the man command would be unable to find the correct page.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_MAN is set to YES.
+
+MAN_LINKS              = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the XML output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_XML tag is set to YES, doxygen will generate an XML file that
+# captures the structure of the code including all documentation.
+# The default value is: NO.
+
+GENERATE_XML           = YES
+
+# The XML_OUTPUT tag is used to specify where the XML pages will be put. If a
+# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of
+# it.
+# The default directory is: xml.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_OUTPUT             = sphinx/xml
+
+# If the XML_PROGRAMLISTING tag is set to YES, doxygen will dump the program
+# listings (including syntax highlighting and cross-referencing information) to
+# the XML output. Note that enabling this will significantly increase the size
+# of the XML output.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_PROGRAMLISTING     = YES
+
+# If the XML_NS_MEMB_FILE_SCOPE tag is set to YES, doxygen will include
+# namespace members in file scope as well, matching the HTML output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_XML is set to YES.
+
+XML_NS_MEMB_FILE_SCOPE = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the DOCBOOK output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_DOCBOOK tag is set to YES, doxygen will generate Docbook files
+# that can be used to generate PDF.
+# The default value is: NO.
+
+GENERATE_DOCBOOK       = NO
+
+# The DOCBOOK_OUTPUT tag is used to specify where the Docbook pages will be put.
+# If a relative path is entered the value of OUTPUT_DIRECTORY will be put in
+# front of it.
+# The default directory is: docbook.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_OUTPUT         = docbook
+
+# If the DOCBOOK_PROGRAMLISTING tag is set to YES, doxygen will include the
+# program listings (including syntax highlighting and cross-referencing
+# information) to the DOCBOOK output. Note that enabling this will significantly
+# increase the size of the DOCBOOK output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_DOCBOOK is set to YES.
+
+DOCBOOK_PROGRAMLISTING = NO
+
+#---------------------------------------------------------------------------
+# Configuration options for the AutoGen Definitions output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_AUTOGEN_DEF tag is set to YES, doxygen will generate an
+# AutoGen Definitions (see http://autogen.sourceforge.net/) file that captures
+# the structure of the code including all documentation. Note that this feature
+# is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_AUTOGEN_DEF   = NO
+
+#---------------------------------------------------------------------------
+# Configuration options related to the Perl module output
+#---------------------------------------------------------------------------
+
+# If the GENERATE_PERLMOD tag is set to YES, doxygen will generate a Perl module
+# file that captures the structure of the code including all documentation.
+#
+# Note that this feature is still experimental and incomplete at the moment.
+# The default value is: NO.
+
+GENERATE_PERLMOD       = NO
+
+# If the PERLMOD_LATEX tag is set to YES, doxygen will generate the necessary
+# Makefile rules, Perl scripts and LaTeX code to be able to generate PDF and DVI
+# output from the Perl module output.
+# The default value is: NO.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_LATEX          = NO
+
+# If the PERLMOD_PRETTY tag is set to YES, the Perl module output will be nicely
+# formatted so it can be parsed by a human reader. This is useful if you want to
+# understand what is going on. On the other hand, if this tag is set to NO, the
+# size of the Perl module output will be much smaller and Perl will parse it
+# just the same.
+# The default value is: YES.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_PRETTY         = YES
+
+# The names of the make variables in the generated doxyrules.make file are
+# prefixed with the string contained in PERLMOD_MAKEVAR_PREFIX. This is useful
+# so different doxyrules.make files included by the same Makefile don't
+# overwrite each other's variables.
+# This tag requires that the tag GENERATE_PERLMOD is set to YES.
+
+PERLMOD_MAKEVAR_PREFIX =
+
+#---------------------------------------------------------------------------
+# Configuration options related to the preprocessor
+#---------------------------------------------------------------------------
+
+# If the ENABLE_PREPROCESSING tag is set to YES, doxygen will evaluate all
+# C-preprocessor directives found in the sources and include files.
+# The default value is: YES.
+
+ENABLE_PREPROCESSING   = YES
+
+# If the MACRO_EXPANSION tag is set to YES, doxygen will expand all macro names
+# in the source code. If set to NO, only conditional compilation will be
+# performed. Macro expansion can be done in a controlled way by setting
+# EXPAND_ONLY_PREDEF to YES.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+MACRO_EXPANSION        = NO
+
+# If the EXPAND_ONLY_PREDEF and MACRO_EXPANSION tags are both set to YES then
+# the macro expansion is limited to the macros specified with the PREDEFINED and
+# EXPAND_AS_DEFINED tags.
+# The default value is: NO.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_ONLY_PREDEF     = NO
+
+# If the SEARCH_INCLUDES tag is set to YES, the include files in the
+# INCLUDE_PATH will be searched if a #include is found.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SEARCH_INCLUDES        = YES
+
+# The INCLUDE_PATH tag can be used to specify one or more directories that
+# contain include files that are not input files but should be processed by the
+# preprocessor.
+# This tag requires that the tag SEARCH_INCLUDES is set to YES.
+
+INCLUDE_PATH           =
+
+# You can use the INCLUDE_FILE_PATTERNS tag to specify one or more wildcard
+# patterns (like *.h and *.hpp) to filter out the header-files in the
+# directories. If left blank, the patterns specified with FILE_PATTERNS will be
+# used.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+INCLUDE_FILE_PATTERNS  =
+
+# The PREDEFINED tag can be used to specify one or more macro names that are
+# defined before the preprocessor is started (similar to the -D option of e.g.
+# gcc). The argument of the tag is a list of macros of the form: name or
+# name=definition (no spaces). If the definition and the "=" are omitted, "=1"
+# is assumed. To prevent a macro definition from being undefined via #undef or
+# recursively expanded use the := operator instead of the = operator.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+PREDEFINED             = DOXYGEN_FORCED
+
+# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this
+# tag can be used to specify a list of macro names that should be expanded. The
+# macro definition that is found in the sources will be used. Use the PREDEFINED
+# tag if you want to use a different macro definition that overrules the
+# definition found in the source code.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+EXPAND_AS_DEFINED      =
+
+# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
+# remove all references to function-like macros that are alone on a line, have
+# an all uppercase name, and do not end with a semicolon. Such function macros
+# are typically used for boiler-plate code, and will confuse the parser if not
+# removed.
+# The default value is: YES.
+# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
+
+SKIP_FUNCTION_MACROS   = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to external references
+#---------------------------------------------------------------------------
+
+# The TAGFILES tag can be used to specify one or more tag files. For each tag
+# file the location of the external documentation should be added. The format of
+# a tag file without this location is as follows:
+# TAGFILES = file1 file2 ...
+# Adding location for the tag files is done as follows:
+# TAGFILES = file1=loc1 "file2 = loc2" ...
+# where loc1 and loc2 can be relative or absolute paths or URLs. See the
+# section "Linking to external documentation" for more information about the use
+# of tag files.
+# Note: Each tag file must have a unique name (where the name does NOT include
+# the path). If a tag file is not located in the directory in which doxygen is
+# run, you must also specify the path to the tagfile here.
+
+TAGFILES               =
+
+# When a file name is specified after GENERATE_TAGFILE, doxygen will create a
+# tag file that is based on the input files it reads. See section "Linking to
+# external documentation" for more information about the usage of tag files.
+
+GENERATE_TAGFILE       =
+
+# If the ALLEXTERNALS tag is set to YES, all external class will be listed in
+# the class index. If set to NO, only the inherited external classes will be
+# listed.
+# The default value is: NO.
+
+ALLEXTERNALS           = NO
+
+# If the EXTERNAL_GROUPS tag is set to YES, all external groups will be listed
+# in the modules index. If set to NO, only the current project's groups will be
+# listed.
+# The default value is: YES.
+
+EXTERNAL_GROUPS        = YES
+
+# If the EXTERNAL_PAGES tag is set to YES, all external pages will be listed in
+# the related pages index. If set to NO, only the current project's pages will
+# be listed.
+# The default value is: YES.
+
+EXTERNAL_PAGES         = YES
+
+#---------------------------------------------------------------------------
+# Configuration options related to the dot tool
+#---------------------------------------------------------------------------
+
+# If the CLASS_DIAGRAMS tag is set to YES, doxygen will generate a class diagram
+# (in HTML and LaTeX) for classes with base or super classes. Setting the tag to
+# NO turns the diagrams off. Note that this option also works with HAVE_DOT
+# disabled, but it is recommended to install and use dot, since it yields more
+# powerful graphs.
+# The default value is: YES.
+
+CLASS_DIAGRAMS         = YES
+
+# You can include diagrams made with dia in doxygen documentation. Doxygen will
+# then run dia to produce the diagram and insert it in the documentation. The
+# DIA_PATH tag allows you to specify the directory where the dia binary resides.
+# If left empty dia is assumed to be found in the default search path.
+
+DIA_PATH               =
+
+# If set to YES the inheritance and collaboration graphs will hide inheritance
+# and usage relations if the target is undocumented or is not a class.
+# The default value is: YES.
+
+HIDE_UNDOC_RELATIONS   = YES
+
+# If you set the HAVE_DOT tag to YES then doxygen will assume the dot tool is
+# available from the path. This tool is part of Graphviz (see:
+# http://www.graphviz.org/), a graph visualization toolkit from AT&T and Lucent
+# Bell Labs. The other options in this section have no effect if this option is
+# set to NO
+# The default value is: NO.
+
+HAVE_DOT               = NO
+
+# The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
+# to run in parallel. When set to 0 doxygen will base this on the number of
+# processors available in the system. You can set it explicitly to a value
+# larger than 0 to get control over the balance between CPU load and processing
+# speed.
+# Minimum value: 0, maximum value: 32, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_NUM_THREADS        = 0
+
+# When you want a differently looking font in the dot files that doxygen
+# generates you can specify the font name using DOT_FONTNAME. You need to make
+# sure dot is able to find the font, which can be done by putting it in a
+# standard location or by setting the DOTFONTPATH environment variable or by
+# setting DOT_FONTPATH to the directory containing the font.
+# The default value is: Helvetica.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTNAME           = Helvetica
+
+# The DOT_FONTSIZE tag can be used to set the size (in points) of the font of
+# dot graphs.
+# Minimum value: 4, maximum value: 24, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTSIZE           = 10
+
+# By default doxygen will tell dot to use the default font as specified with
+# DOT_FONTNAME. If you specify a different font using DOT_FONTNAME you can set
+# the path where dot can find it using this tag.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_FONTPATH           =
+
+# If the CLASS_GRAPH tag is set to YES then doxygen will generate a graph for
+# each documented class showing the direct and indirect inheritance relations.
+# Setting this tag to YES will force the CLASS_DIAGRAMS tag to NO.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CLASS_GRAPH            = YES
+
+# If the COLLABORATION_GRAPH tag is set to YES then doxygen will generate a
+# graph for each documented class showing the direct and indirect implementation
+# dependencies (inheritance, containment, and class references variables) of the
+# class with other documented classes.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+COLLABORATION_GRAPH    = YES
+
+# If the GROUP_GRAPHS tag is set to YES then doxygen will generate a graph for
+# groups, showing the direct groups dependencies.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GROUP_GRAPHS           = YES
+
+# If the UML_LOOK tag is set to YES, doxygen will generate inheritance and
+# collaboration diagrams in a style similar to the OMG's Unified Modeling
+# Language.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LOOK               = NO
+
+# If the UML_LOOK tag is enabled, the fields and methods are shown inside the
+# class node. If there are many fields or methods and many nodes the graph may
+# become too big to be useful. The UML_LIMIT_NUM_FIELDS threshold limits the
+# number of items for each type to make the size more manageable. Set this to 0
+# for no limit. Note that the threshold may be exceeded by 50% before the limit
+# is enforced. So when you set the threshold to 10, up to 15 fields may appear,
+# but if the number exceeds 15, the total amount of fields shown is limited to
+# 10.
+# Minimum value: 0, maximum value: 100, default value: 10.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+UML_LIMIT_NUM_FIELDS   = 10
+
+# If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and
+# collaboration graphs will show the relations between templates and their
+# instances.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+TEMPLATE_RELATIONS     = NO
+
+# If the INCLUDE_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are set to
+# YES then doxygen will generate a graph for each documented file showing the
+# direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDE_GRAPH          = YES
+
+# If the INCLUDED_BY_GRAPH, ENABLE_PREPROCESSING and SEARCH_INCLUDES tags are
+# set to YES then doxygen will generate a graph for each documented file showing
+# the direct and indirect include dependencies of the file with other documented
+# files.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INCLUDED_BY_GRAPH      = YES
+
+# If the CALL_GRAPH tag is set to YES then doxygen will generate a call
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable call graphs for selected
+# functions only using the \callgraph command. Disabling a call graph can be
+# accomplished by means of the command \hidecallgraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALL_GRAPH             = NO
+
+# If the CALLER_GRAPH tag is set to YES then doxygen will generate a caller
+# dependency graph for every global function or class method.
+#
+# Note that enabling this option will significantly increase the time of a run.
+# So in most cases it will be better to enable caller graphs for selected
+# functions only using the \callergraph command. Disabling a caller graph can be
+# accomplished by means of the command \hidecallergraph.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+CALLER_GRAPH           = NO
+
+# If the GRAPHICAL_HIERARCHY tag is set to YES then doxygen will graphical
+# hierarchy of all classes instead of a textual one.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GRAPHICAL_HIERARCHY    = YES
+
+# If the DIRECTORY_GRAPH tag is set to YES then doxygen will show the
+# dependencies a directory has on other directories in a graphical way. The
+# dependency relations are determined by the #include relations between the
+# files in the directories.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DIRECTORY_GRAPH        = YES
+
+# The DOT_IMAGE_FORMAT tag can be used to set the image format of the images
+# generated by dot. For an explanation of the image formats see the section
+# output formats in the documentation of the dot tool (Graphviz (see:
+# http://www.graphviz.org/)).
+# Note: If you choose svg you need to set HTML_FILE_EXTENSION to xhtml in order
+# to make the SVG files visible in IE 9+ (other browsers do not have this
+# requirement).
+# Possible values are: png, jpg, gif, svg, png:gd, png:gd:gd, png:cairo,
+# png:cairo:gd, png:cairo:cairo, png:cairo:gdiplus, png:gdiplus and
+# png:gdiplus:gdiplus.
+# The default value is: png.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_IMAGE_FORMAT       = png
+
+# If DOT_IMAGE_FORMAT is set to svg, then this option can be set to YES to
+# enable generation of interactive SVG images that allow zooming and panning.
+#
+# Note that this requires a modern browser other than Internet Explorer. Tested
+# and working are Firefox, Chrome, Safari, and Opera.
+# Note: For IE 9+ you need to set HTML_FILE_EXTENSION to xhtml in order to make
+# the SVG files visible. Older versions of IE do not have SVG support.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+INTERACTIVE_SVG        = NO
+
+# The DOT_PATH tag can be used to specify the path where the dot tool can be
+# found. If left blank, it is assumed the dot tool can be found in the path.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_PATH               =
+
+# The DOTFILE_DIRS tag can be used to specify one or more directories that
+# contain dot files that are included in the documentation (see the \dotfile
+# command).
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOTFILE_DIRS           =
+
+# The MSCFILE_DIRS tag can be used to specify one or more directories that
+# contain msc files that are included in the documentation (see the \mscfile
+# command).
+
+MSCFILE_DIRS           =
+
+# The DIAFILE_DIRS tag can be used to specify one or more directories that
+# contain dia files that are included in the documentation (see the \diafile
+# command).
+
+DIAFILE_DIRS           =
+
+# When using plantuml, the PLANTUML_JAR_PATH tag should be used to specify the
+# path where java can find the plantuml.jar file. If left blank, it is assumed
+# PlantUML is not used or called during a preprocessing step. Doxygen will
+# generate a warning when it encounters a \startuml command in this case and
+# will not generate output for the diagram.
+
+PLANTUML_JAR_PATH      =
+
+# When using plantuml, the PLANTUML_CFG_FILE tag can be used to specify a
+# configuration file for plantuml.
+
+PLANTUML_CFG_FILE      =
+
+# When using plantuml, the specified paths are searched for files specified by
+# the !include statement in a plantuml block.
+
+PLANTUML_INCLUDE_PATH  =
+
+# The DOT_GRAPH_MAX_NODES tag can be used to set the maximum number of nodes
+# that will be shown in the graph. If the number of nodes in a graph becomes
+# larger than this value, doxygen will truncate the graph, which is visualized
+# by representing a node as a red box. Note that doxygen if the number of direct
+# children of the root node in a graph is already larger than
+# DOT_GRAPH_MAX_NODES then the graph will not be shown at all. Also note that
+# the size of a graph can be further restricted by MAX_DOT_GRAPH_DEPTH.
+# Minimum value: 0, maximum value: 10000, default value: 50.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_GRAPH_MAX_NODES    = 50
+
+# The MAX_DOT_GRAPH_DEPTH tag can be used to set the maximum depth of the graphs
+# generated by dot. A depth value of 3 means that only nodes reachable from the
+# root by following a path via at most 3 edges will be shown. Nodes that lay
+# further from the root node will be omitted. Note that setting this option to 1
+# or 2 may greatly reduce the computation time needed for large code bases. Also
+# note that the size of a graph can be further restricted by
+# DOT_GRAPH_MAX_NODES. Using a depth of 0 means no depth restriction.
+# Minimum value: 0, maximum value: 1000, default value: 0.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+MAX_DOT_GRAPH_DEPTH    = 0
+
+# Set the DOT_TRANSPARENT tag to YES to generate images with a transparent
+# background. This is disabled by default, because dot on Windows does not seem
+# to support this out of the box.
+#
+# Warning: Depending on the platform used, enabling this option may lead to
+# badly anti-aliased labels on the edges of a graph (i.e. they become hard to
+# read).
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_TRANSPARENT        = NO
+
+# Set the DOT_MULTI_TARGETS tag to YES to allow dot to generate multiple output
+# files in one run (i.e. multiple -o and -T options on the command line). This
+# makes dot run faster, but since only newer versions of dot (>1.8.10) support
+# this, this feature is disabled by default.
+# The default value is: NO.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_MULTI_TARGETS      = NO
+
+# If the GENERATE_LEGEND tag is set to YES doxygen will generate a legend page
+# explaining the meaning of the various boxes and arrows in the dot generated
+# graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+GENERATE_LEGEND        = YES
+
+# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot
+# files that are used to generate the various graphs.
+# The default value is: YES.
+# This tag requires that the tag HAVE_DOT is set to YES.
+
+DOT_CLEANUP            = YES
diff --git a/arduino/libraries/RF24/LICENSE b/arduino/libraries/RF24/LICENSE
new file mode 100644
index 000000000..23cb79033
--- /dev/null
+++ b/arduino/libraries/RF24/LICENSE
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc., <http://fsf.org/>
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                            NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    {description}
+    Copyright (C) {year}  {fullname}
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  {signature of Ty Coon}, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/arduino/libraries/RF24/README.md b/arduino/libraries/RF24/README.md
new file mode 100644
index 000000000..aaf657a92
--- /dev/null
+++ b/arduino/libraries/RF24/README.md
@@ -0,0 +1,16 @@
+<!-- markdownlint-disable MD041 MD034 -->
+[![Arduino CLI 
build](https://github.com/nRF24/RF24/workflows/Arduino%20CLI%20build/badge.svg)](https://github.com/nRF24/RF24/actions?query=workflow%3A%22Arduino+CLI+build%22)
+[![Linux 
build](https://github.com/nRF24/RF24/workflows/Linux%20build/badge.svg)](https://github.com/nRF24/RF24/actions?query=workflow%3A%22Linux+build%22)
+[![PlatformIO 
build](https://github.com/nRF24/RF24/actions/workflows/build_platformIO.yml/badge.svg)](https://github.com/nRF24/RF24/actions/workflows/build_platformIO.yml)
+[![RP2xxx 
build](https://github.com/nRF24/RF24/actions/workflows/build_rp2xxx.yml/badge.svg)](https://github.com/nRF24/RF24/actions/workflows/build_rp2xxx.yml)
+[![Documentation 
Status](https://readthedocs.org/projects/rf24/badge/?version=latest)](https://rf24.readthedocs.io/en/latest/?badge=latest)
+
+# See http://nRF24.github.io/RF24 for all documentation
+
+## Having problems?
+
+Please read our **[solutions to common problems](COMMON_ISSUES.md)**. If that doesn't help, then open an 
issue describing your problem with as much detail as possible.
+
+### Want to contribute?
+
+Awesome! However, please check our [contributing guidelines](CONTRIBUTING.md) before opening a pull request.
diff --git a/arduino/libraries/RF24/RF24.cpp b/arduino/libraries/RF24/RF24.cpp
new file mode 100644
index 000000000..1fd63d82a
--- /dev/null
+++ b/arduino/libraries/RF24/RF24.cpp
@@ -0,0 +1,1895 @@
+/*
+ Copyright (C) 2011 J. Coliz <maniacbug ymail com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+ */
+
+#include "nRF24L01.h"
+#include "RF24_config.h"
+#include "RF24.h"
+
+
+/****************************************************************************/
+
+void RF24::csn(bool mode)
+{
+    #if defined(RF24_TINY)
+    if (ce_pin != csn_pin) {
+        digitalWrite(csn_pin, mode);
+    }
+    else {
+        if (mode == HIGH) {
+            PORTB |= (1<<PINB2);                              // SCK->CSN HIGH
+            delayMicroseconds(RF24_CSN_SETTLE_HIGH_DELAY); // allow csn to settle.
+        }
+        else {
+            PORTB &= ~(1<<PINB2);                            // SCK->CSN LOW
+            delayMicroseconds(RF24_CSN_SETTLE_LOW_DELAY); // allow csn to settle
+        }
+    }
+    // Return, CSN toggle complete
+    return;
+
+    #elif defined (ARDUINO) && !defined(RF24_SPI_TRANSACTIONS)
+    // Minimum ideal SPI bus speed is 2x data rate
+    // If we assume 2Mbs data rate and 16Mhz clock, a
+    // divider of 4 is the minimum we want.
+    // CLK:BUS 8Mhz:2Mhz, 16Mhz:4Mhz, or 20Mhz:5Mhz
+
+        #if !defined(SOFTSPI)
+        // applies to SPI_UART and inherent hardware SPI
+            #if defined (RF24_SPI_PTR)
+    _spi->setBitOrder(MSBFIRST);
+    _spi->setDataMode(SPI_MODE0);
+
+                #if !defined(F_CPU) || F_CPU < 20000000
+    _spi->setClockDivider(SPI_CLOCK_DIV2);
+                #elif F_CPU < 40000000
+    _spi->setClockDivider(SPI_CLOCK_DIV4);
+                #elif F_CPU < 80000000
+    _spi->setClockDivider(SPI_CLOCK_DIV8);
+                #elif F_CPU < 160000000
+    _spi->setClockDivider(SPI_CLOCK_DIV16);
+                #elif F_CPU < 320000000
+    _spi->setClockDivider(SPI_CLOCK_DIV32);
+                #elif F_CPU < 640000000
+    _spi->setClockDivider(SPI_CLOCK_DIV64);
+                #elif F_CPU < 1280000000
+    _spi->setClockDivider(SPI_CLOCK_DIV128);
+                #else // F_CPU >= 1280000000
+                    #error "Unsupported CPU frequency. Please set correct SPI divider."
+                #endif // F_CPU to SPI_CLOCK_DIV translation
+
+            #else // !defined(RF24_SPI_PTR)
+    _SPI.setBitOrder(MSBFIRST);
+    _SPI.setDataMode(SPI_MODE0);
+
+                #if !defined(F_CPU) || F_CPU < 20000000
+    _SPI.setClockDivider(SPI_CLOCK_DIV2);
+                #elif F_CPU < 40000000
+    _SPI.setClockDivider(SPI_CLOCK_DIV4);
+                #elif F_CPU < 80000000
+    _SPI.setClockDivider(SPI_CLOCK_DIV8);
+                #elif F_CPU < 160000000
+    _SPI.setClockDivider(SPI_CLOCK_DIV16);
+                #elif F_CPU < 320000000
+    _SPI.setClockDivider(SPI_CLOCK_DIV32);
+                #elif F_CPU < 640000000
+    _SPI.setClockDivider(SPI_CLOCK_DIV64);
+                #elif F_CPU < 1280000000
+    _SPI.setClockDivider(SPI_CLOCK_DIV128);
+                #else // F_CPU >= 1280000000
+                    #error "Unsupported CPU frequency. Please set correct SPI divider."
+                #endif // F_CPU to SPI_CLOCK_DIV translation
+            #endif // !defined(RF24_SPI_PTR)
+        #endif // !defined(SOFTSPI)
+
+    #elif defined (RF24_RPi)
+    if(!mode)
+        _SPI.chipSelect(csn_pin);
+    #endif // defined(RF24_RPi)
+
+    #if !defined(RF24_LINUX)
+    digitalWrite(csn_pin, mode);
+    delayMicroseconds(csDelay);
+    #else
+    static_cast<void>(mode); // ignore -Wunused-parameter
+    #endif // !defined(RF24_LINUX)
+}
+
+/****************************************************************************/
+
+void RF24::ce(bool level)
+{
+    //Allow for 3-pin use on ATTiny
+    if (ce_pin != csn_pin) {
+        digitalWrite(ce_pin, level);
+    }
+}
+
+/****************************************************************************/
+
+inline void RF24::beginTransaction()
+{
+    #if defined (RF24_SPI_TRANSACTIONS)
+        #if defined (RF24_SPI_PTR)
+            #if defined (RF24_RP2)
+    _spi->beginTransaction(spi_speed);
+            #else // ! defined (RF24_RP2)
+    _spi->beginTransaction(SPISettings(spi_speed, MSBFIRST, SPI_MODE0));
+            #endif // ! defined (RF24_RP2)
+        #else // !defined(RF24_SPI_PTR)
+    _SPI.beginTransaction(SPISettings(spi_speed, MSBFIRST, SPI_MODE0));
+        #endif // !defined(RF24_SPI_PTR)
+    #endif // defined (RF24_SPI_TRANSACTIONS)
+    csn(LOW);
+}
+
+/****************************************************************************/
+
+inline void RF24::endTransaction()
+{
+    csn(HIGH);
+    #if defined (RF24_SPI_TRANSACTIONS)
+        #if defined (RF24_SPI_PTR)
+    _spi->endTransaction();
+        #else // !defined(RF24_SPI_PTR)
+    _SPI.endTransaction();
+        #endif // !defined(RF24_SPI_PTR)
+    #endif // defined (RF24_SPI_TRANSACTIONS)
+}
+
+/****************************************************************************/
+
+void RF24::read_register(uint8_t reg, uint8_t* buf, uint8_t len)
+{
+    #if defined (RF24_LINUX) || defined (RF24_RP2)
+    beginTransaction(); //configures the spi settings for RPi, locks mutex and setting csn low
+    uint8_t * prx = spi_rxbuff;
+    uint8_t * ptx = spi_txbuff;
+    uint8_t size = static_cast<uint8_t>(len + 1); // Add register value to transmit buffer
+
+    *ptx++ = (R_REGISTER | reg);
+
+    while (len--){ *ptx++ = RF24_NOP; } // Dummy operation, just for reading
+
+        #if defined (RF24_RP2)
+    _spi->transfernb((const uint8_t*)spi_txbuff, spi_rxbuff, size);
+        #else // !defined (RF24_RP2)
+    _SPI.transfernb(reinterpret_cast<char *>(spi_txbuff), reinterpret_cast<char *>(spi_rxbuff), size);
+        #endif // !defined (RF24_RP2)
+
+    status = *prx++; // status is 1st byte of receive buffer
+
+    // decrement before to skip status byte
+    while (--size) { *buf++ = *prx++; }
+
+    endTransaction(); // unlocks mutex and setting csn high
+
+    #else // !defined(RF24_LINUX) && !defined(RF24_RP2)
+
+    beginTransaction();
+        #if defined (RF24_SPI_PTR)
+    status = _spi->transfer(R_REGISTER | reg);
+    while (len--) { *buf++ = _spi->transfer(0xFF); }
+
+        #else // !defined(RF24_SPI_PTR)
+    status = _SPI.transfer(R_REGISTER | reg);
+    while (len--) { *buf++ = _SPI.transfer(0xFF); }
+
+        #endif // !defined(RF24_SPI_PTR)
+    endTransaction();
+    #endif // !defined(RF24_LINUX) && !defined(RF24_RP2)
+}
+
+/****************************************************************************/
+
+uint8_t RF24::read_register(uint8_t reg)
+{
+    uint8_t result;
+
+    #if defined (RF24_LINUX) || defined (RF24_RP2)
+    beginTransaction();
+
+    uint8_t * prx = spi_rxbuff;
+    uint8_t * ptx = spi_txbuff;
+    *ptx++ = (R_REGISTER | reg);
+    *ptx++ = RF24_NOP ; // Dummy operation, just for reading
+
+        #if defined (RF24_RP2)
+    _spi->transfernb((const uint8_t*)spi_txbuff, spi_rxbuff, 2);
+        #else // !defined(RF24_RP2)
+    _SPI.transfernb(reinterpret_cast<char *>(spi_txbuff), reinterpret_cast<char *>(spi_rxbuff), 2);
+        #endif // !defined(RF24_RP2)
+
+    status = *prx;     // status is 1st byte of receive buffer
+    result = *++prx;   // result is 2nd byte of receive buffer
+
+    endTransaction();
+    #else // !defined(RF24_LINUX) && !defined(RF24_RP2)
+
+    beginTransaction();
+        #if defined (RF24_SPI_PTR)
+    status = _spi->transfer(R_REGISTER | reg);
+    result = _spi->transfer(0xff);
+
+        #else // !defined(RF24_SPI_PTR)
+    status = _SPI.transfer(R_REGISTER | reg);
+    result = _SPI.transfer(0xff);
+
+        #endif // !defined(RF24_SPI_PTR)
+    endTransaction();
+    #endif // !defined(RF24_LINUX) && !defined(RF24_RP2)
+
+    return result;
+}
+
+/****************************************************************************/
+
+void RF24::write_register(uint8_t reg, const uint8_t* buf, uint8_t len)
+{
+    #if defined (RF24_LINUX) || defined (RF24_RP2)
+    beginTransaction();
+    uint8_t * prx = spi_rxbuff;
+    uint8_t * ptx = spi_txbuff;
+    uint8_t size = static_cast<uint8_t>(len + 1); // Add register value to transmit buffer
+
+    *ptx++ = (W_REGISTER | (REGISTER_MASK & reg));
+    while (len--) { *ptx++ = *buf++; }
+
+        #if defined (RF24_RP2)
+    _spi->transfernb((const uint8_t*)spi_txbuff, spi_rxbuff, size);
+        #else // !defined(RF24_RP2)
+    _SPI.transfernb(reinterpret_cast<char *>(spi_txbuff), reinterpret_cast<char *>(spi_rxbuff), size);
+        #endif // !defined(RF24_RP2)
+
+    status = *prx; // status is 1st byte of receive buffer
+    endTransaction();
+    #else // !defined(RF24_LINUX) && !defined(RF24_RP2)
+
+    beginTransaction();
+        #if defined (RF24_SPI_PTR)
+    status = _spi->transfer(W_REGISTER | reg);
+    while (len--) { _spi->transfer(*buf++); }
+
+        #else // !defined(RF24_SPI_PTR)
+    status = _SPI.transfer(W_REGISTER | reg);
+    while (len--) { _SPI.transfer(*buf++); }
+
+        #endif // !defined(RF24_SPI_PTR)
+    endTransaction();
+    #endif // !defined(RF24_LINUX) && !defined(RF24_RP2)
+}
+
+/****************************************************************************/
+
+void RF24::write_register(uint8_t reg, uint8_t value, bool is_cmd_only)
+{
+    if (is_cmd_only) {
+        if (reg != RF24_NOP) { // don't print the get_status() operation
+            IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x)\r\n"), reg));
+        }
+        beginTransaction();
+        #if defined (RF24_LINUX)
+        status = _SPI.transfer(W_REGISTER | reg);
+        #else // !defined(RF24_LINUX) || defined (RF24_RP2)
+            #if defined (RF24_SPI_PTR)
+        status = _spi->transfer(W_REGISTER | reg);
+            #else // !defined (RF24_SPI_PTR)
+        status = _SPI.transfer(W_REGISTER | reg);
+            #endif // !defined (RF24_SPI_PTR)
+        #endif // !defined(RF24_LINUX) || defined(RF24_RP2)
+        endTransaction();
+    }
+    else {
+        IF_SERIAL_DEBUG(printf_P(PSTR("write_register(%02x,%02x)\r\n"), reg, value));
+        #if defined (RF24_LINUX) || defined (RF24_RP2)
+        beginTransaction();
+        uint8_t * prx = spi_rxbuff;
+        uint8_t * ptx = spi_txbuff;
+        *ptx++ = (W_REGISTER | reg);
+        *ptx = value;
+
+            #if defined (RF24_RP2)
+        _spi->transfernb((const uint8_t*)spi_txbuff, spi_rxbuff, 2);
+            #else // !defined(RF24_RP2)
+        _SPI.transfernb(reinterpret_cast<char *>(spi_txbuff), reinterpret_cast<char *>(spi_rxbuff), 2);
+            #endif // !defined(RF24_RP2)
+
+        status = *prx++; // status is 1st byte of receive buffer
+        endTransaction();
+        #else // !defined(RF24_LINUX) && !defined(RF24_RP2)
+
+        beginTransaction();
+            #if defined (RF24_SPI_PTR)
+        status = _spi->transfer(W_REGISTER | reg);
+        _spi->transfer(value);
+            #else // !defined(RF24_SPI_PTR)
+        status = _SPI.transfer(W_REGISTER | reg);
+        _SPI.transfer(value);
+            #endif // !defined(RF24_SPI_PTR)
+        endTransaction();
+        #endif // !defined(RF24_LINUX) && !defined(RF24_RP2)
+    }
+}
+
+/****************************************************************************/
+
+void RF24::write_payload(const void* buf, uint8_t data_len, const uint8_t writeType)
+{
+    const uint8_t* current = reinterpret_cast<const uint8_t*>(buf);
+
+    uint8_t blank_len = !data_len ? 1 : 0;
+    if (!dynamic_payloads_enabled) {
+        data_len = rf24_min(data_len, payload_size);
+        blank_len = static_cast<uint8_t>(payload_size - data_len);
+    }
+    else {
+        data_len = rf24_min(data_len, static_cast<uint8_t>(32));
+    }
+
+    //printf("[Writing %u bytes %u blanks]",data_len,blank_len);
+    IF_SERIAL_DEBUG(printf("[Writing %u bytes %u blanks]\n", data_len, blank_len); );
+
+    #if defined (RF24_LINUX) || defined (RF24_RP2)
+    beginTransaction();
+    uint8_t * prx = spi_rxbuff;
+    uint8_t * ptx = spi_txbuff;
+    uint8_t size;
+    size = static_cast<uint8_t>(data_len + blank_len + 1); // Add register value to transmit buffer
+
+    *ptx++ =  writeType;
+    while (data_len--) { *ptx++ =  *current++; }
+    while (blank_len--) { *ptx++ =  0; }
+
+        #if defined (RF24_RP2)
+    _spi->transfernb((const uint8_t*)spi_txbuff, spi_rxbuff, size);
+        #else // !defined(RF24_RP2)
+    _SPI.transfernb(reinterpret_cast<char *>(spi_txbuff), reinterpret_cast<char *>(spi_rxbuff), size);
+        #endif // !defined(RF24_RP2)
+
+    status = *prx; // status is 1st byte of receive buffer
+    endTransaction();
+
+    #else // !defined(RF24_LINUX) && !defined(RF24_RP2)
+
+    beginTransaction();
+        #if defined (RF24_SPI_PTR)
+    status = _spi->transfer(writeType);
+    while (data_len--) { _spi->transfer(*current++); }
+    while (blank_len--) { _spi->transfer(0); }
+
+        #else // !defined(RF24_SPI_PTR)
+    status = _SPI.transfer(writeType);
+    while (data_len--) { _SPI.transfer(*current++); }
+
+    while (blank_len--) { _SPI.transfer(0); }
+
+        #endif // !defined(RF24_SPI_PTR)
+    endTransaction();
+    #endif // !defined(RF24_LINUX) && !defined(RF24_RP2)
+}
+
+/****************************************************************************/
+
+void RF24::read_payload(void* buf, uint8_t data_len)
+{
+    uint8_t* current = reinterpret_cast<uint8_t*>(buf);
+
+    uint8_t blank_len = 0;
+    if (!dynamic_payloads_enabled) {
+        data_len = rf24_min(data_len, payload_size);
+        blank_len = static_cast<uint8_t>(payload_size - data_len);
+    }
+    else {
+        data_len = rf24_min(data_len, static_cast<uint8_t>(32));
+    }
+
+    //printf("[Reading %u bytes %u blanks]",data_len,blank_len);
+
+    IF_SERIAL_DEBUG(printf("[Reading %u bytes %u blanks]\n", data_len, blank_len); );
+
+    #if defined (RF24_LINUX) || defined (RF24_RP2)
+    beginTransaction();
+    uint8_t * prx = spi_rxbuff;
+    uint8_t * ptx = spi_txbuff;
+    uint8_t size;
+    size = static_cast<uint8_t>(data_len + blank_len + 1); // Add register value to transmit buffer
+
+    *ptx++ =  R_RX_PAYLOAD;
+    while(--size) { *ptx++ = RF24_NOP; }
+
+    size = static_cast<uint8_t>(data_len + blank_len + 1); // Size has been lost during while, re affect
+
+        #if defined (RF24_RP2)
+    _spi->transfernb((const uint8_t*)spi_txbuff, spi_rxbuff, size);
+        #else // !defined(RF24_RP2)
+    _SPI.transfernb(reinterpret_cast<char *>(spi_txbuff), reinterpret_cast<char *>(spi_rxbuff), size);
+        #endif // !defined(RF24_RP2)
+
+    status = *prx++; // 1st byte is status
+
+    if (data_len > 0) {
+        // Decrement before to skip 1st status byte
+        while (--data_len) { *current++ = *prx++; }
+
+        *current = *prx;
+    }
+    endTransaction();
+    #else // !defined(RF24_LINUX) && !defined(RF24_RP2)
+
+    beginTransaction();
+        #if defined (RF24_SPI_PTR)
+    status = _spi->transfer(R_RX_PAYLOAD);
+    while (data_len--) { *current++ = _spi->transfer(0xFF); }
+
+    while (blank_len--) { _spi->transfer(0xFF); }
+
+        #else // !defined(RF24_SPI_PTR)
+    status = _SPI.transfer(R_RX_PAYLOAD);
+    while (data_len--) { *current++ = _SPI.transfer(0xFF); }
+    while (blank_len--) { _SPI.transfer(0xff); }
+
+        #endif // !defined(RF24_SPI_PTR)
+    endTransaction();
+
+    #endif // !defined(RF24_LINUX) && !defined(RF24_RP2)
+}
+
+/****************************************************************************/
+
+uint8_t RF24::flush_rx(void)
+{
+    write_register(FLUSH_RX, RF24_NOP, true);
+    return status;
+}
+
+/****************************************************************************/
+
+uint8_t RF24::flush_tx(void)
+{
+    write_register(FLUSH_TX, RF24_NOP, true);
+    return status;
+}
+
+/****************************************************************************/
+
+uint8_t RF24::get_status(void)
+{
+    write_register(RF24_NOP, RF24_NOP, true);
+    return status;
+}
+
+/****************************************************************************/
+#if !defined(MINIMAL)
+
+void RF24::print_status(uint8_t _status)
+{
+    printf_P(PSTR("STATUS\t\t= 0x%02x RX_DR=%x TX_DS=%x MAX_RT=%x RX_P_NO=%x TX_FULL=%x\r\n"), _status, 
(_status & _BV(RX_DR)) ? 1 : 0,
+            (_status & _BV(TX_DS)) ? 1 : 0, (_status & _BV(MAX_RT)) ? 1 : 0, ((_status >> RX_P_NO) & 0x07), 
(_status & _BV(TX_FULL)) ? 1 : 0);
+}
+
+/****************************************************************************/
+
+
+void RF24::print_observe_tx(uint8_t value)
+{
+    printf_P(PSTR("OBSERVE_TX=%02x: POLS_CNT=%x ARC_CNT=%x\r\n"), value, (value >> PLOS_CNT) & 0x0F, (value 
ARC_CNT) & 0x0F);
+}
+
+/****************************************************************************/
+
+void RF24::print_byte_register(const char* name, uint8_t reg, uint8_t qty)
+{
+    //char extra_tab = strlen_P(name) < 8 ? '\t' : 0;
+    //printf_P(PSTR(PRIPSTR"\t%c ="),name,extra_tab);
+    #if defined(RF24_LINUX)
+    printf("%s\t=", name);
+    #else // !defined(RF24_LINUX)
+    printf_P(PSTR(PRIPSTR"\t="),name);
+    #endif // !defined(RF24_LINUX)
+    while (qty--) {
+        printf_P(PSTR(" 0x%02x"), read_register(reg++));
+    }
+    printf_P(PSTR("\r\n"));
+}
+
+/****************************************************************************/
+
+void RF24::print_address_register(const char* name, uint8_t reg, uint8_t qty)
+{
+
+    #if defined(RF24_LINUX)
+    printf("%s\t=", name);
+    #else // !defined(RF24_LINUX)
+    printf_P(PSTR(PRIPSTR"\t="), name);
+    #endif // !defined(RF24_LINUX)
+    uint8_t *buffer = new uint8_t[addr_width];
+    while (qty--) {
+        read_register(reg++ & REGISTER_MASK, buffer, addr_width);
+
+        printf_P(PSTR(" 0x"));
+        uint8_t* bufptr = buffer + addr_width;
+        while (--bufptr >= buffer) {
+            printf_P(PSTR("%02x"), *bufptr);
+        }
+    }
+    delete[] buffer;
+    printf_P(PSTR("\r\n"));
+}
+
+#endif // !defined(MINIMAL)
+
+/****************************************************************************/
+
+RF24::RF24(uint16_t _cepin, uint16_t _cspin, uint32_t _spi_speed)
+        :ce_pin(_cepin), csn_pin(_cspin), spi_speed(_spi_speed), payload_size(32), _is_p_variant(false), 
_is_p0_rx(false), addr_width(5), dynamic_payloads_enabled(true),
+         csDelay(5)
+{
+    _init_obj();
+}
+
+/****************************************************************************/
+
+RF24::RF24(uint32_t _spi_speed)
+        :ce_pin(0xFFFF), csn_pin(0xFFFF), spi_speed(_spi_speed), payload_size(32), _is_p_variant(false), 
_is_p0_rx(false), addr_width(5), dynamic_payloads_enabled(true),
+         csDelay(5)
+{
+    _init_obj();
+}
+
+/****************************************************************************/
+
+void RF24::_init_obj()
+{
+    // Use a pointer on the Arduino platform
+
+    #if defined (RF24_SPI_PTR) && !defined (RF24_RP2)
+    _spi = &SPI;
+    #endif // defined (RF24_SPI_PTR)
+
+    pipe0_reading_address[0] = 0;
+    if(spi_speed <= 35000){ //Handle old BCM2835 speed constants, default to RF24_SPI_SPEED
+        spi_speed = RF24_SPI_SPEED;
+    }
+}
+
+/****************************************************************************/
+
+void RF24::setChannel(uint8_t channel)
+{
+    const uint8_t max_channel = 125;
+    write_register(RF_CH, rf24_min(channel, max_channel));
+}
+
+uint8_t RF24::getChannel()
+{
+    return read_register(RF_CH);
+}
+
+/****************************************************************************/
+
+void RF24::setPayloadSize(uint8_t size)
+{
+    // payload size must be in range [1, 32]
+    payload_size = static_cast<uint8_t>(rf24_max(1, rf24_min(32, size)));
+
+    // write static payload size setting for all pipes
+    for (uint8_t i = 0; i < 6; ++i)
+        write_register(static_cast<uint8_t>(RX_PW_P0 + i), payload_size);
+}
+
+/****************************************************************************/
+
+uint8_t RF24::getPayloadSize(void)
+{
+    return payload_size;
+}
+
+/****************************************************************************/
+
+#if !defined(MINIMAL)
+
+static const PROGMEM char rf24_datarate_e_str_0[] = "= 1 MBPS";
+static const PROGMEM char rf24_datarate_e_str_1[] = "= 2 MBPS";
+static const PROGMEM char rf24_datarate_e_str_2[] = "= 250 KBPS";
+static const PROGMEM char * const rf24_datarate_e_str_P[] = {
+  rf24_datarate_e_str_0,
+  rf24_datarate_e_str_1,
+  rf24_datarate_e_str_2,
+};
+static const PROGMEM char rf24_model_e_str_0[] = "nRF24L01";
+static const PROGMEM char rf24_model_e_str_1[] = "nRF24L01+";
+static const PROGMEM char * const rf24_model_e_str_P[] = {
+  rf24_model_e_str_0,
+  rf24_model_e_str_1,
+};
+static const PROGMEM char rf24_crclength_e_str_0[] = "= Disabled";
+static const PROGMEM char rf24_crclength_e_str_1[] = "= 8 bits";
+static const PROGMEM char rf24_crclength_e_str_2[] = "= 16 bits" ;
+static const PROGMEM char * const rf24_crclength_e_str_P[] = {
+  rf24_crclength_e_str_0,
+  rf24_crclength_e_str_1,
+  rf24_crclength_e_str_2,
+};
+static const PROGMEM char rf24_pa_dbm_e_str_0[] = "= PA_MIN";
+static const PROGMEM char rf24_pa_dbm_e_str_1[] = "= PA_LOW";
+static const PROGMEM char rf24_pa_dbm_e_str_2[] = "= PA_HIGH";
+static const PROGMEM char rf24_pa_dbm_e_str_3[] = "= PA_MAX";
+static const PROGMEM char * const rf24_pa_dbm_e_str_P[] = {
+  rf24_pa_dbm_e_str_0,
+  rf24_pa_dbm_e_str_1,
+  rf24_pa_dbm_e_str_2,
+  rf24_pa_dbm_e_str_3,
+};
+
+    #if defined(RF24_LINUX)
+static const char rf24_csn_e_str_0[] = "CE0 (PI Hardware Driven)";
+static const char rf24_csn_e_str_1[] = "CE1 (PI Hardware Driven)";
+static const char rf24_csn_e_str_2[] = "CE2 (PI Hardware Driven)";
+static const char rf24_csn_e_str_3[] = "Custom GPIO Software Driven";
+static const char * const rf24_csn_e_str_P[] = {
+  rf24_csn_e_str_0,
+  rf24_csn_e_str_1,
+  rf24_csn_e_str_2,
+  rf24_csn_e_str_3,
+};
+    #endif // defined(RF24_LINUX)
+
+static const PROGMEM char rf24_feature_e_str_on[] = "= Enabled";
+static const PROGMEM char rf24_feature_e_str_allowed[] = "= Allowed";
+static const PROGMEM char rf24_feature_e_str_open[] = " open ";
+static const PROGMEM char rf24_feature_e_str_closed[] = "closed";
+static const PROGMEM char * const rf24_feature_e_str_P[] = {
+    rf24_crclength_e_str_0,
+    rf24_feature_e_str_on,
+    rf24_feature_e_str_allowed,
+    rf24_feature_e_str_closed,
+    rf24_feature_e_str_open
+};
+
+void RF24::printDetails(void)
+{
+
+    #if defined(RF24_LINUX)
+    printf("================ SPI Configuration ================\n" );
+    uint8_t bus_ce = static_cast<uint8_t>(csn_pin % 10);
+    uint8_t bus_numb = static_cast<uint8_t>((csn_pin - bus_ce) / 10);
+    printf("CSN Pin\t\t= /dev/spidev%d.%d\n", bus_numb, bus_ce);
+    printf("CE Pin\t\t= Custom GPIO%d\n", ce_pin);
+    #endif
+    printf_P(PSTR("SPI Speedz\t= %d Mhz\n"), static_cast<uint8_t>(spi_speed / 1000000)); //Print the SPI 
speed on non-Linux devices
+    #if defined(RF24_LINUX)
+    printf("================ NRF Configuration ================\n");
+    #endif // defined(RF24_LINUX)
+
+    print_status(get_status());
+
+    print_address_register(PSTR("RX_ADDR_P0-1"), RX_ADDR_P0, 2);
+    print_byte_register(PSTR("RX_ADDR_P2-5"), RX_ADDR_P2, 4);
+    print_address_register(PSTR("TX_ADDR\t"), TX_ADDR);
+
+    print_byte_register(PSTR("RX_PW_P0-6"), RX_PW_P0, 6);
+    print_byte_register(PSTR("EN_AA\t"), EN_AA);
+    print_byte_register(PSTR("EN_RXADDR"), EN_RXADDR);
+    print_byte_register(PSTR("RF_CH\t"), RF_CH);
+    print_byte_register(PSTR("RF_SETUP"), RF_SETUP);
+    print_byte_register(PSTR("CONFIG\t"), NRF_CONFIG);
+    print_byte_register(PSTR("DYNPD/FEATURE"), DYNPD, 2);
+
+    printf_P(PSTR("Data Rate\t"
+    PRIPSTR
+    "\r\n"), (char *)(pgm_read_ptr(&rf24_datarate_e_str_P[getDataRate()])));
+    printf_P(PSTR("Model\t\t= "
+    PRIPSTR
+    "\r\n"), (char *)(pgm_read_ptr(&rf24_model_e_str_P[isPVariant()])));
+    printf_P(PSTR("CRC Length\t"
+    PRIPSTR
+    "\r\n"), (char *)(pgm_read_ptr(&rf24_crclength_e_str_P[getCRCLength()])));
+    printf_P(PSTR("PA Power\t"
+    PRIPSTR
+    "\r\n"), (char *)(pgm_read_ptr(&rf24_pa_dbm_e_str_P[getPALevel()])));
+    printf_P(PSTR("ARC\t\t= %d\r\n"), getARC());
+}
+
+void RF24::printPrettyDetails(void) {
+
+    #if defined(RF24_LINUX)
+    printf("================ SPI Configuration ================\n");
+    uint8_t bus_ce = static_cast<uint8_t>(csn_pin % 10);
+    uint8_t bus_numb = static_cast<uint8_t>((csn_pin - bus_ce) / 10);
+    printf("CSN Pin\t\t\t= /dev/spidev%d.%d\n", bus_numb, bus_ce);
+    printf("CE Pin\t\t\t= Custom GPIO%d\n", ce_pin);
+    #endif
+    printf_P(PSTR("SPI Frequency\t\t= %d Mhz\n"), static_cast<uint8_t>(spi_speed / 1000000)); //Print the 
SPI speed on non-Linux devices
+    #if defined(RF24_LINUX)
+    printf("================ NRF Configuration ================\n");
+    #endif // defined(RF24_LINUX)
+
+    uint8_t channel = getChannel();
+    uint16_t frequency = static_cast<uint16_t>(channel + 2400);
+    printf_P(PSTR("Channel\t\t\t= %u (~ %u MHz)\r\n"), channel, frequency);
+
+    printf_P(PSTR("RF Data Rate\t\t"
+    PRIPSTR
+    "\r\n"), (char *)(pgm_read_ptr(&rf24_datarate_e_str_P[getDataRate()])));
+    printf_P(PSTR("RF Power Amplifier\t"
+    PRIPSTR
+    "\r\n"), (char *)(pgm_read_ptr(&rf24_pa_dbm_e_str_P[getPALevel()])));
+    printf_P(PSTR("RF Low Noise Amplifier\t"
+    PRIPSTR
+    "\r\n"), (char *)(pgm_read_ptr(&rf24_feature_e_str_P[(read_register(RF_SETUP) & 1) * 1])));
+    printf_P(PSTR("CRC Length\t\t"
+    PRIPSTR
+    "\r\n"), (char *)(pgm_read_ptr(&rf24_crclength_e_str_P[getCRCLength()])));
+    printf_P(PSTR("Address Length\t\t= %d bytes\r\n"), (read_register(SETUP_AW) & 3) + 2);
+    printf_P(PSTR("Static Payload Length\t= %d bytes\r\n"), getPayloadSize());
+
+    uint8_t setupRetry = read_register(SETUP_RETR);
+    printf_P(PSTR("Auto Retry Delay\t= %d microseconds\r\n"), (setupRetry >> ARD) * 250 + 250);
+    printf_P(PSTR("Auto Retry Attempts\t= %d maximum\r\n"), setupRetry & 0x0F);
+
+    uint8_t observeTx = read_register(OBSERVE_TX);
+    printf_P(PSTR("Packets lost on\n    current channel\t= %d\r\n"), observeTx >> 4);
+    printf_P(PSTR("Retry attempts made for\n    last transmission\t= %d\r\n"), observeTx & 0x0F);
+
+    uint8_t features = read_register(FEATURE);
+    printf_P(PSTR("Multicast\t\t"
+    PRIPSTR
+    "\r\n"), (char *)(pgm_read_ptr(&rf24_feature_e_str_P[static_cast<bool>(features & _BV(EN_DYN_ACK)) * 
2])));
+    printf_P(PSTR("Custom ACK Payload\t"
+    PRIPSTR
+    "\r\n"), (char *)(pgm_read_ptr(&rf24_feature_e_str_P[static_cast<bool>(features & _BV(EN_ACK_PAY)) * 
1])));
+
+    uint8_t dynPl = read_register(DYNPD);
+    printf_P(PSTR("Dynamic Payloads\t"
+    PRIPSTR
+    "\r\n"), (char *)(pgm_read_ptr(&rf24_feature_e_str_P[(dynPl && (features &_BV(EN_DPL))) * 1])));
+
+    uint8_t autoAck = read_register(EN_AA);
+    if (autoAck == 0x3F || autoAck == 0) {
+        // all pipes have the same configuration about auto-ack feature
+        printf_P(PSTR("Auto Acknowledgment\t"
+        PRIPSTR
+        "\r\n"), (char *)(pgm_read_ptr(&rf24_feature_e_str_P[static_cast<bool>(autoAck) * 1])));
+    } else {
+        // representation per pipe
+        printf_P(PSTR("Auto Acknowledgment\t= 0b%c%c%c%c%c%c\r\n"),
+                 static_cast<char>(static_cast<bool>(autoAck & _BV(ENAA_P5)) + 48),
+                 static_cast<char>(static_cast<bool>(autoAck & _BV(ENAA_P4)) + 48),
+                 static_cast<char>(static_cast<bool>(autoAck & _BV(ENAA_P3)) + 48),
+                 static_cast<char>(static_cast<bool>(autoAck & _BV(ENAA_P2)) + 48),
+                 static_cast<char>(static_cast<bool>(autoAck & _BV(ENAA_P1)) + 48),
+                 static_cast<char>(static_cast<bool>(autoAck & _BV(ENAA_P0)) + 48));
+    }
+
+    config_reg = read_register(NRF_CONFIG);
+    printf_P(PSTR("Primary Mode\t\t= %cX\r\n"), config_reg & _BV(PRIM_RX) ? 'R' : 'T');
+    print_address_register(PSTR("TX address\t"), TX_ADDR);
+
+    uint8_t openPipes = read_register(EN_RXADDR);
+    for (uint8_t i = 0; i < 6; ++i) {
+        bool isOpen = openPipes & _BV(i);
+        printf_P(PSTR("pipe %u ("
+        PRIPSTR
+        ") bound"), i, (char *)(pgm_read_ptr(&rf24_feature_e_str_P[isOpen + 3])));
+        if (i < 2) {
+            print_address_register(PSTR(""), static_cast<uint8_t>(RX_ADDR_P0 + i));
+        }
+        else {
+            print_byte_register(PSTR(""), static_cast<uint8_t>(RX_ADDR_P0 + i));
+        }
+    }
+}
+#endif // !defined(MINIMAL)
+
+/****************************************************************************/
+#if defined (RF24_SPI_PTR) || defined (DOXYGEN_FORCED)
+// does not apply to RF24_LINUX
+
+bool RF24::begin(_SPI* spiBus)
+{
+    _spi = spiBus;
+    return _init_pins() && _init_radio();
+}
+
+/****************************************************************************/
+
+bool RF24::begin(_SPI* spiBus, uint16_t _cepin, uint16_t _cspin)
+{
+    ce_pin = _cepin;
+    csn_pin = _cspin;
+    return begin(spiBus);
+}
+
+#endif // defined (RF24_SPI_PTR) || defined (DOXYGEN_FORCED)
+
+/****************************************************************************/
+
+bool RF24::begin(uint16_t _cepin, uint16_t _cspin)
+{
+    ce_pin = _cepin;
+    csn_pin = _cspin;
+    return begin();
+}
+
+/****************************************************************************/
+
+bool RF24::begin(void)
+{
+    #if defined (RF24_LINUX)
+        #if defined (RF24_RPi)
+    switch(csn_pin) {                 // Ensure valid hardware CS pin
+        case 0: break;
+        case 1: break;
+        // Allow BCM2835 enums for RPi
+        case 8: csn_pin = 0; break;
+        case 7: csn_pin = 1; break;
+        case 18: csn_pin = 10; break; // to make it work on SPI1
+        case 17: csn_pin = 11; break;
+        case 16: csn_pin = 12; break;
+        default: csn_pin = 0; break;
+    }
+        #endif // RF24_RPi
+
+    _SPI.begin(csn_pin, spi_speed);
+
+    #elif defined (XMEGA_D3)
+    _spi->begin(csn_pin);
+
+    #elif defined (RF24_RP2)
+    _spi->begin(PICO_DEFAULT_SPI ? spi1 : spi0);
+
+    #else // using an Arduino platform || defined (LITTLEWIRE)
+
+        #if defined (RF24_SPI_PTR)
+    _spi->begin();
+        #else  // !defined(RF24_SPI_PTR)
+    _SPI.begin();
+        #endif // !defined(RF24_SPI_PTR)
+
+    #endif // !defined(XMEGA_D3) && !defined(RF24_LINUX)
+
+    return _init_pins() && _init_radio();
+}
+
+/****************************************************************************/
+
+bool RF24::_init_pins()
+{
+    if (!isValid()) {
+        // didn't specify the CSN & CE pins to c'tor nor begin()
+        return false;
+    }
+
+    #if defined (RF24_LINUX)
+
+        #if defined (MRAA)
+    GPIO();
+    gpio.begin(ce_pin, csn_pin);
+        #endif
+
+    pinMode(ce_pin, OUTPUT);
+    ce(LOW);
+    delay(100);
+
+    #elif defined (LITTLEWIRE)
+    pinMode(csn_pin, OUTPUT);
+    csn(HIGH);
+
+    #elif defined (XMEGA_D3)
+    if (ce_pin != csn_pin) {
+        pinMode(ce_pin, OUTPUT);
+    };
+    ce(LOW);
+    csn(HIGH);
+    delay(200);
+
+    #else // using an Arduino platform
+
+    // Initialize pins
+    if (ce_pin != csn_pin) {
+        pinMode(ce_pin, OUTPUT);
+        pinMode(csn_pin, OUTPUT);
+    }
+
+    ce(LOW);
+    csn(HIGH);
+
+        #if defined (__ARDUINO_X86__)
+    delay(100);
+        #endif
+    #endif // !defined(XMEGA_D3) && !defined(LITTLEWIRE) && !defined(RF24_LINUX)
+
+    return true; // assuming pins are connected properly
+}
+
+/****************************************************************************/
+
+bool RF24::_init_radio()
+{
+    // Must allow the radio time to settle else configuration bits will not necessarily stick.
+    // This is actually only required following power up but some settling time also appears to
+    // be required after resets too. For full coverage, we'll always assume the worst.
+    // Enabling 16b CRC is by far the most obvious case if the wrong timing is used - or skipped.
+    // Technically we require 4.5ms + 14us as a worst case. We'll just call it 5ms for good measure.
+    // WARNING: Delay is based on P-variant whereby non-P *may* require different timing.
+    delay(5);
+
+    // Set 1500uS (minimum for 32B payload in ESB@250KBPS) timeouts, to make testing a little easier
+    // WARNING: If this is ever lowered, either 250KBS mode with AA is broken or maximum packet
+    // sizes must never be used. See datasheet for a more complete explanation.
+    setRetries(5, 15);
+
+    // Then set the data rate to the slowest (and most reliable) speed supported by all
+    // hardware. Since this value occupies the same register as the PA level value, set
+    // the PA level to MAX
+    setRadiation(RF24_PA_MAX, RF24_1MBPS); // LNA enabled by default
+
+    // detect if is a plus variant & use old toggle features command accordingly
+    uint8_t before_toggle = read_register(FEATURE);
+    toggle_features();
+    uint8_t after_toggle = read_register(FEATURE);
+    _is_p_variant = before_toggle == after_toggle;
+    if (after_toggle){
+        if (_is_p_variant){
+            // module did not experience power-on-reset (#401)
+            toggle_features();
+        }
+        // allow use of multicast parameter and dynamic payloads by default
+        write_register(FEATURE, 0);
+    }
+    ack_payloads_enabled = false;     // ack payloads disabled by default
+    write_register(DYNPD, 0);         // disable dynamic payloads by default (for all pipes)
+    dynamic_payloads_enabled = false;
+    write_register(EN_AA, 0x3F);      // enable auto-ack on all pipes
+    write_register(EN_RXADDR, 3);     // only open RX pipes 0 & 1
+    setPayloadSize(32);               // set static payload size to 32 (max) bytes by default
+    setAddressWidth(5);               // set default address length to (max) 5 bytes
+
+    // Set up default configuration.  Callers can always change it later.
+    // This channel should be universally safe and not bleed over into adjacent
+    // spectrum.
+    setChannel(76);
+
+    // Reset current status
+    // Notice reset and flush is the last thing we do
+    write_register(NRF_STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT));
+
+
+    // Flush buffers
+    flush_rx();
+    flush_tx();
+
+    // Clear CONFIG register:
+    //      Reflect all IRQ events on IRQ pin
+    //      Enable PTX
+    //      Power Up
+    //      16-bit CRC (CRC required by auto-ack)
+    // Do not write CE high so radio will remain in standby I mode
+    // PTX should use only 22uA of power
+    write_register(NRF_CONFIG, (_BV(EN_CRC) | _BV(CRCO)) );
+    config_reg = read_register(NRF_CONFIG);
+
+    powerUp();
+
+    // if config is not set correctly then there was a bad response from module
+    return config_reg == (_BV(EN_CRC) | _BV(CRCO) | _BV(PWR_UP)) ? true : false;
+}
+
+/****************************************************************************/
+
+bool RF24::isChipConnected()
+{
+    return read_register(SETUP_AW) == (addr_width - static_cast<uint8_t>(2));
+}
+
+/****************************************************************************/
+
+bool RF24::isValid()
+{
+    return ce_pin != 0xFFFF && csn_pin != 0xFFFF;
+}
+
+/****************************************************************************/
+
+void RF24::startListening(void)
+{
+    #if !defined(RF24_TINY) && !defined(LITTLEWIRE)
+    powerUp();
+    #endif
+    config_reg |= _BV(PRIM_RX);
+    write_register(NRF_CONFIG, config_reg);
+    write_register(NRF_STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT));
+    ce(HIGH);
+
+    // Restore the pipe0 address, if exists
+    if (_is_p0_rx) {
+        write_register(RX_ADDR_P0, pipe0_reading_address, addr_width);
+    } else {
+        closeReadingPipe(0);
+    }
+}
+
+/****************************************************************************/
+static const PROGMEM uint8_t child_pipe_enable[] = {ERX_P0, ERX_P1, ERX_P2,
+                                                    ERX_P3, ERX_P4, ERX_P5};
+
+void RF24::stopListening(void)
+{
+    ce(LOW);
+
+    //delayMicroseconds(100);
+    delayMicroseconds(static_cast<int>(txDelay));
+    if (ack_payloads_enabled){
+        flush_tx();
+    }
+
+    config_reg = static_cast<uint8_t>(config_reg & ~_BV(PRIM_RX));
+    write_register(NRF_CONFIG, config_reg);
+
+    #if defined(RF24_TINY) || defined(LITTLEWIRE)
+    // for 3 pins solution TX mode is only left with additonal powerDown/powerUp cycle
+    if (ce_pin == csn_pin) {
+      powerDown();
+      powerUp();
+    }
+    #endif
+    write_register(EN_RXADDR, static_cast<uint8_t>(read_register(EN_RXADDR) | 
_BV(pgm_read_byte(&child_pipe_enable[0])))); // Enable RX on pipe0
+}
+
+/****************************************************************************/
+
+void RF24::powerDown(void)
+{
+    ce(LOW); // Guarantee CE is low on powerDown
+    config_reg = static_cast<uint8_t>(config_reg & ~_BV(PWR_UP));
+    write_register(NRF_CONFIG, config_reg);
+}
+
+/****************************************************************************/
+
+//Power up now. Radio will not power down unless instructed by MCU for config changes etc.
+void RF24::powerUp(void)
+{
+    // if not powered up then power up and wait for the radio to initialize
+    if (!(config_reg & _BV(PWR_UP))) {
+        config_reg |= _BV(PWR_UP);
+        write_register(NRF_CONFIG, config_reg);
+
+        // For nRF24L01+ to go from power down mode to TX or RX mode it must first pass through stand-by 
mode.
+        // There must be a delay of Tpd2stby (see Table 16.) after the nRF24L01+ leaves power down mode 
before
+        // the CEis set high. - Tpd2stby can be up to 5ms per the 1.0 datasheet
+        delayMicroseconds(RF24_POWERUP_DELAY);
+    }
+}
+
+/******************************************************************/
+#if defined (FAILURE_HANDLING) || defined (RF24_LINUX)
+
+void RF24::errNotify()
+{
+    #if defined (SERIAL_DEBUG) || defined (RF24_LINUX)
+    printf_P(PSTR("RF24 HARDWARE FAIL: Radio not responding, verify pin connections, wiring, etc.\r\n"));
+    #endif
+    #if defined (FAILURE_HANDLING)
+    failureDetected = 1;
+    #else
+    delay(5000);
+    #endif
+}
+
+#endif
+/******************************************************************/
+
+//Similar to the previous write, clears the interrupt flags
+bool RF24::write(const void* buf, uint8_t len, const bool multicast)
+{
+    //Start Writing
+    startFastWrite(buf, len, multicast);
+
+    //Wait until complete or failed
+    #if defined(FAILURE_HANDLING) || defined(RF24_LINUX)
+    uint32_t timer = millis();
+    #endif // defined(FAILURE_HANDLING) || defined(RF24_LINUX)
+
+    while (!(get_status() & (_BV(TX_DS) | _BV(MAX_RT)))) {
+        #if defined(FAILURE_HANDLING) || defined(RF24_LINUX)
+        if (millis() - timer > 95) {
+            errNotify();
+            #if defined(FAILURE_HANDLING)
+            return 0;
+            #else
+            delay(100);
+            #endif
+        }
+        #endif
+    }
+
+    ce(LOW);
+
+    write_register(NRF_STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT));
+
+    //Max retries exceeded
+    if (status & _BV(MAX_RT)) {
+        flush_tx(); // Only going to be 1 packet in the FIFO at a time using this method, so just flush
+        return 0;
+    }
+    //TX OK 1 or 0
+    return 1;
+}
+
+bool RF24::write(const void* buf, uint8_t len)
+{
+    return write(buf, len, 0);
+}
+/****************************************************************************/
+
+//For general use, the interrupt flags are not important to clear
+bool RF24::writeBlocking(const void* buf, uint8_t len, uint32_t timeout)
+{
+    //Block until the FIFO is NOT full.
+    //Keep track of the MAX retries and set auto-retry if seeing failures
+    //This way the FIFO will fill up and allow blocking until packets go through
+    //The radio will auto-clear everything in the FIFO as long as CE remains high
+
+    uint32_t timer = millis();                               // Get the time that the payload transmission 
started
+
+    while ((get_status() & (_BV(TX_FULL)))) {                // Blocking only if FIFO is full. This will 
loop and block until TX is successful or timeout
+
+        if (status & _BV(MAX_RT)) {                          // If MAX Retries have been reached
+            reUseTX();                                       // Set re-transmit and clear the MAX_RT 
interrupt flag
+            if (millis() - timer > timeout) {
+                return 0;                                    // If this payload has exceeded the 
user-defined timeout, exit and return 0
+            }
+        }
+        #if defined(FAILURE_HANDLING) || defined(RF24_LINUX)
+        if (millis() - timer > (timeout + 95)) {
+            errNotify();
+            #if defined(FAILURE_HANDLING)
+            return 0;
+            #endif
+        }
+        #endif
+
+    }
+
+    //Start Writing
+    startFastWrite(buf, len, 0);                             // Write the payload if a buffer is clear
+
+    return 1;                                                // Return 1 to indicate successful transmission
+}
+
+/****************************************************************************/
+
+void RF24::reUseTX()
+{
+    write_register(NRF_STATUS, _BV(MAX_RT));  //Clear max retry flag
+    write_register(REUSE_TX_PL, RF24_NOP, true);
+    ce(LOW);                                  //Re-Transfer packet
+    ce(HIGH);
+}
+
+/****************************************************************************/
+
+bool RF24::writeFast(const void* buf, uint8_t len, const bool multicast)
+{
+    //Block until the FIFO is NOT full.
+    //Keep track of the MAX retries and set auto-retry if seeing failures
+    //Return 0 so the user can control the retrys and set a timer or failure counter if required
+    //The radio will auto-clear everything in the FIFO as long as CE remains high
+
+    #if defined(FAILURE_HANDLING) || defined(RF24_LINUX)
+    uint32_t timer = millis();
+    #endif
+
+    //Blocking only if FIFO is full. This will loop and block until TX is successful or fail
+    while ((get_status() & (_BV(TX_FULL)))) {
+        if (status & _BV(MAX_RT)) {
+            return 0;                                        //Return 0. The previous payload has not been 
retransmitted
+            // From the user perspective, if you get a 0, just keep trying to send the same payload
+        }
+        #if defined(FAILURE_HANDLING) || defined(RF24_LINUX)
+        if (millis() - timer > 95) {
+            errNotify();
+            #if defined(FAILURE_HANDLING)
+            return 0;
+            #endif // defined(FAILURE_HANDLING)
+        }
+        #endif
+    }
+    startFastWrite(buf, len, multicast);                     // Start Writing
+
+    return 1;
+}
+
+bool RF24::writeFast(const void* buf, uint8_t len)
+{
+    return writeFast(buf, len, 0);
+}
+
+/****************************************************************************/
+
+//Per the documentation, we want to set PTX Mode when not listening. Then all we do is write data and set CE 
high
+//In this mode, if we can keep the FIFO buffers loaded, packets will transmit immediately (no 130us delay)
+//Otherwise we enter Standby-II mode, which is still faster than standby mode
+//Also, we remove the need to keep writing the config register over and over and delaying for 150 us each 
time if sending a stream of data
+
+void RF24::startFastWrite(const void* buf, uint8_t len, const bool multicast, bool startTx)
+{ //TMRh20
+
+    write_payload(buf, len, multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD);
+    if (startTx) {
+        ce(HIGH);
+    }
+}
+
+/****************************************************************************/
+
+//Added the original startWrite back in so users can still use interrupts, ack payloads, etc
+//Allows the library to pass all tests
+bool RF24::startWrite(const void* buf, uint8_t len, const bool multicast)
+{
+
+    // Send the payload
+    write_payload(buf, len, multicast ? W_TX_PAYLOAD_NO_ACK : W_TX_PAYLOAD);
+    ce(HIGH);
+    #if !defined(F_CPU) || F_CPU > 20000000
+    delayMicroseconds(10);
+    #endif
+    ce(LOW);
+    return !(status & _BV(TX_FULL));
+}
+
+/****************************************************************************/
+
+bool RF24::rxFifoFull()
+{
+    return read_register(FIFO_STATUS) & _BV(RX_FULL);
+}
+
+/****************************************************************************/
+
+bool RF24::txStandBy()
+{
+
+    #if defined(FAILURE_HANDLING) || defined(RF24_LINUX)
+    uint32_t timeout = millis();
+    #endif
+    while (!(read_register(FIFO_STATUS) & _BV(TX_EMPTY))) {
+        if (status & _BV(MAX_RT)) {
+            write_register(NRF_STATUS, _BV(MAX_RT));
+            ce(LOW);
+            flush_tx();    //Non blocking, flush the data
+            return 0;
+        }
+        #if defined(FAILURE_HANDLING) || defined(RF24_LINUX)
+        if (millis() - timeout > 95) {
+            errNotify();
+            #if defined(FAILURE_HANDLING)
+            return 0;
+            #endif
+        }
+        #endif
+    }
+
+    ce(LOW);               //Set STANDBY-I mode
+    return 1;
+}
+
+/****************************************************************************/
+
+bool RF24::txStandBy(uint32_t timeout, bool startTx)
+{
+
+    if (startTx) {
+        stopListening();
+        ce(HIGH);
+    }
+    uint32_t start = millis();
+
+    while (!(read_register(FIFO_STATUS) & _BV(TX_EMPTY))) {
+        if (status & _BV(MAX_RT)) {
+            write_register(NRF_STATUS, _BV(MAX_RT));
+            ce(LOW); // Set re-transmit
+            ce(HIGH);
+            if (millis() - start >= timeout) {
+                ce(LOW);
+                flush_tx();
+                return 0;
+            }
+        }
+        #if defined(FAILURE_HANDLING) || defined(RF24_LINUX)
+        if (millis() - start > (timeout + 95)) {
+            errNotify();
+            #if defined(FAILURE_HANDLING)
+            return 0;
+            #endif
+        }
+        #endif
+    }
+
+    ce(LOW);  //Set STANDBY-I mode
+    return 1;
+
+}
+
+/****************************************************************************/
+
+void RF24::maskIRQ(bool tx, bool fail, bool rx)
+{
+    /* clear the interrupt flags */
+    config_reg = static_cast<uint8_t>(config_reg & ~(1 << MASK_MAX_RT | 1 << MASK_TX_DS | 1 << MASK_RX_DR));
+    /* set the specified interrupt flags */
+    config_reg = static_cast<uint8_t>(config_reg | fail << MASK_MAX_RT | tx << MASK_TX_DS | rx << 
MASK_RX_DR);
+    write_register(NRF_CONFIG, config_reg);
+}
+
+/****************************************************************************/
+
+uint8_t RF24::getDynamicPayloadSize(void)
+{
+    uint8_t result = read_register(R_RX_PL_WID);
+
+    if (result > 32) {
+        flush_rx();
+        delay(2);
+        return 0;
+    }
+    return result;
+}
+
+/****************************************************************************/
+
+bool RF24::available(void)
+{
+    return available(NULL);
+}
+
+/****************************************************************************/
+
+bool RF24::available(uint8_t* pipe_num)
+{
+    // get implied RX FIFO empty flag from status byte
+    uint8_t pipe = (get_status() >> RX_P_NO) & 0x07;
+    if (pipe > 5)
+        return 0;
+
+    // If the caller wants the pipe number, include that
+    if (pipe_num)
+        *pipe_num = pipe;
+
+    return 1;
+}
+
+/****************************************************************************/
+
+void RF24::read(void* buf, uint8_t len)
+{
+
+    // Fetch the payload
+    read_payload(buf, len);
+
+    //Clear the only applicable interrupt flags
+    write_register(NRF_STATUS, _BV(RX_DR));
+
+}
+
+/****************************************************************************/
+
+void RF24::whatHappened(bool& tx_ok, bool& tx_fail, bool& rx_ready)
+{
+    // Read the status & reset the status in one easy call
+    // Or is that such a good idea?
+    write_register(NRF_STATUS, _BV(RX_DR) | _BV(TX_DS) | _BV(MAX_RT));
+
+    // Report to the user what happened
+    tx_ok = status & _BV(TX_DS);
+    tx_fail = status & _BV(MAX_RT);
+    rx_ready = status & _BV(RX_DR);
+}
+
+/****************************************************************************/
+
+void RF24::openWritingPipe(uint64_t value)
+{
+    // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+)
+    // expects it LSB first too, so we're good.
+
+    write_register(RX_ADDR_P0, reinterpret_cast<uint8_t*>(&value), addr_width);
+    write_register(TX_ADDR, reinterpret_cast<uint8_t*>(&value), addr_width);
+}
+
+/****************************************************************************/
+void RF24::openWritingPipe(const uint8_t* address)
+{
+    // Note that AVR 8-bit uC's store this LSB first, and the NRF24L01(+)
+    // expects it LSB first too, so we're good.
+    write_register(RX_ADDR_P0, address, addr_width);
+    write_register(TX_ADDR, address, addr_width);
+}
+
+/****************************************************************************/
+static const PROGMEM uint8_t child_pipe[] = {RX_ADDR_P0, RX_ADDR_P1, RX_ADDR_P2,
+                                             RX_ADDR_P3, RX_ADDR_P4, RX_ADDR_P5};
+
+void RF24::openReadingPipe(uint8_t child, uint64_t address)
+{
+    // If this is pipe 0, cache the address.  This is needed because
+    // openWritingPipe() will overwrite the pipe 0 address, so
+    // startListening() will have to restore it.
+    if (child == 0) {
+        memcpy(pipe0_reading_address, &address, addr_width);
+        _is_p0_rx = true;
+    }
+
+    if (child <= 5) {
+        // For pipes 2-5, only write the LSB
+        if (child < 2) {
+            write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast<const uint8_t*>(&address), 
addr_width);
+        } else {
+            write_register(pgm_read_byte(&child_pipe[child]), reinterpret_cast<const uint8_t*>(&address), 1);
+        }
+
+        // Note it would be more efficient to set all of the bits for all open
+        // pipes at once.  However, I thought it would make the calling code
+        // more simple to do it this way.
+        write_register(EN_RXADDR, static_cast<uint8_t>(read_register(EN_RXADDR) | 
_BV(pgm_read_byte(&child_pipe_enable[child]))));
+    }
+}
+
+/****************************************************************************/
+
+void RF24::setAddressWidth(uint8_t a_width)
+{
+    a_width = static_cast<uint8_t>(a_width - 2);
+    if (a_width) {
+        write_register(SETUP_AW, static_cast<uint8_t>(a_width % 4));
+        addr_width = static_cast<uint8_t>((a_width % 4) + 2);
+    } else {
+        write_register(SETUP_AW, static_cast<uint8_t>(0));
+        addr_width = static_cast<uint8_t>(2);
+    }
+
+}
+
+/****************************************************************************/
+
+void RF24::openReadingPipe(uint8_t child, const uint8_t* address)
+{
+    // If this is pipe 0, cache the address.  This is needed because
+    // openWritingPipe() will overwrite the pipe 0 address, so
+    // startListening() will have to restore it.
+    if (child == 0) {
+        memcpy(pipe0_reading_address, address, addr_width);
+        _is_p0_rx = true;
+    }
+    if (child <= 5) {
+        // For pipes 2-5, only write the LSB
+        if (child < 2) {
+            write_register(pgm_read_byte(&child_pipe[child]), address, addr_width);
+        } else {
+            write_register(pgm_read_byte(&child_pipe[child]), address, 1);
+        }
+
+        // Note it would be more efficient to set all of the bits for all open
+        // pipes at once.  However, I thought it would make the calling code
+        // more simple to do it this way.
+        write_register(EN_RXADDR, static_cast<uint8_t>(read_register(EN_RXADDR) | 
_BV(pgm_read_byte(&child_pipe_enable[child]))));
+
+    }
+}
+
+/****************************************************************************/
+
+void RF24::closeReadingPipe(uint8_t pipe)
+{
+    write_register(EN_RXADDR, static_cast<uint8_t>(read_register(EN_RXADDR) & 
~_BV(pgm_read_byte(&child_pipe_enable[pipe]))));
+    if (!pipe) {
+        // keep track of pipe 0's RX state to avoid null vs 0 in addr cache
+        _is_p0_rx = false;
+    }
+}
+
+/****************************************************************************/
+
+void RF24::toggle_features(void)
+{
+    beginTransaction();
+    #if defined (RF24_SPI_PTR)
+    status = _spi->transfer(ACTIVATE);
+    _spi->transfer(0x73);
+    #else
+    status = _SPI.transfer(ACTIVATE);
+    _SPI.transfer(0x73);
+    #endif
+    endTransaction();
+}
+
+/****************************************************************************/
+
+void RF24::enableDynamicPayloads(void)
+{
+    // Enable dynamic payload throughout the system
+
+    //toggle_features();
+    write_register(FEATURE, read_register(FEATURE) | _BV(EN_DPL));
+
+    IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n", read_register(FEATURE)));
+
+    // Enable dynamic payload on all pipes
+    //
+    // Not sure the use case of only having dynamic payload on certain
+    // pipes, so the library does not support it.
+    write_register(DYNPD, read_register(DYNPD) | _BV(DPL_P5) | _BV(DPL_P4) | _BV(DPL_P3) | _BV(DPL_P2) | 
_BV(DPL_P1) | _BV(DPL_P0));
+
+    dynamic_payloads_enabled = true;
+}
+
+/****************************************************************************/
+void RF24::disableDynamicPayloads(void)
+{
+    // Disables dynamic payload throughout the system.  Also disables Ack Payloads
+
+    //toggle_features();
+    write_register(FEATURE, 0);
+
+    IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n", read_register(FEATURE)));
+
+    // Disable dynamic payload on all pipes
+    //
+    // Not sure the use case of only having dynamic payload on certain
+    // pipes, so the library does not support it.
+    write_register(DYNPD, 0);
+
+    dynamic_payloads_enabled = false;
+    ack_payloads_enabled = false;
+}
+
+/****************************************************************************/
+
+void RF24::enableAckPayload(void)
+{
+    // enable ack payloads and dynamic payload features
+
+    if (!ack_payloads_enabled){
+        write_register(FEATURE, read_register(FEATURE) | _BV(EN_ACK_PAY) | _BV(EN_DPL));
+
+        IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n", read_register(FEATURE)));
+
+        // Enable dynamic payload on pipes 0 & 1
+        write_register(DYNPD, read_register(DYNPD) | _BV(DPL_P1) | _BV(DPL_P0));
+        dynamic_payloads_enabled = true;
+        ack_payloads_enabled = true;
+    }
+}
+
+/****************************************************************************/
+
+void RF24::disableAckPayload(void)
+{
+    // disable ack payloads (leave dynamic payload features as is)
+    if (ack_payloads_enabled){
+        write_register(FEATURE, static_cast<uint8_t>(read_register(FEATURE) | ~_BV(EN_ACK_PAY)));
+
+        IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n", read_register(FEATURE)));
+
+        ack_payloads_enabled = false;
+    }
+}
+
+/****************************************************************************/
+
+void RF24::enableDynamicAck(void)
+{
+    //
+    // enable dynamic ack features
+    //
+    //toggle_features();
+    write_register(FEATURE, read_register(FEATURE) | _BV(EN_DYN_ACK));
+
+    IF_SERIAL_DEBUG(printf("FEATURE=%i\r\n", read_register(FEATURE)));
+}
+
+/****************************************************************************/
+
+bool RF24::writeAckPayload(uint8_t pipe, const void* buf, uint8_t len)
+{
+    if (ack_payloads_enabled){
+        const uint8_t* current = reinterpret_cast<const uint8_t*>(buf);
+
+        write_payload(current, len, W_ACK_PAYLOAD | (pipe & 0x07));
+        return !(status & _BV(TX_FULL));
+    }
+    return 0;
+}
+
+/****************************************************************************/
+
+bool RF24::isAckPayloadAvailable(void)
+{
+    return available(NULL);
+}
+
+/****************************************************************************/
+
+bool RF24::isPVariant(void)
+{
+    return _is_p_variant;
+}
+
+/****************************************************************************/
+
+void RF24::setAutoAck(bool enable)
+{
+    if (enable){
+        write_register(EN_AA, 0x3F);
+    }else{
+        write_register(EN_AA, 0);
+        // accomodate ACK payloads feature
+        if (ack_payloads_enabled){
+            disableAckPayload();
+        }
+    }
+}
+
+/****************************************************************************/
+
+void RF24::setAutoAck(uint8_t pipe, bool enable)
+{
+    if (pipe < 6) {
+        uint8_t en_aa = read_register(EN_AA);
+        if (enable) {
+            en_aa |= static_cast<uint8_t>(_BV(pipe));
+        }else{
+            en_aa = static_cast<uint8_t>(en_aa & ~_BV(pipe));
+            if (ack_payloads_enabled && !pipe){
+                disableAckPayload();
+            }
+        }
+        write_register(EN_AA, en_aa);
+    }
+}
+
+/****************************************************************************/
+
+bool RF24::testCarrier(void)
+{
+    return (read_register(CD) & 1);
+}
+
+/****************************************************************************/
+
+bool RF24::testRPD(void)
+{
+    return (read_register(RPD) & 1);
+}
+
+/****************************************************************************/
+
+void RF24::setPALevel(uint8_t level, bool lnaEnable)
+{
+    uint8_t setup = read_register(RF_SETUP) & static_cast<uint8_t>(0xF8);
+    setup |= _pa_level_reg_value(level, lnaEnable);
+    write_register(RF_SETUP, setup);
+}
+
+/****************************************************************************/
+
+uint8_t RF24::getPALevel(void)
+{
+
+    return (read_register(RF_SETUP) & (_BV(RF_PWR_LOW) | _BV(RF_PWR_HIGH))) >> 1;
+}
+
+/****************************************************************************/
+
+uint8_t RF24::getARC(void)
+{
+
+    return read_register(OBSERVE_TX) & 0x0F;
+}
+
+/****************************************************************************/
+
+bool RF24::setDataRate(rf24_datarate_e speed)
+{
+    bool result = false;
+    uint8_t setup = read_register(RF_SETUP);
+
+    // HIGH and LOW '00' is 1Mbs - our default
+    setup = static_cast<uint8_t>(setup & ~(_BV(RF_DR_LOW) | _BV(RF_DR_HIGH)));
+    setup |= _data_rate_reg_value(speed);
+
+    write_register(RF_SETUP, setup);
+
+    // Verify our result
+    if (read_register(RF_SETUP) == setup) {
+        result = true;
+    }
+    return result;
+}
+
+/****************************************************************************/
+
+rf24_datarate_e RF24::getDataRate(void)
+{
+    rf24_datarate_e result;
+    uint8_t dr = read_register(RF_SETUP) & (_BV(RF_DR_LOW) | _BV(RF_DR_HIGH));
+
+    // switch uses RAM (evil!)
+    // Order matters in our case below
+    if (dr == _BV(RF_DR_LOW)) {
+        // '10' = 250KBPS
+        result = RF24_250KBPS;
+    } else if (dr == _BV(RF_DR_HIGH)) {
+        // '01' = 2MBPS
+        result = RF24_2MBPS;
+    } else {
+        // '00' = 1MBPS
+        result = RF24_1MBPS;
+    }
+    return result;
+}
+
+/****************************************************************************/
+
+void RF24::setCRCLength(rf24_crclength_e length)
+{
+    config_reg = static_cast<uint8_t>(config_reg & ~(_BV(CRCO) | _BV(EN_CRC)));
+
+    // switch uses RAM (evil!)
+    if (length == RF24_CRC_DISABLED) {
+        // Do nothing, we turned it off above.
+    } else if (length == RF24_CRC_8) {
+        config_reg |= _BV(EN_CRC);
+    } else {
+        config_reg |= _BV(EN_CRC);
+        config_reg |= _BV(CRCO);
+    }
+    write_register(NRF_CONFIG, config_reg);
+}
+
+/****************************************************************************/
+
+rf24_crclength_e RF24::getCRCLength(void)
+{
+    rf24_crclength_e result = RF24_CRC_DISABLED;
+    uint8_t AA = read_register(EN_AA);
+    config_reg = read_register(NRF_CONFIG);
+
+    if (config_reg & _BV(EN_CRC) || AA) {
+        if (config_reg & _BV(CRCO)) {
+            result = RF24_CRC_16;
+        } else {
+            result = RF24_CRC_8;
+        }
+    }
+
+    return result;
+}
+
+/****************************************************************************/
+
+void RF24::disableCRC(void)
+{
+    config_reg = static_cast<uint8_t>(config_reg & ~_BV(EN_CRC));
+    write_register(NRF_CONFIG, config_reg);
+}
+
+/****************************************************************************/
+void RF24::setRetries(uint8_t delay, uint8_t count)
+{
+    write_register(SETUP_RETR, static_cast<uint8_t>(rf24_min(15, delay) << ARD | rf24_min(15, count)));
+}
+
+/****************************************************************************/
+void RF24::startConstCarrier(rf24_pa_dbm_e level, uint8_t channel)
+{
+    stopListening();
+    write_register(RF_SETUP, read_register(RF_SETUP) | _BV(CONT_WAVE) | _BV(PLL_LOCK));
+    if (isPVariant()){
+        setAutoAck(0);
+        setRetries(0, 0);
+        uint8_t dummy_buf[32];
+        for (uint8_t i = 0; i < 32; ++i)
+            dummy_buf[i] = 0xFF;
+
+        // use write_register() instead of openWritingPipe() to bypass
+        // truncation of the address with the current RF24::addr_width value
+        write_register(TX_ADDR, reinterpret_cast<uint8_t*>(&dummy_buf), 5);
+        flush_tx();  // so we can write to top level
+
+        // use write_register() instead of write_payload() to bypass
+        // truncation of the payload with the current RF24::payload_size value
+        write_register(W_TX_PAYLOAD, reinterpret_cast<const uint8_t*>(&dummy_buf), 32);
+
+        disableCRC();
+    }
+    setPALevel(level);
+    setChannel(channel);
+    IF_SERIAL_DEBUG(printf_P(PSTR("RF_SETUP=%02x\r\n"), read_register(RF_SETUP)));
+    ce(HIGH);
+    if (isPVariant()){
+        delay(1); // datasheet says 1 ms is ok in this instance
+        ce(LOW);
+        reUseTX();
+    }
+}
+
+/****************************************************************************/
+
+void RF24::stopConstCarrier()
+{
+    /*
+     * A note from the datasheet:
+     * Do not use REUSE_TX_PL together with CONT_WAVE=1. When both these
+     * registers are set the chip does not react when setting CE low. If
+     * however, both registers are set PWR_UP = 0 will turn TX mode off.
+     */
+    powerDown();  // per datasheet recommendation (just to be safe)
+    write_register(RF_SETUP, static_cast<uint8_t>(read_register(RF_SETUP) & ~_BV(CONT_WAVE) & 
~_BV(PLL_LOCK)));
+    ce(LOW);
+}
+
+/****************************************************************************/
+
+void RF24::toggleAllPipes(bool isEnabled)
+{
+    write_register(EN_RXADDR, static_cast<uint8_t>(isEnabled ? 0x3F : 0));
+}
+
+/****************************************************************************/
+
+uint8_t RF24::_data_rate_reg_value(rf24_datarate_e speed)
+{
+    #if !defined(F_CPU) || F_CPU > 20000000
+    txDelay = 280;
+    #else //16Mhz Arduino
+    txDelay=85;
+    #endif
+    if (speed == RF24_250KBPS) {
+        #if !defined(F_CPU) || F_CPU > 20000000
+        txDelay = 505;
+        #else //16Mhz Arduino
+        txDelay = 155;
+        #endif
+        // Must set the RF_DR_LOW to 1; RF_DR_HIGH (used to be RF_DR) is already 0
+        // Making it '10'.
+        return static_cast<uint8_t>(_BV(RF_DR_LOW));
+    }
+    else if (speed == RF24_2MBPS) {
+        #if !defined(F_CPU) || F_CPU > 20000000
+        txDelay = 240;
+        #else // 16Mhz Arduino
+        txDelay = 65;
+        #endif
+        // Set 2Mbs, RF_DR (RF_DR_HIGH) is set 1
+        // Making it '01'
+        return static_cast<uint8_t>(_BV(RF_DR_HIGH));
+    }
+    // HIGH and LOW '00' is 1Mbs - our default
+    return static_cast<uint8_t>(0);
+
+}
+
+/****************************************************************************/
+
+uint8_t RF24::_pa_level_reg_value(uint8_t level, bool lnaEnable)
+{
+    // If invalid level, go to max PA
+    // Else set level as requested
+    // + lnaEnable (1 or 0) to support the SI24R1 chip extra bit
+    return static_cast<uint8_t>(((level > RF24_PA_MAX ? static_cast<uint8_t>(RF24_PA_MAX) : level) << 1) + 
lnaEnable);
+}
+
+/****************************************************************************/
+
+void RF24::setRadiation(uint8_t level, rf24_datarate_e speed, bool lnaEnable)
+{
+    uint8_t setup = _data_rate_reg_value(speed);
+    setup |= _pa_level_reg_value(level, lnaEnable);
+    write_register(RF_SETUP, setup);
+}
diff --git a/arduino/libraries/RF24/RF24.h b/arduino/libraries/RF24/RF24.h
new file mode 100644
index 000000000..59472461b
--- /dev/null
+++ b/arduino/libraries/RF24/RF24.h
@@ -0,0 +1,2258 @@
+/*
+ Copyright (C) 2011 J. Coliz <maniacbug ymail com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+ */
+
+/**
+ * @file RF24.h
+ *
+ * Class declaration for RF24 and helper enums
+ */
+
+#ifndef __RF24_H__
+#define __RF24_H__
+
+#include "RF24_config.h"
+
+#if defined (RF24_LINUX) || defined (LITTLEWIRE)
+    #include "utility/includes.h"
+#elif defined SOFTSPI
+    #include <DigitalIO.h>
+#endif
+
+
+/**
+ * @defgroup PALevel Power Amplifier level
+ * Power Amplifier level. The units dBm (decibel-milliwatts or dB<sub>mW</sub>)
+ * represents a logarithmic signal loss.
+ * @see
+ * - RF24::setPALevel()
+ * - RF24::getPALevel()
+ * @{
+ */
+typedef enum {
+    /**
+     * (0) represents:
+     * nRF24L01 | Si24R1 with<br>lnaEnabled = 1 | Si24R1 with<br>lnaEnabled = 0
+     * :-------:|:-----------------------------:|:----------------------------:
+     *  -18 dBm | -6 dBm | -12 dBm
+     */
+    RF24_PA_MIN = 0,
+    /**
+     * (1) represents:
+     * nRF24L01 | Si24R1 with<br>lnaEnabled = 1 | Si24R1 with<br>lnaEnabled = 0
+     * :-------:|:-----------------------------:|:----------------------------:
+     *  -12 dBm | 0 dBm | -4 dBm
+     */
+    RF24_PA_LOW,
+    /**
+     * (2) represents:
+     * nRF24L01 | Si24R1 with<br>lnaEnabled = 1 | Si24R1 with<br>lnaEnabled = 0
+     * :-------:|:-----------------------------:|:----------------------------:
+     *  -6 dBm | 3 dBm | 1 dBm
+     */
+    RF24_PA_HIGH,
+    /**
+     * (3) represents:
+     * nRF24L01 | Si24R1 with<br>lnaEnabled = 1 | Si24R1 with<br>lnaEnabled = 0
+     * :-------:|:-----------------------------:|:----------------------------:
+     *  0 dBm | 7 dBm | 4 dBm
+     */
+    RF24_PA_MAX,
+    /**
+     * (4) This should not be used and remains for backward compatibility.
+     */
+    RF24_PA_ERROR
+} rf24_pa_dbm_e;
+
+/**
+ * @}
+ * @defgroup Datarate datarate
+ * How fast data moves through the air. Units are in bits per second (bps).
+ * @see
+ * - RF24::setDataRate()
+ * - RF24::getDataRate()
+ * @{
+ */
+typedef enum {
+    /** (0) represents 1 Mbps */
+    RF24_1MBPS = 0,
+    /** (1) represents 2 Mbps */
+    RF24_2MBPS,
+    /** (2) represents 250 kbps */
+    RF24_250KBPS
+} rf24_datarate_e;
+
+/**
+ * @}
+ * @defgroup CRCLength CRC length
+ * The length of a CRC checksum that is used (if any).<br>Cyclical Redundancy
+ * Checking (CRC) is commonly used to ensure data integrity.
+ * @see
+ * - RF24::setCRCLength()
+ * - RF24::getCRCLength()
+ * - RF24::disableCRC()
+ * @{
+ */
+typedef enum {
+    /** (0) represents no CRC checksum is used */
+    RF24_CRC_DISABLED = 0,
+    /** (1) represents CRC 8 bit checksum is used */
+    RF24_CRC_8,
+    /** (2) represents CRC 16 bit checksum is used */
+    RF24_CRC_16
+} rf24_crclength_e;
+
+/**
+ * @}
+ * @brief Driver class for nRF24L01(+) 2.4GHz Wireless Transceiver
+ */
+
+class RF24 {
+private:
+    #ifdef SOFTSPI
+    SoftSPI<SOFT_SPI_MISO_PIN, SOFT_SPI_MOSI_PIN, SOFT_SPI_SCK_PIN, SPI_MODE> spi;
+    #elif defined (SPI_UART)
+    SPIUARTClass uspi;
+    #endif
+
+    #if defined (RF24_LINUX) || defined (XMEGA_D3) || defined (RF24_RP2) /* XMEGA can use SPI class */
+    SPI spi;
+    #endif // defined (RF24_LINUX) || defined (XMEGA_D3)
+    #if defined (RF24_SPI_PTR)
+    _SPI* _spi;
+    #endif // defined (RF24_SPI_PTR)
+    #if defined (MRAA)
+    GPIO gpio;
+    #endif
+
+    uint16_t ce_pin; /** "Chip Enable" pin, activates the RX or TX role */
+    uint16_t csn_pin; /** SPI Chip select */
+    uint32_t spi_speed; /** SPI Bus Speed */
+    #if defined (RF24_LINUX) || defined (XMEGA_D3) || defined (RF24_RP2)
+    uint8_t spi_rxbuff[32+1] ; //SPI receive buffer (payload max 32 bytes)
+    uint8_t spi_txbuff[32+1] ; //SPI transmit buffer (payload max 32 bytes + 1 byte for the command)
+    #endif
+    uint8_t status; /** The status byte returned from every SPI transaction */
+    uint8_t payload_size; /** Fixed size of payloads */
+    uint8_t pipe0_reading_address[5]; /** Last address set on pipe 0 for reading. */
+    uint8_t config_reg; /** For storing the value of the NRF_CONFIG register */
+    bool _is_p_variant; /** For storing the result of testing the toggleFeatures() affect */
+    bool _is_p0_rx; /** For keeping track of pipe 0's usage in user-triggered RX mode. */
+
+protected:
+    /**
+     * SPI transactions
+     *
+     * Common code for SPI transactions including CSN toggle
+     *
+     */
+    inline void beginTransaction();
+
+    inline void endTransaction();
+
+    bool ack_payloads_enabled; /** Whether ack payloads are enabled. */
+    uint8_t addr_width; /** The address width to use (3, 4 or 5 bytes). */
+    bool dynamic_payloads_enabled; /** Whether dynamic payloads are enabled. */
+
+    /**
+     * Read a chunk of data in from a register
+     *
+     * @param reg Which register. Use constants from nRF24L01.h
+     * @param buf Where to put the data
+     * @param len How many bytes of data to transfer
+     * @return Nothing. Older versions of this function returned the status
+     * byte, but that it now saved to a private member on all SPI transactions.
+     */
+    void read_register(uint8_t reg, uint8_t* buf, uint8_t len);
+
+    /**
+     * Read single byte from a register
+     *
+     * @param reg Which register. Use constants from nRF24L01.h
+     * @return Current value of register @p reg
+     */
+    uint8_t read_register(uint8_t reg);
+
+public:
+
+    /**
+     * @name Primary public interface
+     *
+     *  These are the main methods you need to operate the chip
+     */
+    /**@{*/
+
+    /**
+     * RF24 Constructor
+     *
+     * Creates a new instance of this driver.  Before using, you create an instance
+     * and send in the unique pins that this chip is connected to.
+     *
+     * See [Related Pages](pages.html) for device specific information <br>
+     *
+     * @param _cepin The pin attached to Chip Enable on the RF module
+     * @param _cspin The pin attached to Chip Select (often labeled CSN) on the radio module.
+     * <br><br>For the Arduino Due board, the [Arduino Due extended SPI 
feature](https://www.arduino.cc/en/Reference/DueExtendedSPI)
+     * is not supported. This means that the Due's pins 4, 10, or 52 are not mandated options (can use any 
digital output pin) for the radio's CSN pin.
+     * @param _spi_speed The SPI speed in Hz ie: 1000000 == 1Mhz <br><br>Users can specify default SPI speed 
by modifying
+     * `#define RF24_SPI_SPEED` in RF24_config.h
+     * - For Arduino, the default SPI speed will only be properly configured this way on devices supporting 
SPI TRANSACTIONS
+     * - Older/Unsupported Arduino devices will use a default clock divider & settings configuration
+     * - For Linux: The old way of setting SPI speeds using BCM2835 driver enums has been removed as of 
v1.3.7
+     */
+    RF24(uint16_t _cepin, uint16_t _cspin, uint32_t _spi_speed = RF24_SPI_SPEED);
+
+    /**
+     * A constructor for initializing the radio's hardware dynamically
+     * @warning You MUST use begin(uint16_t, uint16_t) or begin(_SPI*, uint16_t, uint16_t) to pass both the 
digital output pin
+     * numbers connected to the radio's CE and CSN pins.
+     * @param _spi_speed The SPI speed in Hz ie: 1000000 == 1Mhz <br><br>Users can specify default SPI speed 
by modifying
+     * `#define RF24_SPI_SPEED` in RF24_config.h
+     * - For Arduino, the default SPI speed will only be properly configured this way on devices supporting 
SPI TRANSACTIONS
+     * - Older/Unsupported Arduino devices will use a default clock divider & settings configuration
+     * - For Linux: The old way of setting SPI speeds using BCM2835 driver enums has been removed as of 
v1.3.7
+     */
+    RF24(uint32_t _spi_speed = RF24_SPI_SPEED);
+
+    #if defined (RF24_LINUX)
+    virtual ~RF24() {};
+    #endif
+
+    /**
+     * Begin operation of the chip
+     *
+     * Call this in setup(), before calling any other methods.
+     * @code
+     * if (!radio.begin()) {
+     *   Serial.println(F("radio hardware not responding!"));
+     *   while (1) {} // hold program in infinite loop to prevent subsequent errors
+     * }
+     * @endcode
+     * @return
+     * - `true` if the radio was successfully initialized
+     * - `false` if the MCU failed to communicate with the radio hardware
+     */
+    bool begin(void);
+
+    #if defined (RF24_SPI_PTR) || defined (DOXYGEN_FORCED)
+    /**
+     * Same as begin(), but allows specifying a non-default SPI bus to use.
+     * @note This function assumes the `SPI::begin()` method was called before to
+     * calling this function.
+     *
+     * @warning This function is for the Arduino platform only
+     *
+     * @param spiBus A pointer or reference to an instantiated SPI bus object.
+     *
+     * @note The _SPI datatype is a "wrapped" definition that will represent
+     * various SPI implementations based on the specified platform.
+     * @see Review the [Arduino support page](md_docs_arduino.html).
+     *
+     * @return same result as begin()
+     */
+    bool begin(_SPI* spiBus);
+
+    /**
+     * Same as begin(), but allows dynamically specifying a SPI bus, CE pin,
+     * and CSN pin to use.
+     * @note This function assumes the `SPI::begin()` method was called before to
+     * calling this function.
+     *
+     * @warning This function is for the Arduino platform only
+     *
+     * @param spiBus A pointer or reference to an instantiated SPI bus object.
+     * @param _cepin The pin attached to Chip Enable on the RF module
+     * @param _cspin The pin attached to Chip Select (often labeled CSN) on the radio module.
+     * <br><br>For the Arduino Due board, the [Arduino Due extended SPI 
feature](https://www.arduino.cc/en/Reference/DueExtendedSPI)
+     * is not supported. This means that the Due's pins 4, 10, or 52 are not mandated options (can use any 
digital output pin) for the radio's CSN pin.
+     *
+     * @note The _SPI datatype is a "wrapped" definition that will represent
+     * various SPI implementations based on the specified platform.
+     * @see Review the [Arduino support page](md_docs_arduino.html).
+     *
+     * @return same result as begin()
+     */
+    bool begin(_SPI* spiBus, uint16_t _cepin, uint16_t _cspin);
+    #endif // defined (RF24_SPI_PTR) || defined (DOXYGEN_FORCED)
+
+    /**
+     * Same as begin(), but allows dynamically specifying a CE pin
+     * and CSN pin to use.
+     * @param _cepin The pin attached to Chip Enable on the RF module
+     * @param _cspin The pin attached to Chip Select (often labeled CSN) on the radio module.
+     * <br><br>For the Arduino Due board, the [Arduino Due extended SPI 
feature](https://www.arduino.cc/en/Reference/DueExtendedSPI)
+     * is not supported. This means that the Due's pins 4, 10, or 52 are not mandated options (can use any 
digital output pin) for the radio's CSN pin.
+     * @return same result as begin()
+     */
+    bool begin(uint16_t _cepin, uint16_t _cspin);
+
+    /**
+     * Checks if the chip is connected to the SPI bus
+     */
+    bool isChipConnected();
+
+    /**
+     * Start listening on the pipes opened for reading.
+     *
+     * 1. Be sure to call openReadingPipe() first.
+     * 2. Do not call write() while in this mode, without first calling stopListening().
+     * 3. Call available() to check for incoming traffic, and read() to get it.
+     *
+     * Open reading pipe 1 using address `0xCCCECCCECC`
+     * @code
+     * byte address[] = {0xCC, 0xCE, 0xCC, 0xCE, 0xCC};
+     * radio.openReadingPipe(1,address);
+     * radio.startListening();
+     * @endcode
+     *
+     * @note If there was a call to openReadingPipe() about pipe 0 prior to
+     * calling this function, then this function will re-write the address
+     * that was last set to reading pipe 0. This is because openWritingPipe()
+     * will overwrite the address to reading pipe 0 for proper auto-ack
+     * functionality.
+     */
+    void startListening(void);
+
+    /**
+     * Stop listening for incoming messages, and switch to transmit mode.
+     *
+     * Do this before calling write().
+     * @code
+     * radio.stopListening();
+     * radio.write(&data, sizeof(data));
+     * @endcode
+     *
+     * @note When the ACK payloads feature is enabled, the TX FIFO buffers are
+     * flushed when calling this function. This is meant to discard any ACK
+     * payloads that were not appended to acknowledgment packets.
+     */
+    void stopListening(void);
+
+    /**
+     * Check whether there are bytes available to be read
+     * @code
+     * if(radio.available()){
+     *   radio.read(&data,sizeof(data));
+     * }
+     * @endcode
+     *
+     * @see available(uint8_t*)
+     *
+     * @return True if there is a payload available, false if none is
+     *
+     * @warning This function relies on the information about the pipe number
+     * that received the next available payload. According to the datasheet,
+     * the data about the pipe number that received the next available payload
+     * is "unreliable" during a FALLING transition on the IRQ pin. This means
+     * you should call whatHappened() before calling this function
+     * during an ISR (Interrupt Service Routine).<br>For example:
+     * @code
+     * void isrCallbackFunction() {
+     *   bool tx_ds, tx_df, rx_dr;
+     *   radio.whatHappened(tx_ds, tx_df, rx_dr); // resets the IRQ pin to HIGH
+     *   radio.available();                       // returned data should now be reliable
+     * }
+     *
+     * void setup() {
+     *   pinMode(IRQ_PIN, INPUT);
+     *   attachInterrupt(digitalPinToInterrupt(IRQ_PIN), isrCallbackFunction, FALLING);
+     * }
+     * @endcode
+     */
+    bool available(void);
+
+    /**
+     * Read payload data from the RX FIFO buffer(s).
+     *
+     * The length of data read is usually the next available payload's length
+     * @see
+     * - getPayloadSize()
+     * - getDynamicPayloadSize()
+     *
+     * @note I specifically chose `void*` as a data type to make it easier
+     * for beginners to use.  No casting needed.
+     *
+     * @param buf Pointer to a buffer where the data should be written
+     * @param len Maximum number of bytes to read into the buffer. This
+     * value should match the length of the object referenced using the
+     * `buf` parameter. The absolute maximum number of bytes that can be read
+     * in one call is 32 (for dynamic payload lengths) or whatever number was
+     * previously passed to setPayloadSize() (for static payload lengths).
+     * @remark Remember that each call to read() fetches data from the
+     * RX FIFO beginning with the first byte from the first available
+     * payload. A payload is not removed from the RX FIFO until it's
+     * entire length (or more) is fetched using read().
+     * @remarks
+     * - If `len` parameter's value is less than the available payload's
+     *   length, then the payload remains in the RX FIFO.
+     * - If `len` parameter's value is greater than the first of multiple
+     *   available payloads, then the data saved to the `buf`
+     *   parameter's object will be supplemented with data from the next
+     *   available payload.
+     * - If `len` parameter's value is greater than the last available
+     *   payload's length, then the last byte in the payload is used as
+     *   padding for the data saved to the `buf` parameter's object.
+     *   The nRF24L01 will repeatedly use the last byte from the last
+     *   payload even when read() is called with an empty RX FIFO.
+     *
+     * @note To use this function in the python wrapper, remember that
+     * only the `len` parameter is required because this function (in the
+     * python wrapper) returns the payload data as a buffer protocol object
+     * (bytearray object).
+     * @code{.py}
+     * # let `radio` be the instantiated RF24 object
+     * if radio.available():
+     *     length = radio.getDynamicPayloadSize()  # or radio.getPayloadSize() for static payload sizes
+     *     received_payload = radio.read(length)
+     * @endcode
+     *
+     * @return No return value. Use available().
+     * @note This function no longer returns a boolean. Use available to
+     * determine if packets are available. The `RX_DR` Interrupt flag is now
+     * cleared with this function instead of when calling available().
+     * @code
+     * if(radio.available()) {
+     *   radio.read(&data, sizeof(data));
+     * }
+     * @endcode
+     */
+    void read(void* buf, uint8_t len);
+
+    /**
+     * Be sure to call openWritingPipe() first to set the destination
+     * of where to write to.
+     *
+     * This blocks until the message is successfully acknowledged by
+     * the receiver or the timeout/retransmit maxima are reached.  In
+     * the current configuration, the max delay here is 60-70ms.
+     *
+     * The maximum size of data written is the fixed payload size, see
+     * getPayloadSize().  However, you can write less, and the remainder
+     * will just be filled with zeroes.
+     *
+     * TX/RX/RT interrupt flags will be cleared every time write is called
+     *
+     * @param buf Pointer to the data to be sent
+     * @param len Number of bytes to be sent
+     *
+     * @code
+     * radio.stopListening();
+     * radio.write(&data,sizeof(data));
+     * @endcode
+     *
+     * @note The `len` parameter must be omitted when using the python
+     * wrapper because the length of the payload is determined automatically.
+     * <br>To use this function in the python wrapper:
+     * @code{.py}
+     * # let `radio` be the instantiated RF24 object
+     * buffer = b"Hello World"  # a `bytes` object
+     * radio.write(buffer)
+     * @endcode
+     *
+     * @return
+     * - `true` if the payload was delivered successfully and an acknowledgement
+     *   (ACK packet) was received. If auto-ack is disabled, then any attempt
+     *   to transmit will also return true (even if the payload was not
+     *   received).
+     * - `false` if the payload was sent but was not acknowledged with an ACK
+     *   packet. This condition can only be reported if the auto-ack feature
+     *   is on.
+     */
+    bool write(const void* buf, uint8_t len);
+
+    /**
+     * New: Open a pipe for writing via byte array. Old addressing format retained
+     * for compatibility.
+     *
+     * Only one writing pipe can be opened at once, but this function changes
+     * the address that is used to transmit (ACK payloads/packets do not apply
+     * here). Be sure to call stopListening() prior to calling this function.
+     *
+     * Addresses are assigned via a byte array, default is 5 byte address length
+     *
+     * @code
+     *   uint8_t addresses[][6] = {"1Node", "2Node"};
+     *   radio.openWritingPipe(addresses[0]);
+     * @endcode
+     * @code
+     *  uint8_t address[] = { 0xCC, 0xCE, 0xCC, 0xCE, 0xCC };
+     *  radio.openWritingPipe(address);
+     *  address[0] = 0x33;
+     *  radio.openReadingPipe(1, address);
+     * @endcode
+     *
+     * @warning This function will overwrite the address set to reading pipe 0
+     * as stipulated by the datasheet for proper auto-ack functionality in TX
+     * mode. Use this function to ensure proper transmission acknowledgement
+     * when the address set to reading pipe 0 (via openReadingPipe()) does not
+     * match the address passed to this function. If the auto-ack feature is
+     * disabled, then this function will still overwrite the address for
+     * reading pipe 0 regardless.
+     *
+     * @see
+     * - setAddressWidth()
+     * - startListening()
+     *
+     * @param address The address to be used for outgoing transmissions (uses
+     * pipe 0). Coordinate this address amongst other receiving nodes (the
+     * pipe numbers don't need to match).
+     *
+     * @remark There is no address length parameter because this function will
+     * always write the number of bytes that the radio addresses are configured
+     * to use (set with setAddressWidth()).
+     */
+
+    void openWritingPipe(const uint8_t* address);
+
+    /**
+     * Open a pipe for reading
+     *
+     * Up to 6 pipes can be open for reading at once.  Open all the required
+     * reading pipes, and then call startListening().
+     *
+     * @see
+     * - openWritingPipe()
+     * - setAddressWidth()
+     *
+     * @note Pipes 0 and 1 will store a full 5-byte address. Pipes 2-5 will technically
+     * only store a single byte, borrowing up to 4 additional bytes from pipe 1 per the
+     * assigned address width.<br>
+     * Pipes 1-5 should share the same address, except the first byte.
+     * Only the first byte in the array should be unique, e.g.
+     * @code
+     * uint8_t addresses[][6] = {"Prime", "2Node", "3xxxx", "4xxxx"};
+     * openReadingPipe(0, addresses[0]); // address used is "Prime"
+     * openReadingPipe(1, addresses[1]); // address used is "2Node"
+     * openReadingPipe(2, addresses[2]); // address used is "3Node"
+     * openReadingPipe(3, addresses[3]); // address used is "4Node"
+     * @endcode
+     *
+     * @warning If the reading pipe 0 is opened by this function, the address
+     * passed to this function (for pipe 0) will be restored at every call to
+     * startListening().<br> Read
+     * http://maniacalbits.blogspot.com/2013/04/rf24-addressing-nrf24l01-radios-require.html
+     * to understand how to avoid using malformed addresses. This address
+     * restoration is implemented because of the underlying neccessary
+     * functionality of openWritingPipe().
+     *
+     * @param number Which pipe to open. Only pipe numbers 0-5 are available,
+     * an address assigned to any pipe number not in that range will be ignored.
+     * @param address The 24, 32 or 40 bit address of the pipe to open.
+     *
+     * @remark There is no address length parameter because this function will
+     * always write the number of bytes (for pipes 0 and 1) that the radio
+     * addresses are configured to use (set with setAddressWidth()).
+     */
+
+    void openReadingPipe(uint8_t number, const uint8_t* address);
+
+    /**@}*/
+    /**
+     * @name Advanced Operation
+     *
+     *  Methods you can use to drive the chip in more advanced ways
+     */
+    /**@{*/
+
+    /**
+     * Print a giant block of debugging information to stdout
+     *
+     * @warning Does nothing if stdout is not defined.  See fdevopen in stdio.h
+     * The printf.h file is included with the library for Arduino.
+     * @code
+     * #include <printf.h>
+     * setup(){
+     *  Serial.begin(115200);
+     *  printf_begin();
+     *  ...
+     * }
+     * @endcode
+     */
+    void printDetails(void);
+
+    /**
+     * Print a giant block of debugging information to stdout. This function
+     * differs from printDetails() because it makes the information more
+     * understandable without having to look up the datasheet or convert
+     * hexadecimal to binary. Only use this function if your application can
+     * spare extra bytes of memory.
+     *
+     * @warning Does nothing if stdout is not defined.  See fdevopen in stdio.h
+     * The printf.h file is included with the library for Arduino.
+     * @code
+     * #include <printf.h>
+     * setup(){
+     *  Serial.begin(115200);
+     *  printf_begin();
+     *  ...
+     * }
+     * @endcode
+     *
+     * @note If the automatic acknowledgements feature is configured differently
+     * for each pipe, then a binary representation is used in which bits 0-5
+     * represent pipes 0-5 respectively. A `0` means the feature is disabled and
+     * a `1` means the feature is enabled.
+     */
+    void printPrettyDetails(void);
+
+    /**
+     * Test whether there are bytes available to be read from the
+     * FIFO buffers.
+     *
+     * @note This function is named `available_pipe()` in the python wrapper.
+     * Additionally, the `available_pipe()` function (which
+     * takes no arguments) returns a 2 item tuple containing (ordered by
+     * tuple's indices):
+     * - A boolean describing if there is a payload available to read from
+     *   the RX FIFO buffers.
+     * - The pipe number that received the next available payload in the RX
+     *   FIFO buffers. If the item at the tuple's index 0 is `False`, then
+     *   this pipe number is invalid.
+     * @note To use this function in python:
+     * @code{.py}
+     * # let `radio` be the instatiated RF24 object
+     * has_payload, pipe_number = radio.available_pipe()  # expand the tuple to 2 variables
+     * if has_payload:
+     *     print("Received a payload with pipe", pipe_number)
+     * @endcode
+     *
+     * @param[out] pipe_num Which pipe has the payload available
+     * @code
+     * uint8_t pipeNum;
+     * if(radio.available(&pipeNum)){
+     *   radio.read(&data, sizeof(data));
+     *   Serial.print("Received data on pipe ");
+     *   Serial.println(pipeNum);
+     * }
+     * @endcode
+     *
+     * @warning According to the datasheet, the data saved to `pipe_num` is
+     * "unreliable" during a FALLING transition on the IRQ pin. This means you
+     * should call whatHappened() before calling this function during
+     * an ISR (Interrupt Service Routine).<br>For example:
+     * @code
+     * void isrCallbackFunction() {
+     *   bool tx_ds, tx_df, rx_dr;
+     *   radio.whatHappened(tx_ds, tx_df, rx_dr); // resets the IRQ pin to HIGH
+     *   uint8_t pipe;                            // initialize pipe data
+     *   radio.available(&pipe);                  // pipe data should now be reliable
+     * }
+     *
+     * void setup() {
+     *   pinMode(IRQ_PIN, INPUT);
+     *   attachInterrupt(digitalPinToInterrupt(IRQ_PIN), isrCallbackFunction, FALLING);
+     * }
+     * @endcode
+     *
+     * @return
+     * - `true` if there is a payload available in the top (first out)
+     *   level RX FIFO.
+     * - `false` if there is nothing available in the RX FIFO because it is
+     *   empty.
+     */
+    bool available(uint8_t* pipe_num);
+
+    /**
+     * Use this function to check if the radio's RX FIFO levels are all
+     * occupied. This can be used to prevent data loss because any incoming
+     * transmissions are rejected if there is no unoccupied levels in the RX
+     * FIFO to store the incoming payload. Remember that each level can hold
+     * up to a maximum of 32 bytes.
+     * @return
+     * - `true` if all three 3 levels of the RX FIFO buffers are occupied.
+     * - `false` if there is one or more levels available in the RX FIFO
+     *   buffers. Remember that this does not always mean that the RX FIFO
+     *   buffers are empty; use available() to see if the RX FIFO buffers are
+     *   empty or not.
+     */
+    bool rxFifoFull();
+
+    /**
+     * Enter low-power mode
+     *
+     * To return to normal power mode, call powerUp().
+     *
+     * @note After calling startListening(), a basic radio will consume about 13.5mA
+     * at max PA level.
+     * During active transmission, the radio will consume about 11.5mA, but this will
+     * be reduced to 26uA (.026mA) between sending.
+     * In full powerDown mode, the radio will consume approximately 900nA (.0009mA)
+     *
+     * @code
+     * radio.powerDown();
+     * avr_enter_sleep_mode(); // Custom function to sleep the device
+     * radio.powerUp();
+     * @endcode
+     */
+    void powerDown(void);
+
+    /**
+     * Leave low-power mode - required for normal radio operation after calling powerDown()
+     *
+     * To return to low power mode, call powerDown().
+     * @note This will take up to 5ms for maximum compatibility
+     */
+    void powerUp(void);
+
+    /**
+     * Write for single NOACK writes. Optionally disable
+     * acknowledgements/auto-retries for a single payload using the
+     * multicast parameter set to true.
+     *
+     * Can be used with enableAckPayload() to request a response
+     * @see
+     * - setAutoAck()
+     * - write()
+     *
+     * @param buf Pointer to the data to be sent
+     * @param len Number of bytes to be sent
+     * @param multicast Request ACK response (false), or no ACK response
+     * (true). Be sure to have called enableDynamicAck() at least once before
+     * setting this parameter.
+     * @return
+     * - `true` if the payload was delivered successfully and an acknowledgement
+     *   (ACK packet) was received. If auto-ack is disabled, then any attempt
+     *   to transmit will also return true (even if the payload was not
+     *   received).
+     * - `false` if the payload was sent but was not acknowledged with an ACK
+     *   packet. This condition can only be reported if the auto-ack feature
+     *   is on.
+     *
+     * @note The `len` parameter must be omitted when using the python
+     * wrapper because the length of the payload is determined automatically.
+     * <br>To use this function in the python wrapper:
+     * @code{.py}
+     * # let `radio` be the instantiated RF24 object
+     * buffer = b"Hello World"  # a `bytes` object
+     * radio.write(buffer, False)  # False = the multicast parameter
+     * @endcode
+     */
+    bool write(const void* buf, uint8_t len, const bool multicast);
+
+    /**
+     * This will not block until the 3 FIFO buffers are filled with data.
+     * Once the FIFOs are full, writeFast will simply wait for success or
+     * timeout, and return 1 or 0 respectively. From a user perspective, just
+     * keep trying to send the same data. The library will keep auto retrying
+     * the current payload using the built in functionality.
+     * @warning It is important to never keep the nRF24L01 in TX mode and FIFO full for more than 4ms at a 
time. If the auto
+     * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the 
FIFO
+     * to clear by issuing txStandBy() or ensure appropriate time between transmissions.
+     *
+     * Example (Partial blocking):
+     * @code
+     * radio.writeFast(&buf,32);  // Writes 1 payload to the buffers
+     * txStandBy();                       // Returns 0 if failed. 1 if success. Blocks only until MAX_RT 
timeout or success. Data flushed on fail.
+     *
+     * radio.writeFast(&buf,32);  // Writes 1 payload to the buffers
+     * txStandBy(1000);                   // Using extended timeouts, returns 1 if success. Retries failed 
payloads for 1 seconds before returning 0.
+     * @endcode
+     *
+     * @see
+     * - txStandBy()
+     * - write()
+     * - writeBlocking()
+     *
+     * @param buf Pointer to the data to be sent
+     * @param len Number of bytes to be sent
+     * @return
+     * - `true` if the payload was delivered successfully and an acknowledgement
+     *   (ACK packet) was received. If auto-ack is disabled, then any attempt
+     *   to transmit will also return true (even if the payload was not
+     *   received).
+     * - `false` if the payload was sent but was not acknowledged with an ACK
+     *   packet. This condition can only be reported if the auto-ack feature
+     *   is on.
+     *
+     * @note The `len` parameter must be omitted when using the python
+     * wrapper because the length of the payload is determined automatically.
+     * <br>To use this function in the python wrapper:
+     * @code{.py}
+     * # let `radio` be the instantiated RF24 object
+     * buffer = b"Hello World"  # a `bytes` object
+     * radio.writeFast(buffer)
+     * @endcode
+     */
+    bool writeFast(const void* buf, uint8_t len);
+
+    /**
+     * WriteFast for single NOACK writes. Optionally disable
+     * acknowledgements/auto-retries for a single payload using the
+     * multicast parameter set to true.
+     *
+     * @see setAutoAck()
+     *
+     * @param buf Pointer to the data to be sent
+     * @param len Number of bytes to be sent
+     * @param multicast Request ACK response (false), or no ACK response
+     * (true). Be sure to have called enableDynamicAck() at least once before
+     * setting this parameter.
+     * @return
+     * - `true` if the payload passed to `buf` was loaded in the TX FIFO.
+     * - `false` if the payload passed to `buf` was not loaded in the TX FIFO
+     *   because a previous payload already in the TX FIFO failed to
+     *   transmit. This condition can only be reported if the auto-ack feature
+     *   is on.
+     *
+     * @note The `len` parameter must be omitted when using the python
+     * wrapper because the length of the payload is determined automatically.
+     * <br>To use this function in the python wrapper:
+     * @code{.py}
+     * # let `radio` be the instantiated RF24 object
+     * buffer = b"Hello World"  # a `bytes` object
+     * radio.writeFast(buffer, False)  # False = the multicast parameter
+     * @endcode
+     */
+    bool writeFast(const void* buf, uint8_t len, const bool multicast);
+
+    /**
+     * This function extends the auto-retry mechanism to any specified duration.
+     * It will not block until the 3 FIFO buffers are filled with data.
+     * If so the library will auto retry until a new payload is written
+     * or the user specified timeout period is reached.
+     * @warning It is important to never keep the nRF24L01 in TX mode and FIFO full for more than 4ms at a 
time. If the auto
+     * retransmit is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. Allow the 
FIFO
+     * to clear by issuing txStandBy() or ensure appropriate time between transmissions.
+     *
+     * Example (Full blocking):
+     * @code
+     * radio.writeBlocking(&buf, sizeof(buf), 1000); // Wait up to 1 second to write 1 payload to the buffers
+     * radio.txStandBy(1000);                        // Wait up to 1 second for the payload to send. Return 
1 if ok, 0 if failed.
+     *                                               // Blocks only until user timeout or success. Data 
flushed on fail.
+     * @endcode
+     * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); 
called to enable millis().
+     * @see
+     * - txStandBy()
+     * - write()
+     * - writeFast()
+     *
+     * @param buf Pointer to the data to be sent
+     * @param len Number of bytes to be sent
+     * @param timeout User defined timeout in milliseconds.
+     *
+     * @note The `len` parameter must be omitted when using the python
+     * wrapper because the length of the payload is determined automatically.
+     * <br>To use this function in the python wrapper:
+     * @code{.py}
+     * # let `radio` be the instantiated RF24 object
+     * buffer = b"Hello World"  # a `bytes` object
+     * radio.writeBlocking(buffer, 1000)  # 1000 means wait at most 1 second
+     * @endcode
+     *
+     * @return
+     * - `true` if the payload passed to `buf` was loaded in the TX FIFO.
+     * - `false` if the payload passed to `buf` was not loaded in the TX FIFO
+     *   because a previous payload already in the TX FIFO failed to
+     *   transmit. This condition can only be reported if the auto-ack feature
+     *   is on.
+     */
+    bool writeBlocking(const void* buf, uint8_t len, uint32_t timeout);
+
+    /**
+     * This function should be called as soon as transmission is finished to
+     * drop the radio back to STANDBY-I mode. If not issued, the radio will
+     * remain in STANDBY-II mode which, per the data sheet, is not a recommended
+     * operating mode.
+     *
+     * @note When transmitting data in rapid succession, it is still recommended by
+     * the manufacturer to drop the radio out of TX or STANDBY-II mode if there is
+     * time enough between sends for the FIFOs to empty. This is not required if auto-ack
+     * is enabled.
+     *
+     * Relies on built-in auto retry functionality.
+     *
+     * Example (Partial blocking):
+     * @code
+     * radio.writeFast(&buf, 32);
+     * radio.writeFast(&buf, 32);
+     * radio.writeFast(&buf, 32);   //Fills the FIFO buffers up
+     * bool ok = radio.txStandBy(); //Returns 0 if failed. 1 if success.
+     *                              //Blocks only until MAX_RT timeout or success. Data flushed on fail.
+     * @endcode
+     * @see txStandBy(uint32_t timeout, bool startTx)
+     * @return
+     * - `true` if all payloads in the TX FIFO were delivered successfully and
+     *   an acknowledgement (ACK packet) was received for each. If auto-ack is
+     *   disabled, then any attempt to transmit will also return true (even if
+     *   the payload was not received).
+     * - `false` if a payload was sent but was not acknowledged with an ACK
+     *   packet. This condition can only be reported if the auto-ack feature
+     *   is on.
+     */
+    bool txStandBy();
+
+    /**
+     * This function allows extended blocking and auto-retries per a user defined timeout
+     *
+     * Fully Blocking Example:
+     * @code
+     * radio.writeFast(&buf, 32);
+     * radio.writeFast(&buf, 32);
+     * radio.writeFast(&buf, 32);       //Fills the FIFO buffers up
+     * bool ok = radio.txStandBy(1000); //Returns 0 if failed after 1 second of retries. 1 if success.
+     *                                  //Blocks only until user defined timeout or success. Data flushed on 
fail.
+     * @endcode
+     * @note If used from within an interrupt, the interrupt should be disabled until completion, and sei(); 
called to enable millis().
+     * @param timeout Number of milliseconds to retry failed payloads
+     * @param startTx If this is set to `true`, then this function puts the nRF24L01
+     * in TX Mode. `false` leaves the primary mode (TX or RX) as it is, which can
+     * prevent the mandatory wait time to change modes.
+     * @return
+     * - `true` if all payloads in the TX FIFO were delivered successfully and
+     *   an acknowledgement (ACK packet) was received for each. If auto-ack is
+     *   disabled, then any attempt to transmit will also return true (even if
+     *   the payload was not received).
+     * - `false` if a payload was sent but was not acknowledged with an ACK
+     *   packet. This condition can only be reported if the auto-ack feature
+     *   is on.
+     */
+    bool txStandBy(uint32_t timeout, bool startTx = 0);
+
+    /**
+     * Write an acknowledgement (ACK) payload for the specified pipe
+     *
+     * The next time a message is received on a specified `pipe`, the data in
+     * `buf` will be sent back in the ACK payload.
+     *
+     * @see
+     * - enableAckPayload()
+     * - enableDynamicPayloads()
+     *
+     * @note ACK payloads are handled automatically by the radio chip when a
+     * regular payload is received. It is important to discard regular payloads
+     * in the TX FIFO (using flush_tx()) before loading the first ACK payload
+     * into the TX FIFO. This function can be called before and after calling
+     * startListening().
+     *
+     * @warning Only three of these can be pending at any time as there are
+     * only 3 FIFO buffers.<br> Dynamic payloads must be enabled.
+     *
+     * @note ACK payloads are dynamic payloads. Calling enableAckPayload()
+     * will automatically enable dynamic payloads on pipe 0 (required for TX
+     * mode when expecting ACK payloads). To use ACK payloads on any other
+     * pipe in RX mode, call enableDynamicPayloads().
+     *
+     * @param pipe Which pipe# (typically 1-5) will get this response.
+     * @param buf Pointer to data that is sent
+     * @param len Length of the data to send, up to 32 bytes max.  Not affected
+     * by the static payload set by setPayloadSize().
+     *
+     * @note The `len` parameter must be omitted when using the python
+     * wrapper because the length of the payload is determined automatically.
+     * <br>To use this function in the python wrapper:
+     * @code{.py}
+     * # let `radio` be the instantiated RF24 object
+     * buffer = b"Hello World"  # a `bytes` object
+     * radio.writeAckPayload(1, buffer)  # load an ACK payload for response on pipe 1
+     * @endcode
+     *
+     * @return
+     * - `true` if the payload was loaded into the TX FIFO.
+     * - `false` if the payload wasn't loaded into the TX FIFO because it is
+     *   already full or the ACK payload feature is not enabled using
+     *   enableAckPayload().
+     */
+    bool writeAckPayload(uint8_t pipe, const void* buf, uint8_t len);
+
+    /**
+     * Call this when you get an Interrupt Request (IRQ) to find out why
+     *
+     * This function describes what event triggered the IRQ pin to go active
+     * LOW and clears the status of all events.
+     *
+     * @see maskIRQ()
+     *
+     * @param[out] tx_ok The transmission attempt completed (TX_DS). This does
+     * not imply that the transmitted data was received by another radio, rather
+     * this only reports if the attempt to send was completed. This will
+     * always be `true` when the auto-ack feature is disabled.
+     * @param[out] tx_fail The transmission failed to be acknowledged, meaning
+     * too many retries (MAX_RT) were made while expecting an ACK packet. This
+     * event is only triggered when auto-ack feature is enabled.
+     * @param[out] rx_ready There is a newly received payload (RX_DR) saved to
+     * RX FIFO buffers. Remember that the RX FIFO can only hold up to 3
+     * payloads. Once the RX FIFO is full, all further received transmissions
+     * are rejected until there is space to save new data in the RX FIFO
+     * buffers.
+     *
+     * @note This function expects no parameters in the python wrapper.
+     * Instead, this function returns a 3 item tuple describing the IRQ
+     * events' status.<br> To use this function in the python wrapper:
+     * @code{.py}
+     * # let`radio` be the instantiated RF24 object
+     * tx_ds, tx_df, rx_dr = radio.whatHappened()  # get IRQ status flags
+     * print("tx_ds: {}, tx_df: {}, rx_dr: {}".format(tx_ds, tx_df, rx_dr))
+     * @endcode
+     */
+    void whatHappened(bool& tx_ok, bool& tx_fail, bool& rx_ready);
+
+    /**
+     * Non-blocking write to the open writing pipe used for buffered writes
+     *
+     * @note Optimization: This function now leaves the CE pin high, so the radio
+     * will remain in TX or STANDBY-II Mode until a txStandBy() command is issued. Can be used as an 
alternative to startWrite()
+     * if writing multiple payloads at once.
+     * @warning It is important to never keep the nRF24L01 in TX mode with FIFO full for more than 4ms at a 
time. If the auto
+     * retransmit/autoAck is enabled, the nRF24L01 is never in TX mode long enough to disobey this rule. 
Allow the FIFO
+     * to clear by issuing txStandBy() or ensure appropriate time between transmissions.
+     *
+     * @see
+     * - write()
+     * - writeFast()
+     * - startWrite()
+     * - writeBlocking()
+     * - setAutoAck() (for single noAck writes)
+     *
+     * @param buf Pointer to the data to be sent
+     * @param len Number of bytes to be sent
+     * @param multicast Request ACK response (false), or no ACK response
+     * (true). Be sure to have called enableDynamicAck() at least once before
+     * setting this parameter.
+     * @param startTx If this is set to `true`, then this function sets the
+     * nRF24L01's CE pin to active (enabling TX transmissions). `false` has no
+     * effect on the nRF24L01's CE pin and simply loads the payload into the
+     * TX FIFO.
+     *
+     * @note The `len` parameter must be omitted when using the python
+     * wrapper because the length of the payload is determined automatically.
+     * <br>To use this function in the python wrapper:
+     * @code{.py}
+     * # let `radio` be the instantiated RF24 object
+     * buffer = b"Hello World"  # a `bytes` object
+     * radio.startFastWrite(buffer, False, True)  # 3rd parameter is optional
+     * #     False means expecting ACK response (multicast parameter)
+     * #     True means initiate transmission (startTx parameter)
+     * @endcode
+     */
+    void startFastWrite(const void* buf, uint8_t len, const bool multicast, bool startTx = 1);
+
+    /**
+     * Non-blocking write to the open writing pipe
+     *
+     * Just like write(), but it returns immediately. To find out what happened
+     * to the send, catch the IRQ and then call whatHappened().
+     *
+     * @see
+     * - write()
+     * - writeFast()
+     * - startFastWrite()
+     * - whatHappened()
+     * - setAutoAck() (for single noAck writes)
+     *
+     * @param buf Pointer to the data to be sent
+     * @param len Number of bytes to be sent
+     * @param multicast Request ACK response (false), or no ACK response
+     * (true). Be sure to have called enableDynamicAck() at least once before
+     * setting this parameter.
+     *
+     * @return
+     * - `true` if payload was written to the TX FIFO buffers and the
+     *   transmission was started.
+     * - `false` if the TX FIFO is full and the payload could not be written. In
+     *   this condition, the transmission process is restarted.
+     * @note The `len` parameter must be omitted when using the python
+     * wrapper because the length of the payload is determined automatically.
+     * <br>To use this function in the python wrapper:
+     * @code{.py}
+     * # let `radio` be the instantiated RF24 object
+     * buffer = b"Hello World"  # a `bytes` object
+     * radio.startWrite(buffer, False)  # False = the multicast parameter
+     * @endcode
+     */
+    bool startWrite(const void* buf, uint8_t len, const bool multicast);
+
+    /**
+     * The function will instruct the radio to re-use the payload in the
+     * top level (first out) of the TX FIFO buffers. This is used internally
+     * by writeBlocking() to initiate retries when a TX failure
+     * occurs. Retries are automatically initiated except with the standard
+     * write(). This way, data is not flushed from the buffer until calling
+     * flush_tx(). If the TX FIFO has only the one payload (in the top level),
+     * the re-used payload can be overwritten by using write(), writeFast(),
+     * writeBlocking(), startWrite(), or startFastWrite(). If the TX FIFO has
+     * other payloads enqueued, then the aforementioned functions will attempt
+     * to enqueue the a new payload in the TX FIFO (does not overwrite the top
+     * level of the TX FIFO). Currently, stopListening() also calls flush_tx()
+     * when ACK payloads are enabled (via enableAckPayload()).
+     *
+     * Upon exiting, this function will set the CE pin HIGH to initiate the
+     * re-transmission process. If only 1 re-transmission is desired, then the
+     * CE pin should be set to LOW after the mandatory minumum pulse duration
+     * of 10 microseconds.
+     *
+     * @remark This function only applies when taking advantage of the
+     * auto-retry feature. See setAutoAck() and setRetries() to configure the
+     * auto-retry feature.
+     *
+     * @note This is to be used AFTER auto-retry fails if wanting to resend
+     * using the built-in payload reuse feature. After issuing reUseTX(), it
+     * will keep resending the same payload until a transmission failure
+     * occurs or the CE pin is set to LOW (whichever comes first). In the
+     * event of a re-transmission failure, simply call this function again to
+     * resume re-transmission of the same payload.
+     */
+    void reUseTX();
+
+    /**
+     * Empty all 3 of the TX (transmit) FIFO buffers. This is automatically
+     * called by stopListening() if ACK payloads are enabled. However,
+     * startListening() does not call this function.
+     *
+     * @return Current value of status register
+     */
+    uint8_t flush_tx(void);
+
+    /**
+     * Empty all 3 of the RX (receive) FIFO buffers.
+     *
+     * @return Current value of status register
+     */
+    uint8_t flush_rx(void);
+
+    /**
+     * Test whether there was a carrier on the line for the
+     * previous listening period.
+     *
+     * Useful to check for interference on the current channel.
+     *
+     * @return true if was carrier, false if not
+     */
+    bool testCarrier(void);
+
+    /**
+     * Test whether a signal (carrier or otherwise) greater than
+     * or equal to -64dBm is present on the channel. Valid only
+     * on nRF24L01P (+) hardware. On nRF24L01, use testCarrier().
+     *
+     * Useful to check for interference on the current channel and
+     * channel hopping strategies.
+     *
+     * @code
+     * bool goodSignal = radio.testRPD();
+     * if(radio.available()){
+     *    Serial.println(goodSignal ? "Strong signal > 64dBm" : "Weak signal < 64dBm" );
+     *    radio.read(0,0);
+     * }
+     * @endcode
+     * @return true if a signal less than or equal to -64dBm was detected,
+     * false if not.
+     */
+    bool testRPD(void);
+
+    /**
+     * Test whether this is a real radio, or a mock shim for
+     * debugging.  Setting either pin to 0xff is the way to
+     * indicate that this is not a real radio.
+     *
+     * @return true if this is a legitimate radio
+     */
+    bool isValid();
+
+    /**
+     * Close a pipe after it has been previously opened.
+     * Can be safely called without having previously opened a pipe.
+     * @param pipe Which pipe number to close, any integer not in range [0, 5]
+     * is ignored.
+     */
+    void closeReadingPipe(uint8_t pipe);
+
+    #if defined (FAILURE_HANDLING)
+    /**
+     *
+     * If a failure has been detected, it usually indicates a hardware issue. By default the library
+     * will cease operation when a failure is detected.
+     * This should allow advanced users to detect and resolve intermittent hardware issues.
+     *
+     * In most cases, the radio must be re-enabled via radio.begin(); and the appropriate settings
+     * applied after a failure occurs, if wanting to re-enable the device immediately.
+     *
+     * The three main failure modes of the radio include:
+     *
+     * Writing to radio: Radio unresponsive - Fixed internally by adding a timeout to the internal write 
functions in RF24 (failure handling)
+     *
+     * Reading from radio: Available returns true always - Fixed by adding a timeout to available functions 
by the user. This is implemented internally in  RF24Network.
+     *
+     * Radio configuration settings are lost - Fixed by monitoring a value that is different from the 
default, and re-configuring the radio if this setting reverts to the default.
+     *
+     * See the included example, GettingStarted_HandlingFailures
+     *
+     *  @code
+     *  if(radio.failureDetected){
+     *    radio.begin();                        // Attempt to re-configure the radio with defaults
+     *    radio.failureDetected = 0;            // Reset the detection value
+     *  radio.openWritingPipe(addresses[1]);  // Re-configure pipe addresses
+     *    radio.openReadingPipe(1, addresses[0]);
+     *    report_failure();                     // Blink leds, send a message, etc. to indicate failure
+     *  }
+     * @endcode
+     */
+    bool failureDetected;
+    #endif // defined (FAILURE_HANDLING)
+
+    /**@}*/
+    /**
+     * @name Optional Configurators
+     *
+     *  Methods you can use to get or set the configuration of the chip.
+     *  None are required.  Calling begin() sets up a reasonable set of
+     *  defaults.
+     */
+    /**@{*/
+
+    /**
+     * Set the address width from 3 to 5 bytes (24, 32 or 40 bit)
+     *
+     * @param a_width The address width (in bytes) to use; this can be 3, 4 or
+     * 5.
+     */
+    void setAddressWidth(uint8_t a_width);
+
+    /**
+     * Set the number of retry attempts and delay between retry attempts when
+     * transmitting a payload. The radio is waiting for an acknowledgement
+     * (ACK) packet during the delay between retry attempts.
+     *
+     * @param delay How long to wait between each retry, in multiples of
+     * 250 us. The minumum of 0 means 250 us, and the maximum of 15 means
+     * 4000 us. The default value of 5 means 1500us (5 * 250 + 250).
+     * @param count How many retries before giving up. The default/maximum is 15. Use
+     * 0 to disable the auto-retry feature all together.
+     *
+     * @note Disable the auto-retry feature on a transmitter still uses the
+     * auto-ack feature (if enabled), except it will not retry to transmit if
+     * the payload was not acknowledged on the first attempt.
+     */
+    void setRetries(uint8_t delay, uint8_t count);
+
+    /**
+     * Set RF communication channel. The frequency used by a channel is
+     * calculated as:
+     * @verbatim 2400 MHz + <channel number> @endverbatim
+     * Meaning the default channel of 76 uses the approximate frequency of
+     * 2476 MHz.
+     *
+     * @note In the python wrapper, this function is the setter of the
+     * `channel` attribute.<br>To use this function in the python wrapper:
+     * @code{.py}
+     * # let `radio` be the instantiated RF24 object
+     * radio.channel = 2  # set the channel to 2 (2402 MHz)
+     * @endcode
+     *
+     * @param channel Which RF channel to communicate on, 0-125
+     */
+    void setChannel(uint8_t channel);
+
+    /**
+     * Get RF communication channel
+     *
+     * @note In the python wrapper, this function is the getter of the
+     * `channel` attribute.<br>To use this function in the python wrapper:
+     * @code{.py}
+     * # let `radio` be the instantiated RF24 object
+     * chn = radio.channel  # get the channel
+     * @endcode
+     *
+     * @return The currently configured RF Channel
+     */
+    uint8_t getChannel(void);
+
+    /**
+     * Set Static Payload Size
+     *
+     * This implementation uses a pre-stablished fixed payload size for all
+     * transmissions.  If this method is never called, the driver will always
+     * transmit the maximum payload size (32 bytes), no matter how much
+     * was sent to write().
+     *
+     * @note In the python wrapper, this function is the setter of the
+     * `payloadSize` attribute.<br>To use this function in the python wrapper:
+     * @code{.py}
+     * # let `radio` be the instantiated RF24 object
+     * radio.payloadSize = 16  # set the static payload size to 16 bytes
+     * @endcode
+     *
+     * @param size The number of bytes in the payload
+     */
+    void setPayloadSize(uint8_t size);
+
+    /**
+     * Get Static Payload Size
+     *
+     * @note In the python wrapper, this function is the getter of the
+     * `payloadSize` attribute.<br>To use this function in the python wrapper:
+     * @code{.py}
+     * # let `radio` be the instantiated RF24 object
+     * pl_size = radio.payloadSize  # get the static payload size
+     * @endcode
+     *
+     * @see setPayloadSize()
+     *
+     * @return The number of bytes in the payload
+     */
+    uint8_t getPayloadSize(void);
+
+    /**
+     * Get Dynamic Payload Size
+     *
+     * For dynamic payloads, this pulls the size of the payload off
+     * the chip
+     *
+     * @note Corrupt packets are now detected and flushed per the
+     * manufacturer.
+     * @code
+     * if(radio.available()){
+     *   if(radio.getDynamicPayloadSize() < 1){
+     *     // Corrupt payload has been flushed
+     *     return;
+     *   }
+     *   radio.read(&data,sizeof(data));
+     * }
+     * @endcode
+     *
+     * @return Payload length of last-received dynamic payload
+     */
+    uint8_t getDynamicPayloadSize(void);
+
+    /**
+     * Enable custom payloads in the acknowledge packets
+     *
+     * ACK payloads are a handy way to return data back to senders without
+     * manually changing the radio modes on both units.
+     *
+     * @remarks The ACK payload feature requires the auto-ack feature to be
+     * enabled for any pipe using ACK payloads. This function does not
+     * automatically enable the auto-ack feature on pipe 0 since the auto-ack
+     * feature is enabled for all pipes by default.
+     *
+     * @see setAutoAck()
+     *
+     * @note ACK payloads are dynamic payloads. This function automatically
+     * enables dynamic payloads on pipe 0 by default. Call
+     * enableDynamicPayloads() to enable on all pipes (especially for RX nodes
+     * that use pipes other than pipe 0 to receive transmissions expecting
+     * responses with ACK payloads).
+     */
+    void enableAckPayload(void);
+
+    /**
+     * Disable custom payloads on the ackowledge packets
+     *
+     * @see enableAckPayload()
+     */
+    void disableAckPayload(void);
+
+    /**
+     * Enable dynamically-sized payloads
+     *
+     * This way you don't always have to send large packets just to send them
+     * once in a while.  This enables dynamic payloads on ALL pipes.
+     *
+     */
+    void enableDynamicPayloads(void);
+
+    /**
+     * Disable dynamically-sized payloads
+     *
+     * This disables dynamic payloads on ALL pipes. Since Ack Payloads
+     * requires Dynamic Payloads, Ack Payloads are also disabled.
+     * If dynamic payloads are later re-enabled and ack payloads are desired
+     * then enableAckPayload() must be called again as well.
+     *
+     */
+    void disableDynamicPayloads(void);
+
+    /**
+     * Enable dynamic ACKs (single write multicast or unicast) for chosen
+     * messages.
+     *
+     * @note This function must be called once before using the multicast
+     * parameter for any functions that offer it. To use multicast behavior
+     * about all outgoing payloads (using pipe 0) or incoming payloads
+     * (concerning all RX pipes), use setAutoAck()
+     *
+     * @see
+     * - setAutoAck() for all pipes
+     * - setAutoAck(uint8_t, bool) for individual pipes
+     *
+     * @code
+     * radio.write(&data, 32, 1); // Sends a payload with no acknowledgement requested
+     * radio.write(&data, 32, 0); // Sends a payload using auto-retry/autoACK
+     * @endcode
+     */
+    void enableDynamicAck();
+
+    /**
+     * Determine whether the hardware is an nRF24L01+ or not.
+     *
+     * @return true if the hardware is nRF24L01+ (or compatible) and false
+     * if its not.
+     */
+    bool isPVariant(void);
+
+    /**
+     * Enable or disable the auto-acknowledgement feature for all pipes. This
+     * feature is enabled by default. Auto-acknowledgement responds to every
+     * recieved payload with an empty ACK packet. These ACK packets get sent
+     * from the receiving radio back to the transmitting radio. To attach an
+     * ACK payload to a ACK packet, use writeAckPayload().
+     *
+     * If this feature is disabled on a transmitting radio, then the
+     * transmitting radio will always report that the payload was recieved
+     * (even if it was not). Please remember that this feature's configuration
+     * needs to match for transmitting and receiving radios.
+     *
+     * @warning When using the `multicast` parameter to write(), this feature
+     * can be disabled for an individual payload. However, if this feature is
+     * disabled, then the `multicast` parameter will have no effect.
+     *
+     * @note If disabling auto-acknowledgment packets, the ACK payloads
+     * feature is also disabled as this feature is required to send ACK
+     * payloads.
+     *
+     * @see
+     * - write()
+     * - writeFast()
+     * - startFastWrite()
+     * - startWrite()
+     * - writeAckPayload()
+     *
+     * @param enable Whether to enable (true) or disable (false) the
+     * auto-acknowledgment feature for all pipes
+     */
+    void setAutoAck(bool enable);
+
+    /**
+     * Enable or disable the auto-acknowledgement feature for a specific pipe.
+     * This feature is enabled by default for all pipes. Auto-acknowledgement
+     * responds to every recieved payload with an empty ACK packet. These ACK
+     * packets get sent from the receiving radio back to the transmitting
+     * radio. To attach an ACK payload to a ACK packet, use writeAckPayload().
+     *
+     * Pipe 0 is used for TX operations, which include sending ACK packets. If
+     * using this feature on both TX & RX nodes, then pipe 0 must have this
+     * feature enabled for the RX & TX operations. If this feature is disabled
+     * on a transmitting radio's pipe 0, then the transmitting radio will
+     * always report that the payload was recieved (even if it was not).
+     * Remember to also enable this feature for any pipe that is openly
+     * listening to a transmitting radio with this feature enabled.
+     *
+     * @warning If this feature is enabled for pipe 0, then the `multicast`
+     * parameter to write() can be used to disable this feature for an
+     * individual payload. However, if this feature is disabled for pipe 0,
+     * then the `multicast` parameter will have no effect.
+     *
+     * @note If disabling auto-acknowledgment packets on pipe 0, the ACK
+     * payloads feature is also disabled as this feature is required on pipe 0
+     * to send ACK payloads.
+     *
+     * @see
+     * - write()
+     * - writeFast()
+     * - startFastWrite()
+     * - startWrite()
+     * - writeAckPayload()
+     * - enableAckPayload()
+     * - disableAckPayload()
+     *
+     * @param pipe Which pipe to configure. This number should be in range
+     * [0, 5].
+     * @param enable Whether to enable (true) or disable (false) the
+     * auto-acknowledgment feature for the specified pipe
+     */
+    void setAutoAck(uint8_t pipe, bool enable);
+
+    /**
+     * Set Power Amplifier (PA) level and Low Noise Amplifier (LNA) state
+     *
+     * @param level The desired @ref PALevel as defined by @ref rf24_pa_dbm_e.
+     * @param lnaEnable Enable or Disable the LNA (Low Noise Amplifier) Gain.
+     * See table for Si24R1 modules below.<br> @p lnaEnable only affects
+     * nRF24L01 modules with an LNA chip.
+     *
+     * | @p level (enum value) | nRF24L01<br>description | Si24R1<br>description when<br> @p lnaEnable = 1 | 
Si24R1<br>description when<br> @p lnaEnable = 0 |
+     * |:---------------------:|:-------:|:--------:|:-------:|
+     * | @ref RF24_PA_MIN (0)  | -18 dBm |  -6 dBm  | -12 dBm |
+     * | @ref RF24_PA_LOW (1)  | -12 dBm |  -0 dBm  | -4 dBm  |
+     * | @ref RF24_PA_HIGH (2) | -6 dBm  |  3 dBm   | 1 dBm   |
+     * | @ref RF24_PA_MAX (3)  |  0 dBm  |  7 dBm   | 4 dBm   |
+     *
+     * @note The getPALevel() function does not care what was passed @p lnaEnable parameter.
+     */
+    void setPALevel(uint8_t level, bool lnaEnable = 1);
+
+    /**
+     * Fetches the current @ref PALevel.
+     *
+     * @return One of the values defined by @ref rf24_pa_dbm_e.<br>
+     * See tables in @ref rf24_pa_dbm_e or setPALevel()
+     */
+    uint8_t getPALevel(void);
+
+    /**
+     * Returns automatic retransmission count (ARC_CNT)
+     *
+     * Value resets with each new transmission. Allows roughly estimating signal strength.
+     *
+     * @return Returns values from 0 to 15.
+     */
+    uint8_t getARC(void);
+
+    /**
+     * Set the transmission @ref Datarate
+     *
+     * @warning setting @ref RF24_250KBPS will fail for non-plus modules (when
+     * isPVariant() returns false).
+     *
+     * @param speed Specify one of the following values (as defined by
+     * @ref rf24_datarate_e):
+     * | @p speed (enum value) | description  |
+     * |:---------------------:|:------------:|
+     * | @ref RF24_1MBPS (0)   | for 1 Mbps   |
+     * | @ref RF24_2MBPS (1)   | for 2 Mbps   |
+     * | @ref RF24_250KBPS (2) | for 250 kbps |
+     *
+     * @return true if the change was successful
+     */
+    bool setDataRate(rf24_datarate_e speed);
+
+    /**
+     * Fetches the currently configured transmission @ref Datarate
+     *
+     * @return One of the values defined by @ref rf24_datarate_e.<br>
+     * See table in @ref rf24_datarate_e or setDataRate()
+     */
+    rf24_datarate_e getDataRate(void);
+
+    /**
+     * Set the @ref CRCLength (in bits)
+     * <br>CRC cannot be disabled if auto-ack is enabled
+     * @param length Specify one of the values (as defined by @ref rf24_crclength_e)
+     * | @p length (enum value)     | description                    |
+     * |:--------------------------:|:------------------------------:|
+     * | @ref RF24_CRC_DISABLED (0) | to disable using CRC checksums |
+     * | @ref RF24_CRC_8 (1)        | to use 8-bit checksums         |
+     * | @ref RF24_CRC_16 (2)       | to use 16-bit checksums        |
+     */
+    void setCRCLength(rf24_crclength_e length);
+
+    /**
+     * Get the @ref CRCLength (in bits)
+     * <br>CRC checking cannot be disabled if auto-ack is enabled
+     * @return One of the values defined by @ref rf24_crclength_e.<br>
+     * See table in @ref rf24_crclength_e or setCRCLength()
+     */
+    rf24_crclength_e getCRCLength(void);
+
+    /**
+     * Disable CRC validation
+     *
+     * @warning CRC cannot be disabled if auto-ack/ESB is enabled.
+     */
+    void disableCRC(void);
+
+    /**
+     * This function is used to configure what events will trigger the Interrupt
+     * Request (IRQ) pin active LOW.
+     * The following events can be configured:
+     * 1. "data sent": This does not mean that the data transmitted was
+     * recieved, only that the attempt to send it was complete.
+     * 2. "data failed": This means the data being sent was not recieved. This
+     * event is only triggered when the auto-ack feature is enabled.
+     * 3. "data received": This means that data from a receiving payload has
+     * been loaded into the RX FIFO buffers. Remember that there are only 3
+     * levels available in the RX FIFO buffers.
+     *
+     * By default, all events are configured to trigger the IRQ pin active LOW.
+     * When the IRQ pin is active, use whatHappened() to determine what events
+     * triggered it. Remeber that calling whatHappened() also clears these
+     * events' status, and the IRQ pin will then be reset to inactive HIGH.
+     *
+     * The following code configures the IRQ pin to only reflect the "data received"
+     * event:
+     * @code
+     * radio.maskIRQ(1, 1, 0);
+     * @endcode
+     *
+     * @param tx_ok  `true` ignores the "data sent" event, `false` reflects the
+     * "data sent" event on the IRQ pin.
+     * @param tx_fail  `true` ignores the "data failed" event, `false` reflects the
+     * "data failed" event on the IRQ pin.
+     * @param rx_ready `true` ignores the "data received" event, `false` reflects the
+     * "data received" event on the IRQ pin.
+     */
+    void maskIRQ(bool tx_ok, bool tx_fail, bool rx_ready);
+
+    /**
+     *
+     * The driver will delay for this duration when stopListening() is called
+     *
+     * When responding to payloads, faster devices like ARM(RPi) are much faster than Arduino:
+     * 1. Arduino sends data to RPi, switches to RX mode
+     * 2. The RPi receives the data, switches to TX mode and sends before the Arduino radio is in RX mode
+     * 3. If AutoACK is disabled, this can be set as low as 0. If AA/ESB enabled, set to 100uS minimum on RPi
+     *
+     * @warning If set to 0, ensure 130uS delay after stopListening() and before any sends
+     */
+    uint32_t txDelay;
+
+    /**
+     *
+     * On all devices but Linux and ATTiny, a small delay is added to the CSN toggling function
+     *
+     * This is intended to minimise the speed of SPI polling due to radio commands
+     *
+     * If using interrupts or timed requests, this can be set to 0 Default:5
+     */
+    uint32_t csDelay;
+
+    /**
+     * Transmission of constant carrier wave with defined frequency and output power
+     *
+     * @param level Output power to use
+     * @param channel The channel to use
+     *
+     * @warning If isPVariant() returns true, then this function takes extra
+     * measures that alter some settings. These settings alterations include:
+     * - setAutoAck() to false (for all pipes)
+     * - setRetries() to retry `0` times with a delay of 250 microseconds
+     * - set the TX address to 5 bytes of `0xFF`
+     * - flush_tx()
+     * - load a 32 byte payload of `0xFF` into the TX FIFO's top level
+     * - disableCRC()
+     */
+    void startConstCarrier(rf24_pa_dbm_e level, uint8_t channel);
+
+    /**
+     * Stop transmission of constant wave and reset PLL and CONT registers
+     *
+     * @warning this function will powerDown() the radio per recommendation of
+     * datasheet.
+     * @note If isPVariant() returns true, please remember to re-configure the radio's settings
+     * @code
+     * // re-establish default settings
+     * setCRCLength(RF24_CRC_16);
+     * setAutoAck(true);
+     * setRetries(5, 15);
+     * @endcode
+     * @see startConstCarrier()
+     */
+    void stopConstCarrier(void);
+
+    /**
+     * @brief Open or close all data pipes.
+     *
+     * This function does not alter the addresses assigned to pipes. It is simply a
+     * convenience function that allows controling all pipes at once.
+     * @param isEnabled `true` opens all pipes; `false` closes all pipes.
+     */
+    void toggleAllPipes(bool isEnabled);
+
+    /**
+     * @brief configure the RF_SETUP register in 1 transaction
+     * @param level This parameter is the same input as setPALevel()'s `level` parameter.
+     * See @ref rf24_pa_dbm_e enum for accepted values.
+     * @param speed This parameter is the same input as setDataRate()'s `speed` parameter.
+     * See @ref rf24_datarate_e enum for accepted values.
+     * @param lnaEnable This optional parameter is the same as setPALevel()'s `lnaEnable`
+     * optional parameter. Defaults to `true` (meaning LNA feature is enabled) when not specified.
+     */
+    void setRadiation(uint8_t level, rf24_datarate_e speed, bool lnaEnable = true);
+
+    /**@}*/
+    /**
+     * @name Deprecated
+     *
+     *  Methods provided for backwards compabibility.
+     */
+    /**@{*/
+
+    /**
+     * Open a pipe for reading
+     * @deprecated For compatibility with old code only, see newer function
+     * openReadingPipe()
+     *
+     * @warning Pipes 1-5 should share the first 32 bits.
+     * Only the least significant byte should be unique, e.g.
+     * @code
+     * openReadingPipe(1, 0xF0F0F0F0AA);
+     * openReadingPipe(2, 0xF0F0F0F066);
+     * @endcode
+     *
+     * @warning Pipe 0 is also used by the writing pipe so should typically be avoided as a reading pipe.<br>
+     * If used, the reading pipe 0 address needs to be restored at every call to startListening().
+     * <br> See http://maniacalbits.blogspot.com/2013/04/rf24-addressing-nrf24l01-radios-require.html
+     *
+     * @param number Which pipe# to open, 0-5.
+     * @param address The 40-bit address of the pipe to open.
+     */
+    void openReadingPipe(uint8_t number, uint64_t address);
+
+    /**
+     * Open a pipe for writing
+     * @deprecated For compatibility with old code only, see newer function
+     * openWritingPipe()
+     *
+     * Addresses are 40-bit hex values, e.g.:
+     *
+     * @code
+     * openWritingPipe(0xF0F0F0F0F0);
+     * @endcode
+     *
+     * @param address The 40-bit address of the pipe to open.
+     */
+    void openWritingPipe(uint64_t address);
+
+    /**
+     * Determine if an ack payload was received in the most recent call to
+     * write(). The regular available() can also be used.
+     *
+     * @deprecated For compatibility with old code only, see synonomous function available().
+     * Use read() to retrieve the ack payload and getDynamicPayloadSize() to get the ACK payload size.
+     *
+     * @return True if an ack payload is available.
+     */
+    bool isAckPayloadAvailable(void);
+
+private:
+
+    /**@}*/
+    /**
+     * @name Low-level internal interface.
+     *
+     *  Protected methods that address the chip directly.  Regular users cannot
+     *  ever call these.  They are documented for completeness and for developers who
+     *  may want to extend this class.
+     */
+    /**@{*/
+
+    /**
+     * initializing function specific to all constructors
+     * (regardless of constructor parameters)
+     */
+    void _init_obj();
+
+    /**
+     * initialize radio by performing a soft reset.
+     * @warning This function assumes the SPI bus object's begin() method has been
+     * previously called.
+     */
+    bool _init_radio();
+
+    /**
+     * initialize the GPIO pins
+     */
+    bool _init_pins();
+
+    /**
+     * Set chip select pin
+     *
+     * Running SPI bus at PI_CLOCK_DIV2 so we don't waste time transferring data
+     * and best of all, we make use of the radio's FIFO buffers. A lower speed
+     * means we're less likely to effectively leverage our FIFOs and pay a higher
+     * AVR runtime cost as toll.
+     *
+     * @param mode HIGH to take this unit off the SPI bus, LOW to put it on
+     */
+    void csn(bool mode);
+
+    /**
+     * Set chip enable
+     *
+     * @param level HIGH to actively begin transmission or LOW to put in standby.  Please see data sheet
+     * for a much more detailed description of this pin.
+     */
+    void ce(bool level);
+
+    /**
+     * Write a chunk of data to a register
+     *
+     * @param reg Which register. Use constants from nRF24L01.h
+     * @param buf Where to get the data
+     * @param len How many bytes of data to transfer
+     * @return Nothing. Older versions of this function returned the status
+     * byte, but that it now saved to a private member on all SPI transactions.
+     */
+    void write_register(uint8_t reg, const uint8_t* buf, uint8_t len);
+
+    /**
+     * Write a single byte to a register
+     *
+     * @param reg Which register. Use constants from nRF24L01.h
+     * @param value The new value to write
+     * @param is_cmd_only if this parameter is true, then the `reg` parameter
+     * is written, and the `value` param is ignored.
+     * @return Nothing. Older versions of this function returned the status
+     * byte, but that it now saved to a private member on all SPI transactions.
+     */
+    void write_register(uint8_t reg, uint8_t value, bool is_cmd_only = false);
+
+    /**
+     * Write the transmit payload
+     *
+     * The size of data written is the fixed payload size, see getPayloadSize()
+     *
+     * @param buf Where to get the data
+     * @param len Number of bytes to be sent
+     * @param writeType Specify if individual payload should be acknowledged
+     * @return Nothing. Older versions of this function returned the status
+     * byte, but that it now saved to a private member on all SPI transactions.
+     */
+    void write_payload(const void* buf, uint8_t len, const uint8_t writeType);
+
+    /**
+     * Read the receive payload
+     *
+     * The size of data read is the fixed payload size, see getPayloadSize()
+     *
+     * @param buf Where to put the data
+     * @param len Maximum number of bytes to read
+     * @return Nothing. Older versions of this function returned the status
+     * byte, but that it now saved to a private member on all SPI transactions.
+     */
+    void read_payload(void* buf, uint8_t len);
+
+    /**
+     * Retrieve the current status of the chip
+     *
+     * @return Current value of status register
+     */
+    uint8_t get_status(void);
+
+    #if !defined (MINIMAL)
+
+    /**
+     * Decode and print the given status to stdout
+     *
+     * @param status Status value to print
+     *
+     * @warning Does nothing if stdout is not defined.  See fdevopen in stdio.h
+     */
+    void print_status(uint8_t status);
+
+    /**
+     * Decode and print the given 'observe_tx' value to stdout
+     *
+     * @param value The observe_tx value to print
+     *
+     * @warning Does nothing if stdout is not defined.  See fdevopen in stdio.h
+     */
+    void print_observe_tx(uint8_t value);
+
+    /**
+     * Print the name and value of an 8-bit register to stdout
+     *
+     * Optionally it can print some quantity of successive
+     * registers on the same line.  This is useful for printing a group
+     * of related registers on one line.
+     *
+     * @param name Name of the register
+     * @param reg Which register. Use constants from nRF24L01.h
+     * @param qty How many successive registers to print
+     */
+    void print_byte_register(const char* name, uint8_t reg, uint8_t qty = 1);
+
+    /**
+     * Print the name and value of a 40-bit address register to stdout
+     *
+     * Optionally it can print some quantity of successive
+     * registers on the same line.  This is useful for printing a group
+     * of related registers on one line.
+     *
+     * @param name Name of the register
+     * @param reg Which register. Use constants from nRF24L01.h
+     * @param qty How many successive registers to print
+     */
+    void print_address_register(const char* name, uint8_t reg, uint8_t qty = 1);
+
+    #endif
+
+    /**
+     * Turn on or off the special features of the chip
+     *
+     * The chip has certain 'features' which are only available when the 'features'
+     * are enabled.  See the datasheet for details.
+     */
+    void toggle_features(void);
+
+    #if defined (FAILURE_HANDLING) || defined (RF24_LINUX)
+
+    void errNotify(void);
+
+    #endif
+
+    /**
+     * @brief Manipulate the @ref Datarate and txDelay
+     *
+     * This is a helper function to setRadiation() and setDataRate()
+     * @param speed The desired data rate.
+     */
+    inline uint8_t _data_rate_reg_value(rf24_datarate_e speed);
+
+    /**
+     * @brief Manipulate the @ref PALevel
+     *
+     * This is a helper function to setRadiation() and setPALevel()
+     * @param level The desired @ref PALevel.
+     * @param lnaEnable Toggle the LNA feature.
+     */
+    inline uint8_t _pa_level_reg_value(uint8_t level, bool lnaEnable);
+
+    /**@}*/
+
+};
+
+
+/**
+ * @example{lineno} examples/GettingStarted/GettingStarted.ino
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * A simple example of sending data from 1 nRF24L01 transceiver to another.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use the Serial Monitor to change each node's behavior.
+ */
+
+/**
+ * @example{lineno} examples/AcknowledgementPayloads/AcknowledgementPayloads.ino
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * A simple example of sending data from 1 nRF24L01 transceiver to another
+ * with Acknowledgement (ACK) payloads attached to ACK packets.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use the Serial Monitor to change each node's behavior.
+ */
+
+/**
+ * @example{lineno} examples/ManualAcknowledgements/ManualAcknowledgements.ino
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * A simple example of sending data from 1 nRF24L01 transceiver to another
+ * with manually transmitted (non-automatic) Acknowledgement (ACK) payloads.
+ * This example still uses ACK packets, but they have no payloads. Instead the
+ * acknowledging response is sent with `write()`. This tactic allows for more
+ * updated acknowledgement payload data, where actual ACK payloads' data are
+ * outdated by 1 transmission because they have to loaded before receiving a
+ * transmission.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use the Serial Monitor to change each node's behavior.
+ */
+
+/**
+ * @example{lineno} examples/StreamingData/StreamingData.ino
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * A simple example of streaming data from 1 nRF24L01 transceiver to another.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use the Serial Monitor to change each node's behavior.
+ */
+
+/**
+ * @example{lineno} examples/MulticeiverDemo/MulticeiverDemo.ino
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * A simple example of sending data from as many as 6 nRF24L01 transceivers to
+ * 1 receiving transceiver. This technique is trademarked by
+ * Nordic Semiconductors as "MultiCeiver".
+ *
+ * This example was written to be used on up to 6 devices acting as TX nodes &
+ * only 1 device acting as the RX node (that's a maximum of 7 devices).
+ * Use the Serial Monitor to change each node's behavior.
+ */
+
+/**
+ * @example{lineno} examples/InterruptConfigure/InterruptConfigure.ino
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * This example uses Acknowledgement (ACK) payloads attached to ACK packets to
+ * demonstrate how the nRF24L01's IRQ (Interrupt Request) pin can be
+ * configured to detect when data is received, or when data has transmitted
+ * successfully, or when data has failed to transmit.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use the Serial Monitor to change each node's behavior.
+ */
+
+/**
+ * @example{lineno} examples/old_backups/GettingStarted_HandlingFailures/GettingStarted_HandlingFailures.ino
+ * Written by [TMRh20](http://github.com/TMRh20) in 2019
+ *
+ * This example demonstrates the basic getting started functionality, but with
+ * failure handling for the radio chip. Addresses random radio failures etc,
+ * potentially due to loose wiring on breadboards etc.
+ */
+
+/**
+ * @example{lineno} examples/old_backups/TransferTimeouts/TransferTimeouts.ino
+ * Written by [TMRh20](https://github.com/TMRh20)
+ *
+ * This example demonstrates the use of and extended timeout period and
+ * auto-retries/auto-reUse to increase reliability in noisy or low signal scenarios.
+ *
+ * Write this sketch to two different nodes.  Put one of the nodes into 'transmit'
+ * mode by connecting with the serial monitor and sending a 'T'.  The data <br>
+ * transfer will begin, with the receiver displaying the payload count and the
+ * data transfer rate.
+ */
+
+/**
+ * @example{lineno} examples/old_backups/pingpair_irq/pingpair_irq.ino
+ * Updated by [TMRh20](https://github.com/TMRh20)
+ *
+ * This is an example of how to user interrupts to interact with the radio, and a demonstration
+ * of how to use them to sleep when receiving, and not miss any payloads.<br>
+ * The pingpair_sleepy example expands on sleep functionality with a timed sleep option for the transmitter.
+ * Sleep functionality is built directly into my fork of the RF24Network library<br>
+ */
+
+/**
+ * @example{lineno} examples/old_backups/pingpair_sleepy/pingpair_sleepy.ino
+ * Updated by [TMRh20](https://github.com/TMRh20)
+ *
+ * This is an example of how to use the RF24 class to create a battery-
+ * efficient system.  It is just like the GettingStarted_CallResponse example, but the<br>
+ * ping node powers down the radio and sleeps the MCU after every
+ * ping/pong cycle, and the receiver sleeps between payloads. <br>
+ */
+
+/**
+ * @example{lineno} examples/rf24_ATTiny/rf24ping85/rf24ping85.ino
+ * <b>2014 Contribution by [tong67](https://github.com/tong67)</b><br>
+ * Updated 2020 by [2bndy5](http://github.com/2bndy5) for the
+ * [SpenceKonde ATTinyCore](https://github.com/SpenceKonde/ATTinyCore)<br>
+ * The RF24 library uses the [ATTinyCore by
+ * SpenceKonde](https://github.com/SpenceKonde/ATTinyCore)
+ *
+ * This sketch is a duplicate of the ManualAcknowledgements.ino example
+ * (without all the Serial input/output code), and it demonstrates
+ * a ATTiny25/45/85 or ATTiny24/44/84 driving the nRF24L01 transceiver using
+ * the RF24 class to communicate with another node.
+ *
+ * A simple example of sending data from 1 nRF24L01 transceiver to another
+ * with manually transmitted (non-automatic) Acknowledgement (ACK) payloads.
+ * This example still uses ACK packets, but they have no payloads. Instead the
+ * acknowledging response is sent with `write()`. This tactic allows for more
+ * updated acknowledgement payload data, where actual ACK payloads' data are
+ * outdated by 1 transmission because they have to loaded before receiving a
+ * transmission.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ */
+
+/**
+ * @example{lineno} examples/rf24_ATTiny/timingSearch3pin/timingSearch3pin.ino
+ * <b>2014 Contribution by [tong67](https://github.com/tong67)</b><br>
+ * Updated 2020 by [2bndy5](http://github.com/2bndy5) for the
+ * [SpenceKonde ATTinyCore](https://github.com/SpenceKonde/ATTinyCore)<br>
+ * The RF24 library uses the [ATTinyCore by
+ * SpenceKonde](https://github.com/SpenceKonde/ATTinyCore)
+ *
+ * This sketch can be used to determine the best settle time values to use for
+ * RF24::csDelay in RF24::csn() (private function).
+ * @see RF24::csDelay
+ *
+ * The settle time values used here are 100/20. However, these values depend
+ * on the actual used RC combiniation and voltage drop by LED. The
+ * intermediate results are written to TX (PB3, pin 2 -- using Serial).
+ *
+ * For schematic details, see introductory comment block in the rf24ping85.ino sketch.
+ */
+
+/**
+ * @example{lineno} examples/old_backups/pingpair_dyn/pingpair_dyn.ino
+ *
+ * This is an example of how to use payloads of a varying (dynamic) size on Arduino.
+ */
+
+/**
+ * @example{lineno} examples_linux/getting_started.py
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * This is a simple example of using the RF24 class on a Raspberry Pi.
+ *
+ * Remember to install the [Python wrapper](md_docs_python_wrapper.html), then
+ * navigate to the "RF24/examples_linux" folder.
+ * <br>To run this example, enter
+ * @code{.sh}python3 getting_started.py @endcode and follow the prompts.
+ *
+ * @note this example requires python v3.7 or newer because it measures
+ * transmission time with `time.monotonic_ns()`.
+ */
+
+/**
+ * @example{lineno} examples_linux/acknowledgement_payloads.py
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * This is a simple example of using the RF24 class on a Raspberry Pi to
+ * transmit and retrieve custom automatic acknowledgment payloads.
+ *
+ * Remember to install the [Python wrapper](md_docs_python_wrapper.html), then
+ * navigate to the "RF24/examples_linux" folder.
+ * <br>To run this example, enter
+ * @code{.sh}python3 acknowledgement_payloads.py @endcode and follow the prompts.
+ *
+ * @note this example requires python v3.7 or newer because it measures
+ * transmission time with `time.monotonic_ns()`.
+ */
+
+/**
+ * @example{lineno} examples_linux/manual_acknowledgements.py
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * This is a simple example of using the RF24 class on a Raspberry Pi to
+ * transmit and respond with acknowledgment (ACK) transmissions. Notice that
+ * the auto-ack feature is enabled, but this example doesn't use automatic ACK
+ * payloads because automatic ACK payloads' data will always be outdated by 1
+ * transmission. Instead, this example uses a call and response paradigm.
+ *
+ * Remember to install the [Python wrapper](md_docs_python_wrapper.html), then
+ * navigate to the "RF24/examples_linux" folder.
+ * <br>To run this example, enter
+ * @code{.sh}python3 manual_acknowledgements.py @endcode and follow the prompts.
+ *
+ * @note this example requires python v3.7 or newer because it measures
+ * transmission time with `time.monotonic_ns()`.
+ */
+
+/**
+ * @example{lineno} examples_linux/streaming_data.py
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * This is a simple example of using the RF24 class on a Raspberry Pi for
+ * streaming multiple payloads.
+ *
+ * Remember to install the [Python wrapper](md_docs_python_wrapper.html), then
+ * navigate to the "RF24/examples_linux" folder.
+ * <br>To run this example, enter
+ * @code{.sh}python3 streaming_data.py @endcode and follow the prompts.
+ *
+ * @note this example requires python v3.7 or newer because it measures
+ * transmission time with `time.monotonic_ns()`.
+ */
+
+/**
+ * @example{lineno} examples_linux/interrupt_configure.py
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * This is a simple example of using the RF24 class on a Raspberry Pi to
+ * detecting (and verifying) the IRQ (interrupt) pin on the nRF24L01.
+ *
+ * Remember to install the [Python wrapper](md_docs_python_wrapper.html), then
+ * navigate to the "RF24/examples_linux" folder.
+ * <br>To run this example, enter
+ * @code{.sh}python3 interrupt_configure.py @endcode and follow the prompts.
+ *
+ * @note this example requires python v3.7 or newer because it measures
+ * transmission time with `time.monotonic_ns()`.
+ */
+
+/**
+ * @example{lineno} examples_linux/multiceiver_demo.py
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * This is a simple example of using the RF24 class on a Raspberry Pi for
+ * using 1 nRF24L01 to receive data from up to 6 other transceivers. This
+ * technique is called "multiceiver" in the datasheet.
+ *
+ * Remember to install the [Python wrapper](md_docs_python_wrapper.html), then
+ * navigate to the "RF24/examples_linux" folder.
+ * <br>To run this example, enter
+ * @code{.sh}python3 multiceiver_demo.py @endcode and follow the prompts.
+ *
+ * @note this example requires python v3.7 or newer because it measures
+ * transmission time with `time.monotonic_ns()`.
+ */
+
+/**
+ * @example{lineno} examples_linux/scanner.cpp
+ *
+ * Example to detect interference on the various channels available.
+ * This is a good diagnostic tool to check whether you're picking a
+ * good channel for your application.
+ *
+ * Inspired by cpixip.
+ * See http://arduino.cc/forum/index.php/topic,54795.0.html
+ *
+ * Use ctrl+C to exit
+ */
+
+/**
+ * @example{lineno} examples/scanner/scanner.ino
+ *
+ * Example to detect interference on the various channels available.
+ * This is a good diagnostic tool to check whether you're picking a
+ * good channel for your application.
+ *
+ * Inspired by cpixip.
+ * See http://arduino.cc/forum/index.php/topic,54795.0.html
+ */
+
+/**
+ * @example{lineno} examples_linux/gettingstarted.cpp
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * A simple example of sending data from 1 nRF24L01 transceiver to another.
+ *
+ * This example was written  * This example was written to be used on up to 6 devices acting as TX nodes &
+ * only 1 device acting as the RX node (that's a maximum of 7 devices).
+ acting as "nodes".
+ * Use `ctrl+c` to quit at any time.
+ */
+
+/**
+ * @example{lineno} examples_linux/acknowledgementPayloads.cpp
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * A simple example of sending data from 1 nRF24L01 transceiver to another
+ * with Acknowledgement (ACK) payloads attached to ACK packets.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use `ctrl+c` to quit at any time.
+ */
+
+/**
+ * @example{lineno} examples_linux/manualAcknowledgements.cpp
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * A simple example of sending data from 1 nRF24L01 transceiver to another
+ * with manually transmitted (non-automatic) Acknowledgement (ACK) payloads.
+ * This example still uses ACK packets, but they have no payloads. Instead the
+ * acknowledging response is sent with `write()`. This tactic allows for more
+ * updated acknowledgement payload data, where actual ACK payloads' data are
+ * outdated by 1 transmission because they have to loaded before receiving a
+ * transmission.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use `ctrl+c` to quit at any time.
+ */
+
+/**
+ * @example{lineno} examples_linux/streamingData.cpp
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * A simple example of sending data from 1 nRF24L01 transceiver to another.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use `ctrl+c` to quit at any time.
+ */
+
+/**
+ * @example{lineno} examples_linux/multiceiverDemo.cpp
+ * Written by [2bndy5](http://github.com/2bndy5) in 2020
+ *
+ * A simple example of sending data from as many as 6 nRF24L01 transceivers to
+ * 1 receiving transceiver. This technique is trademarked by
+ * Nordic Semiconductors as "MultiCeiver".
+ *
+ * This example was written to be used on up to 6 devices acting as TX nodes &
+ * only 1 device acting as the RX node (that's a maximum of 7 devices).
+ * Use `ctrl+c` to quit at any time.
+ */
+
+#endif // __RF24_H__
diff --git a/arduino/libraries/RF24/RF24_config.h b/arduino/libraries/RF24/RF24_config.h
new file mode 100644
index 000000000..cb7d638e0
--- /dev/null
+++ b/arduino/libraries/RF24/RF24_config.h
@@ -0,0 +1,220 @@
+
+/*
+ Copyright (C)
+    2011            J. Coliz <maniacbug ymail com>
+    2015-2019       TMRh20
+    2015            spaniakos <spaniakos gmail com>
+    2015            nerdralph
+    2015            zador-blood-stained
+    2016            akatran
+    2017-2019       Avamander <avamander gmail com>
+    2019            IkpeohaGodson
+    2021            2bndy5
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+*/
+
+#ifndef __RF24_CONFIG_H__
+#define __RF24_CONFIG_H__
+
+/*** USER DEFINES:    ***/
+#define FAILURE_HANDLING
+//#define SERIAL_DEBUG
+//#define MINIMAL
+//#define SPI_UART    // Requires library from https://github.com/TMRh20/Sketches/tree/master/SPI_UART
+//#define SOFTSPI     // Requires library from https://github.com/greiman/DigitalIO
+
+/**
+ * User access to internally used delay time (in microseconds) during RF24::powerUp()
+ * @warning This default value compensates for all supported hardware. Only adjust this if you
+ * know your radio's hardware is, in fact, genuine and reliable.
+ */
+#if !defined(RF24_POWERUP_DELAY)
+#define RF24_POWERUP_DELAY     5000
+#endif
+
+/**********************/
+#define rf24_max(a, b) (a>b?a:b)
+#define rf24_min(a, b) (a<b?a:b)
+
+/** @brief The default SPI speed (in Hz) */
+#ifndef RF24_SPI_SPEED
+#define RF24_SPI_SPEED 10000000
+#endif
+
+//ATXMega
+#if defined (__AVR_ATxmega64D3__) || defined (__AVR_ATxmega128D3__) || defined (__AVR_ATxmega192D3__) || 
defined (__AVR_ATxmega256D3__) || defined (__AVR_ATxmega384D3__)
+// In order to be available both in Windows and Linux this should take presence here.
+    #define XMEGA
+    #define XMEGA_D3
+    #include "utility/ATXMegaD3/RF24_arch_config.h"
+
+// RaspberryPi rp2xxx-based devices (e.g. RPi Pico board)
+#elif defined (PICO_BUILD) && !defined (ARDUINO)
+    #include "utility/rp2/RF24_arch_config.h"
+
+#elif (!defined (ARDUINO)) // Any non-arduino device is handled via configure/Makefile
+    // The configure script detects device and copies the correct includes.h file to /utility/includes.h
+    // This behavior can be overridden by calling configure with respective parameters
+    // The includes.h file defines either RF24_RPi, MRAA, LITTLEWIRE or RF24_SPIDEV and includes the correct 
RF24_arch_config.h file
+    #include "utility/includes.h"
+
+//ATTiny
+#elif defined (__AVR_ATtiny25__) || defined (__AVR_ATtiny45__) || defined (__AVR_ATtiny85__) || defined 
(__AVR_ATtiny24__) || defined (__AVR_ATtiny44__) || defined (__AVR_ATtiny84__) || defined 
(__AVR_ATtiny2313__) || defined (__AVR_ATtiny4313__) || defined (__AVR_ATtiny861__) || defined 
(__AVR_ATtinyX5__) || defined (__AVR_ATtinyX4__) || defined (__AVR_ATtinyX313__) || defined 
(__AVR_ATtinyX61__)
+    #define RF24_TINY
+    #include "utility/ATTiny/RF24_arch_config.h"
+
+#elif defined (LITTLEWIRE) //LittleWire
+    #include "utility/LittleWire/RF24_arch_config.h"
+
+#elif defined (TEENSYDUINO) //Teensy
+    #include "utility/Teensy/RF24_arch_config.h"
+
+#else //Everything else
+    #include <Arduino.h>
+
+
+    #if defined (ARDUINO) && !defined (__arm__) && !defined (__ARDUINO_X86__)
+        #if defined SPI_UART
+            #include <SPI_UART.h>
+            #define _SPI uspi
+        #elif defined (SOFTSPI)
+            // change these pins to your liking
+            //
+            #ifndef SOFT_SPI_MISO_PIN
+                #define SOFT_SPI_MISO_PIN 9
+            #endif // SOFT_SPI_MISO_PIN
+
+            #ifndef SOFT_SPI_MOSI_PIN
+                #define SOFT_SPI_MOSI_PIN 8
+            #endif // SOFT_SPI_MOSI_PIN
+
+            #ifndef SOFT_SPI_SCK_PIN
+                #define SOFT_SPI_SCK_PIN 7
+            #endif // SOFT_SPI_SCK_PIN
+
+            const uint8_t SPI_MODE = 0;
+            #define _SPI spi
+
+        #elif defined (ARDUINO_SAM_DUE)
+            #include <SPI.h>
+            #define _SPI SPI
+
+        #else // !defined (SPI_UART) && !defined (SOFTSPI)
+            #include <SPI.h>
+            #define _SPI SPIClass
+            #define RF24_SPI_PTR
+        #endif // !defined (SPI_UART) && !defined (SOFTSPI)
+
+    #else // !defined(ARDUINO) || defined (__arm__) || defined (__ARDUINO_X86__)
+        // Define _BV for non-Arduino platforms and for Arduino DUE
+        #include <stdint.h>
+        #include <stdio.h>
+        #include <string.h>
+
+        #if defined (__arm__) || defined (__ARDUINO_X86__)
+            #if defined (__arm__) && defined (SPI_UART)
+                #include <SPI_UART.h>
+                #define _SPI uspi
+
+            #else // !defined (__arm__) || !defined (SPI_UART)
+                #include <SPI.h>
+                #define _SPI SPIClass
+                #define RF24_SPI_PTR
+
+            #endif // !defined (__arm__) || !defined (SPI_UART)
+        #elif !defined(__arm__) && !defined (__ARDUINO_X86__)
+            // fallback to unofficially supported Hardware (courtesy of ManiacBug)
+            extern HardwareSPI SPI;
+            #define _SPI HardwareSPI
+            #define RF24_SPI_PTR
+
+        #endif // !defined(__arm__) && !defined (__ARDUINO_X86__)
+
+        #ifndef _BV
+            #define _BV(x) (1<<(x))
+        #endif
+    #endif // defined (ARDUINO) && !defined (__arm__) && !defined (__ARDUINO_X86__)
+
+    #ifdef SERIAL_DEBUG
+        #define IF_SERIAL_DEBUG(x) ({x;})
+    #else
+        #define IF_SERIAL_DEBUG(x)
+        #if defined(RF24_TINY)
+            #define printf_P(...)
+
+        #endif // defined(RF24_TINY)
+    #endif // SERIAL_DEBUG
+
+    #if defined (__ARDUINO_X86__)
+        #define printf_P printf
+        #define _BV(bit) (1<<(bit))
+
+    #endif // defined (__ARDUINO_X86__)
+
+    // Progmem is Arduino-specific
+    #if defined (ARDUINO_ARCH_ESP8266) || defined (ESP32)
+        #include <pgmspace.h>
+        #define PRIPSTR "%s"
+        #ifndef pgm_read_ptr
+            #define pgm_read_ptr(p) (*(p))
+        #endif
+    #elif defined (ARDUINO) && !defined (ESP_PLATFORM) && !defined (__arm__) && !defined (__ARDUINO_X86__) 
|| defined (XMEGA)
+        #include <avr/pgmspace.h>
+        #define PRIPSTR "%S"
+
+    #else // !defined (ARDUINO) || defined (ESP_PLATFORM) || defined (__arm__) || defined (__ARDUINO_X86__) 
&& !defined (XMEGA)
+        #if !defined (ARDUINO) // This doesn't work on Arduino DUE
+            typedef char const char;
+        #else // Fill in pgm_read_byte that is used
+            #if defined (ARDUINO_ARCH_AVR) || defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_SAM_DUE)
+                #include <avr/pgmspace.h> // added to ArduinoCore-sam (Due core) in 2013
+            #endif
+
+            // Since the official arduino/ArduinoCore-samd repo switched to a unified API in 2016,
+            // Serial.printf() is no longer defined in the unifying Arduino/ArduinoCore-API repo
+            #if defined (ARDUINO_ARCH_SAMD) && defined (ARDUINO_SAMD_ADAFRUIT)
+                // it is defined if using the adafruit/ArduinoCore-samd repo
+                #define printf_P Serial.printf
+            #endif // defined (ARDUINO_ARCH_SAMD)
+
+            #ifndef pgm_read_byte
+                #define pgm_read_byte(addr) (*(const unsigned char *)(addr))
+            #endif
+        #endif // !defined (ARDUINO)
+
+        #ifndef prog_uint16_t
+            typedef uint16_t prog_uint16_t;
+        #endif
+        #ifndef PSTR
+            #define PSTR(x) (x)
+        #endif
+        #ifndef printf_P
+            #define printf_P printf
+        #endif
+        #ifndef strlen_P
+            #define strlen_P strlen
+        #endif
+        #ifndef PROGMEM
+            #define PROGMEM
+        #endif
+        #ifndef pgm_read_word
+            #define pgm_read_word(p) (*(p))
+        #endif
+        #if !defined pgm_read_ptr || defined ARDUINO_ARCH_MBED
+            #define pgm_read_ptr(p) (*(p))
+        #endif
+        #ifndef PRIPSTR
+            #define PRIPSTR "%s"
+        #endif
+
+    #endif // !defined (ARDUINO) || defined (ESP_PLATFORM) || defined (__arm__) || defined (__ARDUINO_X86__) 
&& !defined (XMEGA)
+#endif //Everything else
+
+#if defined (SPI_HAS_TRANSACTION) && !defined (SPI_UART) && !defined (SOFTSPI)
+    #define RF24_SPI_TRANSACTIONS
+#endif // defined (SPI_HAS_TRANSACTION) && !defined (SPI_UART) && !defined (SOFTSPI)
+
+#endif // __RF24_CONFIG_H__
diff --git a/arduino/libraries/RF24/cmake/AutoConfig_RF24_DRIVER.cmake 
b/arduino/libraries/RF24/cmake/AutoConfig_RF24_DRIVER.cmake
new file mode 100644
index 000000000..34f1a043e
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/AutoConfig_RF24_DRIVER.cmake
@@ -0,0 +1,45 @@
+set(RF24_LINKED_DRIVER "")
+set(RF24_DRIVER "UNKNOWN" CACHE STRING "override automatic configuration of RF24's utility driver.
+    Specify 1 of the following supported drivers (ie -DRF24_DRIVER=SPIDEV):
+    wiringPi
+    RPi
+    SPIDEV
+    MRAA
+    LittleWire"
+)
+
+###########################
+# detect pre-existing (locally installed) 3rd party libraries
+###########################
+
+# detect installed libraries despite what RF24_DRIVER is set to
+# this is always done for cross-compiling purposes
+find_library(LibMRAA mraa)
+find_library(LibWiringPi wiringPi)
+find_library(LibLittleWire littlewire-spi)
+if(EXISTS /dev/spidev0.0)
+    set(SPIDEV_EXISTS TRUE)
+else()
+    set(SPIDEV_EXISTS FALSE)
+endif()
+
+
+if(${RF24_DRIVER} STREQUAL "UNKNOWN") # invokes automatic configuration
+    if("${SOC}" STREQUAL "BCM2708" OR "${SOC}" STREQUAL "BCM2709" OR "${SOC}" STREQUAL "BCM2835")
+        set(RF24_DRIVER RPi CACHE STRING "using folder /utility/RPi" FORCE)
+    elseif(NOT "${LibMRAA}" STREQUAL "LibMRAA-NOTFOUND")
+        message(STATUS "Found MRAA library: ${LibMRAA}")
+        set(RF24_DRIVER MRAA CACHE STRING "using folder /utility/MRAA" FORCE)
+    elseif(NOT "${LibWiringPi}" STREQUAL "LibWiringPi-NOTFOUND")
+        message(STATUS "Found wiringPi library: ${LibWiringPi}")
+        set(RF24_DRIVER wiringPi CACHE STRING "using folder /utility/wiringPi" FORCE)
+    elseif(NOT "${LibLittleWire}" STREQUAL "LibLittleWire-NOTFOUND")
+        message(STATUS "Found LittleWire library: ${LibLittleWire}")
+        set(RF24_DRIVER LittleWire CACHE STRING "using folder /utility/LittleWire" FORCE)
+    elseif(SPIDEV_EXISTS) # should be a non-empty string if SPI is enabled
+        message(STAUS "detected that SPIDEV is enabled: ${SPIDEV_EXISTS}")
+        set(RF24_DRIVER SPIDEV CACHE STRING "using folder /utility/SPIDEV" FORCE)
+    endif()
+endif()
+
+message(STATUS "Using driver: ${RF24_DRIVER}")
diff --git a/arduino/libraries/RF24/cmake/CPackInfo.cmake b/arduino/libraries/RF24/cmake/CPackInfo.cmake
new file mode 100644
index 000000000..808cdff2e
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/CPackInfo.cmake
@@ -0,0 +1,75 @@
+# This module will build a debian compatible package to install - handy for cross-compiling
+
+if(NOT PKG_REV)
+    set(PKG_REV "1")
+endif()
+
+# get target arch if not cross-compiling
+if(NOT TARGET_ARCH) # TARGET_ARCH is defined only in the toolchain_<ARCH_TYPE>.cmake files
+    execute_process(COMMAND dpkg --print-architecture
+        OUTPUT_VARIABLE TARGET_ARCH
+        OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+endif()
+
+# set the Cpack generators (specific to types of packages to create)
+set(CPACK_GENERATOR DEB RPM) # RPM requires rpmbuild executable
+
+# assemble a debian package filename from known info
+include(InstallRequiredSystemLibraries)
+set(CPACK_PACKAGE_FILE_NAME 
"lib${LibTargetName}-${RF24_DRIVER}_${${LibName}_VERSION_STRING}-${PKG_REV}_${TARGET_ARCH}")
+set(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/LICENSE")
+set(CPACK_PACKAGE_VERSION_MAJOR "${${LibName}_VERSION_MAJOR}")
+set(CPACK_PACKAGE_VERSION_MINOR "${${LibName}_VERSION_MINOR}")
+set(CPACK_PACKAGE_VERSION_PATCH "${${LibName}_VERSION_PATCH}")
+set(CPACK_PACKAGE_DIRECTORY "${CMAKE_BINARY_DIR}/pkgs") # for easy uploading to github releases
+
+###############################
+# info specific debian packages
+###############################
+set(CPACK_DEBIAN_PACKAGE_ARCHITECTURE ${TARGET_ARCH})
+set(CPACK_DEBIAN_PACKAGE_SECTION libs)
+set(CPACK_DEBIAN_PACKAGE_CONTROL_STRICT_PERMISSION TRUE)
+
+
+###############################
+# info specific rpm (fedora) packages
+###############################
+set(CPACK_RPM_FILE_NAME 
"lib${LibTargetName}-${RF24_DRIVER}-${${LibName}_VERSION_STRING}-${PKG_REV}.${TARGET_ARCH}.rpm")
+set(CPACK_RPM_PACKAGE_ARCHITECTURE ${TARGET_ARCH})
+set(CPACK_RPM_PACKAGE_LICENSE "GPLv2.0")
+set(CPACK_RPM_PACKAGE_VENDOR "Humanity")
+
+# set dependencies based on utility drivers
+if("${RF24_DRIVER}" STREQUAL "MRAA")
+    set(CPACK_DEBIAN_PACKAGE_DEPENDS mraa)
+    set(CPACK_RPM_PACKAGE_REQUIRES "mraa")
+elseif("${RF24_DRIVER}" STREQUAL "wiringPi")
+    set(CPACK_DEBIAN_PACKAGE_DEPENDS wiringPi)
+    set(CPACK_RPM_PACKAGE_REQUIRES "wiringPi")
+elseif("${RF24_DRIVER}" STREQUAL "littlewire-spi")
+    set(CPACK_DEBIAN_PACKAGE_DEPENDS littlewire-spi)
+    set(CPACK_RPM_PACKAGE_REQUIRES "littlewire-spi")
+endif()
+
+# create a post-install & post-removal scripts to update linker
+set(POST_SCRIPTS
+    ${CMAKE_BINARY_DIR}/DEBIAN/postrm
+    ${CMAKE_BINARY_DIR}/DEBIAN/postinst
+)
+foreach(script ${POST_SCRIPTS})
+    file(WRITE ${script} ldconfig)
+    execute_process(COMMAND chmod +x ${script})
+    execute_process(COMMAND chmod 775 ${script})
+endforeach()
+# declare scripts for deb pkgs
+list(JOIN POST_SCRIPTS ";" EXTRA_CTRL_FILES)
+set(CPACK_DEBIAN_PACKAGE_CONTROL_EXTRA ${EXTRA_CTRL_FILES})
+# declare scripts for rpm pkgs
+list(POP_FRONT POST_SCRIPTS CPACK_RPM_POST_UNINSTALL_SCRIPT_FILE)
+list(POP_FRONT POST_SCRIPTS CPACK_RPM_POST_INSTALL_SCRIPT_FILE)
+
+message(STATUS "ready to package: ${CPACK_PACKAGE_FILE_NAME}.deb")
+message(STATUS "ready to package: ${CPACK_RPM_FILE_NAME}")
+
+include(CPack)
diff --git a/arduino/libraries/RF24/cmake/Cache.cmake b/arduino/libraries/RF24/cmake/Cache.cmake
new file mode 100644
index 000000000..e27a501f1
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/Cache.cmake
@@ -0,0 +1,28 @@
+option(ENABLE_CACHE "Enable cache if available" ON)
+if(NOT ENABLE_CACHE)
+    return()
+endif()
+
+set(CACHE_OPTION "ccache" CACHE STRING "Compiler cache to be used")
+set(CACHE_OPTION_VALUES "ccache" "sccache")
+set_property(CACHE CACHE_OPTION PROPERTY STRINGS ${CACHE_OPTION_VALUES})
+list(
+    FIND
+    CACHE_OPTION_VALUES
+    ${CACHE_OPTION}
+    CACHE_OPTION_INDEX
+)
+
+if(${CACHE_OPTION_INDEX} EQUAL -1)
+    message(STATUS
+        "Using custom compiler cache system: '${CACHE_OPTION}', explicitly supported entries are 
${CACHE_OPTION_VALUES}"
+    )
+endif()
+
+find_program(CACHE_BINARY ${CACHE_OPTION})
+if(CACHE_BINARY)
+    message(STATUS "${CACHE_OPTION} found and enabled")
+    set(CMAKE_CXX_COMPILER_LAUNCHER ${CACHE_BINARY})
+else()
+    message(WARNING "${CACHE_OPTION} is enabled but was not found. Not using it")
+endif()
diff --git a/arduino/libraries/RF24/cmake/CompilerWarnings.cmake 
b/arduino/libraries/RF24/cmake/CompilerWarnings.cmake
new file mode 100644
index 000000000..e40f0ab79
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/CompilerWarnings.cmake
@@ -0,0 +1,78 @@
+# from here:
+#
+# https://github.com/lefticus/cppbestpractices/blob/master/02-Use_the_Tools_Available.md
+
+function(set_project_warnings project_name)
+    option(WARNINGS_AS_ERRORS "Treat compiler warnings as errors" TRUE)
+
+    set(MSVC_WARNINGS
+        /W4 # Baseline reasonable warnings
+        /w14242 # 'identifier': conversion from 'type1' to 'type1', possible loss of data
+        /w14254 # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
+        /w14263 # 'function': member function does not override any base class virtual member function
+        /w14265 # 'classname': class has virtual functions, but destructor is not virtual instances of this 
class may not
+                # be destructed correctly
+        /w14287 # 'operator': unsigned/negative constant mismatch
+        /we4289 # nonstandard extension used: 'variable': loop control variable declared in the for-loop is 
used outside
+                # the for-loop scope
+        /w14296 # 'operator': expression is always 'boolean_value'
+        /w14311 # 'variable': pointer truncation from 'type1' to 'type2'
+        /w14545 # expression before comma evaluates to a function which is missing an argument list
+        /w14546 # function call before comma missing argument list
+        /w14547 # 'operator': operator before comma has no effect; expected operator with side-effect
+        /w14549 # 'operator': operator before comma has no effect; did you intend 'operator'?
+        /w14555 # expression has no effect; expected expression with side- effect
+        /w14619 # pragma warning: there is no warning number 'number'
+        /w14640 # Enable warning on thread un-safe static member initialization
+        /w14826 # Conversion from 'type1' to 'type_2' is sign-extended. This may cause unexpected runtime 
behavior.
+        /w14905 # wide string literal cast to 'LPSTR'
+        /w14906 # string literal cast to 'LPWSTR'
+        /w14928 # illegal copy-initialization; more than one user-defined conversion has been implicitly 
applied
+        /permissive- # standards conformance mode for MSVC compiler.
+    )
+
+    set(CLANG_WARNINGS
+        -Wall
+        -Wextra # reasonable and standard
+        -Wshadow # warn the user if a variable declaration shadows one from a parent context
+        -Wnon-virtual-dtor # warn the user if a class with virtual functions has a non-virtual destructor. 
This helps
+                           # catch hard to track down memory errors
+        # -Wold-style-cast # warn for c-style casts
+        -Wcast-align # warn for potential performance problem casts
+        -Wunused # warn on anything being unused
+        -Woverloaded-virtual # warn if you overload (not override) a virtual function
+        -Wpedantic # warn if non-standard C++ is used
+        -Wconversion # warn on type conversions that may lose data
+        -Wsign-conversion # warn on sign conversions
+        -Wnull-dereference # warn if a null dereference is detected
+        -Wdouble-promotion # warn if float is implicit promoted to double
+        -Wformat=2 # warn on security issues around functions that format output (ie printf)
+    )
+
+    if(WARNINGS_AS_ERRORS)
+        set(CLANG_WARNINGS ${CLANG_WARNINGS} -Werror)
+        set(MSVC_WARNINGS ${MSVC_WARNINGS} /WX)
+    endif()
+
+    set(GCC_WARNINGS
+        ${CLANG_WARNINGS}
+        -Wmisleading-indentation # warn if indentation implies blocks where blocks do not exist
+        -Wduplicated-cond # warn if if / else chain has duplicated conditions
+        -Wduplicated-branches # warn if if / else branches have duplicated code
+        -Wlogical-op # warn about logical operations being used where bitwise were probably wanted
+        -Wuseless-cast # warn if you perform a cast to the same type
+    )
+
+    if(MSVC)
+        set(PROJECT_WARNINGS ${MSVC_WARNINGS})
+    elseif(CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
+        set(PROJECT_WARNINGS ${CLANG_WARNINGS})
+    elseif(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
+        set(PROJECT_WARNINGS ${GCC_WARNINGS})
+    else()
+        message(AUTHOR_WARNING "No compiler warnings set for '${CMAKE_CXX_COMPILER_ID}' compiler.")
+    endif()
+
+    target_compile_options(${project_name} INTERFACE ${PROJECT_WARNINGS})
+
+endfunction()
diff --git a/arduino/libraries/RF24/cmake/GetLibInfo.cmake b/arduino/libraries/RF24/cmake/GetLibInfo.cmake
new file mode 100644
index 000000000..3871395ed
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/GetLibInfo.cmake
@@ -0,0 +1,43 @@
+# get lib info from the maintained library.properties required by the Arduino IDE
+file(STRINGS ${CMAKE_CURRENT_LIST_DIR}/../library.properties LibInfo)
+foreach(line ${LibInfo})
+    string(FIND ${line} "=" label_delimiter)
+    if(${label_delimiter} GREATER 0)
+        math(EXPR label_delimiter "${label_delimiter} + 1")
+        string(FIND ${line} "version" has_version)
+        string(FIND ${line} "name" has_name)
+        string(FIND ${line} "maintainer" has_maintainer)
+        string(FIND ${line} "sentence" has_sentence)
+        string(FIND ${line} "url" has_url)
+        string(FIND ${line} "paragraph" has_paragraph)
+        if(${has_version} EQUAL 0)
+            string(SUBSTRING ${line} ${label_delimiter} "-1" VERSION)
+        elseif(${has_name} EQUAL 0)
+            string(SUBSTRING ${line} ${label_delimiter} "-1" LibName)
+        elseif(${has_maintainer} EQUAL 0)
+            string(SUBSTRING ${line} ${label_delimiter} "-1" CPACK_PACKAGE_CONTACT)
+        elseif(${has_sentence} EQUAL 0)
+            string(SUBSTRING ${line} ${label_delimiter} "-1" CPACK_PACKAGE_DESCRIPTION_SUMMARY)
+        elseif(${has_url} EQUAL 0)
+            string(SUBSTRING ${line} ${label_delimiter} "-1" CMAKE_PROJECT_HOMEPAGE_URL)
+        elseif(${has_paragraph} EQUAL 0)
+            string(SUBSTRING ${line} ${label_delimiter} "-1" CPACK_PACKAGE_DESCRIPTION)
+        endif()
+    endif()
+endforeach()
+
+# convert the LibName to lower case
+string(TOLOWER ${LibName} LibTargetName)
+
+#parse the version information into pieces.
+string(REGEX REPLACE "^([0-9]+)\\..*" "\\1" VERSION_MAJOR "${VERSION}")
+string(REGEX REPLACE "^[0-9]+\\.([0-9]+).*" "\\1" VERSION_MINOR "${VERSION}")
+string(REGEX REPLACE "^[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" VERSION_PATCH "${VERSION}")
+
+# this is the library version
+set(${LibName}_VERSION_MAJOR ${VERSION_MAJOR})
+set(${LibName}_VERSION_MINOR ${VERSION_MINOR})
+set(${LibName}_VERSION_PATCH ${VERSION_PATCH})
+set(${LibName}_VERSION_STRING 
${${LibName}_VERSION_MAJOR}.${${LibName}_VERSION_MINOR}.${${LibName}_VERSION_PATCH})
+
+message(STATUS "${LibName} library version: ${${LibName}_VERSION_STRING}")
diff --git a/arduino/libraries/RF24/cmake/PreventInSourceBuilds.cmake 
b/arduino/libraries/RF24/cmake/PreventInSourceBuilds.cmake
new file mode 100644
index 000000000..dc4fd811b
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/PreventInSourceBuilds.cmake
@@ -0,0 +1,18 @@
+#
+# This function will prevent in-source builds
+function(AssureOutOfSourceBuilds)
+    # make sure the user doesn't play dirty with symlinks
+    get_filename_component(srcdir "${CMAKE_SOURCE_DIR}" REALPATH)
+    get_filename_component(bindir "${CMAKE_BINARY_DIR}" REALPATH)
+
+    # disallow in-source builds
+    if("${srcdir}" STREQUAL "${bindir}")
+        message("######################################################")
+        message("Warning: in-source builds are disabled")
+        message("Please create a separate build directory and run cmake from there")
+        message("######################################################")
+        message(FATAL_ERROR "Quitting configuration")
+    endif()
+endfunction()
+
+assureoutofsourcebuilds()
diff --git a/arduino/libraries/RF24/cmake/Sanitizers.cmake b/arduino/libraries/RF24/cmake/Sanitizers.cmake
new file mode 100644
index 000000000..c9d3ac821
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/Sanitizers.cmake
@@ -0,0 +1,55 @@
+function(enable_sanitizers project_name)
+
+    if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
+        option(ENABLE_COVERAGE "Enable coverage reporting for gcc/clang" FALSE)
+
+        if(ENABLE_COVERAGE)
+            target_compile_options(${project_name} INTERFACE --coverage -O0 -g)
+            target_link_libraries(${project_name} INTERFACE --coverage)
+        endif()
+
+        set(SANITIZERS "")
+
+        option(ENABLE_SANITIZER_ADDRESS "Enable address sanitizer" FALSE)
+        if(ENABLE_SANITIZER_ADDRESS)
+            list(APPEND SANITIZERS "address")
+        endif()
+
+        option(ENABLE_SANITIZER_LEAK "Enable leak sanitizer" FALSE)
+        if(ENABLE_SANITIZER_LEAK)
+            list(APPEND SANITIZERS "leak")
+        endif()
+
+        option(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR "Enable undefined behavior sanitizer" FALSE)
+        if(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR)
+            list(APPEND SANITIZERS "undefined")
+        endif()
+
+        option(ENABLE_SANITIZER_THREAD "Enable thread sanitizer" FALSE)
+        if(ENABLE_SANITIZER_THREAD)
+            if("address" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS)
+                message(WARNING "Thread sanitizer does not work with Address and Leak sanitizer enabled")
+            else()
+                list(APPEND SANITIZERS "thread")
+            endif()
+        endif()
+
+        option(ENABLE_SANITIZER_MEMORY "Enable memory sanitizer" FALSE)
+        if(ENABLE_SANITIZER_MEMORY AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang")
+            if("address" IN_LIST SANITIZERS OR "thread" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS)
+                message(WARNING "Memory sanitizer does not work with Address, Thread and Leak sanitizer 
enabled")
+            else()
+                list(APPEND SANITIZERS "memory")
+            endif()
+        endif()
+
+        list(JOIN SANITIZERS "," LIST_OF_SANITIZERS)
+
+    endif()
+
+    if(LIST_OF_SANITIZERS AND NOT "${LIST_OF_SANITIZERS}" STREQUAL "")
+        target_compile_options(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS})
+        target_link_libraries(${project_name} INTERFACE -fsanitize=${LIST_OF_SANITIZERS})
+    endif()
+
+endfunction()
diff --git a/arduino/libraries/RF24/cmake/StandardProjectSettings.cmake 
b/arduino/libraries/RF24/cmake/StandardProjectSettings.cmake
new file mode 100644
index 000000000..35fdec0fd
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/StandardProjectSettings.cmake
@@ -0,0 +1,33 @@
+# Set a default build type if none was specified
+if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+    set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build." FORCE)
+    message(STATUS "Setting build type to '${CMAKE_BUILD_TYPE}' as none was specified.")
+
+    # Set the possible values of build type for cmake-gui, ccmake
+    set_property(
+        CACHE CMAKE_BUILD_TYPE
+        PROPERTY STRINGS
+            "Debug"
+            "Release"
+            "MinSizeRel"
+            "RelWithDebInfo"
+    )
+endif()
+
+# Generate compile_commands.json to make it easier to work with clang based tools
+set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
+
+option(ENABLE_IPO "Enable Interprocedural Optimization, aka Link Time Optimization (LTO)" OFF)
+
+if(ENABLE_IPO)
+    include(CheckIPOSupported)
+    check_ipo_supported(
+        RESULT result
+        OUTPUT output
+    )
+    if(result)
+        set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
+    else()
+        message(SEND_ERROR "IPO is not supported: ${output}")
+    endif()
+endif()
diff --git a/arduino/libraries/RF24/cmake/StaticAnalyzers.cmake 
b/arduino/libraries/RF24/cmake/StaticAnalyzers.cmake
new file mode 100644
index 000000000..5f8e56aa1
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/StaticAnalyzers.cmake
@@ -0,0 +1,38 @@
+option(ENABLE_CPPCHECK "Enable static analysis with cppcheck" OFF)
+option(ENABLE_CLANG_TIDY "Enable static analysis with clang-tidy" OFF)
+option(ENABLE_INCLUDE_WHAT_YOU_USE "Enable static analysis with include-what-you-use" OFF)
+
+if(ENABLE_CPPCHECK)
+    find_program(CPPCHECK cppcheck)
+    if(CPPCHECK)
+        set(CMAKE_CXX_CPPCHECK
+            ${CPPCHECK}
+            --suppress=missingInclude
+            --enable=all
+            --inline-suppr
+            --inconclusive
+            -i
+            ${CMAKE_SOURCE_DIR}/imgui/lib
+        )
+    else()
+        message(SEND_ERROR "cppcheck requested but executable not found")
+    endif()
+endif()
+
+if(ENABLE_CLANG_TIDY)
+    find_program(CLANGTIDY clang-tidy)
+    if(CLANGTIDY)
+        set(CMAKE_CXX_CLANG_TIDY ${CLANGTIDY} -extra-arg=-Wno-unknown-warning-option)
+    else()
+        message(SEND_ERROR "clang-tidy requested but executable not found")
+    endif()
+endif()
+
+if(ENABLE_INCLUDE_WHAT_YOU_USE)
+    find_program(INCLUDE_WHAT_YOU_USE include-what-you-use)
+    if(INCLUDE_WHAT_YOU_USE)
+        set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE ${INCLUDE_WHAT_YOU_USE})
+    else()
+        message(SEND_ERROR "include-what-you-use requested but executable not found")
+    endif()
+endif()
diff --git a/arduino/libraries/RF24/cmake/detectCPU.cmake b/arduino/libraries/RF24/cmake/detectCPU.cmake
new file mode 100644
index 000000000..cc3236282
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/detectCPU.cmake
@@ -0,0 +1,78 @@
+# try to get the CPU model using a Linux bash command
+if(NOT SOC) # if SOC variable not defined by user at CLI
+    if(EXISTS "/sys/class/sunxi_info/sys_info")
+        execute_process(COMMAND grep sunxi_platform  /sys/class/sunxi_info/sys_info
+            OUTPUT_VARIABLE CPU_MODEL
+            OUTPUT_STRIP_TRAILING_WHITESPACE
+        )
+    else()
+        execute_process(COMMAND grep Hardware /proc/cpuinfo
+            OUTPUT_VARIABLE CPU_MODEL
+            OUTPUT_STRIP_TRAILING_WHITESPACE
+        )
+    endif()
+
+    string(FIND "${CPU_MODEL}" ":" cpu_is_described)
+    if(${cpu_is_described} GREATER 0) # Hardware field does exist
+        math(EXPR cpu_is_described "${cpu_is_described} + 1")
+        string(SUBSTRING "${CPU_MODEL}" ${cpu_is_described} -1 SOC)
+        string(STRIP "${SOC}" SOC)
+    else() # Hardware field does not exist
+        set(SOC "UNKNOWN") # use this string as a sentinel
+    endif()
+    message(STATUS "detected SoC: ${SOC}")
+else()
+    message(STATUS "SOC set to ${SOC}")
+endif()
+
+string(FIND "${SOC}" "Generic AM33XX" is_AM33XX)
+
+#[[ detect machine hardware name
+This CPU_TYPE variable is not used anywhere.
+It remains as useful prompt info & to be consistent with old build system ]]
+execute_process(COMMAND uname -m
+    OUTPUT_VARIABLE CPU_TYPE
+    OUTPUT_STRIP_TRAILING_WHITESPACE
+)
+message(STATUS "detected CPU type: ${CPU_TYPE}")
+
+# identify the compiler base name for customizing flags
+# THIS ONLY WORKS/TESTED FOR GNU COMPILERS
+if(NOT CMAKE_CROSSCOMPILING) # need to use /usr/lib/gcc soft symlink
+    # NOTE the following command doesn't work with " | tail -1" appended
+    execute_process(COMMAND ls /usr/lib/gcc
+        OUTPUT_VARIABLE tool_name
+        OUTPUT_STRIP_TRAILING_WHITESPACE
+    )
+
+    # use only last entry if multiple entries are returned; this tactic is a possible point of error
+    string(FIND "${tool_name}" "\n" last_list_delimiter REVERSE)
+    if(last_list_delimiter GREATER -1)
+        math(EXPR last_list_delimiter "${last_list_delimiter} + 1")
+        string(SUBSTRING "${tool_name}" ${last_list_delimiter} -1 tool_name)
+    endif()
+
+else() # we can use the compiler's name of the path set in the toolchain file
+    string(REGEX REPLACE "^\/usr\/bin\/(.*)-gcc.*" "\\1" tool_name "${CMAKE_C_COMPILER}")
+endif()
+
+message(STATUS "tool name being used: ${tool_name}")
+
+# add compiler flags to optomize builds with arm-linux-gnueabihf-g* compilers
+if("${tool_name}" STREQUAL "arm-linux-gnueabihf")
+    if("${SOC}" STREQUAL "BCM2835" OR "${SOC}" STREQUAL "BCM2708")
+        add_compile_options(-marm -march=armv6zk -mtune=arm1176jzf-s -mfpu=vfp -mfloat-abi=hard)
+    elseif("$SOC" STREQUAL "BCM2836" OR "${SOC}" STREQUAL "BCM2709")
+        add_compile_options(-march=armv7-a -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard)
+    elseif(${is_AM33XX} GREATER -1)
+        add_compile_options(-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard)
+    elseif("$SOC" STREQUAL "sun4i" OR "${SOC}" STREQUAL "Sun4iw1p1") # A10
+        add_compile_options(-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard)
+    elseif("$SOC" STREQUAL "sun5i" OR "${SOC}" STREQUAL "Sun4iw2p1") # A13
+        add_compile_options(-march=armv7-a -mtune=cortex-a8 -mfpu=neon -mfloat-abi=hard)
+    elseif("$SOC" STREQUAL "sun7i" OR "${SOC}" STREQUAL "Sun8iw2p1") # A20
+        add_compile_options(-march=armv7-a -mtune=cortex-a7 -mfpu=neon-vfpv4 -mfloat-abi=hard)
+    elseif("$SOC" STREQUAL "sun8i" OR "${SOC}" STREQUAL "Sun8iw7p1") # H3
+        add_compile_options(-march=armv8-a -mtune=cortex-a53 -mfpu=neon-vfpv4 -mfloat-abi=hard)
+    endif()
+endif()
diff --git a/arduino/libraries/RF24/cmake/pico_sdk_import.cmake 
b/arduino/libraries/RF24/cmake/pico_sdk_import.cmake
new file mode 100644
index 000000000..0a1da564f
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/pico_sdk_import.cmake
@@ -0,0 +1,62 @@
+# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
+#
+# This can be dropped into an external project to help locate this SDK
+# It should be include()ed prior to project()
+
+if(DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
+    set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
+    message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
+endif()
+
+if(DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
+    set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
+    message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
+endif()
+
+if(DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
+    set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
+    message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
+endif()
+
+set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
+set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git 
if not otherwise locatable")
+set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
+
+if(NOT PICO_SDK_PATH)
+    if(PICO_SDK_FETCH_FROM_GIT)
+        include(FetchContent)
+        set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
+        if(PICO_SDK_FETCH_FROM_GIT_PATH)
+            get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR 
"${CMAKE_SOURCE_DIR}")
+        endif()
+        FetchContent_Declare(
+            pico_sdk
+            GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
+            GIT_TAG master
+        )
+        if(NOT pico_sdk)
+            message("Downloading Raspberry Pi Pico SDK")
+            FetchContent_Populate(pico_sdk)
+            set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
+        endif()
+        set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
+    else ()
+        message(FATAL_ERROR
+            "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on 
to fetch from git."
+        )
+    endif()
+endif()
+
+get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
+if(NOT EXISTS ${PICO_SDK_PATH})
+    message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
+endif()
+
+set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
+if(NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
+    message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
+endif()
+
+set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
+
+include(${PICO_SDK_INIT_CMAKE_FILE})
diff --git a/arduino/libraries/RF24/cmake/toolchains/arm64.cmake 
b/arduino/libraries/RF24/cmake/toolchains/arm64.cmake
new file mode 100644
index 000000000..23e41ca7f
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/toolchains/arm64.cmake
@@ -0,0 +1,33 @@
+###################### FOR CROSS-COMPILING using the aarch64-linux-gnu-g** compiler
+# invoke this toolchain file using `cmake .. -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/<ARCH_TYPE>.cmake`
+# this file is meant to be used generically, but will not work for all CMake projects
+# this toolchain file's cmds was copied from the CMake docs then modified for better explanation and re-use
+
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR arm64)
+set(TARGET_ARCH arm64) # only used in cmake/createDebianPkg.cmake
+set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc)
+set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++)
+
+# CMAKE_SYSROOT can only be set in a toolchain file
+# set(CMAKE_SYSROOT  /usr/aarch64-linux-gnu) # useful when a target machine's files are available
+
+# set the directory for searching installed headers
+# add_compile_options(-I /usr/aarch64-linux-gnu/include) # this may not be best practice
+
+#[[
+# CMAKE_STAGING_PREFIX is only useful for transfering a built CMake project to a target machine
+set(CMAKE_STAGING_PREFIX /home/devel/stage) # use CMAKE_INSTALL_PREFIX instead (see below comments)
+
+CMAKE_FIND_ROOT_PATH is an empty list by default (this list can be modified where applicable)
+if cross-compiling a dependent lib (like MRAA - which is optional), then
+set the lib's CMAKE_INSTALL_PREFIX to a value that is appended to RF24 lib's CMAKE_FIND_ROOT_PATH
+example using MRAA:
+(for MRAA/build dir) `cmake .. -D CMAKE_TOOLCHAIN_FILE=path/to/RF24/repo/cmake/toolchains/arm64.cmake -D 
CMAKE_INSTALL_PREFIX:PATH=/usr/aarch64-linux-gnu
+(for RF24/build dir) `cmake .. -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/arm64.cmake
+]]
+list(APPEND CMAKE_FIND_ROOT_PATH /usr/aarch64-linux-gnu)
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # search CMAKE_SYSROOT when find_program() is called
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # search CMAKE_FIND_ROOT_PATH entries when find_library() is 
called
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # search CMAKE_FIND_ROOT_PATH entries when find_file() is called
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) # search CMAKE_FIND_ROOT_PATH entries when find_package() is 
called
diff --git a/arduino/libraries/RF24/cmake/toolchains/armhf.cmake 
b/arduino/libraries/RF24/cmake/toolchains/armhf.cmake
new file mode 100644
index 000000000..488b81a89
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/toolchains/armhf.cmake
@@ -0,0 +1,33 @@
+###################### FOR CROSS-COMPILING using the arm-linux-gnueabihf-g** compiler
+# invoke this toolchain file using `cmake .. -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/<ARCH_TYPE>.cmake`
+# this file is meant to be used generically, but will not work for all CMake projects
+# this toolchain file's cmds was copied from the CMake docs then modified for better explanation and re-use
+
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR armhf)
+set(TARGET_ARCH armhf) # only used in cmake/CPackInfo.cmake
+set(CMAKE_C_COMPILER /usr/bin/arm-linux-gnueabihf-gcc)
+set(CMAKE_CXX_COMPILER /usr/bin/arm-linux-gnueabihf-g++)
+
+# CMAKE_SYSROOT can only be set in a toolchain file
+# set(CMAKE_SYSROOT  /usr/arm-linux-gnueabihf) # useful when a target machine's files are available
+
+# set the directory for searching installed headers
+# add_compile_options(-I /usr/arm-linux-gnueabihf/include) # this may not be best practice
+
+#[[
+# CMAKE_STAGING_PREFIX is only useful for transfering a built CMake project to a target machine
+set(CMAKE_STAGING_PREFIX /home/devel/stage) # use CMAKE_INSTALL_PREFIX instead (see below comments)
+
+CMAKE_FIND_ROOT_PATH is an empty list by default (this list can be modified where applicable)
+if cross-compiling a dependent lib (like MRAA - which is optional), then
+set the lib's CMAKE_INSTALL_PREFIX to a value that is appended to RF24 lib's CMAKE_FIND_ROOT_PATH
+example using MRAA:
+(for MRAA/build dir) `cmake .. -D CMAKE_TOOLCHAIN_FILE=path/to/RF24/repo/cmake/toolchains/arm.cmake -D 
CMAKE_INSTALL_PREFIX:PATH=/usr/arm-linux-gnueabihf
+(for RF24/build dir) `cmake .. -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/arm.cmake
+]]
+list(APPEND CMAKE_FIND_ROOT_PATH /usr/arm-linux-gnueabihf)
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # search CMAKE_SYSROOT when find_program() is called
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # search CMAKE_FIND_ROOT_PATH entries when find_library() is 
called
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # search CMAKE_FIND_ROOT_PATH entries when find_file() is called
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) # search CMAKE_FIND_ROOT_PATH entries when find_package() is 
called
diff --git a/arduino/libraries/RF24/cmake/toolchains/default.cmake 
b/arduino/libraries/RF24/cmake/toolchains/default.cmake
new file mode 100644
index 000000000..e67d723f8
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/toolchains/default.cmake
@@ -0,0 +1 @@
+# empty toolchain file to allow CI scripts to still use system default toolchains
\ No newline at end of file
diff --git a/arduino/libraries/RF24/cmake/toolchains/i686.cmake 
b/arduino/libraries/RF24/cmake/toolchains/i686.cmake
new file mode 100644
index 000000000..f3c42be06
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/toolchains/i686.cmake
@@ -0,0 +1,33 @@
+###################### FOR CROSS-COMPILING using the i686-linux-gnu-g** compiler
+# invoke this toolchain file using `cmake .. -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/<ARCH_TYPE>.cmake`
+# this file is meant to be used generically, but will not work for all CMake projects
+# this toolchain file's cmds was copied from the CMake docs then modified for better explanation and re-use
+
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR i686)
+set(TARGET_ARCH i686) # only used in cmake/createDebianPkg.cmake
+set(CMAKE_C_COMPILER /usr/bin/i686-linux-gnu-gcc)
+set(CMAKE_CXX_COMPILER /usr/bin/i686-linux-gnu-g++)
+
+# CMAKE_SYSROOT can only be set in a toolchain file
+# set(CMAKE_SYSROOT  /usr/i686-linux-gnu) # useful when a target machine's files are available
+
+# set the directory for searching installed headers
+# add_compile_options(-I /usr/i686-linux-gnu/include) # this may not be best practice
+
+#[[
+# CMAKE_STAGING_PREFIX is only useful for transfering a built CMake project to a target machine
+set(CMAKE_STAGING_PREFIX /home/devel/stage) # use CMAKE_INSTALL_PREFIX instead (see below comments)
+
+CMAKE_FIND_ROOT_PATH is an empty list by default (this list can be modified where applicable)
+if cross-compiling a dependent lib (like MRAA - which is optional), then
+set the lib's CMAKE_INSTALL_PREFIX to a value that is appended to RF24 lib's CMAKE_FIND_ROOT_PATH
+example using MRAA:
+(for MRAA/build dir) `cmake .. -D CMAKE_TOOLCHAIN_FILE=path/to/RF24/repo/cmake/toolchains/i686.cmake -D 
CMAKE_INSTALL_PREFIX:PATH=/usr/i686-linux-gnu
+(for RF24/build dir) `cmake .. -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/i686.cmake
+]]
+list(APPEND CMAKE_FIND_ROOT_PATH /usr/i686-linux-gnu)
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # search CMAKE_SYSROOT when find_program() is called
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # search CMAKE_FIND_ROOT_PATH entries when find_library() is 
called
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # search CMAKE_FIND_ROOT_PATH entries when find_file() is called
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) # search CMAKE_FIND_ROOT_PATH entries when find_package() is 
called
diff --git a/arduino/libraries/RF24/cmake/toolchains/x86_64.cmake 
b/arduino/libraries/RF24/cmake/toolchains/x86_64.cmake
new file mode 100644
index 000000000..e4ff737a8
--- /dev/null
+++ b/arduino/libraries/RF24/cmake/toolchains/x86_64.cmake
@@ -0,0 +1,33 @@
+###################### FOR CROSS-COMPILING using the x86_64-linux-gnux32-g** compiler
+# invoke this toolchain file using `cmake .. -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/<ARCH_TYPE>.cmake`
+# this file is meant to be used generically, but will not work for all CMake projects
+# this toolchain file's cmds was copied from the CMake docs then modified for better explanation and re-use
+
+set(CMAKE_SYSTEM_NAME Linux)
+set(CMAKE_SYSTEM_PROCESSOR x86_64)
+set(TARGET_ARCH x86_64) # only used in cmake/createDebianPkg.cmake
+set(CMAKE_C_COMPILER /usr/bin/x86_64-linux-gnux32-gcc)
+set(CMAKE_CXX_COMPILER /usr/bin/x86_64-linux-gnux32-g++)
+
+# CMAKE_SYSROOT can only be set in a toolchain file
+# set(CMAKE_SYSROOT  /usr/x86_64-linux-gnux32) # useful when a target machine's files are available
+
+# set the directory for searching installed headers
+# add_compile_options(-I /usr/x86_64-linux-gnux32/include) # this may not be best practice
+
+#[[
+# CMAKE_STAGING_PREFIX is only useful for transfering a built CMake project to a target machine
+set(CMAKE_STAGING_PREFIX /home/devel/stage) # use CMAKE_INSTALL_PREFIX instead (see below comments)
+
+CMAKE_FIND_ROOT_PATH is an empty list by default (this list can be modified where applicable)
+if cross-compiling a dependent lib (like MRAA - which is optional), then
+set the lib's CMAKE_INSTALL_PREFIX to a value that is appended to RF24 lib's CMAKE_FIND_ROOT_PATH
+example using MRAA:
+(for MRAA/build dir) `cmake .. -D CMAKE_TOOLCHAIN_FILE=path/to/RF24/repo/cmake/toolchains/x86_64.cmake -D 
CMAKE_INSTALL_PREFIX:PATH=/usr/x86_64-linux-gnux32
+(for RF24/build dir) `cmake .. -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/x86_64.cmake
+]]
+list(APPEND CMAKE_FIND_ROOT_PATH /usr/x86_64-linux-gnux32)
+set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) # search CMAKE_SYSROOT when find_program() is called
+set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) # search CMAKE_FIND_ROOT_PATH entries when find_library() is 
called
+set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # search CMAKE_FIND_ROOT_PATH entries when find_file() is called
+set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY) # search CMAKE_FIND_ROOT_PATH entries when find_package() is 
called
diff --git a/arduino/libraries/RF24/datasheets/nRF24L01P_datasheet_v1.pdf 
b/arduino/libraries/RF24/datasheets/nRF24L01P_datasheet_v1.pdf
new file mode 100644
index 000000000..b35803355
Binary files /dev/null and b/arduino/libraries/RF24/datasheets/nRF24L01P_datasheet_v1.pdf differ
diff --git a/arduino/libraries/RF24/datasheets/nRF24L01_datasheet_v2.pdf 
b/arduino/libraries/RF24/datasheets/nRF24L01_datasheet_v2.pdf
new file mode 100644
index 000000000..f8d77a456
Binary files /dev/null and b/arduino/libraries/RF24/datasheets/nRF24L01_datasheet_v2.pdf differ
diff --git a/arduino/libraries/RF24/docs/README.md b/arduino/libraries/RF24/docs/README.md
new file mode 100644
index 000000000..51aa389e8
--- /dev/null
+++ b/arduino/libraries/RF24/docs/README.md
@@ -0,0 +1,3 @@
+# Intended for use in Doxygen
+
+These markdown files (*.md) contain relative hyperlinks. Any relative hyperlinks will not work when viewing 
these markdown files in github.
diff --git a/arduino/libraries/RF24/docs/arduino.md b/arduino/libraries/RF24/docs/arduino.md
new file mode 100644
index 000000000..c0e106eee
--- /dev/null
+++ b/arduino/libraries/RF24/docs/arduino.md
@@ -0,0 +1,286 @@
+# Arduino
+
+<!-- markdownlint-disable MD031 -->
+RF24 is fully compatible with Arduino boards.
+
+See [Arduino Board reference](http://www.arduino.cc/en/Reference/Board) and [Arduino SPI 
reference](http://arduino.cc/en/Reference/SPI) for more information
+
+RF24 makes use of the standard hardware SPI pins (MISO, MOSI, SCK) and requires two additional pins, to 
control
+the chip-select and chip-enable functions.
+
+```cpp
+RF24 radio(ce_pin, cs_pin);
+```
+
+These pins must be chosen and designated by the user and can use any
+available pins.
+
+## Alternate SPI Support
+
+RF24 supports alternate SPI methods, in case the standard hardware SPI pins are otherwise unavailable.
+
+### Software Driven SPI
+
+Software driven SPI is provided by the [DigitalIO library](https://github.com/greiman/DigitalIO).
+
+Setup:
+
+1. Install the digitalIO library
+2. Open RF24_config.h in a text editor.
+   Uncomment the line
+   ```cpp
+   #define SOFTSPI
+   ```
+   or add the build flag/option
+   ```shell
+   -DSOFTSPI
+   ```
+3. In your sketch, add
+   ```cpp
+   #include <DigitalIO.h>
+   ```
+
+@note Note: Pins are listed as follows and can be modified by editing the RF24_config.h file.
+
+```cpp
+#define SOFT_SPI_MISO_PIN 16
+#define SOFT_SPI_MOSI_PIN 15
+#define SOFT_SPI_SCK_PIN 14
+```
+
+Or add the build flag/option
+
+```shell
+-DSOFT_SPI_MISO_PIN=16 -DSOFT_SPI_MOSI_PIN=15 -DSOFT_SPI_SCK_PIN=14
+```
+
+### Alternate Hardware (UART) Driven SPI
+
+The Serial Port (UART) on Arduino can also function in SPI mode, and can double-buffer data, while the
+default SPI hardware cannot.
+
+The SPI_UART library is available at 
[TMRh20/Sketches](https://github.com/TMRh20/Sketches/tree/master/SPI_UART)
+
+Enabling:
+
+1. Install the SPI_UART library
+2. Edit RF24_config.h and uncomment
+   ```cpp
+   #define SPI_UART
+   ```
+3. In your sketch, add
+   ```cpp
+   #include <SPI_UART.h>
+   ```
+
+SPI_UART SPI Pin Connections:
+| NRF |Arduino Uno Pin|
+|----:|:--------------|
+| MOSI| TX(0) |
+| MISO| RX(1) |
+| SCK | XCK(4) |
+| CE | User Specified|
+| CSN | User Specified|
+
+@note SPI_UART on Mega boards requires soldering to an unused pin on the chip. See 
[#24](https://github.com/TMRh20/RF24/issues/24) for more information on SPI_UART.
+
+### Using a specific SPI Bus
+
+An alternate SPI bus can be specified using the overloaded `RF24::begin(_SPI*)` method.
+This is useful for some boards that offer more than 1 hardware-driven SPI bus or cetain Arduino
+cores that implement a software-driven (AKA bit-banged) SPI bus that does not use the DigitalIO
+library.
+
+@warning The SPI bus object's `SPIClass::begin()` method **must** be called before
+calling the overloaded `RF24::begin(_SPI*)` method.
+
+Below are some example snippets that demonstrate how this can be done.
+
+#### ESP8266 example
+
+@see The following example code is meant for the popular NodeMCU board. Please refer to the
+[ESP8266 ArduinoCore's SPI 
documentation](https://arduino-esp8266.readthedocs.io/en/latest/libraries.html#spi)
+for [other ESP8266-based boards](https://arduino-esp8266.readthedocs.io/en/latest/boards.html#).
+
+```cpp
+#include <SPI.h>
+#include <RF24.h>
+
+// notice these pin numbers are not the same used in the library examples
+RF24 radio(D4, D3); // the (ce_pin, csn_pin) connected to the radio
+
+void setup() {
+  Serial.begin(115200);
+  while (!Serial) {} //some boards need this
+
+  // by default (with no arguments passed) SPI uses D5 (HSCLK), D6 (HMISO), D7 (HMOSI)
+  SPI.pins(6, 7, 8, 0);
+  // this means the following pins are used for the SPI bus:
+  // MOSI = SD1
+  // MISO = SD0
+  // SCLK = CLK
+  // CSN = GPIO0 (labeled D3 on the board)
+  // **notice we also passed `D3` to the RF24 contructor's csn_pin parameter**
+
+  SPI.begin();
+
+  if (!radio.begin(&SPI)) {
+    Serial.println(F("radio hardware not responding!!"));
+    while (1) {} // hold program in infinite loop to prevent subsequent errors
+  }
+
+  // ... continue with program as normal (see library examples/ folder)
+}
+```
+
+#### ESP32 example
+
+@see Please review the Espressif's
+[SPI_Multiple_Buses.ino example for the 
ESP32](https://github.com/espressif/arduino-esp32/blob/master/libraries/SPI/examples/SPI_Multiple_Buses/SPI_Multiple_Buses.ino)
+located in their ArduinoCore repository (along with the SPI library for the ESP32).
+
+```cpp
+#include <SPI.h>
+#include <RF24.h>
+
+// to use custom-defined pins, uncomment the following
+// #define MY_MISO 26
+// #define MY_MOSI 27
+// #define MY_SCLK 25
+// #define MY_SS   32  // pass MY_SS as the csn_pin parameter to the RF24 constructor
+
+// notice these pin numbers are not the same used in the library examples
+RF24 radio(2, 0); // the (ce_pin, csn_pin) connected to the radio
+
+SPIClass* hspi = nullptr; // we'll instantiate this in the `setup()` function
+// by default the HSPI bus predefines the following pins
+// HSPI_MISO = 12
+// HSPI_MOSI = 13
+// HSPI_SCLK = 14
+// HSPI_SS   = 15
+
+void setup() {
+  Serial.begin(115200);
+  while (!Serial) {} //some boards need this
+
+  hspi = new SPIClass(HSPI); // by default VSPI is used
+  hspi->begin();
+  // to use the custom defined pins, uncomment the following
+  // hspi->begin(MY_SCLK, MY_MISO, MY_MOSI, MY_SS)
+
+  if (!radio.begin(hspi)) {
+    Serial.println(F("radio hardware not responding!!"));
+    while (1) {} // hold program in infinite loop to prevent subsequent errors
+  }
+
+  // ... continue with program as normal (see library examples/ folder)
+}
+```
+
+#### Teensy example
+
+@see The overloaded RF24::begin(\_SPI\*) is not needed according to the
+[Teensyduino SPI documentation](https://www.pjrc.com/teensy/td_libs_SPI.html).
+Please review the table provided in the
+[Teensyduino documentation](https://www.pjrc.com/teensy/td_libs_SPI.html) for what pins are used by
+default for certain Teensy boards.
+
+```cpp
+#include <SPI.h>
+#include <RF24.h>
+
+// these pins are the alternate SPI pins available for Teensy LC/3.0/3.1/3.2/3.5/3.6
+#define MY_MISO 8
+#define MY_MOSI 7
+#define MY_SCLK 14
+
+// notice these pin numbers are not the same used in the library examples
+RF24 radio(2, 0); // the (ce_pin, csn_pin) connected to the radio
+
+void setup() {
+  Serial.begin(115200);
+  while (!Serial) {} //some boards need this
+
+  SPI.setMOSI(MY_MOSI);
+  SPI.setMISO(MY_MISO);
+  SPI.setSCK(MY_SCLK);
+
+  if (!radio.begin()) {
+    Serial.println(F("radio hardware not responding!!"));
+    while (1) {} // hold program in infinite loop to prevent subsequent errors
+  }
+
+  // ... continue with program as normal (see library examples/ folder)
+}
+```
+
+<!--
+#### MBED example
+@see The [ArduinoCore-mbed SPI 
library](https://github.com/arduino/ArduinoCore-mbed/tree/master/libraries/SPI)
+has predefined the possible hardware-driven SPI buses. This applies to Arduino Nano 33 BLE and
+Arduino Portenta boards.
+
+```cpp
+#include <SPI.h>
+#include <RF24.h>
+
+RF24 radio(7, 8); // the (ce_pin, csn_pin) connected to the radio
+
+void setup() {
+  Serial.begin(115200);
+  while (!Serial) {} //some boards need this
+
+  SPI1.begin();
+
+  if (!radio.begin(&SPI1)) {
+    Serial.println(F("radio hardware not responding!!"));
+    while (1) {} // hold program in infinite loop to prevent subsequent errors
+  }
+
+  // ... continue with program as normal (see library examples/ folder)
+}
+```
+
+#### ATSAMD21 example
+@see The Suport for secondary SPI bus on ATSAMD21 chips is sparcely documented. However,
+[Sparkfun has a tutorial about using a second SPI 
bus](https://learn.sparkfun.com/tutorials/adding-more-sercom-ports-for-samd-boards/adding-an-spi)
+that often refers to the
+[ArduinoCore-samd SPI library source 
code](https://github.com/arduino/ArduinoCore-samd/blob/master/libraries/SPI/SPI.h). This example applies 
Sparkfun's tutorial toward the RF24 library.
+Special thanks to [ex-caliper](https://github.com/ex-caliper) for the lead!
+
+```cpp
+#include <SPI.h>
+#include <RF24.h>
+#include "wiring_private.h" // pinPeripheral()
+
+// Define the pins are the alternate SPI pins we will use. These pin numbers ARE NOT chosen at random.
+// The Sparkfun tutorial explains these choices in more detail (link is in "See Also" comment above).
+#define MY_MISO 3
+#define MY_MOSI 4
+#define MY_SCLK 5
+
+// instantiate the secondary SPI bus
+SPIClass MY_SPI(&sercom2, MY_MISO, MY_SCLK, MY_MOSI, SPI_PAD_0_SCK_3, SERCOM_RX_PAD_1);
+
+// notice these pin numbers are not the same used in the library examples
+RF24 radio(7, 6); // the (ce_pin, csn_pin) connected to the radio
+
+void setup() {
+  Serial.begin(115200);
+  while (!Serial) {} //some boards need this
+
+  // enable the alternate functionality for our secondary SPI bus' pins
+  pinPeripheral(MY_MISO, PIO_SERCOM_ALT);
+  pinPeripheral(MY_MOSI, PIO_SERCOM_ALT);
+  pinPeripheral(MY_SCLK, PIO_SERCOM);
+
+  MY_SPI.begin(); // initialize the secondary SPI bus
+
+  if (!radio.begin(&MY_SPI)) {
+    Serial.println(F("radio hardware not responding!!"));
+    while (1) {} // hold program in infinite loop to prevent subsequent errors
+  }
+
+  // ... continue with program as normal (see library examples/ folder)
+}
+``` -->
diff --git a/arduino/libraries/RF24/docs/attiny.md b/arduino/libraries/RF24/docs/attiny.md
new file mode 100644
index 000000000..379d81b1f
--- /dev/null
+++ b/arduino/libraries/RF24/docs/attiny.md
@@ -0,0 +1,79 @@
+# ATTiny
+
+ATTiny support for this library relys on the SpenceKonde ATTinyCore. Be sure to have added this core to the 
Arduino Boards Manager with [this install guide](http://highlowtech.org/?p=1695)
+
+See the included rf24ping85 example for pin info and usage
+
+@warning The ATTiny2313 is unsupported due to lack of sufficient memory resources
+
+Some versions of Arduino IDE may require a patch to allow use of the full program space on ATTiny. See [this 
resource](https://github.com/TCWORLD/ATTinyCore/tree/master/PCREL%20Patch%20for%20GCC) for the ATTiny patch.
+
+ATTiny board support initially added from [jscrane/RF24](https://github.com/jscrane/RF24)
+
+## Hardware Configuration
+
+By [tong67](https://github.com/tong67)
+
+### ATtiny25/45/85 Pin map with CE_PIN 3 and CSN_PIN 4
+
+```text
+                             +-\/-+
+               NC      PB5  1|o   |8  Vcc --- nRF24L01  VCC, pin2 --- LED --- 5V
+nRF24L01  CE, pin3 --- PB3  2|    |7  PB2 --- nRF24L01  SCK, pin5
+nRF24L01 CSN, pin4 --- PB4  3|    |6  PB1 --- nRF24L01 MOSI, pin6
+nRF24L01 GND, pin1 --- GND  4|    |5  PB0 --- nRF24L01 MISO, pin7
+                             +----+
+```
+
+### ATtiny25/45/85 Pin map with CE_PIN 3 and CSN_PIN 3 - PB3 and PB4 are free to use for application
+
+- Circuit idea from [NerdRalph's 3 pin 
solution](http://nerdralph.blogspot.ca/2014/01/nrf24l01-control-with-3-attiny85-pins.html)
+- Original RC combination was 1K/100nF. 22K/10nF combination worked better.
+- For best settletime delay value in RF24::csn() the timingSearch3pin.ino sketch can be used.
+- This configuration is enabled when CE_PIN and CSN_PIN are equal, e.g. both 3
+- Because CE is always high the power consumption is higher than for 5 pins solution
+
+```text
+                                                                                       ^^
+                             +-\/-+           nRF24L01   CE, pin3 ------|              //
+                       PB5  1|o   |8  Vcc --- nRF24L01  VCC, pin2 ------x----------x--|<|-- 5V
+               NC      PB3  2|    |7  PB2 --- nRF24L01  SCK, pin5 --|<|---x-[22k]--|  LED
+               NC      PB4  3|    |6  PB1 --- nRF24L01 MOSI, pin6  1n4148 |
+nRF24L01 GND, pin1 -x- GND  4|    |5  PB0 --- nRF24L01 MISO, pin7         |
+                    |        +----+                                       |
+                    |-----------------------------------------------||----x-- nRF24L01 CSN, pin4
+                                                                  10nF
+```
+
+### ATtiny24/44/84 Pin map with CE_PIN 8 and CSN_PIN 7
+
+- Schematic provided and successfully tested by [Carmine Pastore](https://github.com/Carminepz)
+
+```text
+                              +-\/-+
+nRF24L01  VCC, pin2 --- VCC  1|o   |14 GND --- nRF24L01  GND, pin1
+                        PB0  2|    |13 AREF
+                        PB1  3|    |12 PA1
+                        PB3  4|    |11 PA2 --- nRF24L01   CE, pin3
+                        PB2  5|    |10 PA3 --- nRF24L01  CSN, pin4
+                        PA7  6|    |9  PA4 --- nRF24L01  SCK, pin5
+nRF24L01 MISO, pin7 --- PA6  7|    |8  PA5 --- nRF24L01 MOSI, pin6
+                              +----+
+```
+
+### ATtiny2313/4313 Pin map with CE_PIN 12 and CSN_PIN 13
+
+```text
+                              +-\/-+
+                        PA2  1|o   |20 VCC --- nRF24L01  VCC, pin2
+                        PD0  2|    |19 PB7 --- nRF24L01  SCK, pin5
+                        PD1  3|    |18 PB6 --- nRF24L01 MOSI, pin6
+                        PA1  4|    |17 PB5 --- nRF24L01 MISO, pin7
+                        PA0  5|    |16 PB4 --- nRF24L01  CSN, pin4
+                        PD2  6|    |15 PB3 --- nRF24L01   CE, pin3
+                        PD3  7|    |14 PB2
+                        PD4  8|    |13 PB1
+                        PD5  9|    |12 PB0
+nRF24L01  GND, pin1 --- GND 10|    |11 PD6
+                              +----+
+```
diff --git a/arduino/libraries/RF24/docs/atxmega.md b/arduino/libraries/RF24/docs/atxmega.md
new file mode 100644
index 000000000..b79659072
--- /dev/null
+++ b/arduino/libraries/RF24/docs/atxmega.md
@@ -0,0 +1,55 @@
+# ATXMEGA
+
+The RF24 driver can be build as a static library with Atmel Studio 7 in order to be included as any other 
library in another program for the XMEGA family.
+
+Currently only the [ATXMEGA D3](https://www.microchip.com/wwwproducts/en/ATxmega64d3) family is implemented.
+
+## Preparation
+
+Create an empty GCC Static Library project in Atmel Studio 7.
+
+As not all files are required, copy the following directory structure in the project:
+
+```text
+utility\
+  ATXMegaD3\
+    compatibility.c
+    compatibility.h
+    gpio.cpp
+    gpio.h
+    gpio_helper.c
+    gpio_helper.h
+    includes.h
+    RF24_arch_config.h
+    spi.cpp
+    spi.h
+nRF24L01.h
+printf.h
+RF24.cpp
+RF24.h
+RF24_config.h
+```
+
+## Usage
+
+Add the library to your project!
+
+In the file where the `main()` is put the following in order to update the millisecond functionality:
+
+```cpp
+ISR(TCE0_OVF_vect)
+{
+    update_milisec();
+}
+```
+
+Declare the rf24 radio with `RF24 radio(XMEGA_PORTC_PIN3, XMEGA_SPI_PORT_C);`
+
+1. First parameter is the CE pin which can be any available pin on the microcontroller.
+2. Second parameter is the CS which can be on port C (**XMEGA_SPI_PORT_C**) or on port D 
(**XMEGA_SPI_PORT_D**).
+
+Call the `__start_timer()` to start the millisecond timer.
+
+@note The millisecond functionality is based on the TCE0 so don't use these pins as IO.
+
+@note The operating frequency of the uC is 32MHz. If you have a different frequency, change the TCE0 
registers appropriatly in function `__start_timer()` in **compatibility.c** file for your frequency.
diff --git a/arduino/libraries/RF24/docs/cross_compile.md b/arduino/libraries/RF24/docs/cross_compile.md
new file mode 100644
index 000000000..bcff6c5fc
--- /dev/null
+++ b/arduino/libraries/RF24/docs/cross_compile.md
@@ -0,0 +1,98 @@
+# Linux cross-compilation
+
+<!-- markdownlint-disable MD031 -->
+@warning These instructions are no longer recommended because they involve disabling security measures
+for the target system. Please try the [instructions using CMake](md_docs_using_cmake.html).
+
+RF24 library supports cross-compilation. Advantages of cross-compilation:
+
+- development tools don't have to be installed on target machine
+- resources of target machine don't have to be sufficient for compilation
+- compilation time can be reduced for large projects
+
+Following prerequisites need to be assured:
+
+- ssh passwordless access to target machine [Here is a hint](https://linuxconfig.org/passwordless-ssh)
+- sudo of a remote user without password [Here is a 
hint](http://askubuntu.com/questions/334318/sudoers-file-enable-nopasswd-for-user-all-commands)
+- cross-compilation toolchain for your target machine; for RPi
+  ```shell
+  git clone https://github.com/raspberrypi/tools rpi_tools
+  ```
+  and cross-compilation tools must be in PATH, for example
+  ```shell
+  export PATH=$PATH:/your/dir/rpi-tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian-x64/bin
+  ```
+
+## Cross compilation steps
+
+1. clone RF24 to a machine for cross-compilation
+   ```shell
+   git clone https://github.com/nRF24/RF24
+   cd RF24
+   ```
+2. configure for cross compilation
+   ```shell
+   ./configure --remote=pi@target_linux_host
+   ```
+   eventually
+   ```shell
+   ./configure --remote=pi@target_linux_host --driver=<driver>
+   ```
+3. build
+   ```shell
+   make
+   ```
+4. (optional) install library to cross-compilation machine into cross-exvironment - important for 
compilation of examples
+   ```shell
+   sudo make install
+   ```
+5. upload library to target machine
+   ```shell
+   make upload
+   ```
+6. (optional) compile examples
+   ```shell
+   cd examples_linux
+   make
+   ```
+7. (optional) upload examples to target machine
+   ```shell
+   make upload
+   ```
+
+## Cross comilation steps for python wrapper
+
+### Prerequisites
+
+- Python setuptools must be installed on both target and cross-compilation machines
+  ```shell
+  sudo pip install setuptools
+  ```
+  or
+  ```shell
+  sudo apt-get install python-setuptools
+  ```
+
+### Installation steps
+
+1. Assure having libboost-python-dev library in your cross-compilation environment. Alternatively, you can 
install it into your target machine and copy /usr and /lib directories to the cross-compilation machine.
+   For example
+   ```shell
+   mkdir -p rpi_root && rsync -a pi@target_linux_host:/usr :/lib rpi_root
+   export CFLAGS="--sysroot=/your/dir/rpi_root -I/your/dir/rpi_root/usr/include/python2.7/"
+   ```
+2. Build the python wrapper
+   ```shell
+   cd pyRF24
+   ./setup.py build --compiler=crossunix
+   ```
+3. Make the egg package
+   ```shell
+   ./setup.py bdist_egg --plat-name=cross
+   ```
+   `dist/RF24-<version>-cross.egg` should be created.
+4. Upload it to the target machine and install there:
+   ```shell
+    scp dist/RF24-*-cross.egg pi@target_linux_host:
+    ssh pi@target_linux_host 'sudo easy_install RF24-*-cross.egg'
+   ```
diff --git a/arduino/libraries/RF24/docs/linux_install.md b/arduino/libraries/RF24/docs/linux_install.md
new file mode 100644
index 000000000..e1b23c866
--- /dev/null
+++ b/arduino/libraries/RF24/docs/linux_install.md
@@ -0,0 +1,92 @@
+# Linux Installation
+
+<!-- markdownlint-disable MD031 -->
+Generic Linux devices are supported via SPIDEV, MRAA, RPi native via BCM2835, or using LittleWire.
+
+@note The SPIDEV option should work with most Linux systems supporting spi userspace device.
+
+@warning These instructions are beginning to age because they were designed with the assumption that
+the arm-linux-gnueabihf-g\*\* compilers were available and default for the system. If you have problems
+using these instructions, please try the [instructions using CMake](md_docs_using_cmake.html).
+
+## Automated Install
+
+**Designed & Tested on RPi** - Defaults to SPIDEV on devices supporting it
+
+1. Install prerequisites if there are any (MRAA, LittleWire libraries, setup SPI device etc)
+2. Download the install.sh file from 
[tmrh20.github.io/RF24Installer/RPi](http://tmrh20.github.io/RF24Installer/RPi/install.sh)
+   ```shell
+   wget http://tmrh20.github.io/RF24Installer/RPi/install.sh
+   ```
+3. Make it executable
+   ```shell
+   chmod +x install.sh
+   ```
+4. Run it and choose your options
+   ```shell
+   ./install.sh
+   ```
+5. Run an example from one of the libraries
+   ```shell
+   cd rf24libs/RF24/examples_linux
+   ```
+   Edit the gettingstarted example, to set your pin configuration
+   ```shell
+   nano gettingstarted.cpp
+   make
+   sudo ./gettingstarted
+   ```
+
+## Manual Install
+
+1. Install prerequisites if there are any (MRAA, LittleWire libraries, setup SPI device etc)
+   @note See the [MRAA](http://iotdk.intel.com/docs/master/mraa/index.html) documentation for more info on 
installing MRAA
+2. Make a directory to contain the RF24 and possibly RF24Network lib and enter it
+   ```shell
+   mkdir ~/rf24libs
+   cd ~/rf24libs
+   ```
+3. Clone the RF24 repo
+   ```shell
+   git clone https://github.com/tmrh20/RF24.git RF24
+   ```
+4. Change to the new RF24 directory
+   ```shell
+   cd RF24
+   ```
+5. Configure build environment using
+   ```shell
+   ./configure
+   ```
+   script. It auto detectes device and build environment.
+
+   For overriding autodetections, use command-line switches, see
+   ```shell
+   ./configure --help
+   ```
+   for description.
+6. Build the library, and run an example file
+   ```shell
+   make
+   sudo make install
+   ```
+   ```shell
+   cd examples_linux
+   ```
+   Edit the gettingstarted example, to set your pin configuration
+   ```shell
+   ano gettingstarted.cpp
+   make
+   sudo ./gettingstarted
+   ```
+
+### Build using SPIDEV
+
+1. Make sure that spi device support is enabled and /dev/spidev\<a\>.\<b\> is present
+2. Manual Install using SPIDEV:
+   ```shell
+   ./configure --driver=SPIDEV
+   make
+   sudo make install
+   ```
+3. See the gettingstarted example for an example of pin configuration
diff --git a/arduino/libraries/RF24/docs/main_page.md b/arduino/libraries/RF24/docs/main_page.md
new file mode 100644
index 000000000..2c814d185
--- /dev/null
+++ b/arduino/libraries/RF24/docs/main_page.md
@@ -0,0 +1,82 @@
+# Optimized High Speed Driver for nRF24L01(+) 2.4GHz Wireless Transceiver
+
+## Design Goals
+
+This library fork is designed to be...
+
+- More compliant with the manufacturer specified operation of the chip, while allowing advanced users
+  to work outside the recommended operation.
+- Utilize the capabilities of the radio to their full potential via Arduino
+- More reliable, responsive, bug-free and feature rich
+- Easy for beginners to use, with well documented examples and features
+- Consumed with a public interface that's similar to other Arduino standard libraries
+
+## News
+
+See the releases' descriptions on
+[the library's release page](http://github.com/nRF24/RF24/releases) for a list of
+changes.
+
+## Useful References
+
+- [RF24 Class Documentation](classRF24.html)
+- [Support & Configuration](pages.html)
+- [Source Code](https://github.com/nRF24/RF24/)
+- [nRF24L01 v2.0 Datasheet](http://github.com/nRF24/RF24/raw/master/datasheets/nRF24L01_datasheet_v2.pdf)
+- [nRF24L01+ v1.0 Datasheet](http://github.com/nRF24/RF24/raw/master/datasheets/nRF24L01P_datasheet_v1.pdf)
+
+### Additional Information and Add-ons
+
+- [RF24Network](http://nRF24.github.io/RF24Network): OSI Network Layer for multi-device communication. 
Create a home sensor network.
+- [RF24Mesh](http://nRF24.github.io/RF24Mesh): Dynamic Mesh Layer for RF24Network
+- [RF24Ethernet](http://nRF24.github.io/RF24Ethernet): TCP/IP Radio Mesh Networking (shares Arduino Ethernet 
API)
+- [RF24Audio](http://nRF24.github.io/RF24Audio): Realtime Wireless Audio streaming
+- [TMRh20 Blog: RF24 Optimization 
Overview](http://tmrh20.blogspot.com/2014/03/high-speed-data-transfers-and-wireless.html)
+- [TMRh20: RPi/Linux with 
RF24Gateway](http://tmrh20.blogspot.com/2016/08/raspberry-pilinux-with-nrf24l01.html)
+- [All TMRh20 Documentation Main Page](http://tmrh20.github.io/)
+
+### More Information
+
+- [Project Blog: TMRh20.blogspot.com](http://TMRh20.blogspot.com)
+- [Maniacal Bits Blog](http://maniacalbits.blogspot.ca/)
+- [Original Maniacbug RF24Network Blog Post](https://maniacbug.wordpress.com/2012/03/30/rf24network/)
+- [ManiacBug on GitHub](https://github.com/maniacbug/RF24) (Original Library Author)
+- [MySensors.org](http://www.mysensors.org/) (User friendly sensor networks/IoT)
+
+## Platform Support Pages
+
+- [Arduino](md_docs_arduino.html) (Uno, Nano, Mega, Due, Galileo, etc)
+- [ATTiny](md_docs_attiny.html)
+- [Pico SDK support](md_docs_pico_sdk.html)
+- [Linux Installation](md_docs_linux_install.html) (or the alternative [instructions using 
CMake](md_docs_using_cmake.html)),
+  ([Linux/RPi General](md_docs_rpi_general.html), [MRAA](md_docs_mraa.html) supported boards (Galileo, 
Edison, etc), LittleWire)
+- [Cross-compilation](md_docs_cross_compile.html) for linux devices
+- [Python wrapper](md_docs_python_wrapper.html) available for Linux devices
+
+### General µC Pin layout
+
+@see also the individual board [support pages](pages.html) for more info
+
+Observe
+
+![
+    @image html pinout.jpg
+    ](https://github.com/nRF24/RF24/blob/master/images/pinout.jpg)
+
+The table below shows how to connect the the pins of the NRF24L01(+) to different boards.
+CE and CSN are configurable.
+
+| PIN | NRF24L01 | Arduino UNO | ATtiny25/45/85 [0] | ATtiny44/84 [1] | LittleWire [2]          | RPI        
| RPi -P1 Connector |
+| --- | -------- | ----------- | ------------------ | --------------- | ----------------------- | ---------- 
| ----------------- |
+| 1   | GND      | GND         | pin 4              | pin 14          | GND                     | rpi-gnd    
| (25)              |
+| 2   | VCC      | 3.3V        | pin 8              | pin 1           | regulator 3.3V required | rpi-3v3    
| (17)              |
+| 3   | CE       | digIO 7     | pin 2              | pin 12          | pin to 3.3V             | rpi-gpio22 
| (15)              |
+| 4   | CSN      | digIO 8     | pin 3              | pin 11          | RESET                   | rpi-gpio8  
| (24)              |
+| 5   | SCK      | digIO 13    | pin 7              | pin 9           | SCK                     | rpi-sckl   
| (23)              |
+| 6   | MOSI     | digIO 11    | pin 6              | pin 7           | MOSI                    | rpi-mosi   
| (19)              |
+| 7   | MISO     | digIO 12    | pin 5              | pin 8           | MISO                    | rpi-miso   
| (21)              |
+| 8   | IRQ      | -           | -                  | -               | -                       | -          
| -                 |
+
+- [0] https://learn.sparkfun.com/tutorials/tiny-avr-programmer-hookup-guide/attiny85-use-hints
+- [1] http://highlowtech.org/?p=1695 The ATTiny2313 is unsupported due to lack of sufficient memory 
resources.
+- [2] http://littlewire.github.io/
diff --git a/arduino/libraries/RF24/docs/mraa.md b/arduino/libraries/RF24/docs/mraa.md
new file mode 100644
index 000000000..7d8f5d936
--- /dev/null
+++ b/arduino/libraries/RF24/docs/mraa.md
@@ -0,0 +1,40 @@
+# MRAA
+
+<!-- markdownlint-disable MD031 -->
+MRAA is a Low Level Skeleton Library for Communication on GNU/Linux platforms.
+Review the [MRAA documentation](http://iotdk.intel.com/docs/master/mraa/index.html) for more information
+
+RF24 supports all MRAA supported platforms, but it might not have been tested on each individual platform 
due to the wide range of hardware support:
+
+- [Report an RF24 bug or issue](https://github.com/TMRh20/RF24/issues)
+
+## Setup and installation
+
+Build using the **MRAA** library from [MRAA 
documentation](http://iotdk.intel.com/docs/master/mraa/index.html)
+
+MRAA is not included.
+
+1. Install, and build MRAA
+   ```shell
+   git clone https://github.com/intel-iot-devkit/mraa.git
+   cd mraa
+   mkdir build
+   cd build
+   cmake .. -DBUILDSWIGNODE=OFF
+   sudo make install
+   ```
+2. Complete the install
+   ```shell
+   nano /etc/ld.so.conf
+   ```
+   Add the line
+   ```shell
+   /usr/local/lib/arm-linux-gnueabihf
+   ```
+   Run
+   ```shell
+   sudo ldconfig
+   ```
+3. Install RF24, using MRAA
+
+   See [Linux Install instructions](md_docs_linux_install.html)
diff --git a/arduino/libraries/RF24/docs/pico_sdk.md b/arduino/libraries/RF24/docs/pico_sdk.md
new file mode 100644
index 000000000..cfdddee05
--- /dev/null
+++ b/arduino/libraries/RF24/docs/pico_sdk.md
@@ -0,0 +1,175 @@
+# RaspberryPi Pico SDK (for the RP2040)
+
+<!-- markdownlint-disable MD031 MD046 -->
+Just to be very clear and concise: The following instructions are
+**not required to use the Arduino IDE** with any RP2040-based boards.
+These instructions pertain to using only the
+[Raspberry Pi foundation's Pico SDK](https://github.com/raspberrypi/pico-sdk)
+for RP2040 based boards.
+
+## Prerequisite
+
+Follow the Raspberry Pi Foundation's
+["Getting Started with Pico" document](https://rptl.io/pico-get-started) to
+setup a proper development environment on your host PC (the machine that
+will build your project). Notice that the setup instructions are a little
+different for building on a Windows PC.
+
+Either set an environment variable named `PICO_SDK_PATH` that points to your
+local clone of the pico-sdk or put the pico-sdk next to the RF24 folder or
+next to the folder containing your project using the RF24 library:
+
+    path/to/github/repos/
+        pico-sdk/
+        RF24/
+        my_rf24_project/
+
+Alternatively, the RF24 repository (and optionally the RF24Network and RF24Mesh
+repositories) can be included into your project's "lib" folder as copies or
+git submodules. For more detail, see the below instructions to incorporate
+RF24 libs into your project.
+
+## Building the RF24 examples for the Pico SDK
+
+Each example imports a set of constants (`CE_PIN`, `CSN_PIN`, `IRQ_PIN`) from the
+examples_pico/defaultPins.h file. Default values for SPI pins (SCK, MOSI, MISO)
+come from the Pico SDK repository's
+[pico-sdk/src/boards/include/boards/\<board_name>.h 
files](https://github.com/raspberrypi/pico-sdk/tree/master/src/boards/include/boards)
+
+1. Create a "build" directory in the RF24 repository's root directory and
+   navigate to it:
+   ```shell
+   cd RF24
+   mkdir build
+   cd build
+   ```
+2. Configure CMake for your desired build type and specific RP2040-based board
+   ```shell
+   cmake ../examples_pico -DCMAKE_BUILD_TYPE=Release -DPICO_BOARD=pico
+   ```
+   Or if building on a Windows PC:
+   ```shell
+   cmake -G "NMake Makefiles" ../examples_pico -DCMAKE_BUILD_TYPE=Release -DPICO_BOARD=pico
+   ```
+   The supported RP2040-based boards are listed in header files in the Pico SDK
+   repository's [pico-sdk/src/boards/include/boards/\<board_name>.h 
files](https://github.com/raspberrypi/pico-sdk/tree/master/src/boards/include/boards).
+   If the `-DPICO_BOARD` option is not specified, then the Pico SDK will default to building for the 
Raspberry Pi Pico board.
+3. Build the examples using the CMakeLists.txt file located in the
+   RF24/examples_pico directory.
+   ```shell
+   cmake --build . --config Release
+   ```
+   Notice we specified the build type again using the `--config` option.
+
+   @note If you see an error stating "'PICO_DEFAULT_SPI_SCK_PIN' was not declared in this scope",
+   then it means the board you selected with the `-DPICO_BOARD` option (in step 2) does not have a
+   default set of SPI pins defined for it. To workaround this error, see the below instructions to
+   use different pins for the SPI bus.
+
+   @warning If doing consecutive build attempts, it is strongly encouraged to delete all files in the build
+   directory before re-attempting to build the project.
+
+## Incorporating RF24 libs into your project
+
+In order to use the RF24 libraries in your RP2040 based project:
+
+1. Make a copy of the RF24 library (and optionally RF24Network and RF24Mesh
+   libraries) in a "lib" directory located in your project's root directory.
+
+         path/to/my/project/
+             lib/
+                 RF24/
+                 RF24Network/
+                 RF24Mesh/
+             src/
+                 CMakeLists.txt
+                 ...
+
+   Alternatively you can add the RF24\* repositories as [git 
submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules).
+2. Include their CMakeLists.txt files from the RF24 libraries in your project's top-level
+   CMakeLists.txt file (usually located in the "src" directory). The following snippet
+   assumes that your project's "src" directory is on the same level as the previously
+   mentioned "lib" directory.
+   ```cmake
+   include(../lib/RF24/CMakeLists.txt)
+   include(../lib/RF24Network/CMakeLists.txt)
+   include(../lib/RF24Mesh/CMakeLists.txt)
+   ```
+3. In the same CMakeLists.txt file from step 2, add the RF24 libraries into the
+   `target_link_libraries` configuration:
+   ```cmake
+   target_link_libraries(${CMAKE_PROJECT_NAME}
+       # ... Your project's other libraries ...
+       RF24
+       RF24Network
+       RF24Mesh
+   )
+   ```
+   If you are using tinyUSB, this line (or similar) should already exist:
+   ```cmake
+   target_include_directories(${CMAKE_PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR})
+   ```
+4. Finally, remember to include the necessary RF24\* libraries' header files in your
+   project's source code where applicable.
+   ```cpp
+   #include <RF24.h>
+   #include <RF24Network.h>
+   #include <RF24Mesh.h>
+   ```
+   @note Any of the project's source file(s) that includes any of RF24\* libraries must be a C++ file.
+   It is highly recommended to use the `.cpp` file extension for such project source files.
+
+## Using different pins for the SPI bus
+
+Initially (without modification), the SPI bus uses the default pins defined in the
+Pico SDK repository's [pico-sdk/src/boards/include/boards/\<board_name>.h 
files](https://github.com/raspberrypi/pico-sdk/tree/master/src/boards/include/boards).
+However, there may be some boards that do not define the necessary pins to use as defaults. This can
+be rememdied using either project source code or build-time configuration.
+
+@warning There is no support for software driven SPI on RP2040 based boards at this time.
+If someone is so inclined to implement this using the Pico SDK's PIO (Programable Input
+Output) feature, please submit an issue or pull request to the
+[RF24 repository](http://github.com/nRF24/RF24).
+
+@note Before deciding what pins to use for the SPI bus, review the
+[GPIO pins' "Function Select Table" in the Pico SDK 
documentation](https://raspberrypi.github.io/pico-sdk-doxygen/group__hardware__gpio.html#details).
+There are essentially 2 SPI buses with multiple pin options for each.
+
+### Project Source code option
+
+This option is the most reliable and flexible. It involves calling `SPI.begin()` and then passing the `SPI` 
object to `RF24::begin(_SPI*)`.
+
+```cpp
+#include <RF24.h>
+
+RF24 radio(7, 8); // pin numbers connected to the radio's CE and CSN pins (respectively)
+
+int main()
+{
+    // again please review the GPIO pins' "Function Select Table" in the Pico SDK docs
+    spi.begin(spi0, 2, 3, 4); // spi0 or spi1 bus, SCK, TX, RX
+
+    if (!radio.begin(&spi)) {
+        printf("Radio hardware is not responding!\n");
+    }
+    // continue with program as normal ...
+}
+```
+
+### Build-time configuration option
+
+To specify the default SPI pins used at build time, you can use either:
+
+1. declare these pins in the CMakeLists.txt file
+   ```cmake
+   target_compile_definitions(${CMAKE_PROJECT_NAME}
+       PUBLIC PICO_DEFAULT_SPI=0 # can only be 0 or 1 (as in `spi0` or `spi1`)
+       PUBLIC PICO_DEFAULT_SPI_SCK_PIN=2 # depends on which SPI bus (0 or 1) is being used
+       PUBLIC PICO_DEFAULT_SPI_TX_PIN=3  # depends on which SPI bus (0 or 1) is being used
+       PUBLIC PICO_DEFAULT_SPI_RX_PIN=4  # depends on which SPI bus (0 or 1) is being used
+   )
+   ```
+2. additional command line arguments
+   ```shell
+   cmake --build . --config Release -DPICO_DEFAULT_SPI=0 -DPICO_DEFAULT_SPI_SCK_PIN=2 
-DPICO_DEFAULT_SPI_TX_PIN=3 -DPICO_DEFAULT_SPI_RX_PIN=4
+   ```
diff --git a/arduino/libraries/RF24/docs/portability.md b/arduino/libraries/RF24/docs/portability.md
new file mode 100644
index 000000000..3d92ebbaa
--- /dev/null
+++ b/arduino/libraries/RF24/docs/portability.md
@@ -0,0 +1,38 @@
+# RF24 Portability
+
+The RF24 radio driver mainly utilizes the [Arduino API](http://arduino.cc/en/reference/homePage)
+for GPIO, SPI, and timing functions, which are easily replicated on various platforms.
+
+Support files for these platforms are stored under RF24/utility, and can be modified to provide the
+required functionality.
+
+## Basic Hardware Template
+
+### RF24/utility
+
+The RF24 library now includes a basic hardware template to assist in porting to various platforms.
+
+The following files can be included to replicate standard Arduino functions as needed, allowing devices from 
ATTiny to Raspberry Pi to utilize the same core RF24 driver.
+
+| File               | Purpose                                                                      |
+| ------------------ | ---------------------------------------------------------------------------- |
+| RF24_arch_config.h | Basic Arduino/AVR compatibility, includes for remaining support files, etc   |
+| includes.h         | Linux only. Defines specific platform, include correct RF24_arch_config file |
+| spi.h              | Provides standardized SPI (`transfer()`) methods                             |
+| gpio.h             | Provides standardized GPIO (`digitalWrite()`) methods                        |
+| compatibility.h    | Provides standardized timing (`millis()`, `delay()`) methods                 |
+| your_custom_file.h | Provides access to custom drivers for spi, gpio, etc                         |
+
+Examples are provided via the included hardware support templates in **RF24/utility**
+
+See the [modules](modules.html) page for examples of class declarations.
+
+## Device Detection
+
+1. The main detection for Linux devices is done in the configure script, with the includes.h from the proper 
hardware directory copied to RF24/utility/includes.h
+2. Secondary detection is completed in RF24_config.h, causing the include.h file to be included for all 
supported Linux devices
+3. RF24.h contains the declaration for SPI and GPIO objects 'spi' and 'gpio' to be used for porting-in 
related functions.
+
+## Code
+
+To have your ported code included in this library, or for assistance in porting, create a pull request or 
open an issue at [RF24 Library issues](https://github.com/nRF24/RF24)
diff --git a/arduino/libraries/RF24/docs/python_wrapper.md b/arduino/libraries/RF24/docs/python_wrapper.md
new file mode 100644
index 000000000..d2e777d18
--- /dev/null
+++ b/arduino/libraries/RF24/docs/python_wrapper.md
@@ -0,0 +1,101 @@
+# Python Wrapper
+
+<!-- markdownlint-disable MD031 -->
+By [mz-fuzzy](https://github.com/mz-fuzzy)
+
+## Python Wrapper Prerequisites
+
+### RF24
+
+The RF24 lib needs to be built in C++ & installed for the python wrapper to wrap it.
+
+See [Linux Installation](md_docs_linux_install.html) (or [installing with CMake](md_docs_using_cmake.html)
+alternatively) and [Linux/RPi General](md_docs_rpi_general.html)
+
+### Python2
+
+```shell
+sudo apt-get install python-dev libboost-python-dev python-pip python-rpi.gpio
+```
+
+Next, install some up-to-date python packages.
+
+```shell
+python -m pip install --upgrade pip setuptools
+```
+
+### Python3
+
+```shell
+sudo apt-get install python3-dev libboost-python-dev python3-pip python3-rpi.gpio
+```
+
+Next, install some up-to-date python3 packages.
+
+```shell
+python3 -m pip install --upgrade pip setuptools
+```
+
+## Installation
+
+@note Steps 2 and 3 have to be repeated if installing the python wrappers for
+RF24Network and RF24Mesh libraries. The prerequisites stated above still apply
+to each library.
+
+1. For python3, setup.py needs a manually created symlink for the boost.python library:
+   ```shell
+   sudo ln -s $(ls /usr/lib/$(ls /usr/lib/gcc | tail -1)/libboost_python3*.so | tail -1) /usr/lib/$(ls 
/usr/lib/gcc | tail -1)/libboost_python3.so
+   ```
+2. Build the library.
+
+   This step and the next step need to be executed from the appropriate directory of
+   the cloned RF24* repository:
+   - navigate to *pyRF24* directory in the RF24 cloned repository
+   - navigate to *RPi/pyRF24Network* directory in the RF24Network cloned repository
+   - navigate to *pyRF24Mesh* directory in the RF24Mesh cloned repository
+
+   When in the correct directory, run the following command:
+   ```shell
+   ./setup.py build
+   ```
+   or for python3
+   ```shell
+   python3 setup.py build
+   ```
+   @note Build takes several minutes on arm-based machines. Machines with RAM less than 1GB may need to 
increase amount of swap for build.
+3. Install the library
+   ```shell
+   sudo ./setup.py install
+   ```
+   or for python3
+   ```shell
+   sudo python3 setup.py install
+   ```
+   See the additional [Platform Support pages](pages.html) for information on connecting your hardware.
+
+   See the included [\*.py files in the "examples_linux" folder](examples.html) for usage information.
+4. Running the Example
+
+   The python examples location differ for each RF24* resopitories.
+   - navigate to *examples_linux* directory in the RF24 cloned repository
+   - navigate to *RPi/pyRF24Network/examples* directory in the RF24Network cloned repository
+   - navigate to *examples_RPi* directory in the RF24Mesh cloned repository
+
+   Navigate to the examples_linux directory
+   ```shell
+   cd ../examples_linux
+   ```
+   Edit the getting_started.py example to configure the appropriate pins per the [Linux/RPi 
General](md_docs_rpi_general.html) documentation.
+   ```shell
+   nano getting_started.py
+   ```
+   Configure another device, Arduino or RPi with the same example code. It could be C++ or python (see the 
[list of available examples](examples.html)), but we'll use the same example file on a different device in 
this tutorial.
+
+   Run the example
+   ```shell
+   sudo python getting_started.py
+   ```
+   or for python3
+   ```shell
+   sudo python3 getting_started.py
+   ```
diff --git a/arduino/libraries/RF24/docs/rpi_general.md b/arduino/libraries/RF24/docs/rpi_general.md
new file mode 100644
index 000000000..1081b2c08
--- /dev/null
+++ b/arduino/libraries/RF24/docs/rpi_general.md
@@ -0,0 +1,91 @@
+# Linux General/Raspberry Pi
+
+<!-- markdownlint-disable MD031 -->
+RF24 supports a variety of Linux based devices via various drivers. Some boards like RPi can utilize 
multiple methods
+to drive the GPIO and SPI functionality.
+
+## Potential PreConfiguration
+
+If SPI is not already enabled, load it on boot:
+
+```shell
+sudo raspi-config
+```
+
+1. Update the tool via the menu as required
+2. Select **Advanced** and **enable the SPI kernel module**
+3. Update other software and libraries
+   ```shell
+   sudo apt-get update
+   sudo apt-get upgrade
+   ```
+
+## Build Options
+
+The default build on Raspberry Pi utilizes the included **BCM2835** driver from [the BCM2835 
Library](http://www.airspayce.com/mikem/bcm2835)
+
+1. See [the Linux section for automated installation](md_docs_linux_install.html).
+2. Manual install:
+   ```shell
+   make
+   sudo make install
+   ```
+
+## Connections and Pin Configuration
+
+Using pin 15(GPIO22) for CE, pin 24(GPIO8 commonly labeled as CE0) for CSN
+
+Can use any available SPI BUS for CSN.
+
+In general, use
+
+```cpp
+RF24 radio(<ce_pin>, <a>*10+<b>);
+```
+
+for proper constructor to address the correct spi device at /dev/spidev\<a\>.\<b\>
+
+Choose any GPIO output pin for radio CE pin.
+
+### General
+
+```cpp
+RF24 radio(22,0);
+```
+
+### MRAA Constructor
+
+```cpp
+RF24 radio(15,0);
+```
+
+See [the MRAA documentation for Raspberry Pi support](http://iotdk.intel.com/docs/master/mraa/rasppi.html)
+
+### SPI_DEV Constructor
+
+```cpp
+RF24 radio(22, 0);
+```
+
+See [the Raspberry Pi documentation about the GPIO 
pins](https://www.raspberrypi.com/documentation/computers/os.html#gpio-and-the-40-pin-header)
+
+### Pins
+
+| PIN | NRF24L01 | RPI        | RPi -P1 Connector |
+| --- | -------- | ---------- | ----------------- |
+| 1   | GND      | rpi-gnd    | (25)              |
+| 2   | VCC      | rpi-3v3    | (17)              |
+| 3   | CE       | rpi-gpio22 | (15)              |
+| 4   | CSN      | rpi-gpio8  | (24)              |
+| 5   | SCK      | rpi-sckl   | (23)              |
+| 6   | MOSI     | rpi-mosi   | (19)              |
+| 7   | MISO     | rpi-miso   | (21)              |
+| 8   | IRQ      | -          | -                 |
+
+## breif history of RF24 library lineage
+
+Based on the arduino lib from [J. Coliz](maniacbug ymail com),
+the library was berryfied by [Purinda Gunasekara](purinda gmail com)
+then forked from github [stanleyseow/RF24](https://github.com/stanleyseow/RF24) to 
[jscrane/RF24-rpi](https://github.com/jscrane/RF24-rpi)
+
+Network lib also based on [farconada/RF24Network](https://github.com/farconada/RF24Network)
diff --git a/arduino/libraries/RF24/docs/sphinx/README.md b/arduino/libraries/RF24/docs/sphinx/README.md
new file mode 100644
index 000000000..5ca5047f1
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/README.md
@@ -0,0 +1,3 @@
+# Intended for Sphinx only
+
+The files in this folder are only used to generate documentation using Sphinx (from Doxygen's XML output).
diff --git a/arduino/libraries/RF24/docs/sphinx/RF24_config_8h.rst 
b/arduino/libraries/RF24/docs/sphinx/RF24_config_8h.rst
new file mode 100644
index 000000000..8bfe17def
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/RF24_config_8h.rst
@@ -0,0 +1,7 @@
+RF24_config.h
+=============
+
+.. literalinclude:: ../../RF24_config.h
+    :linenos:
+    :lineno-match:
+    :start-at: /*** USER DEFINES:    ***/
diff --git a/arduino/libraries/RF24/docs/sphinx/_static/Logo large.png 
b/arduino/libraries/RF24/docs/sphinx/_static/Logo large.png
new file mode 100644
index 000000000..25e42159f
Binary files /dev/null and b/arduino/libraries/RF24/docs/sphinx/_static/Logo large.png differ
diff --git a/arduino/libraries/RF24/docs/sphinx/_static/custom_material.css 
b/arduino/libraries/RF24/docs/sphinx/_static/custom_material.css
new file mode 100644
index 000000000..aef855bd4
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/_static/custom_material.css
@@ -0,0 +1,107 @@
+.md-typeset .admonition,
+.md-typeset details {
+  font-size: 0.75rem;
+}
+
+.md-typeset .admonition.tip>.admonition-title::before,
+.md-typeset .admonition.hint>.admonition-title::before {
+  mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg"; viewBox="0 0 24 
24"><path d="M12,3L1,9L12,15L21,10.09V17H23V9M5,13.18V17.18L12,21L19,17.18V13.18L12,17L5,13.18Z" /></svg>');
+}
+
+.md-typeset .admonition.seealso>.admonition-title::before {
+  mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg"; viewBox="0 0 576 
512"><path d="M572.52 241.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 
29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400a144 144 0 
1 1 144-144 143.93 143.93 0 0 1-144 144zm0-240a95.31 95.31 0 0 0-25.31 3.79 47.85 47.85 0 0 1-66.9 66.9A95.78 
95.78 0 1 0 288 160z"/></svg>');
+  background-color: hsl(301, 100%, 63%);
+}
+
+.md-typeset .admonition.seealso {
+  border-left: .2rem solid hsl(301, 100%, 63%);
+}
+
+.md-typeset .admonition.seealso>.admonition-title {
+  background-color: hsla(287, 100%, 63%, 0.25);
+}
+
+.md-typeset .admonition.important>.admonition-title::before {
+  mask-image: url('data:image/svg+xml;charset=utf-8,<svg xmlns="http://www.w3.org/2000/svg"; viewBox="0 0 512 
512"><path d="M256 8C119.043 8 8 119.083 8 256c0 136.997 111.043 248 248 248s248-111.003 248-248C504 119.083 
392.957 8 256 8zm0 110c23.196 0 42 18.804 42 42s-18.804 42-42 42-42-18.804-42-42 18.804-42 42-42zm56 254c0 
6.627-5.373 12-12 12h-88c-6.627 0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h12v-64h-12c-6.627 
0-12-5.373-12-12v-24c0-6.627 5.373-12 12-12h64c6.627 0 12 5.373 12 12v100h12c6.627 0 12 5.373 12 
12v24z"/></svg>');
+  background-color: hsl(123, 100%, 63%);
+}
+
+.md-typeset .admonition.important {
+  border-left: .2rem solid hsl(123, 100%, 63%);
+}
+
+.md-typeset .admonition.important>.admonition-title {
+  background-color: hsla(123, 100%, 63%, 0.25);
+}
+
+.md-typeset .admonition.warning>.admonition-title::before {
+  background-color: hsl(0, 100%, 63%);
+}
+
+.md-typeset .admonition.warning {
+  border-left: .2rem solid hsl(0, 100%, 63%);
+}
+
+.md-typeset .admonition.warning>.admonition-title {
+  background-color: hsla(0, 100%, 63%, 0.25);
+}
+
+html .md-nav--primary .md-nav__title--site .md-nav__button {
+  top: 0;
+  left: 0;
+  width: inherit;
+  height: auto;
+}
+
+.md-typeset table:not([class]) th {
+  background-color: rgba(23, 35, 83, 0.8);
+}
+
+.md-typeset table:not([class]) tr:hover {
+  background-color: rgba(56, 2, 81, 0.8);
+  box-shadow: inset 0 .05rem 0 #9515ff;
+}
+
+[data-md-color-scheme="default"] {
+  --md-code-bg-color: #e8e7e7;
+}
+
+.md-nav__title .md-nav__button.md-logo img, .md-nav__title .md-nav__button.md-logo svg {
+  height: 3rem;
+  width: auto;
+}
+
+.md-header__button.md-logo img, .md-header__button.md-logo svg {
+  width: auto;
+}
+
+.linenos {
+       background-color: var(--md-default-bg-color--light);
+       margin-right: 0.5rem;
+}
+
+/* ************* temp workaround styling multi-line API signatures ******************* */
+/* In the future, we plan to do this without CSS in a more programatic way, but for now...  */
+
+.md-typeset dl.objdesc>dt.sig-wrap .n + .sig-paren::before,
+.md-typeset dl.api-field>dt.sig-wrap .n + .sig-paren::before {
+  content: "\a ";
+  white-space: pre;
+}
+
+.md-typeset dl.objdesc>dt.sig-wrap .sig-paren + .n:not(.sig-param)::before,
+.md-typeset dl.api-field>dt.sig-wrap .sig-paren + .n:not(.sig-param)::before,
+.md-typeset dl.objdesc>dt.sig-wrap .sig-param + .sig-param::before,
+.md-typeset dl.api-field>dt.sig-wrap .n.sig-param + .kt::before,
+.md-typeset dl.objdesc>dt.sig-wrap .n.sig-param + .kt::before,
+.md-typeset dl.api-field>dt.sig-wrap .n.sig-param + .n:not(.sig-param)::before,
+.md-typeset dl.objdesc>dt.sig-wrap .n.sig-param + .n:not(.sig-param)::before {
+  content: "\a     ";
+  white-space: break-spaces;
+}
+
+.md-typeset dl.api-field>dt.sig-wrap .sig-param:before,
+.md-typeset dl.objdesc>dt.sig-wrap .sig-param:before {
+  content: unset;
+  white-space: pre;
+}
diff --git a/arduino/libraries/RF24/docs/sphinx/_static/new_favicon.ico 
b/arduino/libraries/RF24/docs/sphinx/_static/new_favicon.ico
new file mode 100644
index 000000000..c15a1650a
Binary files /dev/null and b/arduino/libraries/RF24/docs/sphinx/_static/new_favicon.ico differ
diff --git a/arduino/libraries/RF24/docs/sphinx/classRF24.rst 
b/arduino/libraries/RF24/docs/sphinx/classRF24.rst
new file mode 100644
index 000000000..42c16c883
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/classRF24.rst
@@ -0,0 +1,101 @@
+RF24 class
+~~~~~~~~~~
+
+.. cpp:class:: RF24
+
+    .. doxygenfunction:: RF24::RF24 (uint16_t,uint16_t,uint32_t)
+    .. doxygenfunction:: RF24::RF24 (uint32_t _spi_speed=RF24_SPI_SPEED)
+
+Basic API
+============
+
+.. doxygenfunction:: RF24::begin (void)
+.. doxygenfunction:: RF24::begin (_SPI *spiBus)
+.. doxygenfunction:: RF24::begin (_SPI *spiBus, uint16_t _cepin, uint16_t _cspin)
+.. doxygenfunction:: RF24::begin (uint16_t _cepin, uint16_t _cspin)
+.. doxygenfunction:: RF24::isChipConnected
+.. doxygenfunction:: RF24::startListening
+.. doxygenfunction:: RF24::stopListening
+.. doxygenfunction:: RF24::available (void)
+.. doxygenfunction:: RF24::read
+.. doxygenfunction:: RF24::write (const void *buf, uint8_t len)
+.. doxygenfunction:: RF24::openWritingPipe (const uint8_t *address)
+.. doxygenfunction:: RF24::openWritingPipe (uint64_t address)
+.. doxygenfunction:: RF24::openReadingPipe (uint8_t number, const uint8_t *address)
+.. doxygenfunction:: RF24::openReadingPipe (uint8_t number, uint64_t address)
+
+Advanced API
+============
+
+.. doxygenvariable:: RF24::failureDetected
+.. doxygenfunction:: RF24::printDetails
+.. doxygenfunction:: RF24::printPrettyDetails
+.. doxygenfunction:: RF24::available (uint8_t *pipe_num)
+.. doxygenfunction:: RF24::rxFifoFull
+.. doxygenfunction:: RF24::powerDown
+.. doxygenfunction:: RF24::powerUp
+.. doxygenfunction:: RF24::write (const void *buf, uint8_t len, const bool multicast)
+.. doxygenfunction:: RF24::writeFast (const void *buf, uint8_t len)
+.. doxygenfunction:: RF24::writeFast (const void *buf, uint8_t len, const bool multicast)
+.. doxygenfunction:: RF24::writeBlocking
+.. doxygenfunction:: RF24::txStandBy()
+.. doxygenfunction:: RF24::txStandBy (uint32_t timeout, bool startTx=0)
+.. doxygenfunction:: RF24::writeAckPayload
+.. doxygenfunction:: RF24::isAckPayloadAvailable
+.. doxygenfunction:: RF24::whatHappened
+.. doxygenfunction:: RF24::startFastWrite
+.. doxygenfunction:: RF24::startWrite
+.. doxygenfunction:: RF24::reUseTX
+.. doxygenfunction:: RF24::flush_tx
+.. doxygenfunction:: RF24::flush_rx
+.. doxygenfunction:: RF24::testCarrier
+.. doxygenfunction:: RF24::testRPD
+.. doxygenfunction:: RF24::isValid
+.. doxygenfunction:: RF24::closeReadingPipe
+
+Configuration API
+==================
+
+.. doxygenvariable:: RF24::txDelay
+.. doxygenvariable:: RF24::csDelay
+.. doxygenfunction:: RF24::setAddressWidth
+.. doxygenfunction:: RF24::setRetries
+.. doxygenfunction:: RF24::setChannel
+.. doxygenfunction:: RF24::getChannel
+.. doxygenfunction:: RF24::setPayloadSize
+.. doxygenfunction:: RF24::getPayloadSize
+.. doxygenfunction:: RF24::getDynamicPayloadSize
+.. doxygenfunction:: RF24::enableAckPayload
+.. doxygenfunction:: RF24::disableAckPayload
+.. doxygenfunction:: RF24::enableDynamicPayloads
+.. doxygenfunction:: RF24::disableDynamicPayloads
+.. doxygenfunction:: RF24::enableDynamicAck
+.. doxygenfunction:: RF24::isPVariant
+.. doxygenfunction:: RF24::setAutoAck (bool enable)
+.. doxygenfunction:: RF24::setAutoAck (uint8_t pipe, bool enable)
+.. doxygenfunction:: RF24::setPALevel
+.. doxygenfunction:: RF24::getPALevel
+.. doxygenfunction:: RF24::getARC
+.. doxygenfunction:: RF24::setDataRate
+.. doxygenfunction:: RF24::getDataRate
+.. doxygenfunction:: RF24::setCRCLength
+.. doxygenfunction:: RF24::getCRCLength
+.. doxygenfunction:: RF24::disableCRC
+.. doxygenfunction:: RF24::maskIRQ
+.. doxygenfunction:: RF24::startConstCarrier
+.. doxygenfunction:: RF24::stopConstCarrier
+.. doxygenfunction:: RF24::toggleAllPipes
+.. doxygenfunction:: RF24::setRadiation
+
+Protected API
+==============
+
+These are the members and functions made available to derivatives that inherit from the RF24 class.
+
+.. doxygenfunction:: RF24::beginTransaction
+.. doxygenfunction:: RF24::endTransaction
+.. doxygenfunction:: RF24::read_register (uint8_t reg, uint8_t *buf, uint8_t len)
+.. doxygenfunction:: RF24::read_register (uint8_t reg)
+.. doxygenvariable:: RF24::ack_payloads_enabled
+.. doxygenvariable:: RF24::addr_width
+.. doxygenvariable:: RF24::dynamic_payloads_enabled
diff --git a/arduino/libraries/RF24/docs/sphinx/conf.py b/arduino/libraries/RF24/docs/sphinx/conf.py
new file mode 100644
index 000000000..43cfb45ac
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/conf.py
@@ -0,0 +1,151 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+import subprocess
+import os
+import json
+
+# -- Project information -----------------------------------------------------
+
+project = "RF24 library"
+copyright = "2021, nRF24 org"
+author = "nRF24"
+
+# The full version, including alpha/beta/rc tags
+release = "1.4.2"  # the minimum version that supports sphinx builds and RTD hosting
+with open("../../library.json", "rb") as lib_json:
+    # get updated info from PlatformIO JSON
+    release = json.load(lib_json)["version"]
+
+# The language for content autogenerated by Sphinx. Refer to documentation
+# for a list of supported languages.
+# This is also used if you do content translation via gettext catalogs.
+# Usually you set "language" from the command line for these cases.
+language = "en"
+
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+    "breathe",
+    "sphinx_immaterial",
+    "sphinx.ext.intersphinx",
+    "sphinx.ext.autosectionlabel",
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ["_templates"]
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
+
+# code-blocks will use this as their default syntax highlighting
+highlight_language="c++"
+primary_domain="cpp"
+
+# If true, '()' will be appended to :func: etc. cross-reference text.
+add_function_parentheses = True
+
+# -- Options for breathe (XML output from doxygen) ---------------------------
+
+breathe_projects = {"RF24": "xml"}
+breathe_default_project = "RF24"
+breathe_show_define_initializer = True
+breathe_show_enumvalue_initializer = True
+breathe_domain_by_extension = { "h" : "cpp" }
+
+READTHEDOCS = os.environ.get('READTHEDOCS', None) == 'True'
+
+if READTHEDOCS:
+    subprocess.call("cd ../..; doxygen", shell=True)
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = "sphinx_immaterial"
+html_theme_options = {
+    "features": [
+        # "navigation.expand",
+        "navigation.tabs",
+        # "toc.integrate",
+        # "navigation.sections",
+        "navigation.instant",
+        # "header.autohide",
+        "navigation.top",
+        # "search.highlight",
+        "search.share",
+    ],
+    "palette": [
+        {
+            "media": "(prefers-color-scheme: dark)",
+            "scheme": "slate",
+            "primary": "lime",
+            "accent": "light-blue",
+            "toggle": {
+                "icon": "material/lightbulb",
+                "name": "Switch to light mode",
+            },
+        },
+        {
+            "media": "(prefers-color-scheme: light)",
+            "scheme": "default",
+            "primary": "light-blue",
+            "accent": "green",
+            "toggle": {
+                "icon": "material/lightbulb-outline",
+                "name": "Switch to dark mode",
+            },
+        },
+    ],
+    # Set the repo location to get a badge with stats
+    "repo_url": "https://github.com/nRF24/RF24/";,
+    "repo_name": "RF24",
+    "repo_type": "github",
+    # Visible levels of the global TOC; -1 means unlimited
+    "globaltoc_depth": 3,
+    # If False, expand all TOC entries
+    "globaltoc_collapse": False,
+    # If True, show hidden TOC entries
+    "globaltoc_includehidden": True,
+}
+# Set link name generated in the top bar.
+html_title = "RF24 C++ library"
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ["_static"]
+
+# The name of an image file (relative to this directory) to use as a favicon of
+# the docs.  This file should be a Windows icon file (.ico) being 16x16 or 32x32
+# pixels large.
+#
+html_favicon = "_static/new_favicon.ico"
+
+# project logo
+html_logo = "_static/Logo large.png"
+
+# These paths are either relative to html_static_path
+# or fully qualified paths (eg. https://...)
+html_css_files = [
+    "custom_material.css",
+]
diff --git a/arduino/libraries/RF24/docs/sphinx/deprecated.rst 
b/arduino/libraries/RF24/docs/sphinx/deprecated.rst
new file mode 100644
index 000000000..3e1b5a0a5
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/deprecated.rst
@@ -0,0 +1,5 @@
+Deprecated API
+==============
+
+.. doxygenpage:: deprecated
+   :content-only:
diff --git a/arduino/libraries/RF24/docs/sphinx/enums.rst b/arduino/libraries/RF24/docs/sphinx/enums.rst
new file mode 100644
index 000000000..4c6090539
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/enums.rst
@@ -0,0 +1,12 @@
+Enumerations
+============
+
+
+.. doxygengroup:: PALevel
+    :members:
+
+.. doxygengroup:: Datarate
+    :members:
+
+.. doxygengroup:: CRCLength
+    :members:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples.rst b/arduino/libraries/RF24/docs/sphinx/examples.rst
new file mode 100644
index 000000000..3dbdead76
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples.rst
@@ -0,0 +1,58 @@
+Examples
+==========
+
+Arduino Examples
+----------------
+
+.. toctree::
+    :maxdepth: 1
+
+    examples/Arduino/GettingStarted
+    examples/Arduino/AcknowledgementPayloads
+    examples/Arduino/ManualAcknowledgements
+    examples/Arduino/StreamingData
+    examples/Arduino/MulticeiverDemo
+    examples/Arduino/Scanner
+    examples/Arduino/InterruptConfigure
+
+Linux Examples
+----------------
+
+.. toctree::
+    :maxdepth: 1
+
+    examples/Linux/GettingStarted
+    examples/Linux/AcknowledgementPayloads
+    examples/Linux/ManualAcknowledgements
+    examples/Linux/StreamingData
+    examples/Linux/MulticeiverDemo
+    examples/Linux/Scanner
+    examples/Linux/InterruptConfigure
+
+PicoSDK Examples
+----------------
+
+.. toctree::
+    :maxdepth: 1
+
+    examples/PicoSDK/default_pins
+    examples/PicoSDK/GettingStarted
+    examples/PicoSDK/AcknowledgementPayloads
+    examples/PicoSDK/ManualAcknowledgements
+    examples/PicoSDK/StreamingData
+    examples/PicoSDK/MulticeiverDemo
+    examples/PicoSDK/Scanner
+    examples/PicoSDK/InterruptConfigure
+
+Python Examples
+----------------
+
+.. toctree::
+    :maxdepth: 1
+
+    examples/Python/GettingStarted
+    examples/Python/AcknowledgementPayloads
+    examples/Python/ManualAcknowledgements
+    examples/Python/StreamingData
+    examples/Python/MulticeiverDemo
+    examples/Python/InterruptConfigure
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Arduino/AcknowledgementPayloads.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Arduino/AcknowledgementPayloads.rst
new file mode 100644
index 000000000..70deeb2fe
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Arduino/AcknowledgementPayloads.rst
@@ -0,0 +1,7 @@
+AcknowledgementPayloads.ino
+===========================
+
+.. literalinclude:: ../../../../examples/AcknowledgementPayloads/AcknowledgementPayloads.ino
+    :lines: 7-
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Arduino/GettingStarted.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Arduino/GettingStarted.rst
new file mode 100644
index 000000000..f873a6750
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Arduino/GettingStarted.rst
@@ -0,0 +1,7 @@
+GettingStarted.ino
+==================
+
+.. literalinclude:: ../../../../examples/GettingStarted/GettingStarted.ino
+    :lines: 7-
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Arduino/InterruptConfigure.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Arduino/InterruptConfigure.rst
new file mode 100644
index 000000000..fe59f38d6
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Arduino/InterruptConfigure.rst
@@ -0,0 +1,7 @@
+InterruptConfigure.ino
+======================
+
+.. literalinclude:: ../../../../examples/InterruptConfigure/InterruptConfigure.ino
+    :lines: 7-
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Arduino/ManualAcknowledgements.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Arduino/ManualAcknowledgements.rst
new file mode 100644
index 000000000..ad6b904c2
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Arduino/ManualAcknowledgements.rst
@@ -0,0 +1,7 @@
+ManualAcknowledgements.ino
+==========================
+
+.. literalinclude:: ../../../../examples/ManualAcknowledgements/ManualAcknowledgements.ino
+    :lines: 7-
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Arduino/MulticeiverDemo.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Arduino/MulticeiverDemo.rst
new file mode 100644
index 000000000..1e30f7b00
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Arduino/MulticeiverDemo.rst
@@ -0,0 +1,7 @@
+MulticeiverDemo.ino
+====================
+
+.. literalinclude:: ../../../../examples/MulticeiverDemo/MulticeiverDemo.ino
+    :lines: 7-
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Arduino/Scanner.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Arduino/Scanner.rst
new file mode 100644
index 000000000..e9e64bce7
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Arduino/Scanner.rst
@@ -0,0 +1,7 @@
+Scanner.ino
+==================
+
+.. literalinclude:: ../../../../examples/scanner/scanner.ino
+    :lines: 10-
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Arduino/StreamingData.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Arduino/StreamingData.rst
new file mode 100644
index 000000000..b13add5da
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Arduino/StreamingData.rst
@@ -0,0 +1,7 @@
+StreamingData.ino
+==================
+
+.. literalinclude:: ../../../../examples/StreamingData/StreamingData.ino
+    :lines: 7-
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Linux/AcknowledgementPayloads.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Linux/AcknowledgementPayloads.rst
new file mode 100644
index 000000000..00473fb66
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Linux/AcknowledgementPayloads.rst
@@ -0,0 +1,7 @@
+AcknowledgementPayloads.cpp
+===========================
+
+.. literalinclude:: ../../../../examples_linux/acknowledgementPayloads.cpp
+    :lines: 7-
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Linux/GettingStarted.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Linux/GettingStarted.rst
new file mode 100644
index 000000000..d4d5c24db
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Linux/GettingStarted.rst
@@ -0,0 +1,7 @@
+GettingStarted.cpp
+==================
+
+.. literalinclude:: ../../../../examples_linux/gettingstarted.cpp
+    :lines: 7-
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Linux/InterruptConfigure.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Linux/InterruptConfigure.rst
new file mode 100644
index 000000000..7591bb0f9
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Linux/InterruptConfigure.rst
@@ -0,0 +1,7 @@
+InterruptConfigure.cpp
+======================
+
+.. literalinclude:: ../../../../examples_linux/interruptConfigure.cpp
+    :lines: 7-
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Linux/ManualAcknowledgements.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Linux/ManualAcknowledgements.rst
new file mode 100644
index 000000000..a2a8b9a04
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Linux/ManualAcknowledgements.rst
@@ -0,0 +1,7 @@
+ManualAcknowledgements.cpp
+==========================
+
+.. literalinclude:: ../../../../examples_linux/manualAcknowledgements.cpp
+    :lines: 7-
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Linux/MulticeiverDemo.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Linux/MulticeiverDemo.rst
new file mode 100644
index 000000000..bb4e9e816
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Linux/MulticeiverDemo.rst
@@ -0,0 +1,7 @@
+MulticeiverDemo.cpp
+====================
+
+.. literalinclude:: ../../../../examples_linux/multiceiverDemo.cpp
+    :lines: 7-
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Linux/Scanner.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Linux/Scanner.rst
new file mode 100644
index 000000000..4cd1b6fd8
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Linux/Scanner.rst
@@ -0,0 +1,7 @@
+Scanner.cpp
+==================
+
+.. literalinclude:: ../../../../examples_linux/scanner.cpp
+    :lines: 15-
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Linux/StreamingData.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Linux/StreamingData.rst
new file mode 100644
index 000000000..53121fae5
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Linux/StreamingData.rst
@@ -0,0 +1,7 @@
+StreamingData.cpp
+==================
+
+.. literalinclude:: ../../../../examples_linux/streamingData.cpp
+    :lines: 7-
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/AcknowledgementPayloads.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/AcknowledgementPayloads.rst
new file mode 100644
index 000000000..d7163893a
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/AcknowledgementPayloads.rst
@@ -0,0 +1,9 @@
+acknowledgementPayloads
+===========================
+
+.. seealso::
+    `defaultPins.h <default_pins.html>`_
+
+.. literalinclude:: ../../../../examples_pico/acknowledgementPayloads.cpp
+    :caption: examples_pico/acknowledgementPayloads.cpp
+    :linenos:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/GettingStarted.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/GettingStarted.rst
new file mode 100644
index 000000000..0477328ca
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/GettingStarted.rst
@@ -0,0 +1,9 @@
+gettingStarted
+==================
+
+.. seealso::
+    `defaultPins.h <default_pins.html>`_
+
+.. literalinclude:: ../../../../examples_pico/gettingStarted.cpp
+    :caption: examples_pico/gettingStarted.cpp
+    :linenos:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/InterruptConfigure.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/InterruptConfigure.rst
new file mode 100644
index 000000000..4b8533ed4
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/InterruptConfigure.rst
@@ -0,0 +1,9 @@
+interruptConfigure
+======================
+
+.. seealso::
+    `defaultPins.h <default_pins.html>`_
+
+.. literalinclude:: ../../../../examples_pico/interruptConfigure.cpp
+    :caption: examples_pico/interruptConfigure.cpp
+    :linenos:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/ManualAcknowledgements.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/ManualAcknowledgements.rst
new file mode 100644
index 000000000..66f8aa471
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/ManualAcknowledgements.rst
@@ -0,0 +1,9 @@
+manualAcknowledgements
+==========================
+
+.. seealso::
+    `defaultPins.h <default_pins.html>`_
+
+.. literalinclude:: ../../../../examples_pico/manualAcknowledgements.cpp
+    :caption: examples_pico/manualAcknowledgements.cpp
+    :linenos:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/MulticeiverDemo.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/MulticeiverDemo.rst
new file mode 100644
index 000000000..612e4d2fb
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/MulticeiverDemo.rst
@@ -0,0 +1,9 @@
+multiceiverDemo
+====================
+
+.. seealso::
+    `defaultPins.h <default_pins.html>`_
+
+.. literalinclude:: ../../../../examples_pico/multiceiverDemo.cpp
+    :caption: examples_pico/multiceiverDemo.cpp
+    :linenos:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/Scanner.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/Scanner.rst
new file mode 100644
index 000000000..0f29e1496
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/Scanner.rst
@@ -0,0 +1,9 @@
+scanner
+==================
+
+.. seealso::
+    `defaultPins.h <default_pins.html>`_
+
+.. literalinclude:: ../../../../examples_pico/scanner.cpp
+    :caption: examples_pico/scanner.cpp
+    :linenos:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/StreamingData.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/StreamingData.rst
new file mode 100644
index 000000000..d6d2e8780
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/StreamingData.rst
@@ -0,0 +1,9 @@
+streamingData
+==================
+
+.. seealso::
+    `defaultPins.h <default_pins.html>`_
+
+.. literalinclude:: ../../../../examples_pico/streamingData.cpp
+    :caption: examples_pico/streamingData.cpp
+    :linenos:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/default_pins.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/default_pins.rst
new file mode 100644
index 000000000..54087260a
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/PicoSDK/default_pins.rst
@@ -0,0 +1,5 @@
+PicoSDK Examples' Default Pins
+==============================
+
+.. literalinclude:: ../../../../examples_pico/defaultPins.h
+    :linenos:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Python/AcknowledgementPayloads.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Python/AcknowledgementPayloads.rst
new file mode 100644
index 000000000..c7d719d96
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Python/AcknowledgementPayloads.rst
@@ -0,0 +1,8 @@
+acknowledgement_payloads.py
+===========================
+
+.. literalinclude:: ../../../../examples_linux/acknowledgement_payloads.py
+    :language: python
+    :caption: examples_linux/acknowledgement_payloads.py
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Python/GettingStarted.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Python/GettingStarted.rst
new file mode 100644
index 000000000..70b242fd0
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Python/GettingStarted.rst
@@ -0,0 +1,8 @@
+getting_started.py
+==================
+
+.. literalinclude:: ../../../../examples_linux/getting_started.py
+    :language: python
+    :caption: examples_linux/getting_started.py
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Python/InterruptConfigure.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Python/InterruptConfigure.rst
new file mode 100644
index 000000000..8c89ad3d2
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Python/InterruptConfigure.rst
@@ -0,0 +1,8 @@
+interrupt_configure.py
+======================
+
+.. literalinclude:: ../../../../examples_linux/interrupt_configure.py
+    :language: python
+    :caption: examples_linux/interrupt_configure.py
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Python/ManualAcknowledgements.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Python/ManualAcknowledgements.rst
new file mode 100644
index 000000000..d94e852d4
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Python/ManualAcknowledgements.rst
@@ -0,0 +1,8 @@
+manual_acknowledgements.py
+==========================
+
+.. literalinclude:: ../../../../examples_linux/manual_acknowledgements.py
+    :language: python
+    :caption: examples_linux/manual_acknowledgements.py
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Python/MulticeiverDemo.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Python/MulticeiverDemo.rst
new file mode 100644
index 000000000..96ef84fd3
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Python/MulticeiverDemo.rst
@@ -0,0 +1,8 @@
+multiceiver_demo.py
+====================
+
+.. literalinclude:: ../../../../examples_linux/multiceiver_demo.py
+    :language: python
+    :caption: examples_linux/multiceiver_demo.py
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/examples/Python/StreamingData.rst 
b/arduino/libraries/RF24/docs/sphinx/examples/Python/StreamingData.rst
new file mode 100644
index 000000000..f2f1f9fe8
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/examples/Python/StreamingData.rst
@@ -0,0 +1,8 @@
+streaming_data.py
+==================
+
+.. literalinclude:: ../../../../examples_linux/streaming_data.py
+    :language: python
+    :caption: examples_linux/streaming_data.py
+    :linenos:
+    :lineno-match:
diff --git a/arduino/libraries/RF24/docs/sphinx/index.rst b/arduino/libraries/RF24/docs/sphinx/index.rst
new file mode 100644
index 000000000..52e791372
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/index.rst
@@ -0,0 +1,44 @@
+
+:hero: Optimized high speed nRF24L01+ driver class documentation
+
+Introduction
+=============
+
+.. doxygenpage:: index
+    :content-only:
+
+Site Index
+-----------
+
+:ref:`Site index<genindex>`
+
+.. toctree::
+    :maxdepth: 2
+    :caption: API Reference
+    :hidden:
+
+    classRF24
+    enums
+    nRF24L01_8h
+    RF24_config_8h
+    deprecated
+
+
+.. toctree::
+    :maxdepth: 1
+    :hidden:
+
+    pages
+
+
+.. toctree::
+    :maxdepth: 1
+    :hidden:
+
+    modules
+
+.. toctree::
+    :maxdepth: 2
+    :hidden:
+
+    examples
diff --git a/arduino/libraries/RF24/docs/sphinx/make.bat b/arduino/libraries/RF24/docs/sphinx/make.bat
new file mode 100644
index 000000000..bba10073f
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+       set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=../sphinx
+set BUILDDIR=../_build
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+       echo.
+       echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+       echo.installed, then set the SPHINXBUILD environment variable to point
+       echo.to the full path of the 'sphinx-build' executable. Alternatively you
+       echo.may add the Sphinx directory to PATH.
+       echo.
+       echo.If you don't have Sphinx installed, grab it from
+       echo.https://www.sphinx-doc.org/
+       exit /b 1
+)
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/arduino/libraries/RF24/docs/sphinx/md_common_issues.rst 
b/arduino/libraries/RF24/docs/sphinx/md_common_issues.rst
new file mode 100644
index 000000000..26fecd73b
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/md_common_issues.rst
@@ -0,0 +1,14 @@
+Common Issues
+=============
+
+.. doxygenpage:: md_COMMON_ISSUES
+   :content-only:
+
+
+.. image:: ../../images/ghetto_sheilding_1.png
+    :target: https://github.com/nRF24/RF24/blob/master/images/ghetto_sheilding_1.png
+    :height: 750
+
+.. image:: ../../images/ghetto_sheilding_2.png
+    :target: https://github.com/nRF24/RF24/blob/master/images/ghetto_sheilding_2.png
+    :height: 750
diff --git a/arduino/libraries/RF24/docs/sphinx/md_contributing.rst 
b/arduino/libraries/RF24/docs/sphinx/md_contributing.rst
new file mode 100644
index 000000000..8a9b00db5
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/md_contributing.rst
@@ -0,0 +1,5 @@
+Contributing
+=============
+
+.. doxygenpage:: md_CONTRIBUTING
+   :content-only:
diff --git a/arduino/libraries/RF24/docs/sphinx/md_docs_arduino.rst 
b/arduino/libraries/RF24/docs/sphinx/md_docs_arduino.rst
new file mode 100644
index 000000000..48f9376f8
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/md_docs_arduino.rst
@@ -0,0 +1,5 @@
+Arduino
+=============
+
+.. doxygenpage:: md_docs_arduino
+   :content-only:
diff --git a/arduino/libraries/RF24/docs/sphinx/md_docs_attiny.rst 
b/arduino/libraries/RF24/docs/sphinx/md_docs_attiny.rst
new file mode 100644
index 000000000..1c07080f1
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/md_docs_attiny.rst
@@ -0,0 +1,6 @@
+ATTiny
+=============
+.. highlight:: none
+
+.. doxygenpage:: md_docs_attiny
+   :content-only:
diff --git a/arduino/libraries/RF24/docs/sphinx/md_docs_atxmega.rst 
b/arduino/libraries/RF24/docs/sphinx/md_docs_atxmega.rst
new file mode 100644
index 000000000..5a9ff21d9
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/md_docs_atxmega.rst
@@ -0,0 +1,5 @@
+ATXMEGA
+=============
+
+.. doxygenpage:: md_docs_atxmega
+   :content-only:
diff --git a/arduino/libraries/RF24/docs/sphinx/md_docs_cross_compile.rst 
b/arduino/libraries/RF24/docs/sphinx/md_docs_cross_compile.rst
new file mode 100644
index 000000000..8ffccf90a
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/md_docs_cross_compile.rst
@@ -0,0 +1,7 @@
+Linux cross-compilation
+=======================
+
+.. highlight:: none
+
+.. doxygenpage:: md_docs_cross_compile
+   :content-only:
diff --git a/arduino/libraries/RF24/docs/sphinx/md_docs_linux_install.rst 
b/arduino/libraries/RF24/docs/sphinx/md_docs_linux_install.rst
new file mode 100644
index 000000000..2f3d04aa5
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/md_docs_linux_install.rst
@@ -0,0 +1,5 @@
+Linux Installation
+==================
+
+.. doxygenpage:: md_docs_linux_install
+   :content-only:
diff --git a/arduino/libraries/RF24/docs/sphinx/md_docs_mraa.rst 
b/arduino/libraries/RF24/docs/sphinx/md_docs_mraa.rst
new file mode 100644
index 000000000..361d98c3e
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/md_docs_mraa.rst
@@ -0,0 +1,5 @@
+MRAA
+=============
+
+.. doxygenpage:: md_docs_mraa
+   :content-only:
diff --git a/arduino/libraries/RF24/docs/sphinx/md_docs_pico_sdk.rst 
b/arduino/libraries/RF24/docs/sphinx/md_docs_pico_sdk.rst
new file mode 100644
index 000000000..983a95367
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/md_docs_pico_sdk.rst
@@ -0,0 +1,7 @@
+Raspberry Pi Pico SDK
+=====================
+
+.. highlight:: cmake
+
+.. doxygenpage:: md_docs_pico_sdk
+   :content-only:
diff --git a/arduino/libraries/RF24/docs/sphinx/md_docs_portability.rst 
b/arduino/libraries/RF24/docs/sphinx/md_docs_portability.rst
new file mode 100644
index 000000000..7801a9cc2
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/md_docs_portability.rst
@@ -0,0 +1,5 @@
+RF24 portability
+================
+
+.. doxygenpage:: md_docs_portability
+   :content-only:
diff --git a/arduino/libraries/RF24/docs/sphinx/md_docs_python_wrapper.rst 
b/arduino/libraries/RF24/docs/sphinx/md_docs_python_wrapper.rst
new file mode 100644
index 000000000..d0120e4bc
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/md_docs_python_wrapper.rst
@@ -0,0 +1,5 @@
+Python wrapper
+==============
+
+.. doxygenpage:: md_docs_python_wrapper
+   :content-only:
diff --git a/arduino/libraries/RF24/docs/sphinx/md_docs_rpi_general.rst 
b/arduino/libraries/RF24/docs/sphinx/md_docs_rpi_general.rst
new file mode 100644
index 000000000..a69ff21e6
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/md_docs_rpi_general.rst
@@ -0,0 +1,5 @@
+Linux General/Raspberry Pi
+==========================
+
+.. doxygenpage:: md_docs_rpi_general
+   :content-only:
diff --git a/arduino/libraries/RF24/docs/sphinx/md_docs_using_cmake.rst 
b/arduino/libraries/RF24/docs/sphinx/md_docs_using_cmake.rst
new file mode 100644
index 000000000..8ef13be89
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/md_docs_using_cmake.rst
@@ -0,0 +1,7 @@
+Using CMake
+=============
+
+.. highlight:: none
+
+.. doxygenpage:: md_docs_using_cmake
+   :content-only:
diff --git a/arduino/libraries/RF24/docs/sphinx/modules.rst b/arduino/libraries/RF24/docs/sphinx/modules.rst
new file mode 100644
index 000000000..3fd4cccc0
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/modules.rst
@@ -0,0 +1,11 @@
+Modules
+=========
+
+.. toctree::
+    :maxdepth: 2
+
+    modules/Porting_Timing
+    modules/Porting_GPIO
+    modules/Porting_Includes
+    modules/Porting_General
+    modules/Porting_SPI
diff --git a/arduino/libraries/RF24/docs/sphinx/modules/Porting_GPIO.rst 
b/arduino/libraries/RF24/docs/sphinx/modules/Porting_GPIO.rst
new file mode 100644
index 000000000..99ad4a37b
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/modules/Porting_GPIO.rst
@@ -0,0 +1,15 @@
+Porting: GPIO
+=============
+
+.. doxygengroup:: Porting_GPIO
+    :members:
+    :undoc-members:
+    :protected-members:
+    :private-members:
+    :content-only:
+
+gpio.h
+-----------
+
+.. literalinclude:: ../../../utility/Template/gpio.h
+    :caption: utility/Template/gpio.h
diff --git a/arduino/libraries/RF24/docs/sphinx/modules/Porting_General.rst 
b/arduino/libraries/RF24/docs/sphinx/modules/Porting_General.rst
new file mode 100644
index 000000000..2e9af1fde
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/modules/Porting_General.rst
@@ -0,0 +1,15 @@
+Porting: General
+================
+
+.. doxygengroup:: Porting_General
+    :members:
+    :undoc-members:
+    :protected-members:
+    :private-members:
+    :content-only:
+
+RF24_arch_config.h
+------------------
+
+.. literalinclude:: ../../../utility/Template/RF24_arch_config.h
+    :caption: utility/Template/RF24_arch_config.h
diff --git a/arduino/libraries/RF24/docs/sphinx/modules/Porting_Includes.rst 
b/arduino/libraries/RF24/docs/sphinx/modules/Porting_Includes.rst
new file mode 100644
index 000000000..a2feea4e7
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/modules/Porting_Includes.rst
@@ -0,0 +1,15 @@
+Porting: Includes
+==================
+
+.. doxygengroup:: Porting_Includes
+    :members:
+    :undoc-members:
+    :protected-members:
+    :private-members:
+    :content-only:
+
+includes.h
+-----------
+
+.. literalinclude:: ../../../utility/Template/includes.h
+    :caption: utility/Template/includes.h
diff --git a/arduino/libraries/RF24/docs/sphinx/modules/Porting_SPI.rst 
b/arduino/libraries/RF24/docs/sphinx/modules/Porting_SPI.rst
new file mode 100644
index 000000000..d07887840
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/modules/Porting_SPI.rst
@@ -0,0 +1,15 @@
+Porting: SPI
+============
+
+.. doxygengroup:: Porting_SPI
+    :members:
+    :undoc-members:
+    :protected-members:
+    :private-members:
+    :content-only:
+
+spi.h
+-----------
+
+.. literalinclude:: ../../../utility/Template/spi.h
+    :caption: utility/Template/spi.h
diff --git a/arduino/libraries/RF24/docs/sphinx/modules/Porting_Timing.rst 
b/arduino/libraries/RF24/docs/sphinx/modules/Porting_Timing.rst
new file mode 100644
index 000000000..feca6c206
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/modules/Porting_Timing.rst
@@ -0,0 +1,15 @@
+Porting: Timing
+===============
+
+.. doxygengroup:: Porting_Timing
+    :members:
+    :undoc-members:
+    :protected-members:
+    :private-members:
+    :content-only:
+
+compatibility.h
+---------------
+
+.. literalinclude:: ../../../utility/Template/compatibility.h
+    :caption: utility/Template/compatibility.h
diff --git a/arduino/libraries/RF24/docs/sphinx/nRF24L01_8h.rst 
b/arduino/libraries/RF24/docs/sphinx/nRF24L01_8h.rst
new file mode 100644
index 000000000..3ea691364
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/nRF24L01_8h.rst
@@ -0,0 +1,7 @@
+nRF24L01.h
+==========
+
+.. literalinclude:: ../../nRF24L01.h
+    :linenos:
+    :lineno-match:
+    :start-at: /* Memory Map */
diff --git a/arduino/libraries/RF24/docs/sphinx/pages.rst b/arduino/libraries/RF24/docs/sphinx/pages.rst
new file mode 100644
index 000000000..a14c72a35
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/pages.rst
@@ -0,0 +1,19 @@
+Related Pages
+=============
+
+.. toctree::
+    :maxdepth: 1
+
+    md_common_issues
+    md_contributing
+    md_docs_arduino
+    md_docs_attiny
+    md_docs_atxmega
+    md_docs_cross_compile
+    md_docs_linux_install
+    md_docs_mraa
+    md_docs_pico_sdk
+    md_docs_portability
+    md_docs_python_wrapper
+    md_docs_rpi_general
+    md_docs_using_cmake
diff --git a/arduino/libraries/RF24/docs/sphinx/requirements.txt 
b/arduino/libraries/RF24/docs/sphinx/requirements.txt
new file mode 100644
index 000000000..1707fa2f1
--- /dev/null
+++ b/arduino/libraries/RF24/docs/sphinx/requirements.txt
@@ -0,0 +1,2 @@
+breathe
+sphinx-immaterial
diff --git a/arduino/libraries/RF24/docs/using_cmake.md b/arduino/libraries/RF24/docs/using_cmake.md
new file mode 100644
index 000000000..4e7cf4360
--- /dev/null
+++ b/arduino/libraries/RF24/docs/using_cmake.md
@@ -0,0 +1,170 @@
+# Using CMake
+
+<!-- markdownlint-disable MD031 -->
+A more modern approach instead of using hand-crafted _Makefiles_ & _configure_ scripts
+to build & install software. Please note that these instructions are not needed if you
+have already installed the library using [these older instructions](md_docs_linux_install.html)
+
+## Installing the library
+
+### Using a package manager
+
+The RF24 library now (as of v1.4.1) has pre-built packages (_.deb_ or _.rpm_ files) that can be installed on 
a
+Raspberry Pi. These packages can be found on the library's
+[GitHub release page](https://GitHub.com/nRF24/RF24/releases)
+
+@warning If you have previously installed the library from source code, then you will need to uninstall it 
manually to avoid runtime conflicts.
+```shell
+sudo rm /usr/local/lib/librf24.*
+sudo rm /usr/local/lib/librf24-bcm.so
+sudo rm -r /usr/local/include/RF24
+```
+
+The _librf24-bcm.so_ file may not exist if you used CMake to install the library.
+
+1. Download the appropriate package for your machine
+
+   Go to the library's [GitHub release page](https://GitHub.com/nRF24/RF24/releases), and look for
+   the latest release's assets.
+
+   For all Raspberry Pi variants using the Raspberry Pi OS (aka Raspbian), you need the file marked
+   for _armhf_ architecture.
+
+   For Raspberry Pi variants using a 64-bit OS (like Ubuntu), you need the file marked for
+   _arm64_ architecture.
+
+   Notice that the filenames will include the name of the utility driver that the package was built with.
+   This does not mean that the LittleWire, MRAA, or wiringPi libraries are included in the package (you will 
still
+   need to install those yourself beforehand).
+2. Install the downloaded pkg
+
+   If you downloaded the file directly from your target machine using the desktop environment, then
+   you only need to double-click the package (deb or rpm) file, and the OS should do the rest.
+
+   If you downloaded the file remotely and want to copy it over ssh, then use the `scp` command in a 
terminal.
+   ```shell
+   scp pkg_filename.deb pi@host_name:~/Downloads
+   ```
+   @note You do not need to do this from within an ssh session. Also, you can use the target machine's IP
+   address instead of its host name.
+
+   The `scp` command will ask you for a password belonging to the user's name on the remote machine (we used
+   `pi` in the above example).
+
+   Now you can open up a ssh session and install the copied package from the terminal.
+   ```shell
+   ssh pi@host_name
+   cd Downloads
+   dpkg -i pkg_filename.deb
+   ```
+
+### Building from source code
+
+1. Install prerequisites if there are any (wiringPi, MRAA, LittleWire libraries, setup SPI device etc)
+
+   CMake may need to be installed
+   ```shell
+   sudo apt-get install cmake
+   ```
+   @note See the [MRAA documentation](http://iotdk.intel.com/docs/master/mraa/index.html) for more 
information on installing MRAA
+2. Make a directory to contain the RF24 library and possibly other RF24\* libraries and enter it
+   ```shell
+   mkdir ~/rf24libs
+   cd ~/rf24libs
+   ```
+3. Clone the [RF24 repo](https://github.com/nRF24/RF24) and navigate to it
+   ```shell
+   git clone https://github.com/nRF24/RF24.git RF24
+   cd RF24
+   ```
+4. Create a build directory inside the RF24 directory and navigate to it.
+   ```shell
+   mkdir build
+   cd build
+   ```
+5. Configure build environment
+
+   @note When using these instructions to install RF24Mesh, RF24Network, or RF24Gateway,
+   the following `RF24_DRIVER` option is not needed (it is only for the RF24 library).
+
+   ```shell
+   cmake .. -D RF24_DRIVER=SPIDEV
+   ```
+   Instead of using `SPIDEV` driver (recommended), you can also specify the `RPi`, `wiringPi`,
+   `MRAA`, or `LittleWire` as alternative drivers.
+
+   If the `RF24_DRIVER` option is not specified, then it will be automatically configured based
+   on the detected CPU or installed libraries (defaults to `SPIDEV` when auto-detection fails).
+6. Build and install the library
+   ```shell
+   make
+   sudo make install
+   ```
+7. Build the examples
+
+   Navigate to the _examples_linux_ directory
+   ```shell
+   cd ../examples_linux
+   ```
+   Make sure the pins used in the examples match the pins you used to connect the radio module
+   ```shell
+   nano gettingstarted.cpp
+   ```
+   and edit the pin numbers as directed in the [linux/RPi general documation](md_docs_rpi_general.html).
+   Create a build directory in the examples_linux directory and navigate to it.
+   ```shell
+   mkdir build
+   cd build
+   ```
+   Now you are ready to build the examples.
+   ```shell
+   cmake ..
+   make
+   ```
+   If using the `MRAA` or `wiringPi` drivers, then you may need to specify the `RF24_DRIVER`
+   option again.
+   ```shell
+   cmake .. -D RF24_DRIVER=wiringPi
+   make
+   ```
+8. Run an example file
+   ```shell
+   sudo ./gettingstarted
+   ```
+
+## Cross-compiling the library
+
+The RF24 library comes with some pre-made toolchain files (located in the _RF24/cmake/toolchains_
+directory) to use in CMake. To use these toolchain files, additional command line options are needed
+when configuring CMake to build the library (step 5 in the above instructions to build from source).
+
+```shell
+cmake .. -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/armhf.cmake
+make
+```
+
+If you plan on using the cross-compiled library with your personal cross-compiled project, then
+it is advised to specify the path that your project will look in when linking to the RF24 library:
+
+```shell
+cmake .. -D CMAKE_INSTALL_PREFIX=/usr/arm-linux-gnueabihf -D 
CMAKE_TOOLCHAIN_FILE=cmake/toolchains/armhf.cmake
+make
+sudo make install
+```
+
+Remember to also specify the `RF24_DRIVER` option if not using the auto-configuration feature (see step 5
+in the above instructions to build from source).
+
+### Installing the library remotely
+
+To install remotely, you can create an installable package file using CMake's built-in program called CPack.
+
+```shell
+cmake .. -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/armhf.cmake
+make
+cpack
+```
+
+This will create a deb file and a rpm file in a new sub-directory called "pkgs" within the build directory.
+You can use either of these packages to install the library to your target machine (see the above
+instructions about using a package manager).
diff --git a/arduino/libraries/RF24/doxygen-custom.css b/arduino/libraries/RF24/doxygen-custom.css
new file mode 100644
index 000000000..236f3e3b9
--- /dev/null
+++ b/arduino/libraries/RF24/doxygen-custom.css
@@ -0,0 +1,815 @@
+/* The standard CSS for doxygen */
+
+body, table, div, p, dl {
+    font-family: Lucida Grande, Verdana, Geneva, Arial, sans-serif;
+    font-size: 12px;
+}
+
+/* @group Heading Levels */
+
+h1 {
+    font-size: 150%;
+}
+
+.title {
+    font-size: 150%;
+    font-weight: bold;
+    margin: 10px 2px;
+}
+
+h2 {
+    font-size: 120%;
+}
+
+h3 {
+    font-size: 100%;
+}
+
+dt {
+    font-weight: bold;
+}
+
+div.multicol {
+    -moz-column-gap: 1em;
+    -webkit-column-gap: 1em;
+    -moz-column-count: 3;
+    -webkit-column-count: 3;
+}
+
+p.startli, p.startdd, p.starttd {
+    margin-top: 2px;
+}
+
+p.endli {
+    margin-bottom: 0px;
+}
+
+p.enddd {
+    margin-bottom: 4px;
+}
+
+p.endtd {
+    margin-bottom: 2px;
+}
+
+/* @end */
+
+caption {
+    font-weight: bold;
+}
+
+span.legend {
+    font-size: 70%;
+    text-align: center;
+}
+
+h3.version {
+    font-size: 90%;
+    text-align: center;
+}
+
+div.qindex, div.navtab {
+    background-color: #EBEFF6;
+    border: 1px solid #A3B4D7;
+    text-align: center;
+    margin: 2px;
+    padding: 2px;
+}
+
+div.qindex, div.navpath {
+    width: 100%;
+    line-height: 110%;
+}
+
+div.navtab {
+    margin-right: 15px;
+}
+
+/* @group Link Styling */
+
+a {
+    color: #3D578C;
+    font-weight: normal;
+    text-decoration: none;
+}
+
+.contents a:visited {
+    color: #4665A2;
+}
+
+a:hover {
+    text-decoration: underline;
+}
+
+a.qindex {
+    font-weight: bold;
+}
+
+a.qindexHL {
+    font-weight: bold;
+    background-color: #9CAFD4;
+    color: #ffffff;
+    border: 1px double #869DCA;
+}
+
+.contents a.qindexHL:visited {
+    color: #ffffff;
+}
+
+a.el {
+    font-weight: bold;
+}
+
+a.elRef {
+}
+
+a.code {
+    color: #4665A2;
+}
+
+a.codeRef {
+    color: #4665A2;
+}
+
+/* @end */
+
+dl.el {
+    margin-left: -1cm;
+}
+
+.fragment {
+    font-family: monospace, fixed;
+    font-size: 105%;
+}
+
+pre.fragment {
+    border: 1px solid #C4CFE5;
+    background-color: #FBFCFD;
+    padding: 4px 6px;
+    margin: 4px 8px 4px 2px;
+    overflow: auto;
+    word-wrap: break-word;
+    font-size: 9pt;
+    line-height: 125%;
+}
+
+div.ah {
+    background-color: black;
+    font-weight: bold;
+    color: #ffffff;
+    margin-bottom: 3px;
+    margin-top: 3px;
+    padding: 0.2em;
+    border: solid thin #333;
+    border-radius: 0.5em;
+    -webkit-border-radius: .5em;
+    -moz-border-radius: .5em;
+    box-shadow: 2px 2px 3px #999;
+    -webkit-box-shadow: 2px 2px 3px #999;
+    -moz-box-shadow: rgba(0, 0, 0, 0.15) 2px 2px 2px;
+    background-image: -webkit-gradient(linear, left top, left bottom, from(#eee), to(#000), color-stop(0.3, 
#444));
+    background-image: -moz-linear-gradient(center top, #eee 0%, #444 40%, #000);
+}
+
+div.groupHeader {
+    margin-left: 16px;
+    margin-top: 12px;
+    font-weight: bold;
+}
+
+div.groupText {
+    margin-left: 16px;
+    font-style: italic;
+}
+
+body {
+    background: white;
+    color: black;
+    margin: 0;
+}
+
+div.contents {
+    margin-top: 10px;
+    margin-left: 10px;
+    margin-right: 5px;
+}
+
+td.indexkey {
+    background-color: #EBEFF6;
+    font-weight: bold;
+    border: 1px solid #C4CFE5;
+    margin: 2px 0px 2px 0;
+    padding: 2px 10px;
+}
+
+td.indexvalue {
+    background-color: #EBEFF6;
+    border: 1px solid #C4CFE5;
+    padding: 2px 10px;
+    margin: 2px 0px;
+}
+
+tr.memlist {
+    background-color: #EEF1F7;
+}
+
+p.formulaDsp {
+    text-align: center;
+}
+
+img.formulaDsp {
+
+}
+
+img.formulaInl {
+    vertical-align: middle;
+}
+
+div.center {
+    text-align: center;
+    margin-top: 0px;
+    margin-bottom: 0px;
+    padding: 0px;
+}
+
+div.center img {
+    border: 0px;
+}
+
+address.footer {
+    text-align: right;
+    padding-right: 12px;
+}
+
+img.footer {
+    border: 0px;
+    vertical-align: middle;
+}
+
+/* @group Code Colorization */
+
+span.keyword {
+    color: #008000
+}
+
+span.keywordtype {
+    color: #604020
+}
+
+span.keywordflow {
+    color: #e08000
+}
+
+span.comment {
+    color: #800000
+}
+
+span.preprocessor {
+    color: #806020
+}
+
+span.stringliteral {
+    color: #002080
+}
+
+span.charliteral {
+    color: #008080
+}
+
+span.vhdldigit {
+    color: #ff00ff
+}
+
+span.vhdlchar {
+    color: #000000
+}
+
+span.vhdlkeyword {
+    color: #700070
+}
+
+span.vhdllogic {
+    color: #ff0000
+}
+
+/* @end */
+
+/*
+.search {
+       color: #003399;
+       font-weight: bold;
+}
+
+form.search {
+       margin-bottom: 0px;
+       margin-top: 0px;
+}
+
+input.search {
+       font-size: 75%;
+       color: #000080;
+       font-weight: normal;
+       background-color: #e8eef2;
+}
+*/
+
+td.tiny {
+    font-size: 75%;
+}
+
+.dirtab {
+    padding: 4px;
+    border-collapse: collapse;
+    border: 1px solid #A3B4D7;
+}
+
+th.dirtab {
+    background: #EBEFF6;
+    font-weight: bold;
+}
+
+hr {
+    height: 0px;
+    border: none;
+    border-top: 1px solid #4A6AAA;
+}
+
+hr.footer {
+    height: 1px;
+}
+
+/* @group Member Descriptions */
+
+table.memberdecls {
+    border-spacing: 0px;
+    padding: 0px;
+}
+
+.mdescLeft, .mdescRight,
+.memItemLeft, .memItemRight,
+.memTemplItemLeft, .memTemplItemRight, .memTemplParams {
+    background-color: #F9FAFC;
+    border: none;
+    margin: 4px;
+    padding: 1px 0 0 8px;
+}
+
+.mdescLeft, .mdescRight {
+    padding: 0px 8px 4px 8px;
+    color: #555;
+}
+
+.memItemLeft, .memItemRight, .memTemplParams {
+    border-top: 1px solid #C4CFE5;
+}
+
+.memItemLeft, .memTemplItemLeft {
+    white-space: nowrap;
+}
+
+.memItemRight {
+    width: 100%;
+}
+
+.memTemplParams {
+    color: #4665A2;
+    white-space: nowrap;
+}
+
+/* @end */
+
+/* @group Member Details */
+
+/* Styles for detailed member documentation */
+
+.memtemplate {
+    font-size: 80%;
+    color: #4665A2;
+    font-weight: normal;
+    margin-left: 9px;
+}
+
+.memnav {
+    background-color: #EBEFF6;
+    border: 1px solid #A3B4D7;
+    text-align: center;
+    margin: 2px;
+    margin-right: 15px;
+    padding: 2px;
+}
+
+.mempage {
+    width: 100%;
+}
+
+.memitem {
+    padding: 0;
+    margin-bottom: 10px;
+    margin-right: 5px;
+}
+
+.memname {
+    white-space: nowrap;
+    font-weight: bold;
+    margin-left: 6px;
+}
+
+.memproto {
+    border-top: 1px solid #A8B8D9;
+    border-left: 1px solid #A8B8D9;
+    border-right: 1px solid #A8B8D9;
+    padding: 6px 0px 6px 0px;
+    color: #253555;
+    font-weight: bold;
+    text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9);
+    /* opera specific markup */
+    box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+    border-top-right-radius: 8px;
+    border-top-left-radius: 8px;
+    /* firefox specific markup */
+    -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
+    -moz-border-radius-topright: 8px;
+    -moz-border-radius-topleft: 8px;
+    /* webkit specific markup */
+    -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+    -webkit-border-top-right-radius: 8px;
+    -webkit-border-top-left-radius: 8px;
+    background-image: url('nav_f.png');
+    background-repeat: repeat-x;
+    background-color: #E2E8F2;
+
+}
+
+.memdoc {
+    border-bottom: 1px solid #A8B8D9;
+    border-left: 1px solid #A8B8D9;
+    border-right: 1px solid #A8B8D9;
+    padding: 2px 5px;
+    background-color: #FBFCFD;
+    border-top-width: 0;
+    /* opera specific markup */
+    border-bottom-left-radius: 8px;
+    border-bottom-right-radius: 8px;
+    box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+    /* firefox specific markup */
+    -moz-border-radius-bottomleft: 8px;
+    -moz-border-radius-bottomright: 8px;
+    -moz-box-shadow: rgba(0, 0, 0, 0.15) 5px 5px 5px;
+    background-image: -moz-linear-gradient(center top, #FFFFFF 0%, #FFFFFF 60%, #F7F8FB 95%, #EEF1F7);
+    /* webkit specific markup */
+    -webkit-border-bottom-left-radius: 8px;
+    -webkit-border-bottom-right-radius: 8px;
+    -webkit-box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.15);
+    background-image: -webkit-gradient(linear, center top, center bottom, from(#FFFFFF), color-stop(0.6, 
#FFFFFF), color-stop(0.60, #FFFFFF), color-stop(0.95, #F7F8FB), to(#EEF1F7));
+}
+
+.paramkey {
+    text-align: right;
+}
+
+.paramtype {
+    white-space: nowrap;
+}
+
+.paramname {
+    color: #602020;
+    white-space: nowrap;
+}
+
+.paramname em {
+    font-style: normal;
+}
+
+.params, .retval, .exception, .tparams {
+    border-spacing: 6px 2px;
+}
+
+.params .paramname, .retval .paramname {
+    font-weight: bold;
+    vertical-align: top;
+}
+
+.params .paramtype {
+    font-style: italic;
+    vertical-align: top;
+}
+
+.params .paramdir {
+    font-family: "courier new", courier, monospace;
+    vertical-align: top;
+}
+
+
+/* @end */
+
+/* @group Directory (tree) */
+
+/* for the tree view */
+
+.ftvtree {
+    font-family: sans-serif;
+    margin: 0px;
+}
+
+/* these are for tree view when used as main index */
+
+.directory {
+    font-size: 9pt;
+    font-weight: bold;
+    margin: 5px;
+}
+
+.directory h3 {
+    margin: 0px;
+    margin-top: 1em;
+    font-size: 11pt;
+}
+
+/*
+The following two styles can be used to replace the root node title
+with an image of your choice.  Simply uncomment the next two styles,
+specify the name of your image and be sure to set 'height' to the
+proper pixel height of your image.
+*/
+
+/*
+.directory h3.swap {
+       height: 61px;
+       background-repeat: no-repeat;
+       background-image: url("yourimage.gif");
+}
+.directory h3.swap span {
+       display: none;
+}
+*/
+
+.directory > h3 {
+    margin-top: 0;
+}
+
+.directory p {
+    margin: 0px;
+    white-space: nowrap;
+}
+
+.directory div {
+    display: none;
+    margin: 0px;
+}
+
+.directory img {
+    vertical-align: -30%;
+}
+
+/* these are for tree view when not used as main index */
+
+.directory-alt {
+    font-size: 100%;
+    font-weight: bold;
+}
+
+.directory-alt h3 {
+    margin: 0px;
+    margin-top: 1em;
+    font-size: 11pt;
+}
+
+.directory-alt > h3 {
+    margin-top: 0;
+}
+
+.directory-alt p {
+    margin: 0px;
+    white-space: nowrap;
+}
+
+.directory-alt div {
+    display: none;
+    margin: 0px;
+}
+
+.directory-alt img {
+    vertical-align: -30%;
+}
+
+/* @end */
+
+div.dynheader {
+    margin-top: 8px;
+}
+
+address {
+    font-style: normal;
+    color: #2A3D61;
+}
+
+table.doxtable {
+    border-collapse: collapse;
+}
+
+table.doxtable td, table.doxtable th {
+    border: 1px solid #2D4068;
+    padding: 3px 7px 2px;
+}
+
+table.doxtable th {
+    background-color: #374F7F;
+    color: #FFFFFF;
+    font-size: 110%;
+    padding-bottom: 4px;
+    padding-top: 5px;
+    text-align: left;
+}
+
+.tabsearch {
+    top: 0px;
+    left: 10px;
+    height: 36px;
+    background-image: url('tab_b.png');
+    z-index: 101;
+    overflow: hidden;
+    font-size: 13px;
+}
+
+.navpath ul {
+    font-size: 11px;
+    background-image: url('tab_b.png');
+    background-repeat: repeat-x;
+    height: 30px;
+    line-height: 30px;
+    color: #8AA0CC;
+    border: solid 1px #C2CDE4;
+    overflow: hidden;
+    margin: 0px;
+    padding: 0px;
+}
+
+.navpath li {
+    list-style-type: none;
+    float: left;
+    padding-left: 10px;
+    padding-right: 15px;
+    background-image: url('bc_s.png');
+    background-repeat: no-repeat;
+    background-position: right;
+    color: #364D7C;
+}
+
+.navpath li.navelem a {
+    height: 32px;
+    display: block;
+    text-decoration: none;
+    outline: none;
+}
+
+.navpath li.navelem a:hover {
+    color: #6884BD;
+}
+
+.navpath li.footer {
+    list-style-type: none;
+    float: right;
+    padding-left: 10px;
+    padding-right: 15px;
+    background-image: none;
+    background-repeat: no-repeat;
+    background-position: right;
+    color: #364D7C;
+    font-size: 8pt;
+}
+
+
+div.summary {
+    float: right;
+    font-size: 8pt;
+    padding-right: 5px;
+    width: 50%;
+    text-align: right;
+}
+
+div.summary a {
+    white-space: nowrap;
+}
+
+div.ingroups {
+    font-size: 8pt;
+    padding-left: 5px;
+    width: 50%;
+    text-align: left;
+}
+
+div.ingroups a {
+    white-space: nowrap;
+}
+
+div.header {
+    background-image: url('nav_h.png');
+    background-repeat: repeat-x;
+    background-color: #F9FAFC;
+    margin: 0px;
+    border-bottom: 1px solid #C4CFE5;
+}
+
+div.headertitle {
+    padding: 5px 5px 5px 10px;
+}
+
+dl {
+    padding: 0 0 0 10px;
+}
+
+dl.note, dl.warning, dl.attention, dl.pre, dl.post, dl.invariant, dl.deprecated, dl.todo, dl.test, dl.bug {
+    border-left: 4px solid;
+    padding: 0 0 0 6px;
+}
+
+dl.note {
+    border-color: #D0C000;
+}
+
+dl.warning, dl.attention {
+    border-color: #FF0000;
+}
+
+dl.pre, dl.post, dl.invariant {
+    border-color: #00D000;
+}
+
+dl.deprecated {
+    border-color: #505050;
+}
+
+dl.todo {
+    border-color: #00C0E0;
+}
+
+dl.test {
+    border-color: #3030E0;
+}
+
+dl.bug {
+    border-color: #C08050;
+}
+
+#projectlogo {
+    text-align: center;
+    vertical-align: bottom;
+    border-collapse: separate;
+}
+
+#projectlogo img {
+    border: 0px none;
+}
+
+#projectname {
+    font: 300% Tahoma, Arial, sans-serif;
+    margin: 0px;
+    padding: 2px 0px;
+}
+
+#projectbrief {
+    font: 120% Tahoma, Arial, sans-serif;
+    margin: 0px;
+    padding: 0px;
+}
+
+#projectnumber {
+    font: 50% Tahoma, Arial, sans-serif;
+    margin: 0px;
+    padding: 0px;
+}
+
+#titlearea {
+    padding: 0px;
+    margin: 0px;
+    width: 100%;
+    border-bottom: 1px solid #5373B4;
+}
+
+.image {
+    text-align: left;
+}
+
+.dotgraph {
+    text-align: center;
+}
+
+.mscgraph {
+    text-align: center;
+}
+
+.caption {
+    font-weight: bold;
+}
+td.fielddoc
+th.markdownTableHeadLeft,
+th.markdownTableHeadRight,
+th.markdownTableHeadCenter,
+th.markdownTableHeadNone {
+    background-image: none;
+    border-radius: unset;
+}
+
+td.fielddoc tr:last-child {
+    border-bottom: 1px solid #2D4068;
+}
diff --git a/arduino/libraries/RF24/examples/AcknowledgementPayloads/AcknowledgementPayloads.ino 
b/arduino/libraries/RF24/examples/AcknowledgementPayloads/AcknowledgementPayloads.ino
new file mode 100644
index 000000000..bc2cbc238
--- /dev/null
+++ b/arduino/libraries/RF24/examples/AcknowledgementPayloads/AcknowledgementPayloads.ino
@@ -0,0 +1,208 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty (2bndy5)
+ */
+
+/**
+ * A simple example of sending data from 1 nRF24L01 transceiver to another
+ * with Acknowledgement (ACK) payloads attached to ACK packets.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use the Serial Monitor to change each node's behavior.
+ */
+#include <SPI.h>
+#include "printf.h"
+#include "RF24.h"
+
+// instantiate an object for the nRF24L01 transceiver
+RF24 radio(7, 8); // using pin 7 for the CE pin, and pin 8 for the CSN pin
+
+// an identifying device destination
+// Let these addresses be used for the pair
+uint8_t address[][6] = {"1Node", "2Node"};
+// It is very helpful to think of an address as a path instead of as
+// an identifying device destination
+// to use different addresses on a pair of radios, we need a variable to
+
+// uniquely identify which address this radio will use to transmit
+bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+// Used to control whether this node is sending or receiving
+bool role = false;  // true = TX role, false = RX role
+
+// For this example, we'll be using a payload containing
+// a string & an integer number that will be incremented
+// on every successful transmission.
+// Make a data structure to store the entire payload of different datatypes
+struct PayloadStruct {
+  char message[7];          // only using 6 characters for TX & ACK payloads
+  uint8_t counter;
+};
+PayloadStruct payload;
+
+void setup() {
+
+  Serial.begin(115200);
+  while (!Serial) {
+    // some boards need to wait to ensure access to serial over USB
+  }
+
+  // initialize the transceiver on the SPI bus
+  if (!radio.begin()) {
+    Serial.println(F("radio hardware is not responding!!"));
+    while (1) {} // hold in infinite loop
+  }
+
+  // print example's introductory prompt
+  Serial.println(F("RF24/examples/AcknowledgementPayloads"));
+
+  // To set the radioNumber via the Serial monitor on startup
+  Serial.println(F("Which radio is this? Enter '0' or '1'. Defaults to '0'"));
+  while (!Serial.available()) {
+    // wait for user input
+  }
+  char input = Serial.parseInt();
+  radioNumber = input == 1;
+  Serial.print(F("radioNumber = "));
+  Serial.println((int)radioNumber);
+
+  // role variable is hardcoded to RX behavior, inform the user of this
+  Serial.println(F("*** PRESS 'T' to begin transmitting to the other node"));
+
+  // Set the PA Level low to try preventing power supply related problems
+  // because these examples are likely run with nodes in close proximity to
+  // each other.
+  radio.setPALevel(RF24_PA_LOW);     // RF24_PA_MAX is default.
+
+  // to use ACK payloads, we need to enable dynamic payload lengths (for all nodes)
+  radio.enableDynamicPayloads();    // ACK payloads are dynamically sized
+
+  // Acknowledgement packets have no payloads by default. We need to enable
+  // this feature for all nodes (TX & RX) to use ACK payloads.
+  radio.enableAckPayload();
+
+  // set the TX address of the RX node into the TX pipe
+  radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0
+
+  // set the RX address of the TX node into a RX pipe
+  radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+  // additional setup specific to the node's role
+  if (role) {
+    // setup the TX payload
+
+    memcpy(payload.message, "Hello ", 6);                       // set the payload message
+    radio.stopListening();                                      // put radio in TX mode
+  } else {
+    // setup the ACK payload & load the first response into the FIFO
+
+    memcpy(payload.message, "World ", 6);                       // set the payload message
+    // load the payload for the first received transmission on pipe 0
+    radio.writeAckPayload(1, &payload, sizeof(payload));
+
+    radio.startListening();                                     // put radio in RX mode
+  }
+
+  // For debugging info
+  // printf_begin();             // needed only once for printing details
+  // radio.printDetails();       // (smaller) function that prints raw register values
+  // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+}
+
+void loop() {
+
+  if (role) {
+    // This device is a TX node
+
+    unsigned long start_timer = micros();                    // start the timer
+    bool report = radio.write(&payload, sizeof(payload));    // transmit & save the report
+    unsigned long end_timer = micros();                      // end the timer
+
+    if (report) {
+      Serial.print(F("Transmission successful! "));          // payload was delivered
+      Serial.print(F("Time to transmit = "));
+      Serial.print(end_timer - start_timer);                 // print the timer result
+      Serial.print(F(" us. Sent: "));
+      Serial.print(payload.message);                         // print the outgoing message
+      Serial.print(payload.counter);                         // print the outgoing counter
+      uint8_t pipe;
+      if (radio.available(&pipe)) {                          // is there an ACK payload? grab the pipe 
number that received it
+        PayloadStruct received;
+        radio.read(&received, sizeof(received));             // get incoming ACK payload
+        Serial.print(F(" Recieved "));
+        Serial.print(radio.getDynamicPayloadSize());         // print incoming payload size
+        Serial.print(F(" bytes on pipe "));
+        Serial.print(pipe);                                  // print pipe number that received the ACK
+        Serial.print(F(": "));
+        Serial.print(received.message);                      // print incoming message
+        Serial.println(received.counter);                    // print incoming counter
+
+        // save incoming counter & increment for next outgoing
+        payload.counter = received.counter + 1;
+
+      } else {
+        Serial.println(F(" Recieved: an empty ACK packet")); // empty ACK packet received
+      }
+
+
+    } else {
+      Serial.println(F("Transmission failed or timed out"));    // payload was not delivered
+    }
+
+    // to make this example readable in the serial monitor
+    delay(1000);  // slow transmissions down by 1 second
+
+  } else {
+    // This device is a RX node
+
+    uint8_t pipe;
+    if (radio.available(&pipe)) {                    // is there a payload? get the pipe number that 
recieved it
+      uint8_t bytes = radio.getDynamicPayloadSize(); // get the size of the payload
+      PayloadStruct received;
+      radio.read(&received, sizeof(received));       // get incoming payload
+      Serial.print(F("Received "));
+      Serial.print(bytes);                           // print the size of the payload
+      Serial.print(F(" bytes on pipe "));
+      Serial.print(pipe);                            // print the pipe number
+      Serial.print(F(": "));
+      Serial.print(received.message);                // print incoming message
+      Serial.print(received.counter);                // print incoming counter
+      Serial.print(F(" Sent: "));
+      Serial.print(payload.message);                 // print outgoing message
+      Serial.println(payload.counter);               // print outgoing counter
+
+      // save incoming counter & increment for next outgoing
+      payload.counter = received.counter + 1;
+      // load the payload for the first received transmission on pipe 0
+      radio.writeAckPayload(1, &payload, sizeof(payload));
+    }
+  } // role
+
+  if (Serial.available()) {
+    // change the role via the serial monitor
+
+    char c = toupper(Serial.read());
+    if (c == 'T' && !role) {
+      // Become the TX node
+
+      role = true;
+      Serial.println(F("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK"));
+
+      memcpy(payload.message, "Hello ", 6); // change payload message
+      radio.stopListening();                // this also discards any unused ACK payloads
+
+    } else if (c == 'R' && role) {
+      // Become the RX node
+
+      role = false;
+      Serial.println(F("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK"));
+      memcpy(payload.message, "World ", 6); // change payload message
+
+      // load the payload for the first received transmission on pipe 0
+      radio.writeAckPayload(1, &payload, sizeof(payload));
+      radio.startListening();
+    }
+  }
+} // loop
\ No newline at end of file
diff --git a/arduino/libraries/RF24/examples/GettingStarted/GettingStarted.ino 
b/arduino/libraries/RF24/examples/GettingStarted/GettingStarted.ino
new file mode 100644
index 000000000..d38ed9969
--- /dev/null
+++ b/arduino/libraries/RF24/examples/GettingStarted/GettingStarted.ino
@@ -0,0 +1,154 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty (2bndy5)
+ */
+
+/**
+ * A simple example of sending data from 1 nRF24L01 transceiver to another.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use the Serial Monitor to change each node's behavior.
+ */
+#include <SPI.h>
+#include "printf.h"
+#include "RF24.h"
+
+// instantiate an object for the nRF24L01 transceiver
+RF24 radio(7, 8); // using pin 7 for the CE pin, and pin 8 for the CSN pin
+
+// Let these addresses be used for the pair
+uint8_t address[][6] = {"1Node", "2Node"};
+// It is very helpful to think of an address as a path instead of as
+// an identifying device destination
+
+// to use different addresses on a pair of radios, we need a variable to
+// uniquely identify which address this radio will use to transmit
+bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+// Used to control whether this node is sending or receiving
+bool role = false;  // true = TX role, false = RX role
+
+// For this example, we'll be using a payload containing
+// a single float number that will be incremented
+// on every successful transmission
+float payload = 0.0;
+
+void setup() {
+
+  Serial.begin(115200);
+  while (!Serial) {
+    // some boards need to wait to ensure access to serial over USB
+  }
+
+  // initialize the transceiver on the SPI bus
+  if (!radio.begin()) {
+    Serial.println(F("radio hardware is not responding!!"));
+    while (1) {} // hold in infinite loop
+  }
+
+  // print example's introductory prompt
+  Serial.println(F("RF24/examples/GettingStarted"));
+
+  // To set the radioNumber via the Serial monitor on startup
+  Serial.println(F("Which radio is this? Enter '0' or '1'. Defaults to '0'"));
+  while (!Serial.available()) {
+    // wait for user input
+  }
+  char input = Serial.parseInt();
+  radioNumber = input == 1;
+  Serial.print(F("radioNumber = "));
+  Serial.println((int)radioNumber);
+
+  // role variable is hardcoded to RX behavior, inform the user of this
+  Serial.println(F("*** PRESS 'T' to begin transmitting to the other node"));
+
+  // Set the PA Level low to try preventing power supply related problems
+  // because these examples are likely run with nodes in close proximity to
+  // each other.
+  radio.setPALevel(RF24_PA_LOW);  // RF24_PA_MAX is default.
+
+  // save on transmission time by setting the radio to only transmit the
+  // number of bytes we need to transmit a float
+  radio.setPayloadSize(sizeof(payload)); // float datatype occupies 4 bytes
+
+  // set the TX address of the RX node into the TX pipe
+  radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0
+
+  // set the RX address of the TX node into a RX pipe
+  radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+  // additional setup specific to the node's role
+  if (role) {
+    radio.stopListening();  // put radio in TX mode
+  } else {
+    radio.startListening(); // put radio in RX mode
+  }
+
+  // For debugging info
+  // printf_begin();             // needed only once for printing details
+  // radio.printDetails();       // (smaller) function that prints raw register values
+  // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+} // setup
+
+void loop() {
+
+  if (role) {
+    // This device is a TX node
+
+    unsigned long start_timer = micros();                    // start the timer
+    bool report = radio.write(&payload, sizeof(float));      // transmit & save the report
+    unsigned long end_timer = micros();                      // end the timer
+
+    if (report) {
+      Serial.print(F("Transmission successful! "));          // payload was delivered
+      Serial.print(F("Time to transmit = "));
+      Serial.print(end_timer - start_timer);                 // print the timer result
+      Serial.print(F(" us. Sent: "));
+      Serial.println(payload);                               // print payload sent
+      payload += 0.01;                                       // increment float payload
+    } else {
+      Serial.println(F("Transmission failed or timed out")); // payload was not delivered
+    }
+
+    // to make this example readable in the serial monitor
+    delay(1000);  // slow transmissions down by 1 second
+
+  } else {
+    // This device is a RX node
+
+    uint8_t pipe;
+    if (radio.available(&pipe)) {             // is there a payload? get the pipe number that recieved it
+      uint8_t bytes = radio.getPayloadSize(); // get the size of the payload
+      radio.read(&payload, bytes);            // fetch payload from FIFO
+      Serial.print(F("Received "));
+      Serial.print(bytes);                    // print the size of the payload
+      Serial.print(F(" bytes on pipe "));
+      Serial.print(pipe);                     // print the pipe number
+      Serial.print(F(": "));
+      Serial.println(payload);                // print the payload's value
+    }
+  } // role
+
+  if (Serial.available()) {
+    // change the role via the serial monitor
+
+    char c = toupper(Serial.read());
+    if (c == 'T' && !role) {
+      // Become the TX node
+
+      role = true;
+      Serial.println(F("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK"));
+      radio.stopListening();
+
+    } else if (c == 'R' && role) {
+      // Become the RX node
+
+      role = false;
+      Serial.println(F("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK"));
+      radio.startListening();
+    }
+  }
+
+} // loop
diff --git a/arduino/libraries/RF24/examples/InterruptConfigure/InterruptConfigure.ino 
b/arduino/libraries/RF24/examples/InterruptConfigure/InterruptConfigure.ino
new file mode 100644
index 000000000..5d616edba
--- /dev/null
+++ b/arduino/libraries/RF24/examples/InterruptConfigure/InterruptConfigure.ino
@@ -0,0 +1,346 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty (2bndy5)
+ */
+
+/**
+ * This example uses Acknowledgement (ACK) payloads attached to ACK packets to
+ * demonstrate how the nRF24L01's IRQ (Interrupt Request) pin can be
+ * configured to detect when data is received, or when data has transmitted
+ * successfully, or when data has failed to transmit.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use the Serial Monitor to change each node's behavior.
+ */
+#include <SPI.h>
+#include "printf.h"
+#include "RF24.h"
+
+// We will be using the nRF24L01's IRQ pin for this example
+#define IRQ_PIN 2 // this needs to be a digital input capable pin
+volatile bool wait_for_event = false; // used to wait for an IRQ event to trigger
+
+// instantiate an object for the nRF24L01 transceiver
+RF24 radio(7, 8); // using pin 7 for the CE pin, and pin 8 for the CSN pin
+
+// Let these addresses be used for the pair
+uint8_t address[][6] = {"1Node", "2Node"};
+// It is very helpful to think of an address as a path instead of as
+// an identifying device destination
+
+// to use different addresses on a pair of radios, we need a variable to
+// uniquely identify which address this radio will use to transmit
+bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+// Used to control whether this node is sending or receiving
+bool role = false;  // true = TX node, false = RX node
+
+// For this example, we'll be using a payload containing
+// a string that changes on every transmission. (successful or not)
+// Make a couple arrays of payloads & an iterator to traverse them
+const uint8_t tx_pl_size = 5;
+const uint8_t ack_pl_size = 4;
+uint8_t pl_iterator = 0;
+// The " + 1" compensates for the c-string's NULL terminating 0
+char tx_payloads[][tx_pl_size + 1] = {"Ping ", "Pong ", "Radio", "1FAIL"};
+char ack_payloads[][ack_pl_size + 1] = {"Yak ", "Back", " ACK"};
+
+void interruptHandler(); // prototype to handle IRQ events
+void printRxFifo();      // prototype to print RX FIFO with 1 buffer
+
+
+void setup() {
+  Serial.begin(115200);
+  while (!Serial) {
+    // some boards need to wait to ensure access to serial over USB
+  }
+
+  // initialize the transceiver on the SPI bus
+  if (!radio.begin()) {
+    Serial.println(F("radio hardware is not responding!!"));
+    while (1) {} // hold in infinite loop
+  }
+
+  // print example's introductory prompt
+  Serial.println(F("RF24/examples/InterruptConfigure"));
+
+  // To set the radioNumber via the Serial monitor on startup
+  Serial.println(F("Which radio is this? Enter '0' or '1'. Defaults to '0'"));
+  while (!Serial.available()) {
+    // wait for user input
+  }
+  char input = Serial.parseInt();
+  radioNumber = input == 1;
+  Serial.print(F("radioNumber = "));
+  Serial.println((int)radioNumber);
+
+  // role variable is hardcoded to RX behavior, inform the user of this
+  Serial.println(F("*** PRESS 'T' to begin transmitting to the other node"));
+
+  // setup the IRQ_PIN
+  pinMode(IRQ_PIN, INPUT);
+  attachInterrupt(digitalPinToInterrupt(IRQ_PIN), interruptHandler, FALLING);
+  // IMPORTANT: do not call radio.available() before calling
+  // radio.whatHappened() when the interruptHandler() is triggered by the
+  // IRQ pin FALLING event. According to the datasheet, the pipe information
+  // is unreliable during the IRQ pin FALLING transition.
+
+  // Set the PA Level low to try preventing power supply related problems
+  // because these examples are likely run with nodes in close proximity to
+  // each other.
+  radio.setPALevel(RF24_PA_LOW);    // RF24_PA_MAX is default.
+
+  // For this example we use acknowledgment (ACK) payloads to trigger the
+  // IRQ pin when data is received on the TX node.
+  // to use ACK payloads, we need to enable dynamic payload lengths
+  radio.enableDynamicPayloads();    // ACK payloads are dynamically sized
+
+  // Acknowledgement packets have no payloads by default. We need to enable
+  // this feature for all nodes (TX & RX) to use ACK payloads.
+  radio.enableAckPayload();
+  // Fot this example, we use the same address to send data back and forth
+
+  // set the TX address of the RX node into the TX pipe
+  radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0
+
+  // set the RX address of the TX node into a RX pipe
+  radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+  // additional setup specific to the node's role
+  if (role) {
+    // setup for TX mode
+    radio.stopListening();                                  // put radio in TX mode
+
+  } else {
+    // setup for RX mode
+
+    // let IRQ pin only trigger on "data ready" event in RX mode
+    radio.maskIRQ(1, 1, 0); // args = "data_sent", "data_fail", "data_ready"
+
+    // Fill the TX FIFO with 3 ACK payloads for the first 3 received
+    // transmissions on pipe 1
+    radio.writeAckPayload(1, &ack_payloads[0], ack_pl_size);
+    radio.writeAckPayload(1, &ack_payloads[1], ack_pl_size);
+    radio.writeAckPayload(1, &ack_payloads[2], ack_pl_size);
+
+    radio.startListening(); // put radio in RX mode
+  }
+
+  // For debugging info
+  // printf_begin();             // needed only once for printing details
+  // radio.printDetails();       // (smaller) function that prints raw register values
+  // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+}
+
+void loop() {
+  if (role && !wait_for_event) {
+
+    // delay(1); // wait for IRQ pin to fully RISE
+
+    // This device is a TX node. This if block is only triggered when
+    // NOT waiting for an IRQ event to happen
+
+    if (pl_iterator == 0) {
+      // Test the "data ready" event with the IRQ pin
+
+      Serial.println(F("\nConfiguring IRQ pin to ignore the 'data sent' event"));
+      radio.maskIRQ(true, false, false); // args = "data_sent", "data_fail", "data_ready"
+      Serial.println(F("   Pinging RX node for 'data ready' event..."));
+
+    } else if (pl_iterator == 1) {
+      // Test the "data sent" event with the IRQ pin
+
+      Serial.println(F("\nConfiguring IRQ pin to ignore the 'data ready' event"));
+      radio.maskIRQ(false, false, true); // args = "data_sent", "data_fail", "data_ready"
+      Serial.println(F("   Pinging RX node for 'data sent' event..."));
+
+    } else if (pl_iterator == 2) {
+      // Use this iteration to fill the RX node's FIFO which sets us up for the next test.
+
+      // write() uses virtual interrupt flags that work despite the masking of the IRQ pin
+      radio.maskIRQ(1, 1, 1); // disable IRQ masking for this step
+
+      Serial.println(F("\nSending 1 payload to fill RX node's FIFO. IRQ pin is neglected."));
+      // write() will call flush_tx() on 'data fail' events
+      if (radio.write(&tx_payloads[pl_iterator], tx_pl_size)) {
+        if (radio.rxFifoFull()) {
+          Serial.println(F("RX node's FIFO is full; it is not listening any more"));
+        } else {
+          Serial.println("Transmission successful, but the RX node might still be listening.");
+        }
+      } else {
+        Serial.println(F("Transmission failed or timed out. Continuing anyway."));
+        radio.flush_tx(); // discard payload(s) that failed to transmit
+      }
+
+    } else if (pl_iterator == 3) {
+      // test the "data fail" event with the IRQ pin
+
+      Serial.println(F("\nConfiguring IRQ pin to reflect all events"));
+      radio.maskIRQ(0, 0, 0); // args = "data_sent", "data_fail", "data_ready"
+      Serial.println(F("   Pinging inactive RX node for 'data fail' event..."));
+    }
+
+    if (pl_iterator < 4 && pl_iterator != 2) {
+
+      // IRQ pin is LOW when activated. Otherwise it is always HIGH
+      // Wait until IRQ pin is activated.
+      wait_for_event = true;
+
+      // use the non-blocking call to write a payload and begin transmission
+      // the "false" argument means we are expecting an ACK packet response
+      radio.startFastWrite(tx_payloads[pl_iterator++], tx_pl_size, false);
+
+      // In this example, the "data fail" event is always configured to
+      // trigger the IRQ pin active. Because the auto-ACK feature is on by
+      // default, we don't need a timeout check to prevent an infinite loop.
+
+    } else if (pl_iterator == 4) {
+      // all IRQ tests are done; flush_tx() and print the ACK payloads for fun
+
+      // CE pin is still HIGH which consumes more power. Example is now idling so...
+      radio.stopListening(); // ensure CE pin is LOW
+      // stopListening() also calls flush_tx() when ACK payloads are enabled
+
+      printRxFifo();
+      pl_iterator++;
+
+
+      // inform user what to do next
+      Serial.println(F("\n*** PRESS 'T' to restart the transmissions"));
+      Serial.println(F("*** PRESS 'R' to change to Receive role\n"));
+
+
+    } else if (pl_iterator == 2) {
+      pl_iterator++; // proceed from step 3 to last step (stop at step 4 for readability)
+    }
+
+  } else if (!role) {
+    // This device is a RX node
+
+    if (radio.rxFifoFull()) {
+      // wait until RX FIFO is full then stop listening
+
+      delay(100);             // let ACK payload finish transmitting
+      radio.stopListening();  // also discards unused ACK payloads
+      printRxFifo();          // flush the RX FIFO
+
+      // Fill the TX FIFO with 3 ACK payloads for the first 3 received
+      // transmissions on pipe 1.
+      radio.writeAckPayload(1, &ack_payloads[0], ack_pl_size);
+      radio.writeAckPayload(1, &ack_payloads[1], ack_pl_size);
+      radio.writeAckPayload(1, &ack_payloads[2], ack_pl_size);
+
+      delay(100);             // let TX node finish its role
+      radio.startListening(); // We're ready to start over. Begin listening.
+    }
+
+  } // role
+
+  if (Serial.available()) {
+    // change the role via the serial monitor
+
+    char c = toupper(Serial.read());
+    if (c == 'T') {
+      // Become the TX node
+      if (!role)
+        Serial.println(F("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK"));
+      else
+        Serial.println(F("*** RESTARTING IRQ PIN TEST ***"));
+
+      role = true;
+      wait_for_event = false;
+      pl_iterator = 0;   // reset the iterator
+      radio.flush_tx();  // discard any payloads in the TX FIFO
+
+      // startListening() clears the IRQ masks also. This is required for
+      // continued TX operations when a transmission fails.
+      radio.stopListening(); // this also discards any unused ACK payloads
+
+    } else if (c == 'R' && role) {
+      // Become the RX node
+      Serial.println(F("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK"));
+
+      role = false;
+
+      radio.maskIRQ(0, 0, 0); // the IRQ pin should only trigger on "data ready" event
+
+      // Fill the TX FIFO with 3 ACK payloads for the first 3 received
+      // transmissions on pipe 1
+      radio.flush_tx(); // make sure there is room for 3 new ACK payloads
+      radio.writeAckPayload(1, &ack_payloads[0], ack_pl_size);
+      radio.writeAckPayload(1, &ack_payloads[1], ack_pl_size);
+      radio.writeAckPayload(1, &ack_payloads[2], ack_pl_size);
+      radio.startListening();
+    }
+  } // Serial.available()
+} // loop
+
+
+/**
+ * when the IRQ pin goes active LOW, call this fuction print out why
+ */
+void interruptHandler() {
+  // print IRQ status and all masking flags' states
+
+  Serial.println(F("\tIRQ pin is actively LOW")); // show that this function was called
+  delayMicroseconds(250);
+  bool tx_ds, tx_df, rx_dr;                       // declare variables for IRQ masks
+  radio.whatHappened(tx_ds, tx_df, rx_dr);        // get values for IRQ masks
+  // whatHappened() clears the IRQ masks also. This is required for
+  // continued TX operations when a transmission fails.
+  // clearing the IRQ masks resets the IRQ pin to its inactive state (HIGH)
+
+  Serial.print(F("\tdata_sent: "));
+  Serial.print(tx_ds);                            // print "data sent" mask state
+  Serial.print(F(", data_fail: "));
+  Serial.print(tx_df);                            // print "data fail" mask state
+  Serial.print(F(", data_ready: "));
+  Serial.println(rx_dr);                          // print "data ready" mask state
+
+  if (tx_df)                                      // if TX payload failed
+    radio.flush_tx();                             // clear all payloads from the TX FIFO
+
+  // print if test passed or failed. Unintentional fails mean the RX node was not listening.
+  // pl_iterator has already been incremented by now
+  if (pl_iterator <= 1) {
+    Serial.print(F("   'Data Ready' event test "));
+    Serial.println(rx_dr ? F("passed") : F("failed"));
+  } else if (pl_iterator == 2) {
+    Serial.print(F("   'Data Sent' event test "));
+    Serial.println(tx_ds ? F("passed") : F("failed"));
+  } else if (pl_iterator == 4) {
+    Serial.print(F("   'Data Fail' event test "));
+    Serial.println(tx_df ? F("passed") : F("failed"));
+  }
+  wait_for_event = false; // ready to continue with loop() operations
+} // interruptHandler
+
+
+/**
+ * Print the entire RX FIFO with one buffer. This will also flush the RX FIFO.
+ * Remember that the payload sizes are declared as tx_pl_size and ack_pl_size.
+ */
+void printRxFifo() {
+  if (radio.available()) {                   // if there is data in the RX FIFO
+    // to flush the data from the RX FIFO, we'll fetch it all using 1 buffer
+
+    uint8_t pl_size = !role ? tx_pl_size : ack_pl_size;
+    char rx_fifo[pl_size * 3 + 1];       // RX FIFO is full & we know ACK payloads' size
+    if (radio.rxFifoFull()) {
+      rx_fifo[pl_size * 3] = 0;          // add a NULL terminating char to use as a c-string
+      radio.read(&rx_fifo, pl_size * 3); // this clears the RX FIFO (for this example)
+    } else {
+      uint8_t i = 0;
+      while (radio.available()) {
+        radio.read(&rx_fifo + (i * pl_size), pl_size);
+        i++;
+      }
+      rx_fifo[i * pl_size] = 0;          // add a NULL terminating char to use as a c-string
+    }
+    Serial.print(F("Complete RX FIFO: "));
+    Serial.println(rx_fifo);                 // print the entire RX FIFO with 1 buffer
+  }
+}
\ No newline at end of file
diff --git a/arduino/libraries/RF24/examples/ManualAcknowledgements/ManualAcknowledgements.ino 
b/arduino/libraries/RF24/examples/ManualAcknowledgements/ManualAcknowledgements.ino
new file mode 100644
index 000000000..17d45933b
--- /dev/null
+++ b/arduino/libraries/RF24/examples/ManualAcknowledgements/ManualAcknowledgements.ino
@@ -0,0 +1,220 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty (2bndy5)
+ */
+
+/**
+ * A simple example of sending data from 1 nRF24L01 transceiver to another
+ * with manually transmitted (non-automatic) Acknowledgement (ACK) payloads.
+ * This example still uses ACK packets, but they have no payloads. Instead the
+ * acknowledging response is sent with `write()`. This tactic allows for more
+ * updated acknowledgement payload data, where actual ACK payloads' data are
+ * outdated by 1 transmission because they have to loaded before receiving a
+ * transmission.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use the Serial Monitor to change each node's behavior.
+ */
+#include <SPI.h>
+#include "printf.h"
+#include "RF24.h"
+
+// instantiate an object for the nRF24L01 transceiver
+RF24 radio(7, 8); // using pin 7 for the CE pin, and pin 8 for the CSN pin
+
+// Let these addresses be used for the pair
+uint8_t address[][6] = {"1Node", "2Node"};
+// It is very helpful to think of an address as a path instead of as
+// an identifying device destination
+
+// to use different addresses on a pair of radios, we need a variable to
+// uniquely identify which address this radio will use to transmit
+bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+// Used to control whether this node is sending or receiving
+bool role = false;  // true = TX node, false = RX node
+
+// For this example, we'll be using a payload containing
+// a string & an integer number that will be incremented
+// on every successful transmission.
+// Make a data structure to store the entire payload of different datatypes
+struct PayloadStruct {
+  char message[7];          // only using 6 characters for TX & RX payloads
+  uint8_t counter;
+};
+PayloadStruct payload;
+
+void setup() {
+
+  // append a NULL terminating character for printing as a c-string
+  payload.message[6] = 0;
+
+  Serial.begin(115200);
+  while (!Serial) {
+    // some boards need to wait to ensure access to serial over USB
+  }
+
+  // initialize the transceiver on the SPI bus
+  if (!radio.begin()) {
+    Serial.println(F("radio hardware is not responding!!"));
+    while (1) {} // hold in infinite loop
+  }
+
+  // print example's introductory prompt
+  Serial.println(F("RF24/examples/ManualAcknowledgements"));
+
+  // To set the radioNumber via the Serial monitor on startup
+  Serial.println(F("Which radio is this? Enter '0' or '1'. Defaults to '0'"));
+  while (!Serial.available()) {
+    // wait for user input
+  }
+  char input = Serial.parseInt();
+  radioNumber = input == 1;
+  Serial.print(F("radioNumber = "));
+  Serial.println((int)radioNumber);
+
+  // role variable is hardcoded to RX behavior, inform the user of this
+  Serial.println(F("*** PRESS 'T' to begin transmitting to the other node"));
+
+  // Set the PA Level low to try preventing power supply related problems
+  // because these examples are likely run with nodes in close proximity to
+  // each other.
+  radio.setPALevel(RF24_PA_LOW); // RF24_PA_MAX is default.
+
+  // save on transmission time by setting the radio to only transmit the
+  // number of bytes we need to transmit a float
+  radio.setPayloadSize(sizeof(payload)); // char[7] & uint8_t datatypes occupy 8 bytes
+
+  // set the TX address of the RX node into the TX pipe
+  radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0
+
+  // set the RX address of the TX node into a RX pipe
+  radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+  if (role) {
+    // setup the TX node
+
+    memcpy(payload.message, "Hello ", 6); // set the outgoing message
+    radio.stopListening();                // put radio in TX mode
+  } else {
+    // setup the RX node
+
+    memcpy(payload.message, "World ", 6); // set the outgoing message
+    radio.startListening();               // put radio in RX mode
+  }
+
+  // For debugging info
+  // printf_begin();             // needed only once for printing details
+  // radio.printDetails();       // (smaller) function that prints raw register values
+  // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+} // setup()
+
+void loop() {
+
+  if (role) {
+    // This device is a TX node
+
+    unsigned long start_timer = micros();                 // start the timer
+    bool report = radio.write(&payload, sizeof(payload)); // transmit & save the report
+
+    if (report) {
+      // transmission successful; wait for response and print results
+
+      radio.startListening();                                // put in RX mode
+      unsigned long start_timeout = millis();                // timer to detect timeout
+      while (!radio.available()) {                           // wait for response
+        if (millis() - start_timeout > 200)                  // only wait 200 ms
+          break;
+      }
+      unsigned long end_timer = micros();                    // end the timer
+      radio.stopListening();                                 // put back in TX mode
+
+      // print summary of transactions
+      Serial.print(F("Transmission successful!"));           // payload was delivered
+      uint8_t pipe;
+      if (radio.available(&pipe)) {                          // is there a payload received
+        Serial.print(F(" Round-trip delay: "));
+        Serial.print(end_timer - start_timer);               // print the timer result
+        Serial.print(F(" us. Sent: "));
+        Serial.print(payload.message);                       // print the outgoing payload's message
+        Serial.print(payload.counter);                       // print outgoing payload's counter
+        PayloadStruct received;
+        radio.read(&received, sizeof(received));             // get payload from RX FIFO
+        Serial.print(F(" Received "));
+        Serial.print(radio.getPayloadSize());                // print the size of the payload
+        Serial.print(F(" bytes on pipe "));
+        Serial.print(pipe);                                  // print the pipe number
+        Serial.print(F(": "));
+        Serial.print(received.message);                      // print the incoming payload's message
+        Serial.println(received.counter);                    // print the incoming payload's counter
+        payload.counter = received.counter;                  // save incoming counter for next outgoing 
counter
+      } else {
+        Serial.println(F(" Recieved no response."));         // no response received
+      }
+    } else {
+      Serial.println(F("Transmission failed or timed out")); // payload was not delivered
+    } // report
+
+    // to make this example readable in the serial monitor
+    delay(1000);  // slow transmissions down by 1 second
+
+  } else {
+    // This device is a RX node
+
+    uint8_t pipe;
+    if (radio.available(&pipe)) {              // is there a payload? get the pipe number that recieved it
+      PayloadStruct received;
+      radio.read(&received, sizeof(received)); // get incoming payload
+      payload.counter = received.counter + 1;  // increment incoming counter for next outgoing response
+
+      // transmit response & save result to `report`
+      radio.stopListening();                   // put in TX mode
+
+      radio.writeFast(&payload, sizeof(payload));  // load response to TX FIFO
+      bool report = radio.txStandBy(150);   // keep retrying for 150 ms
+
+      radio.startListening();                  // put back in RX mode
+
+      // print summary of transactions
+      Serial.print(F("Received "));
+      Serial.print(radio.getPayloadSize());    // print the size of the payload
+      Serial.print(F(" bytes on pipe "));
+      Serial.print(pipe);                      // print the pipe number
+      Serial.print(F(": "));
+      Serial.print(received.message);          // print incoming message
+      Serial.print(received.counter);          // print incoming counter
+
+      if (report) {
+        Serial.print(F(" Sent: "));
+        Serial.print(payload.message);         // print outgoing message
+        Serial.println(payload.counter);       // print outgoing counter
+      } else {
+        Serial.println(" Response failed.");   // failed to send response
+      }
+    }
+  } // role
+
+  if (Serial.available()) {
+    // change the role via the serial monitor
+
+    char c = toupper(Serial.read());
+    if (c == 'T' && !role) {
+      // Become the TX node
+
+      role = true;
+      memcpy(payload.message, "Hello ", 6); // set the outgoing message
+      Serial.println(F("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK"));
+      radio.stopListening();                // put in TX mode
+
+    } else if (c == 'R' && role) {
+      // Become the RX node
+
+      role = false;
+      memcpy(payload.message, "World ", 6); // set the response message
+      Serial.println(F("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK"));
+      radio.startListening();               // put in RX mode
+    }
+  }
+} // loop
\ No newline at end of file
diff --git a/arduino/libraries/RF24/examples/MulticeiverDemo/MulticeiverDemo.ino 
b/arduino/libraries/RF24/examples/MulticeiverDemo/MulticeiverDemo.ino
new file mode 100644
index 000000000..c3ed97b7b
--- /dev/null
+++ b/arduino/libraries/RF24/examples/MulticeiverDemo/MulticeiverDemo.ino
@@ -0,0 +1,198 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty 2bndy5
+ */
+
+/**
+ * A simple example of sending data from as many as 6 nRF24L01 transceivers to
+ * 1 receiving transceiver. This technique is trademarked by
+ * Nordic Semiconductors as "MultiCeiver".
+ *
+ * This example was written to be used on up to 6 devices acting as TX nodes &
+ * only 1 device acting as the RX node (that's a maximum of 7 devices).
+ * Use the Serial Monitor to change each node's behavior.
+ */
+#include <SPI.h>
+#include "printf.h"
+#include "RF24.h"
+
+// instantiate an object for the nRF24L01 transceiver
+RF24 radio(7, 8); // using pin 7 for the CE pin, and pin 8 for the CSN pin
+
+// For this example, we'll be using 6 addresses; 1 for each TX node
+// It is very helpful to think of an address as a path instead of as
+// an identifying device destination
+// Notice that the last byte is the only byte that changes in the last 5
+// addresses. This is a limitation of the nRF24L01 transceiver for pipes 2-5
+// because they use the same first 4 bytes from pipe 1.
+uint64_t address[6] = {0x7878787878LL,
+                       0xB3B4B5B6F1LL,
+                       0xB3B4B5B6CDLL,
+                       0xB3B4B5B6A3LL,
+                       0xB3B4B5B60FLL,
+                       0xB3B4B5B605LL
+                      };
+
+// Because this example allow up to 6 nodes (specified by numbers 0-5) to
+// transmit and only 1 node to receive, we will use a negative value in our
+// role variable to signify this node is a receiver.
+// role variable is used to control whether this node is sending or receiving
+char role = 'R'; // integers 0-5 = TX node; character 'R' or integer 82 = RX node
+
+// For this example, we'll be using a payload containing
+// a node ID number and a single integer number that will be incremented
+// on every successful transmission.
+// Make a data structure to use as a payload.
+struct PayloadStruct
+{
+  unsigned long nodeID;
+  unsigned long payloadID;
+};
+PayloadStruct payload;
+
+// This example uses all 6 pipes to receive while TX nodes only use 2 pipes
+// To make this easier we'll use a function to manage the addresses, and the
+// payload's nodeID
+void setRole(); // declare a prototype; definition is found after the loop()
+
+void setup() {
+
+  Serial.begin(115200);
+  while (!Serial) {
+    // some boards need to wait to ensure access to serial over USB
+  }
+
+  // initialize the transceiver on the SPI bus
+  if (!radio.begin()) {
+    Serial.println(F("radio hardware is not responding!!"));
+    while (1) {} // hold in infinite loop
+  }
+
+  // print example's introductory prompt
+  Serial.println(F("RF24/examples/MulticeiverDemo"));
+  Serial.println(F("*** Enter a number between 0 and 5 (inclusive) to change"));
+  Serial.println(F("    the identifying node number that transmits."));
+
+  // Set the PA Level low to try preventing power supply related problems
+  // because these examples are likely run with nodes in close proximity of
+  // each other.
+  radio.setPALevel(RF24_PA_LOW); // RF24_PA_MAX is default.
+
+  // save on transmission time by setting the radio to only transmit the
+  // number of bytes we need to transmit a float
+  radio.setPayloadSize(sizeof(payload)); // 2x int datatype occupy 8 bytes
+
+  // Set the pipe addresses accordingly. This function additionally also
+  // calls startListening() or stopListening() and sets the payload's nodeID
+  setRole();
+
+  // For debugging info
+  // printf_begin();             // needed only once for printing details
+  // radio.printDetails();       // (smaller) function that prints raw register values
+  // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+} // setup()
+
+void loop() {
+
+  if (role <= 53) {
+    // This device is a TX node
+
+    unsigned long start_timer = micros();                    // start the timer
+    bool report = radio.write(&payload, sizeof(payload));    // transmit & save the report
+    unsigned long end_timer = micros();                      // end the timer
+
+    if (report) {
+      // payload was delivered
+
+      Serial.print(F("Transmission of payloadID "));
+      Serial.print(payload.payloadID);                       // print payloadID
+      Serial.print(F(" as node "));
+      Serial.print(payload.nodeID);                          // print nodeID
+      Serial.print(F(" successful!"));
+      Serial.print(F(" Time to transmit: "));
+      Serial.print(end_timer - start_timer);                 // print the timer result
+      Serial.println(F(" us"));
+    } else {
+      Serial.println(F("Transmission failed or timed out")); // payload was not delivered
+    }
+    payload.payloadID++;                                     // increment payload number
+
+    // to make this example readable in the serial monitor
+    delay(1000); // slow transmissions down by 1 second
+
+  } else if (role == 'R') {
+    // This device is the RX node
+
+    uint8_t pipe;
+    if (radio.available(&pipe)) {             // is there a payload? get the pipe number that recieved it
+      uint8_t bytes = radio.getPayloadSize(); // get the size of the payload
+      radio.read(&payload, bytes);            // fetch payload from FIFO
+      Serial.print(F("Received "));
+      Serial.print(bytes);                    // print the size of the payload
+      Serial.print(F(" bytes on pipe "));
+      Serial.print(pipe);                     // print the pipe number
+      Serial.print(F(" from node "));
+      Serial.print(payload.nodeID);           // print the payload's origin
+      Serial.print(F(". PayloadID: "));
+      Serial.println(payload.payloadID);      // print the payload's number
+    }
+  } // role
+
+  if (Serial.available()) {
+    // change the role via the serial monitor
+
+    char c = Serial.read();
+    if (toupper(c) == 'R' && role <= 53) {
+      // Become the RX node
+
+      role = 'R';
+      Serial.println(F("*** CHANGING ROLE TO RECEIVER ***"));
+      Serial.println(F("--- Enter a number between 0 and 5 (inclusive) to act as"));
+      Serial.println(F("    a unique node number that transmits to the RX node."));
+      setRole(); // change address on all pipes to TX nodes
+
+    } else if (c >= 48 && c <= 53 && c != role) {
+      // Become a TX node with identifier 'c'
+
+      role = c - 48;
+      Serial.print(F("*** CHANGING ROLE TO NODE "));
+      Serial.print(c);
+      Serial.println(F(" ***"));
+      Serial.println(F("--- Enter a number between 0 and 5 (inclusive) to change"));
+      Serial.println(F("    the identifying node number that transmits."));
+      Serial.println(F("--- PRESS 'R' to act as the RX node."));
+      setRole(); // change address on pipe 0 to the RX node
+    }
+  }
+
+} // loop
+
+void setRole() {
+  if (role == 'R') {
+    // For the RX node
+
+    // Set the addresses for all pipes to TX nodes
+    for (uint8_t i = 0; i < 6; ++i)
+      radio.openReadingPipe(i, address[i]);
+
+    radio.startListening(); // put radio in RX mode
+
+  } else {
+    // For the TX node
+
+    // set the payload's nodeID & reset the payload's identifying number
+    payload.nodeID = role;
+    payload.payloadID = 0;
+
+    // Set the address on pipe 0 to the RX node.
+    radio.stopListening(); // put radio in TX mode
+    radio.openWritingPipe(address[role]);
+
+    // According to the datasheet, the auto-retry features's delay value should
+    // be "skewed" to allow the RX node to receive 1 transmission at a time.
+    // So, use varying delay between retry attempts and 15 (at most) retry attempts
+    radio.setRetries(((role * 3) % 12) + 3, 15); // maximum value is 15 for both args
+  }
+} // setRole
diff --git a/arduino/libraries/RF24/examples/StreamingData/StreamingData.ino 
b/arduino/libraries/RF24/examples/StreamingData/StreamingData.ino
new file mode 100644
index 000000000..e17d9b509
--- /dev/null
+++ b/arduino/libraries/RF24/examples/StreamingData/StreamingData.ino
@@ -0,0 +1,185 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty 2bndy5
+ */
+
+/**
+ * A simple example of streaming data from 1 nRF24L01 transceiver to another.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use the Serial Monitor to change each node's behavior.
+ */
+#include <SPI.h>
+#include "printf.h"
+#include "RF24.h"
+
+// instantiate an object for the nRF24L01 transceiver
+RF24 radio(7, 8); // using pin 7 for the CE pin, and pin 8 for the CSN pin
+
+// Let these addresses be used for the pair
+uint8_t address[][6] = {"1Node", "2Node"};
+// It is very helpful to think of an address as a path instead of as
+// an identifying device destination
+
+// to use different addresses on a pair of radios, we need a variable to
+// uniquely identify which address this radio will use to transmit
+bool radioNumber; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+// Used to control whether this node is sending or receiving
+bool role = false; // true = TX node, false = RX node
+
+// For this example, we'll be sending 32 payloads each containing
+// 32 bytes of data that looks like ASCII art when printed to the serial
+// monitor. The TX node and RX node needs only a single 32 byte buffer.
+#define SIZE 32            // this is the maximum for this example. (minimum is 1)
+char buffer[SIZE + 1];     // for the RX node
+uint8_t counter = 0;       // for counting the number of received payloads
+void makePayload(uint8_t); // prototype to construct a payload dynamically
+
+
+void setup() {
+
+  buffer[SIZE] = 0;        // add a NULL terminating character (for easy printing)
+
+  Serial.begin(115200);
+  while (!Serial) {
+    // some boards need to wait to ensure access to serial over USB
+  }
+
+  // initialize the transceiver on the SPI bus
+  if (!radio.begin()) {
+    Serial.println(F("radio hardware is not responding!!"));
+    while (1) {} // hold in infinite loop
+  }
+
+  // print example's introductory prompt
+  Serial.println(F("RF24/examples/StreamingData"));
+
+  // To set the radioNumber via the Serial monitor on startup
+  Serial.println(F("Which radio is this? Enter '0' or '1'. Defaults to '0'"));
+  while (!Serial.available()) {
+    // wait for user input
+  }
+  char input = Serial.parseInt();
+  radioNumber = input == 1;
+  Serial.print(F("radioNumber = "));
+  Serial.println((int)radioNumber);
+
+  // role variable is hardcoded to RX behavior, inform the user of this
+  Serial.println(F("*** PRESS 'T' to begin transmitting to the other node"));
+
+  // Set the PA Level low to try preventing power supply related problems
+  // because these examples are likely run with nodes in close proximity to
+  // each other.
+  radio.setPALevel(RF24_PA_LOW);  // RF24_PA_MAX is default.
+
+  // save on transmission time by setting the radio to only transmit the
+  // number of bytes we need to transmit
+  radio.setPayloadSize(SIZE);     // default value is the maximum 32 bytes
+
+  // set the TX address of the RX node into the TX pipe
+  radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0
+
+  // set the RX address of the TX node into a RX pipe
+  radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+  // additional setup specific to the node's role
+  if (role) {
+    radio.stopListening();  // put radio in TX mode
+  } else {
+    radio.startListening(); // put radio in RX mode
+  }
+
+  // For debugging info
+  // printf_begin();             // needed only once for printing details
+  // radio.printDetails();       // (smaller) function that prints raw register values
+  // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+} // setup()
+
+
+void loop() {
+
+  if (role) {
+    // This device is a TX node
+
+    radio.flush_tx();
+    uint8_t i = 0;
+    uint8_t failures = 0;
+    unsigned long start_timer = micros();       // start the timer
+    while (i < SIZE) {
+      makePayload(i);                           // make the payload
+      if (!radio.writeFast(&buffer, SIZE)) {
+        failures++;
+        radio.reUseTX();
+      } else {
+        i++;
+      }
+
+      if (failures >= 100) {
+        Serial.print(F("Too many failures detected. Aborting at payload "));
+        Serial.println(buffer[0]);
+        break;
+      }
+    }
+    unsigned long end_timer = micros();         // end the timer
+
+    Serial.print(F("Time to transmit = "));
+    Serial.print(end_timer - start_timer);      // print the timer result
+    Serial.print(F(" us with "));
+    Serial.print(failures);                     // print failures detected
+    Serial.println(F(" failures detected"));
+
+    // to make this example readable in the serial monitor
+    delay(1000);  // slow transmissions down by 1 second
+
+  } else {
+    // This device is a RX node
+
+    if (radio.available()) {         // is there a payload?
+      radio.read(&buffer, SIZE);     // fetch payload from FIFO
+      Serial.print(F("Received: "));
+      Serial.print(buffer);          // print the payload's value
+      Serial.print(F(" - "));
+      Serial.println(counter++);     // print the received counter
+    }
+  } // role
+
+  if (Serial.available()) {
+    // change the role via the serial monitor
+
+    char c = toupper(Serial.read());
+    if (c == 'T' && !role) {
+      // Become the TX node
+
+      role = true;
+      counter = 0; //reset the RX node's counter
+      Serial.println(F("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK"));
+      radio.stopListening();
+
+    } else if (c == 'R' && role) {
+      // Become the RX node
+
+      role = false;
+      Serial.println(F("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK"));
+      radio.startListening();
+    }
+  }
+
+} // loop
+
+
+void makePayload(uint8_t i) {
+  // Make a single payload based on position in stream.
+  // This example employs function to save memory on certain boards.
+
+  // let the first character be an identifying alphanumeric prefix
+  // this lets us see which payload didn't get received
+  buffer[0] = i + (i < 26 ? 65 : 71);
+  for (uint8_t j = 0; j < SIZE - 1; ++j) {
+    char chr = j >= (SIZE - 1) / 2 + abs((SIZE - 1) / 2 - i);
+    chr |= j < (SIZE - 1) / 2 - abs((SIZE - 1) / 2 - i);
+    buffer[j + 1] = chr + 48;
+  }
+}
\ No newline at end of file
diff --git a/arduino/libraries/RF24/examples/examples_formatter.conf 
b/arduino/libraries/RF24/examples/examples_formatter.conf
new file mode 100644
index 000000000..57270c833
--- /dev/null
+++ b/arduino/libraries/RF24/examples/examples_formatter.conf
@@ -0,0 +1,31 @@
+# This configuration file contains a selection of the available options provided by the formatting tool 
"Artistic Style"
+# http://astyle.sourceforge.net/astyle.html
+#
+# If you wish to change them, don't edit this file.
+# Instead, copy it in the same folder of file "preferences.txt" and modify the copy. This way, you won't 
lose your custom formatter settings when upgrading the IDE
+# If you don't know where file preferences.txt is stored, open the IDE, File -> Preferences and you'll find 
a link
+
+mode=c
+
+# 2 spaces indentation
+indent=spaces=2
+
+# also indent macros
+indent-preprocessor
+
+# indent classes, switches (and cases), comments starting at column 1
+indent-classes
+indent-switches
+indent-cases
+indent-col1-comments
+
+# put a space around operators
+pad-oper
+
+# put a space after if/for/while
+pad-header
+
+# if you like one-liners, keep them
+keep-one-line-statements
+
+# remove-comment-prefix
diff --git 
a/arduino/libraries/RF24/examples/old_backups/GettingStarted_HandlingFailures/GettingStarted_HandlingFailures.ino
 
b/arduino/libraries/RF24/examples/old_backups/GettingStarted_HandlingFailures/GettingStarted_HandlingFailures.ino
new file mode 100644
index 000000000..6e25c6f6e
--- /dev/null
+++ 
b/arduino/libraries/RF24/examples/old_backups/GettingStarted_HandlingFailures/GettingStarted_HandlingFailures.ino
@@ -0,0 +1,217 @@
+
+/*
+  Getting Started example sketch for nRF24L01+ radios
+  This is a very basic example of how to send data from one node to another
+  but modified to include failure handling.
+
+  The nrf24l01+ radios are fairly reliable devices, but on breadboards etc, with inconsistent wiring, 
failures may
+  occur randomly after many hours to days or weeks. This sketch demonstrates how to handle the various 
failures and
+  keep the radio operational.
+
+  The three main failure modes of the radio include:
+  Writing to radio: Radio unresponsive - Fixed internally by adding a timeout to the internal write 
functions in RF24 (failure handling)
+  Reading from radio: Available returns true always - Fixed by adding a timeout to available functions by 
the user. This is implemented internally in  RF24Network.
+  Radio configuration settings are lost - Fixed by monitoring a value that is different from the default, 
and re-configuring the radio if this setting reverts to the default.
+
+  The printDetails output should appear as follows for radio #0:
+
+  STATUS         = 0x0e RX_DR=0 TX_DS=0 MAX_RT=0 RX_P_NO=7 TX_FULL=0
+  RX_ADDR_P0-1   = 0x65646f4e31 0x65646f4e32
+  RX_ADDR_P2-5   = 0xc3 0xc4 0xc5 0xc6
+  TX_ADDR        = 0x65646f4e31
+  RX_PW_P0-6     = 0x20 0x20 0x00 0x00 0x00 0x00
+  EN_AA          = 0x3f
+  EN_RXADDR      = 0x02
+  RF_CH          = 0x4c
+  RF_SETUP       = 0x03
+  CONFIG         = 0x0f
+  DYNPD/FEATURE  = 0x00 0x00
+  Data Rate      = 1MBPS
+  Model          = nRF24L01+
+  CRC Length     = 16 bits
+  PA Power       = PA_LOW
+
+  Users can use this sketch to troubleshoot radio module wiring etc. as it makes the radios hot-swapable
+
+  Updated: 2019 by TMRh20
+*/
+
+#include <SPI.h>
+#include "RF24.h"
+#include "printf.h"
+
+/****************** User Config ***************************/
+/***      Set this radio as radio number 0 or 1         ***/
+bool radioNumber = 0;
+
+/* Hardware configuration: Set up nRF24L01 radio on SPI bus plus pins 7 & 8 */
+RF24 radio(7, 8);
+/**********************************************************/
+
+byte addresses[][6] = {"1Node", "2Node"};
+
+// Used to control whether this node is sending or receiving
+bool role = 0;
+
+
+/**********************************************************/
+//Function to configure the radio
+void configureRadio() {
+
+  radio.begin();
+
+  // Set the PA Level low to prevent power supply related issues since this is a
+  // getting_started sketch, and the likelihood of close proximity of the devices. RF24_PA_MAX is default.
+  radio.setPALevel(RF24_PA_LOW);
+
+  // Open a writing and reading pipe on each radio, with opposite addresses
+  if (radioNumber) {
+    radio.openWritingPipe(addresses[1]);
+    radio.openReadingPipe(1, addresses[0]);
+  } else {
+    radio.openWritingPipe(addresses[0]);
+    radio.openReadingPipe(1, addresses[1]);
+  }
+
+  // Start the radio listening for data
+  radio.startListening();
+  radio.printDetails();
+}
+
+
+/**********************************************************/
+
+void setup() {
+  Serial.begin(115200);
+  Serial.println(F("RF24/examples/GettingStarted"));
+  Serial.println(F("*** PRESS 'T' to begin transmitting to the other node"));
+
+  printf_begin();
+
+  configureRadio();
+}
+
+uint32_t configTimer =  millis();
+
+void loop() {
+
+  if (radio.failureDetected) {
+    radio.failureDetected = false;
+    delay(250);
+    Serial.println("Radio failure detected, restarting radio");
+    configureRadio();
+  }
+  // Every 5 seconds, verify the configuration of the radio. This can be
+  // done using any setting that is different from the radio defaults.
+  if (millis() - configTimer > 5000) {
+    configTimer = millis();
+    if (radio.getDataRate() != RF24_1MBPS) {
+      radio.failureDetected = true;
+      Serial.print("Radio configuration error detected");
+    }
+  }
+
+
+  /****************** Ping Out Role ***************************/
+
+  if (role == 1) {
+
+    radio.stopListening();                                     // First, stop listening so we can talk.
+
+    Serial.println(F("Now sending"));
+
+    unsigned long start_time = micros();                       // Take the time, and send it.  This will 
block until complete
+    if (!radio.write(&start_time, sizeof(unsigned long))) {
+      Serial.println(F("failed"));
+    }
+
+    radio.startListening();                                    // Now, continue listening
+
+    unsigned long started_waiting_at = micros();               // Set up a timeout period, get the current 
microseconds
+    bool timeout = false;                                      // Set up a variable to indicate if a 
response was received or not
+
+    while (!radio.available())                                 // While nothing is received
+    {
+      if (micros() - started_waiting_at > 200000 )             // If waited longer than 200ms, indicate 
timeout and exit while loop
+      {
+        timeout = true;
+        break;
+      }
+    }
+
+    if (timeout) {
+      // Describe the results
+      Serial.println(F("Failed, response timed out."));
+    } else {
+      // Grab the response, compare, and send to debugging spew
+
+      unsigned long got_time;                                  // Variable for the received timestamp
+
+      // Failure Handling
+      uint32_t failTimer = millis();
+      while (radio.available())                                // If available() always returns true, there 
is a problem
+      {
+        if (millis() - failTimer > 250) {
+          radio.failureDetected = true;
+          Serial.println("Radio available failure detected");
+          break;
+        }
+        radio.read(&got_time, sizeof(unsigned long));
+      }
+      unsigned long end_time = micros();
+
+      // Spew it
+      Serial.print(F("Sent "));
+      Serial.print(start_time);
+      Serial.print(F(", Got response "));
+      Serial.print(got_time);
+      Serial.print(F(", Round-trip delay "));
+      Serial.print(end_time - start_time);
+      Serial.println(F(" microseconds"));
+    }
+
+    delay(1000);                                               // Try again 1s later
+  }
+
+
+  /****************** Pong Back Role ***************************/
+
+  if (role == 0) {
+    unsigned long got_time;                                    // Variable for the received timestamp
+
+    if (radio.available()) {
+      uint32_t failTimer = millis();
+
+      while (radio.available())                                // While there is data ready
+      {
+        if (millis() - failTimer > 500) {
+          Serial.println("Radio available failure detected");
+          radio.failureDetected = true;
+          break;
+        }
+        radio.read(&got_time, sizeof(unsigned long));          // Get the payload
+      }
+
+      radio.stopListening();                                   // First, stop listening so we can talk
+      radio.write(&got_time, sizeof(unsigned long));           // Send the final one back.
+      radio.startListening();                                  // Now, resume listening so we catch the next 
packets.
+      Serial.print(F("Sent response "));
+      Serial.println(got_time);
+    }
+  }
+
+
+  /****************** Change Roles via Serial Commands ***************************/
+
+  if (Serial.available()) {
+    char c = toupper(Serial.read());
+    if (c == 'T' && role == 0) {
+      Serial.println(F("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK"));
+      role = 1;                  // Become the primary transmitter (ping out)
+    } else if ( c == 'R' && role == 1 ) {
+      Serial.println(F("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK"));
+      role = 0;                  // Become the primary receiver (pong back)
+      radio.startListening();
+    }
+  }
+} // Loop
diff --git a/arduino/libraries/RF24/examples/old_backups/TransferTimeouts/TransferTimeouts.ino 
b/arduino/libraries/RF24/examples/old_backups/TransferTimeouts/TransferTimeouts.ino
new file mode 100644
index 000000000..cec44ed96
--- /dev/null
+++ b/arduino/libraries/RF24/examples/old_backups/TransferTimeouts/TransferTimeouts.ino
@@ -0,0 +1,191 @@
+/*
+  TMRh20 2014
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License
+  version 2 as published by the Free Software Foundation.
+*/
+
+/** Reliably transmitting large volumes of data with a low signal or in noisy environments
+   This example demonstrates data transfer functionality with the use of auto-retry
+  and auto-reUse functionality enabled. This sketch demonstrates how a user can extend
+  the auto-retry functionality to any chosen time period, preventing data loss and ensuring
+  the consistency of data.
+
+  This sketh demonstrates use of the writeBlocking() functionality, and extends the standard
+  retry functionality of the radio. Payloads will be auto-retried until successful or the
+  extended timeout period is reached.
+*/
+
+
+
+#include <SPI.h>
+#include "nRF24L01.h"
+#include "RF24.h"
+#include "printf.h"
+
+/*************  USER Configuration *****************************/
+
+RF24 radio(7, 8);                       // Set up nRF24L01 radio on SPI bus plus pins 7 & 8
+unsigned long timeoutPeriod = 3000;     // Set a user-defined timeout period. With auto-retransmit set to 
(15,15) retransmission will take up to 60ms and as little as 7.5ms with it set to (1,15).
+// With a timeout period of 1000, the radio will retry each payload for up to 1 second before giving up on 
the transmission and starting over
+
+/***************************************************************/
+
+const uint64_t pipes[2] = { 0xABCDABCD71LL, 0x544d52687CLL };   // Radio pipe addresses for the 2 nodes to 
communicate.
+
+byte data[32];                           //Data buffer
+
+volatile unsigned long counter;
+unsigned long rxTimer, startTime, stopTime, payloads = 0;
+bool tx = 1, rx = 0, role = 0, transferInProgress = 0;
+
+
+void setup(void) {
+
+  Serial.begin(115200);
+  printf_begin();
+
+  radio.begin();                           // Setup and configure rf radio
+  radio.setChannel(1);                     // Set the channel
+  radio.setPALevel(RF24_PA_LOW);           // Set PA LOW for this demonstration. We want the radio to be as 
lossy as possible for this example.
+  radio.setDataRate(RF24_1MBPS);           // Raise the data rate to reduce transmission distance and 
increase lossiness
+  radio.setAutoAck(1);                     // Ensure autoACK is enabled
+  radio.setRetries(2, 15);                 // Optionally, increase the delay between retries. Want the 
number of auto-retries as high as possible (15)
+  radio.setCRCLength(RF24_CRC_16);         // Set CRC length to 16-bit to ensure quality of data
+  radio.openWritingPipe(pipes[0]);         // Open the default reading and writing pipe
+  radio.openReadingPipe(1, pipes[1]);
+
+  radio.startListening();                 // Start listening
+  radio.printDetails();                   // Dump the configuration of the rf unit for debugging
+
+  printf("\n\rRF24/examples/Transfer Rates/\n\r");
+  printf("*** PRESS 'T' to begin transmitting to the other node\n\r");
+
+  randomSeed(analogRead(0));              //Seed for random number generation
+  for (int i = 0; i < 32; i++) {
+    data[i] = random(255);               //Load the buffer with random data
+  }
+  radio.powerUp();                        //Power up the radio
+
+
+}
+
+
+
+void loop(void) {
+
+
+  if (role == tx) {
+    delay(2000);                                              // Pause for a couple seconds between transfers
+    printf("Initiating Extended Timeout Data Transfer\n\r");
+
+    unsigned long cycles = 1000;                              // Change this to a higher or lower number. 
This is the number of payloads that will be sent.
+
+    unsigned long transferCMD[] = {'H', 'S', cycles };        // Indicate to the other radio that we are 
starting, and provide the number of payloads that will be sent
+    radio.writeFast(&transferCMD, 12);                        // Send the transfer command
+    if (radio.txStandBy(timeoutPeriod)) {                     // If transfer initiation was successful, do 
the following
+
+      startTime = millis();                                 // For calculating transfer rate
+      boolean timedOut = 0;                                 // Boolean for keeping track of failures
+
+      for (unsigned long i = 0; i < cycles; i++)            // Loop through a number of cycles
+      {
+        data[0] = i;                                        // Change the first byte of the payload for 
identification
+
+        if (!radio.writeBlocking(&data, 32, timeoutPeriod)) { // If retries are failing and the user defined 
timeout is exceeded
+          timedOut = 1;                                   // Indicate failure
+          counter = cycles;                               // Set the fail count to maximum
+          break;                                          // Break out of the for loop
+        }
+      }
+
+
+      stopTime = millis();                                  // Capture the time of completion or failure
+
+      //This should be called to wait for completion and put the radio in standby mode after transmission, 
returns 0 if data still in FIFO (timed out), 1 if success
+      if (timedOut) {
+        radio.txStandBy();  //Partially blocking standby, blocks until success or max retries. FIFO flushed 
if auto timeout reached
+      }
+      else {
+        radio.txStandBy(timeoutPeriod);      //Standby, block until FIFO empty (sent) or user specified 
timeout reached. FIFO flushed if user timeout reached.
+      }
+
+    } else {
+      Serial.println("Communication not established");       //If unsuccessful initiating transfer, exit and 
retry later
+    }
+
+    float rate = cycles * 32 / (stopTime - startTime);         //Display results:
+
+    Serial.print("Transfer complete at "); Serial.print(rate); printf(" KB/s \n\r");
+    Serial.print(counter);
+    Serial.print(" of ");
+    Serial.print(cycles); Serial.println(" Packets Failed to Send");
+    counter = 0;
+
+  }
+
+
+
+  if (role == rx) {
+
+    if (!transferInProgress) {                     // If a bulk data transfer has not been started
+      if (radio.available()) {
+        radio.read(&data, 32);                   //Read any available payloads for analysis
+
+        if (data[0] == 'H' && data[4] == 'S') {  // If a bulk data transfer command has been received
+          payloads = data[8];                    // Read the first two bytes of the unsigned long. Need to 
read the 3rd and 4th if sending more than 65535 payloads
+          payloads |= data[9] << 8;              // This is the number of payloads that will be sent
+          counter = 0;                           // Reset the payload counter to 0
+          transferInProgress = 1;                // Indicate it has started
+          startTime = rxTimer = millis();        // Capture the start time to measure transfer rate and 
calculate timeouts
+        }
+      }
+    } else {
+      if (radio.available()) {                   // If in bulk transfer mode, and a payload is available
+        radio.read(&data, 32);                   // Read the payload
+        rxTimer = millis();                      // Reset the timeout timer
+        counter++;                               // Keep a count of received payloads
+      } else if (millis() - rxTimer > timeoutPeriod) { // If no data available, check the timeout period
+        Serial.println("Transfer Failed");       // If per-payload timeout exceeeded, end the transfer
+        transferInProgress = 0;
+      } else if (counter >= payloads) {                // If the specified number of payloads is reached, 
transfer is completed
+        startTime = millis() - startTime;         // Calculate the total time spent during transfer
+        float numBytes = counter * 32;            // Calculate the number of bytes transferred
+        Serial.print("Rate: ");                   // Print the transfer rate and number of payloads
+        Serial.print(numBytes / startTime);
+        Serial.println(" KB/s");
+        Serial.print("Payload Count: ");
+        Serial.println(counter);
+        transferInProgress = 0;                   // End the transfer as complete
+      }
+    }
+
+
+  }
+
+  //
+  // Change roles
+  //
+
+  if ( Serial.available() )
+  {
+    char c = toupper(Serial.read());
+    if ( c == 'T' && role == rx )
+    {
+      printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n\r");
+      radio.openWritingPipe(pipes[1]);
+      radio.openReadingPipe(1, pipes[0]);
+      radio.stopListening();
+      role = tx;                  // Become the primary transmitter (ping out)
+    }
+    else if ( c == 'R' && role == tx )
+    {
+      radio.openWritingPipe(pipes[0]);
+      radio.openReadingPipe(1, pipes[1]);
+      radio.startListening();
+      printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n\r");
+      role = rx;                // Become the primary receiver (pong back)
+    }
+  }
+}
diff --git a/arduino/libraries/RF24/examples/old_backups/pingpair_dyn/pingpair_dyn.ino 
b/arduino/libraries/RF24/examples/old_backups/pingpair_dyn/pingpair_dyn.ino
new file mode 100644
index 000000000..47636f87c
--- /dev/null
+++ b/arduino/libraries/RF24/examples/old_backups/pingpair_dyn/pingpair_dyn.ino
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2011 J. Coliz <maniacbug ymail com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+/**
+ * Example using Dynamic Payloads
+ *
+ * This is an example of how to use payloads of a varying (dynamic) size.
+ */
+
+#include <SPI.h>
+#include "nRF24L01.h"
+#include "RF24.h"
+#include "printf.h"
+
+// Hardware configuration
+RF24 radio(7, 8);  // Set up nRF24L01 radio on SPI bus plus pins 7 & 8
+
+// Radio pipe addresses for the 2 nodes to communicate.
+const uint64_t addresses[2] = {0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL};
+
+/************************* Role management ****************************/
+// Set up role.  This sketch uses the same software for all the nodes in this
+// system.  Doing so greatly simplifies testing.
+
+// The role_pin is a digital input pin used to set the role of this radio.
+// Connect the role_pin to GND to be the 'pong' receiver
+// Leave the role_pin open to be the 'ping' transmitter
+const short role_pin = 5;                                                 // use pin 5
+typedef enum { role_ping_out = 1, role_pong_back } role_e;                // The various roles supported by 
this sketch
+const char* role_friendly_name[] = {"invalid", "Ping out", "Pong back"};  // The debug-friendly names of 
those roles
+role_e role;                                                              // The role of the current running 
sketch
+
+
+// variables used for changing the payload size dynamically (used when role == role_ping_out)
+const int min_payload_size = 4;
+const int max_payload_size = 32;
+const int payload_size_increment = 1;
+int send_payload_size = min_payload_size;
+
+char receive_payload[max_payload_size + 1];  // +1 to allow room for a terminating NULL char
+
+void setup(void)
+{
+  pinMode(role_pin, INPUT);       // set up the role pin
+  digitalWrite(role_pin, HIGH);
+  delay(20);                      // Just to get a solid reading on the role pin
+
+  // read the role_pin, establish our role
+  if (digitalRead(role_pin)) {
+    role = role_ping_out;
+  } else {
+    role = role_pong_back;
+  }
+
+  Serial.begin(115200);
+  printf_begin();                 // needed for printDetails()
+
+  // Print preamble
+  Serial.println(F("RF24/examples/pingpair_dyn/"));
+  Serial.print(F("ROLE: "));
+  Serial.println(role_friendly_name[role]);
+
+  // Setup and configure rf radio
+  radio.begin();
+  radio.enableDynamicPayloads();  // Enable dynamic payloads
+  radio.setRetries(5, 15);        // delay between retries = 5 * 250 + 250 = 1500 microseconds, number of 
retries = 15
+
+  // Open a writing and reading pipe on each radio, with opposite addresses
+  if (role == role_ping_out) {
+    radio.openWritingPipe(addresses[0]);
+    radio.openReadingPipe(1, addresses[1]);
+  } else {
+    radio.openWritingPipe(addresses[1]);
+    radio.openReadingPipe(1, addresses[0]);
+  }
+
+  radio.startListening();         // Start listening
+  radio.printDetails();           // Dump the configuration of the rf unit for debugging
+}
+
+void loop() {
+
+
+  /****************** Ping Out Role ***************************/
+
+  if (role == role_ping_out) {
+    // The payload will always be the same, what will change is how much of it we send.
+    static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012";
+
+    radio.stopListening();                          // First, stop listening so we can talk.
+
+    // Send the payload
+    Serial.print(F("Now sending length "));
+    Serial.println(send_payload_size);
+    radio.write(send_payload, send_payload_size);   // This will block until complete
+
+    radio.startListening();                         // Now, continue listening
+
+    unsigned long started_waiting_at = millis();    // Start a timer for measuring timout
+    bool timeout = false;
+    while (!radio.available() && !timeout)          // Wait until we get a response or timeout is reached
+    {
+      if (millis() - started_waiting_at > 500)      // Only wait for 500 milliseconds
+        timeout = true;
+    }
+
+    // Describe the results
+    if (timeout) {
+      Serial.println(F("Failed, response timed out."));
+    } else {
+      // Grab the response and print it
+
+      uint8_t len = radio.getDynamicPayloadSize();  // get payload's length
+
+      // If an illegal payload size was detected, all RX payloads will be flushed
+      if (!len)
+        return;
+
+      radio.read(receive_payload, len);
+
+      // Use payload as a C-string (for easy printing)
+      receive_payload[len] = 0;                     // put a NULL terminating zero at the end
+
+      // Spew it
+      Serial.print(F("Got response size="));
+      Serial.print(len);
+      Serial.print(F(" value="));
+      Serial.println(receive_payload);
+    }
+
+    send_payload_size += payload_size_increment;    // Update size for next time.
+    if (send_payload_size > max_payload_size)       // if payload length is larger than the radio can handle
+      send_payload_size = min_payload_size;         // reset the payload length
+
+    delay(1000);                                    // Try again 1s later
+  }
+
+
+  /****************** Pong Back Role ***************************/
+  // Receive each packet, send it back, and dump it out
+
+  if (role == role_pong_back) {
+    while (radio.available())                       // if there is data ready
+    {
+
+      uint8_t len = radio.getDynamicPayloadSize();  // Fetch the the payload size
+
+      // If an illegal payload size was detected, all RX payloads will be flushed
+      if (!len)
+        continue;
+
+      radio.read(receive_payload, len);
+
+      // Use payload as a C-string (for easy printing)
+      receive_payload[len] = 0;                     // put a NULL terminating zero at the end
+
+      // Spew it
+      Serial.print(F("Got response size="));
+      Serial.print(len);
+      Serial.print(F(" value="));
+      Serial.println(receive_payload);
+
+      radio.stopListening();                        // First, stop listening so we can talk
+
+      // Send a reply that the packet was received
+      //
+      // You will have better luck delivering your message if
+      // you wait for the other node to start listening first
+      delay(20);
+      radio.write(receive_payload, len);
+      Serial.println(F("Sent response."));
+
+      radio.startListening();                       // Now, resume listening so we catch the next packets.
+    }
+  }
+} // loop
+// vim:cin:ai:sts=2 sw=2 ft=cpp
diff --git a/arduino/libraries/RF24/examples/old_backups/pingpair_irq/pingpair_irq.ino 
b/arduino/libraries/RF24/examples/old_backups/pingpair_irq/pingpair_irq.ino
new file mode 100644
index 000000000..2a5ed9125
--- /dev/null
+++ b/arduino/libraries/RF24/examples/old_backups/pingpair_irq/pingpair_irq.ino
@@ -0,0 +1,172 @@
+/*
+  Copyright (C) 2011 J. Coliz <maniacbug ymail com>
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License
+  version 2 as published by the Free Software Foundation.
+
+  Update 2014 - TMRh20
+*/
+
+/**
+   Example of using interrupts
+
+   This is an example of how to user interrupts to interact with the radio, and a demonstration
+   of how to use them to sleep when receiving, and not miss any payloads.
+   The pingpair_sleepy example expands on sleep functionality with a timed sleep option for the transmitter.
+   Sleep functionality is built directly into my fork of the RF24Network library
+*/
+
+#include <SPI.h>
+#include "nRF24L01.h"
+#include "RF24.h"
+#include "printf.h"
+
+// Hardware configuration
+RF24 radio(7, 8);                   // Set up nRF24L01 radio on SPI bus plus pins 7 & 8
+
+// Our ACK payload will simply be 4 bytes containing the number of payloads received
+static uint32_t message_count = 1;  // start counting at 1
+
+// Demonstrates another method of setting up the addresses
+byte address[][5] = {0xCC, 0xCE, 0xCC, 0xCE, 0xCC, 0xCE, 0xCC, 0xCE, 0xCC, 0xCE};
+
+/************************* Role management ****************************/
+// Set up role.  This sketch uses the same software for all the nodes in this
+// system.  Doing so greatly simplifies testing.
+
+// The role_pin is a digital input pin used to set the role of this radio.
+// Connect the role_pin to GND to be the 'pong' receiver
+// Leave the role_pin open to be the 'ping' transmitter
+const short role_pin = 5;                                              // use pin 5
+typedef enum { role_sender = 1, role_receiver } role_e;                // The various roles supported by 
this sketch
+const char* role_friendly_name[] = {"invalid", "Sender", "Receiver"};  // The debug-friendly names of those 
roles
+role_e role;                                                           // The role of the current running 
sketch
+
+
+void setup() {
+
+  pinMode(role_pin, INPUT);      // set up the role pin
+  digitalWrite(role_pin, HIGH);  // Change this to LOW/HIGH instead of using an external pin
+  delay(20);                     // Just to get a solid reading on the role pin
+
+  if (digitalRead(role_pin))     // read the role_pin pin to establish our role
+    role = role_sender;
+  else
+    role = role_receiver;
+
+
+  Serial.begin(115200);
+  printf_begin();                // needed for printDetails()
+
+  // print introduction
+  Serial.print(F("\n\rRF24/examples/pingpair_irq\n\rROLE: "));
+  Serial.println(role_friendly_name[role]);
+
+
+  /********************** Setup and configure rf radio *********************/
+  radio.begin();
+
+  // Examples are usually run with both radios in close proximity to each other
+  radio.setPALevel(RF24_PA_LOW);          // defaults to RF24_PA_MAX
+  radio.enableAckPayload();               // We will be using the ACK Payload feature which is not enabled 
by default
+  radio.enableDynamicPayloads();          // Ack payloads are dynamic payloads
+
+  // Open a writing and reading pipe on each radio, with opposite addresses
+  if (role == role_sender) {
+    radio.openWritingPipe(address[0]);
+    radio.openReadingPipe(1, address[1]);
+  } else {
+    radio.openWritingPipe(address[1]);
+    radio.openReadingPipe(1, address[0]);
+    radio.startListening();               // First we need to start listening
+
+    // Add an ACK payload for the first time around; 1 is the pipe number to acknowledge
+    radio.writeAckPayload(1, &message_count, sizeof(message_count));
+    ++message_count;                      // increment counter by 1 for next ACK payload
+  }
+
+  radio.printDetails();                   // Dump the configuration of the rf unit for debugging
+  delay(50);
+
+  // Attach interrupt handler to interrupt #0 (using pin 2) on BOTH the sender and receiver
+  attachInterrupt(0, check_radio, LOW);
+} // setup
+
+
+void loop() {
+
+
+  /****************** Ping Out Role ***************************/
+
+  if (role == role_sender) {
+    // Repeatedly send the current time
+
+    unsigned long time = millis();                      // Take the time
+    Serial.print(F("Now sending "));
+    Serial.println(time);
+    radio.startWrite(&time, sizeof(unsigned long), 0);  // Send the time
+    delay(2000);                                        // Try again soon (in 2 seconds)
+  }
+
+
+  /****************** Pong Back Role ***************************/
+  // Receiver does nothing!  All the work is in Interrupt Handler
+
+  if (role == role_receiver) {}
+
+} // loop
+
+
+/********************** Interrupt Handler *********************/
+
+void check_radio(void) {
+
+  bool tx, fail, rx;                               // declare variables to store IRQ flags
+  radio.whatHappened(tx, fail, rx);                // What happened?
+
+  if (tx) {                                        // Have we successfully transmitted?
+    if (role == role_sender)
+      Serial.println(F("Send:OK"));
+    if (role == role_receiver)
+      Serial.println(F("Ack Payload:Sent"));
+  }
+
+  if (fail) {                                      // Have we failed to transmit?
+    if (role == role_sender)
+      Serial.println(F("Send:Failed"));
+    if (role == role_receiver)
+      Serial.println(F("Ack Payload:Failed"));
+  }
+
+  if (rx || radio.available()) {                   // Did we receive a message?
+
+
+
+
+    /**************** Ping Out Role (about received ACK payload) ************************/
+    // If we're the sender, we've received an ack payload
+    if (role == role_sender) {
+      // Get the payload and dump it
+      radio.read(&message_count, sizeof(message_count));
+      Serial.print(F("Ack: "));
+      Serial.println(message_count);
+    }
+
+
+    /****************** Pong Back Role ***************************/
+    // If we're the receiver, we've received a time message
+    if (role == role_receiver) {
+      // Get the payload and dump it
+
+      static unsigned long got_time;                                    // variable to hold the received time
+      radio.read(&got_time, sizeof(got_time));                          // get the payload
+      Serial.print(F("Got payload "));
+      Serial.println(got_time);
+
+      // Add an ACK payload for the next time around; 1 is the pipe number to acknowledge
+      radio.writeAckPayload(1, &message_count, sizeof(message_count));
+      ++message_count;                                                  // increment packet counter
+    }
+  }
+} // check_radio
diff --git a/arduino/libraries/RF24/examples/old_backups/pingpair_multi_dyn/pingpair_multi_dyn.ino 
b/arduino/libraries/RF24/examples/old_backups/pingpair_multi_dyn/pingpair_multi_dyn.ino
new file mode 100644
index 000000000..5f169328c
--- /dev/null
+++ b/arduino/libraries/RF24/examples/old_backups/pingpair_multi_dyn/pingpair_multi_dyn.ino
@@ -0,0 +1,263 @@
+/*
+  Copyright (C) 2011 James Coliz, Jr. <maniacbug ymail com>
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License
+  version 2 as published by the Free Software Foundation.
+*/
+
+/**
+   Example using Dynamic Payloads
+
+   This is an example of how to use payloads of a varying (dynamic) size.
+*/
+
+#include <SPI.h>
+#include "RF24.h"
+
+//
+// Hardware configuration
+//
+
+// Set up nRF24L01 radio on SPI bus plus pins 8 & 9
+RF24 radio(7, 8);
+
+// Use multicast?
+// sets the multicast behavior this unit in hardware.  Connect to GND to use unicast
+// Leave open (default) to use multicast.
+const int multicast_pin = 6;
+
+// sets the role of this unit in hardware.  Connect to GND to be the 'pong' receiver
+// Leave open to be the 'ping' transmitter
+const int role_pin = 5;
+bool multicast = true ;
+
+//
+// Topology
+//
+
+// Radio pipe addresses for the 2 nodes to communicate.
+const uint64_t pipes[2] = { 0xEEFAFDFDEELL, 0xEEFDFAF50DFLL };
+
+//
+// Role management
+//
+// Set up role.  This sketch uses the same software for all the nodes
+// in this system.  Doing so greatly simplifies testing.  The hardware itself specifies
+// which node it is.
+//
+// This is done through the role_pin
+//
+
+// The various roles supported by this sketch
+typedef enum { role_ping_out = 1, role_pong_back } role_e;
+
+// The debug-friendly names of those roles
+const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"};
+
+// The role of the current running sketch
+role_e role;
+
+//
+// Payload
+//
+
+const int min_payload_size = 1;
+const int max_payload_size = 32;
+const int payload_size_increments_by = 1;
+int next_payload_size = min_payload_size;
+
+char receive_payload[max_payload_size + 1]; // +1 to allow room for a terminating NULL char
+
+void setup(void)
+{
+  //
+  // Multicast
+  //
+  pinMode(multicast_pin, INPUT);
+  digitalWrite(multicast_pin, HIGH);
+  delay( 20 ) ;
+
+  // read multicast role, LOW for unicast
+  if ( digitalRead( multicast_pin ) )
+    multicast = true ;
+  else
+    multicast = false ;
+
+
+  //
+  // Role
+  //
+
+  // set up the role pin
+  pinMode(role_pin, INPUT);
+  digitalWrite(role_pin, HIGH);
+  delay( 20 ); // Just to get a solid reading on the role pin
+
+  // read the address pin, establish our role
+  if ( digitalRead(role_pin) )
+    role = role_ping_out;
+  else
+    role = role_pong_back;
+
+  //
+  // Print preamble
+  //
+
+  Serial.begin(115200);
+
+  Serial.println(F("RF24/examples/pingpair_multi_dyn/"));
+  Serial.print(F("ROLE: "));
+  Serial.println(role_friendly_name[role]);
+
+  Serial.print(F("MULTICAST: "));
+  Serial.println(multicast ? F("true (unreliable)") : F("false (reliable)"));
+
+  //
+  // Setup and configure rf radio
+  //
+
+  radio.begin();
+
+  // enable dynamic payloads
+  radio.enableDynamicPayloads();
+  radio.setCRCLength( RF24_CRC_16 ) ;
+
+  // optionally, increase the delay between retries & # of retries
+  radio.setRetries( 15, 5 ) ;
+  radio.setAutoAck( true ) ;
+  //radio.setPALevel( RF24_PA_LOW ) ;
+
+  //
+  // Open pipes to other nodes for communication
+  //
+
+  // This simple sketch opens two pipes for these two nodes to communicate
+  // back and forth.
+  // Open 'our' pipe for writing
+  // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading)
+
+  if ( role == role_ping_out )
+  {
+    radio.openWritingPipe(pipes[0]);
+    radio.openReadingPipe(1, pipes[1]);
+  }
+  else
+  {
+    radio.openWritingPipe(pipes[1]);
+    radio.openReadingPipe(1, pipes[0]);
+  }
+
+  //
+  // Start listening
+  //
+  radio.powerUp() ;
+  radio.startListening();
+
+  //
+  // Dump the configuration of the rf unit for debugging
+  //
+
+  radio.printDetails();
+}
+
+void loop(void)
+{
+  //
+  // Ping out role.  Repeatedly send the current time
+  //
+
+  if (role == role_ping_out)
+  {
+    // The payload will always be the same, what will change is how much of it we send.
+    static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012";
+
+    // First, stop listening so we can talk.
+    radio.stopListening();
+
+    // Take the time, and send it.  This will block until complete
+    Serial.print(F("Now sending length "));
+    Serial.println(next_payload_size);
+    radio.write( send_payload, next_payload_size, multicast );
+
+    // Now, continue listening
+    radio.startListening();
+
+    // Wait here until we get a response, or timeout
+    unsigned long started_waiting_at = millis();
+    bool timeout = false;
+    while ( ! radio.available() && ! timeout )
+      if (millis() - started_waiting_at > 500 )
+        timeout = true;
+
+    // Describe the results
+    if ( timeout )
+    {
+      Serial.println(F("Failed, response timed out."));
+    }
+    else
+    {
+      // Grab the response, compare, and send to debugging spew
+      uint8_t len = radio.getDynamicPayloadSize();
+      radio.read( receive_payload, len );
+
+      // Put a zero at the end for easy printing
+      receive_payload[len] = 0;
+
+      // Spew it
+      Serial.print(F("Got response size="));
+      Serial.print(len);
+      Serial.print(F(" value="));
+      Serial.println(receive_payload);
+    }
+
+    // Update size for next time.
+    next_payload_size += payload_size_increments_by;
+    if ( next_payload_size > max_payload_size )
+      next_payload_size = min_payload_size;
+
+    // Try again 1s later
+    delay(250);
+  }
+
+  //
+  // Pong back role.  Receive each packet, dump it out, and send it back
+  //
+
+  if ( role == role_pong_back )
+  {
+    // if there is data ready
+    if ( radio.available() )
+    {
+      // Dump the payloads until we've gotten everything
+      uint8_t len;
+      bool done = false;
+      while (radio.available())
+      {
+        // Fetch the payload, and see if this was the last one.
+        len = radio.getDynamicPayloadSize();
+        radio.read( receive_payload, len );
+
+        // Put a zero at the end for easy printing
+        receive_payload[len] = 0;
+
+        // Spew it
+        Serial.print(F("Got response size="));
+        Serial.print(len);
+        Serial.print(F(" value="));
+        Serial.println(receive_payload);
+      }
+
+      // First, stop listening so we can talk
+      radio.stopListening();
+
+      // Send the final one back.
+      radio.write( receive_payload, len, multicast );
+      Serial.println(F("Sent response."));
+
+      // Now, resume listening so we catch the next packets.
+      radio.startListening();
+    }
+  }
+}
+// vim:cin:ai:sts=2 sw=2 ft=cpp
diff --git a/arduino/libraries/RF24/examples/old_backups/pingpair_sleepy/pingpair_sleepy.ino 
b/arduino/libraries/RF24/examples/old_backups/pingpair_sleepy/pingpair_sleepy.ino
new file mode 100644
index 000000000..7f034b3c5
--- /dev/null
+++ b/arduino/libraries/RF24/examples/old_backups/pingpair_sleepy/pingpair_sleepy.ino
@@ -0,0 +1,226 @@
+/*
+  Copyright (C) 2011 J. Coliz <maniacbug ymail com>
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License
+  version 2 as published by the Free Software Foundation.
+
+  TMRh20 2014 - Updates to the library allow sleeping both in TX and RX modes:
+      TX Mode: The radio can be powered down (.9uA current) and the Arduino slept using the watchdog timer
+      RX Mode: The radio can be left in standby mode (22uA current) and the Arduino slept using an interrupt 
pin
+*/
+
+/**
+   Example RF Radio Ping Pair which Sleeps between Sends
+
+   This is an example of how to use the RF24 class to create a battery-
+   efficient system.  It is just like the GettingStarted_CallResponse example, but the
+   ping node powers down the radio and sleeps the MCU after every
+   ping/pong cycle, and the receiver sleeps between payloads.
+
+   Write this sketch to two different nodes,
+   connect the role_pin to ground on one.  The ping node sends the current
+   time to the pong node, which responds by sending the value back.  The ping
+   node can then see how long the whole cycle took.
+*/
+
+#include <SPI.h>
+#include <avr/sleep.h>
+#include <avr/power.h>
+#include "nRF24L01.h"
+#include "RF24.h"
+#include "printf.h"
+
+
+// Set up nRF24L01 radio on SPI bus plus pins 7 & 8
+RF24 radio(7, 8);
+
+// sets the role of this unit in hardware.  Connect to GND to be the 'pong' receiver
+// Leave open to be the 'ping' transmitter
+const int role_pin = 5;
+
+const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };   // Radio pipe addresses for the 2 nodes to 
communicate.
+
+// Role management
+// Set up role.  This sketch uses the same software for all the nodes
+// in this system.  Doing so greatly simplifies testing.  The hardware itself specifies
+// which node it is.
+
+// The various roles supported by this sketch
+typedef enum { role_ping_out = 1, role_pong_back } role_e;
+
+// The debug-friendly names of those roles
+const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"};
+
+// The role of the current running sketch
+role_e role;
+
+
+// Sleep declarations
+typedef enum { wdt_16ms = 0, wdt_32ms, wdt_64ms, wdt_128ms, wdt_250ms, wdt_500ms, wdt_1s, wdt_2s, wdt_4s, 
wdt_8s } wdt_prescalar_e;
+
+void setup_watchdog(uint8_t prescalar);
+void do_sleep(void);
+
+const short sleep_cycles_per_transmission = 4;
+volatile short sleep_cycles_remaining = sleep_cycles_per_transmission;
+
+
+
+void setup() {
+
+  // set up the role pin
+  pinMode(role_pin, INPUT);
+  digitalWrite(role_pin, HIGH);
+  delay(20); // Just to get a solid reading on the role pin
+
+  // read the address pin, establish our role
+  if ( digitalRead(role_pin) )
+    role = role_ping_out;
+  else
+    role = role_pong_back;
+
+  Serial.begin(115200);
+  printf_begin();
+  Serial.print(F("\n\rRF24/examples/pingpair_sleepy/\n\rROLE: "));
+  Serial.println(role_friendly_name[role]);
+
+  // Prepare sleep parameters
+  // Only the ping out role uses WDT.  Wake up every 4s to send a ping
+  //if ( role == role_ping_out )
+  setup_watchdog(wdt_4s);
+
+  // Setup and configure rf radio
+
+  radio.begin();
+
+  // Open pipes to other nodes for communication
+
+  // This simple sketch opens two pipes for these two nodes to communicate
+  // back and forth.
+  // Open 'our' pipe for writing
+  // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading)
+
+  if ( role == role_ping_out ) {
+    radio.openWritingPipe(pipes[0]);
+    radio.openReadingPipe(1, pipes[1]);
+  } else {
+    radio.openWritingPipe(pipes[1]);
+    radio.openReadingPipe(1, pipes[0]);
+  }
+
+  // Start listening
+  radio.startListening();
+
+  // Dump the configuration of the rf unit for debugging
+  //radio.printDetails();
+}
+
+void loop() {
+
+
+  if (role == role_ping_out)  {                     // Ping out role.  Repeatedly send the current time
+    radio.powerUp();                                // Power up the radio after sleeping
+    radio.stopListening();                          // First, stop listening so we can talk.
+
+    unsigned long time = millis();                  // Take the time, and send it.
+    Serial.print(F("Now sending... "));
+    Serial.println(time);
+
+    radio.write( &time, sizeof(unsigned long) );
+
+    radio.startListening();                         // Now, continue listening
+
+    unsigned long started_waiting_at = millis();    // Wait here until we get a response, or timeout (250ms)
+    bool timeout = false;
+    while ( ! radio.available()  ) {
+      if (millis() - started_waiting_at > 250 ) { // Break out of the while loop if nothing available
+        timeout = true;
+        break;
+      }
+    }
+
+    if ( timeout ) {                                // Describe the results
+      Serial.println(F("Failed, response timed out."));
+    } else {
+      unsigned long got_time;                     // Grab the response, compare, and send to debugging spew
+      radio.read( &got_time, sizeof(unsigned long) );
+
+      printf("Got response %lu, round-trip delay: %lu\n\r", got_time, millis() - got_time);
+    }
+
+    // Shut down the system
+    delay(500);                     // Experiment with some delay here to see if it has an effect
+    // Power down the radio.
+    radio.powerDown();              // NOTE: The radio MUST be powered back up again manually
+
+    // Sleep the MCU.
+    do_sleep();
+
+
+  }
+
+
+  // Pong back role.  Receive each packet, dump it out, and send it back
+  if ( role == role_pong_back ) {
+
+    if ( radio.available() ) {                                  // if there is data ready
+
+      unsigned long got_time;
+      while (radio.available()) {                             // Dump the payloads until we've gotten 
everything
+        radio.read( &got_time, sizeof(unsigned long) );       // Get the payload, and see if this was the 
last one.
+        // Spew it.  Include our time, because the ping_out millis counter is unreliable
+        printf("Got payload %lu @ %lu...", got_time, millis()); // due to it sleeping
+      }
+
+      radio.stopListening();                                  // First, stop listening so we can talk
+      radio.write( &got_time, sizeof(unsigned long) );        // Send the final one back.
+      Serial.println(F("Sent response."));
+      radio.startListening();                                 // Now, resume listening so we catch the next 
packets.
+    } else {
+      Serial.println(F("Sleeping"));
+      delay(50);                                             // Delay so the serial data can print out
+      do_sleep();
+
+    }
+  }
+}
+
+void wakeUp() {
+  sleep_disable();
+}
+
+// Sleep helpers
+
+//Prescaler values
+// 0=16ms, 1=32ms,2=64ms,3=125ms,4=250ms,5=500ms
+// 6=1 sec,7=2 sec, 8=4 sec, 9= 8sec
+
+void setup_watchdog(uint8_t prescalar) {
+
+  uint8_t wdtcsr = prescalar & 7;
+  if ( prescalar & 8 )
+    wdtcsr |= _BV(WDP3);
+  MCUSR &= ~_BV(WDRF);                      // Clear the WD System Reset Flag
+  WDTCSR = _BV(WDCE) | _BV(WDE);            // Write the WD Change enable bit to enable changing the 
prescaler and enable system reset
+  WDTCSR = _BV(WDCE) | wdtcsr | _BV(WDIE);  // Write the prescalar bits (how long to sleep, enable the 
interrupt to wake the MCU
+}
+
+ISR(WDT_vect)
+{
+  //--sleep_cycles_remaining;
+  Serial.println(F("WDT"));
+}
+
+void do_sleep(void)
+{
+  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // sleep mode is set here
+  sleep_enable();
+  attachInterrupt(0, wakeUp, LOW);
+  WDTCSR |= _BV(WDIE);
+  sleep_mode();                        // System sleeps here
+  // The WDT_vect interrupt wakes the MCU from here
+  sleep_disable();                     // System continues execution here when watchdog timed out
+  detachInterrupt(0);
+  WDTCSR &= ~_BV(WDIE);
+}
diff --git a/arduino/libraries/RF24/examples/old_backups/recipes/led_remote/led_remote.ino 
b/arduino/libraries/RF24/examples/old_backups/recipes/led_remote/led_remote.ino
new file mode 100644
index 000000000..f1394f619
--- /dev/null
+++ b/arduino/libraries/RF24/examples/old_backups/recipes/led_remote/led_remote.ino
@@ -0,0 +1,254 @@
+/*
+  Copyright (C) 2011 J. Coliz <maniacbug ymail com>
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License
+  version 2 as published by the Free Software Foundation.
+*/
+
+/**
+   Example LED Remote
+
+   This is an example of how to use the RF24 class to control a remote
+   bank of LED's using buttons on a remote control.
+
+   On the 'remote', connect any number of buttons or switches from
+   an arduino pin to ground.  Update 'button_pins' to reflect the
+   pins used.
+
+   On the 'led' board, connect the same number of LED's from an
+   arduino pin to a resistor to ground.  Update 'led_pins' to reflect
+   the pins used.  Also connect a separate pin to ground and change
+   the 'role_pin'.  This tells the sketch it's running on the LED board.
+
+   Every time the buttons change on the remote, the entire state of
+   buttons is send to the led board, which displays the state.
+*/
+
+#include <SPI.h>
+#include "nRF24L01.h"
+#include "RF24.h"
+#include "printf.h"
+
+//
+// Hardware configuration
+//
+
+// Set up nRF24L01 radio on SPI bus plus pins 9 & 10 (CE & CS)
+
+RF24 radio(9, 10);
+
+// sets the role of this unit in hardware.  Connect to GND to be the 'led' board receiver
+// Leave open to be the 'remote' transmitter
+const int role_pin = A4;
+
+// Pins on the remote for buttons
+const uint8_t button_pins[] = { 2, 3, 4, 5, 6, 7 };
+const uint8_t num_button_pins = sizeof(button_pins);
+
+// Pins on the LED board for LED's
+const uint8_t led_pins[] = { 2, 3, 4, 5, 6, 7 };
+const uint8_t num_led_pins = sizeof(led_pins);
+
+//
+// Topology
+//
+
+// Single radio pipe address for the 2 nodes to communicate.
+const uint64_t pipe = 0xE8E8F0F0E1LL;
+
+//
+// Role management
+//
+// Set up role.  This sketch uses the same software for all the nodes in this
+// system.  Doing so greatly simplifies testing.  The hardware itself specifies
+// which node it is.
+//
+// This is done through the role_pin
+//
+
+// The various roles supported by this sketch
+typedef enum { role_remote = 1, role_led } role_e;
+
+// The debug-friendly names of those roles
+const char* role_friendly_name[] = { "invalid", "Remote", "LED Board"};
+
+// The role of the current running sketch
+role_e role;
+
+//
+// Payload
+//
+
+uint8_t button_states[num_button_pins];
+uint8_t led_states[num_led_pins];
+
+//
+// Setup
+//
+
+void setup(void)
+{
+  //
+  // Role
+  //
+
+  // set up the role pin
+  pinMode(role_pin, INPUT);
+  digitalWrite(role_pin, HIGH);
+  delay(20); // Just to get a solid reading on the role pin
+
+  // read the address pin, establish our role
+  if ( digitalRead(role_pin) )
+    role = role_remote;
+  else
+    role = role_led;
+
+  //
+  // Print preamble
+  //
+
+  Serial.begin(115200);
+  printf_begin();
+  printf("\n\rRF24/examples/led_remote/\n\r");
+  printf("ROLE: %s\n\r", role_friendly_name[role]);
+
+  //
+  // Setup and configure rf radio
+  //
+
+  radio.begin();
+
+  //
+  // Open pipes to other nodes for communication
+  //
+
+  // This simple sketch opens a single pipes for these two nodes to communicate
+  // back and forth.  One listens on it, the other talks to it.
+
+  if ( role == role_remote )
+  {
+    radio.openWritingPipe(pipe);
+  }
+  else
+  {
+    radio.openReadingPipe(1, pipe);
+  }
+
+  //
+  // Start listening
+  //
+
+  if ( role == role_led )
+    radio.startListening();
+
+  //
+  // Dump the configuration of the rf unit for debugging
+  //
+
+  radio.printDetails();
+
+  //
+  // Set up buttons / LED's
+  //
+
+  // Set pull-up resistors for all buttons
+  if ( role == role_remote )
+  {
+    int i = num_button_pins;
+    while (i--)
+    {
+      pinMode(button_pins[i], INPUT);
+      digitalWrite(button_pins[i], HIGH);
+    }
+  }
+
+  // Turn LED's ON until we start getting keys
+  if ( role == role_led )
+  {
+    int i = num_led_pins;
+    while (i--)
+    {
+      pinMode(led_pins[i], OUTPUT);
+      led_states[i] = HIGH;
+      digitalWrite(led_pins[i], led_states[i]);
+    }
+  }
+
+}
+
+//
+// Loop
+//
+
+void loop(void)
+{
+  //
+  // Remote role.  If the state of any button has changed, send the whole state of
+  // all buttons.
+  //
+
+  if ( role == role_remote )
+  {
+    // Get the current state of buttons, and
+    // Test if the current state is different from the last state we sent
+    int i = num_button_pins;
+    bool different = false;
+    while (i--)
+    {
+      uint8_t state = ! digitalRead(button_pins[i]);
+      if ( state != button_states[i] )
+      {
+        different = true;
+        button_states[i] = state;
+      }
+    }
+
+    // Send the state of the buttons to the LED board
+    if ( different )
+    {
+      printf("Now sending...");
+      bool ok = radio.write( button_states, num_button_pins );
+      if (ok)
+        printf("ok\n\r");
+      else
+        printf("failed\n\r");
+    }
+
+    // Try again in a short while
+    delay(20);
+  }
+
+  //
+  // LED role.  Receive the state of all buttons, and reflect that in the LEDs
+  //
+
+  if ( role == role_led )
+  {
+    // if there is data ready
+    if ( radio.available() )
+    {
+      // Dump the payloads until we've gotten everything
+      while (radio.available())
+      {
+        // Fetch the payload, and see if this was the last one.
+        radio.read( button_states, num_button_pins );
+
+        // Spew it
+        printf("Got buttons\n\r");
+
+        // For each button, if the button now on, then toggle the LED
+        int i = num_led_pins;
+        while (i--)
+        {
+          if ( button_states[i] )
+          {
+            led_states[i] ^= HIGH;
+            digitalWrite(led_pins[i], led_states[i]);
+          }
+        }
+      }
+    }
+  }
+}
+// vim:ai:cin:sts=2 sw=2 ft=cpp
diff --git a/arduino/libraries/RF24/examples/old_backups/recipes/nordic_fob/nordic_fob.ino 
b/arduino/libraries/RF24/examples/old_backups/recipes/nordic_fob/nordic_fob.ino
new file mode 100644
index 000000000..cf40cbce7
--- /dev/null
+++ b/arduino/libraries/RF24/examples/old_backups/recipes/nordic_fob/nordic_fob.ino
@@ -0,0 +1,142 @@
+/*
+  Copyright (C) 2012 J. Coliz <maniacbug ymail com>
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License
+  version 2 as published by the Free Software Foundation.
+*/
+
+/**
+   Example Nordic FOB Receiver
+
+   This is an example of how to use the RF24 class to receive signals from the
+   Sparkfun Nordic FOB.  Thanks to Kirk Mower for providing test hardware.
+
+   See blog post at http://maniacbug.wordpress.com/2012/01/08/nordic-fob/
+*/
+
+#include <SPI.h>
+#include <RF24.h>
+#include "nRF24L01.h"
+#include "printf.h"
+
+//
+// Hardware configuration
+//
+
+// Set up nRF24L01 radio on SPI bus plus pins 9 & 10
+
+RF24 radio(9, 10);
+
+//
+// Payload
+//
+
+struct payload_t
+{
+  uint8_t buttons;
+  uint16_t id;
+  uint8_t empty;
+};
+
+const char* button_names[] = { "Up", "Down", "Left", "Right", "Center" };
+const int num_buttons = 5;
+
+//
+// Forward declarations
+//
+
+uint16_t flip_endian(uint16_t in);
+
+//
+// Setup
+//
+
+void setup(void)
+{
+  //
+  // Print preamble
+  //
+
+  Serial.begin(115200);
+  printf_begin();
+  printf("\r\nRF24/examples/nordic_fob/\r\n");
+
+  //
+  // Setup and configure rf radio according to the built-in parameters
+  // of the FOB.
+  //
+
+  radio.begin();
+  radio.setChannel(2);
+  radio.setPayloadSize(4);
+  radio.setAutoAck(false);
+  radio.setCRCLength(RF24_CRC_8);
+  radio.openReadingPipe(1, 0xE7E7E7E7E7LL);
+
+  //
+  // Start listening
+  //
+
+  radio.startListening();
+
+  //
+  // Dump the configuration of the rf unit for debugging
+  //
+
+  radio.printDetails();
+}
+
+//
+// Loop
+//
+
+void loop(void)
+{
+  //
+  // Receive each packet, dump it out
+  //
+
+  // if there is data ready
+  if ( radio.available() )
+  {
+    // Get the packet from the radio
+    payload_t payload;
+    radio.read( &payload, sizeof(payload) );
+
+    // Print the ID of this message.  Note that the message
+    // is sent 'big-endian', so we have to flip it.
+    printf("#%05u Buttons ", flip_endian(payload.id));
+
+    // Print the name of each button
+    int i = num_buttons;
+    while (i--)
+    {
+      if ( ! ( payload.buttons & _BV(i) ) )
+      {
+        printf("%s ", button_names[i]);
+      }
+    }
+
+    // If no buttons, print None
+    if ( payload.buttons == _BV(num_buttons) - 1 )
+      printf("None");
+
+    printf("\r\n");
+  }
+}
+
+//
+// Helper functions
+//
+
+// Change a big-endian word into a little-endian
+uint16_t flip_endian(uint16_t in)
+{
+  uint16_t low = in >> 8;
+  uint16_t high = in << 8;
+
+  return high | low;
+}
+
+// vim:cin:ai:sts=2 sw=2 ft=cpp
diff --git a/arduino/libraries/RF24/examples/old_backups/recipes/pingpair_maple/main.cpp 
b/arduino/libraries/RF24/examples/old_backups/recipes/pingpair_maple/main.cpp
new file mode 100644
index 000000000..a6f505a4e
--- /dev/null
+++ b/arduino/libraries/RF24/examples/old_backups/recipes/pingpair_maple/main.cpp
@@ -0,0 +1,87 @@
+#ifdef MAPLE_IDE
+
+#include <stdio.h>
+#include "wirish.h"
+
+extern void setup(void);
+extern void loop(void);
+
+void board_start(const char* program_name)
+{
+  // Set up the LED to steady on
+  pinMode(BOARD_LED_PIN, OUTPUT);
+  digitalWrite(BOARD_LED_PIN, HIGH);
+
+  // Setup the button as input
+  pinMode(BOARD_BUTTON_PIN, INPUT);
+  digitalWrite(BOARD_BUTTON_PIN, HIGH);
+
+  SerialUSB.begin();
+  SerialUSB.println("Press BUT");
+
+  // Wait for button press
+  while ( !isButtonPressed() )
+  {
+  }
+
+  SerialUSB.println("Welcome!");
+  SerialUSB.println(program_name);
+
+  int i = 11;
+  while (i--)
+  {
+    toggleLED();
+    delay(50);
+  }
+}
+
+/**
+   Custom version of _write, which will print to the USB.
+   In order to use it you MUST ADD __attribute__((weak))
+   to _write in libmaple/syscalls.c
+*/
+extern "C" int _write (int file, char * ptr, int len)
+{
+  if ( (file != 1) && (file != 2) )
+    return 0;
+  else
+    SerialUSB.write(ptr, len);
+  return len;
+}
+
+/**
+   Re-entrant version of _write.  Yagarto and Devkit now use
+   the re-entrant newlib, so these get called instead of the
+   non_r versions.
+*/
+extern "C" int _write_r (void*, int file, char * ptr, int len)
+{
+  return _write( file, ptr, len);
+}
+
+__attribute__((constructor)) __attribute__ ((weak)) void premain()
+{
+  init();
+}
+
+__attribute__((weak)) void setup(void)
+{
+  board_start("No program defined");
+}
+
+__attribute__((weak)) void loop(void)
+{
+}
+
+__attribute__((weak)) int main(void)
+{
+  setup();
+
+  while (true)
+  {
+    loop();
+  }
+  return 0;
+}
+#endif // ifdef MAPLE_IDE
+// vim:cin:ai:sts=2 sw=2 ft=cpp
diff --git a/arduino/libraries/RF24/examples/old_backups/recipes/pingpair_maple/pingpair_maple.ino 
b/arduino/libraries/RF24/examples/old_backups/recipes/pingpair_maple/pingpair_maple.ino
new file mode 100644
index 000000000..83d99c8ac
--- /dev/null
+++ b/arduino/libraries/RF24/examples/old_backups/recipes/pingpair_maple/pingpair_maple.ino
@@ -0,0 +1,242 @@
+/*
+  Copyright (C) 2011 J. Coliz <maniacbug ymail com>
+
+  This program is free software; you can redistribute it and/or
+  modify it under the terms of the GNU General Public License
+  version 2 as published by the Free Software Foundation.
+*/
+
+/**
+   Example RF Radio Ping Pair ... for Maple
+
+   This is an example of how to use the RF24 class.  Write this sketch to two different nodes,
+   connect the role_pin to ground on one.  The ping node sends the current time to the pong node,
+   which responds by sending the value back.  The ping node can then see how long the whole cycle
+   took.
+*/
+
+#include "WProgram.h"
+#include <SPI.h>
+#include "nRF24L01.h"
+#include "RF24.h"
+
+//
+// Maple specific setup.  Other than this section, the sketch is the same on Maple as on
+// Arduino
+//
+
+#ifdef MAPLE_IDE
+
+// External startup function
+extern void board_start(const char* program_name);
+
+// Use SPI #2.
+HardwareSPI SPI(2);
+
+#else
+#define board_startup printf
+#define toggleLED(x) (x)
+#endif
+
+//
+// Hardware configuration
+//
+
+// Set up nRF24L01 radio on SPI bus plus pins 7 & 6
+// (This works for the Getting Started board plugged into the
+// Maple Native backwards.)
+
+RF24 radio(7, 6);
+
+// sets the role of this unit in hardware.  Connect to GND to be the 'pong' receiver
+// Leave open to be the 'ping' transmitter
+const int role_pin = 10;
+
+//
+// Topology
+//
+
+// Radio pipe addresses for the 2 nodes to communicate.
+const uint64_t pipes[2] = { 0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL };
+
+//
+// Role management
+//
+// Set up role.  This sketch uses the same software for all the nodes
+// in this system.  Doing so greatly simplifies testing.  The hardware itself specifies
+// which node it is.
+//
+// This is done through the role_pin
+//
+
+// The various roles supported by this sketch
+typedef enum { role_ping_out = 1, role_pong_back } role_e;
+
+// The debug-friendly names of those roles
+const char* role_friendly_name[] = { "invalid", "Ping out", "Pong back"};
+
+// The role of the current running sketch
+role_e role;
+
+void setup(void)
+{
+  //
+  // Role
+  //
+
+  // set up the role pin
+  pinMode(role_pin, INPUT);
+  digitalWrite(role_pin, HIGH);
+  delay(20); // Just to get a solid reading on the role pin
+
+  // read the address pin, establish our role
+  if ( digitalRead(role_pin) )
+    role = role_ping_out;
+  else
+    role = role_pong_back;
+
+  //
+  // Print preamble
+  //
+
+  board_start("\n\rRF24/examples/pingpair/\n\r");
+  printf("ROLE: %s\n\r", role_friendly_name[role]);
+
+  //
+  // Setup and configure rf radio
+  //
+
+  radio.begin();
+
+  // optionally, increase the delay between retries & # of retries
+  radio.setRetries(15, 15);
+
+  // optionally, reduce the payload size.  seems to
+  // improve reliability
+  radio.setPayloadSize(8);
+
+  //
+  // Open pipes to other nodes for communication
+  //
+
+  // This simple sketch opens two pipes for these two nodes to communicate
+  // back and forth.
+  // Open 'our' pipe for writing
+  // Open the 'other' pipe for reading, in position #1 (we can have up to 5 pipes open for reading)
+
+  if ( role == role_ping_out )
+  {
+    radio.openWritingPipe(pipes[0]);
+    radio.openReadingPipe(1, pipes[1]);
+  }
+  else
+  {
+    radio.openWritingPipe(pipes[1]);
+    radio.openReadingPipe(1, pipes[0]);
+  }
+
+  //
+  // Start listening
+  //
+
+  radio.startListening();
+
+  //
+  // Dump the configuration of the rf unit for debugging
+  //
+
+  radio.printDetails();
+}
+
+void loop(void)
+{
+  //
+  // Ping out role.  Repeatedly send the current time
+  //
+
+  if (role == role_ping_out)
+  {
+    toggleLED();
+
+    // First, stop listening so we can talk.
+    radio.stopListening();
+
+    // Take the time, and send it.  This will block until complete
+    unsigned long time = millis();
+    printf("Now sending %lu...", time);
+    bool ok = radio.write( &time, sizeof(unsigned long) );
+
+    if (ok)
+      printf("ok...\r\n");
+    else
+      printf("failed.\r\n");
+
+    // Now, continue listening
+    radio.startListening();
+
+    // Wait here until we get a response, or timeout (250ms)
+    unsigned long started_waiting_at = millis();
+    bool timeout = false;
+    while ( ! radio.available() && ! timeout )
+      if (millis() - started_waiting_at > 200 )
+        timeout = true;
+
+    // Describe the results
+    if ( timeout )
+    {
+      printf("Failed, response timed out.\r\n");
+    }
+    else
+    {
+      // Grab the response, compare, and send to debugging spew
+      unsigned long got_time;
+      radio.read( &got_time, sizeof(unsigned long) );
+
+      // Spew it
+      printf("Got response %lu, round-trip delay: %lu\r\n", got_time, millis() - got_time);
+    }
+
+    toggleLED();
+
+    // Try again 1s later
+    delay(1000);
+  }
+
+  //
+  // Pong back role.  Receive each packet, dump it out, and send it back
+  //
+
+  if ( role == role_pong_back )
+  {
+    // if there is data ready
+    if ( radio.available() )
+    {
+      // Dump the payloads until we've gotten everything
+      unsigned long got_time;
+      bool done = false;
+      while (!done)
+      {
+        // Fetch the payload, and see if this was the last one.
+        done = radio.read( &got_time, sizeof(unsigned long) );
+
+        // Spew it
+        printf("Got payload %lu...", got_time);
+
+        // Delay just a little bit to let the other unit
+        // make the transition to receiver
+        delay(20);
+      }
+
+      // First, stop listening so we can talk
+      radio.stopListening();
+
+      // Send the final one back.
+      radio.write( &got_time, sizeof(unsigned long) );
+      printf("Sent response.\r\n");
+
+      // Now, resume listening so we catch the next packets.
+      radio.startListening();
+    }
+  }
+}
+// vim:cin:ai:sts=2 sw=2 ft=cpp
diff --git a/arduino/libraries/RF24/examples/old_backups/recipes/readme.md 
b/arduino/libraries/RF24/examples/old_backups/recipes/readme.md
new file mode 100644
index 000000000..c22d7a24c
--- /dev/null
+++ b/arduino/libraries/RF24/examples/old_backups/recipes/readme.md
@@ -0,0 +1,2 @@
+Note: These recipe examples may have not been maintained with library updates, and are provided as-is for 
reference purposes.
+Warning: These are recipe examples are intended for specific hardware usage.
\ No newline at end of file
diff --git a/arduino/libraries/RF24/examples/rf24_ATTiny/rf24ping85/rf24ping85.ino 
b/arduino/libraries/RF24/examples/rf24_ATTiny/rf24ping85/rf24ping85.ino
new file mode 100644
index 000000000..49bec97e5
--- /dev/null
+++ b/arduino/libraries/RF24/examples/rf24_ATTiny/rf24ping85/rf24ping85.ino
@@ -0,0 +1,204 @@
+/**
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * written in 2014 by tong67 (https://github.com/tong67)
+ * Updated 2020 by 2bndy5 (http://github.com/2bndy5) for the
+ * SpenceKonde ATTinyCore (https://github.com/SpenceKonde/ATTinyCore)
+ */
+
+/**
+ * The RF24 library uses the [ATTinyCore by
+ * SpenceKonde](https://github.com/SpenceKonde/ATTinyCore)
+ *
+ * This sketch is a duplicate of the ManualAcknowledgements.ino example
+ * (without all the Serial input/output code), and it demonstrates
+ * a ATTiny25/45/85 or ATTiny24/44/84 driving the nRF24L01 transceiver using
+ * the RF24 class to communicate with another node.
+ *
+ * A simple example of sending data from 1 nRF24L01 transceiver to another
+ * with manually transmitted (non-automatic) Acknowledgement (ACK) payloads.
+ * This example still uses ACK packets, but they have no payloads. Instead the
+ * acknowledging response is sent with `write()`. This tactic allows for more
+ * updated acknowledgement payload data, where actual ACK payloads' data are
+ * outdated by 1 transmission because they have to loaded before receiving a
+ * transmission.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ */
+
+/*
+ * ********** Hardware configuration (& schematics) *******************
+ *
+ * When direct use of 3V does not work (UNO boards tend to have poor 3V supply),
+ * use 5V with LED (1.8V ~ 2.2V drop) instead.
+ * For low power consumption solutions floating pins (SCK and MOSI) should be
+ * pulled HIGH or LOW with 10K resistors.
+ *
+ * ATTiny25/45/85 Pin map with CE_PIN 3 and CSN_PIN 4
+ *                                                              ^^
+ *                         +-\/-+                              //
+ *                   PB5  1|o   |8  Vcc --- nRF24L01  VCC --- |<|--- 5V
+ *  nRF24L01  CE --- PB3  2|    |7  PB2 --- nRF24L01  SCK     LED
+ *  nRF24L01 CSN --- PB4  3|    |6  PB1 --- nRF24L01 MOSI
+ *  nRF24L01 GND --- GND  4|    |5  PB0 --- nRF24L01 MISO
+ *                         +----+
+ *
+ * ATTiny25/45/85 Pin map with CE_PIN 3 and CSN_PIN 3 => PB3 and PB4 are
+ * free to use for other purposes. This "3 pin solution" is from
+ * Ralph Doncaster (AKA NerdRalph) which is outlined on his blog at
+ * http://nerdralph.blogspot.ca/2014/01/nrf24l01-control-with-3-attiny85-pins.html
+ * Original RC combination was 1K/100nF. 22K/10nF combination worked better.
+ *
+ * For best settle time delay value to use for RF24::csDelay in RF24::csn(), use
+ * the examples/rf24_ATTiny/timingSearch3pin/timingSearch3pin.ino sketch.
+ *
+ * This configuration is enabled in the RF24 library when CE_PIN and
+ * CSN_PIN parameters to the constructor are equal. Notice (in the schematic
+ * below) that these pins aren't directly to the ATTiny85. Because the CE pin
+ * is always HIGH, the power consumption is higher than it would be for the
+ * typical 5 pins solution.
+ *                                                                              ^^
+ *                         +-\/-+           nRF24L01 CE --------|              //
+ *                   PB5  1|o   |8  Vcc --- nRF24L01 VCC -------x----------x--|<|-- 5V
+ *                   PB3  2|    |7  PB2 --- nRF24L01 SCK ---|<|---x-[22k]--|  LED
+ *                   PB4  3|    |6  PB1 --- nRF24L01 MOSI  1n4148 |
+ *  nRF24L01 GND -x- GND  4|    |5  PB0 --- nRF24L01 MISO         |
+ *                |        +----+                                 |
+ *                |-----------------------------------------||----x-- nRF24L01 CSN
+ *                                                         10nF
+ *
+ * ATTiny24/44/84 Pin map with CE_PIN 8 and CSN_PIN 7 & assuming 1.9V to 3V on VCC
+ * Schematic provided and successfully tested by
+ * Carmine Pastore (https://github.com/Carminepz)
+ *
+ *                          +-\/-+
+ *  nRF24L01 VCC ---- VCC  1|o   |14 GND --- nRF24L01 GND
+ *                    PB0  2|    |13 AREF
+ *                    PB1  3|    |12 PA1
+ *                    PB3  4|    |11 PA2 --- nRF24L01 CE
+ *                    PB2  5|    |10 PA3 --- nRF24L01 CSN
+ *                    PA7  6|    |9  PA4 --- nRF24L01 SCK
+ *  nRF24L01 MOSI --- PA6  7|    |8  PA5 --- nRF24L01 MISO
+ *                          +----+
+ */
+
+#include "SPI.h"
+#include "RF24.h"
+
+// CE and CSN are configurable, specified values for ATTiny85 as connected above
+#define CE_PIN 3
+#define CSN_PIN 4
+//#define CSN_PIN 3 // uncomment for ATTiny85 3 pins solution
+
+// instantiate an object for the nRF24L01 transceiver
+RF24 radio(CE_PIN, CSN_PIN);
+
+// Let these addresses be used for the pair
+uint8_t address[][6] = {"1Node", "2Node"};
+// It is very helpful to think of an address as a path instead of as
+// an identifying device destination
+
+// to use different addresses on a pair of radios, we need a variable to
+// uniquely identify which address this radio will use to transmit
+bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+// Used to control whether this node is sending or receiving
+bool role = false;  // true = TX node, false = RX node
+
+// For this example, we'll be using a payload containing
+// a string & an integer number that will be incremented
+// on every successful transmission.
+// Make a data structure to store the entire payload of different datatypes
+struct PayloadStruct {
+  char message[7];          // only using 6 characters for TX & RX payloads
+  uint8_t counter;
+};
+PayloadStruct payload;
+
+void setup() {
+
+  // append a NULL terminating character for printing as a c-string
+  payload.message[6] = 0;
+
+  // initialize the transceiver on the SPI bus
+  if (!radio.begin()) {
+    while (1) {} // hold in infinite loop
+  }
+
+  // Set the PA Level low to try preventing power supply related problems
+  // because these examples are likely run with nodes in close proximity to
+  // each other.
+  radio.setPALevel(RF24_PA_LOW); // RF24_PA_MAX is default.
+
+  // save on transmission time by setting the radio to only transmit the
+  // number of bytes we need to transmit a float
+  radio.setPayloadSize(sizeof(payload)); // char[7] & uint8_t datatypes occupy 8 bytes
+
+  // set the TX address of the RX node into the TX pipe
+  radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0
+
+  // set the RX address of the TX node into a RX pipe
+  radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+  if (role) {
+    // setup the TX node
+
+    memcpy(payload.message, "Hello ", 6); // set the outgoing message
+    radio.stopListening();                // put radio in TX mode
+  } else {
+    // setup the RX node
+
+    memcpy(payload.message, "World ", 6); // set the outgoing message
+    radio.startListening();               // put radio in RX mode
+  }
+} // setup()
+
+void loop() {
+
+  if (role) {
+    // This device is a TX node
+
+    bool report = radio.write(&payload, sizeof(payload)); // transmit & save the report
+
+    if (report) {
+      // transmission successful; wait for response and print results
+
+      radio.startListening();                    // put in RX mode
+      unsigned long start_timeout = millis();    // timer to detect no response
+      while (!radio.available()) { // wait for response or timeout
+        if (millis() - start_timeout > 200)      // only wait 200 ms
+          break;
+      }
+      radio.stopListening();                     // put back in TX mode
+
+      // print summary of transactions
+      if (radio.available()) {                   // is there a payload received?
+
+        PayloadStruct received;
+        radio.read(&received, sizeof(received)); // get payload from RX FIFO
+        payload.counter = received.counter;      // save incoming counter for next outgoing counter
+      }
+    } // report
+
+    // to make this example readable in the serial monitor
+    delay(1000);  // slow transmissions down by 1 second
+
+  } else {
+    // This device is a RX node
+
+    if (radio.available()) {                      // is there a payload?
+
+      PayloadStruct received;
+      radio.read(&received, sizeof(received));    // get incoming payload
+      payload.counter = received.counter + 1;     // increment incoming counter for next outgoing response
+
+      // transmit response & save result to `report`
+      radio.stopListening();                      // put in TX mode
+
+      radio.writeFast(&payload, sizeof(payload)); // load response to TX FIFO
+      radio.txStandBy(150);                       // keep retrying for 150 ms
+
+      radio.startListening();                     // put back in RX mode
+    }
+  } // role
+} // loop
\ No newline at end of file
diff --git a/arduino/libraries/RF24/examples/rf24_ATTiny/timingSearch3pin/timingSearch3pin.ino 
b/arduino/libraries/RF24/examples/rf24_ATTiny/timingSearch3pin/timingSearch3pin.ino
new file mode 100644
index 000000000..ea1cd6ac3
--- /dev/null
+++ b/arduino/libraries/RF24/examples/rf24_ATTiny/timingSearch3pin/timingSearch3pin.ino
@@ -0,0 +1,209 @@
+/**
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * written by tong67 (https://github.com/tong67)
+ * edited by 2bndy5 (http://github.com/2bndy5) for compatibility with SpenceKonde's ATTinyCore
+ */
+
+/*
+ * This sketch can determine the best settle time values to use for
+ * macros, defined as RF24_CSN_SETTLE_HIGH_DELAY and RF24_CSN_SETTLE_LOW_DELAY,
+ * in RF24::csn().
+ * The settle time values used here are 100/20. However, these values depend
+ * on the actual used RC combiniation and voltage drop by LED. The
+ * intermediate results are written to TX (PB3, pin 2 -- using Serial).
+ *
+ * For schematic details, see introductory comment block in the
+ * examples/rf24_ATTiny/rf24ping85/rf24ping85.ino sketch.
+ */
+
+#include <stdio.h>
+#include <SPI.h>
+#include <Arduino.h>
+#include <nRF24L01.h>
+
+
+#if defined (ARDUINO) && !defined (__arm__)
+#if defined(__AVR_ATtinyX5__) || defined(__AVR_ATtinyX4__)
+#define RF24_TINY
+#endif
+#endif
+
+/****************************************************************************/
+
+#if defined(RF24_TINY)
+
+// when Attiny84 or Attiny85 is detected
+#define CE_PIN   3 /** "Chip Enable" pin, activates the RX or TX role */
+#define CSN_PIN  3 /** SPI Chip Select Not */
+
+#else
+// when not running on an ATTiny84 or ATTiny85
+#define CE_PIN   7 /** "Chip Enable" pin, activates the RX or TX role */
+#define CSN_PIN  8 /** SPI Chip Select Not */
+
+#endif
+
+#define MAX_HIGH       100
+#define MAX_LOW                100
+#define MINIMAL                8
+
+// Use these adjustable variables to test for best configuration to be used on
+// the ATTiny chips. These variables are defined as macros in the library's
+// RF24/utility/ATTiny/RF24_arch_config.h file. To change them, simply define
+// the corresponding macro(s) before #include <RF24> in your sketch.
+uint8_t csnHighSettle = MAX_HIGH; // defined as RF24_CSN_SETTLE_HIGH_DELAY
+uint8_t csnLowSettle = MAX_LOW;   // defined as RF24_CSN_SETTLE_LOW_DELAY
+
+/****************************************************************************/
+void ce(bool level) {
+  if (CE_PIN != CSN_PIN) digitalWrite(CE_PIN, level);
+}
+
+/****************************************************************************/
+void csn(bool mode) {
+  if (CE_PIN != CSN_PIN) {
+    digitalWrite(CSN_PIN, mode);
+  } else {
+    // digitalWrite(SCK, mode);
+    if (mode == HIGH) {
+      PORTB |= (1 << PINB2); // SCK->CSN HIGH
+      delayMicroseconds(csnHighSettle); // allow csn to settle
+    } else {
+      PORTB &= ~(1 << PINB2); // SCK->CSN LOW
+      delayMicroseconds(csnLowSettle);  // allow csn to settle
+    }
+  }
+}
+
+/****************************************************************************/
+uint8_t read_register(uint8_t reg)
+{
+  csn(LOW);
+  SPI.transfer(R_REGISTER | reg);
+  uint8_t result = SPI.transfer(0xff);
+  csn(HIGH);
+  return result;
+}
+
+/****************************************************************************/
+void write_register(uint8_t reg, uint8_t value)
+{
+  csn(LOW);
+  SPI.transfer(W_REGISTER | reg);
+  SPI.transfer(value);
+  csn(HIGH);
+}
+
+/****************************************************************************/
+void setup(void) {
+
+#ifndef __AVR_ATtinyX313__
+  // not enough memory on ATTiny4313 or ATTint2313(a) to use Serial I/O for this sketch
+
+  // start serial port and SPI
+  Serial.begin(115200);
+  SPI.begin();
+  // configure CE and CSN as output when used
+  pinMode(CE_PIN, OUTPUT);
+  if (CSN_PIN != CE_PIN)
+    pinMode(CSN_PIN, OUTPUT);
+
+  // csn is used in SPI transfers. Set to LOW at start and HIGH after transfer. Set to HIGH to reflect no 
transfer active
+  // SPI command are accepted in Power Down state.
+  // CE pin represent PRX (LOW) or PTX (HIGH) mode apart from register settings. Start in PRX mode.
+  ce(LOW);
+  csn(HIGH);
+
+  // nRF24L01 goes from to Power Down state 100ms after Power on Reset ( Vdd > 1.9V) or when PWR_UP is 0 in 
config register
+  // Goto Power Down state (Powerup or force) and set in transmit mode
+  write_register(NRF_CONFIG, read_register(NRF_CONFIG) & ~_BV(PWR_UP) & ~_BV(PRIM_RX));
+  delay(100);
+
+  // Goto Standby-I
+  // Technically we require 4.5ms Tpd2stby+ 14us as a worst case. We'll just call it 5ms for good measure.
+  // WARNING: Delay is based on P-variant whereby non-P *may* require different timing.
+  write_register(NRF_CONFIG, read_register(NRF_CONFIG) | _BV(PWR_UP));
+  delay(5) ;
+
+  // Goto Standby-II
+  ce(HIGH);
+  Serial.print("Scanning for optimal setting time for csn");
+
+
+  /************************** Main program *********************************/
+
+  uint8_t result; // used to compare read/write results with read/write cmds
+  bool success = true;
+  uint8_t bottom_success;
+  bool bottom_found;
+  uint8_t value[] = {5, 10};
+  uint8_t limit[] = {MAX_HIGH, MAX_LOW};
+  uint8_t advice[] = {MAX_HIGH, MAX_LOW};
+
+  // check max values give correct behavior
+  for (uint8_t k = 0; k < 2; k++) {
+    bottom_found = false;
+    bottom_success = 0;
+    while (bottom_success < 255) {
+      csnHighSettle = limit[0];
+      csnLowSettle = limit[1];
+      // check current values
+      uint8_t i = 0;
+      while (i < 255 && success) {
+        for (uint8_t j = 0; j < 2; j++) {
+          write_register(EN_AA, value[j]);
+          result = read_register(EN_AA);
+          if (value[j] != result) {
+            success = false;
+          }
+        }
+        i++;
+      }
+      // process result of current values
+      if (!success) {
+        Serial.print("Settle Not OK. csnHigh=");
+        Serial.print(limit[0], DEC);
+        Serial.print(" csnLow=");
+        Serial.println(limit[1], DEC);
+        limit[k]++;
+        bottom_found = true;
+        bottom_success = 0;
+        success = true;
+      } else {
+        Serial.print("Settle OK. csnHigh=");
+        Serial.print(limit[0], DEC);
+        Serial.print(" csnLow=");
+        Serial.println(limit[1], DEC);
+        if (!bottom_found) {
+          limit[k]--;
+          if (limit[k] == MINIMAL) {
+            bottom_found = true;
+            bottom_success = 0;
+            success = true;
+          }
+        } else {
+          bottom_success++;
+        }
+      }
+    } // while (bottom_success < 255)
+    Serial.print("Settle value found for ");
+    if (k == 0) {
+      Serial.print("csnHigh: ");
+    } else {
+      Serial.print("csnLow: ");
+    }
+    Serial.println(limit[k], DEC);
+    advice[k] = limit[k] + (limit[k] / 10);
+    limit[k] = 100;
+  } // for (uint8_t k = 0; k < 2; k++)
+  Serial.print("Advised Settle times are: csnHigh=");
+  Serial.print(advice[0], DEC);
+  Serial.print(" csnLow=");
+  Serial.println(advice[1], DEC);
+
+#endif // not defined __AVR_ATtinyX313__
+}
+
+
+void loop(void) {} // this program runs only once, thus it resides in setup()
diff --git a/arduino/libraries/RF24/examples/scanner/scanner.ino 
b/arduino/libraries/RF24/examples/scanner/scanner.ino
new file mode 100644
index 000000000..0473193d0
--- /dev/null
+++ b/arduino/libraries/RF24/examples/scanner/scanner.ino
@@ -0,0 +1,157 @@
+/*
+ * Copyright (C) 2011 J. Coliz <maniacbug ymail com>
+ * Updated 2020 TMRh20
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+/**
+ * Channel scanner and Continuous Carrier Wave Output
+ *
+ * Example to detect interference on the various channels available.
+ * This is a good diagnostic tool to check whether you're picking a
+ * good channel for your application.
+ *
+ * Run this sketch on two devices. On one device, start CCW output by sending a 'g'
+ * character over Serial. The other device scanning should detect the output of the sending
+ * device on the given channel. Adjust channel and output power of CCW below.
+ *
+ * Inspired by cpixip.
+ * See http://arduino.cc/forum/index.php/topic,54795.0.html
+ */
+
+#include "RF24.h"
+#include "printf.h"
+
+//
+// Hardware configuration
+//
+
+// Set up nRF24L01 radio on SPI bus plus pins 7 & 8
+
+RF24 radio(7, 8);
+
+//
+// Channel info
+//
+
+const uint8_t num_channels = 126;
+uint8_t values[num_channels];
+
+//
+// Setup
+//
+
+void setup(void)
+{
+  //
+  // Print preamble
+  //
+
+  Serial.begin(115200);
+  printf_begin();
+  Serial.println(F("\n\rRF24/examples/scanner/"));
+
+  //
+  // Setup and configure rf radio
+  //
+
+  radio.begin();
+  radio.setAutoAck(false);
+
+  // Get into standby mode
+  radio.startListening();
+  radio.stopListening();
+  radio.printDetails();
+
+  //delay(1000);
+  // Print out header, high then low digit
+  int i = 0;
+  while ( i < num_channels )
+  {
+    Serial.print(i >> 4, HEX);
+    ++i;
+  }
+  Serial.println();
+  i = 0;
+  while ( i < num_channels )
+  {
+    Serial.print(i & 0xf, HEX);
+    ++i;
+  }
+  Serial.println();
+  //delay(1000);
+}
+
+//
+// Loop
+//
+
+const int num_reps = 100;
+bool constCarrierMode = 0;
+
+void loop(void)
+{
+  /****************************************/
+  // Send g over Serial to begin CCW output
+  // Configure the channel and power level below
+  if (Serial.available()) {
+    char c = Serial.read();
+    if (c == 'g') {
+      constCarrierMode = 1;
+      radio.stopListening();
+      delay(2);
+      Serial.println("Starting Carrier Out");
+      radio.startConstCarrier(RF24_PA_LOW, 40);
+    } else if (c == 'e') {
+      constCarrierMode = 0;
+      radio.stopConstCarrier();
+      Serial.println("Stopping Carrier Out");
+    }
+  }
+  /****************************************/
+
+  if (constCarrierMode == 0) {
+    // Clear measurement values
+    memset(values, 0, sizeof(values));
+
+    // Scan all channels num_reps times
+    int rep_counter = num_reps;
+    while (rep_counter--)
+    {
+      int i = num_channels;
+      while (i--)
+      {
+        // Select this channel
+        radio.setChannel(i);
+
+        // Listen for a little
+        radio.startListening();
+        delayMicroseconds(128);
+        radio.stopListening();
+
+        // Did we get a carrier?
+        if ( radio.testCarrier() ) {
+          ++values[i];
+        }
+      }
+    }
+
+
+    // Print out channel measurements, clamped to a single hex digit
+    int i = 0;
+    while ( i < num_channels )
+    {
+      if (values[i])
+        Serial.print(min(0xf, values[i]), HEX);
+      else
+        Serial.print(F("-"));
+
+      ++i;
+    }
+    Serial.println();
+
+  }//If constCarrierMode == 0
+}
diff --git a/arduino/libraries/RF24/examples_linux/CMakeLists.txt 
b/arduino/libraries/RF24/examples_linux/CMakeLists.txt
new file mode 100644
index 000000000..c27646a7a
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/CMakeLists.txt
@@ -0,0 +1,46 @@
+cmake_minimum_required(VERSION 3.12)
+
+# iterate over a list of examples by filename
+set(EXAMPLES_LIST
+    gettingstarted
+    acknowledgementPayloads
+    manualAcknowledgements
+    streamingData
+    multiceiverDemo
+    scanner
+)
+
+project(RF24LogExamples)
+add_compile_options(-Ofast -Wall) # passing the compiler a `-pthread` flag doesn't work here
+
+# detect the CPU make and type
+include(../cmake/detectCPU.cmake) # sets the variable SOC accordingly
+
+# auto-detect what driver to use
+# auto-detect can be overriden using `cmake .. -D RF24_DRIVER=<supported driver>`
+include(../cmake/AutoConfig_RF24_DRIVER.cmake)
+
+find_library(RF24 rf24 REQUIRED)
+message(STATUS "using RF24 library: ${RF24}")
+
+# conditionally append "interruptConfigure" to the EXAMPLES_LIST
+if("${RF24_DRIVER}" STREQUAL "MRAA" OR "${RF24_DRIVER}" STREQUAL "wiringPi")
+    message(STATUS "Skipping interruptConfigure.cpp example as it is incompatible with selected driver 
library")
+else() # not using MRAA or wiringPi drivers
+    list(APPEND EXAMPLES_LIST interruptConfigure)
+endif()
+
+foreach(example ${EXAMPLES_LIST})
+    #make a target
+    add_executable(${example} ${example}.cpp)
+
+    # link the RF24 lib to the target. Notice we specify pthread as a linked lib here
+    if("${RF24_DRIVER}" STREQUAL "MRAA")
+        target_link_libraries(${example} PUBLIC ${RF24} pthread ${LibMRAA})
+    elseif("${RF24_DRIVER}" STREQUAL "wiringPi")
+        # wiringPi additionally needs to link to crypt and shm_open libraries
+        target_link_libraries(${example} PUBLIC ${RF24} pthread ${LibWiringPi} crypt rt)
+    else() # not using MRAA or wiringPi drivers
+        target_link_libraries(${example} PUBLIC ${RF24} pthread)
+    endif()
+endforeach()
diff --git a/arduino/libraries/RF24/examples_linux/Makefile.examples 
b/arduino/libraries/RF24/examples_linux/Makefile.examples
new file mode 100644
index 000000000..a03483e10
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/Makefile.examples
@@ -0,0 +1,52 @@
+#############################################################################
+#
+# Makefile for librf24 examples on Linux
+#
+# License: GPL (General Public License)
+# Author:  gnulnulf <arco appeltaart mine nu>
+# Date:    2013/02/07 (version 1.0)
+#
+# Description:
+# ------------
+# use make all and make install to install the examples
+#
+
+BINARY_PREFIX = rf24
+SOURCES = $(PROGRAMS:=.cpp)
+
+LIBS=-l$(LIB)
+ifeq ($(DRIVER), LittleWire)
+       LIBS+= -llittlewire-spi
+endif
+
+all: $(PROGRAMS)
+
+$(PROGRAMS): $(SOURCES)
+       $(CXX) $(CFLAGS) -I$(HEADER_DIR)/.. -I.. -L$(LIB_DIR) $@.cpp $(LIBS) -o $@
+
+clean:
+       @echo "[Cleaning]"
+       rm -rf $(PROGRAMS)
+
+install: all
+       @echo "[Installing examples to $(EXAMPLES_DIR)]"
+       @mkdir -p $(EXAMPLES_DIR)
+       @for prog in $(PROGRAMS); do \
+               install -m 0755 $${prog} $(EXAMPLES_DIR)/$(BINARY_PREFIX)-$${prog}; \
+       done
+
+upload: all
+       @echo "[Uploading examples to $(REMOTE):$(REMOTE_EXAMPLES_DIR)]"
+ifeq ($(REMOTE),)
+       @echo "[ERROR] Remote machine not configured. Run configure with respective arguments."
+       @exit 1
+endif
+       @ssh -q -t -p $(REMOTE_PORT) $(REMOTE) "mkdir -p $(REMOTE_EXAMPLES_DIR)"
+       @ssh -q -t -p $(REMOTE_PORT) $(REMOTE) "mkdir -p /tmp/RF24_examples"
+       @scp -q -P $(REMOTE_PORT) $(PROGRAMS) $(REMOTE):/tmp/RF24_examples
+       @for prog in $(PROGRAMS); do \
+               ssh -q -t -p $(REMOTE_PORT) $(REMOTE) "sudo install -m 0755 /tmp/RF24_examples/$${prog} 
$(REMOTE_EXAMPLES_DIR)/$(BINARY_PREFIX)-$${prog}"; \
+       done
+       @ssh -q -t -p $(REMOTE_PORT) $(REMOTE) "rm -rf /tmp/RF24_examples"
+
+.PHONY: install upload
diff --git a/arduino/libraries/RF24/examples_linux/acknowledgementPayloads.cpp 
b/arduino/libraries/RF24/examples_linux/acknowledgementPayloads.cpp
new file mode 100644
index 000000000..d72831cb2
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/acknowledgementPayloads.cpp
@@ -0,0 +1,230 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty (2bndy5)
+ */
+
+/**
+ * A simple example of sending data from 1 nRF24L01 transceiver to another
+ * with Acknowledgement (ACK) payloads attached to ACK packets.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use `ctrl+c` to quit at any time.
+ */
+#include <ctime>       // time()
+#include <iostream>    // cin, cout, endl
+#include <string>      // string, getline()
+#include <time.h>      // CLOCK_MONOTONIC_RAW, timespec, clock_gettime()
+#include <RF24/RF24.h> // RF24, RF24_PA_LOW, delay()
+
+using namespace std;
+
+/****************** Linux ***********************/
+// Radio CE Pin, CSN Pin, SPI Speed
+// CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use their own pin numbering
+// CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
+// ie: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
+
+// Generic:
+RF24 radio(22, 0);
+/****************** Linux (BBB,x86,etc) ***********************/
+// See http://nRF24.github.io/RF24/pages.html for more information on usage
+// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
+// See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV
+
+// For this example, we'll be using a payload containing
+// a string & an integer number that will be incremented
+// on every successful transmission.
+// Make a data structure to store the entire payload of different datatypes
+struct PayloadStruct {
+  char message[7]; // only using 6 characters for TX & ACK payloads
+  uint8_t counter;
+};
+PayloadStruct payload;
+
+void setRole(); // prototype to set the node's role
+void master();  // prototype of the TX node's behavior
+void slave();   // prototype of the RX node's behavior
+
+// custom defined timer for evaluating transmission time in microseconds
+struct timespec startTimer, endTimer;
+uint32_t getMicros(); // prototype to get ellapsed time in microseconds
+
+
+int main(int argc, char** argv) {
+    // perform hardware check
+    if (!radio.begin()) {
+        cout << "radio hardware is not responding!!" << endl;
+        return 0; // quit now
+    }
+
+    // Let these addresses be used for the pair
+    uint8_t address[2][6] = {"1Node", "2Node"};
+    // It is very helpful to think of an address as a path instead of as
+    // an identifying device destination
+
+    // to use different addresses on a pair of radios, we need a variable to
+    // uniquely identify which address this radio will use to transmit
+    bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+    // print example's name
+    cout << argv[0] << endl;
+
+    // Set the radioNumber via the terminal on startup
+    cout << "Which radio is this? Enter '0' or '1'. Defaults to '0' ";
+    string input;
+    getline(cin, input);
+    radioNumber = input.length() > 0 && (uint8_t)input[0] == 49;
+
+    // to use ACK payloads, we need to enable dynamic payload lengths
+    radio.enableDynamicPayloads();    // ACK payloads are dynamically sized
+
+    // Acknowledgement packets have no payloads by default. We need to enable
+    // this feature for all nodes (TX & RX) to use ACK payloads.
+    radio.enableAckPayload();
+
+    // Set the PA Level low to try preventing power supply related problems
+    // because these examples are likely run with nodes in close proximity to
+    // each other.
+    radio.setPALevel(RF24_PA_LOW);  // RF24_PA_MAX is default.
+
+    // set the TX address of the RX node into the TX pipe
+    radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0
+
+    // set the RX address of the TX node into a RX pipe
+    radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+    // For debugging info
+    // radio.printDetails();       // (smaller) function that prints raw register values
+    // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+    // ready to execute program now
+    setRole(); // calls master() or slave() based on user input
+    return 0;
+}
+
+
+/**
+ * set this node's role from stdin stream.
+ * this only considers the first char as input.
+ */
+void setRole() {
+    string input = "";
+    while (!input.length()) {
+        cout << "*** PRESS 'T' to begin transmitting to the other node\n";
+        cout << "*** PRESS 'R' to begin receiving from the other node\n";
+        cout << "*** PRESS 'Q' to exit" << endl;
+        getline(cin, input);
+        if (input.length() >= 1) {
+            if (input[0] == 'T' || input[0] == 't')
+                master();
+            else if (input[0] == 'R' || input[0] == 'r')
+                slave();
+            else if (input[0] == 'Q' || input[0] == 'q')
+                break;
+            else
+                cout << input[0] << " is an invalid input. Please try again." << endl;
+        }
+        input = ""; // stay in the while loop
+    } // while
+} // setRole()
+
+
+/**
+ * make this node act as the transmitter
+ */
+void master() {
+    memcpy(payload.message, "Hello ", 6);                     // set the payload message
+    radio.stopListening();                                    // put radio in TX mode
+
+    unsigned int failures = 0;                                // keep track of failures
+    while (failures < 6) {
+        clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);      // start the timer
+        bool report = radio.write(&payload, sizeof(payload)); // transmit & save the report
+        uint32_t timerEllapsed = getMicros();                 // end the timer
+
+        if (report) {
+            // payload was delivered
+            cout << "Transmission successful! Time to transmit = ";
+            cout << timerEllapsed;                                  // print the timer result
+            cout << " us. Sent: ";
+            cout << payload.message;                                // print outgoing message
+            cout << (unsigned int)payload.counter;                  // print outgoing counter counter
+
+            uint8_t pipe;
+            if (radio.available(&pipe)) {
+                PayloadStruct received;
+                radio.read(&received, sizeof(received));             // get incoming ACK payload
+                cout << " Received ";
+                cout << (unsigned int)radio.getDynamicPayloadSize(); // print incoming payload size
+                cout << " bytes on pipe " << (unsigned int)pipe;     // print pipe that received it
+                cout << ": " << received.message;                    // print incoming message
+                cout << (unsigned int)received.counter << endl;      // print incoming counter
+                payload.counter = received.counter + 1;              // save incoming counter & increment 
for next outgoing
+            } // if got an ACK payload
+            else {
+                cout << " Received an empty ACK packet." << endl; // ACK had no payload
+            }
+        } // if delivered
+        else {
+            cout << "Transmission failed or timed out" << endl;   // payload was not delivered
+            failures++;                                           // increment failures
+        }
+
+        // to make this example readable in the terminal
+        delay(1000);  // slow transmissions down by 1 second
+    } // while
+    cout << failures << " failures detected. Leaving TX role." << endl;
+} // master
+
+
+/**
+ * make this node act as the receiver
+ */
+void slave() {
+    memcpy(payload.message, "World ", 6);                    // set the payload message
+
+    // load the payload for the first received transmission on pipe 0
+    radio.writeAckPayload(1, &payload, sizeof(payload));
+
+    radio.startListening();                                  // put radio in RX mode
+    time_t startTimer = time(nullptr);                       // start a timer
+    while (time(nullptr) - startTimer < 6) {                 // use 6 second timeout
+        uint8_t pipe;
+        if (radio.available(&pipe)) {                        // is there a payload? get the pipe number that 
recieved it
+            uint8_t bytes = radio.getDynamicPayloadSize();   // get the size of the payload
+            PayloadStruct received;
+            radio.read(&received, sizeof(received));         // fetch payload from RX FIFO
+            cout << "Received " << (unsigned int)bytes;      // print the size of the payload
+            cout << " bytes on pipe " << (unsigned int)pipe; // print the pipe number
+            cout << ": " << received.message;
+            cout << (unsigned int)received.counter;          // print received payload
+            cout << " Sent: ";
+            cout << payload.message;
+            cout << (unsigned int)payload.counter << endl;   // print ACK payload sent
+            startTimer = time(nullptr);                      // reset timer
+
+            // save incoming counter & increment for next outgoing
+            payload.counter = received.counter + 1;
+            // load the payload for the first received transmission on pipe 0
+            radio.writeAckPayload(1, &payload, sizeof(payload));
+        } // if received something
+    } // while
+    cout << "Nothing received in 6 seconds. Leaving RX role." << endl;
+    radio.stopListening(); // recommended idle behavior is TX mode
+} // slave
+
+
+/**
+ * Calculate the ellapsed time in microseconds
+ */
+uint32_t getMicros() {
+    // this function assumes that the timer was started using
+    // `clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);`
+
+    clock_gettime(CLOCK_MONOTONIC_RAW, &endTimer);
+    uint32_t seconds = endTimer.tv_sec - startTimer.tv_sec;
+    uint32_t useconds = (endTimer.tv_nsec - startTimer.tv_nsec) / 1000;
+
+    return ((seconds) * 1000 + useconds) + 0.5;
+}
diff --git a/arduino/libraries/RF24/examples_linux/acknowledgement_payloads.py 
b/arduino/libraries/RF24/examples_linux/acknowledgement_payloads.py
new file mode 100644
index 000000000..c326a6580
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/acknowledgement_payloads.py
@@ -0,0 +1,234 @@
+"""
+A simple example of sending data from 1 nRF24L01 transceiver to another
+with Acknowledgement (ACK) payloads attached to ACK packets.
+
+This example was written to be used on 2 devices acting as 'nodes'.
+"""
+import sys
+import argparse
+import time
+from RF24 import RF24, RF24_PA_LOW
+
+
+parser = argparse.ArgumentParser(
+    description=__doc__,
+    formatter_class=argparse.RawDescriptionHelpFormatter
+)
+parser.add_argument(
+    "-n",
+    "--node",
+    type=int,
+    choices=range(2),
+    help="the identifying radio number (or node ID number)"
+)
+parser.add_argument(
+    "-r",
+    "--role",
+    type=int,
+    choices=range(2),
+    help="'1' specifies the TX role. '0' specifies the RX role."
+)
+
+########### USER CONFIGURATION ###########
+# See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md
+# Radio CE Pin, CSN Pin, SPI Speed
+# CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use
+# their own pin numbering
+# CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
+# ie: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
+
+# Generic:
+radio = RF24(22, 0)
+################## Linux (BBB,x86,etc) #########################
+# See http://nRF24.github.io/RF24/pages.html for more information on usage
+# See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
+# See https://www.kernel.org/doc/Documentation/spi/spidev for more
+# information on SPIDEV
+
+# using the python keyword global is bad practice. Instead we'll use a
+# 1 item list to store our integer number for the payloads' counter
+counter = [0]
+
+def master():
+    """Transmits a message and an incrementing integer every second."""
+    radio.stopListening()  # put radio in TX mode
+    failures = 0
+    while failures < 6:
+        # construct a payload to send
+        buffer = b"Hello \x00" + bytes(counter)
+
+        # send the payload and prompt
+        start_timer = time.monotonic_ns()  # start timer
+        result = radio.write(buffer)  # save the report
+        end_timer = time.monotonic_ns()  # stop timer
+        if result:
+            # print timer results upon transmission success
+            print(
+                "Transmission successful! Time to transmit: "
+                "{} us. Sent: {}{}".format(
+                    int((end_timer - start_timer) / 1000),
+                    buffer[:6].decode("utf-8"),
+                    counter[0]
+                ),
+                end=" "
+            )
+            has_payload, pipe_number = radio.available_pipe()
+            if has_payload:
+                # print the received ACK that was automatically sent
+                length = radio.getDynamicPayloadSize()
+                response = radio.read(length)
+                print(
+                    "Received {} on pipe {}: {}{}".format(
+                        length,
+                        pipe_number,
+                        bytes(response[:6]).decode("utf-8"),
+                        response[7:8][0]
+                    )
+                )
+                # increment counter from received payload
+                if response[7:8][0] < 255:
+                    counter[0] = response[7:8][0] + 1
+                else:
+                    counter[0] = 0
+            else:
+                print("Received an empty ACK packet")
+        else:
+            failures += 1
+            print("Transmission failed or timed out")
+        time.sleep(1)  # let the RX node prepare a new ACK payload
+    print(failures, "failures detected. Leaving TX role.")
+
+
+def slave(timeout=6):
+    """Listen for any payloads and print the transaction
+
+    :param int timeout: The number of seconds to wait (with no transmission)
+        until exiting function.
+    """
+    radio.startListening()  # put radio in RX mode
+
+    # setup the first transmission's ACK payload
+    buffer = b"World \x00" + bytes(counter)
+    # we must set the ACK payload data and corresponding
+    # pipe number [0,5]
+    radio.writeAckPayload(1, buffer)  # load ACK for first response
+
+    start_timer = time.monotonic()  # start timer
+    while (time.monotonic() - start_timer) < timeout:
+        has_payload, pipe_number = radio.available_pipe()
+        if has_payload:
+            length = radio.getDynamicPayloadSize()  # grab the payload length
+            received = radio.read(length)  # fetch 1 payload from RX FIFO
+            # increment counter from received payload
+            counter[0] = received[7:8][0] + 1 if received[7:8][0] < 255 else 0
+            print(
+                "Received {} bytes on pipe {}: {}{} Sent: {}{}".format(
+                    length,
+                    pipe_number,
+                    bytes(received[:6]).decode("utf-8"),
+                    received[7:8][0],
+                    buffer[:6].decode("utf-8"),
+                    buffer[7:8][0]
+                )
+            )
+            buffer = b"World \x00" + bytes(counter)  # build a new ACK payload
+            radio.writeAckPayload(1, buffer)  # load ACK for next response
+            start_timer = time.monotonic()  # reset timer
+
+    print("Nothing received in", timeout, "seconds. Leaving RX role")
+    # recommended behavior is to keep in TX mode while idle
+    radio.stopListening()  # put radio in TX mode & flush unused ACK payloads
+
+
+def set_role():
+    """Set the role using stdin stream. Timeout arg for slave() can be
+    specified using a space delimiter (e.g. 'R 10' calls `slave(10)`)
+
+    :return:
+        - True when role is complete & app should continue running.
+        - False when app should exit
+    """
+    user_input = input(
+        "*** Enter 'R' for receiver role.\n"
+        "*** Enter 'T' for transmitter role.\n"
+        "*** Enter 'Q' to quit example.\n"
+    ) or "?"
+    user_input = user_input.split()
+    if user_input[0].upper().startswith("R"):
+        if len(user_input) > 1:
+            slave(int(user_input[1]))
+        else:
+            slave()
+        return True
+    elif user_input[0].upper().startswith("T"):
+        master()
+        return True
+    elif user_input[0].upper().startswith("Q"):
+        radio.powerDown()
+        return False
+    print(user_input[0], "is an unrecognized input. Please try again.")
+    return set_role()
+
+
+if __name__ == "__main__":
+
+    args = parser.parse_args()  # parse any CLI args
+
+    # initialize the nRF24L01 on the spi bus
+    if not radio.begin():
+        raise RuntimeError("radio hardware is not responding")
+
+    # For this example, we will use different addresses
+    # An address need to be a buffer protocol object (bytearray)
+    address = [b"1Node", b"2Node"]
+    # It is very helpful to think of an address as a path instead of as
+    # an identifying device destination
+
+    print(sys.argv[0])  # print example name
+
+    # to use different addresses on a pair of radios, we need a variable to
+    # uniquely identify which address this radio will use to transmit
+    # 0 uses address[0] to transmit, 1 uses address[1] to transmit
+    radio_number = args.node  # uses default value from `parser`
+    if args.node is None:  # if '--node' arg wasn't specified
+        radio_number = bool(
+            int(
+                input(
+                    "Which radio is this? Enter '0' or '1'. Defaults to '0' "
+                ) or 0
+            )
+        )
+
+    # ACK payloads are dynamically sized.
+    radio.enableDynamicPayloads()  # to use ACK payloads
+
+    # to enable the custom ACK payload feature
+    radio.enableAckPayload()
+
+    # set the Power Amplifier level to -12 dBm since this test example is
+    # usually run with nRF24L01 transceivers in close proximity of each other
+    radio.setPALevel(RF24_PA_LOW)  # RF24_PA_MAX is default
+
+    # set the TX address of the RX node into the TX pipe
+    radio.openWritingPipe(address[radio_number])  # always uses pipe 0
+
+    # set the RX address of the TX node into a RX pipe
+    radio.openReadingPipe(1, address[not radio_number])  # using pipe 1
+
+    # for debugging, we have 2 options that print a large block of details
+    # (smaller) function that prints raw register values
+    # radio.printDetails()
+    # (larger) function that prints human readable data
+    # radio.printPrettyDetails()
+
+    try:
+        if args.role is None:  # if not specified with CLI arg '-r'
+            while set_role():
+                pass  # continue example until 'Q' is entered
+        else:  # if role was set using CLI args
+            # run role once and exit
+            master() if bool(args.role) else slave()
+    except KeyboardInterrupt:
+        print(" Keyboard Interrupt detected. Exiting...")
+        radio.powerDown()
+        sys.exit()
diff --git a/arduino/libraries/RF24/examples_linux/extra/rpi-hub.cpp 
b/arduino/libraries/RF24/examples_linux/extra/rpi-hub.cpp
new file mode 100644
index 000000000..bac485785
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/extra/rpi-hub.cpp
@@ -0,0 +1,125 @@
+/* 
+ *
+ *  Filename : rpi-hub.cpp
+ *
+ *  This program makes the RPi as a hub listening to all six pipes from the remote sensor nodes ( usually 
Arduino )
+ *  and will return the packet back to the sensor on pipe0 so that the sender can calculate the round trip 
delays
+ *  when the payload matches.
+ *  
+ *  I encounter that at times, it also receive from pipe7 ( or pipe0 ) with content of FFFFFFFFF that I will 
not sent
+ *  back to the sender
+ *
+ *  Refer to RF24/examples/rpi_hub_arduino/ for the corresponding Arduino sketches to work with this code.
+ * 
+ *  
+ *  CE is not used and CSN is GPIO25 (not pinout)
+ *
+ *  Refer to RPi docs for GPIO numbers
+ *
+ *  Author : Stanley Seow
+ *  e-mail : stanleyseow gmail com
+ *  date   : 6th Mar 2013
+ *
+ * 03/17/2013 : Charles-Henri Hallard (http://hallard.me)
+ *              Modified to use with Arduipi board http://hallard.me/arduipi
+ *                                               Changed to use modified bcm2835 and RF24 library 
+ *
+ *
+ */
+
+#include <cstdlib>
+#include <iostream>
+#include <RF24/RF24.h>
+
+using namespace std;
+
+// Radio pipe addresses for the 2 nodes to communicate.
+// First pipe is for writing, 2nd, 3rd, 4th, 5th & 6th is for reading...
+const uint64_t pipes[6] = {0xF0F0F0F0D2LL, 0xF0F0F0F0E1LL, 0xF0F0F0F0E2LL, 0xF0F0F0F0E3LL, 0xF0F0F0F0F1, 
0xF0F0F0F0F2};
+
+// CE Pin, CSN Pin, SPI Speed
+
+// Setup for GPIO 22 CE and GPIO 25 CSN with SPI Speed @ 1Mhz
+//RF24 radio(RPI_V2_GPIO_P1_22, RPI_V2_GPIO_P1_18, BCM2835_SPI_SPEED_1MHZ);
+
+// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz
+//RF24 radio(RPI_V2_GPIO_P1_15, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ); 
+
+// Setup for GPIO 22 CE and CE1 CSN with SPI Speed @ 8Mhz
+RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ);
+
+int main(int argc, char** argv)
+{
+    uint8_t len;
+
+    // Refer to RF24.h or nRF24L01 DS for settings
+    radio.begin();
+    radio.enableDynamicPayloads();
+    radio.setAutoAck(1);
+    radio.setRetries(15, 15);
+    radio.setDataRate(RF24_1MBPS);
+    radio.setPALevel(RF24_PA_MAX);
+    radio.setChannel(76);
+    radio.setCRCLength(RF24_CRC_16);
+
+    // Open 6 pipes for readings ( 5 plus pipe0, also can be used for reading )
+    radio.openWritingPipe(pipes[0]);
+    radio.openReadingPipe(1, pipes[1]);
+    radio.openReadingPipe(2, pipes[2]);
+    radio.openReadingPipe(3, pipes[3]);
+    radio.openReadingPipe(4, pipes[4]);
+    radio.openReadingPipe(5, pipes[5]);
+
+    //
+    // Start listening
+    //
+    radio.startListening();
+
+    //
+    // Dump the configuration of the rf unit for debugging
+    //
+    radio.printDetails();
+
+    printf("Output below : \n");
+    delay(1);
+
+    while (1) {
+        char receivePayload[32];
+        uint8_t pipe = 1;
+
+        // Start listening
+        radio.startListening();
+
+        while (radio.available(&pipe)) {
+            len = radio.getDynamicPayloadSize();
+            radio.read(receivePayload, len);
+
+            // Display it on screen
+            printf("Recv: size=%i payload=%s pipe=%i", len, receivePayload, pipe);
+
+            // Send back payload to sender
+            radio.stopListening();
+
+            // if pipe is 7, do not send it back
+            if (pipe != 7) {
+                radio.write(receivePayload, len);
+                receivePayload[len] = 0;
+                printf("\t Send: size=%i payload=%s pipe:%i\n", len, receivePayload, pipe);
+            } else {
+                printf("\n");
+            }
+
+            pipe++;
+
+            // reset pipe to 0
+            if (pipe > 6) {
+                pipe = 0;
+            }
+        }
+
+        delayMicroseconds(20);
+    }
+
+    return 0;
+}
+
diff --git a/arduino/libraries/RF24/examples_linux/getting_started.py 
b/arduino/libraries/RF24/examples_linux/getting_started.py
new file mode 100644
index 000000000..9f5d4968b
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/getting_started.py
@@ -0,0 +1,203 @@
+"""
+A simple example of sending data from 1 nRF24L01 transceiver to another.
+This example was written to be used on 2 devices acting as 'nodes'.
+"""
+import sys
+import argparse
+import time
+import struct
+from RF24 import RF24, RF24_PA_LOW
+
+
+parser = argparse.ArgumentParser(
+    description=__doc__,
+    formatter_class=argparse.RawDescriptionHelpFormatter
+)
+parser.add_argument(
+    "-n",
+    "--node",
+    type=int,
+    choices=range(2),
+    help="the identifying radio number (or node ID number)"
+)
+parser.add_argument(
+    "-r",
+    "--role",
+    type=int,
+    choices=range(2),
+    help="'1' specifies the TX role. '0' specifies the RX role."
+)
+
+
+########### USER CONFIGURATION ###########
+# See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md
+# Radio CE Pin, CSN Pin, SPI Speed
+# CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use
+# their own pin numbering
+# CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
+# ie: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
+
+# Generic:
+radio = RF24(22, 0)
+################## Linux (BBB,x86,etc) #########################
+# See http://nRF24.github.io/RF24/pages.html for more information on usage
+# See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
+# See https://www.kernel.org/doc/Documentation/spi/spidev for more
+# information on SPIDEV
+
+# using the python keyword global is bad practice. Instead we'll use a 1 item
+# list to store our float number for the payloads sent/received
+payload = [0.0]
+
+
+def master():
+    """Transmits an incrementing float every second"""
+    radio.stopListening()  # put radio in TX mode
+    failures = 0
+    while failures < 6:
+        # use struct.pack() to packet your data into the payload
+        # "<f" means a single little endian (4 byte) float value.
+        buffer = struct.pack("<f", payload[0])
+        start_timer = time.monotonic_ns()  # start timer
+        result = radio.write(buffer)
+        end_timer = time.monotonic_ns()  # end timer
+        if not result:
+            print("Transmission failed or timed out")
+            failures += 1
+        else:
+            print(
+                "Transmission successful! Time to Transmit: "
+                "{} us. Sent: {}".format(
+                    (end_timer - start_timer) / 1000,
+                    payload[0]
+                )
+            )
+            payload[0] += 0.01
+        time.sleep(1)
+    print(failures, "failures detected. Leaving TX role.")
+
+
+def slave(timeout=6):
+    """Listen for any payloads and print the transaction
+
+    :param int timeout: The number of seconds to wait (with no transmission)
+        until exiting function.
+    """
+    radio.startListening()  # put radio in RX mode
+
+    start_timer = time.monotonic()
+    while (time.monotonic() - start_timer) < timeout:
+        has_payload, pipe_number = radio.available_pipe()
+        if has_payload:
+            # fetch 1 payload from RX FIFO
+            buffer = radio.read(radio.payloadSize)
+            # use struct.unpack() to convert the buffer into usable data
+            # expecting a little endian float, thus the format string "<f"
+            # buffer[:4] truncates padded 0s in case payloadSize was not set
+            payload[0] = struct.unpack("<f", buffer[:4])[0]
+            # print details about the received packet
+            print(
+                "Received {} bytes on pipe {}: {}".format(
+                    radio.payloadSize,
+                    pipe_number,
+                    payload[0]
+                )
+            )
+            start_timer = time.monotonic()  # reset the timeout timer
+
+    print("Nothing received in", timeout, "seconds. Leaving RX role")
+    # recommended behavior is to keep in TX mode while idle
+    radio.stopListening()  # put the radio in TX mode
+
+
+def set_role():
+    """Set the role using stdin stream. Timeout arg for slave() can be
+    specified using a space delimiter (e.g. 'R 10' calls `slave(10)`)
+
+    :return:
+        - True when role is complete & app should continue running.
+        - False when app should exit
+    """
+    user_input = input(
+        "*** Enter 'R' for receiver role.\n"
+        "*** Enter 'T' for transmitter role.\n"
+        "*** Enter 'Q' to quit example.\n"
+    ) or "?"
+    user_input = user_input.split()
+    if user_input[0].upper().startswith("R"):
+        if len(user_input) > 1:
+            slave(int(user_input[1]))
+        else:
+            slave()
+        return True
+    elif user_input[0].upper().startswith("T"):
+        master()
+        return True
+    elif user_input[0].upper().startswith("Q"):
+        radio.powerDown()
+        return False
+    print(user_input[0], "is an unrecognized input. Please try again.")
+    return set_role()
+
+
+if __name__ == "__main__":
+
+    args = parser.parse_args()  # parse any CLI args
+
+    # initialize the nRF24L01 on the spi bus
+    if not radio.begin():
+        raise RuntimeError("radio hardware is not responding")
+
+    # For this example, we will use different addresses
+    # An address need to be a buffer protocol object (bytearray)
+    address = [b"1Node", b"2Node"]
+    # It is very helpful to think of an address as a path instead of as
+    # an identifying device destination
+
+    print(sys.argv[0])  # print example name
+
+    # to use different addresses on a pair of radios, we need a variable to
+    # uniquely identify which address this radio will use to transmit
+    # 0 uses address[0] to transmit, 1 uses address[1] to transmit
+    radio_number = args.node  # uses default value from `parser`
+    if args.node is None:  # if '--node' arg wasn't specified
+        radio_number = bool(
+            int(
+                input(
+                    "Which radio is this? Enter '0' or '1'. Defaults to '0' "
+                ) or 0
+            )
+        )
+
+    # set the Power Amplifier level to -12 dBm since this test example is
+    # usually run with nRF24L01 transceivers in close proximity of each other
+    radio.setPALevel(RF24_PA_LOW)  # RF24_PA_MAX is default
+
+    # set the TX address of the RX node into the TX pipe
+    radio.openWritingPipe(address[radio_number])  # always uses pipe 0
+
+    # set the RX address of the TX node into a RX pipe
+    radio.openReadingPipe(1, address[not radio_number])  # using pipe 1
+
+    # To save time during transmission, we'll set the payload size to be only
+    # what we need. A float value occupies 4 bytes in memory using
+    # struct.pack(); "<f" means a little endian unsigned float
+    radio.payloadSize = len(struct.pack("<f", payload[0]))
+
+    # for debugging, we have 2 options that print a large block of details
+    # (smaller) function that prints raw register values
+    # radio.printDetails()
+    # (larger) function that prints human readable data
+    # radio.printPrettyDetails()
+
+    try:
+        if args.role is None:  # if not specified with CLI arg '-r'
+            while set_role():
+                pass  # continue example until 'Q' is entered
+        else:  # if role was set using CLI args
+            # run role once and exit
+            master() if bool(args.role) else slave()
+    except KeyboardInterrupt:
+        print(" Keyboard Interrupt detected. Exiting...")
+        radio.powerDown()
+        sys.exit()
diff --git a/arduino/libraries/RF24/examples_linux/gettingstarted.cpp 
b/arduino/libraries/RF24/examples_linux/gettingstarted.cpp
new file mode 100644
index 000000000..2ba5ee09b
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/gettingstarted.cpp
@@ -0,0 +1,191 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty (2bndy5)
+ */
+
+/**
+ * A simple example of sending data from 1 nRF24L01 transceiver to another.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use `ctrl+c` to quit at any time.
+ */
+#include <ctime>       // time()
+#include <iostream>    // cin, cout, endl
+#include <string>      // string, getline()
+#include <time.h>      // CLOCK_MONOTONIC_RAW, timespec, clock_gettime()
+#include <RF24/RF24.h> // RF24, RF24_PA_LOW, delay()
+
+using namespace std;
+
+/****************** Linux ***********************/
+// Radio CE Pin, CSN Pin, SPI Speed
+// CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use their own pin numbering
+// CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
+// ie: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
+
+// Generic:
+RF24 radio(22, 0);
+/****************** Linux (BBB,x86,etc) ***********************/
+// See http://nRF24.github.io/RF24/pages.html for more information on usage
+// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
+// See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV
+
+// For this example, we'll be using a payload containing
+// a single float number that will be incremented
+// on every successful transmission
+float payload = 0.0;
+
+void setRole(); // prototype to set the node's role
+void master();  // prototype of the TX node's behavior
+void slave();   // prototype of the RX node's behavior
+
+// custom defined timer for evaluating transmission time in microseconds
+struct timespec startTimer, endTimer;
+uint32_t getMicros(); // prototype to get ellapsed time in microseconds
+
+int main(int argc, char** argv) {
+
+    // perform hardware check
+    if (!radio.begin()) {
+        cout << "radio hardware is not responding!!" << endl;
+        return 0; // quit now
+    }
+
+    // to use different addresses on a pair of radios, we need a variable to
+    // uniquely identify which address this radio will use to transmit
+    bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+    // print example's name
+    cout << argv[0] << endl;
+
+    // Let these addresses be used for the pair
+    uint8_t address[2][6] = {"1Node", "2Node"};
+    // It is very helpful to think of an address as a path instead of as
+    // an identifying device destination
+
+    // Set the radioNumber via the terminal on startup
+    cout << "Which radio is this? Enter '0' or '1'. Defaults to '0' ";
+    string input;
+    getline(cin, input);
+    radioNumber = input.length() > 0 && (uint8_t)input[0] == 49;
+
+    // save on transmission time by setting the radio to only transmit the
+    // number of bytes we need to transmit a float
+    radio.setPayloadSize(sizeof(payload)); // float datatype occupies 4 bytes
+
+    // Set the PA Level low to try preventing power supply related problems
+    // because these examples are likely run with nodes in close proximity to
+    // each other.
+    radio.setPALevel(RF24_PA_LOW); // RF24_PA_MAX is default.
+
+    // set the TX address of the RX node into the TX pipe
+    radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0
+
+    // set the RX address of the TX node into a RX pipe
+    radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+    // For debugging info
+    // radio.printDetails();       // (smaller) function that prints raw register values
+    // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+    // ready to execute program now
+    setRole(); // calls master() or slave() based on user input
+    return 0;
+}
+
+
+/**
+ * set this node's role from stdin stream.
+ * this only considers the first char as input.
+ */
+void setRole() {
+    string input = "";
+    while (!input.length()) {
+        cout << "*** PRESS 'T' to begin transmitting to the other node\n";
+        cout << "*** PRESS 'R' to begin receiving from the other node\n";
+        cout << "*** PRESS 'Q' to exit" << endl;
+        getline(cin, input);
+        if (input.length() >= 1) {
+            if (input[0] == 'T' || input[0] == 't')
+                master();
+            else if (input[0] == 'R' || input[0] == 'r')
+                slave();
+            else if (input[0] == 'Q' || input[0] == 'q')
+                break;
+            else
+                cout << input[0] << " is an invalid input. Please try again." << endl;
+        }
+        input = ""; // stay in the while loop
+    } // while
+} // setRole()
+
+
+/**
+ * make this node act as the transmitter
+ */
+void master() {
+    radio.stopListening();                                          // put radio in TX mode
+
+    unsigned int failure = 0;                                       // keep track of failures
+    while (failure < 6) {
+        clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);            // start the timer
+        bool report = radio.write(&payload, sizeof(float));         // transmit & save the report
+        uint32_t timerEllapsed = getMicros();                       // end the timer
+
+        if (report) {
+            // payload was delivered
+            cout << "Transmission successful! Time to transmit = ";
+            cout << timerEllapsed;                                  // print the timer result
+            cout << " us. Sent: " << payload << endl;               // print payload sent
+            payload += 0.01;                                        // increment float payload
+
+        } else {
+            // payload was not delivered
+            cout << "Transmission failed or timed out" << endl;
+            failure++;
+        }
+
+        // to make this example readable in the terminal
+        delay(1000);  // slow transmissions down by 1 second
+    }
+    cout << failure << " failures detected. Leaving TX role." << endl;
+}
+
+/**
+ * make this node act as the receiver
+ */
+void slave() {
+
+    radio.startListening();                                  // put radio in RX mode
+
+    time_t startTimer = time(nullptr);                       // start a timer
+    while (time(nullptr) - startTimer < 6) {                 // use 6 second timeout
+        uint8_t pipe;
+        if (radio.available(&pipe)) {                        // is there a payload? get the pipe number that 
recieved it
+            uint8_t bytes = radio.getPayloadSize();          // get the size of the payload
+            radio.read(&payload, bytes);                     // fetch payload from FIFO
+            cout << "Received " << (unsigned int)bytes;      // print the size of the payload
+            cout << " bytes on pipe " << (unsigned int)pipe; // print the pipe number
+            cout << ": " << payload << endl;                 // print the payload's value
+            startTimer = time(nullptr);                      // reset timer
+        }
+    }
+    cout << "Nothing received in 6 seconds. Leaving RX role." << endl;
+    radio.stopListening();
+}
+
+
+/**
+ * Calculate the ellapsed time in microseconds
+ */
+uint32_t getMicros() {
+    // this function assumes that the timer was started using
+    // `clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);`
+
+    clock_gettime(CLOCK_MONOTONIC_RAW, &endTimer);
+    uint32_t seconds = endTimer.tv_sec - startTimer.tv_sec;
+    uint32_t useconds = (endTimer.tv_nsec - startTimer.tv_nsec) / 1000;
+
+    return ((seconds) * 1000 + useconds) + 0.5;
+}
diff --git a/arduino/libraries/RF24/examples_linux/interruptConfigure.cpp 
b/arduino/libraries/RF24/examples_linux/interruptConfigure.cpp
new file mode 100644
index 000000000..048f281f1
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/interruptConfigure.cpp
@@ -0,0 +1,319 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty (2bndy5)
+ */
+
+/**
+ * This example uses Acknowledgement (ACK) payloads attached to ACK packets to
+ * demonstrate how the nRF24L01's IRQ (Interrupt Request) pin can be
+ * configured to detect when data is received, or when data has transmitted
+ * successfully, or when data has failed to transmit.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use `ctrl+c` to quit at any time.
+ */
+#include <ctime>       // time()
+#include <iostream>    // cin, cout, endl
+#include <string>      // string, getline()
+#include <time.h>      // CLOCK_MONOTONIC_RAW, timespec, clock_gettime()
+#include <RF24/RF24.h> // RF24, RF24_PA_LOW, delay(), pinMode(), INPUT, attachInterrupt(), INT_EDGE_FALLING
+
+using namespace std;
+
+// We will be using the nRF24L01's IRQ pin for this example
+#define IRQ_PIN 12 // this needs to be a digital input capable pin
+
+// this example is a sequential program. so we need to wait for the event to be handled
+volatile bool wait_for_event = false; // used to signify that the event is handled
+
+/****************** Linux ***********************/
+// Radio CE Pin, CSN Pin, SPI Speed
+// CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use their own pin numbering
+// CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
+// ie: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
+
+// Generic:
+RF24 radio(22, 0);
+/****************** Linux (BBB,x86,etc) ***********************/
+// See http://nRF24.github.io/RF24/pages.html for more information on usage
+// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
+// See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV
+
+// For this example, we'll be using a payload containing
+// a string that changes on every transmission. (successful or not)
+// Make a couple arrays of payloads & an iterator to traverse them
+const uint8_t tx_pl_size = 5;
+const uint8_t ack_pl_size = 4;
+uint8_t pl_iterator = 0;
+// The " + 1" compensates for the c-string's NULL terminating 0
+char tx_payloads[4][tx_pl_size + 1] = {"Ping ", "Pong ", "Radio", "1FAIL"};
+char ack_payloads[3][ack_pl_size + 1] = {"Yak ", "Back", " ACK"};
+
+void interruptHandler();         // prototype to handle the interrupt request (IRQ) pin
+void setRole();                  // prototype to set the node's role
+void master();                   // prototype of the TX node's behavior
+void slave();                    // prototype of the RX node's behavior
+void ping_n_wait();              // prototype that sends a payload and waits for the IRQ pin to get triggered
+void printRxFifo(const uint8_t); // prototype to print entire contents of RX FIFO with 1 buffer
+
+
+int main(int argc, char** argv) {
+
+    // perform hardware check
+    if (!radio.begin()) {
+        cout << "radio hardware is not responding!!" << endl;
+        return 0; // quit now
+    }
+
+    // Let these addresses be used for the pair
+    uint8_t address[2][6] = {"1Node", "2Node"};
+    // It is very helpful to think of an address as a path instead of as
+    // an identifying device destination
+
+    // to use different addresses on a pair of radios, we need a variable to
+    // uniquely identify which address this radio will use to transmit
+    bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+    // print example's name
+    cout << argv[0] << endl;
+
+    // Set the radioNumber via the terminal on startup
+    cout << "Which radio is this? Enter '0' or '1'. Defaults to '0' ";
+    string input;
+    getline(cin, input);
+    radioNumber = input.length() > 0 && (uint8_t)input[0] == 49;
+
+    // to use ACK payloads, we need to enable dynamic payload lengths
+    radio.enableDynamicPayloads();    // ACK payloads are dynamically sized
+
+    // Acknowledgement packets have no payloads by default. We need to enable
+    // this feature for all nodes (TX & RX) to use ACK payloads.
+    radio.enableAckPayload();
+
+    // Set the PA Level low to try preventing power supply related problems
+    // because these examples are likely run with nodes in close proximity to
+    // each other.
+    radio.setPALevel(RF24_PA_LOW);  // RF24_PA_MAX is default.
+
+    // set the TX address of the RX node into the TX pipe
+    radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0
+
+    // set the RX address of the TX node into a RX pipe
+    radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+    // For debugging info
+    // radio.printDetails();       // (smaller) function that prints raw register values
+    // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+    // setup the digital input pin connected to the nRF24L01's IRQ pin
+    pinMode(IRQ_PIN, INPUT);
+
+    // register the interrupt request (IRQ) to call our
+    // Interrupt Service Routine (ISR) callback function interruptHandler()
+    attachInterrupt(IRQ_PIN, INT_EDGE_FALLING, &interruptHandler);
+    // IMPORTANT: do not call radio.available() before calling
+    // radio.whatHappened() when the interruptHandler() is triggered by the
+    // IRQ pin FALLING event. According to the datasheet, the pipe information
+    // is unreliable during the IRQ pin FALLING transition.
+
+    // ready to execute program now
+    setRole(); // calls master() or slave() based on user input
+    return 0;
+} // main
+
+
+/**
+ * set this node's role from stdin stream.
+ * this only considers the first char as input.
+ */
+void setRole() {
+    string input = "";
+    while (!input.length()) {
+        cout << "*** PRESS 'T' to begin transmitting to the other node\n";
+        cout << "*** PRESS 'R' to begin receiving from the other node\n";
+        cout << "*** PRESS 'Q' to exit" << endl;
+        getline(cin, input);
+        if (input.length() >= 1) {
+            if (input[0] == 'T' || input[0] == 't')
+                master();
+            else if (input[0] == 'R' || input[0] == 'r')
+                slave();
+            else if (input[0] == 'Q' || input[0] == 'q')
+                break;
+            else
+                cout << input[0] << " is an invalid input. Please try again." << endl;
+        }
+        input = ""; // stay in the while loop
+    } // while
+} // setRole
+
+
+/**
+ * act as the transmitter to show 3 different IRQ events by sending 4 payloads:
+ * 1. Successfully receive ACK payload first
+ * 2. Successfully transmit on second
+ * 3. Send a third payload to fill RX node's RX FIFO (supposedly making RX node unresponsive)
+ * 4. intentionally fail transmit on the fourth
+ */
+void master() {
+    pl_iterator = 0; // reset the iterator for the following tests done in master()
+
+    // Test the "data ready" event with the IRQ pin
+    cout << "\nConfiguring IRQ pin to ignore the 'data sent' event\n";
+    radio.maskIRQ(true, false, false); // args = "data_sent", "data_fail", "data_ready"
+    cout << "   Pinging RX node for 'data ready' event...";
+    ping_n_wait();                     // transmit a payload and detect the IRQ pin
+    pl_iterator++;                     // increment iterator for next test
+
+
+    // Test the "data sent" event with the IRQ pin
+    cout << "\nConfiguring IRQ pin to ignore the 'data ready' event\n";
+    radio.maskIRQ(false, false, true); // args = "data_sent", "data_fail", "data_ready"
+    cout << "   Pinging RX node for 'data sent' event...";
+    radio.flush_tx();                  // flush payloads from any failed prior test
+    ping_n_wait();                     // transmit a payload and detect the IRQ pin
+    pl_iterator++;                     // increment iterator for next test
+
+
+    // Use this iteration to fill the RX node's FIFO which sets us up for the next test.
+    // write() uses virtual interrupt flags that work despite the masking of the IRQ pin
+    radio.maskIRQ(1, 1, 1); // disable IRQ masking for this step
+
+    cout << "\nSending 1 payload to fill RX node's FIFO. IRQ pin is neglected.\n";
+    // write() will call flush_tx() on 'data fail' events
+    if (radio.write(&tx_payloads[pl_iterator], tx_pl_size))
+        cout << "RX node's FIFO is full; it is not listening any more" << endl;
+    else {
+        cout << "Transmission failed or timed out. Continuing anyway." << endl;
+        radio.flush_tx();
+    }
+    pl_iterator++;                     // increment iterator for next test
+
+
+    // test the "data fail" event with the IRQ pin
+    cout << "\nConfiguring IRQ pin to reflect all events\n";
+    radio.maskIRQ(0, 0, 0); // args = "data_sent", "data_fail", "data_ready"
+    cout << "   Pinging inactive RX node for 'data fail' event...";
+    ping_n_wait();                     // transmit a payload and detect the IRQ pin
+
+    // CE pin is still HIGH which consumes more power. Example is now idling so...
+    radio.stopListening(); // ensure CE pin is LOW
+    // stopListening() also calls flush_tx() when ACK payloads are enabled
+
+    if (radio.available()) {
+        printRxFifo(ack_pl_size); // doing this will flush the RX FIFO
+    }
+} // master
+
+
+/**
+ * act as the receiver
+ */
+void slave() {
+
+    // let IRQ pin only trigger on "data_ready" event in RX mode
+    radio.maskIRQ(1, 1, 0); // args = "data_sent", "data_fail", "data_ready"
+
+    // Fill the TX FIFO with 3 ACK payloads for the first 3 received
+    // transmissions on pipe 0.
+    radio.writeAckPayload(1, &ack_payloads[0], ack_pl_size);
+    radio.writeAckPayload(1, &ack_payloads[1], ack_pl_size);
+    radio.writeAckPayload(1, &ack_payloads[2], ack_pl_size);
+
+    radio.startListening();            // put radio in RX mode
+    time_t startTimer = time(nullptr); // start a timer
+    while (time(nullptr) - startTimer < 6 && !radio.rxFifoFull()) {
+        // use 6 second timeout & wait till RX FIFO is full
+    }
+    delay(100);                        // wait for ACK payload to finish transmitting
+    radio.stopListening();             // also discards unused ACK payloads
+
+    if (radio.available()) {
+        printRxFifo(tx_pl_size);
+    }
+    else {
+        cout << "Timeout was reached. Going back to setRole()" << endl;
+    }
+} // slave
+
+
+/**
+ * pings the receiver with a non-blocking startWrite(), then waits till
+ * the IRQ pin is triggered
+ */
+void ping_n_wait() {
+    // use the non-blocking call to write a payload and begin transmission
+    // the "false" argument means we are expecting an ACK packet response
+    radio.startFastWrite(tx_payloads[pl_iterator], tx_pl_size, false);
+
+    wait_for_event = true;
+    while (wait_for_event) {
+        /*
+         * IRQ pin is LOW when activated. Otherwise it is always HIGH
+         * Wait in this empty loop until IRQ pin is activated.
+         *
+         * In this example, the "data fail" event is always configured to
+         * trigger the IRQ pin active. Because the auto-ACK feature is on by
+         * default, we don't need a timeout check to prevent an infinite loop.
+         */
+    }
+}
+
+
+/**
+ * when the IRQ pin goes active LOW, call this fuction print out why
+ */
+void interruptHandler() {
+    // print IRQ status and all masking flags' states
+
+    cout << "\tIRQ pin is actively LOW" << endl;  // show that this function was called
+
+    bool tx_ds, tx_df, rx_dr;                     // declare variables for IRQ masks
+    radio.whatHappened(tx_ds, tx_df, rx_dr);      // get values for IRQ masks
+    // whatHappened() clears the IRQ masks also. This is required for
+    // continued TX operations when a transmission fails.
+    // clearing the IRQ masks resets the IRQ pin to its inactive state (HIGH)
+
+    cout << "\tdata_sent: " << tx_ds;             // print "data sent" mask state
+    cout << ", data_fail: " << tx_df;             // print "data fail" mask state
+    cout << ", data_ready: " << rx_dr << endl;    // print "data ready" mask state
+
+    if (tx_df)                                    // if TX payload failed
+        radio.flush_tx();                         // clear all payloads from the TX FIFO
+
+    // print if test passed or failed. Unintentional fails mean the RX node was not listening.
+    if (pl_iterator == 0)
+        cout << "   'Data Ready' event test " << (rx_dr ? "passed" : "failed") << endl;
+    else if (pl_iterator == 1)
+        cout << "   'Data Sent' event test " << (tx_ds ? "passed" : "failed") << endl;
+    else if (pl_iterator == 3)
+        cout << "   'Data Fail' event test " << (tx_df ? "passed" : "failed") << endl;
+
+    wait_for_event = false; // ready to continue
+} // interruptHandler
+
+
+/**
+ * Print the entire RX FIFO with one buffer. This will also flush the RX FIFO.
+ * @param pl_size used to determine received payload size. Remember that the
+ * payload sizes are declared as tx_pl_size and ack_pl_size.
+ */
+void printRxFifo(const uint8_t pl_size) {
+    char rx_fifo[pl_size * 3 + 1];         // assuming RX FIFO is full; declare a buffer to hold it all
+    if (radio.rxFifoFull()) {
+        rx_fifo[pl_size * 3] = 0;          // add a NULL terminating char to use as a c-string
+        radio.read(&rx_fifo, pl_size * 3); // this clears the RX FIFO (for this example)
+    }
+    else {
+        uint8_t i = 0;
+        while (radio.available()) {
+            radio.read(&rx_fifo + (i * pl_size), pl_size);
+            i++;
+        }
+        rx_fifo[i * pl_size] = 0;          // add a NULL terminating char to use as a c-string
+    }
+
+    // print the entire RX FIFO with 1 buffer
+    cout << "Complete RX FIFO: " << rx_fifo << endl;
+}
diff --git a/arduino/libraries/RF24/examples_linux/interrupt_configure.py 
b/arduino/libraries/RF24/examples_linux/interrupt_configure.py
new file mode 100644
index 000000000..8dd7f6380
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/interrupt_configure.py
@@ -0,0 +1,300 @@
+"""
+This example uses Acknowledgement (ACK) payloads attached to ACK packets to
+demonstrate how the nRF24L01's IRQ (Interrupt Request) pin can be
+configured to detect when data is received, or when data has transmitted
+successfully, or when data has failed to transmit.
+
+This example was written to be used on 2 devices acting as "nodes".
+"""
+import sys
+import argparse
+import time
+import RPi.GPIO as GPIO
+from RF24 import RF24, RF24_PA_LOW
+
+
+parser = argparse.ArgumentParser(
+    description=__doc__,
+    formatter_class=argparse.RawDescriptionHelpFormatter
+)
+parser.add_argument(
+    "-n",
+    "--node",
+    type=int,
+    choices=range(2),
+    help="the identifying radio number (or node ID number)"
+)
+parser.add_argument(
+    "-r",
+    "--role",
+    type=int,
+    choices=range(2),
+    help="'1' specifies the TX role. '0' specifies the RX role."
+)
+
+########### USER CONFIGURATION ###########
+# See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md
+# Radio CE Pin, CSN Pin, SPI Speed
+# CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use
+# their own pin numbering
+# CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
+# ie: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
+
+# Generic:
+radio = RF24(22, 0)
+################## Linux (BBB,x86,etc) #########################
+# See http://nRF24.github.io/RF24/pages.html for more information on usage
+# See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
+# See https://www.kernel.org/doc/Documentation/spi/spidev for more
+# information on SPIDEV
+
+# select your digital input pin that's connected to the IRQ pin on the nRF24L01
+IRQ_PIN = 12
+
+# For this example, we'll be using a payload containing
+# a string that changes on every transmission. (successful or not)
+# Make a couple tuples of payloads & an iterator to traverse them
+pl_iterator = [0]  # use a 1-item list instead of python's global keyword
+tx_payloads = (b"Ping ", b"Pong ", b"Radio", b"1FAIL")
+ack_payloads = (b"Yak ", b"Back", b" ACK")
+
+
+def interrupt_handler(channel):
+    """This function is called when IRQ pin is detected active LOW"""
+    print("IRQ pin", channel, "went active LOW.")
+    tx_ds, tx_df, rx_dr = radio.whatHappened()   # get IRQ status flags
+    if tx_df:
+        radio.flush_tx()
+    print("\ttx_ds: {}, tx_df: {}, rx_dr: {}".format(tx_ds, tx_df, rx_dr))
+    if pl_iterator[0] == 0:
+        print(
+            "    'data ready' event test {}".format(
+                "passed" if rx_dr else "failed"
+            )
+        )
+    elif pl_iterator[0] == 1:
+        print(
+            "    'data sent' event test {}".format(
+                "passed" if tx_ds else "failed"
+            )
+        )
+    elif pl_iterator[0] == 3:
+        print(
+            "    'data fail' event test {}".format(
+                "passed" if tx_df else "failed"
+            )
+        )
+
+
+# setup IRQ GPIO pin
+GPIO.setmode(GPIO.BCM)
+GPIO.setup(IRQ_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP)
+GPIO.add_event_detect(IRQ_PIN, GPIO.FALLING, callback=interrupt_handler)
+# IMPORTANT: do not call radio.available() before calling
+# radio.whatHappened() when the interruptHandler() is triggered by the
+# IRQ pin FALLING event. According to the datasheet, the pipe information
+# is unreliable during the IRQ pin FALLING transition.
+
+
+def _ping_n_wait(pl_iter):
+    """private function to ping RX node and wait for IRQ pin to be handled
+
+    :param int pl_iter: The index of the buffer in `tx_payloads` tuple to
+        send. This number is also used to determine if event test was
+        successful or not.
+    """
+    # set pl_iterator[0] so interrupt_handler() can determine if test was
+    # successful or not
+    pl_iterator[0] = pl_iter
+    # the following False parameter means we're expecting an ACK packet
+    radio.startFastWrite(tx_payloads[pl_iter], False)
+    time.sleep(0.1) # wait 100 ms for interrupt_handler() to complete
+
+
+def print_rx_fifo(pl_size):
+    """fush RX FIFO by printing all available payloads with 1 buffer
+
+    :param int pl_size: the expected size of each payload
+    """
+    if radio.rxFifoFull():
+            # all 3 payloads received were 5 bytes each, and RX FIFO is full
+            # so, fetching 15 bytes from the RX FIFO also flushes RX FIFO
+            print(
+                "Complete RX FIFO:",
+                radio.read(pl_size * 3).decode("utf-8")
+            )
+    else:
+        buffer = bytearray()
+        while radio.available():
+            buffer += radio.read(pl_size)
+        if buffer:  # if any payloads were read from the RX FIFO
+            print("Complete RX FIFO:", buffer.decode("utf-8"))
+
+
+def master():
+    """Transmits 4 times and reports results
+
+        1. successfully receive ACK payload first
+        2. successfully transmit on second
+        3. send a third payload to fill RX node's RX FIFO
+           (supposedly making RX node unresponsive)
+        4. intentionally fail transmit on the fourth
+    """
+    radio.stopListening()  # put radio in TX mode
+
+    # on data ready test
+    print("\nConfiguring IRQ pin to only ignore 'on data sent' event")
+    radio.maskIRQ(True, False, False)  # args = tx_ds, tx_df, rx_dr
+    print("    Pinging slave node for an ACK payload...", end=" ")
+    _ping_n_wait(0)
+
+    # on "data sent" test
+    print("\nConfiguring IRQ pin to only ignore 'on data ready' event")
+    radio.maskIRQ(False, False, True)  # args = tx_ds, tx_df, rx_dr
+    print("    Pinging slave node again...             ", end=" ")
+    _ping_n_wait(1)
+
+    # trigger slave node to stopListening() by filling slave node's RX FIFO
+    print("\nSending one extra payload to fill RX FIFO on slave node.")
+    radio.maskIRQ(1, 1, 1)  # disable IRQ pin for this step
+    if radio.write(tx_payloads[2]):
+        # when send_only parameter is True, send() ignores RX FIFO usage
+        if radio.rxFifoFull():
+            print("RX node's FIFO is full; it is not listening any more")
+        else:
+            print(
+                "Transmission successful, but the RX node might still be "
+                "listening."
+            )
+    else:
+        radio.flush_tx()
+        print("Transmission failed or timed out. Continuing anyway.")
+
+    # on "data fail" test
+    print("\nConfiguring IRQ pin to go active for all events.")
+    radio.maskIRQ(False, False, False)  # args = tx_ds, tx_df, rx_dr
+    print("    Sending a ping to inactive slave node...", end=" ")
+    _ping_n_wait(3)
+
+    # CE pin is still HIGH which consumes more power. Example is now idling so...
+    radio.stopListening()  # ensure CE pin is LOW
+    # stopListening() also calls flush_tx() when ACK payloads are enabled
+
+    print_rx_fifo(len(ack_payloads[0]))  # empty RX FIFO
+
+
+def slave(timeout=6):  # will listen for 6 seconds before timing out
+    """Only listen for 3 payload from the master node
+
+    :param int timeout: The number of seconds to wait (with no transmission)
+        until exiting function.
+    """
+    pl_iterator[0] = 0  # reset this to indicate event is a 'data_ready' event
+    # setup radio to recieve pings, fill TX FIFO with ACK payloads
+    radio.writeAckPayload(1, ack_payloads[0])
+    radio.writeAckPayload(1, ack_payloads[1])
+    radio.writeAckPayload(1, ack_payloads[2])
+    radio.startListening()  # start listening & clear status flags
+    start_timer = time.monotonic()  # start timer now
+    while not radio.rxFifoFull() and time.monotonic() - start_timer < timeout:
+        # if RX FIFO is not full and timeout is not reached, then keep waiting
+        pass
+    time.sleep(0.1)  # wait for last ACK payload to transmit
+    radio.stopListening()  # put radio in TX mode & discard any ACK payloads
+    print_rx_fifo(len(tx_payloads[0]))
+
+
+def set_role():
+    """Set the role using stdin stream. Timeout arg for slave() can be
+    specified using a space delimiter (e.g. 'R 10' calls `slave(10)`)
+
+    :return:
+        - True when role is complete & app should continue running.
+        - False when app should exit
+    """
+    user_input = input(
+        "*** Enter 'R' for receiver role.\n"
+        "*** Enter 'T' for transmitter role.\n"
+        "*** Enter 'Q' to quit example.\n"
+    ) or "?"
+    user_input = user_input.split()
+    if user_input[0].upper().startswith("R"):
+        if len(user_input) > 1:
+            slave(int(user_input[1]))
+        else:
+            slave()
+        return True
+    elif user_input[0].upper().startswith("T"):
+        master()
+        return True
+    elif user_input[0].upper().startswith("Q"):
+        radio.powerDown()
+        return False
+    else:
+        print(user_input[0], "is an unrecognized input. Please try again.")
+        return set_role()
+
+
+if __name__ == "__main__":
+
+    args = parser.parse_args()  # parse any CLI args
+
+    # initialize the nRF24L01 on the spi bus
+    if not radio.begin():
+        raise RuntimeError("radio hardware is not responding")
+
+    # For this example, we will use different addresses
+    # An address need to be a buffer protocol object (bytearray)
+    address = [b"1Node", b"2Node"]
+    # It is very helpful to think of an address as a path instead of as
+    # an identifying device destination
+
+    print(sys.argv[0])  # print example name
+
+    # to use different addresses on a pair of radios, we need a variable to
+    # uniquely identify which address this radio will use to transmit
+    # 0 uses address[0] to transmit, 1 uses address[1] to transmit
+    radio_number = args.node  # uses default value from `parser`
+    if args.node is None:  # if '--node' arg wasn't specified
+        radio_number = bool(
+            int(
+                input(
+                    "Which radio is this? Enter '0' or '1'. Defaults to '0' "
+                ) or 0
+            )
+        )
+
+    # set the Power Amplifier level to -12 dBm since this test example is
+    # usually run with nRF24L01 transceivers in close proximity of each other
+    radio.setPALevel(RF24_PA_LOW)  # RF24_PA_MAX is default
+
+    # ACK payloads are dynamically sized.
+    radio.enableDynamicPayloads()  # to use ACK payloads
+
+    # this example uses the ACK payload to trigger the IRQ pin active for
+    # the "on data received" event
+    radio.enableAckPayload()  # enable ACK payloads
+
+    # set the TX address of the RX node into the TX pipe
+    radio.openWritingPipe(address[radio_number])  # always uses pipe 0
+
+    # set the RX address of the TX node into a RX pipe
+    radio.openReadingPipe(1, address[not radio_number])  # using pipe 1
+
+    # for debugging, we have 2 options that print a large block of details
+    # (smaller) function that prints raw register values
+    # radio.printDetails()
+    # (larger) function that prints human readable data
+    # radio.printPrettyDetails()
+
+    try:
+        if args.role is None:  # if not specified with CLI arg '-r'
+            while set_role():
+                pass  # continue example until 'Q' is entered
+        else:  # if role was set using CLI args
+            # run role once and exit
+            master() if bool(args.role) else slave()
+    except KeyboardInterrupt:
+        print(" Keyboard Interrupt detected. Exiting...")
+        radio.powerDown()
+        sys.exit()
diff --git a/arduino/libraries/RF24/examples_linux/interrupts/gettingstarted_call_response_int.cpp 
b/arduino/libraries/RF24/examples_linux/interrupts/gettingstarted_call_response_int.cpp
new file mode 100644
index 000000000..3a9f9e27b
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/interrupts/gettingstarted_call_response_int.cpp
@@ -0,0 +1,147 @@
+/*
+TMRh20 2014 - Updated to work with optimized RF24 Arduino library
+*/
+
+
+/**
+ * Example for efficient call-response using ack-payloads and interrupts
+ *
+ * This example continues to make use of all the normal functionality of the radios including
+ * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well.
+ * This allows very fast call-response communication, with the responding radio never having to
+ * switch out of Primary Receiver mode to send back a payload, but having the option to switch to
+ * primary transmitter if wanting to initiate communication instead of respond to a commmunication.
+ */
+
+#include <cstdlib>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <unistd.h>
+#include <RF24/RF24.h>
+
+using namespace std;
+
+//
+// Hardware configuration
+// Configure the appropriate pins for your connections
+
+/****************** Raspberry Pi ***********************/
+
+RF24 radio(22, 0); //GPIO, SPI-BUS
+
+/********** User Config *********/
+// Assign a unique identifier for this node, 0 or 1. Arduino example uses radioNumber 0 by default.
+bool radioNumber = 1;
+int interruptPin = 23;
+/********************************/
+
+
+// Radio pipe addresses for the 2 nodes to communicate.
+const uint8_t addresses[][6] = {"1Node", "2Node"};
+
+volatile bool role_ping_out = 1, role_pong_back = 0, role = 0;
+uint8_t counter = 1;                                                          // A single byte to keep track 
of the data being sent back and forth
+
+volatile bool gotResponse = false;
+
+void intHandler()
+{
+
+    if (role == role_pong_back) {
+        uint8_t pipeNo, gotByte;                        // Declare variables for the pipe and the byte 
received
+        if (radio.available(&pipeNo)) {                // Read all available payloads
+            radio.read(&gotByte, 1);
+            // Since this is a call-response. Respond directly with an ack payload.
+            gotByte += 1;                                // Ack payloads are much more efficient than 
switching to transmit mode to respond to a call
+            radio.writeAckPayload(pipeNo, &gotByte, 1);   // This can be commented out to send empty 
payloads.
+            printf("Loaded next response %d \n\r", gotByte);
+
+        }
+    }
+}
+
+int main(int argc, char** argv)
+{
+
+    cout << "RPi/RF24/examples/gettingstarted_call_response_int\n";
+    radio.begin();
+    radio.enableAckPayload();               // Allow optional ack payloads
+    radio.enableDynamicPayloads();          // needed for using ACK payloads
+    radio.printDetails();                   // Dump the configuration of the rf unit for debugging
+
+
+    /********* Role chooser ***********/
+
+    printf("\n ************ Role Setup ***********\n");
+    string input = "";
+    char myChar = {0};
+    cout << "Choose a role: Enter 0 for pong_back, 1 for ping_out (CTRL+C to exit)\n>";
+    getline(cin, input);
+
+    if (input.length() == 1) {
+        myChar = input[0];
+        if (myChar == '0') {
+            cout << "Role: Pong Back, awaiting transmission " << endl << endl;
+        } else {
+            cout << "Role: Ping Out, starting transmission " << endl << endl;
+            role = role_ping_out;
+        }
+    }
+
+    /***********************************/
+    // This opens two pipes for these two nodes to communicate
+    // back and forth.
+    if (!radioNumber){
+        radio.openWritingPipe(addresses[0]);
+        radio.openReadingPipe(1, addresses[1]);
+    }else{
+        radio.openWritingPipe(addresses[1]);
+        radio.openReadingPipe(1, addresses[0]);
+    }
+    radio.startListening();
+    radio.writeAckPayload(1, &counter, 1);
+
+    radio.maskIRQ(1, 1, 0);                                      //Mask tx_ok & tx_fail interrupts
+    attachInterrupt(interruptPin, INT_EDGE_FALLING, intHandler); //Attach interrupt to bcm pin 23
+
+    // forever loop
+    while (1) {
+
+
+        /****************** Ping Out Role ***************************/
+
+        if (role == role_ping_out){                                 // Radio is in ping mode
+
+            uint8_t gotByte;                                        // Initialize a variable for the 
incoming response
+
+            radio.stopListening();                                  // First, stop listening so we can talk.
+            printf("Now sending %d as payload. ", counter);         // Use a simple byte counter as payload
+            unsigned long time = millis();                          // Record the current microsecond count
+
+            if (radio.write(&counter, 1))                           // Send the counter variable to the 
other radio
+            {
+                if (!radio.available()){                            // If nothing in the buffer, we got an 
ack but it is blank
+                    printf("Got blank response. round-trip delay: %lu ms\n\r", millis() - time);
+                }else{
+                    while (radio.available())                       // If an ack with payload was received
+                    {
+                        radio.read(&gotByte, 1);                    // Read it, and display the response time
+                        printf("Got response %d, round-trip delay: %lu ms\n\r", gotByte, millis() - time);
+                        counter++;                                  // Increment the counter variable
+                    }
+                }
+            }else{
+                printf("Sending failed.\n\r");                      // If no ack response, sending failed
+            }
+            sleep(1);                                               // Try again later
+        }
+
+
+        /****************** Pong Back Role ***************************/
+        // This is done using ACK payloads & IRQ
+
+    } //while 1
+} //main
+
+
diff --git a/arduino/libraries/RF24/examples_linux/interrupts/gettingstarted_call_response_int2.cpp 
b/arduino/libraries/RF24/examples_linux/interrupts/gettingstarted_call_response_int2.cpp
new file mode 100644
index 000000000..ae01e608c
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/interrupts/gettingstarted_call_response_int2.cpp
@@ -0,0 +1,152 @@
+/*
+TMRh20 2014 - Updated to work with optimized RF24 Arduino library
+*/
+
+
+/**
+ * Example for efficient call-response using ack-payloads
+ *
+ * This example continues to make use of all the normal functionality of the radios including
+ * the auto-ack and auto-retry features, but allows ack-payloads to be written optionlly as well.
+ * This allows very fast call-response communication, with the responding radio never having to
+ * switch out of Primary Receiver mode to send back a payload, but having the option to switch to
+ * primary transmitter if wanting to initiate communication instead of respond to a commmunication.
+ */
+
+#include <cstdlib>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <unistd.h>
+#include <RF24/RF24.h>
+
+using namespace std;
+
+//
+// Hardware configuration
+// Configure the appropriate pins for your connections
+
+/****************** Raspberry Pi ***********************/
+
+RF24 radio(22, 0);
+
+/********** User Config *********/
+// Assign a unique identifier for this node, 0 or 1. Arduino example uses radioNumber 0 by default.
+bool radioNumber = 1;
+int interruptPin = 23;
+/********************************/
+
+
+// Radio pipe addresses for the 2 nodes to communicate.
+const uint8_t addresses[][6] = {"1Node", "2Node"};
+
+bool role_ping_out = 1, role_pong_back = 0, role = 0;
+uint8_t counter = 1;  // A single byte to keep track of the data being sent back and forth
+uint32_t timer = 0;
+
+void intHandler(){
+
+    bool tx_ok, tx_fail, rx;
+    radio.whatHappened(tx_ok, tx_fail, rx);
+
+    if (tx_fail) {
+        printf("Sending failed.\n\r");
+    }
+
+    if (role == role_ping_out && tx_ok){
+        if (!radio.available()){
+            printf("Got blank response. round-trip delay: %u ms\n\r", millis() - timer);
+        }
+    }
+
+    if (role == role_ping_out){
+        while (radio.available()){
+            uint8_t gotByte;
+            radio.read(&gotByte, 1);
+            printf("Got response %d, round-trip delay: %u ms\n\r", gotByte, millis() - timer);
+            counter++;
+        }
+    }
+
+    if (role == role_pong_back) {
+        if (tx_ok) {
+            printf("Ack Payload Sent\n");
+        }
+        uint8_t pipeNo, gotByte;
+        if (radio.available(&pipeNo)) {
+            radio.read(&gotByte, 1);
+
+            gotByte += 1;
+            radio.writeAckPayload(pipeNo, &gotByte, 1);
+            printf("Loaded next response %d \n\r", gotByte);
+        }
+    }
+}
+
+int main(int argc, char** argv){
+
+    cout << "RPi/RF24/examples/gettingstarted_call_response\n";
+    radio.begin();
+    radio.enableAckPayload();               // Allow optional ack payloads
+    radio.enableDynamicPayloads();          // needed for using ACK payloads
+    radio.printDetails();                   // Dump the configuration of the rf unit for debugging
+
+
+    /********* Role chooser ***********/
+
+    printf("\n ************ Role Setup ***********\n");
+    string input = "";
+    char myChar = {0};
+    cout << "Choose a role: Enter 0 for pong_back, 1 for ping_out (CTRL+C to exit)\n>";
+    getline(cin, input);
+
+    if (input.length() == 1){
+        myChar = input[0];
+        if (myChar == '0'){
+            cout << "Role: Pong Back, awaiting transmission " << endl << endl;
+        }else{
+            cout << "Role: Ping Out, starting transmission " << endl << endl;
+            role = role_ping_out;
+        }
+    }
+
+
+    /***********************************/
+    // This opens two pipes for these two nodes to communicate
+    // back and forth.
+    if (!radioNumber){
+        radio.openWritingPipe(addresses[0]);
+        radio.openReadingPipe(1, addresses[1]);
+    }else{
+        radio.openWritingPipe(addresses[1]);
+        radio.openReadingPipe(1, addresses[0]);
+    }
+    radio.startListening();
+    radio.writeAckPayload(1, &counter, 1);
+
+    attachInterrupt(interruptPin, INT_EDGE_FALLING, intHandler); //Attach interrupt to bcm pin 23
+
+    // forever loop
+    while (1){
+
+
+        /****************** Ping Out Role ***************************/
+
+        if (role == role_ping_out)                                  // Radio is in ping mode
+        {
+            //uint8_t gotByte;                                      // Initialize a variable for the 
incoming response
+
+            radio.stopListening();                                  // First, stop listening so we can talk.
+            printf("Now sending %d as payload. ", counter);         // Use a simple byte counter as payload
+            timer = millis();                                       // Record the current microsecond count
+
+            radio.startWrite(&counter, 1, false);                   // Send the counter variable to the 
other radio
+            sleep(1);                                               // Try again later
+        }
+
+
+        /****************** Pong Back Role ***************************/
+        // This is done by using ACK payloads & IRQ
+
+    } //while 1
+} //main
diff --git a/arduino/libraries/RF24/examples_linux/interrupts/pingpair_dyn_int.cpp 
b/arduino/libraries/RF24/examples_linux/interrupts/pingpair_dyn_int.cpp
new file mode 100644
index 000000000..e90e1739d
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/interrupts/pingpair_dyn_int.cpp
@@ -0,0 +1,177 @@
+/*
+       TMRh20 2014 - Optimized RF24 Library Fork
+*/
+
+/**
+ * Example using Dynamic Payloads
+ *
+ * This is an example of how to use payloads of a varying (dynamic) size.
+ */
+
+#include <cstdlib>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <RF24/RF24.h>
+
+using namespace std;
+//
+// Hardware configuration
+// Configure the appropriate pins for your connections
+
+/****************** Raspberry Pi ***********************/
+
+RF24 radio(22, 0); // CE GPIO, CSN SPI-BUS
+
+int interruptPin = 23; // GPIO pin for interrupts
+
+/**************************************************************/
+
+// Radio pipe addresses for the 2 nodes to communicate.
+const uint64_t pipes[2] = {0xF0F0F0F0E1LL, 0xF0F0F0F0D2LL};
+
+const int min_payload_size = 4;
+const int max_payload_size = 32;
+const int payload_size_increments_by = 1;
+int next_payload_size = min_payload_size;
+
+char receive_payload[max_payload_size + 1]; // +1 to allow room for a terminating NULL char
+bool role_ping_out = 1, role_pong_back = 0;
+bool role = 0;
+
+void intHandler()
+{
+    //
+    // Pong back role.  Receive each packet, dump it out, and send it back
+    //
+
+    if (role == role_pong_back) {
+        // if there is data ready
+        if (radio.available()) {
+            // Dump the payloads until we've gotten everything
+            uint8_t len = 0;
+
+            while (radio.available()) {
+                // Fetch the payload, and see if this was the last one.
+                len = radio.getDynamicPayloadSize();
+                radio.read(receive_payload, len);
+
+                // Put a zero at the end for easy printing
+                receive_payload[len] = 0;
+
+                // Spew it
+                printf("Got payload size=%i value=%s\n\r", len, receive_payload);
+            }
+
+            // First, stop listening so we can talk
+            radio.stopListening();
+
+            // Send the final one back.
+            radio.write(receive_payload, len);
+            printf("Sent response.\n\r");
+
+            // Now, resume listening so we catch the next packets.
+            radio.startListening();
+        }
+    }
+}
+
+int main(int argc, char** argv)
+{
+
+
+    // Print preamble:
+    cout << "RF24/examples/pingpair_dyn/\n";
+
+    // Setup and configure rf radio
+    radio.begin();
+    radio.enableDynamicPayloads();
+    radio.setRetries(5, 15);
+    radio.printDetails();
+
+
+    /********* Role chooser ***********/
+
+    printf("\n ************ Role Setup ***********\n");
+    string input = "";
+    char myChar = {0};
+    cout << "Choose a role: Enter 0 for receiver, 1 for transmitter (CTRL+C to exit) \n>";
+    getline(cin, input);
+
+    if (input.length() == 1) {
+        myChar = input[0];
+        if (myChar == '0') {
+            cout << "Role: Pong Back, awaiting transmission " << endl << endl;
+        } else {
+            cout << "Role: Ping Out, starting transmission " << endl << endl;
+            role = role_ping_out;
+        }
+    }
+    /***********************************/
+
+    if (role == role_ping_out) {
+        radio.openWritingPipe(pipes[0]);
+        radio.openReadingPipe(1, pipes[1]);
+    } else {
+        radio.openWritingPipe(pipes[1]);
+        radio.openReadingPipe(1, pipes[0]);
+        radio.startListening();
+    }
+    attachInterrupt(interruptPin, INT_EDGE_FALLING, intHandler); //Attach interrupt to bcm pin 23
+
+    // forever loop
+    while (1) {
+
+        if (role == role_ping_out) {
+            // The payload will always be the same, what will change is how much of it we send.
+            static char send_payload[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ789012";
+
+            // First, stop listening so we can talk.
+            radio.stopListening();
+
+            // Take the time, and send it.  This will block until complete
+            printf("Now sending length %i...", next_payload_size);
+            radio.write(send_payload, next_payload_size);
+
+            // Now, continue listening
+            radio.startListening();
+
+            // Wait here until we get a response, or timeout
+            unsigned long started_waiting_at = millis();
+            bool timeout = false;
+            while (!radio.available() && !timeout) {
+                if (millis() - started_waiting_at > 500) {
+                    timeout = true;
+                }
+            }
+
+            // Describe the results
+            if (timeout) {
+                printf("Failed, response timed out.\n\r");
+            } else {
+                // Grab the response, compare, and send to debugging spew
+                uint8_t len = radio.getDynamicPayloadSize();
+                radio.read(receive_payload, len);
+
+                // Put a zero at the end for easy printing
+                receive_payload[len] = 0;
+
+                // Spew it
+                printf("Got response size=%i value=%s\n\r", len, receive_payload);
+            }
+
+            // Update size for next time.
+            next_payload_size += payload_size_increments_by;
+            if (next_payload_size > max_payload_size) {
+                next_payload_size = min_payload_size;
+            }
+
+            // Try again 1s later
+            delay(100);
+        }
+
+
+    }
+}
+
+
diff --git a/arduino/libraries/RF24/examples_linux/interrupts/transfer_interrupt.cpp 
b/arduino/libraries/RF24/examples_linux/interrupts/transfer_interrupt.cpp
new file mode 100644
index 000000000..3fe0c178f
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/interrupts/transfer_interrupt.cpp
@@ -0,0 +1,190 @@
+/*
+TMRh20 2014
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+ */
+
+/** General Data Transfer Rate Test
+ * This example demonstrates basic data transfer functionality with the
+ updated library. This example will display the transfer rates acheived using
+ the slower form of high-speed transfer using blocking-writes.
+ */
+
+#include <cstdlib>
+#include <iostream>
+#include <sstream>
+#include <string>
+#include <RF24/RF24.h>
+#include <unistd.h>
+
+using namespace std;
+//
+// Hardware configuration
+//
+
+/****************** Raspberry Pi ***********************/
+
+// Radio CE Pin, CSN Pin, SPI Speed
+// See http://www.airspayce.com/mikem/bcm2835/group__constants.html#ga63c029bd6500167152db4e57736d0939 and 
the related enumerations for pin information.
+
+// Setup for GPIO 22 CE and CE0 CSN with SPI Speed @ 4Mhz
+//RF24 radio(RPI_V2_GPIO_P1_22, BCM2835_SPI_CS0, BCM2835_SPI_SPEED_4MHZ);
+
+// NEW: Setup for RPi B+
+//RF24 radio(RPI_BPLUS_GPIO_J8_15,RPI_BPLUS_GPIO_J8_24, BCM2835_SPI_SPEED_8MHZ);
+
+// Setup for GPIO 15 CE and CE0 CSN with SPI Speed @ 8Mhz
+RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ);
+
+/*** RPi Alternate ***/
+//Note: Specify SPI BUS 0 or 1 instead of CS pin number.
+// See http://tmrh20.github.io/RF24/RPi.html for more information on usage
+
+//RPi Alternate, with MRAA
+//RF24 radio(15,0);
+
+//RPi Alternate, with SPIDEV - Note: Edit RF24/arch/BBB/spi.cpp and  set 'this->device = "/dev/spidev0.0";;' 
or as listed in /dev
+//RF24 radio(22,0);
+
+
+/****************** Linux (BBB,x86,etc) ***********************/
+
+// See http://tmrh20.github.io/RF24/pages.html for more information on usage
+// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
+// See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV
+
+// Setup for ARM(Linux) devices like BBB using spidev (default is "/dev/spidev1.0" )
+//RF24 radio(115,0);
+
+//BBB Alternate, with mraa
+// CE pin = (Header P9, Pin 13) = 59 = 13 + 46 
+//Note: Specify SPI BUS 0 or 1 instead of CS pin number. 
+//RF24 radio(59,0);
+
+/**************************************************************/
+
+// Radio pipe addresses for the 2 nodes to communicate.
+const uint64_t addresses[2] = {0xABCDABCD71LL, 0x544d52687CLL};
+
+uint8_t data[32];
+unsigned long startTime, stopTime, counter, rxTimer = 0;
+
+void intHandler()
+{
+    //Read as long data is available
+    //Single interrupts may be lost if a lot of data comes in.
+    while (radio.available()) {
+        radio.read(&data, 32);
+        counter++;
+    }
+}
+
+int main(int argc, char** argv)
+{
+
+    bool role_ping_out = 1, role_pong_back = 0;
+    bool role = 0;
+
+    // Print preamble:
+
+    cout << "RF24/examples/Transfer/\n";
+
+    radio.begin();                           // Setup and configure rf radio
+    radio.setChannel(1);
+    radio.setPALevel(RF24_PA_MAX);
+    radio.setDataRate(RF24_1MBPS);
+    radio.setAutoAck(1);                     // Ensure autoACK is enabled
+    radio.setRetries(2, 15);                  // Optionally, increase the delay between retries & # of 
retries
+    radio.setCRCLength(RF24_CRC_8);          // Use 8-bit CRC for performance
+    radio.printDetails();
+    /********* Role chooser ***********/
+
+    printf("\n ************ Role Setup ***********\n");
+    string input = "";
+    char myChar = {0};
+    cout << "Choose a role: Enter 0 for receiver, 1 for transmitter (CTRL+C to exit)\n>";
+    getline(cin, input);
+
+    attachInterrupt(23, INT_EDGE_FALLING, intHandler); //Attach interrupt to bcm pin 23
+
+    if (input.length() == 1) {
+        myChar = input[0];
+        if (myChar == '0') {
+            cout << "Role: Pong Back, awaiting transmission " << endl << endl;
+        } else {
+            cout << "Role: Ping Out, starting transmission " << endl << endl;
+            role = role_ping_out;
+        }
+    }
+    /***********************************/
+
+    if (role == role_ping_out) {
+        radio.openWritingPipe(addresses[1]);
+        radio.openReadingPipe(1, addresses[0]);
+        radio.stopListening();
+    } else {
+        radio.openWritingPipe(addresses[0]);
+        radio.openReadingPipe(1, addresses[1]);
+        radio.startListening();
+    }
+
+    for (int i = 0; i < 32; i++) {
+        data[i] = rand() % 255;                        //Load the buffer with random data
+    }
+
+    // forever loop
+    while (1) {
+        if (role == role_ping_out) {
+            sleep(2);
+            printf("Initiating Basic Data Transfer\n\r");
+
+            long int cycles = 10000;                    //Change this to a higher or lower number.
+
+            // unsigned long pauseTime = millis();             //Uncomment if autoAck == 1 ( NOACK )
+            startTime = millis();
+
+            for (int i = 0; i < cycles; i++) {                //Loop through a number of cycles
+                data[0] = i;                        //Change the first byte of the payload for identification
+                if (!radio.writeFast(&data, 32)) {     //Write to the FIFO buffers
+                    counter++;                      //Keep count of failed payloads
+                }
+
+
+                //This is only required when NO ACK ( enableAutoAck(0) ) payloads are used
+                /*             if(millis() - pauseTime > 3){       // Need to drop out of TX mode every 4ms 
if sending a steady stream of multicast data
+                            pauseTime = millis();
+                            radio.txStandBy();                         // This gives the PLL time to sync 
back up
+                        }
+                */
+            }
+            stopTime = millis();
+
+            if (!radio.txStandBy()) {
+                counter += 3;
+            }
+
+            float numBytes = cycles * 32;
+            float rate = numBytes / (stopTime - startTime);
+
+            printf("Transfer complete at %.2f KB/s \n\r", rate);
+            printf("%lu of %lu Packets Failed to Send\n\r", counter, cycles);
+            counter = 0;
+
+        }
+
+        if (role == role_pong_back) {
+            if (millis() - rxTimer > 1000) {
+                rxTimer = millis();
+                printf("Rate: ");
+                float numBytes = counter * 32;
+                printf("%.2f KB/s \n\r", numBytes / 1000);
+                printf("Payload Count: %lu \n\r", counter);
+                counter = 0;
+            }
+            delay(2);
+        }
+
+    } // loop
+} // main
diff --git a/arduino/libraries/RF24/examples_linux/manualAcknowledgements.cpp 
b/arduino/libraries/RF24/examples_linux/manualAcknowledgements.cpp
new file mode 100644
index 000000000..9f9f45de5
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/manualAcknowledgements.cpp
@@ -0,0 +1,259 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty (2bndy5)
+ */
+
+/**
+ * A simple example of sending data from 1 nRF24L01 transceiver to another
+ * with manually transmitted (non-automatic) Acknowledgement (ACK) payloads.
+ * This example still uses ACK packets, but they have no payloads. Instead the
+ * acknowledging response is sent with `write()`. This tactic allows for more
+ * updated acknowledgement payload data, where actual ACK payloads' data are
+ * outdated by 1 transmission because they have to loaded before receiving a
+ * transmission.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use `ctrl+c` to quit at any time.
+ */
+#include <ctime>       // time()
+#include <iostream>    // cin, cout, endl
+#include <string>      // string, getline()
+#include <time.h>      // CLOCK_MONOTONIC_RAW, timespec, clock_gettime()
+#include <RF24/RF24.h> // RF24, RF24_PA_LOW, delay()
+
+using namespace std;
+
+/****************** Linux ***********************/
+// Radio CE Pin, CSN Pin, SPI Speed
+// CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use their own pin numbering
+// CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
+// ie: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
+
+// Generic:
+RF24 radio(22, 0);
+/****************** Linux (BBB,x86,etc) ***********************/
+// See http://nRF24.github.io/RF24/pages.html for more information on usage
+// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
+// See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV
+
+// For this example, we'll be using a payload containing
+// a string & an integer number that will be incremented
+// on every successful transmission.
+// Make a data structure to store the entire payload of different datatypes
+struct PayloadStruct {
+  char message[7];          // only using 6 characters for TX & RX payloads
+  uint8_t counter;
+};
+PayloadStruct payload;
+
+void setRole(); // prototype to set the node's role
+void master();  // prototype of the TX node's behavior
+void slave();   // prototype of the RX node's behavior
+
+// custom defined timer for evaluating transmission time in microseconds
+struct timespec startTimer, endTimer;
+uint32_t getMicros(); // prototype to get ellapsed time in microseconds
+
+
+int main(int argc, char** argv) {
+
+    // perform hardware check
+    if (!radio.begin()) {
+        cout << "radio hardware is not responding!!" << endl;
+        return 0; // quit now
+    }
+
+    // append a NULL terminating 0 for printing as a c-string
+    payload.message[6] = 0;
+
+    // Let these addresses be used for the pair of nodes used in this example
+    uint8_t address[2][6] = {"1Node", "2Node"};
+    //             the TX address^ ,  ^the RX address
+    // It is very helpful to think of an address as a path instead of as
+    // an identifying device destination
+
+    // to use different addresses on a pair of radios, we need a variable to
+    // uniquely identify which address this radio will use to transmit
+    bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+    // print example's name
+    cout << argv[0] << endl;
+
+    // Set the radioNumber via the terminal on startup
+    cout << "Which radio is this? Enter '0' or '1'. Defaults to '0' ";
+    string input;
+    getline(cin, input);
+    radioNumber = input.length() > 0 && (uint8_t)input[0] == 49;
+
+    // Set the PA Level low to try preventing power supply related problems
+    // because these examples are likely run with nodes in close proximity to
+    // each other.
+    radio.setPALevel(RF24_PA_LOW);  // RF24_PA_MAX is default.
+
+    // save on transmission time by setting the radio to only transmit the
+    // number of bytes we need to transmit a float
+    radio.setPayloadSize(sizeof(payload)); // char[7] & uint8_t datatypes occupy 8 bytes
+
+    // set the TX address of the RX node into the TX pipe
+    radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0
+
+    // set the RX address of the TX node into a RX pipe
+    radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+    // For debugging info
+    // radio.printDetails();       // (smaller) function that prints raw register values
+    // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+    // ready to execute program now
+    setRole(); // calls master() or slave() based on user input
+    return 0;
+} // main
+
+
+/**
+ * set this node's role from stdin stream.
+ * this only considers the first char as input.
+ */
+void setRole() {
+    string input = "";
+    while (!input.length()) {
+        cout << "*** PRESS 'T' to begin transmitting to the other node\n";
+        cout << "*** PRESS 'R' to begin receiving from the other node\n";
+        cout << "*** PRESS 'Q' to exit" << endl;
+        getline(cin, input);
+        if (input.length() >= 1) {
+            if (input[0] == 'T' || input[0] == 't')
+                master();
+            else if (input[0] == 'R' || input[0] == 'r')
+                slave();
+            else if (input[0] == 'Q' || input[0] == 'q')
+                break;
+            else
+                cout << input[0] << " is an invalid input. Please try again." << endl;
+        }
+        input = ""; // stay in the while loop
+    } // while
+} // setRole()
+
+
+/**
+ * make this node act as the transmitter
+ */
+void master() {
+
+    memcpy(payload.message, "Hello ", 6); // set the outgoing message
+    radio.stopListening();                // put in TX mode
+
+    unsigned int failures = 0;                                // keep track of failures
+    while (failures < 6) {
+        clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);      // start the timer
+        bool report = radio.write(&payload, sizeof(payload)); // transmit & save the report
+
+        if (report) {
+            // transmission successful; wait for response and print results
+
+            radio.startListening();                          // put in RX mode
+            unsigned long start_timeout = millis();          // timer to detect no response
+            while (!radio.available()) {                     // wait for response
+                if (millis() - start_timeout > 200)          // only wait 200 ms
+                    break;
+            }
+            unsigned long ellapsedTime = getMicros();        // end the timer
+            radio.stopListening();                           // put back in TX mode
+
+            // print summary of transactions
+            uint8_t pipe;
+            cout << "Transmission successful! ";
+            if (radio.available(&pipe)) {                    // is there a payload received? grab the pipe 
number that received it
+                uint8_t bytes = radio.getPayloadSize();      // grab the incoming payload size
+                cout << "Round trip delay = ";
+                cout << ellapsedTime;                        // print the timer result
+                cout << " us. Sent: " << payload.message;    // print outgoing message
+                cout << (unsigned int)payload.counter;       // print outgoing counter
+                PayloadStruct received;
+                radio.read(&received, sizeof(received));     // get incoming payload
+                cout << " Recieved " << (unsigned int)bytes; // print incoming payload size
+                cout << " on pipe " << (unsigned int)pipe;   // print RX pipe number
+                cout << ": " << received.message;            // print the incoming message
+                cout << (unsigned int)received.counter;      // print the incoming counter
+                cout << endl;
+                payload.counter = received.counter;          // save incoming counter for next outgoing 
counter
+            }
+            else {
+                cout << "Recieved no response." << endl;     // no response received
+            }
+        }
+        else {
+            cout << "Transmission failed or timed out";      // payload was not delivered
+            cout << endl;
+            failures++;                                      // increment failure counter
+        } // report
+
+        // to make this example readable in the terminal
+        delay(1000);  // slow transmissions down by 1 second
+    } // while
+
+    cout << failures << " failures detected. Leaving TX role." << endl;
+} // master
+
+
+/**
+ * make this node act as the receiver
+ */
+void slave() {
+    memcpy(payload.message, "World ", 6);                    // set the response message
+    radio.startListening();                                  // put in RX mode
+
+    time_t startTimer = time(nullptr);                       // start a timer
+    while (time(nullptr) - startTimer < 6) {                 // use 6 second timeout
+        uint8_t pipe;
+        if (radio.available(&pipe)) {                        // is there a payload? get the pipe number that 
recieved it
+            uint8_t bytes = radio.getPayloadSize();          // get size of incoming payload
+            PayloadStruct received;
+            radio.read(&received, sizeof(received));         // get incoming payload
+            payload.counter = received.counter + 1;          // increment payload for response
+
+            // transmit response & save result to `report`
+            radio.stopListening();                           // put in TX mode
+            radio.writeFast(&payload, sizeof(payload));      // load response into TX FIFO
+            bool report = radio.txStandBy(150);              // keep retrying for 150 ms
+            radio.startListening();                          // put back in RX mode
+
+            // print summary of transactions
+            cout << "Received " << (unsigned int)bytes;      // print the size of the payload
+            cout << " bytes on pipe ";
+            cout << (unsigned int)pipe;                      // print the pipe number
+            cout << ": " << received.message;                // print incoming message
+            cout << (unsigned int)received.counter;          // print incoming counter
+
+            if (report) {
+                cout << " Sent: " << payload.message;        // print outgoing message
+                cout << (unsigned int)payload.counter;       // print outgoing counter
+                cout << endl;
+            }
+            else {
+                cout << " Response failed to send." << endl; // failed to send response
+            }
+            startTimer = time(nullptr);                      // reset timer
+        } // available
+    } // while
+
+    cout << "Nothing received in 6 seconds. Leaving RX role." << endl;
+    radio.stopListening(); // recommended idle mode is TX mode
+} // slave
+
+
+/**
+ * Calculate the ellapsed time in microseconds
+ */
+uint32_t getMicros() {
+    // this function assumes that the timer was started using
+    // `clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);`
+
+    clock_gettime(CLOCK_MONOTONIC_RAW, &endTimer);
+    uint32_t seconds = endTimer.tv_sec - startTimer.tv_sec;
+    uint32_t useconds = (endTimer.tv_nsec - startTimer.tv_nsec) / 1000;
+
+    return ((seconds) * 1000 + useconds) + 0.5;
+}
diff --git a/arduino/libraries/RF24/examples_linux/manual_acknowledgements.py 
b/arduino/libraries/RF24/examples_linux/manual_acknowledgements.py
new file mode 100644
index 000000000..e0f79687a
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/manual_acknowledgements.py
@@ -0,0 +1,254 @@
+"""
+A simple example of sending data from 1 nRF24L01 transceiver to another
+with manually transmitted (non-automatic) Acknowledgement (ACK) payloads.
+This example still uses ACK packets, but they have no payloads. Instead the
+acknowledging response is sent with `write()`. This tactic allows for more
+updated acknowledgement payload data, where actual ACK payloads' data are
+outdated by 1 transmission because they have to loaded before receiving a
+transmission.
+
+This example was written to be used on 2 devices acting as 'nodes'.
+"""
+import sys
+import argparse
+import time
+import struct
+from RF24 import RF24, RF24_PA_LOW
+
+
+parser = argparse.ArgumentParser(
+    description=__doc__,
+    formatter_class=argparse.RawDescriptionHelpFormatter
+)
+parser.add_argument(
+    "-n",
+    "--node",
+    type=int,
+    choices=range(2),
+    help="the identifying radio number (or node ID number)"
+)
+parser.add_argument(
+    "-r",
+    "--role",
+    type=int,
+    choices=range(2),
+    help="'1' specifies the TX role. '0' specifies the RX role."
+)
+
+########### USER CONFIGURATION ###########
+# See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md
+# Radio CE Pin, CSN Pin, SPI Speed
+# CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use
+# their own pin numbering
+# CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
+# ie: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
+
+# Generic:
+radio = RF24(22, 0)
+################## Linux (BBB,x86,etc) #########################
+# See http://nRF24.github.io/RF24/pages.html for more information on usage
+# See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
+# See https://www.kernel.org/doc/Documentation/spi/spidev for more
+# information on SPIDEV
+
+# using the python keyword global is bad practice. Instead we'll use a 1 item
+# list to store our integer number for the payloads' counter
+counter = [0]
+
+
+def master():
+    """Transmits a message and an incrementing integer every second, then
+    wait for a response for up to 200 ms.
+    """
+    radio.stopListening()  # put radio in TX mode
+    failures = 0
+    while failures < 6:
+        # use bytes() to pack our counter data into the payload
+        # NOTE b"\x00" byte is a c-string's NULL terminating 0
+        buffer = b"Hello \x00" + bytes(counter)
+        start_timer = time.monotonic_ns()  # start timer
+        result = radio.write(buffer)
+        if not result:
+            failures += 1
+            print("Transmission failed or timed out")
+        else:
+            radio.startListening()  # put radio in RX mode
+            timout = time.monotonic() * 1000 + 200  # use 200 ms timeout
+            # declare a variable to save the incoming response
+            while not radio.available() and time.monotonic() * 1000 < timout:
+                pass  # wait for incoming payload or timeout
+            radio.stopListening()  # put radio in TX mode
+            end_timer = time.monotonic_ns()  # end timer
+            print(
+                "Transmission successful. Sent: {}{}.".format(
+                    buffer[:6].decode("utf-8"),
+                    counter[0]
+                ),
+                end=" "
+            )
+            has_payload, pipe_number = radio.available_pipe()
+            if has_payload:
+                # grab the incoming payload
+                received = radio.read(radio.payloadSize)
+                # NOTE received[7:8] discards NULL terminating 0
+                counter[0] = received[7:8][0]  # save the counter
+                print(
+                    "Received {} bytes on pipe {}: {}{}. "
+                    "Round-trip delay: {} us.".format(
+                        radio.payloadSize,
+                        pipe_number,
+                        bytes(received[:6]).decode("utf-8"),
+                        counter[0],
+                        (end_timer - start_timer) / 1000
+                    )
+                )
+            else:
+                print("No response received.")
+        time.sleep(1)  # make example readable by slowing down transmissions
+    print(failures, "failures detected. Leaving TX role.")
+
+
+def slave(timeout=6):
+    """Listen for any payloads and print the transaction
+
+    :param int timeout: The number of seconds to wait (with no transmission)
+        until exiting function.
+    """
+    radio.startListening()  # put radio in RX mode
+
+    start_timer = time.monotonic()  # start a timer to detect timeout
+    while (time.monotonic() - start_timer) < timeout:
+        # receive `count` payloads or wait 6 seconds till timing out
+        has_payload, pipe_number = radio.available_pipe()
+        if has_payload:
+            received = radio.read(radio.payloadSize)  # fetch the payload
+            # NOTE received[7:8] discards NULL terminating 0
+            # increment the counter from received payload
+            counter[0] = received[7:8][0] + 1 if received[7:8][0] < 255 else 0
+            # use bytes() to pack our counter data into the payload
+            # NOTE b"\x00" byte is a c-string's NULL terminating 0
+            buffer = b"World \x00" + bytes(counter)
+            radio.stopListening()  # put radio in TX mode
+            radio.writeFast(buffer)  # load response into TX FIFO
+            # keep retrying to send response for 150 milliseconds
+            result = radio.txStandBy(150)  # save response's result
+            # NOTE txStandBy() flushes TX FIFO on transmission failure
+            radio.startListening()  # put radio back in RX mode
+            # print the payload received payload
+            print(
+                "Received {} bytes on pipe {}: {}{}.".format(
+                    radio.payloadSize,
+                    pipe_number,
+                    bytes(received[:6]).decode("utf-8"),
+                    received[7:8][0]
+                ),
+                end=" "
+            )
+            if result:  # did response succeed?
+                # print response's payload
+                print(
+                    "Sent: {}{}".format(
+                        buffer[:6].decode("utf-8"),
+                        counter[0]
+                    )
+                )
+            else:
+                print("Response failed or timed out")
+            start_timer = time.monotonic()  # reset the timeout timer
+
+    print("Nothing received in", timeout, "seconds. Leaving RX role")
+    # recommended behavior is to keep in TX mode while idle
+    radio.stopListening()  # put the radio in TX mode
+
+
+def set_role():
+    """Set the role using stdin stream. Timeout arg for slave() can be
+    specified using a space delimiter (e.g. 'R 10' calls `slave(10)`)
+
+    :return:
+        - True when role is complete & app should continue running.
+        - False when app should exit
+    """
+    user_input = input(
+        "*** Enter 'R' for receiver role.\n"
+        "*** Enter 'T' for transmitter role.\n"
+        "*** Enter 'Q' to quit example.\n"
+    ) or "?"
+    user_input = user_input.split()
+    if user_input[0].upper().startswith("R"):
+        if len(user_input) > 1:
+            slave(int(user_input[1]))
+        else:
+            slave()
+        return True
+    elif user_input[0].upper().startswith("T"):
+        master()
+        return True
+    elif user_input[0].upper().startswith("Q"):
+        radio.powerDown()
+        return False
+    print(user_input[0], "is an unrecognized input. Please try again.")
+    return set_role()
+
+
+if __name__ == "__main__":
+
+    args = parser.parse_args()  # parse any CLI args
+
+    # initialize the nRF24L01 on the spi bus
+    if not radio.begin():
+        raise RuntimeError("radio hardware is not responding")
+
+    # For this example, we will use different addresses
+    # An address need to be a buffer protocol object (bytearray)
+    address = [b"1Node", b"2Node"]
+    # It is very helpful to think of an address as a path instead of as
+    # an identifying device destination
+
+    print(sys.argv[0])  # print example name
+
+    # to use different addresses on a pair of radios, we need a variable to
+    # uniquely identify which address this radio will use to transmit
+    # 0 uses address[0] to transmit, 1 uses address[1] to transmit
+    radio_number = args.node  # uses default value from `parser`
+    if args.node is None:  # if '--node' arg wasn't specified
+        radio_number = bool(
+            int(
+                input(
+                    "Which radio is this? Enter '0' or '1'. Defaults to '0' "
+                ) or 0
+            )
+        )
+
+    # set the Power Amplifier level to -12 dBm since this test example is
+    # usually run with nRF24L01 transceivers in close proximity of each other
+    radio.setPALevel(RF24_PA_LOW)  # RF24_PA_MAX is default
+
+    # set the TX address of the RX node into the TX pipe
+    radio.openWritingPipe(address[radio_number])  # always uses pipe 0
+
+    # set the RX address of the TX node into a RX pipe
+    radio.openReadingPipe(1, address[not radio_number])  # using pipe 1
+
+    # To save time during transmission, we'll set the payload size to be only
+    # what we need. For this example, we'll be using a byte for the
+    # payload counter and 7 bytes for the payload message
+    radio.payloadSize = 8
+
+    # for debugging, we have 2 options that print a large block of details
+    # (smaller) function that prints raw register values
+    # radio.printDetails()
+    # (larger) function that prints human readable data
+    # radio.printPrettyDetails()
+
+    try:
+        if args.role is None:  # if not specified with CLI arg '-r'
+            while set_role():
+                pass  # continue example until 'Q' is entered
+        else:  # if role was set using CLI args
+            # run role once and exit
+            master() if bool(args.role) else slave()
+    except KeyboardInterrupt:
+        print(" Keyboard Interrupt detected. Exiting...")
+        radio.powerDown()
+        sys.exit()
diff --git a/arduino/libraries/RF24/examples_linux/multiceiverDemo.cpp 
b/arduino/libraries/RF24/examples_linux/multiceiverDemo.cpp
new file mode 100644
index 000000000..58bab7aed
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/multiceiverDemo.cpp
@@ -0,0 +1,273 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty (2bndy5)
+ */
+
+/**
+ * A simple example of sending data from as many as 6 nRF24L01 transceivers to
+ * 1 receiving transceiver. This technique is trademarked by
+ * Nordic Semiconductors as "MultiCeiver".
+ *
+ * This example was written to be used on up to 6 devices acting as TX nodes &
+ * only 1 device acting as the RX node (that's a maximum of 7 devices).
+ * Use `ctrl+c` to quit at any time.
+ */
+#include <ctime>       // time()
+#include <cstring>     // strcmp()
+#include <iostream>    // cin, cout, endl
+#include <string>      // string, getline()
+#include <time.h>      // CLOCK_MONOTONIC_RAW, timespec, clock_gettime()
+#include <RF24/RF24.h> // RF24, RF24_PA_LOW, delay()
+
+using namespace std;
+
+/****************** Linux ***********************/
+// Radio CE Pin, CSN Pin, SPI Speed
+// CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use their own pin numbering
+// CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
+// ie: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
+
+// Generic:
+RF24 radio(22, 0);
+/****************** Linux (BBB,x86,etc) ***********************/
+// See http://nRF24.github.io/RF24/pages.html for more information on usage
+// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
+// See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV
+
+
+// For this example, we'll be using 6 addresses; 1 for each TX node
+// It is very helpful to think of an address as a path instead of as
+// an identifying device destination
+// Notice that the last byte is the only byte that changes in the last 5
+// addresses. This is a limitation of the nRF24L01 transceiver for pipes 2-5
+// because they use the same first 4 bytes from pipe 1.
+uint64_t address[6] = {0x7878787878LL,
+                       0xB3B4B5B6F1LL,
+                       0xB3B4B5B6CDLL,
+                       0xB3B4B5B6A3LL,
+                       0xB3B4B5B60FLL,
+                       0xB3B4B5B605LL};
+
+
+// For this example, we'll be using a payload containing
+// a node ID number and a single integer number that will be incremented
+// on every successful transmission.
+// Make a data structure to use as a payload.
+struct PayloadStruct
+{
+  unsigned int nodeID;
+  unsigned int payloadID;
+};
+PayloadStruct payload;
+
+void setRole();            // prototype to set the node's role
+void master(unsigned int); // prototype of a TX node's behavior
+void slave();              // prototype of the RX node's behavior
+void printHelp(string);    // prototype to function that explain CLI arg usage
+
+// custom defined timer for evaluating transmission time in microseconds
+struct timespec startTimer, endTimer;
+uint32_t getMicros(); // prototype to get ellapsed time in microseconds
+
+
+int main(int argc, char** argv) {
+
+    // perform hardware check
+    if (!radio.begin()) {
+        cout << "radio hardware is not responding!!" << endl;
+        return 0; // quit now
+    }
+
+    // to use different addresses on a pair of radios, we need a variable to
+    // uniquely identify which address this radio will use to transmit
+    unsigned int nodeNumber = 'R'; // integers 0-5 = TX node; character 'R' or integer 82 = RX node
+
+    bool foundArgNode = false;
+    if (argc > 1) {
+        if ((argc - 1) != 2) {
+            // CLI arg "-n"/"--node" needs an option specified for it
+            // only 1 arg is expected, so only traverse the first "--arg option" pair
+            printHelp(string(argv[0]));
+            return 0;
+        }
+        else if (strcmp(argv[1], "-n") == 0 || strcmp(argv[1], "--node") == 0) {
+            // "-n" or "--node" has been specified
+            foundArgNode = true;
+            if ((argv[2][0] - 48) < 6 && (argv[2][0] - 48) >= 0) {
+                nodeNumber = argv[2][0] - 48;
+            }
+            else if (argv[2][0] == 'R' || argv[2][0] == 'r') {
+                nodeNumber = 'R';
+            }
+            else {
+                printHelp(string(argv[0]));
+                return 0;
+            }
+        }
+        else {
+            // "-n"/"--node" arg was not specified
+            printHelp(string(argv[0]));
+            return 0;
+        }
+    }
+
+    // print example's name
+    cout << argv[0] << endl;
+
+    // Set the PA Level low to try preventing power supply related problems
+    // because these examples are likely run with nodes in close proximity to
+    // each other.
+    radio.setPALevel(RF24_PA_LOW);         // RF24_PA_MAX is default.
+
+    // save on transmission time by setting the radio to only transmit the
+    // number of bytes we need to transmit a float
+    radio.setPayloadSize(sizeof(payload)); // 2x int datatype occupy 8 bytes
+
+    // For debugging info
+    // radio.printDetails();       // (smaller) function that prints raw register values
+    // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+    // ready to execute program now
+    if (!foundArgNode) {
+        setRole(); // calls master() or slave() based on user input
+    }
+    else {
+        nodeNumber < 6 ? master(nodeNumber) : slave();
+    }
+    return 0;
+}
+
+
+/**
+ * set this node's role from stdin stream.
+ * this only considers the first char as input.
+ */
+void setRole() {
+
+    string input = "";
+    while (!input.length()) {
+        cout << "*** Enter a number between 0 and 5 (inclusive) to act as\n";
+        cout << "    a unique node number that transmits to the RX node.\n";
+        cout << "*** PRESS 'R' to begin receiving from the other nodes\n";
+        cout << "*** PRESS 'Q' to exit" << endl;
+        getline(cin, input);
+        if (input.length() >= 1) {
+            unsigned int toNumber = (unsigned int)(input[0]) - 48;
+            if (toNumber < 6 && toNumber >= 0)
+                master(toNumber);
+            else if (input[0] == 'R' || input[0] == 'r')
+                slave();
+            else if (input[0] == 'Q' || input[0] == 'q')
+                break;
+            else
+                cout << input[0] << " is an invalid input. Please try again." << endl;
+        }
+        input = ""; // stay in the while loop
+    } // while
+} // setRole
+
+
+/**
+ * act as unique TX node identified by the `role` number
+ */
+void master(unsigned int role) {
+    // set the payload's nodeID & reset the payload's identifying number
+    payload.nodeID = role;
+    payload.payloadID = 0;
+
+    // Set the address on pipe 0 to the RX node.
+    radio.stopListening(); // put radio in TX mode
+    radio.openWritingPipe(address[role]);
+
+    // According to the datasheet, the auto-retry features's delay value should
+    // be "skewed" to allow the RX node to receive 1 transmission at a time.
+    // So, use varying delay between retry attempts and 15 (at most) retry attempts
+    radio.setRetries(((role * 3) % 12) + 3, 15); // maximum value is 15 for both args
+
+    unsigned int failures = 0;
+    while (failures < 6) {
+        clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);       // start the timer
+        bool report = radio.write(&payload, sizeof(payload));  // transmit & save the report
+        uint32_t timerEllapsed = getMicros();                  // end the timer
+
+        if (report) {
+            // payload was delivered
+            cout << "Transmission of PayloadID ";
+            cout << payload.payloadID;                         // print payload number
+            cout << " as node " << payload.nodeID;             // print node number
+            cout << " successful! Time to transmit = ";
+            cout << timerEllapsed << " us" << endl;            // print the timer result
+        }
+        else {
+            // payload was not delivered
+            failures++;
+            cout << "Transmission failed or timed out" << endl;
+        }
+        payload.payloadID++;                                   // increment payload number
+
+        // to make this example readable in the terminal
+        delay(1000); // slow transmissions down by 1 second
+    } // while
+    cout << failures << " failures detected. Leaving TX role." << endl;
+} // master
+
+
+/**
+ * act as the RX node that receives from up to 6 other TX nodes
+ */
+void slave() {
+
+    // Set the addresses for all pipes to TX nodes
+    for (uint8_t i = 0; i < 6; ++i)
+      radio.openReadingPipe(i, address[i]);
+
+    radio.startListening();                                        // put radio in RX mode
+
+    time_t startTimer = time(nullptr);                             // start a timer
+    while (time(nullptr) - startTimer < 6) {                       // use 6 second timeout
+        uint8_t pipe;
+        if (radio.available(&pipe)) {                              // is there a payload? get the pipe 
number that recieved it
+            uint8_t bytes = radio.getPayloadSize();                // get the size of the payload
+            radio.read(&payload, bytes);                           // fetch payload from FIFO
+            cout << "Received " << (unsigned int)bytes;            // print the size of the payload
+            cout << " bytes on pipe " << (unsigned int)pipe;       // print the pipe number
+            cout << " from node " << payload.nodeID;               // print the payload's origin
+            cout << ". PayloadID: " << payload.payloadID << endl;  // print the payload's number
+            startTimer = time(nullptr);                            // reset timer
+        }
+    }
+    cout << "Nothing received in 6 seconds. Leaving RX role." << endl;
+} // slave
+
+
+/**
+ * Calculate the ellapsed time in microseconds
+ */
+uint32_t getMicros() {
+    // this function assumes that the timer was started using
+    // `clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);`
+
+    clock_gettime(CLOCK_MONOTONIC_RAW, &endTimer);
+    uint32_t seconds = endTimer.tv_sec - startTimer.tv_sec;
+    uint32_t useconds = (endTimer.tv_nsec - startTimer.tv_nsec) / 1000;
+
+    return ((seconds) * 1000 + useconds) + 0.5;
+}
+
+
+/**
+ * print a manual page of instructions on how to use this example's CLI args
+ */
+void printHelp(string progName) {
+    cout << "usage: " << progName << " [-h] [-n {0,1,2,3,4,5,r,R}]\n\n"
+         << "A simple example of sending data from as many as 6 nRF24L01 transceivers to\n"
+         << "1 receiving transceiver. This technique is trademarked by\n"
+         << "Nordic Semiconductors as 'MultiCeiver'.\n"
+         << "\nThis example was written to be used on up to 6 devices acting as TX nodes with\n"
+         << "another device acting as a RX node (that's a total of 7 devices).\n"
+         << "\noptional arguments:\n  -h, --help\t\tshow this help message and exit\n"
+         << "  -n {0,1,2,3,4,5,r,R}, --node {0,1,2,3,4,5,r,R}"
+         << "\n\t\t\t0-5 specifies the identifying node ID number for the TX role."
+         << "\n\t\t\t'r' or 'R' specifies the RX role." << endl;
+}
diff --git a/arduino/libraries/RF24/examples_linux/multiceiver_demo.py 
b/arduino/libraries/RF24/examples_linux/multiceiver_demo.py
new file mode 100644
index 000000000..1209dc81d
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/multiceiver_demo.py
@@ -0,0 +1,204 @@
+"""
+A simple example of sending data from as many as 6 nRF24L01 transceivers to
+1 receiving transceiver. This technique is trademarked by
+Nordic Semiconductors as "MultiCeiver".
+
+This example was written to be used on up to 6 devices acting as TX nodes &
+only 1 device acting as the RX node (that's a maximum of 7 devices).
+"""
+import sys
+import argparse
+import time
+import struct
+from RF24 import RF24, RF24_PA_LOW
+
+
+parser = argparse.ArgumentParser(
+    description=__doc__,
+    formatter_class=argparse.RawDescriptionHelpFormatter
+)
+parser.add_argument(
+    "-n",
+    "--node",
+    choices=("0", "1", "2", "3", "4", "5", "R", "r"),
+    help="the identifying node ID number for the TX role. "
+         "Use 'R' or 'r' to specify the RX role"
+)
+
+########### USER CONFIGURATION ###########
+# See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md
+# Radio CE Pin, CSN Pin, SPI Speed
+# CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use
+# their own pin numbering
+# CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
+# ie: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
+
+# Generic:
+radio = RF24(22, 0)
+################## Linux (BBB,x86,etc) #########################
+# See http://nRF24.github.io/RF24/pages.html for more information on usage
+# See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
+# See https://www.kernel.org/doc/Documentation/spi/spidev for more
+# information on SPIDEV
+
+# setup the addresses for all transmitting radio nodes
+addresses = [
+    b"\x78" * 5,
+    b"\xF1\xB6\xB5\xB4\xB3",
+    b"\xCD\xB6\xB5\xB4\xB3",
+    b"\xA3\xB6\xB5\xB4\xB3",
+    b"\x0F\xB6\xB5\xB4\xB3",
+    b"\x05\xB6\xB5\xB4\xB3"
+]
+# It is very helpful to think of an address as a path instead of as
+# an identifying device destination
+
+
+def master(node_number):
+    """start transmitting to the base station.
+
+    :param int node_number: the node's identifying index (from the
+        the `addresses` list). This is a required parameter
+    """
+    # According to the datasheet, the auto-retry features's delay value should
+    # be "skewed" to allow the RX node to receive 1 transmission at a time.
+    # So, use varying delay between retry attempts and 15 (at most) retry attempts
+    radio.setRetries(((node_number * 3) % 12) + 3, 15) # maximum value is 15 for both args
+
+    radio.stopListening()  # put radio in TX mode
+    # set the TX address to the address of the base station.
+    radio.openWritingPipe(addresses[node_number])
+    counter = 0
+    failures = 0
+    while failures < 6:
+        counter += 1
+        # payloads will include the node_number and a payload ID character
+        payload = struct.pack("<ii", node_number, counter)
+        start_timer = time.monotonic_ns()
+        report = radio.write(payload)
+        end_timer = time.monotonic_ns()
+        # show something to see it isn't frozen
+        print(
+            "Transmission of payloadID {} as node {}".format(
+                counter,
+                node_number
+            ),
+            end=" "
+        )
+        if report:
+            print(
+                "successfull! Time to transmit = {} us".format(
+                    (end_timer - start_timer) / 1000
+                )
+            )
+        else:
+            failures += 1
+            print("failed or timed out")
+        time.sleep(1)  # slow down the test for readability
+    print(failures, "failures detected. Leaving TX role.")
+
+
+def slave(timeout=10):
+    """Use the radio as a base station for lisening to all nodes
+
+    :param int timeout: The number of seconds to wait (with no transmission)
+        until exiting function.
+    """
+    # write the addresses to all pipes.
+    for pipe_n, addr in enumerate(addresses):
+        radio.openReadingPipe(pipe_n, addr)
+    radio.startListening()  # put radio in RX mode
+    start_timer = time.monotonic()  # start timer
+    while time.monotonic() - start_timer < timeout:
+        has_payload, pipe_number = radio.available_pipe()
+        if has_payload:
+            # unpack payload
+            nodeID, payloadID = struct.unpack(
+                "<ii",
+                radio.read(radio.payloadSize)
+            )
+            # show the pipe number that received the payload
+            print(
+                "Received {} bytes on pipe {} from node {}. PayloadID: "
+                "{}".format(
+                    radio.payloadSize,
+                    pipe_number,
+                    nodeID,
+                    payloadID
+                )
+            )
+            start_timer = time.monotonic()  # reset timer with every payload
+
+    print("Nothing received in", timeout, "seconds. Leaving RX role")
+    radio.stopListening()
+
+
+def set_role():
+    """Set the role using stdin stream. Timeout arg for slave() can be
+    specified using a space delimiter (e.g. 'R 10' calls `slave(10)`)
+
+    :return:
+        - True when role is complete & app should continue running.
+        - False when app should exit
+    """
+    user_input = input(
+        "*** Enter 'R' for receiver role.\n"
+        "*** Enter a number in range [0, 5] to use a specific node ID for "
+        "transmitter role.\n"
+        "*** Enter 'Q' to quit example.\n"
+    ) or "?"
+    user_input = user_input.split()
+    if user_input[0].upper().startswith("R"):
+        if len(user_input) > 1:
+            slave(int(user_input[1]))
+        else:
+            slave()
+        return True
+    elif user_input[0].isdigit() and 0 <= int(user_input[0]) <= 5:
+        master(int(user_input[0]))
+        return True
+    elif user_input[0].upper().startswith("Q"):
+        radio.powerDown()
+        return False
+    print(user_input[0], "is an unrecognized input. Please try again.")
+    return set_role()
+
+
+if __name__ == "__main__":
+
+    args = parser.parse_args()  # parse any CLI args
+
+    # initialize the nRF24L01 on the spi bus
+    if not radio.begin():
+        raise RuntimeError("radio hardware is not responding")
+
+    print(sys.argv[0])  # print example name
+
+    # set the Power Amplifier level to -12 dBm since this test example is
+    # usually run with nRF24L01 transceivers in close proximity of each other
+    radio.setPALevel(RF24_PA_LOW)  # RF24_PA_MAX is default
+
+    # To save time during transmission, we'll set the payload size to be only what
+    # we need.
+    # 2 int occupy 8 bytes in memory using len(struct.pack())
+    # "<ii" means 2x little endian unsigned int
+    radio.payloadSize = len(struct.pack("<ii", 0, 0))
+
+    # for debugging, we have 2 options that print a large block of details
+    # radio.printDetails();  # (smaller) function that prints raw register values
+    # radio.printPrettyDetails();  # (larger) function that prints human readable data
+
+    try:
+        if args.node is None:  # if not specified with CLI arg '-n'
+            while set_role():
+                pass  # continue example until 'Q' is entered
+        else:  # if role was set using CLI args
+            # run role once and exit
+            if args.node.isdigit():
+                master(int(args.node))
+            else:
+                slave()
+    except KeyboardInterrupt:
+        print(" Keyboard Interrupt detected. Exiting...")
+        radio.powerDown()
+        sys.exit()
diff --git a/arduino/libraries/RF24/examples_linux/readme.md b/arduino/libraries/RF24/examples_linux/readme.md
new file mode 100644
index 000000000..4728bd321
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/readme.md
@@ -0,0 +1,3 @@
+Note: These examples were originally designed for RPi, but should work on any supported Linux platform, with 
the proper pin configuration.
+
+See http://tmrh20.github.io/RF24 for more information
\ No newline at end of file
diff --git a/arduino/libraries/RF24/examples_linux/scanner.cpp 
b/arduino/libraries/RF24/examples_linux/scanner.cpp
new file mode 100644
index 000000000..7a3be9744
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/scanner.cpp
@@ -0,0 +1,131 @@
+/*
+ Copyright (C) 2011 J. Coliz <maniacbug ymail com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+
+
+ 03/17/2013 : Charles-Henri Hallard (http://hallard.me)
+              Modified to use with Arduipi board http://hallard.me/arduipi
+                                                 Changed to use modified bcm2835 and RF24 library
+
+ */
+
+/**
+ * Channel scanner
+ *
+ * Example to detect interference on the various channels available.
+ * This is a good diagnostic tool to check whether you're picking a
+ * good channel for your application.
+ *
+ * Inspired by cpixip.
+ * See http://arduino.cc/forum/index.php/topic,54795.0.html
+ */
+
+#include <cstdlib>
+#include <iostream>
+#include <RF24/RF24.h>
+
+using namespace std;
+
+/****************** Linux ***********************/
+// Radio CE Pin, CSN Pin, SPI Speed
+// CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use their own pin numbering
+// CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
+// ie: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
+
+// Generic:
+RF24 radio(22, 0);
+/****************** Linux (BBB,x86,etc) ***********************/
+// See http://nRF24.github.io/RF24/pages.html for more information on usage
+// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
+// See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV
+
+// Channel info
+const uint8_t num_channels = 126;
+uint8_t values[num_channels];
+
+const int num_reps = 100;
+int reset_array = 0;
+
+int main(int argc, char** argv)
+{
+    // Print preamble
+
+    // print example's name
+    printf("%s", argv[0]);
+
+    //
+    // Setup and configure rf radio
+    //
+    radio.begin();
+
+    radio.setAutoAck(false);
+
+    // Get into standby mode
+    radio.startListening();
+    radio.stopListening();
+
+    radio.printDetails();
+
+    // Print out header, high then low digit
+    int i = 0;
+
+    while (i < num_channels) {
+        printf("%x", i >> 4);
+        ++i;
+    }
+    printf("\n");
+
+    i = 0;
+    while (i < num_channels) {
+        printf("%x", i & 0xf);
+        ++i;
+    }
+    printf("\n");
+
+    // forever loop
+    while (1) {
+        // Clear measurement values
+        memset(values, 0, sizeof(values));
+
+        // Scan all channels num_reps times
+        int rep_counter = num_reps;
+        while (rep_counter--) {
+
+            int i = num_channels;
+            while (i--) {
+
+                // Select this channel
+                radio.setChannel(i);
+
+                // Listen for a little
+                radio.startListening();
+                delayMicroseconds(128);
+                radio.stopListening();
+
+                // Did we get a carrier?
+                if (radio.testCarrier()) {
+                    ++values[i];
+                }
+            }
+        }
+
+        // Print out channel measurements, clamped to a single hex digit
+        i = 0;
+        while (i < num_channels) {
+            if (values[i])
+                printf("%x", min(0xf, (values[i] & 0xf)));
+            else
+                printf("-");
+
+            ++i;
+        }
+        printf("\n");
+    }
+
+    return 0;
+}
+
+// vim:ai:cin:sts=2 sw=2 ft=cpp
diff --git a/arduino/libraries/RF24/examples_linux/streamingData.cpp 
b/arduino/libraries/RF24/examples_linux/streamingData.cpp
new file mode 100644
index 000000000..bb27baf48
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/streamingData.cpp
@@ -0,0 +1,291 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty (2bndy5)
+ */
+
+/**
+ * A simple example of streaming data from 1 nRF24L01 transceiver to another.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use `ctrl+c` to quit at any time.
+ */
+#include <cmath>       // abs()
+#include <ctime>       // time()
+#include <cstring>     // strcmp()
+#include <iostream>    // cin, cout, endl
+#include <string>      // string, getline()
+#include <time.h>      // CLOCK_MONOTONIC_RAW, timespec, clock_gettime()
+#include <RF24/RF24.h> // RF24, RF24_PA_LOW, delay()
+
+using namespace std;
+
+/****************** Linux ***********************/
+// Radio CE Pin, CSN Pin, SPI Speed
+// CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use their own pin numbering
+// CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
+// ie: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
+
+// Generic:
+RF24 radio(22, 0);
+/****************** Linux (BBB,x86,etc) ***********************/
+// See http://nRF24.github.io/RF24/pages.html for more information on usage
+// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
+// See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV
+
+// For this example, we'll be sending 32 payloads each containing
+// 32 bytes of data that looks like ASCII art when printed to the serial
+// monitor. The TX node and RX node needs only a single 32 byte buffer.
+#define SIZE 32            // this is the maximum for this example. (minimum is 1)
+char buffer[SIZE + 1];     // for the RX node
+unsigned int counter = 0;  // for counting the number of received payloads
+void makePayload(uint8_t); // prototype to construct a payload dynamically
+void setRole();            // prototype to set the node's role
+void master();             // prototype of the TX node's behavior
+void slave();              // prototype of the RX node's behavior
+void printHelp(string);    // prototype to function that explain CLI arg usage
+
+// custom defined timer for evaluating transmission time in microseconds
+struct timespec startTimer, endTimer;
+uint32_t getMicros(); // prototype to get ellapsed time in microseconds
+
+
+int main(int argc, char** argv) {
+
+    // perform hardware check
+    if (!radio.begin()) {
+        cout << "radio hardware is not responding!!" << endl;
+        return 0; // quit now
+    }
+
+    // add a NULL terminating 0 for printing as a c-string
+    buffer[SIZE] = 0;
+
+    // Let these addresses be used for the pair of nodes used in this example
+    uint8_t address[2][6] = {"1Node", "2Node"};
+    //             the TX address^ ,  ^the RX address
+    // It is very helpful to think of an address as a path instead of as
+    // an identifying device destination
+
+    // to use different addresses on a pair of radios, we need a variable to
+    // uniquely identify which address this radio will use to transmit
+    bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+    bool foundArgNode = false;
+    bool foundArgRole = false;
+    bool role = false;
+    if (argc > 1) {
+        // CLI args are specified
+        if ((argc - 1) % 2 != 0) {
+            // some CLI arg doesn't have an option specified for it
+            printHelp(string(argv[0])); // all args need an option in this example
+            return 0;
+        }
+        else {
+            // iterate through args starting after program name
+            int a = 1;
+            while (a < argc) {
+                bool invalidOption = false;
+                if (strcmp(argv[a], "-n") == 0 || strcmp(argv[a], "--node") == 0) {
+                    // "-n" or "--node" has been specified
+                    foundArgNode = true;
+                    if (argv[a + 1][0] - 48 <= 1) {
+                        radioNumber = (argv[a + 1][0] - 48) == 1;
+                    }
+                    else {
+                        // option is invalid
+                        invalidOption = true;
+                    }
+                }
+                else if (strcmp(argv[a], "-r") == 0 || strcmp(argv[a], "--role") == 0) {
+                    // "-r" or "--role" has been specified
+                    foundArgRole = true;
+                    if (argv[a + 1][0] - 48 <= 1) {
+                        role = (argv[a + 1][0] - 48) == 1;
+                    }
+                    else {
+                        // option is invalid
+                        invalidOption = true;
+                    }
+                }
+                if (invalidOption) {
+                    printHelp(string(argv[0]));
+                    return 0;
+                }
+                a += 2;
+            } // while
+            if (!foundArgNode && !foundArgRole) {
+                // no valid args were specified
+                printHelp(string(argv[0]));
+                return 0;
+            }
+        } // else
+    } // if
+
+    // print example's name
+    cout << argv[0] << endl;
+
+    if (!foundArgNode) {
+        // Set the radioNumber via the terminal on startup
+        cout << "Which radio is this? Enter '0' or '1'. Defaults to '0' ";
+        string input;
+        getline(cin, input);
+        radioNumber = input.length() > 0 && (uint8_t)input[0] == 49;
+    }
+
+    // save on transmission time by setting the radio to only transmit the
+    // number of bytes we need to transmit a float
+    radio.setPayloadSize(SIZE);    // default value is the maximum 32 bytes
+
+    // Set the PA Level low to try preventing power supply related problems
+    // because these examples are likely run with nodes in close proximity to
+    // each other.
+    radio.setPALevel(RF24_PA_LOW); // RF24_PA_MAX is default.
+
+    // set the TX address of the RX node into the TX pipe
+    radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0
+
+    // set the RX address of the TX node into a RX pipe
+    radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+    // For debugging info
+    // radio.printDetails();       // (smaller) function that prints raw register values
+    // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+    // ready to execute program now
+    if (!foundArgRole) {           // if CLI arg "-r"/"--role" was not specified
+        setRole();                 // calls master() or slave() based on user input
+    }
+    else {                         // if CLI arg "-r"/"--role" was specified
+        role ? master() : slave(); // based on CLI arg option
+    }
+    return 0;
+}
+
+
+/**
+ * set this node's role from stdin stream.
+ * this only considers the first char as input.
+ */
+void setRole() {
+    string input = "";
+    while (!input.length()) {
+        cout << "*** PRESS 'T' to begin transmitting to the other node\n";
+        cout << "*** PRESS 'R' to begin receiving from the other node\n";
+        cout << "*** PRESS 'Q' to exit" << endl;
+        getline(cin, input);
+        if (input.length() >= 1) {
+            if (input[0] == 'T' || input[0] == 't')
+                master();
+            else if (input[0] == 'R' || input[0] == 'r')
+                slave();
+            else if (input[0] == 'Q' || input[0] == 'q')
+                break;
+            else
+                cout << input[0] << " is an invalid input. Please try again." << endl;
+        }
+        input = ""; // stay in the while loop
+    } // while
+} // setRole()
+
+
+/**
+ * make this node act as the transmitter
+ */
+void master() {
+    radio.stopListening();                           // put radio in TX mode
+
+    unsigned int failures = 0;                       // keep track of failures
+    uint8_t i = 0;
+    clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer); // start the timer
+    while (i < SIZE) {
+        makePayload(i);
+        if (!radio.writeFast(&buffer, SIZE)) {
+            failures++;
+            radio.reUseTX();
+        } else {
+            i++;
+        }
+
+        if (failures >= 100) {
+            // most likely no device is listening for the data stream
+            cout << "Too many failures detected. ";
+            cout << "Aborting at payload " << buffer[0];
+            break;
+        }
+    } // while
+    uint32_t ellapsedTime = getMicros();             // end the timer
+    cout << "Time to transmit data = ";
+    cout << ellapsedTime;                            // print the timer result
+    cout << " us. " << failures;                     // print number of retries
+    cout << " failures detected. Leaving TX role." << endl;
+} // master
+
+
+/**
+ * make this node act as the receiver
+ */
+void slave() {
+
+    counter = 0;
+    radio.startListening();                   // put radio in RX mode
+    time_t startTimer = time(nullptr);        // start a timer
+    while (time(nullptr) - startTimer < 6) {  // use 6 second timeout
+        if (radio.available()) {              // is there a payload
+            radio.read(&buffer, SIZE);        // fetch payload from FIFO
+            cout << "Received: " << buffer;   // print the payload's value
+            cout << " - " << counter << endl; // print the counter
+            counter++;                        // increment counter
+            startTimer = time(nullptr);       // reset timer
+        }
+    }
+    radio.stopListening();                    // use TX mode for idle behavior
+
+    cout << "Nothing received in 6 seconds. Leaving RX role." << endl;
+}
+
+
+/**
+ * Make a single payload based on position in stream.
+ * This example employs this function to save on memory allocated.
+ */
+void makePayload(uint8_t i) {
+
+    // let the first character be an identifying alphanumeric prefix
+    // this lets us see which payload didn't get received
+    buffer[0] = i + (i < 26 ? 65 : 71);
+    for (uint8_t j = 0; j < SIZE - 1; ++j) {
+        char chr = j >= (SIZE - 1) / 2 + abs((SIZE - 1) / 2 - i);
+        chr |= j < (SIZE - 1) / 2 - abs((SIZE - 1) / 2 - i);
+        buffer[j + 1] = chr + 48;
+    }
+}
+
+
+/**
+ * Calculate the ellapsed time in microseconds
+ */
+uint32_t getMicros() {
+    // this function assumes that the timer was started using
+    // `clock_gettime(CLOCK_MONOTONIC_RAW, &startTimer);`
+
+    clock_gettime(CLOCK_MONOTONIC_RAW, &endTimer);
+    uint32_t seconds = endTimer.tv_sec - startTimer.tv_sec;
+    uint32_t useconds = (endTimer.tv_nsec - startTimer.tv_nsec) / 1000;
+
+    return ((seconds) * 1000 + useconds) + 0.5;
+}
+
+
+/**
+ * print a manual page of instructions on how to use this example's CLI args
+ */
+void printHelp(string progName) {
+    cout << "usage: " << progName << " [-h] [-n {0,1}] [-r {0,1}]\n\n"
+         << "A simple example of streaming data from 1 nRF24L01 transceiver to another.\n"
+         << "\nThis example was written to be used on 2 devices acting as 'nodes'.\n"
+         << "\noptional arguments:\n  -h, --help\t\tshow this help message and exit\n"
+         << "  -n {0,1}, --node {0,1}\n\t\t\tthe identifying radio number\n"
+         << "  -r {0,1}, --role {0,1}\n\t\t\t'1' specifies the TX role."
+         << " '0' specifies the RX role." << endl;
+}
\ No newline at end of file
diff --git a/arduino/libraries/RF24/examples_linux/streaming_data.py 
b/arduino/libraries/RF24/examples_linux/streaming_data.py
new file mode 100644
index 000000000..02efedf0a
--- /dev/null
+++ b/arduino/libraries/RF24/examples_linux/streaming_data.py
@@ -0,0 +1,221 @@
+"""
+A simple example of streaming data from 1 nRF24L01 transceiver to another.
+
+This example was written to be used on 2 devices acting as 'nodes'.
+"""
+import sys
+import argparse
+import time
+from RF24 import RF24, RF24_PA_LOW
+
+
+parser = argparse.ArgumentParser(
+    description=__doc__,
+    formatter_class=argparse.RawDescriptionHelpFormatter
+)
+parser.add_argument(
+    "-n",
+    "--node",
+    type=int,
+    choices=range(2),
+    help="the identifying radio number (or node ID number)"
+)
+parser.add_argument(
+    "-r",
+    "--role",
+    type=int,
+    choices=range(2),
+    help="'1' specifies the TX role. '0' specifies the RX role."
+)
+
+########### USER CONFIGURATION ###########
+# See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md
+# Radio CE Pin, CSN Pin, SPI Speed
+# CE Pin uses GPIO number with BCM and SPIDEV drivers, other platforms use
+# their own pin numbering
+# CS Pin addresses the SPI bus number at /dev/spidev<a>.<b>
+# ie: RF24 radio(<ce_pin>, <a>*10+<b>); spidev1.0 is 10, spidev1.1 is 11 etc..
+
+# Generic:
+radio = RF24(22, 0)
+################## Linux (BBB,x86,etc) #########################
+# See http://nRF24.github.io/RF24/pages.html for more information on usage
+# See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA
+# See https://www.kernel.org/doc/Documentation/spi/spidev for more
+# information on SPIDEV
+
+# Specify the number of bytes in the payload. This is also used to
+# specify the number of payloads in 1 stream of data
+SIZE = 32  # this is the default maximum payload size
+
+
+def make_buffer(buf_iter):
+    """Returns a dynamically created payloads
+
+    :param int buf_iter: The position of the payload in the data stream
+    """
+    # we'll use `SIZE` for the number of payloads in the list and the
+    # payloads' length
+    # prefix payload with a sequential letter to indicate which
+    # payloads were lost (if any)
+    buff = bytes([buf_iter + (65 if 0 <= buf_iter < 26 else 71)])
+    for j in range(SIZE - 1):
+        char = bool(j >= (SIZE - 1) / 2 + abs((SIZE - 1) / 2 - buf_iter))
+        char |= bool(j < (SIZE - 1) / 2 - abs((SIZE - 1) / 2 - buf_iter))
+        buff += bytes([char + 48])
+    return buff
+
+
+def master(count=1):
+    """Uses all 3 levels of the TX FIFO to send a stream of data
+
+    :param int count: how many times to transmit the stream of data.
+    """
+    radio.stopListening()  # put radio in TX mode
+    radio.flush_tx()  # clear the TX FIFO so we can use all 3 levels
+    failures = 0  # keep track of manual retries
+    start_timer = time.monotonic_ns()  # start timer
+    for multiplier in range(count):  # repeat transmit the same data stream
+        buf_iter = 0  # iterator of payloads for the while loop
+        while buf_iter < SIZE:  # cycle through all the payloads
+            buffer = make_buffer(buf_iter)  # make a payload
+
+            if not radio.writeFast(buffer):  # transmission failed
+                failures += 1  # increment manual retry count
+                if failures > 99 and buf_iter < 7 and multiplier < 2:
+                    # we need to prevent an infinite loop
+                    print(
+                        "Too many failures detected. Aborting at payload ",
+                        buffer[0]
+                    )
+                    multiplier = count  # be sure to exit the for loop
+                    break  # exit the while loop
+                radio.reUseTX()  # resend payload in top level of TX FIFO
+            else:  # transmission succeeded
+                buf_iter += 1
+    end_timer = time.monotonic_ns()  # end timer
+    print(
+        "Time to transmit data = {} us. Detected {} failures.".format(
+            (end_timer - start_timer) / 1000,
+            failures
+        )
+    )
+
+
+def slave(timeout=6):
+    """Listen for any payloads and print them out (suffixed with received
+    counter)
+
+    :param int timeout: The number of seconds to wait (with no transmission)
+        until exiting function.
+    """
+    radio.startListening()  # put radio in RX mode
+    count = 0  # keep track of the number of received payloads
+    start_timer = time.monotonic()  # start timer
+    while (time.monotonic() - start_timer) < timeout:
+        if radio.available():
+            count += 1
+            # retreive the received packet's payload
+            receive_payload = radio.read(radio.payloadSize)
+            print("Received: {} - {}".format(receive_payload, count))
+            start_timer = time.monotonic()  # reset timer on every RX payload
+
+    print("Nothing received in", timeout, "seconds. Leaving RX role")
+    # recommended behavior is to keep in TX mode while idle
+    radio.stopListening()  # put the radio in TX mode
+
+    print("Nothing received in ", timeout, " seconds. Leaving RX role")
+
+def set_role():
+    """Set the role using stdin stream. Role args can be specified using space
+    delimiters (e.g. 'R 10' calls `slave(10)` & 'T 3' calls `master(3)`)
+
+    :return:
+        - True when role is complete & app should continue running.
+        - False when app should exit
+    """
+    user_input = input(
+        "*** Enter 'R' for receiver role.\n"
+        "*** Enter 'T' for transmitter role.\n"
+        "*** Enter 'Q' to quit example.\n"
+    ) or "?"
+    user_input = user_input.split()
+    if user_input[0].upper().startswith("R"):
+        if len(user_input) > 1:
+            slave(int(user_input[1]))
+        else:
+            slave()
+        return True
+    elif user_input[0].upper().startswith("T"):
+        if len(user_input) > 1:
+            master(int(user_input[1]))
+        else:
+            master()
+        return True
+    elif user_input[0].upper().startswith("Q"):
+        radio.powerDown()
+        return False
+    print(user_input[0], "is an unrecognized input. Please try again.")
+    return set_role()
+
+
+if __name__ == "__main__":
+
+    args = parser.parse_args()  # parse any CLI args
+
+    # initialize the nRF24L01 on the spi bus
+    if not radio.begin():
+        raise RuntimeError("radio hardware is not responding")
+
+    # For this example, we will use different addresses
+    # An address need to be a buffer protocol object (bytearray)
+    address = [b"1Node", b"2Node"]
+    # It is very helpful to think of an address as a path instead of as
+    # an identifying device destination
+
+    print(sys.argv[0])  # print example name
+
+    # to use different addresses on a pair of radios, we need a variable to
+    # uniquely identify which address this radio will use to transmit
+    # 0 uses address[0] to transmit, 1 uses address[1] to transmit
+    radio_number = args.node  # uses default value from `parser`
+    if args.node is None:  # if '--node' arg wasn't specified
+        radio_number = bool(
+            int(
+                input(
+                    "Which radio is this? Enter '0' or '1'. Defaults to '0' "
+                ) or 0
+            )
+        )
+
+    # set the Power Amplifier level to -12 dBm since this test example is
+    # usually run with nRF24L01 transceivers in close proximity of each other
+    radio.setPALevel(RF24_PA_LOW)  # RF24_PA_MAX is default
+
+    # set the TX address of the RX node into the TX pipe
+    radio.openWritingPipe(address[radio_number])  # always uses pipe 0
+
+    # set the RX address of the TX node into a RX pipe
+    radio.openReadingPipe(1, address[not radio_number])  # using pipe 1
+
+    # To save time during transmission, we'll set the payload size to be only
+    # what we need. For this example, we'll be using the default maximum 32
+    radio.payloadSize = SIZE
+
+    # for debugging, we have 2 options that print a large block of details
+    # (smaller) function that prints raw register values
+    # radio.printDetails()
+    # (larger) function that prints human readable data
+    # radio.printPrettyDetails()
+
+    try:
+        if args.role is None:  # if not specified with CLI arg '-r'
+            while set_role():
+                pass  # continue example until 'Q' is entered
+        else:  # if role was set using CLI args
+            # run role once and exit
+            master() if bool(args.role) else slave()
+    except KeyboardInterrupt:
+        print(" Keyboard Interrupt detected. Exiting...")
+        radio.powerDown()
+        sys.exit()
diff --git a/arduino/libraries/RF24/examples_pico/CMakeLists.txt 
b/arduino/libraries/RF24/examples_pico/CMakeLists.txt
new file mode 100644
index 000000000..fe1968489
--- /dev/null
+++ b/arduino/libraries/RF24/examples_pico/CMakeLists.txt
@@ -0,0 +1,45 @@
+cmake_minimum_required(VERSION 3.12)
+
+# Pull in SDK (must be before project)
+include(../cmake/pico_sdk_import.cmake)
+
+project(pico_examples C CXX ASM)
+
+# Initialize the Pico SDK
+pico_sdk_init()
+
+# In YOUR project, include RF24's CMakeLists.txt
+# giving the path depending on where the library
+# is cloned to in your project
+include(../CMakeLists.txt)
+
+# iterate over a list of examples by name
+set(EXAMPLES_LIST
+    gettingStarted
+    acknowledgementPayloads
+    streamingData
+    manualAcknowledgements
+    multiceiverDemo
+    interruptConfigure
+    scanner
+)
+
+foreach(example ${EXAMPLES_LIST})
+    # make a target
+    add_executable(${example} ${example}.cpp defaultPins.h)
+
+    # link the necessary libs to the target
+    target_link_libraries(${example} PUBLIC
+        RF24
+        pico_stdlib
+        hardware_spi
+        hardware_gpio
+    )
+
+    # specify USB port as default serial communication's interface (not UART RX/TX pins)
+    pico_enable_stdio_usb(${example} 1)
+    pico_enable_stdio_uart(${example} 0)
+
+    # create map/bin/hex file etc.
+    pico_add_extra_outputs(${example})
+endforeach()
diff --git a/arduino/libraries/RF24/examples_pico/acknowledgementPayloads.cpp 
b/arduino/libraries/RF24/examples_pico/acknowledgementPayloads.cpp
new file mode 100644
index 000000000..318540a7a
--- /dev/null
+++ b/arduino/libraries/RF24/examples_pico/acknowledgementPayloads.cpp
@@ -0,0 +1,229 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty (2bndy5)
+ */
+
+/**
+ * A simple example of sending data from 1 nRF24L01 transceiver to another
+ * with Acknowledgement (ACK) payloads attached to ACK packets.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use the Serial Terminal to change each node's behavior.
+ */
+#include "pico/stdlib.h"  // printf(), sleep_ms(), getchar_timeout_us(), to_us_since_boot(), 
get_absolute_time()
+#include "pico/bootrom.h" // reset_usb_boot()
+#include <tusb.h>         // tud_cdc_connected()
+#include <RF24.h>         // RF24 radio object
+#include "defaultPins.h"  // board presumptive default pin numbers for CE_PIN and CSN_PIN
+
+// instantiate an object for the nRF24L01 transceiver
+RF24 radio(CE_PIN, CSN_PIN);
+
+// Used to control whether this node is sending or receiving
+bool role = false;  // true = TX role, false = RX role
+
+// For this example, we'll be using a payload containing
+// a string & an integer number that will be incremented
+// on every successful transmission.
+// Make a data structure to store the entire payload of different datatypes
+struct PayloadStruct {
+    char message[7];   // only using 6 characters for TX & ACK payloads
+    uint8_t counter;
+};
+PayloadStruct payload;
+
+
+bool setup() {
+
+    // an identifying device destination
+    // Let these addresses be used for the pair
+    uint8_t address[][6] = {"1Node", "2Node"};
+    // It is very helpful to think of an address as a path instead of as
+    // an identifying device destination
+    // to use different addresses on a pair of radios, we need a variable to
+
+    // uniquely identify which address this radio will use to transmit
+    bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+    // wait here until the CDC ACM (serial port emulation) is connected
+    while (!tud_cdc_connected()) {
+        sleep_ms(10);
+    }
+
+    // initialize the transceiver on the SPI bus
+    if (!radio.begin()) {
+        printf("radio hardware is not responding!!\n");
+        return false;
+    }
+
+    // print example's introductory prompt
+    printf("RF24/examples_pico/acknowledgementPayloads\n");
+
+    // To set the radioNumber via the Serial monitor on startup
+    printf("Which radio is this? Enter '0' or '1'. Defaults to '0'\n");
+    char input = getchar();
+    radioNumber = input == 49;
+    printf("radioNumber = %d\n", (int)radioNumber);
+
+    // Set the PA Level low to try preventing power supply related problems
+    // because these examples are likely run with nodes in close proximity to
+    // each other.
+    radio.setPALevel(RF24_PA_LOW);     // RF24_PA_MAX is default.
+
+    // to use ACK payloads, we need to enable dynamic payload lengths (for all nodes)
+    radio.enableDynamicPayloads();    // ACK payloads are dynamically sized
+
+    // Acknowledgement packets have no payloads by default. We need to enable
+    // this feature for all nodes (TX & RX) to use ACK payloads.
+    radio.enableAckPayload();
+
+    // set the TX address of the RX node into the TX pipe
+    radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0
+
+    // set the RX address of the TX node into a RX pipe
+    radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+    // additional setup specific to the node's role
+    if (role) {
+        // setup the TX payload
+
+        memcpy(payload.message, "Hello ", 6);                       // set the payload message
+        radio.stopListening();                                      // put radio in TX mode
+    }
+    else {
+        // setup the ACK payload & load the first response into the FIFO
+
+        memcpy(payload.message, "World ", 6);                       // set the payload message
+        // load the payload for the first received transmission on pipe 0
+        radio.writeAckPayload(1, &payload, sizeof(payload));
+
+        radio.startListening();                                     // put radio in RX mode
+    }
+
+    // For debugging info
+    // radio.printDetails();       // (smaller) function that prints raw register values
+    // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+    // role variable is hardcoded to RX behavior, inform the user of this
+    printf("*** PRESS 'T' to begin transmitting to the other node\n");
+
+    return true;
+}
+
+void loop() {
+
+    if (role) {
+        // This device is a TX node
+
+        uint64_t start_timer = to_us_since_boot(get_absolute_time()); // start the timer
+        bool report = radio.write(&payload, sizeof(payload));         // transmit & save the report
+        uint64_t end_timer = to_us_since_boot(get_absolute_time());   // end the timer
+
+        if (report) {
+
+            // print details about outgoing payload
+            printf("Transmission successful! Time to transmit = %llu us. Sent: %s%d",
+                   end_timer - start_timer,
+                   payload.message,
+                   payload.counter);
+
+            uint8_t pipe;
+            if (radio.available(&pipe)) {                          // is there an ACK payload? grab the pipe 
number that received it
+                PayloadStruct received;
+                radio.read(&received, sizeof(received));             // get incoming ACK payload
+
+                // print details about incoming payload
+                printf(" Recieved %d bytes on pipe %d: %s%d\n",
+                       radio.getDynamicPayloadSize(),
+                       pipe,
+                       received.message,
+                       received.counter);
+
+                // save incoming counter & increment for next outgoing
+                payload.counter = received.counter + 1;
+
+            }
+            else {
+                printf(" Recieved: an empty ACK packet\n"); // empty ACK packet received
+            }
+
+
+        }
+        else {
+            printf("Transmission failed or timed out\n");    // payload was not delivered
+        }
+
+        // to make this example readable in the serial terminal
+        sleep_ms(1000); // slow transmissions down by 1 second
+    }
+    else {
+        // This device is a RX node
+
+        uint8_t pipe;
+        if (radio.available(&pipe)) {                    // is there a payload? get the pipe number that 
recieved it
+            uint8_t bytes = radio.getDynamicPayloadSize(); // get the size of the payload
+            PayloadStruct received;
+            radio.read(&received, sizeof(received));       // get incoming payload
+
+            // print the details of transaction
+            printf("Received %d bytes on pipe %d: %s%d Sent: %s%d\n",
+                   bytes,
+                   pipe,
+                   received.message,
+                   received.counter,
+                   payload.message,
+                   payload.counter);
+
+            // save incoming counter & increment for next outgoing
+            payload.counter = received.counter + 1;
+            // load the payload for the first received transmission on pipe 0
+            radio.writeAckPayload(1, &payload, sizeof(payload));
+        }
+    } // role
+
+    char input = getchar_timeout_us(0); // get char from buffer for user input
+    if (input != PICO_ERROR_TIMEOUT) {
+        // change the role via the serial terminal
+
+        if ((input == 'T' || input == 't') && !role) {
+            // Become the TX node
+
+            role = true;
+            printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n");
+
+            memcpy(payload.message, "Hello ", 6); // change payload message
+            radio.stopListening();                // this also discards any unused ACK payloads
+
+        }
+        else if ((input == 'R' || input == 'r') && role) {
+            // Become the RX node
+
+            role = false;
+            printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n");
+            memcpy(payload.message, "World ", 6); // change payload message
+
+            // load the payload for the first received transmission on pipe 0
+            radio.writeAckPayload(1, &payload, sizeof(payload));
+            radio.startListening();
+        }
+        else if (input == 'b' || input == 'B') {
+            // reset to bootloader
+            radio.powerDown();
+            reset_usb_boot(0, 0);
+        }
+    }
+} // loop
+
+int main()
+{
+    stdio_init_all(); // init necessary IO for the RP2040
+
+    while (!setup()) { // if radio.begin() failed
+        // hold program in infinite attempts to initialize radio
+    }
+    while (true) {
+        loop();
+    }
+    return 0; // we will never reach this
+}
diff --git a/arduino/libraries/RF24/examples_pico/defaultPins.h 
b/arduino/libraries/RF24/examples_pico/defaultPins.h
new file mode 100644
index 000000000..1178e2601
--- /dev/null
+++ b/arduino/libraries/RF24/examples_pico/defaultPins.h
@@ -0,0 +1,33 @@
+// pre-chossen pins for different boards
+#ifndef DEFAULTPINS_H
+#define DEFAULTPINS_H
+
+#if defined (ADAFRUIT_QTPY_RP2040)
+// for this board, you can still use the Stemma QT connector as a separate I2C bus (`i2c1`)
+#define CE_PIN PICO_DEFAULT_I2C_SDA_PIN  // the pin labeled SDA
+#define CSN_PIN PICO_DEFAULT_I2C_SCL_PIN // the pin labeled SCL
+#define IRQ_PIN PICO_DEFAULT_UART_RX_PIN // the pin labeled RX
+
+#elif defined (PIMORONI_TINY2040)
+// default SPI_SCK_PIN = 6
+// default SPI_TX_PIN = 7
+// default SPI_RX_PIN = 4
+#define CE_PIN PICO_DEFAULT_I2C_SCL_PIN  // pin 3
+#define CSN_PIN PICO_DEFAULT_SPI_CSN_PIN // pin 5
+#define IRQ_PIN PICO_DEFAULT_I2C_SDA_PIN // pin 2
+
+
+#elif defined(SPARFUN_THINGPLUS)
+#define CSN_PIN 16 // the pin labeled 16
+#define CE_PIN 7   // the pin labeled SCL
+#define IRQ_PIN 6  // the pin labeled SDA
+
+#else
+// pins available on (ADAFRUIT_ITSYBITSY_RP2040 || ADAFRUIT_FEATHER_RP2040 || Pico_board || 
Sparkfun_ProMicro || SparkFun MicroMod)
+
+#define CE_PIN 7
+#define CSN_PIN 8
+#define IRQ_PIN 6
+#endif // board detection macro defs
+
+#endif // DEFAULTPINS_H
diff --git a/arduino/libraries/RF24/examples_pico/gettingStarted.cpp 
b/arduino/libraries/RF24/examples_pico/gettingStarted.cpp
new file mode 100644
index 000000000..cf2637221
--- /dev/null
+++ b/arduino/libraries/RF24/examples_pico/gettingStarted.cpp
@@ -0,0 +1,169 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty (2bndy5)
+ */
+
+/**
+ * A simple example of sending data from 1 nRF24L01 transceiver to another.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use the Serial Terminal to change each node's behavior.
+ */
+#include "pico/stdlib.h"  // printf(), sleep_ms(), getchar_timeout_us(), to_us_since_boot(), 
get_absolute_time()
+#include "pico/bootrom.h" // reset_usb_boot()
+#include <tusb.h>         // tud_cdc_connected()
+#include <RF24.h>         // RF24 radio object
+#include "defaultPins.h"  // board presumptive default pin numbers for CE_PIN and CSN_PIN
+
+// instantiate an object for the nRF24L01 transceiver
+RF24 radio(CE_PIN, CSN_PIN);
+
+// Used to control whether this node is sending or receiving
+bool role = false; // true = TX role, false = RX role
+
+// For this example, we'll be using a payload containing
+// a single float number that will be incremented
+// on every successful transmission
+float payload = 0.0;
+
+
+bool setup()
+{
+    // Let these addresses be used for the pair
+    uint8_t address[][6] = {"1Node", "2Node"};
+    // It is very helpful to think of an address as a path instead of as
+    // an identifying device destination
+
+    // to use different addresses on a pair of radios, we need a variable to
+    // uniquely identify which address this radio will use to transmit
+    bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+    // wait here until the CDC ACM (serial port emulation) is connected
+    while (!tud_cdc_connected()) {
+        sleep_ms(10);
+    }
+
+    // initialize the transceiver on the SPI bus
+    if (!radio.begin()) {
+        printf("radio hardware is not responding!!\n");
+        return false;
+    }
+
+    // print example's introductory prompt
+    printf("RF24/examples_pico/gettingStarted\n");
+
+    // To set the radioNumber via the Serial terminal on startup
+    printf("Which radio is this? Enter '0' or '1'. Defaults to '0'\n");
+    char input = getchar();
+    radioNumber = input == 49;
+    printf("radioNumber = %d\n", (int)radioNumber);
+
+    // Set the PA Level low to try preventing power supply related problems
+    // because these examples are likely run with nodes in close proximity to
+    // each other.
+    radio.setPALevel(RF24_PA_LOW); // RF24_PA_MAX is default.
+
+    // save on transmission time by setting the radio to only transmit the
+    // number of bytes we need to transmit a float
+    radio.setPayloadSize(sizeof(payload)); // float datatype occupies 4 bytes
+
+    // set the TX address of the RX node into the TX pipe
+    radio.openWritingPipe(address[radioNumber]); // always uses pipe 0
+
+    // set the RX address of the TX node into a RX pipe
+    radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+    // additional setup specific to the node's role
+    if (role) {
+        radio.stopListening(); // put radio in TX mode
+    }
+    else {
+        radio.startListening(); // put radio in RX mode
+    }
+
+    // For debugging info
+    // radio.printDetails();       // (smaller) function that prints raw register values
+    // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+    // role variable is hardcoded to RX behavior, inform the user of this
+    printf("*** PRESS 'T' to begin transmitting to the other node\n");
+
+    return true;
+} // setup
+
+void loop()
+{
+    if (role) {
+        // This device is a TX node
+
+        uint64_t start_timer = to_us_since_boot(get_absolute_time()); // start the timer
+        bool report = radio.write(&payload, sizeof(payload));         // transmit & save the report
+        uint64_t end_timer = to_us_since_boot(get_absolute_time());   // end the timer
+
+        if (report) {
+            // payload was delivered; print the payload sent & the timer result
+            printf("Transmission successful! Time to transmit = %llu us. Sent: %f\n", end_timer - 
start_timer, payload);
+
+            // increment float payload
+            payload += 0.01;
+        }
+        else {
+            // payload was not delivered
+            printf("Transmission failed or timed out\n");
+        }
+
+        // to make this example readable in the serial terminal
+        sleep_ms(1000); // slow transmissions down by 1 second
+    }
+    else {
+        // This device is a RX node
+
+        uint8_t pipe;
+        if (radio.available(&pipe)) {               // is there a payload? get the pipe number that recieved 
it
+            uint8_t bytes = radio.getPayloadSize(); // get the size of the payload
+            radio.read(&payload, bytes);            // fetch payload from FIFO
+
+            // print the size of the payload, the pipe number, payload's value
+            printf("Received %d bytes on pipe %d: %f\n", bytes, pipe, payload);
+        }
+    } // role
+
+    char input = getchar_timeout_us(0); // get char from buffer for user input
+    if (input != PICO_ERROR_TIMEOUT) {
+        // change the role via the serial terminal
+
+        if ((input == 'T' || input == 't') && !role) {
+            // Become the TX node
+
+            role = true;
+            printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n");
+            radio.stopListening();
+        }
+        else if ((input == 'R' || input == 'r') && role) {
+            // Become the RX node
+
+            role = false;
+            printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n");
+            radio.startListening();
+        }
+        else if (input == 'b' || input == 'B') {
+            // reset to bootloader
+            radio.powerDown();
+            reset_usb_boot(0, 0);
+        }
+    }
+} // loop
+
+int main()
+{
+    stdio_init_all(); // init necessary IO for the RP2040
+
+    while (!setup()) { // if radio.begin() failed
+        // hold program in infinite attempts to initialize radio
+    }
+    while (true) {
+        loop();
+    }
+    return 0; // we will never reach this
+}
diff --git a/arduino/libraries/RF24/examples_pico/interruptConfigure.cpp 
b/arduino/libraries/RF24/examples_pico/interruptConfigure.cpp
new file mode 100644
index 000000000..134a90c97
--- /dev/null
+++ b/arduino/libraries/RF24/examples_pico/interruptConfigure.cpp
@@ -0,0 +1,367 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty (2bndy5)
+ */
+
+/**
+ * This example uses Acknowledgement (ACK) payloads attached to ACK packets to
+ * demonstrate how the nRF24L01's IRQ (Interrupt Request) pin can be
+ * configured to detect when data is received, or when data has transmitted
+ * successfully, or when data has failed to transmit.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use the Serial Terminal to change each node's behavior.
+ */
+#include "pico/stdlib.h"  // printf(), sleep_ms(), getchar_timeout_us(), to_us_since_boot(), 
get_absolute_time()
+#include "pico/bootrom.h" // reset_usb_boot()
+#include <tusb.h>         // tud_cdc_connected()
+#include <RF24.h>         // RF24 radio object
+#include "defaultPins.h"  // board presumptive default pin numbers for IRQ_PIN, CE_PIN, and CSN_PIN
+
+// We will be using the nRF24L01's IRQ pin for this example
+volatile bool wait_for_event = false; // used to wait for an IRQ event to trigger
+
+// instantiate an object for the nRF24L01 transceiver
+RF24 radio(CE_PIN, CSN_PIN);
+
+// Used to control whether this node is sending or receiving
+bool role = false;  // true = TX node, false = RX node
+
+// For this example, we'll be using a payload containing
+// a string that changes on every transmission. (successful or not)
+// Make a couple arrays of payloads & an iterator to traverse them
+const uint8_t tx_pl_size = 5;
+const uint8_t ack_pl_size = 4;
+uint8_t pl_iterator = 0;
+// The " + 1" compensates for the c-string's NULL terminating 0
+char tx_payloads[][tx_pl_size + 1] = {"Ping ", "Pong ", "Radio", "1FAIL"};
+char ack_payloads[][ack_pl_size + 1] = {"Yak ", "Back", " ACK"};
+
+void interruptHandler(uint gpio, uint32_t events); // prototype to handle IRQ events
+void printRxFifo();                                // prototype to print RX FIFO with 1 buffer
+
+
+bool setup()
+{
+    // Let these addresses be used for the pair
+    uint8_t address[][6] = {"1Node", "2Node"};
+    // It is very helpful to think of an address as a path instead of as
+    // an identifying device destination
+
+    // to use different addresses on a pair of radios, we need a variable to
+    // uniquely identify which address this radio will use to transmit
+    bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+    // wait here until the CDC ACM (serial port emulation) is connected
+    while (!tud_cdc_connected()) {
+        sleep_ms(10);
+    }
+
+    // initialize the transceiver on the SPI bus
+    if (!radio.begin()) {
+        printf("radio hardware is not responding!!\n");
+        return false;
+    }
+
+    // print example's introductory prompt
+    printf("RF24/examples_pico/interruptConfigure\n");
+
+    // To set the radioNumber via the Serial terminal on startup
+    printf("Which radio is this? Enter '0' or '1'. Defaults to '0'\n");
+    char input = getchar();
+    radioNumber = input == 49;
+    printf("radioNumber = %d\n", (int)radioNumber);
+
+    // setup the IRQ_PIN
+    gpio_set_irq_enabled_with_callback(IRQ_PIN, GPIO_IRQ_EDGE_FALL, true, &interruptHandler);
+    // IMPORTANT: do not call radio.available() before calling
+    // radio.whatHappened() when the interruptHandler() is triggered by the
+    // IRQ pin FALLING event. According to the datasheet, the pipe information
+    // is unreliable during the IRQ pin FALLING transition.
+
+    // Set the PA Level low to try preventing power supply related problems
+    // because these examples are likely run with nodes in close proximity to
+    // each other.
+    radio.setPALevel(RF24_PA_LOW);    // RF24_PA_MAX is default.
+
+    // For this example we use acknowledgment (ACK) payloads to trigger the
+    // IRQ pin when data is received on the TX node.
+    // to use ACK payloads, we need to enable dynamic payload lengths
+    radio.enableDynamicPayloads();    // ACK payloads are dynamically sized
+
+    // Acknowledgement packets have no payloads by default. We need to enable
+    // this feature for all nodes (TX & RX) to use ACK payloads.
+    radio.enableAckPayload();
+    // Fot this example, we use the same address to send data back and forth
+
+    // set the TX address of the RX node into the TX pipe
+    radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0
+
+    // set the RX address of the TX node into a RX pipe
+    radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+    // additional setup specific to the node's role
+    if (role) {
+        // setup for TX mode
+        radio.stopListening(); // put radio in TX mode
+
+    }
+    else {
+        // setup for RX mode
+
+        // let IRQ pin not trigger in RX mode
+        radio.maskIRQ(1, 1, 1); // args = "data_sent", "data_fail", "data_ready"
+
+        // Fill the TX FIFO with 3 ACK payloads for the first 3 received
+        // transmissions on pipe 1
+        radio.writeAckPayload(1, &ack_payloads[0], ack_pl_size);
+        radio.writeAckPayload(1, &ack_payloads[1], ack_pl_size);
+        radio.writeAckPayload(1, &ack_payloads[2], ack_pl_size);
+
+        radio.startListening(); // put radio in RX mode
+    }
+
+    // For debugging info
+    // printf_begin();             // needed only once for printing details
+    // radio.printDetails();       // (smaller) function that prints raw register values
+    // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+    // role variable is hardcoded to RX behavior, inform the user of this
+    printf("*** PRESS 'T' to begin transmitting to the other node\n");
+
+    return true;
+}
+
+void loop()
+{
+    if (role && !wait_for_event) {
+        // This device is a TX node. This if block is only triggered when
+        // NOT waiting for an IRQ event to happen
+
+        if (pl_iterator == 0) {
+            // Test the "data ready" event with the IRQ pin
+
+            printf("\nConfiguring IRQ pin to ignore the 'data sent' event\n");
+            radio.maskIRQ(true, false, false); // args = "data_sent", "data_fail", "data_ready"
+            printf("   Pinging RX node for 'data ready' event...\n");
+
+        }
+        else if (pl_iterator == 1) {
+            // Test the "data sent" event with the IRQ pin
+
+            printf("\nConfiguring IRQ pin to ignore the 'data ready' event\n");
+            radio.maskIRQ(false, false, true); // args = "data_sent", "data_fail", "data_ready"
+            printf("   Pinging RX node for 'data sent' event...\n");
+
+        }
+        else if (pl_iterator == 2) {
+            // Use this iteration to fill the RX node's FIFO which sets us up for the next test.
+
+            // write() uses virtual interrupt flags that work despite the masking of the IRQ pin
+            radio.maskIRQ(1, 1, 1); // disable IRQ masking for this step
+
+            printf("\nSending 1 payload to fill RX node's FIFO. IRQ pin is neglected.\n");
+            // write() will call flush_tx() on 'data fail' events
+            if (radio.write(&tx_payloads[pl_iterator], tx_pl_size)) {
+                if (radio.rxFifoFull()) {
+                    printf("RX node's FIFO is full; it is not listening any more\n");
+                }
+                else {
+                    printf("Transmission successful, but the RX node might still be listening.\n");
+                }
+            }
+            else {
+                printf("Transmission failed or timed out. Continuing anyway.\n");
+                radio.flush_tx(); // discard payload(s) that failed to transmit
+            }
+
+        }
+        else if (pl_iterator == 3) {
+            // test the "data fail" event with the IRQ pin
+
+            printf("\nConfiguring IRQ pin to reflect all events\n");
+            radio.maskIRQ(0, 0, 0); // args = "data_sent", "data_fail", "data_ready"
+            printf("   Pinging inactive RX node for 'data fail' event...\n");
+        }
+
+        if (pl_iterator < 4 && pl_iterator != 2) {
+
+            // IRQ pin is LOW when activated. Otherwise it is always HIGH
+            // Wait until IRQ pin is activated.
+            wait_for_event = true;
+
+            // use the non-blocking call to write a payload and begin transmission
+            // the "false" argument means we are expecting an ACK packet response
+            radio.startFastWrite(tx_payloads[pl_iterator++], tx_pl_size, false);
+
+            // In this example, the "data fail" event is always configured to
+            // trigger the IRQ pin active. Because the auto-ACK feature is on by
+            // default, we don't need a timeout check to prevent an infinite loop.
+        }
+        else if (pl_iterator == 4) {
+            // all IRQ tests are done; flush_tx() and print the ACK payloads for fun
+
+            // CE pin is still HIGH which consumes more power. Example is now idling so...
+            radio.stopListening(); // ensure CE pin is LOW
+            // stopListening() also calls flush_tx() when ACK payloads are enabled
+
+            printRxFifo();
+            pl_iterator++;
+
+            // inform user what to do next
+            printf("\n*** PRESS 'T' to restart the transmissions");
+            printf("\n*** PRESS 'R' to change to Receive role\n");
+
+        }
+        else if (pl_iterator == 2) {
+            pl_iterator++; // proceed from step 3 to last step (stop at step 4 for readability)
+        }
+
+    }
+    else if (!role && radio.rxFifoFull()) {
+        // This device is a RX node
+        //
+        // The RX role waits until RX FIFO is full then stops listening while
+        // the FIFOs get reset and starts listening again.
+        radio.stopListening(); // also calls flush_tx() when ACKs are enabled
+        printRxFifo();         // also clears RX FIFO
+
+        // Fill the TX FIFO with 3 ACK payloads for the first 3 received
+        // transmissions on pipe 1.
+        radio.writeAckPayload(1, &ack_payloads[0], ack_pl_size);
+        radio.writeAckPayload(1, &ack_payloads[1], ack_pl_size);
+        radio.writeAckPayload(1, &ack_payloads[2], ack_pl_size);
+
+        sleep_ms(200);          // let radio TX role complete
+        radio.startListening(); // We're ready to start over. Begin listening.
+
+    } // role
+
+    char input = getchar_timeout_us(0); // get char from buffer for user input
+    if (input != PICO_ERROR_TIMEOUT) {
+        // change the role via the serial terminal
+
+        if (input == 'T' || input == 't') {
+            // Become the TX node
+            if (!role)
+                printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n");
+            else if (role && wait_for_event) // don't interrupt on ongoing test
+                return;                      // exit now; start next loop()
+            else
+                printf("*** RESTARTING IRQ PIN TEST ***\n");
+
+            role = true;
+            wait_for_event = false;
+            pl_iterator = 0;   // reset the iterator
+            radio.flush_tx();  // discard any payloads in the TX FIFO
+
+            // startListening() clears the IRQ masks also. This is required for
+            // continued TX operations when a transmission fails.
+            radio.stopListening(); // this also discards any unused ACK payloads
+        }
+        else if ((input == 'R' || input == 'r')/*  && role */) {
+            // Become the RX node
+            printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n");
+
+            role = false;
+            radio.maskIRQ(1, 1, 1); // the IRQ pin should not trigger in this example's RX rode
+
+            // Fill the TX FIFO with 3 ACK payloads for the first 3 received
+            // transmissions on pipe 1
+            radio.flush_tx(); // make sure there is room for 3 new ACK payloads
+            radio.writeAckPayload(1, &ack_payloads[0], ack_pl_size);
+            radio.writeAckPayload(1, &ack_payloads[1], ack_pl_size);
+            radio.writeAckPayload(1, &ack_payloads[2], ack_pl_size);
+            radio.startListening();
+        }
+        else if (input == 'b' || input == 'B') {
+            // reset to bootloader
+            radio.powerDown();
+            reset_usb_boot(0, 0);
+        }
+    } // user input
+} // loop
+
+/**
+ * when the IRQ pin goes active LOW, call this fuction print out why
+ */
+void interruptHandler(uint gpio, uint32_t events)
+{
+
+    if (gpio != IRQ_PIN && !(events | GPIO_IRQ_EDGE_FALL)) {
+        // the gpio pin and event does not match the configuration we specified
+        return;
+    }
+
+    // print IRQ status and all masking flags' states
+    printf("\tIRQ pin is actively LOW\n");   // show that this function was called
+    bool tx_ds, tx_df, rx_dr;                // declare variables for IRQ masks
+    radio.whatHappened(tx_ds, tx_df, rx_dr); // get values for IRQ masks
+    // whatHappened() clears the IRQ masks also. This is required for
+    // continued TX operations when a transmission fails.
+    // clearing the IRQ masks resets the IRQ pin to its inactive state (HIGH)
+
+    // print "data sent", "data fail", "data ready" mask states
+    printf("\tdata_sent: %s, data_fail: %s, data_ready: %s\n",
+           tx_ds ? "true" : "false",
+           tx_df ? "true" : "false",
+           rx_dr ? "true" : "false");
+
+    if (tx_df)            // if TX payload failed
+        radio.flush_tx(); // clear all payloads from the TX FIFO
+
+    // print if test passed or failed. Unintentional fails mean the RX node was not listening.
+    // pl_iterator has already been incremented by now
+    if (pl_iterator <= 1) {
+        printf("   'Data Ready' event test %s\n", rx_dr ? "passed" : "failed");
+    }
+    else if (pl_iterator == 2) {
+        printf("   'Data Sent' event test %s\n", tx_ds ? "passed" : "failed");
+    }
+    else if (pl_iterator == 4) {
+        printf("   'Data Fail' event test %s\n", tx_df ? "passed" : "failed");
+    }
+    wait_for_event = false; // ready to continue with loop() operations
+} // interruptHandler
+
+
+/**
+ * Print the entire RX FIFO with one buffer. This will also flush the RX FIFO.
+ * Remember that the payload sizes are declared as tx_pl_size and ack_pl_size.
+ */
+void printRxFifo()
+{
+    if (radio.available()) {                   // if there is data in the RX FIFO
+        // to flush the data from the RX FIFO, we'll fetch it all using 1 buffer
+
+        uint8_t pl_size = !role ? tx_pl_size : ack_pl_size;
+        char rx_fifo[pl_size * 3 + 1];         // RX FIFO is full & we know ACK payloads' size
+        if (radio.rxFifoFull()) {
+            rx_fifo[pl_size * 3] = 0;          // add a NULL terminating char to use as a c-string
+            radio.read(&rx_fifo, pl_size * 3); // this clears the RX FIFO (for this example)
+        }
+        else {
+            uint8_t i = 0;
+            while (radio.available()) {
+                radio.read(&rx_fifo + (i * pl_size), pl_size);
+                i++;
+            }
+            rx_fifo[i * pl_size] = 0;            // add a NULL terminating char to use as a c-string
+        }
+        printf("Complete RX FIFO: %s\n", rx_fifo); // print the entire RX FIFO with 1 buffer
+    }
+}
+
+int main()
+{
+    stdio_init_all(); // init necessary IO for the RP2040
+
+    while (!setup()) { // if radio.begin() failed
+        // hold program in infinite attempts to initialize radio
+    }
+    while (true) {
+        loop();
+    }
+    return 0; // we will never reach this
+}
diff --git a/arduino/libraries/RF24/examples_pico/manualAcknowledgements.cpp 
b/arduino/libraries/RF24/examples_pico/manualAcknowledgements.cpp
new file mode 100644
index 000000000..9f40ecaf1
--- /dev/null
+++ b/arduino/libraries/RF24/examples_pico/manualAcknowledgements.cpp
@@ -0,0 +1,239 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty (2bndy5)
+ */
+
+/**
+ * A simple example of sending data from 1 nRF24L01 transceiver to another
+ * with manually transmitted (non-automatic) Acknowledgement (ACK) payloads.
+ * This example still uses ACK packets, but they have no payloads. Instead the
+ * acknowledging response is sent with `write()`. This tactic allows for more
+ * updated acknowledgement payload data, where actual ACK payloads' data are
+ * outdated by 1 transmission because they have to loaded before receiving a
+ * transmission.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use the Serial Terminal to change each node's behavior.
+ */
+#include "pico/stdlib.h"  // printf(), sleep_ms(), getchar_timeout_us(), to_us_since_boot(), 
get_absolute_time()
+#include "pico/bootrom.h" // reset_usb_boot()
+#include <tusb.h>         // tud_cdc_connected()
+#include <RF24.h>         // RF24 radio object
+#include "defaultPins.h"  // board presumptive default pin numbers for CE_PIN and CSN_PIN
+
+// instantiate an object for the nRF24L01 transceiver
+RF24 radio(CE_PIN, CSN_PIN);
+
+// Used to control whether this node is sending or receiving
+bool role = false;  // true = TX node, false = RX node
+
+// For this example, we'll be using a payload containing
+// a string & an integer number that will be incremented
+// on every successful transmission.
+// Make a data structure to store the entire payload of different datatypes
+struct PayloadStruct {
+  char message[7];          // only using 6 characters for TX & RX payloads
+  uint8_t counter;
+};
+PayloadStruct payload;
+
+bool setup()
+{
+    // append a NULL terminating character for printing as a c-string
+    payload.message[6] = 0;
+
+    // Let these addresses be used for the pair
+    uint8_t address[][6] = {"1Node", "2Node"};
+    // It is very helpful to think of an address as a path instead of as
+    // an identifying device destination
+
+    // to use different addresses on a pair of radios, we need a variable to
+    // uniquely identify which address this radio will use to transmit
+    bool radioNumber = 1; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+    // wait here until the CDC ACM (serial port emulation) is connected
+    while (!tud_cdc_connected()) {
+        sleep_ms(10);
+    }
+
+    // initialize the transceiver on the SPI bus
+    if (!radio.begin()) {
+        printf("radio hardware is not responding!!\n");
+        return false;
+    }
+
+    // print example's introductory prompt
+    printf("RF24/examples_pico/manualAcknowledgements\n");
+
+    // To set the radioNumber via the Serial monitor on startup
+    printf("Which radio is this? Enter '0' or '1'. Defaults to '0'\n");
+    char input = getchar();
+    radioNumber = input == 49;
+    printf("radioNumber = %d\n", (int)radioNumber);
+
+    // Set the PA Level low to try preventing power supply related problems
+    // because these examples are likely run with nodes in close proximity to
+    // each other.
+    radio.setPALevel(RF24_PA_LOW); // RF24_PA_MAX is default.
+
+    // save on transmission time by setting the radio to only transmit the
+    // number of bytes we need to transmit a float
+    radio.setPayloadSize(sizeof(payload)); // char[7] & uint8_t datatypes occupy 8 bytes
+
+    // set the TX address of the RX node into the TX pipe
+    radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0
+
+    // set the RX address of the TX node into a RX pipe
+    radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+    if (role) {
+        // setup the TX node
+
+        memcpy(payload.message, "Hello ", 6); // set the outgoing message
+        radio.stopListening();                // put radio in TX mode
+    }
+    else {
+        // setup the RX node
+
+        memcpy(payload.message, "World ", 6); // set the outgoing message
+        radio.startListening();               // put radio in RX mode
+    }
+
+    // For debugging info
+    // printf_begin();             // needed only once for printing details
+    // radio.printDetails();       // (smaller) function that prints raw register values
+    // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+    // role variable is hardcoded to RX behavior, inform the user of this
+    printf("*** PRESS 'T' to begin transmitting to the other node\n");
+
+    return true;
+} // setup()
+
+void loop()
+{
+    if (role) {
+        // This device is a TX node
+
+        uint64_t start_timer = to_us_since_boot(get_absolute_time()); // start the timer
+        bool report = radio.write(&payload, sizeof(payload));         // transmit & save the report
+
+        if (report) {
+            // transmission successful; wait for response and print results
+
+            radio.startListening();                                              // put in RX mode
+            uint64_t start_timeout = to_ms_since_boot(get_absolute_time());      // timer to detect timeout
+            while (!radio.available()) {                                         // wait for response
+                if (to_ms_since_boot(get_absolute_time()) - start_timeout > 200)                  // only 
wait 200 ms
+                    break;
+            }
+            uint64_t end_timer = to_us_since_boot(get_absolute_time());          // end the timer
+            radio.stopListening();                                               // put back in TX mode
+
+            // print summary of transactions
+            printf("Transmission successful!");            // payload was delivered
+            uint8_t pipe;
+            if (radio.available(&pipe)) {                  // is there a payload received
+                // print details about outgoing payload
+                printf(" Round-trip delay: %llu us. Sent: %s%d",
+                       end_timer - start_timer,
+                       payload.message,
+                       payload.counter);
+
+                PayloadStruct received;
+                radio.read(&received, sizeof(received)); // get payload from RX FIFO
+
+                // print details about incoming payload
+                printf(" Received %d bytes on pipe %d: %s%d\n",
+                       radio.getPayloadSize(),
+                       pipe,
+                       received.message,
+                       received.counter);
+
+                payload.counter = received.counter; // save incoming counter for next outgoing counter
+            }
+            else {
+                printf(" Recieved no response.\n"); // no response received
+            }
+        }
+        else {
+            printf("Transmission failed or timed out\n"); // payload was not delivered
+        } // report
+
+        // to make this example readable in the serial terminal
+        sleep_ms(1000); // slow transmissions down by 1 second
+    }
+    else {
+        // This device is a RX node
+
+        uint8_t pipe;
+        if (radio.available(&pipe)) {                // is there a payload? get the pipe number that 
recieved it
+            PayloadStruct received;
+            radio.read(&received, sizeof(received)); // get incoming payload
+            payload.counter = received.counter + 1;  // increment incoming counter for next outgoing response
+
+            // transmit response & save result to `report`
+            radio.stopListening();                       // put in TX mode
+            radio.writeFast(&payload, sizeof(payload));  // load response to TX FIFO
+            bool report = radio.txStandBy(150);          // keep retrying for 150 ms
+            radio.startListening();                      // put back in RX mode
+
+            // print summary of transactions, starting with details about incoming payload
+            printf("Received %d bytes on pipe %d: %s%d",
+                   radio.getPayloadSize(),
+                   pipe,
+                   received.message,
+                   received.counter);
+
+            if (report) {
+                // print outgoing payload and its counter
+                printf(" Sent: %s%d", payload.message, payload.counter);
+            }
+            else {
+                printf(" Response failed.\n");   // failed to send response
+            }
+        }
+    } // role
+
+    char input = getchar_timeout_us(0); // get char from buffer for user input
+    if (input != PICO_ERROR_TIMEOUT) {
+        // change the role via the serial terminal
+
+        if ((input == 'T' || input == 't') && !role) {
+            // Become the TX node
+
+            role = true;
+            memcpy(payload.message, "Hello ", 6); // set the outgoing message
+            printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n");
+            radio.stopListening();                // put in TX mode
+
+        }
+        else if ((input == 'R' || input == 'r') && role) {
+            // Become the RX node
+
+            role = false;
+            memcpy(payload.message, "World ", 6); // set the response message
+            printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n");
+            radio.startListening();               // put in RX mode
+        }
+        else if (input == 'b' || input == 'B') {
+            // reset to bootloader
+            radio.powerDown();
+            reset_usb_boot(0, 0);
+        }
+    }
+} // loop
+
+int main()
+{
+    stdio_init_all(); // init necessary IO for the RP2040
+
+    while (!setup()) { // if radio.begin() failed
+        // hold program in infinite attempts to initialize radio
+    }
+    while (true) {
+        loop();
+    }
+    return 0; // we will never reach this
+}
diff --git a/arduino/libraries/RF24/examples_pico/multiceiverDemo.cpp 
b/arduino/libraries/RF24/examples_pico/multiceiverDemo.cpp
new file mode 100644
index 000000000..d970ece84
--- /dev/null
+++ b/arduino/libraries/RF24/examples_pico/multiceiverDemo.cpp
@@ -0,0 +1,220 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty 2bndy5
+ */
+
+/**
+ * A simple example of sending data from as many as 6 nRF24L01 transceivers to
+ * 1 receiving transceiver. This technique is trademarked by
+ * Nordic Semiconductors as "MultiCeiver".
+ *
+ * This example was written to be used on up to 6 devices acting as TX nodes &
+ * only 1 device acting as the RX node (that's a maximum of 7 devices).
+ * Use the Serial Terminal to change each node's behavior.
+ */
+#include "pico/stdlib.h"  // printf(), sleep_ms(), getchar_timeout_us(), to_us_since_boot(), 
get_absolute_time()
+#include "pico/bootrom.h" // reset_usb_boot()
+#include <tusb.h>         // tud_cdc_connected()
+#include <RF24.h>         // RF24 radio object
+#include "defaultPins.h"  // board presumptive default pin numbers for CE_PIN and CSN_PIN
+
+// instantiate an object for the nRF24L01 transceiver
+RF24 radio(CE_PIN, CSN_PIN);
+
+// For this example, we'll be using 6 addresses; 1 for each TX node
+// It is very helpful to think of an address as a path instead of as
+// an identifying device destination
+// Notice that the last byte is the only byte that changes in the last 5
+// addresses. This is a limitation of the nRF24L01 transceiver for pipes 2-5
+// because they use the same first 4 bytes from pipe 1.
+uint64_t address[6] = {0x7878787878LL,
+                       0xB3B4B5B6F1LL,
+                       0xB3B4B5B6CDLL,
+                       0xB3B4B5B6A3LL,
+                       0xB3B4B5B60FLL,
+                       0xB3B4B5B605LL
+                      };
+
+// Because this example allow up to 6 nodes (specified by numbers 0-5) to
+// transmit and only 1 node to receive, we will use a negative value in our
+// role variable to signify this node is a receiver.
+// role variable is used to control whether this node is sending or receiving
+char role = 'R'; // integers 0-5 = TX node; character 'R' or integer 82 = RX node
+
+// For this example, we'll be using a payload containing
+// a node ID number and a single integer number that will be incremented
+// on every successful transmission.
+// Make a data structure to use as a payload.
+struct PayloadStruct
+{
+  unsigned long nodeID;
+  unsigned long payloadID;
+};
+PayloadStruct payload;
+
+// This example uses all 6 pipes to receive while TX nodes only use 2 pipes
+// To make this easier we'll use a function to manage the addresses, and the
+// payload's nodeID
+void setRole(); // declare a prototype; definition is found after the loop()
+
+
+bool setup()
+{
+    // wait here until the CDC ACM (serial port emulation) is connected
+    while (!tud_cdc_connected()) {
+        sleep_ms(10);
+    }
+
+    // initialize the transceiver on the SPI bus
+    if (!radio.begin()) {
+        printf("radio hardware is not responding!!\n");
+        return false;
+    }
+
+    // print example's introductory prompt
+    printf("RF24/examples_pico/multiceiverDemo\n");
+
+    // Set the PA Level low to try preventing power supply related problems
+    // because these examples are likely run with nodes in close proximity of
+    // each other.
+    radio.setPALevel(RF24_PA_LOW); // RF24_PA_MAX is default.
+
+    // save on transmission time by setting the radio to only transmit the
+    // number of bytes we need to transmit a float
+    radio.setPayloadSize(sizeof(payload)); // 2x int datatype occupy 8 bytes
+
+    // Set the pipe addresses accordingly. This function additionally also
+    // calls startListening() or stopListening() and sets the payload's nodeID
+    setRole();
+
+    // For debugging info
+    // printf_begin();             // needed only once for printing details
+    // radio.printDetails();       // (smaller) function that prints raw register values
+    // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+    // role variable is hardcoded to RX behavior, inform the user of this
+    printf("*** Enter a number between 0 and 5 (inclusive) to change\n");
+    printf("    the identifying node number that transmits.\n");
+
+    return true;
+} // setup()
+
+
+void loop()
+{
+    if (role <= 53) {
+        // This device is a TX node
+
+        uint64_t start_timer = to_us_since_boot(get_absolute_time()); // start the timer
+        bool report = radio.write(&payload, sizeof(payload));         // transmit & save the report
+        uint64_t end_timer = to_us_since_boot(get_absolute_time());   // end the timer
+
+        if (report) {
+            // payload was delivered
+            printf("Transmission of payloadID %ld as node %ld successful! Time to transmit: %llu us\n",
+                   payload.payloadID,
+                   payload.nodeID,
+                   end_timer - start_timer);
+        }
+        else {
+            printf("Transmission failed or timed out\n"); // payload was not delivered
+        }
+
+        payload.payloadID++; // increment payload number
+
+        // to make this example readable in the serial terminal
+        sleep_ms(1000); // slow transmissions down by 1 second
+    }
+    else if (role == 'R') {
+        // This device is the RX node
+
+        uint8_t pipe;
+        while (radio.available(&pipe)) {             // is there a payload? get the pipe number that 
recieved it
+            uint8_t bytes = radio.getPayloadSize(); // get the size of the payload
+            radio.read(&payload, bytes);            // fetch payload from FIFO
+
+            // print details about incoming payload
+            printf("Received %d bytes on pipe %d from node %ld. PayloadID: %ld\n",
+                   bytes,
+                   pipe,
+                   payload.nodeID,
+                   payload.payloadID);
+        }
+    } // role
+
+    char input = getchar_timeout_us(0); // get char from buffer for user input
+    if (input != PICO_ERROR_TIMEOUT) {
+        // change the role via the serial terminal
+
+        if ((input == 'R' || input == 'r') && role <= 53) {
+            // Become the RX node
+
+            role = 'R';
+            printf("*** CHANGING ROLE TO RECEIVER ***\n");
+            printf("--- Enter a number between 0 and 5 (inclusive) to act as\n");
+            printf("    a unique node number that transmits to the RX node.\n");
+            setRole(); // change address on all pipes to TX nodes
+
+        }
+        else if (input >= 48 && input <= 53 && input != role) {
+            // Become a TX node with identifier 'input'
+
+            role = input - 48;
+            printf("*** CHANGING ROLE TO NODE %c ***\n", input);
+            printf("--- Enter a number between 0 and 5 (inclusive) to change\n");
+            printf("    the identifying node number that transmits.\n");
+            printf("--- PRESS 'R' to act as the RX node.\n");
+            setRole(); // change address on pipe 0 to the RX node
+        }
+        else if (input == 'b' || input == 'B') {
+            // reset to bootloader
+            radio.powerDown();
+            reset_usb_boot(0, 0);
+        }
+    }
+
+} // loop
+
+void setRole()
+{
+    if (role == 'R') {
+        // For the RX node
+
+        // Set the addresses for all pipes to TX nodes
+        for (uint8_t i = 0; i < 6; ++i)
+            radio.openReadingPipe(i, address[i]);
+
+        radio.startListening(); // put radio in RX mode
+
+    }
+    else {
+        // For the TX node
+
+        // set the payload's nodeID & reset the payload's identifying number
+        payload.nodeID = role;
+        payload.payloadID = 0;
+
+        // Set the address on pipe 0 to the RX node.
+        radio.stopListening(); // put radio in TX mode
+        radio.openWritingPipe(address[role]);
+
+        // According to the datasheet, the auto-retry features's delay value should
+        // be "skewed" to allow the RX node to receive 1 transmission at a time.
+        // So, use varying delay between retry attempts and 15 (at most) retry attempts
+        radio.setRetries(((role * 3) % 12) + 3, 15); // maximum value is 15 for both args
+    }
+} // setRole
+
+int main()
+{
+    stdio_init_all(); // init necessary IO for the RP2040
+
+    while (!setup()) { // if radio.begin() failed
+        // hold program in infinite attempts to initialize radio
+    }
+    while (true) {
+        loop();
+    }
+    return 0; // we will never reach this
+}
diff --git a/arduino/libraries/RF24/examples_pico/scanner.cpp 
b/arduino/libraries/RF24/examples_pico/scanner.cpp
new file mode 100644
index 000000000..d67bd8b43
--- /dev/null
+++ b/arduino/libraries/RF24/examples_pico/scanner.cpp
@@ -0,0 +1,123 @@
+/*
+ Copyright (C) 2011 J. Coliz <maniacbug ymail com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+
+
+ 06/04/2021 : Brendan Doherty (https://github.com/2bndy5)
+              Modified to use with PicoSDK
+
+ */
+
+/**
+ * Channel scanner
+ *
+ * Example to detect interference on the various channels available.
+ * This is a good diagnostic tool to check whether you're picking a
+ * good channel for your application.
+ *
+ * Inspired by cpixip.
+ * See http://arduino.cc/forum/index.php/topic,54795.0.html
+ */
+
+#include "pico/stdlib.h"  // printf(), sleep_ms(), getchar_timeout_us(), to_us_since_boot(), 
get_absolute_time()
+#include "pico/bootrom.h" // reset_usb_boot()
+#include <tusb.h>         // tud_cdc_connected()
+#include <RF24.h>         // RF24 radio object, rf24_min()
+#include "defaultPins.h"  // board presumptive default pin numbers for CE_PIN and CSN_PIN
+
+// instantiate an object for the nRF24L01 transceiver
+RF24 radio(CE_PIN, CSN_PIN);
+
+// Channel info
+const uint8_t num_channels = 126;
+uint8_t values[num_channels];
+
+const int num_reps = 100;
+int reset_array = 0;
+
+int main()
+{
+    stdio_init_all(); // init necessary IO for the RP2040
+
+    // wait here until the CDC ACM (serial port emulation) is connected
+    while (!tud_cdc_connected()) {
+        sleep_ms(10);
+    }
+
+    // initialize the transceiver on the SPI bus
+    while (!radio.begin()) {
+        printf("radio hardware is not responding!!\n");
+    }
+
+    // print example's name
+    printf("RF24/examples_pico/scanner\n");
+
+    radio.setAutoAck(false);
+
+    // Get into standby mode
+    radio.startListening();
+    radio.stopListening();
+
+    // radio.printDetails();
+
+    // Print out header, high then low digit
+    int i = 0;
+
+    while (i < num_channels) {
+        printf("%x", i >> 4);
+        ++i;
+    }
+    printf("\n");
+
+    i = 0;
+    while (i < num_channels) {
+        printf("%x", i & 0xf);
+        ++i;
+    }
+    printf("\n");
+
+    // forever loop
+    while (1) {
+        // Clear measurement values
+        memset(values, 0, sizeof(values));
+
+        // Scan all channels num_reps times
+        int rep_counter = num_reps;
+        while (rep_counter--) {
+
+            int i = num_channels;
+            while (i--) {
+
+                // Select this channel
+                radio.setChannel(i);
+
+                // Listen for a little
+                radio.startListening();
+                sleep_us(128);
+                radio.stopListening();
+
+                // Did we get a carrier?
+                if (radio.testCarrier()) {
+                    ++values[i];
+                }
+            }
+        }
+
+        // Print out channel measurements, clamped to a single hex digit
+        i = 0;
+        while (i < num_channels) {
+            if (values[i])
+                printf("%x", rf24_min(0xf, (values[i] & 0xf)));
+            else
+                printf("-");
+
+            ++i;
+        }
+        printf("\n");
+    }
+
+    return 0;
+}
diff --git a/arduino/libraries/RF24/examples_pico/streamingData.cpp 
b/arduino/libraries/RF24/examples_pico/streamingData.cpp
new file mode 100644
index 000000000..be7f4d484
--- /dev/null
+++ b/arduino/libraries/RF24/examples_pico/streamingData.cpp
@@ -0,0 +1,199 @@
+/*
+ * See documentation at https://nRF24.github.io/RF24
+ * See License information at root directory of this library
+ * Author: Brendan Doherty 2bndy5
+ */
+
+/**
+ * A simple example of streaming data from 1 nRF24L01 transceiver to another.
+ *
+ * This example was written to be used on 2 devices acting as "nodes".
+ * Use the Serial Terminal to change each node's behavior.
+ */
+#include "pico/stdlib.h"  // printf(), sleep_ms(), getchar_timeout_us(), to_us_since_boot(), 
get_absolute_time()
+#include "pico/bootrom.h" // reset_usb_boot()
+#include <tusb.h>         // tud_cdc_connected()
+#include <math.h>         // abs()
+#include <RF24.h>         // RF24 radio object
+#include "defaultPins.h"  // board presumptive default pin numbers for CE_PIN and CSN_PIN
+
+// instantiate an object for the nRF24L01 transceiver
+RF24 radio(CE_PIN, CSN_PIN);
+
+// Used to control whether this node is sending or receiving
+bool role = false; // true = TX node, false = RX node
+
+// For this example, we'll be sending 32 payloads each containing
+// 32 bytes of data that looks like ASCII art when printed to the serial
+// monitor. The TX node and RX node needs only a single 32 byte buffer.
+#define SIZE 32            // this is the maximum for this example. (minimum is 1)
+char buffer[SIZE + 1];     // for the RX node
+uint8_t counter = 0;       // for counting the number of received payloads
+void makePayload(uint8_t); // prototype to construct a payload dynamically
+
+
+bool setup()
+{
+    buffer[SIZE] = 0; // add a NULL terminating character (for easy printing)
+
+    // Let these addresses be used for the pair
+    uint8_t address[][6] = {"1Node", "2Node"};
+    // It is very helpful to think of an address as a path instead of as
+    // an identifying device destination
+
+    // to use different addresses on a pair of radios, we need a variable to
+    // uniquely identify which address this radio will use to transmit
+    bool radioNumber; // 0 uses address[0] to transmit, 1 uses address[1] to transmit
+
+    // wait here until the CDC ACM (serial port emulation) is connected
+    while (!tud_cdc_connected()) {
+        sleep_ms(10);
+    }
+
+    // initialize the transceiver on the SPI bus
+    if (!radio.begin()) {
+        printf("radio hardware is not responding!!\n");
+        return false;
+    }
+
+    // print example's introductory prompt
+    printf("RF24/examples_pico/streamingData\n");
+
+    // To set the radioNumber via the Serial monitor on startup
+    printf("Which radio is this? Enter '0' or '1'. Defaults to '0'\n");
+    char input = getchar();
+    radioNumber = input == 49;
+    printf("radioNumber = %d\n", (int)radioNumber);
+
+    // Set the PA Level low to try preventing power supply related problems
+    // because these examples are likely run with nodes in close proximity to
+    // each other.
+    radio.setPALevel(RF24_PA_LOW);  // RF24_PA_MAX is default.
+
+    // save on transmission time by setting the radio to only transmit the
+    // number of bytes we need to transmit
+    radio.setPayloadSize(SIZE);     // default value is the maximum 32 bytes
+
+    // set the TX address of the RX node into the TX pipe
+    radio.openWritingPipe(address[radioNumber]);     // always uses pipe 0
+
+    // set the RX address of the TX node into a RX pipe
+    radio.openReadingPipe(1, address[!radioNumber]); // using pipe 1
+
+    // additional setup specific to the node's role
+    if (role) {
+        radio.stopListening();  // put radio in TX mode
+    }
+    else {
+        radio.startListening(); // put radio in RX mode
+    }
+
+    // For debugging info
+    // radio.printDetails();       // (smaller) function that prints raw register values
+    // radio.printPrettyDetails(); // (larger) function that prints human readable data
+
+    // role variable is hardcoded to RX behavior, inform the user of this
+    printf("*** PRESS 'T' to begin transmitting to the other node\n");
+
+    return true;
+} // setup()
+
+
+void loop() {
+
+    if (role) {
+        // This device is a TX node
+
+        radio.flush_tx();
+        uint8_t i = 0;
+        uint8_t failures = 0;
+        uint64_t start_timer = to_us_since_boot(get_absolute_time()); // start the timer
+        while (i < SIZE) {
+            makePayload(i);                                           // make the payload
+            if (!radio.writeFast(&buffer, SIZE)) {
+                failures++;
+                radio.reUseTX();
+            }
+            else {
+                i++;
+            }
+
+            if (failures >= 100) {
+                printf("Too many failures detected. Aborting at payload %c\n", buffer[0]);
+                break;
+            }
+        }
+        uint64_t end_timer = to_us_since_boot(get_absolute_time()); // end the timer
+
+        // print results from transmitting stream
+        printf("Time to transmit = %llu us with %d failures detected\n", end_timer - start_timer, failures);
+
+        // to make this example readable in the serial terminal
+        sleep_ms(1000); // slow transmissions down by 1 second
+    }
+    else {
+        // This device is a RX node
+
+        if (radio.available()) {         // is there a payload?
+            radio.read(&buffer, SIZE);     // fetch payload from FIFO
+
+            // print the received payload and its counter
+            printf("Received: %s - %d\n", buffer, counter++);
+        }
+    } // role
+
+    char input = getchar_timeout_us(0); // get char from buffer for user input
+    if (input != PICO_ERROR_TIMEOUT) {
+        // change the role via the serial terminal
+
+        if ((input == 'T' || input == 't') && !role) {
+            // Become the TX node
+
+            role = true;
+            counter = 0; //reset the RX node's counter
+            printf("*** CHANGING TO TRANSMIT ROLE -- PRESS 'R' TO SWITCH BACK\n");
+            radio.stopListening();
+
+        }
+        else if ((input == 'R' || input == 'r') && role) {
+            // Become the RX node
+
+            role = false;
+            printf("*** CHANGING TO RECEIVE ROLE -- PRESS 'T' TO SWITCH BACK\n");
+            radio.startListening();
+        }
+        else if (input == 'b' || input == 'B') {
+            // reset to bootloader
+            radio.powerDown();
+            reset_usb_boot(0, 0);
+        }
+    }
+} // loop
+
+
+void makePayload(uint8_t i) {
+    // Make a single payload based on position in stream.
+    // This example employs function to save memory on certain boards.
+
+    // let the first character be an identifying alphanumeric prefix
+    // this lets us see which payload didn't get received
+    buffer[0] = i + (i < 26 ? 65 : 71);
+    for (uint8_t j = 0; j < SIZE - 1; ++j) {
+        char chr = j >= (SIZE - 1) / 2 + abs((SIZE - 1) / 2 - i);
+        chr |= j < (SIZE - 1) / 2 - abs((SIZE - 1) / 2 - i);
+        buffer[j + 1] = chr + 48;
+    }
+}
+
+int main()
+{
+    stdio_init_all(); // init necessary IO for the RP2040
+
+    while (!setup()) { // if radio.begin() failed
+        // hold program in infinite attempts to initialize radio
+    }
+    while (true) {
+        loop();
+    }
+    return 0; // we will never reach this
+}
diff --git a/arduino/libraries/RF24/images/ghetto_sheilding_1.png 
b/arduino/libraries/RF24/images/ghetto_sheilding_1.png
new file mode 100644
index 000000000..1cdaaced2
Binary files /dev/null and b/arduino/libraries/RF24/images/ghetto_sheilding_1.png differ
diff --git a/arduino/libraries/RF24/images/ghetto_sheilding_2.png 
b/arduino/libraries/RF24/images/ghetto_sheilding_2.png
new file mode 100644
index 000000000..5480898dc
Binary files /dev/null and b/arduino/libraries/RF24/images/ghetto_sheilding_2.png differ
diff --git a/arduino/libraries/RF24/images/pinout.jpg b/arduino/libraries/RF24/images/pinout.jpg
new file mode 100644
index 000000000..f83ddb709
Binary files /dev/null and b/arduino/libraries/RF24/images/pinout.jpg differ
diff --git a/arduino/libraries/RF24/keywords.txt b/arduino/libraries/RF24/keywords.txt
new file mode 100644
index 000000000..5e39f1e88
--- /dev/null
+++ b/arduino/libraries/RF24/keywords.txt
@@ -0,0 +1,59 @@
+RF24                     KEYWORD1
+begin                    KEYWORD2
+isChipConnected         KEYWORD2
+startListening         KEYWORD2
+stopListening            KEYWORD2
+available                    KEYWORD2
+read                     KEYWORD2
+write                    KEYWORD2
+openWritingPipe                KEYWORD2
+openReadingPipe                KEYWORD2
+printDetails             KEYWORD2
+printPrettyDetails      KEYWORD2
+rxFifoFull              KEYWORD2
+powerDown               KEYWORD2
+powerUp                        KEYWORD2
+writeFast                    KEYWORD2
+writeBlocking            KEYWORD2
+txStandBy                    KEYWORD2
+writeAckPayload         KEYWORD2
+whatHappened            KEYWORD2
+startFastWrite          KEYWORD2
+startWrite              KEYWORD2
+reUseTX                 KEYWORD2
+flush_tx                KEYWORD2
+flush_rx                KEYWORD2
+testCarrier             KEYWORD2
+testRPD                 KEYWORD2
+isValid                 KEYWORD2
+closeReadingPipe        KEYWORD2
+failureDetected         KEYWORD2
+setAddressWidth         KEYWORD2
+setRetries                 KEYWORD2
+setChannel                 KEYWORD2
+getChannel              KEYWORD2
+setPayloadSize         KEYWORD2
+getPayloadSize         KEYWORD2
+getDynamicPayloadSize   KEYWORD2
+enableAckPayload        KEYWORD2
+disableAckPayload       KEYWORD2
+enableDynamicPayloads   KEYWORD2
+disableDynamicPayloads  KEYWORD2
+isPVariant              KEYWORD2
+setAutoAck                 KEYWORD2
+setPALevel                 KEYWORD2
+getPALevel              KEYWORD2
+getARC                  KEYWORD2
+setDataRate             KEYWORD2
+getDataRate             KEYWORD2
+setCRCLength             KEYWORD2
+getCRCLength            KEYWORD2
+disableCRC              KEYWORD2
+maskIRQ                 KEYWORD2
+txDelay                 KEYWORD2
+csDelay                 KEYWORD2
+startConstCarrier       KEYWORD2
+stopConstCarrier        KEYWORD2
+enableDynamicAck        KEYWORD2
+isAckPayloadAvailable   KEYWORD2
+printf_begin             KEYWORD2
\ No newline at end of file
diff --git a/arduino/libraries/RF24/library.json b/arduino/libraries/RF24/library.json
new file mode 100644
index 000000000..79208dbd0
--- /dev/null
+++ b/arduino/libraries/RF24/library.json
@@ -0,0 +1,43 @@
+{
+    "name": "RF24",
+    "keywords": "rf, radio, wireless, spi",
+    "description": "Radio driver, OSI layer 2 library for nRF24L01(+) transceiver modules.",
+    "license": "GPL-2.0-only",
+    "repository": {
+        "type": "git",
+        "url": "https://github.com/nRF24/RF24.git";
+    },
+    "version": "1.4.2",
+    "export": {
+        "exclude": [
+            "datasheets",
+            ".github/*",
+            "keywords.txt",
+            "configure",
+            "examples_pico/*",
+            "examples_linux/*",
+            "pyRF24/*",
+            "cmake/*",
+            "CMakeLists.txt",
+            "Makefile",
+            "utility/CMakeLists.txt",
+            "utility/wiringPi/*",
+            "utility/MRAA/*",
+            "utility/LittleWire/*",
+            "utility/RPi/*",
+            "utility/SPIDEV/*",
+            "utility/rp2/*",
+            "utility/ATXMegaD3/*"
+        ]
+    },
+    "frameworks": "arduino",
+    "platforms": [
+        "atmelavr",
+        "atmelsam",
+        "teensy",
+        "atmelmegaavr",
+        "espressif32",
+        "espressif8266",
+        "ststm32"
+    ]
+}
diff --git a/arduino/libraries/RF24/library.properties b/arduino/libraries/RF24/library.properties
new file mode 100644
index 000000000..2fa682c5b
--- /dev/null
+++ b/arduino/libraries/RF24/library.properties
@@ -0,0 +1,9 @@
+name=RF24
+version=1.4.2
+author=TMRh20
+maintainer=TMRh20,Avamander
+sentence=Radio driver, OSI layer 2 library for nrf24L01(+) modules.
+paragraph=Core library for nRF24L01(+) communication. Simple to use for beginners, but offers advanced 
configuration options. Many examples are included to demonstrate various modes of communication.
+category=Communication
+url=https://nRF24.github.io/RF24/
+architectures=*
diff --git a/arduino/libraries/RF24/nRF24L01.h b/arduino/libraries/RF24/nRF24L01.h
new file mode 100644
index 000000000..7ed9d267c
--- /dev/null
+++ b/arduino/libraries/RF24/nRF24L01.h
@@ -0,0 +1,128 @@
+/*
+    Copyright (c) 2007 Stefan Engelke <mbox stefanengelke de>
+       Portions Copyright (C) 2011 Greg Copeland
+
+    Permission is hereby granted, free of charge, to any person
+    obtaining a copy of this software and associated documentation
+    files (the "Software"), to deal in the Software without
+    restriction, including without limitation the rights to use, copy,
+    modify, merge, publish, distribute, sublicense, and/or sell copies
+    of the Software, and to permit persons to whom the Software is
+    furnished to do so, subject to the following conditions:
+
+    The above copyright notice and this permission notice shall be
+    included in all copies or substantial portions of the Software.
+
+    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+    MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
+    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+    OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+    DEALINGS IN THE SOFTWARE.
+*/
+
+/* Memory Map */
+#define NRF_CONFIG  0x00
+#define EN_AA       0x01
+#define EN_RXADDR   0x02
+#define SETUP_AW    0x03
+#define SETUP_RETR  0x04
+#define RF_CH       0x05
+#define RF_SETUP    0x06
+#define NRF_STATUS  0x07
+#define OBSERVE_TX  0x08
+#define CD          0x09
+#define RX_ADDR_P0  0x0A
+#define RX_ADDR_P1  0x0B
+#define RX_ADDR_P2  0x0C
+#define RX_ADDR_P3  0x0D
+#define RX_ADDR_P4  0x0E
+#define RX_ADDR_P5  0x0F
+#define TX_ADDR     0x10
+#define RX_PW_P0    0x11
+#define RX_PW_P1    0x12
+#define RX_PW_P2    0x13
+#define RX_PW_P3    0x14
+#define RX_PW_P4    0x15
+#define RX_PW_P5    0x16
+#define FIFO_STATUS 0x17
+#define DYNPD       0x1C
+#define FEATURE     0x1D
+
+/* Bit Mnemonics */
+#define MASK_RX_DR  6
+#define MASK_TX_DS  5
+#define MASK_MAX_RT 4
+#define EN_CRC      3
+#define CRCO        2
+#define PWR_UP      1
+#define PRIM_RX     0
+#define ENAA_P5     5
+#define ENAA_P4     4
+#define ENAA_P3     3
+#define ENAA_P2     2
+#define ENAA_P1     1
+#define ENAA_P0     0
+#define ERX_P5      5
+#define ERX_P4      4
+#define ERX_P3      3
+#define ERX_P2      2
+#define ERX_P1      1
+#define ERX_P0      0
+#define AW          0
+#define ARD         4
+#define ARC         0
+#define PLL_LOCK    4
+#define CONT_WAVE   7
+#define RF_DR       3
+#define RF_PWR      6
+#define RX_DR       6
+#define TX_DS       5
+#define MAX_RT      4
+#define RX_P_NO     1
+#define TX_FULL     0
+#define PLOS_CNT    4
+#define ARC_CNT     0
+#define TX_REUSE    6
+#define FIFO_FULL   5
+#define TX_EMPTY    4
+#define RX_FULL     1
+#define RX_EMPTY    0
+#define DPL_P5      5
+#define DPL_P4      4
+#define DPL_P3      3
+#define DPL_P2      2
+#define DPL_P1      1
+#define DPL_P0      0
+#define EN_DPL      2
+#define EN_ACK_PAY  1
+#define EN_DYN_ACK  0
+
+/* Instruction Mnemonics */
+#define R_REGISTER    0x00
+#define W_REGISTER    0x20
+#define REGISTER_MASK 0x1F
+#define ACTIVATE      0x50
+#define R_RX_PL_WID   0x60
+#define R_RX_PAYLOAD  0x61
+#define W_TX_PAYLOAD  0xA0
+#define W_ACK_PAYLOAD 0xA8
+#define FLUSH_TX      0xE1
+#define FLUSH_RX      0xE2
+#define REUSE_TX_PL   0xE3
+#define RF24_NOP      0xFF
+
+/* Non-P omissions */
+#define LNA_HCURR   0
+
+/* P model memory Map */
+#define RPD                  0x09
+#define W_TX_PAYLOAD_NO_ACK  0xB0
+
+/* P model bit Mnemonics */
+#define RF_DR_LOW   5
+#define RF_DR_HIGH  3
+#define RF_PWR_LOW  1
+#define RF_PWR_HIGH 2
diff --git a/arduino/libraries/RF24/printf.h b/arduino/libraries/RF24/printf.h
new file mode 100644
index 000000000..31f8c5d5a
--- /dev/null
+++ b/arduino/libraries/RF24/printf.h
@@ -0,0 +1,46 @@
+/*
+ Copyright (C) 2011 J. Coliz <maniacbug ymail com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+ */
+/*  Galileo support from spaniakos <spaniakos gmail com> */
+
+/**
+ * @file printf.h
+ *
+ * Setup necessary to direct stdout to the Arduino Serial library, which
+ * enables 'printf'
+ */
+
+#ifndef __PRINTF_H__
+#define __PRINTF_H__
+
+#if defined (ARDUINO_ARCH_AVR) || defined (__ARDUINO_X86__)
+
+int serial_putc(char c, FILE *)
+{
+    Serial.write(c);
+    return c;
+}
+
+#elif defined (ARDUINO_ARCH_MBED)
+REDIRECT_STDOUT_TO(Serial);
+
+#endif // defined (ARDUINO_ARCH_AVR) || defined (__ARDUINO_X86__) || defined (ARDUINO_ARCH_MBED)
+
+void printf_begin(void)
+{
+    #if defined (ARDUINO_ARCH_AVR)
+    fdevopen(&serial_putc, 0);
+
+    #elif defined (__ARDUINO_X86__)
+    // For redirect stdout to /dev/ttyGS0 (Serial Monitor port)
+    stdout = freopen("/dev/ttyGS0", "w", stdout);
+    delay(500);
+    printf("Redirecting to Serial...");
+    #endif // defined(__ARDUINO_X86__)
+}
+
+#endif // __PRINTF_H__
diff --git a/arduino/libraries/RF24/pyRF24/crossunixccompiler.py 
b/arduino/libraries/RF24/pyRF24/crossunixccompiler.py
new file mode 100644
index 000000000..881d907d3
--- /dev/null
+++ b/arduino/libraries/RF24/pyRF24/crossunixccompiler.py
@@ -0,0 +1,70 @@
+import sys
+from distutils import unixccompiler
+from distutils import ccompiler
+
+
+def register():
+    sys.modules["distutils.crossunixccompiler"] = sys.modules[__name__]
+    ccompiler.compiler_class["crossunix"] = (
+        __name__,
+        "CrossUnixCCompiler",
+        "UNIX-style compiler for cross compilation",
+    )
+
+
+def try_remove_all(lst, starts):
+    lst[:] = [x for x in lst if not x.startswith(starts)]
+
+
+class CrossUnixCCompiler(unixccompiler.UnixCCompiler):
+    def _compile(self, obj, src, ext, cc_args, extra_postargs, pp_opts):
+        try_remove_all(
+            self.compiler_so, ("-m64", "-fstack-protector-strong", "-mtune=generic")
+        )
+        try_remove_all(cc_args, "-I/usr")
+        try_remove_all(pp_opts, "-I/usr")
+        return unixccompiler.UnixCCompiler._compile(
+            self, obj, src, ext, cc_args, extra_postargs, pp_opts
+        )
+
+    def link(
+        self,
+        target_desc,
+        objects,
+        output_filename,
+        output_dir=None,
+        libraries=None,
+        library_dirs=None,
+        runtime_library_dirs=None,
+        export_symbols=None,
+        debug=0,
+        extra_preargs=None,
+        extra_postargs=None,
+        build_temp=None,
+        target_lang=None,
+    ):
+        try_remove_all(self.library_dirs, ("/usr"))
+        return unixccompiler.UnixCCompiler.link(
+            self,
+            target_desc,
+            objects,
+            output_filename,
+            output_dir,
+            libraries,
+            library_dirs,
+            runtime_library_dirs,
+            export_symbols,
+            debug,
+            extra_preargs,
+            extra_postargs,
+            build_temp,
+            target_lang,
+        )
+
+    def _fix_lib_args(self, libraries, library_dirs, runtime_library_dirs):
+        self.__class__ = unixccompiler.UnixCCompiler
+        ret = unixccompiler.UnixCCompiler._fix_lib_args(
+            self, libraries, library_dirs, runtime_library_dirs
+        )
+        self.__class__ = CrossUnixCCompiler
+        return ret
diff --git a/arduino/libraries/RF24/pyRF24/pyRF24.cpp b/arduino/libraries/RF24/pyRF24/pyRF24.cpp
new file mode 100644
index 000000000..9e72c00b9
--- /dev/null
+++ b/arduino/libraries/RF24/pyRF24/pyRF24.cpp
@@ -0,0 +1,338 @@
+#include <boost/python.hpp>
+#include <RF24/RF24.h>
+
+namespace bp = boost::python;
+
+// ******************** explicit wrappers **************************
+// for methods which need it - mostly for buffer operations
+//
+
+void throw_ba_exception(void)
+{
+    PyErr_SetString(PyExc_TypeError, "buf parameter must be bytes or bytearray");
+    bp::throw_error_already_set();
+}
+
+char* get_bytes_or_bytearray_str(bp::object buf)
+{
+    PyObject* py_ba;
+    py_ba = buf.ptr();
+    if (PyByteArray_Check(py_ba))
+        return PyByteArray_AsString(py_ba);
+    else if (PyBytes_Check(py_ba))
+        return PyBytes_AsString(py_ba);
+    else
+        throw_ba_exception();
+
+    return NULL;
+}
+
+int get_bytes_or_bytearray_ln(bp::object buf)
+{
+    PyObject* py_ba;
+    py_ba = buf.ptr();
+    if (PyByteArray_Check(py_ba))
+        return PyByteArray_Size(py_ba);
+    else if (PyBytes_Check(py_ba))
+        return PyBytes_Size(py_ba);
+    else
+        throw_ba_exception();
+
+    return 0;
+}
+
+bp::object read_wrap(RF24& ref, int maxlen)
+{
+    char* buf = new char[maxlen + 1];
+    ref.read(buf, maxlen);
+    bp::object
+    py_ba(bp::handle<>(PyByteArray_FromStringAndSize(buf, maxlen < ref.getPayloadSize() ? maxlen : 
ref.getPayloadSize())));
+    delete[] buf;
+    return py_ba;
+}
+
+bool write_wrap1(RF24& ref, bp::object buf)
+{
+    return ref.write(get_bytes_or_bytearray_str(buf), get_bytes_or_bytearray_ln(buf));
+}
+
+bool write_wrap2(RF24& ref, bp::object buf, const bool multicast)
+{
+    return ref.write(get_bytes_or_bytearray_str(buf), get_bytes_or_bytearray_ln(buf), multicast);
+}
+
+void writeAckPayload_wrap(RF24& ref, uint8_t pipe, bp::object buf)
+{
+    ref.writeAckPayload(pipe, get_bytes_or_bytearray_str(buf), get_bytes_or_bytearray_ln(buf));
+}
+
+bool writeFast_wrap1(RF24& ref, bp::object buf)
+{
+    return ref.writeFast(get_bytes_or_bytearray_str(buf), get_bytes_or_bytearray_ln(buf));
+}
+
+bool writeFast_wrap2(RF24& ref, bp::object buf, const bool multicast)
+{
+    return ref.writeFast(get_bytes_or_bytearray_str(buf), get_bytes_or_bytearray_ln(buf), multicast);
+}
+
+bool writeBlocking_wrap(RF24& ref, bp::object buf, uint32_t timeout)
+{
+    return ref.writeBlocking(get_bytes_or_bytearray_str(buf), get_bytes_or_bytearray_ln(buf), timeout);
+}
+
+void startFastWrite_wrap1(RF24& ref, bp::object buf, const bool multicast)
+{
+    ref.startFastWrite(get_bytes_or_bytearray_str(buf), get_bytes_or_bytearray_ln(buf), multicast);
+}
+
+void startFastWrite_wrap2(RF24& ref, bp::object buf, const bool multicast, bool startTx)
+{
+    ref.startFastWrite(get_bytes_or_bytearray_str(buf), get_bytes_or_bytearray_ln(buf), multicast, startTx);
+}
+
+void startWrite_wrap(RF24& ref, bp::object buf, const bool multicast)
+{
+    ref.startWrite(get_bytes_or_bytearray_str(buf), get_bytes_or_bytearray_ln(buf), multicast);
+}
+
+void openWritingPipe_wrap(RF24& ref, const bp::object address)
+{
+    ref.openWritingPipe((const uint8_t*) (get_bytes_or_bytearray_str(address)));
+}
+
+void openReadingPipe_wrap(RF24& ref, uint8_t number, const bp::object address)
+{
+    ref.openReadingPipe(number, (const uint8_t*) (get_bytes_or_bytearray_str(address)));
+}
+
+bp::tuple whatHappened_wrap(RF24& ref)
+{
+    bool tx_ok;
+    bool tx_fail;
+    bool tx_ready;
+
+    ref.whatHappened(tx_ok, tx_fail, tx_ready);
+    return bp::make_tuple(tx_ok, tx_fail, tx_ready);
+}
+
+bp::tuple available_wrap(RF24& ref)
+{
+    bool result;
+    uint8_t pipe;
+
+    result = ref.available(&pipe);
+    return bp::make_tuple(result, pipe);
+}
+
+void setPALevel_wrap(RF24& ref, rf24_pa_dbm_e level)
+{
+    ref.setPALevel(level, 1);
+}
+
+bool begin_with_pins(RF24& ref, uint16_t _cepin, uint16_t _cspin)
+{
+    return ref.begin(_cepin, _cspin);
+}
+
+BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(txStandBy_wrap1, RF24::txStandBy, 0, 2)
+//BOOST_PYTHON_FUNCTION_OVERLOADS(txStandBy_wrap2, RF24::txStandBy, 1, 2)
+
+// ******************** enums **************************
+// from both RF24 and bcm2835
+
+BOOST_PYTHON_MODULE(RF24)
+{
+
+#ifdef BCM2835_H
+    bp::enum_< RPiGPIOPin>("RPiGPIOPin")
+        .value("RPI_GPIO_P1_03", RPI_GPIO_P1_03)
+        .value("RPI_GPIO_P1_05", RPI_GPIO_P1_05)
+        .value("RPI_GPIO_P1_07", RPI_GPIO_P1_07)
+        .value("RPI_GPIO_P1_08", RPI_GPIO_P1_08)
+        .value("RPI_GPIO_P1_10", RPI_GPIO_P1_10)
+        .value("RPI_GPIO_P1_11", RPI_GPIO_P1_11)
+        .value("RPI_GPIO_P1_12", RPI_GPIO_P1_12)
+        .value("RPI_GPIO_P1_13", RPI_GPIO_P1_13)
+        .value("RPI_GPIO_P1_15", RPI_GPIO_P1_15)
+        .value("RPI_GPIO_P1_16", RPI_GPIO_P1_16)
+        .value("RPI_GPIO_P1_18", RPI_GPIO_P1_18)
+        .value("RPI_GPIO_P1_19", RPI_GPIO_P1_19)
+        .value("RPI_GPIO_P1_21", RPI_GPIO_P1_21)
+        .value("RPI_GPIO_P1_22", RPI_GPIO_P1_22)
+        .value("RPI_GPIO_P1_23", RPI_GPIO_P1_23)
+        .value("RPI_GPIO_P1_24", RPI_GPIO_P1_24)
+        .value("RPI_GPIO_P1_26", RPI_GPIO_P1_26)
+        .value("RPI_V2_GPIO_P1_03", RPI_V2_GPIO_P1_03)
+        .value("RPI_V2_GPIO_P1_05", RPI_V2_GPIO_P1_05)
+        .value("RPI_V2_GPIO_P1_07", RPI_V2_GPIO_P1_07)
+        .value("RPI_V2_GPIO_P1_08", RPI_V2_GPIO_P1_08)
+        .value("RPI_V2_GPIO_P1_10", RPI_V2_GPIO_P1_10)
+        .value("RPI_V2_GPIO_P1_11", RPI_V2_GPIO_P1_11)
+        .value("RPI_V2_GPIO_P1_12", RPI_V2_GPIO_P1_12)
+        .value("RPI_V2_GPIO_P1_13", RPI_V2_GPIO_P1_13)
+        .value("RPI_V2_GPIO_P1_15", RPI_V2_GPIO_P1_15)
+        .value("RPI_V2_GPIO_P1_16", RPI_V2_GPIO_P1_16)
+        .value("RPI_V2_GPIO_P1_18", RPI_V2_GPIO_P1_18)
+        .value("RPI_V2_GPIO_P1_19", RPI_V2_GPIO_P1_19)
+        .value("RPI_V2_GPIO_P1_21", RPI_V2_GPIO_P1_21)
+        .value("RPI_V2_GPIO_P1_22", RPI_V2_GPIO_P1_22)
+        .value("RPI_V2_GPIO_P1_23", RPI_V2_GPIO_P1_23)
+        .value("RPI_V2_GPIO_P1_24", RPI_V2_GPIO_P1_24)
+        .value("RPI_V2_GPIO_P1_26", RPI_V2_GPIO_P1_26)
+        .value("RPI_V2_GPIO_P5_03", RPI_V2_GPIO_P5_03)
+        .value("RPI_V2_GPIO_P5_04", RPI_V2_GPIO_P5_04)
+        .value("RPI_V2_GPIO_P5_05", RPI_V2_GPIO_P5_05)
+        .value("RPI_V2_GPIO_P5_06", RPI_V2_GPIO_P5_06)
+        .value("RPI_BPLUS_GPIO_J8_03", RPI_BPLUS_GPIO_J8_03)
+        .value("RPI_BPLUS_GPIO_J8_05", RPI_BPLUS_GPIO_J8_05)
+        .value("RPI_BPLUS_GPIO_J8_07", RPI_BPLUS_GPIO_J8_07)
+        .value("RPI_BPLUS_GPIO_J8_08", RPI_BPLUS_GPIO_J8_08)
+        .value("RPI_BPLUS_GPIO_J8_10", RPI_BPLUS_GPIO_J8_10)
+        .value("RPI_BPLUS_GPIO_J8_11", RPI_BPLUS_GPIO_J8_11)
+        .value("RPI_BPLUS_GPIO_J8_12", RPI_BPLUS_GPIO_J8_12)
+        .value("RPI_BPLUS_GPIO_J8_13", RPI_BPLUS_GPIO_J8_13)
+        .value("RPI_BPLUS_GPIO_J8_15", RPI_BPLUS_GPIO_J8_15)
+        .value("RPI_BPLUS_GPIO_J8_16", RPI_BPLUS_GPIO_J8_16)
+        .value("RPI_BPLUS_GPIO_J8_18", RPI_BPLUS_GPIO_J8_18)
+        .value("RPI_BPLUS_GPIO_J8_19", RPI_BPLUS_GPIO_J8_19)
+        .value("RPI_BPLUS_GPIO_J8_21", RPI_BPLUS_GPIO_J8_21)
+        .value("RPI_BPLUS_GPIO_J8_22", RPI_BPLUS_GPIO_J8_22)
+        .value("RPI_BPLUS_GPIO_J8_23", RPI_BPLUS_GPIO_J8_23)
+        .value("RPI_BPLUS_GPIO_J8_24", RPI_BPLUS_GPIO_J8_24)
+        .value("RPI_BPLUS_GPIO_J8_26", RPI_BPLUS_GPIO_J8_26)
+        .value("RPI_BPLUS_GPIO_J8_29", RPI_BPLUS_GPIO_J8_29)
+        .value("RPI_BPLUS_GPIO_J8_31", RPI_BPLUS_GPIO_J8_31)
+        .value("RPI_BPLUS_GPIO_J8_32", RPI_BPLUS_GPIO_J8_32)
+        .value("RPI_BPLUS_GPIO_J8_33", RPI_BPLUS_GPIO_J8_33)
+        .value("RPI_BPLUS_GPIO_J8_35", RPI_BPLUS_GPIO_J8_35)
+        .value("RPI_BPLUS_GPIO_J8_36", RPI_BPLUS_GPIO_J8_36)
+        .value("RPI_BPLUS_GPIO_J8_37", RPI_BPLUS_GPIO_J8_37)
+        .value("RPI_BPLUS_GPIO_J8_38", RPI_BPLUS_GPIO_J8_38)
+        .value("RPI_BPLUS_GPIO_J8_40", RPI_BPLUS_GPIO_J8_40)
+        .export_values()
+        ;
+
+    bp::enum_< bcm2835SPIClockDivider>("bcm2835SPIClockDivider")
+        .value("BCM2835_SPI_CLOCK_DIVIDER_65536", BCM2835_SPI_CLOCK_DIVIDER_65536)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_32768", BCM2835_SPI_CLOCK_DIVIDER_32768)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_16384", BCM2835_SPI_CLOCK_DIVIDER_16384)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_8192", BCM2835_SPI_CLOCK_DIVIDER_8192)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_4096", BCM2835_SPI_CLOCK_DIVIDER_4096)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_2048", BCM2835_SPI_CLOCK_DIVIDER_2048)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_1024", BCM2835_SPI_CLOCK_DIVIDER_1024)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_512", BCM2835_SPI_CLOCK_DIVIDER_512)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_256", BCM2835_SPI_CLOCK_DIVIDER_256)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_128", BCM2835_SPI_CLOCK_DIVIDER_128)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_64", BCM2835_SPI_CLOCK_DIVIDER_64)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_32", BCM2835_SPI_CLOCK_DIVIDER_32)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_16", BCM2835_SPI_CLOCK_DIVIDER_16)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_8", BCM2835_SPI_CLOCK_DIVIDER_8)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_4", BCM2835_SPI_CLOCK_DIVIDER_4)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_2", BCM2835_SPI_CLOCK_DIVIDER_2)
+        .value("BCM2835_SPI_CLOCK_DIVIDER_1", BCM2835_SPI_CLOCK_DIVIDER_1)
+        .export_values();
+
+
+    bp::enum_< bcm2835SPIChipSelect>("bcm2835SPIChipSelect")
+        .value("BCM2835_SPI_CS0", BCM2835_SPI_CS0)
+        .value("BCM2835_SPI_CS1", BCM2835_SPI_CS1)
+        .value("BCM2835_SPI_CS2", BCM2835_SPI_CS2)
+        .value("BCM2835_SPI_CS_NONE", BCM2835_SPI_CS_NONE)
+        .export_values();
+
+#endif  // BCM2835_H
+
+    bp::enum_<rf24_crclength_e>("rf24_crclength_e")
+        .value("RF24_CRC_DISABLED", RF24_CRC_DISABLED)
+        .value("RF24_CRC_8", RF24_CRC_8)
+        .value("RF24_CRC_16", RF24_CRC_16)
+        .export_values();
+
+    bp::enum_<rf24_datarate_e>("rf24_datarate_e")
+        .value("RF24_1MBPS", RF24_1MBPS)
+        .value("RF24_2MBPS", RF24_2MBPS)
+        .value("RF24_250KBPS", RF24_250KBPS)
+        .export_values();
+
+    bp::enum_<rf24_pa_dbm_e>("rf24_pa_dbm_e")
+        .value("RF24_PA_MIN", RF24_PA_MIN)
+        .value("RF24_PA_LOW", RF24_PA_LOW)
+        .value("RF24_PA_HIGH", RF24_PA_HIGH)
+        .value("RF24_PA_MAX", RF24_PA_MAX)
+        .value("RF24_PA_ERROR", RF24_PA_ERROR)
+        .export_values();
+
+    // ******************** RF24 class  **************************
+    bp::class_< RF24 >("RF24", bp::init< uint16_t, uint16_t >((bp::arg("_cepin"), bp::arg("_cspin"))))
+        #if defined (RF24_LINUX) && !defined (MRAA)
+        .def(bp::init< uint16_t, uint16_t, uint32_t >((bp::arg("_cepin"), bp::arg("_cspin"), 
bp::arg("spispeed"))))
+        .def(bp::init< uint32_t >((bp::arg("spispeed"))))
+        .def(bp::init< >())
+        #endif
+        .def("available", (bool (::RF24::*)())(&::RF24::available))
+        .def("available_pipe", &available_wrap)    // needed to rename this method as python does not allow 
such overloading
+        .def("begin", (bool (::RF24::*)(void))(&::RF24::begin))
+        .def("begin", &begin_with_pins)
+        .def("closeReadingPipe", &RF24::closeReadingPipe)
+        .def("disableCRC", &RF24::disableCRC)
+        .def("enableAckPayload", &RF24::enableAckPayload)
+        .def("enableDynamicAck", &RF24::enableDynamicAck)
+        .def("enableDynamicPayloads", &RF24::enableDynamicPayloads)
+        .def("disableDynamicPayloads", &RF24::disableDynamicPayloads)
+        .def("flush_tx", &RF24::flush_tx)
+        .def("flush_rx", &RF24::flush_rx)
+        .def("getCRCLength", &RF24::getCRCLength)
+        .def("getDataRate", &RF24::getDataRate)
+        .def("getDynamicPayloadSize", &RF24::getDynamicPayloadSize)
+        .def("getPALevel", &RF24::getPALevel)
+        .def("isAckPayloadAvailable", &RF24::isAckPayloadAvailable)
+        .def("isPVariant", &RF24::isPVariant)
+        .def("isValid", &RF24::isValid)
+        .def("maskIRQ", &RF24::maskIRQ, (bp::arg("tx_ok"), bp::arg("tx_fail"), bp::arg("rx_ready")))
+        .def("openReadingPipe", &openReadingPipe_wrap, (bp::arg("number"), bp::arg("address")))
+        .def("openReadingPipe", (void (::RF24::*)(::uint8_t,::uint64_t))(&::RF24::openReadingPipe), 
(bp::arg("number"), bp::arg("address")))
+        .def("openWritingPipe", &openWritingPipe_wrap, (bp::arg("address")))
+        .def("openWritingPipe", (void (::RF24::*)(::uint64_t))(&::RF24::openWritingPipe), 
(bp::arg("address")))
+        .def("powerDown", &RF24::powerDown)
+        .def("powerUp", &RF24::powerUp)
+        .def("printDetails", &RF24::printDetails)
+        .def("printPrettyDetails", &RF24::printPrettyDetails)
+        .def("reUseTX", &RF24::reUseTX)
+        .def("read", &read_wrap, (bp::arg("maxlen")))
+        .def("rxFifoFull", &RF24::rxFifoFull)
+        .def("setAddressWidth", &RF24::setAddressWidth)
+        .def("setAutoAck", (void (::RF24::*)(bool))(&::RF24::setAutoAck), (bp::arg("enable")))
+        .def("setAutoAck", (void (::RF24::*)(::uint8_t, bool))(&::RF24::setAutoAck), (bp::arg("pipe"), 
bp::arg("enable")))
+        .def("setCRCLength", &RF24::setCRCLength, (bp::arg("length")))
+        .def("setDataRate", &RF24::setDataRate, (bp::arg("speed")))
+        .def("setPALevel", &RF24::setPALevel, (bp::arg("level"), bp::arg("lnaEnable")=1))
+        .def("setPALevel", &setPALevel_wrap, (bp::arg("level")))
+        .def("setRetries", &RF24::setRetries, (bp::arg("delay"), bp::arg("count")))
+        .def("startFastWrite", &startFastWrite_wrap1, (bp::arg("buf"), bp::arg("len"), bp::arg("multicast")))
+        .def("startFastWrite", &startFastWrite_wrap2, (bp::arg("buf"), bp::arg("len"), bp::arg("multicast"), 
bp::arg("startTx")))
+        .def("startListening", &RF24::startListening)
+        .def("startWrite", &startWrite_wrap, (bp::arg("buf"), bp::arg("len"), bp::arg("multicast")))
+        .def("stopListening", &RF24::stopListening)
+        .def("testCarrier", &RF24::testCarrier)
+        .def("testRPD", &RF24::testRPD)
+        .def("toggleAllPipes", &RF24::toggleAllPipes)
+        .def("setRadiation", &RF24::setRadiation)
+        .def("txStandBy", (bool (::RF24::*)(::uint32_t, bool))(&RF24::txStandBy), 
txStandBy_wrap1(bp::args("timeout", "startTx")))
+        .def("whatHappened", &whatHappened_wrap)
+        .def("startConstCarrier", &RF24::startConstCarrier, (bp::arg("level"), bp::arg("channel")))
+        .def("stopConstCarrier", &RF24::stopConstCarrier)
+        .def("write", &write_wrap1, (bp::arg("buf")))
+        .def("write", &write_wrap2, (bp::arg("buf"), bp::arg("multicast")))
+        .def("writeAckPayload", writeAckPayload_wrap, (bp::arg("pipe"), bp::arg("buf")))
+        .def("writeBlocking", &writeBlocking_wrap, (bp::arg("buf"), bp::arg("timeout")))
+        .def("writeFast", &writeFast_wrap1, (bp::arg("buf")))
+        .def("writeFast", &writeFast_wrap2, (bp::arg("buf"), bp::arg("multicast")))
+        .add_property("channel", &RF24::getChannel, &RF24::setChannel)
+        .def("setChannel", &RF24::setChannel, (bp::arg("channel")))
+        .def("getChannel", &RF24::getChannel)
+        .add_property("payloadSize", &RF24::getPayloadSize, &RF24::setPayloadSize)
+        .def("setPayloadSize", &RF24::setPayloadSize, (bp::arg("size")))
+        .def("getPayloadSize", &RF24::getPayloadSize)
+        .def_readwrite("failureDetected", &RF24::failureDetected);
+}
diff --git a/arduino/libraries/RF24/pyRF24/setup.py b/arduino/libraries/RF24/pyRF24/setup.py
new file mode 100644
index 000000000..b0cbfda09
--- /dev/null
+++ b/arduino/libraries/RF24/pyRF24/setup.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+
+import os
+from sys import version_info
+from setuptools import setup, Extension
+import crossunixccompiler
+
+version = ""
+cflags = os.getenv("CFLAGS", "")
+symlink_directory = []
+
+# NOTE current repo directory structure requires the use of
+# `python3 setup.py build` and `python3 setup.py install`
+# where `pip3 install ./pyRF24` copies pyRF24 directory to
+# `tmp` folder that doesn't have the needed `../Makefile.inc`
+# NOTE can't access "../Makefile.inc" from working dir because
+# it's relative. Brute force absolute path dynamically.
+git_dir = os.path.split(os.path.abspath(os.getcwd()))[0]
+
+try:  # get compiler options from the generated Makefile.inc
+    with open(os.path.join(git_dir, "Makefile.inc"), "r") as f:
+        for line in f.read().splitlines():
+            identifier, value = line.split("=", 1)
+            if identifier == "CPUFLAGS":
+                # this is never reached as CPUFLAGS var is concatenated into CFLAGS var.
+                # however the CFLAGS var may conditionally contain "-lwiringPi"
+                cflags += " " + value
+            elif identifier == "HEADER_DIR":
+                cflags += " -I" + os.path.dirname(value)
+            elif identifier == "LIB_DIR":
+                cflags += " -L" + value
+            elif identifier == "LIB_VERSION":
+                version = value
+            elif identifier in ("CC", "CXX"):
+                os.environ[identifier] = value
+
+except FileNotFoundError:  # assuming lib was built & installed with CMake
+
+    # get LIB_VERSION from library.properties file for Arduino IDE
+    with open(os.path.join(git_dir, "library.properties"), "r") as f:
+        for line in f.read().splitlines():
+            if line.startswith("version"):
+                version = line.split("=")[1]
+
+# check C++ RF24 lib is installed
+finally:
+
+    # check for possible linker flags set via CFLAGS environment varible
+    for flag in cflags.split("-"):
+        if flag.startswith("L"):
+            symlink_directory.append(
+                flag[1:].strip() + (" " if len(symlink_directory) else "")
+            )
+
+    if not symlink_directory:
+        print(
+            "install location of librf24.so isn't explicit; trying default locations."
+        )
+        symlink_directory = ["/usr/local/lib", "/usr/lib"]
+
+    found_symlink = False
+    for symlink_loc in symlink_directory:
+        if os.path.isfile(symlink_loc + "/librf24.so"):
+            print("librf24.so found at", symlink_loc)
+            found_symlink = True
+
+    if not found_symlink:
+        raise FileNotFoundError(
+            "RF24 library is not installed. librf24.so does not exist in {}.".format(
+                " or ".join(symlink_directory)
+            )
+        )
+
+# append any additionally found compiler flags
+os.environ["CFLAGS"] = cflags
+
+# get the proper boost.python lib symlink name according to version of python
+if version_info >= (3,):
+    BOOST_LIB = "boost_python3"
+else:
+    BOOST_LIB = "boost_python"
+
+crossunixccompiler.register()
+
+long_description = """
+.. warning:: This python wrapper for the RF24 C++ library was not intended
+    for distribution on pypi.org. If you're reading this, then this package
+    is likely unauthorized or unofficial.
+"""
+
+
+setup(
+    name="RF24",
+    version=version,
+    license="GPLv2",
+    license_files=(os.path.join(git_dir, "LICENSE"),),
+    long_description=long_description,
+    long_description_content_type="text/x-rst",
+    classifiers=[
+        "Programming Language :: Python :: 2",
+        "Programming Language :: Python :: 3",
+        "Programming Language :: C++",
+        "License :: OSI Approved :: GNU General Public License v2 (GPLv2)",
+    ],
+    ext_modules=[
+        Extension("RF24", sources=["pyRF24.cpp"], libraries=["rf24", BOOST_LIB])
+    ],
+)
diff --git a/arduino/libraries/RF24/utility/ATTiny/RF24_arch_config.h 
b/arduino/libraries/RF24/utility/ATTiny/RF24_arch_config.h
new file mode 100644
index 000000000..4b527a899
--- /dev/null
+++ b/arduino/libraries/RF24/utility/ATTiny/RF24_arch_config.h
@@ -0,0 +1,53 @@
+
+/*
+ TMRh20 2015
+ ATTiny Configuration File
+*/
+
+#ifndef __RF24_ARCH_CONFIG_H__
+#define __RF24_ARCH_CONFIG_H__
+
+/*** USER DEFINES:  ***/
+//#define FAILURE_HANDLING
+//#define MINIMAL
+/**********************/
+
+#define rf24_max(a, b) (a>b?a:b)
+#define rf24_min(a, b) (a<b?a:b)
+
+#if ARDUINO < 100
+    #include <WProgram.h>
+#else
+    #include <Arduino.h>
+#endif
+
+#include <stddef.h>
+
+#include <SPI.h>
+
+#define _SPI SPI
+
+#if !defined(RF24_CSN_SETTLE_LOW_DELAY)
+#define RF24_CSN_SETTLE_LOW_DELAY 11
+#endif
+
+#if !defined(RF24_CSN_SETTLE_HIGH_DELAY)
+#define RF24_CSN_SETTLE_HIGH_DELAY 100
+#endif
+
+
+#ifdef SERIAL_DEBUG
+    #define IF_SERIAL_DEBUG(x) ({x;})
+#else
+    #define IF_SERIAL_DEBUG(x)
+    #if defined(RF24_TINY)
+        #define printf_P(...)
+    #endif
+#endif
+
+#include <avr/pgmspace.h>
+
+#define PRIPSTR "%S"
+
+#endif // __RF24_ARCH_CONFIG_H__
+
diff --git a/arduino/libraries/RF24/utility/ATXMegaD3/RF24_arch_config.h 
b/arduino/libraries/RF24/utility/ATXMegaD3/RF24_arch_config.h
new file mode 100644
index 000000000..ab51c15bc
--- /dev/null
+++ b/arduino/libraries/RF24/utility/ATXMegaD3/RF24_arch_config.h
@@ -0,0 +1,84 @@
+/*
+ Copyright (C) 2011 J. Coliz <maniacbug ymail com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+
+ */
+
+/**
+* @file RF24_arch_config.h
+* General defines and includes for RF24/Linux
+*/
+
+/**
+* Example of RF24_arch_config.h for RF24 portability
+*
+* @defgroup Porting_General Porting: General
+*
+*
+* @{
+*/
+
+
+#ifndef __RF24_ARCH_CONFIG_H__
+#define __RF24_ARCH_CONFIG_H__
+
+#include <stddef.h>
+#include <avr/pgmspace.h>
+#include "spi.h"
+#include "gpio.h"
+#include "compatibility.h"
+#include <stdint.h>
+#include <stdio.h>
+//#include <time.h>
+#include <string.h>
+//#include <sys/time.h>
+
+//#define _BV(x) (1<<(x))
+#define _SPI SPI
+#define RF24_SPI_PTR
+
+#undef SERIAL_DEBUG
+#ifdef SERIAL_DEBUG
+    #define IF_SERIAL_DEBUG(x) ({x;})
+#else
+    #define IF_SERIAL_DEBUG(x)
+#endif
+
+// Use the avr pgmspace commands
+//// Avoid spurious warnings
+//#if 1
+//#if ! defined( NATIVE ) && defined( ARDUINO )
+//#undef PROGMEM
+//#define PROGMEM __attribute__(( section(".progmem.data") ))
+//#undef PSTR
+//#define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))
+//#endif
+//#endif
+
+typedef uint16_t prog_uint16_t;
+//#define PSTR(x) (x)
+//#define printf_P printf
+//#define strlen_P strlen
+//#define PROGMEM
+//#define pgm_read_word(p) (*(p))
+#define PRIPSTR "%s"
+//#define pgm_read_byte(p) (*(p))
+
+// Function, constant map as a result of migrating from Arduino
+#define LOW GPIO::OUTPUT_LOW
+#define HIGH GPIO::OUTPUT_HIGH
+#define INPUT GPIO::DIRECTION_IN
+#define OUTPUT GPIO::DIRECTION_OUT
+#define digitalWrite(pin, value) GPIO::write(pin, value)
+#define pinMode(pin, direction) GPIO::open(pin, direction)
+#define delay(milisec) __msleep(milisec)
+#define delayMicroseconds(usec) __usleep(usec)
+#define millis() __millis()
+
+#endif // __RF24_ARCH_CONFIG_H__
+
+
+/*@}*/
diff --git a/arduino/libraries/RF24/utility/ATXMegaD3/compatibility.c 
b/arduino/libraries/RF24/utility/ATXMegaD3/compatibility.c
new file mode 100644
index 000000000..ae07a9a93
--- /dev/null
+++ b/arduino/libraries/RF24/utility/ATXMegaD3/compatibility.c
@@ -0,0 +1,64 @@
+/*
+ * compatibility.c
+ *
+ * Created: 19/1/2016 15:31:35
+ *  Author: akatran
+ */
+
+#include <avr/io.h>
+#include <stdint.h>
+#include <util/delay.h>
+
+volatile uint32_t _millis;
+
+void __msleep(int milisec)
+{
+    while (milisec-- > 0) {
+        _delay_ms(1);
+    }
+}
+
+void __usleep(int usec)
+{
+    while (usec-- > 0) {
+        _delay_us(1);
+    }
+}
+
+void __start_timer()
+{
+
+    // Timer details : Clock is 32MHz, Timer resolution is 8bit, Prescaler is 256, Period is 124, Real Time 
is 0.001s
+
+    /* Set the timer to run at the fastest rate. */
+    TCE0.CTRLA = TC_CLKSEL_DIV256_gc;
+
+    /* Configure the timer for normal counting. */
+    TCE0.CTRLB = TC_WGMODE_NORMAL_gc;
+
+    /* At 2 MHz, one tick is 0.5 us.  Set period to 8 us. */
+    TCE0.PER = 124;
+    //TCC0.PER = 2;
+
+    /* Configure timer to generate an interrupt on overflow. */
+    TCE0.INTCTRLA = TC_OVFINTLVL_HI_gc;
+
+    /* Enable this interrupt level. */
+    PMIC.CTRL = PMIC_LOLVLEN_bm | PMIC_MEDLVLEN_bm | PMIC_HILVLEN_bm;
+
+    _millis = 0;
+}
+
+long __millis()
+{
+    return _millis;
+}
+
+void update_milisec()
+{
+    _millis++;
+}
+
+
+
+
diff --git a/arduino/libraries/RF24/utility/ATXMegaD3/compatibility.h 
b/arduino/libraries/RF24/utility/ATXMegaD3/compatibility.h
new file mode 100644
index 000000000..31e8c4c23
--- /dev/null
+++ b/arduino/libraries/RF24/utility/ATXMegaD3/compatibility.h
@@ -0,0 +1,49 @@
+/*
+ * File:   compatiblity.h
+ * Author: purinda
+ *
+ * Created on 24 June 2012, 3:08 PM
+ */
+
+/**
+* @file compatibility.h
+* Class declaration for SPI helper files
+*/
+
+/**
+* Example of compatibility.h class declaration for timing functions portability
+*
+* @defgroup Porting_Timing Porting: Timing
+*
+*
+* @{
+*/
+
+#ifndef COMPATIBLITY_H
+#define    COMPATIBLITY_H
+
+#ifdef    __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+//#include <time.h>
+//#include <sys/time.h>
+
+void __msleep(int milisec);
+
+void __usleep(int usec);
+
+void __start_timer();
+
+long __millis();
+
+void update_milisec();
+
+#ifdef    __cplusplus
+}
+#endif
+
+#endif    /* COMPATIBLITY_H */
+
+/*@}*/
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/ATXMegaD3/gpio.cpp 
b/arduino/libraries/RF24/utility/ATXMegaD3/gpio.cpp
new file mode 100644
index 000000000..23a644179
--- /dev/null
+++ b/arduino/libraries/RF24/utility/ATXMegaD3/gpio.cpp
@@ -0,0 +1,46 @@
+/*
+ * gpio.cpp
+ *
+ * Created: 20/1/2016 11:57:21
+ *  Author: akatran
+ */
+
+//#include "gpio_helper.h"
+#include "gpio.h"
+#include <stdlib.h>
+
+void GPIO::open(int port, int DDR)
+{
+    uint8_t pin;
+    PORT_t* p = GPIO_getPort(port, &pin);
+    if (DDR == 0) {
+        p->DIRCLR = pin;
+    } else if (DDR == 1) {
+        p->DIRSET = pin;
+    }
+}
+
+void GPIO::close(int port)
+{
+    // Nothing to do with close;
+}
+
+int GPIO::read(int port)
+{
+    uint8_t pin;
+    PORT_t* p = GPIO_getPort(port, &pin);
+    return p->IN;
+}
+
+void GPIO::write(int port, int value)
+{
+    uint8_t pin;
+    PORT_t* p = GPIO_getPort(port, &pin);
+    if (value == 0) {
+        p->OUTCLR = pin;
+    } else if (value == 1) {
+        p->OUTSET = pin;
+    }
+
+}
+
diff --git a/arduino/libraries/RF24/utility/ATXMegaD3/gpio.h b/arduino/libraries/RF24/utility/ATXMegaD3/gpio.h
new file mode 100644
index 000000000..01c0b1896
--- /dev/null
+++ b/arduino/libraries/RF24/utility/ATXMegaD3/gpio.h
@@ -0,0 +1,64 @@
+
+
+/**
+ * @file gpio.h
+ * Class declaration for SPI helper files
+ */
+
+/**
+* Example of gpio.h class declaration for GPIO portability
+*
+* @defgroup Porting_GPIO Porting: GPIO
+*
+*
+* @{
+*/
+#ifndef GPIO_H
+#define GPIO_H
+
+#include <avr/io.h>
+#include "gpio_helper.h"
+
+class GPIO {
+public:
+    /* Constants */
+    static const int DIRECTION_OUT = 1;
+    static const int DIRECTION_IN = 0;
+
+    static const int OUTPUT_HIGH = 1;
+    static const int OUTPUT_LOW = 0;
+
+    GPIO();
+
+    /**
+     * Similar to Arduino pinMode(pin,mode);
+     * @param port
+     * @param DDR
+     */
+    static void open(int port, int DDR);
+
+    /**
+     *
+     * @param port
+     */
+    static void close(int port);
+
+    /**
+     * Similar to Arduino digitalRead(pin);
+     * @param port
+     */
+    static int read(int port);
+
+    /**
+    * Similar to Arduino digitalWrite(pin,state);
+    * @param port
+    * @param value
+    */
+    static void write(int port, int value);
+
+    virtual ~GPIO();
+
+};
+
+#endif    /* GPIO_H */
+/*@}*/
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/ATXMegaD3/gpio_helper.c 
b/arduino/libraries/RF24/utility/ATXMegaD3/gpio_helper.c
new file mode 100644
index 000000000..a0b5245d8
--- /dev/null
+++ b/arduino/libraries/RF24/utility/ATXMegaD3/gpio_helper.c
@@ -0,0 +1,39 @@
+/*
+ * gpio_helper.c
+ *
+ * Created: 22/1/2016 15:28:48
+ *  Author: akatran
+ */
+
+#include "gpio_helper.h"
+
+/**
+* Get the port corresponding in portnum. Default is PORTC.
+*/
+PORT_t* GPIO_getPort(int pinnum, uint8_t* pin_bm)
+//PORT_t * GPIO_getPort(int portnum)
+{
+    PORT_t* port = &PORTC;
+    if ((pinnum >= XMEGA_PORTA_PIN0) && (pinnum <= XMEGA_PORTA_PIN7)) {
+        port = &PORTA;
+        *pin_bm = (1 << pinnum);
+    } else if ((pinnum >= XMEGA_PORTB_PIN0) && (pinnum <= XMEGA_PORTB_PIN7)) {
+        port = &PORTB;
+        *pin_bm = (1 << (pinnum - 8));
+    } else if ((pinnum >= XMEGA_PORTC_PIN0) && (pinnum <= XMEGA_PORTC_PIN7)) {
+        port = &PORTC;
+        *pin_bm = (1 << (pinnum - 16));
+    } else if ((pinnum >= XMEGA_PORTD_PIN0) && (pinnum <= XMEGA_PORTD_PIN7)) {
+        port = &PORTD;
+        *pin_bm = (1 << (pinnum - 24));
+    } else if ((pinnum >= XMEGA_PORTE_PIN0) && (pinnum <= XMEGA_PORTE_PIN7)) {
+        port = &PORTE;
+        *pin_bm = (1 << (pinnum - 32));
+    } else if ((pinnum >= XMEGA_PORTF_PIN0) && (pinnum <= XMEGA_PORTF_PIN7)) {
+        port = &PORTF;
+        *pin_bm = (1 << (pinnum - 40));
+    }
+
+    return port;
+
+}
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/ATXMegaD3/gpio_helper.h 
b/arduino/libraries/RF24/utility/ATXMegaD3/gpio_helper.h
new file mode 100644
index 000000000..1146fb015
--- /dev/null
+++ b/arduino/libraries/RF24/utility/ATXMegaD3/gpio_helper.h
@@ -0,0 +1,86 @@
+/*
+ * gpio_helper.h
+ *
+ * Created: 22/1/2016 15:29:12
+ *  Author: akatran
+ */
+
+
+#ifndef GPIO_HELPER_H_
+#define GPIO_HELPER_H_
+
+#include <avr/io.h>
+
+#ifdef    __cplusplus
+extern "C" {
+#endif
+
+/* Defines */
+
+
+#define XMEGA_PORTA_PIN0   0
+#define XMEGA_PORTA_PIN1   1
+#define XMEGA_PORTA_PIN2   2
+#define XMEGA_PORTA_PIN3   3
+#define XMEGA_PORTA_PIN4   4
+#define XMEGA_PORTA_PIN5   5
+#define XMEGA_PORTA_PIN6   6
+#define XMEGA_PORTA_PIN7   7
+
+#define XMEGA_PORTB_PIN0   8
+#define XMEGA_PORTB_PIN1   9
+#define XMEGA_PORTB_PIN2  10
+#define XMEGA_PORTB_PIN3  11
+#define XMEGA_PORTB_PIN4  12
+#define XMEGA_PORTB_PIN5  13
+#define XMEGA_PORTB_PIN6  14
+#define XMEGA_PORTB_PIN7  15
+
+#define XMEGA_PORTC_PIN0  16
+#define XMEGA_PORTC_PIN1  17
+#define XMEGA_PORTC_PIN2  18
+#define XMEGA_PORTC_PIN3  19
+#define XMEGA_PORTC_PIN4  20
+#define XMEGA_PORTC_PIN5  21
+#define XMEGA_PORTC_PIN6  22
+#define XMEGA_PORTC_PIN7  23
+
+#define XMEGA_PORTD_PIN0  24
+#define XMEGA_PORTD_PIN1  25
+#define XMEGA_PORTD_PIN2  26
+#define XMEGA_PORTD_PIN3  27
+#define XMEGA_PORTD_PIN4  28
+#define XMEGA_PORTD_PIN5  29
+#define XMEGA_PORTD_PIN6  30
+#define XMEGA_PORTD_PIN7  31
+
+#define XMEGA_PORTE_PIN0  32
+#define XMEGA_PORTE_PIN1  33
+#define XMEGA_PORTE_PIN2  34
+#define XMEGA_PORTE_PIN3  35
+#define XMEGA_PORTE_PIN4  36
+#define XMEGA_PORTE_PIN5  37
+#define XMEGA_PORTE_PIN6  38
+#define XMEGA_PORTE_PIN7  39
+
+#define XMEGA_PORTF_PIN0  40
+#define XMEGA_PORTF_PIN1  41
+#define XMEGA_PORTF_PIN2  42
+#define XMEGA_PORTF_PIN3  43
+#define XMEGA_PORTF_PIN4  44
+#define XMEGA_PORTF_PIN5  45
+#define XMEGA_PORTF_PIN6  46
+#define XMEGA_PORTF_PIN7  47
+
+#define XMEGA_SPI_PORT_C 20
+#define XMEGA_SPI_PORT_D 28
+
+//void GPIO_getPort(int pinnum, PORT_t * port, uint8_t pin);
+//void GPIO_getPort(int pinnum, PORT_t * port, uint8_t * pin_bm);
+PORT_t* GPIO_getPort(int pinnum, uint8_t* pin_bm);
+
+#ifdef    __cplusplus
+}
+#endif
+
+#endif /* GPIO_HELPER_H_ */
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/ATXMegaD3/includes.h 
b/arduino/libraries/RF24/utility/ATXMegaD3/includes.h
new file mode 100644
index 000000000..cf12b0302
--- /dev/null
+++ b/arduino/libraries/RF24/utility/ATXMegaD3/includes.h
@@ -0,0 +1,30 @@
+/**
+* @file includes.h
+* Configuration defines for RF24/Linux
+*/
+
+/**
+* Example of includes.h for RF24 Linux portability
+*
+* @defgroup Porting_Includes Porting: Includes
+*
+*
+* @{
+*/
+
+#ifndef __RF24_INCLUDES_H__
+#define __RF24_INCLUDES_H__
+
+/**
+ * Define a specific platform for this configuration
+ */
+// #define RF24_BBB
+
+/**
+ * Load the correct configuration for this platform
+*/
+//#include "BBB/RF24_arch_config.h"
+
+#endif
+
+/*@}*/ 
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/ATXMegaD3/spi.cpp 
b/arduino/libraries/RF24/utility/ATXMegaD3/spi.cpp
new file mode 100644
index 000000000..3207e50dd
--- /dev/null
+++ b/arduino/libraries/RF24/utility/ATXMegaD3/spi.cpp
@@ -0,0 +1,64 @@
+/*
+ * spi.cpp
+ *
+ * Created: 20/1/2016 10:10:39
+ *  Author: akatran
+ */
+
+#include <avr/io.h>
+#include "gpio_helper.h"
+#include "spi.h"
+
+using namespace std;
+
+void SPI::begin(uint8_t _port)
+{
+    if (_port == XMEGA_SPI_PORT_C) // Select SPI on PORTC
+    {
+        device = &SPIC;
+        port = &PORTC;
+    } else if (_port == XMEGA_SPI_PORT_D) // Select SPI on PORTD
+    {
+        device = &SPID;
+        port = &PORTD;
+    }
+
+    init();
+}
+
+uint8_t SPI::transfer(uint8_t tx_)
+{
+    register8_t data;
+    device->DATA = tx_;
+    while (!(device->STATUS & (1 << 7))) {
+    }
+    data = device->DATA;
+    //PORTF.OUT = data;
+    return data;
+}
+
+void SPI::init()
+{
+    port->DIRCLR = SPI_MISO_bm;
+    port->DIRSET = SPI_MOSI_bm | SPI_SCK_bm | SPI_SS_bm;
+
+    //device->CTRL = 0;
+    device->CTRL = SPI_ENABLE_bm | SPI_MASTER_bm | SPI_MODE_0_gc | SPI_PRESCALER_DIV4_gc;
+    device->INTCTRL = 0; //Disable interrupts
+
+}
+
+SPI::SPI()
+{
+
+}
+
+SPI::~SPI()
+{
+
+}
+
+void operator delete(void* p) // or delete(void *, std::size_t)
+{
+
+}
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/ATXMegaD3/spi.h b/arduino/libraries/RF24/utility/ATXMegaD3/spi.h
new file mode 100644
index 000000000..caf479236
--- /dev/null
+++ b/arduino/libraries/RF24/utility/ATXMegaD3/spi.h
@@ -0,0 +1,88 @@
+/**
+ * @file spi.h
+ * Class declaration for SPI helper files
+ */
+
+/**
+* Example of spi.h class declaration for SPI portability
+*
+* @defgroup Porting_SPI Porting: SPI
+*
+*
+* @{
+*/
+
+#ifndef __XMEGASPI_H__
+#define __XMEGASPI_H__
+
+#include <avr/io.h>
+#include <stdint.h>
+//#include <string.h>
+//#include <unistd.h>
+//#include <stdio.h>
+//#include <stdlib.h>
+//#include <getopt.h>
+//#include <fcntl.h>
+//#include <sys/ioctl.h>
+//#include <inttypes.h>
+//#include <linux/types.h>
+//#include <linux/spi/spidev.h> 
+
+#define SPI_SS_bm (1<<4) /* SPI SS pin mask 4 */
+#define SPI_MOSI_bm (1<<5) /* SPI MOSI pin mask 5 */
+#define SPI_MISO_bm (1<<6) /* SPI MISO pin mask 6 */
+#define SPI_SCK_bm (1<<7) /* SPI SCK pin mask 7 */
+
+using namespace std;
+
+class SPI {
+public:
+
+    /**
+    * SPI constructor
+    */
+    SPI();
+
+    /**
+    * Start SPI
+    * @param port is the SPI port (XMEGA_SPI_PORT_C for SPI on PORTC, XMEGA_SPI_PORT_D on PORTD etc).
+    */
+    void begin(uint8_t port);
+
+    /**
+    * Transfer a single byte
+    * @param tx_ Byte to send
+    * @return Data returned via spi
+    */
+    uint8_t transfer(uint8_t tx_);
+
+    /**
+    * Transfer a buffer of data
+    * @param tbuf Transmit buffer
+    * @param rbuf Receive buffer
+    * @param len Length of the data
+    */
+    void transfernb(char* tbuf, char* rbuf, uint32_t len);
+
+    /**
+    * Transfer a buffer of data without an rx buffer
+    * @param buf Pointer to a buffer of data
+    * @param len Length of the data
+    */
+    void transfern(char* buf, uint32_t len);
+
+    virtual ~SPI();
+
+private:
+
+    /** Default SPI device */
+    SPI_t* device;
+    /* Port of the SPI */
+    PORT_t* port;
+
+    void init();
+};
+
+#endif /*__XMEGASPI_H__*/
+
+/*@}*/
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/CMakeLists.txt b/arduino/libraries/RF24/utility/CMakeLists.txt
new file mode 100644
index 000000000..0ad11f50a
--- /dev/null
+++ b/arduino/libraries/RF24/utility/CMakeLists.txt
@@ -0,0 +1,102 @@
+###########################
+### declare the appropriate sources and install rules based on driver selected
+###########################
+if ("${RF24_DRIVER}" STREQUAL  "wiringPi") # Use wiringPi
+    set(RF24_LINKED_DRIVER ${LibWiringPi} PARENT_SCOPE)
+    add_compile_options(-pthread)
+    set(RF24_DRIVER_SOURCES
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/includes.h
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/spi.cpp
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/RF24_arch_config.h
+        PARENT_SCOPE
+    )
+    install(FILES
+            ${RF24_DRIVER}/includes.h
+            ${RF24_DRIVER}/spi.h
+            ${RF24_DRIVER}/RF24_arch_config.h
+        DESTINATION include/RF24/utility/${RF24_DRIVER}
+    )
+elseif("${RF24_DRIVER}" STREQUAL "RPi") # use RPi
+    set(RF24_DRIVER_SOURCES
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/includes.h
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/bcm2835.c
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/spi.cpp
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/compatibility.cpp
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/RF24_arch_config.h
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/interrupt.cpp
+        PARENT_SCOPE
+    )
+    install(FILES
+            ${RF24_DRIVER}/includes.h
+            ${RF24_DRIVER}/bcm2835.h
+            ${RF24_DRIVER}/spi.h
+            ${RF24_DRIVER}/compatibility.h
+            ${RF24_DRIVER}/RF24_arch_config.h
+            ${RF24_DRIVER}/interrupt.h
+        DESTINATION include/RF24/utility/${RF24_DRIVER}
+    )
+elseif("${RF24_DRIVER}" STREQUAL "SPIDEV") # use SPIDEV
+    if(NOT SPIDEV_EXISTS)
+        message(WARNING "Detecting /dev/spidev0.0 failed - continuing anyway. Please make sure SPI is 
enabled.")
+    endif()
+    set(RF24_DRIVER_SOURCES
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/includes.h
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/gpio.cpp
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/spi.cpp
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/compatibility.cpp
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/RF24_arch_config.h
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/interrupt.cpp
+        PARENT_SCOPE
+    )
+    install(FILES
+            ${RF24_DRIVER}/includes.h
+            ${RF24_DRIVER}/gpio.h
+            ${RF24_DRIVER}/spi.h
+            ${RF24_DRIVER}/compatibility.h
+            ${RF24_DRIVER}/RF24_arch_config.h
+            ${RF24_DRIVER}/interrupt.h
+        DESTINATION include/RF24/utility/${RF24_DRIVER}
+    )
+elseif("${RF24_DRIVER}" STREQUAL "MRAA") # use MRAA
+    set(RF24_LINKED_DRIVER ${LibMRAA} PARENT_SCOPE)
+    set(RF24_DRIVER_SOURCES
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/includes.h
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/gpio.cpp
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/spi.cpp
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/compatibility.cpp
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/RF24_arch_config.h
+        PARENT_SCOPE
+    )
+    install(FILES
+            ${RF24_DRIVER}/includes.h
+            ${RF24_DRIVER}/gpio.h
+            ${RF24_DRIVER}/spi.h
+            ${RF24_DRIVER}/compatibility.h
+            ${RF24_DRIVER}/RF24_arch_config.h
+        DESTINATION include/RF24/utility/${RF24_DRIVER}
+    )
+elseif("${RF24_DRIVER}" STREQUAL "LittleWire") # use LittleWire
+    set(RF24_LINKED_DRIVER ${LibLittleWire} PARENT_SCOPE)
+    set(RF24_DRIVER_SOURCES
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/includes.h
+        ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/RF24_arch_config.h
+        PARENT_SCOPE
+    )
+    install(FILES
+            ${RF24_DRIVER}/includes.h
+            ${RF24_DRIVER}/RF24_arch_config.h
+        DESTINATION include/RF24/utility/${RF24_DRIVER}
+    )
+else() # No valid/supported driver selected nor detected... this is vital
+    message(FATAL_ERROR "No valid/supported driver selected or auto-detection failed to resolve one.
+        Please specify 1 of the following supported drivers (ie `-D RF24_DRIVER=SPIDEV`):
+        \twiringPi
+        \tRPi
+        \tSPIDEV
+        \tMRAA
+        \tLittleWire"
+    )
+endif()
+
+# copy the includes file to the project source directory's utility folder
+execute_process(COMMAND cp ${CMAKE_CURRENT_LIST_DIR}/${RF24_DRIVER}/includes.h 
${CMAKE_CURRENT_LIST_DIR}/includes.h)
diff --git a/arduino/libraries/RF24/utility/LittleWire/RF24_arch_config.h 
b/arduino/libraries/RF24/utility/LittleWire/RF24_arch_config.h
new file mode 100644
index 000000000..9bf308d8c
--- /dev/null
+++ b/arduino/libraries/RF24/utility/LittleWire/RF24_arch_config.h
@@ -0,0 +1,39 @@
+#ifndef __ARCH_CONFIG_H__
+#define __ARCH_CONFIG_H__
+
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <sys/time.h>
+#include <stddef.h>
+
+// Additional fixes for LittleWire
+#include <LittleWireSPI/littlewirespi.h>
+#include <LittleWireSPI/avr_fixes.h>
+
+extern LittleWireSPI _SPI;
+
+// GCC a Arduino Missing
+#define _BV(x) (1<<(x))
+#define pgm_read_word(p) (*(p))
+#define pgm_read_byte(p) (*(p))
+#define pgm_read_ptr(p) (*(p))
+
+//typedef uint16_t prog_uint16_t;
+#define PSTR(x) (x)
+#define printf_P printf
+#define strlen_P strlen
+#define PROGMEM
+#define PRIPSTR "%s"
+
+#ifdef SERIAL_DEBUG
+    #define IF_SERIAL_DEBUG(x) ({x;})
+#else
+    #define IF_SERIAL_DEBUG(x)
+    #if defined(RF24_TINY)
+        #define printf_P(...)
+    #endif
+#endif
+
+#endif
diff --git a/arduino/libraries/RF24/utility/LittleWire/includes.h 
b/arduino/libraries/RF24/utility/LittleWire/includes.h
new file mode 100644
index 000000000..197e4473f
--- /dev/null
+++ b/arduino/libraries/RF24/utility/LittleWire/includes.h
@@ -0,0 +1,30 @@
+/**
+* @file includes.h
+* Configuration defines for RF24/Linux
+*/
+
+/**
+* Example of includes.h for RF24 Linux portability
+*
+* @defgroup Porting_Includes Porting: Includes
+*
+*
+* @{
+*/
+
+#ifndef __RF24_INCLUDES_H__
+#define __RF24_INCLUDES_H__
+
+/**
+ * Define a specific platform for this configuration
+ */
+#define LITTLEWIRE
+
+/**
+ * Load the correct configuration for this platform
+*/
+#include "LittleWire/RF24_arch_config.h"
+
+#endif
+
+/*@}*/ 
diff --git a/arduino/libraries/RF24/utility/MRAA/RF24_arch_config.h 
b/arduino/libraries/RF24/utility/MRAA/RF24_arch_config.h
new file mode 100644
index 000000000..0699ca9d0
--- /dev/null
+++ b/arduino/libraries/RF24/utility/MRAA/RF24_arch_config.h
@@ -0,0 +1,64 @@
+#ifndef __ARCH_CONFIG_H__
+#define __ARCH_CONFIG_H__
+
+#include "mraa.h"
+#include "spi.h"
+#include "gpio.h"
+#include "compatibility.h"
+
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <sys/time.h>
+#include <stddef.h>
+#include <iostream>
+#include <unistd.h>
+#include <stdlib.h>
+
+//#include <UtilTime.h> // Precompiled arduino x86 based utiltime for timing functions
+
+// GCC a Arduino Missing
+#define HIGH 1
+#define LOW 0
+#define _BV(x) (1<<(x))
+#define pgm_read_word(p) (*(p))
+#define pgm_read_byte(p) (*(p))
+#define pgm_read_ptr(p) (*(p))
+#define _SPI spi
+
+#define RF24_LINUX
+//typedef uint16_t prog_uint16_t;
+#define PSTR(x) (x)
+#define printf_P printf
+#define sprintf_P sprintf
+#define strlen_P strlen
+#define PROGMEM
+#define PRIPSTR "%s"
+
+#ifdef SERIAL_DEBUG
+    #define IF_SERIAL_DEBUG(x) ({x;})
+#else
+    #define IF_SERIAL_DEBUG(x)
+#endif
+
+#define digitalWrite(pin, value) gpio.write(pin, value)
+#define digitalRead(pin) GPIO::read(pin)
+#define pinMode(pin, direction) gpio.open(pin, direction)
+
+#ifndef __TIME_H__
+// Prophet: Redefine time functions only if precompiled arduino time is not included
+    #define delay(milisec) __msleep(milisec)
+    #define delayMicroseconds(usec) __usleep(usec)
+    #define millis() __millis()
+#endif
+
+#define INPUT mraa::DIR_IN
+#define OUTPUT mraa::DIR_OUT
+
+// SPI defines for ARDUINO API
+#define MSBFIRST 1
+//#define SPI_MODE0 mraa::SPI_MODE0
+#define SPI_CLOCK_DIV2 RF24_SPI_SPEED
+
+#endif
diff --git a/arduino/libraries/RF24/utility/MRAA/compatibility.cpp 
b/arduino/libraries/RF24/utility/MRAA/compatibility.cpp
new file mode 100644
index 000000000..5bc2a4034
--- /dev/null
+++ b/arduino/libraries/RF24/utility/MRAA/compatibility.cpp
@@ -0,0 +1,44 @@
+#include "compatibility.h"
+#include <chrono>
+//static struct timeval start, end;
+//static long mtime, seconds, useconds;
+
+/**********************************************************************/
+/**
+ * This function is added in order to simulate arduino delay() function
+ * @param milisec
+ */
+void __msleep(int milisec)
+{
+    struct timespec req = {0};
+    req.tv_sec = 0;
+    req.tv_nsec = milisec * 1000000L;
+    nanosleep(&req, (struct timespec*) NULL);
+    //usleep(milisec*1000);
+}
+
+void __usleep(int milisec)
+{
+    struct timespec req = {0};
+    req.tv_sec = 0;
+    req.tv_nsec = milisec * 1000L;
+    nanosleep(&req, (struct timespec*) NULL);
+    //usleep(milisec);
+}
+
+auto start = std::chrono::steady_clock::now();
+
+/**
+ * This function is added in order to simulate arduino millis() function
+ */
+void __start_timer()
+{
+    //gettimeofday(&start, NULL);
+}
+
+long __millis()
+{
+    auto end = std::chrono::steady_clock::now();
+       
+       return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
+}
diff --git a/arduino/libraries/RF24/utility/MRAA/compatibility.h 
b/arduino/libraries/RF24/utility/MRAA/compatibility.h
new file mode 100644
index 000000000..f881d358e
--- /dev/null
+++ b/arduino/libraries/RF24/utility/MRAA/compatibility.h
@@ -0,0 +1,32 @@
+/*
+ * File:   compatiblity.h
+ * Author: purinda
+ *
+ * Created on 24 June 2012, 3:08 PM
+ */
+
+#ifndef COMPATIBLITY_H
+#define COMPATIBLITY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <time.h>
+#include <sys/time.h>
+
+void __msleep(int milisec);
+
+void __usleep(int milisec);
+
+void __start_timer();
+
+long __millis();
+
+#ifdef    __cplusplus
+}
+#endif
+
+#endif    /* COMPATIBLITY_H */
+
diff --git a/arduino/libraries/RF24/utility/MRAA/gpio.cpp b/arduino/libraries/RF24/utility/MRAA/gpio.cpp
new file mode 100644
index 000000000..12c01d87f
--- /dev/null
+++ b/arduino/libraries/RF24/utility/MRAA/gpio.cpp
@@ -0,0 +1,87 @@
+/*
+ * TMRh20 2015
+ *
+ */
+
+#include "gpio.h"
+
+GPIO::GPIO()
+{
+    // Prophet: basic members initialization
+    gpio_ce_pin = -1;
+    //gpio_cs_pin = -1;
+    gpio_0 = NULL;
+    //gpio_1 = NULL;
+}
+
+GPIO::~GPIO()
+{
+    // Prophet: this should free memory, and unexport pins when RF24 and/or GPIO gets deleted or goes out of 
scope
+    this->close(gpio_ce_pin);
+    //this->close(gpio_cs_pin);
+}
+
+void GPIO::begin(uint8_t ce_pin, uint8_t cs_pin)
+{
+    gpio_ce_pin = ce_pin;
+    //gpio_cs_pin = cs_pin;
+
+    // Prophet: owner can be set here, because we use our pins exclusively, and are making mraa:Gpio context 
persistent
+    // so pins will be unexported only if close is called, or on destruction
+    gpio_0 = new mraa::Gpio(ce_pin/*,0*/);
+    //gpio_1 = new mraa::Gpio(cs_pin/*,0*/);
+}
+
+void GPIO::open(int port, int DDR)
+{
+    if (port == gpio_ce_pin) {
+        gpio_0 = new mraa::Gpio(port, 0);
+        // WARNING: use of memory mapped file system is deprecated in MRAA lib
+        gpio_0->useMmap(true); // `false` (or just not calling `useMmap()`) uses default file system?
+        gpio_0->dir((mraa::Dir) DDR);
+    }/*else
+       if(port == gpio_cs_pin){
+               gpio_1 = new mraa::Gpio(port,0);
+               gpio_1->useMmap(true);
+               gpio_1->dir( (mraa::Dir)DDR);
+       }*/
+}
+
+void GPIO::close(int port)
+{
+    // Prophet: using same theme of working with port numbers as with GPIO::open,
+    // checking for mraa::Gpio context existence to be sure, that GPIO::begin was called
+    if (port == gpio_ce_pin) {
+        if (gpio_0 != NULL) {
+            delete gpio_0;
+        }
+    }
+
+    /*if(port == gpio_cs_pin) {
+        if (gpio_1 != NULL)    {
+            delete gpio_1;
+        }
+    }*/
+}
+
+int GPIO::read(int port)
+{
+    if (port == gpio_ce_pin) {
+        return gpio_0->read();
+    }/*else
+       if(port == gpio_cs_pin){
+               return gpio_1->read();
+       }*/
+    return -1;
+}
+
+void GPIO::write(int port, int value)
+{
+
+    if (port == gpio_ce_pin) {
+        gpio_0->write(value);
+    }/*else
+       if(port == gpio_cs_pin){
+               gpio_1->write( value);
+       }*/
+}
diff --git a/arduino/libraries/RF24/utility/MRAA/gpio.h b/arduino/libraries/RF24/utility/MRAA/gpio.h
new file mode 100644
index 000000000..e00624ecb
--- /dev/null
+++ b/arduino/libraries/RF24/utility/MRAA/gpio.h
@@ -0,0 +1,68 @@
+/*
+ * TMRh20 2015
+ *
+ */
+
+#ifndef RF24_ARCH_GPIO_H
+#define RF24_ARCH_GPIO_H
+/**
+* @file spi.h
+* @cond HIDDEN_SYMBOLS
+* Class declaration for GPIO helper files
+*/
+#include <cstdio>
+#include <stdio.h>
+#include "mraa.hpp"
+
+class GPIO {
+public:
+
+    /* Constants */
+
+    GPIO();
+
+    virtual ~GPIO();
+
+    /**
+     * Sets up GPIO on the CE & CS pins
+     * @param ce_pin
+     * @param cs_pin
+     */
+    void begin(uint8_t ce_pin, uint8_t cs_pin);
+
+    /**
+     *
+     * @param port
+     * @param DDR
+     */
+    void open(int port, int DDR);
+
+    /**
+     *
+     * @param port
+     */
+    void close(int port);
+
+    /**
+     *
+     * @param port
+     */
+    int read(int port);
+
+    /**
+     *
+     * @param port
+     * @param value
+     */
+    void write(int port, int value);
+
+private:
+    int gpio_ce_pin; /** ce_pin value of the RF24 device **/
+    //int gpio_cs_pin; /** cs_pin value of the RF24 device **/
+    mraa::Gpio* gpio_0; /** gpio object for ce_pin **/
+    //mraa::Gpio* gpio_1; /** gpio object for cs_pin **/
+};
+/**
+ * @endcond
+ */
+#endif /* RF24_ARCH_GPIO_H */
diff --git a/arduino/libraries/RF24/utility/MRAA/includes.h b/arduino/libraries/RF24/utility/MRAA/includes.h
new file mode 100644
index 000000000..feb55f46b
--- /dev/null
+++ b/arduino/libraries/RF24/utility/MRAA/includes.h
@@ -0,0 +1,10 @@
+#ifndef __RF24_INCLUDES_H__
+#define __RF24_INCLUDES_H__
+
+#ifndef MRAA
+    #define MRAA
+#endif
+
+#include "MRAA/RF24_arch_config.h"
+
+#endif
diff --git a/arduino/libraries/RF24/utility/MRAA/spi.cpp b/arduino/libraries/RF24/utility/MRAA/spi.cpp
new file mode 100644
index 000000000..799cb2a84
--- /dev/null
+++ b/arduino/libraries/RF24/utility/MRAA/spi.cpp
@@ -0,0 +1,59 @@
+
+#include "spi.h"
+#include "mraa.h"
+
+SPI::SPI()
+{
+    mspi = NULL;
+}
+
+void SPI::begin(int busNo, uint32_t spi_speed)
+{
+    mspi = new mraa::Spi(
+            busNo); // init mraa spi bus, it will handle chip select internally. For CS pin wiring user must 
check SPI details in hardware manual
+
+    mspi->mode(mraa::SPI_MODE0);
+    mspi->bitPerWord(8);
+    mspi->frequency(
+            spi_speed); // Prophet: this will try to set 8MHz, however MRAA will reset to max platform speed 
and syslog a message of it
+}
+
+void SPI::end()
+{
+    // Prophet: we should check for existence of mspi before deleting it
+    if (mspi != NULL) {
+        delete mspi;
+    }
+}
+
+void SPI::setBitOrder(uint8_t bit_order)
+{
+    if (mspi != NULL) {
+        mspi->lsbmode((mraa_boolean_t) bit_order);
+    } // Prophet: bit_order
+}
+
+void SPI::setDataMode(uint8_t data_mode)
+{
+    if (mspi != NULL) {
+        mspi->mode((mraa::Spi_Mode) data_mode);
+    }
+}
+
+void SPI::setClockDivider(uint32_t spi_speed)
+{
+    if (mspi != NULL) {
+        mspi->frequency(spi_speed);
+    }
+}
+
+void SPI::chipSelect(int csn_pin)
+{
+
+}
+
+SPI::~SPI()
+{
+    // Prophet: we should call end here to free used memory and unexport SPI interface
+    this->end();
+}
diff --git a/arduino/libraries/RF24/utility/MRAA/spi.h b/arduino/libraries/RF24/utility/MRAA/spi.h
new file mode 100644
index 000000000..6ea3eb030
--- /dev/null
+++ b/arduino/libraries/RF24/utility/MRAA/spi.h
@@ -0,0 +1,66 @@
+/*
+ * TMRh20 2015
+ * SPI layer for RF24
+ */
+
+#ifndef _SPI_H_INCLUDED
+#define _SPI_H_INCLUDED
+/**
+* @file spi.h
+* \cond HIDDEN_SYMBOLS
+* Class declaration for SPI helper files
+*/
+
+#include <stdio.h>
+#include "mraa.h"
+#include "mraa.hpp"
+
+#include "../../RF24_config.h"
+
+class SPI {
+public:
+
+    SPI();
+
+    virtual ~SPI();
+
+    mraa::Spi* mspi;
+
+    inline uint8_t transfer(uint8_t _data);
+
+    inline void transfernb(char* tbuf, char* rbuf, uint32_t len);
+
+    inline void transfern(char* buf, uint32_t len);
+
+    void begin(int busNo, uint32_t spi_speed = RF24_SPI_SPEED);
+
+    void end();
+
+    void setBitOrder(uint8_t bit_order);
+
+    void setDataMode(uint8_t data_mode);
+
+    void setClockDivider(uint32_t spi_speed);
+
+    void chipSelect(int csn_pin);
+};
+
+uint8_t SPI::transfer(uint8_t _data)
+{
+    return mspi->writeByte(_data);
+}
+
+void SPI::transfernb(char* tbuf, char* rbuf, uint32_t len)
+{
+    mspi->transfer((uint8_t*) tbuf, (uint8_t*) rbuf, len);
+}
+
+void SPI::transfern(char* buf, uint32_t len)
+{
+    transfernb(buf, buf, len);
+}
+
+/**
+ * \endcond
+ */
+#endif
diff --git a/arduino/libraries/RF24/utility/RPi/RF24_arch_config.h 
b/arduino/libraries/RF24/utility/RPi/RF24_arch_config.h
new file mode 100644
index 000000000..1ecec48d9
--- /dev/null
+++ b/arduino/libraries/RF24/utility/RPi/RF24_arch_config.h
@@ -0,0 +1,47 @@
+#ifndef __ARCH_CONFIG_H__
+#define __ARCH_CONFIG_H__
+
+#define RF24_LINUX
+
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <sys/time.h>
+#include <stddef.h>
+
+#include "bcm2835.h"
+#include "spi.h"
+#include "compatibility.h"
+
+#define _SPI spi
+
+#if defined (SPI_HAS_TRANSACTION)
+    // this gets triggered as /utility/RPi/spi.h defines SPI_HAS_TRANSACTION (unless modified by end-user)
+    #define RF24_SPI_TRANSACTIONS
+#endif
+
+// GCC a Arduino Missing
+#define _BV(x) (1<<(x))
+#define pgm_read_word(p) (*(p))
+#define pgm_read_byte(p) (*(p))
+#define pgm_read_ptr(p) (*(p))
+
+//typedef uint16_t prog_uint16_t;
+#define PSTR(x) (x)
+#define printf_P printf
+#define strlen_P strlen
+#define PROGMEM
+#define PRIPSTR "%s"
+
+#ifdef SERIAL_DEBUG
+    #define IF_SERIAL_DEBUG(x) ({x;})
+#else
+    #define IF_SERIAL_DEBUG(x)
+#endif
+
+#define digitalWrite(pin, value) bcm2835_gpio_write(pin, value)
+#define pinMode(pin, value) bcm2835_gpio_fsel(pin, value)
+#define OUTPUT BCM2835_GPIO_FSEL_OUTP
+#define INPUT BCM2835_GPIO_FSEL_INPT
+#endif
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/RPi/bcm2835.c b/arduino/libraries/RF24/utility/RPi/bcm2835.c
new file mode 100644
index 000000000..557b3aa63
--- /dev/null
+++ b/arduino/libraries/RF24/utility/RPi/bcm2835.c
@@ -0,0 +1,2031 @@
+/* bcm2835.c
+// C and C++ support for Broadcom BCM 2835 as used in Raspberry Pi
+// http://elinux.org/RPi_Low-level_peripherals
+// http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
+//
+// Author: Mike McCauley
+// Copyright (C) 2011-2013 Mike McCauley
+// $Id: bcm2835.c,v 1.28 2020/01/11 05:07:13 mikem Exp mikem $
+*/
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#define BCK2835_LIBRARY_BUILD
+#include "bcm2835.h"
+
+/* This define enables a little test program (by default a blinking output on pin RPI_GPIO_PIN_11)
+// You can do some safe, non-destructive testing on any platform with:
+// gcc bcm2835.c -D BCM2835_TEST
+// ./a.out
+*/
+/*#define BCM2835_TEST*/
+
+/* Uncommenting this define compiles alternative I2C code for the version 1 RPi
+// The P1 header I2C pins are connected to SDA0 and SCL0 on V1.
+// By default I2C code is generated for the V2 RPi which has SDA1 and SCL1 connected.
+*/
+/* #define I2C_V1*/
+
+/* Physical address and size of the peripherals block
+// May be overridden on RPi2
+*/
+off_t bcm2835_peripherals_base = BCM2835_PERI_BASE;
+size_t bcm2835_peripherals_size = BCM2835_PERI_SIZE;
+
+/* Virtual memory address of the mapped peripherals block 
+ */
+uint32_t *bcm2835_peripherals = (uint32_t *)MAP_FAILED;
+
+/* And the register bases within the peripherals block
+ */
+volatile uint32_t *bcm2835_gpio        = (uint32_t *)MAP_FAILED;
+volatile uint32_t *bcm2835_pwm         = (uint32_t *)MAP_FAILED;
+volatile uint32_t *bcm2835_clk         = (uint32_t *)MAP_FAILED;
+volatile uint32_t *bcm2835_pads        = (uint32_t *)MAP_FAILED;
+volatile uint32_t *bcm2835_spi0        = (uint32_t *)MAP_FAILED;
+volatile uint32_t *bcm2835_bsc0        = (uint32_t *)MAP_FAILED;
+volatile uint32_t *bcm2835_bsc1        = (uint32_t *)MAP_FAILED;
+volatile uint32_t *bcm2835_st         = (uint32_t *)MAP_FAILED;
+volatile uint32_t *bcm2835_aux        = (uint32_t *)MAP_FAILED;
+volatile uint32_t *bcm2835_spi1        = (uint32_t *)MAP_FAILED;
+
+
+
+/* This variable allows us to test on hardware other than RPi.
+// It prevents access to the kernel memory, and does not do any peripheral access
+// Instead it prints out what it _would_ do if debug were 0
+ */
+static uint8_t debug = 0;
+
+/* RPI 4 has different pullup registers - we need to know if we have that type */
+
+static uint8_t pud_type_rpi4 = 0;
+
+/* RPI 4 has different pullup operation - make backwards compat */
+
+static uint8_t pud_compat_setting = BCM2835_GPIO_PUD_OFF;
+
+/* I2C The time needed to transmit one byte. In microseconds.
+ */
+static int i2c_byte_wait_us = 0;
+
+/* SPI bit order. BCM2835 SPI0 only supports MSBFIRST, so we instead 
+ * have a software based bit reversal, based on a contribution by Damiano Benedetti
+ */
+static uint8_t bcm2835_spi_bit_order = BCM2835_SPI_BIT_ORDER_MSBFIRST;
+static uint8_t bcm2835_byte_reverse_table[] = 
+{
+    0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0,
+    0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0,
+    0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8,
+    0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8,
+    0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4,
+    0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4,
+    0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec,
+    0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc,
+    0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2,
+    0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2,
+    0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea,
+    0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa,
+    0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6,
+    0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6,
+    0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee,
+    0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe,
+    0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1,
+    0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1,
+    0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9,
+    0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9,
+    0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5,
+    0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5,
+    0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed,
+    0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd,
+    0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3,
+    0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3,
+    0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb,
+    0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb,
+    0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7,
+    0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7,
+    0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef,
+    0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff
+};
+
+static uint8_t bcm2835_correct_order(uint8_t b)
+{
+    if (bcm2835_spi_bit_order == BCM2835_SPI_BIT_ORDER_LSBFIRST)
+       return bcm2835_byte_reverse_table[b];
+    else
+       return b;
+}
+
+#ifdef BCM2835_HAVE_LIBCAP
+#include <sys/capability.h>
+static int bcm2835_has_capability(cap_value_t capability)
+{
+    int ok = 0;
+    cap_t cap = cap_get_proc();
+    if (cap)
+    {
+        cap_flag_value_t value;
+        if (cap_get_flag(cap,capability,CAP_EFFECTIVE,&value) == 0 && value == CAP_SET)
+            ok = 1;
+       cap_free(cap);
+    }
+    return ok;
+}
+#endif
+
+/*
+// Low level register access functions
+*/
+
+/* Function to return the pointers to the hardware register bases */
+uint32_t* bcm2835_regbase(uint8_t regbase)
+{
+    switch (regbase)
+    {
+       case BCM2835_REGBASE_ST:
+           return (uint32_t *)bcm2835_st;
+       case BCM2835_REGBASE_GPIO:
+           return (uint32_t *)bcm2835_gpio;
+       case BCM2835_REGBASE_PWM:
+           return (uint32_t *)bcm2835_pwm;
+       case BCM2835_REGBASE_CLK:
+           return (uint32_t *)bcm2835_clk;
+       case BCM2835_REGBASE_PADS:
+           return (uint32_t *)bcm2835_pads;
+       case BCM2835_REGBASE_SPI0:
+           return (uint32_t *)bcm2835_spi0;
+       case BCM2835_REGBASE_BSC0:
+           return (uint32_t *)bcm2835_bsc0;
+       case BCM2835_REGBASE_BSC1:
+           return (uint32_t *)bcm2835_st;
+       case BCM2835_REGBASE_AUX:
+           return (uint32_t *)bcm2835_aux;
+       case BCM2835_REGBASE_SPI1:
+           return (uint32_t *)bcm2835_spi1;
+
+    }
+    return (uint32_t *)MAP_FAILED;
+}
+
+void  bcm2835_set_debug(uint8_t d)
+{
+    debug = d;
+}
+
+unsigned int bcm2835_version(void) 
+{
+    return BCM2835_VERSION;
+}
+
+/* Read with memory barriers from peripheral
+ *
+ */
+uint32_t bcm2835_peri_read(volatile uint32_t* paddr)
+{
+    uint32_t ret;
+    if (debug)
+    {
+               printf("bcm2835_peri_read  paddr %p\n", (void *) paddr);
+               return 0;
+    }
+    else
+    {
+       __sync_synchronize();
+       ret = *paddr;
+       __sync_synchronize();
+       return ret;
+    }
+}
+
+/* read from peripheral without the read barrier
+ * This can only be used if more reads to THE SAME peripheral
+ * will follow.  The sequence must terminate with memory barrier
+ * before any read or write to another peripheral can occur.
+ * The MB can be explicit, or one of the barrier read/write calls.
+ */
+uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr)
+{
+    if (debug)
+    {
+       printf("bcm2835_peri_read_nb  paddr %p\n", paddr);
+       return 0;
+    }
+    else
+    {
+       return *paddr;
+    }
+}
+
+/* Write with memory barriers to peripheral
+ */
+
+void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value)
+{
+    if (debug)
+    {
+       printf("bcm2835_peri_write paddr %p, value %08X\n", paddr, value);
+    }
+    else
+    {
+        __sync_synchronize();
+        *paddr = value;
+        __sync_synchronize();
+    }
+}
+
+/* write to peripheral without the write barrier */
+void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value)
+{
+    if (debug)
+    {
+       printf("bcm2835_peri_write_nb paddr %p, value %08X\n",
+                paddr, value);
+    }
+    else
+    {
+       *paddr = value;
+    }
+}
+
+/* Set/clear only the bits in value covered by the mask
+ * This is not atomic - can be interrupted.
+ */
+void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask)
+{
+    uint32_t v = bcm2835_peri_read(paddr);
+    v = (v & ~mask) | (value & mask);
+    bcm2835_peri_write(paddr, v);
+}
+
+/*
+// Low level convenience functions
+*/
+
+/* Function select
+// pin is a BCM2835 GPIO pin number NOT RPi pin number
+//      There are 6 control registers, each control the functions of a block
+//      of 10 pins.
+//      Each control register has 10 sets of 3 bits per GPIO pin:
+//
+//      000 = GPIO Pin X is an input
+//      001 = GPIO Pin X is an output
+//      100 = GPIO Pin X takes alternate function 0
+//      101 = GPIO Pin X takes alternate function 1
+//      110 = GPIO Pin X takes alternate function 2
+//      111 = GPIO Pin X takes alternate function 3
+//      011 = GPIO Pin X takes alternate function 4
+//      010 = GPIO Pin X takes alternate function 5
+//
+// So the 3 bits for port X are:
+//      X / 10 + ((X % 10) * 3)
+*/
+void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode)
+{
+    /* Function selects are 10 pins per 32 bit word, 3 bits per pin */
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFSEL0/4 + (pin/10);
+    uint8_t   shift = (pin % 10) * 3;
+    uint32_t  mask = BCM2835_GPIO_FSEL_MASK << shift;
+    uint32_t  value = mode << shift;
+    bcm2835_peri_set_bits(paddr, value, mask);
+}
+
+/* Set output pin */
+void bcm2835_gpio_set(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPSET0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    bcm2835_peri_write(paddr, 1 << shift);
+}
+
+/* Clear output pin */
+void bcm2835_gpio_clr(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPCLR0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    bcm2835_peri_write(paddr, 1 << shift);
+}
+
+/* Set all output pins in the mask */
+void bcm2835_gpio_set_multi(uint32_t mask)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPSET0/4;
+    bcm2835_peri_write(paddr, mask);
+}
+
+/* Clear all output pins in the mask */
+void bcm2835_gpio_clr_multi(uint32_t mask)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPCLR0/4;
+    bcm2835_peri_write(paddr, mask);
+}
+
+/* Read input pin */
+uint8_t bcm2835_gpio_lev(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEV0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    uint32_t value = bcm2835_peri_read(paddr);
+    return (value & (1 << shift)) ? HIGH : LOW;
+}
+
+/* See if an event detection bit is set
+// Sigh cant support interrupts yet
+*/
+uint8_t bcm2835_gpio_eds(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    uint32_t value = bcm2835_peri_read(paddr);
+    return (value & (1 << shift)) ? HIGH : LOW;
+}
+
+uint32_t bcm2835_gpio_eds_multi(uint32_t mask)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4;
+    uint32_t value = bcm2835_peri_read(paddr);
+    return (value & mask);
+}
+
+/* Write a 1 to clear the bit in EDS */
+void bcm2835_gpio_set_eds(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    uint32_t value = 1 << shift;
+    bcm2835_peri_write(paddr, value);
+}
+
+void bcm2835_gpio_set_eds_multi(uint32_t mask)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPEDS0/4;
+    bcm2835_peri_write(paddr, mask);
+}
+
+/* Rising edge detect enable */
+void bcm2835_gpio_ren(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPREN0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    uint32_t value = 1 << shift;
+    bcm2835_peri_set_bits(paddr, value, value);
+}
+void bcm2835_gpio_clr_ren(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPREN0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    uint32_t value = 1 << shift;
+    bcm2835_peri_set_bits(paddr, 0, value);
+}
+
+/* Falling edge detect enable */
+void bcm2835_gpio_fen(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFEN0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    uint32_t value = 1 << shift;
+    bcm2835_peri_set_bits(paddr, value, value);
+}
+void bcm2835_gpio_clr_fen(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPFEN0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    uint32_t value = 1 << shift;
+    bcm2835_peri_set_bits(paddr, 0, value);
+}
+
+/* High detect enable */
+void bcm2835_gpio_hen(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPHEN0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    uint32_t value = 1 << shift;
+    bcm2835_peri_set_bits(paddr, value, value);
+}
+void bcm2835_gpio_clr_hen(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPHEN0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    uint32_t value = 1 << shift;
+    bcm2835_peri_set_bits(paddr, 0, value);
+}
+
+/* Low detect enable */
+void bcm2835_gpio_len(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEN0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    uint32_t value = 1 << shift;
+    bcm2835_peri_set_bits(paddr, value, value);
+}
+void bcm2835_gpio_clr_len(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPLEN0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    uint32_t value = 1 << shift;
+    bcm2835_peri_set_bits(paddr, 0, value);
+}
+
+/* Async rising edge detect enable */
+void bcm2835_gpio_aren(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAREN0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    uint32_t value = 1 << shift;
+    bcm2835_peri_set_bits(paddr, value, value);
+}
+void bcm2835_gpio_clr_aren(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAREN0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    uint32_t value = 1 << shift;
+    bcm2835_peri_set_bits(paddr, 0, value);
+}
+
+/* Async falling edge detect enable */
+void bcm2835_gpio_afen(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAFEN0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    uint32_t value = 1 << shift;
+    bcm2835_peri_set_bits(paddr, value, value);
+}
+void bcm2835_gpio_clr_afen(uint8_t pin)
+{
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPAFEN0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    uint32_t value = 1 << shift;
+    bcm2835_peri_set_bits(paddr, 0, value);
+}
+
+/* Set pullup/down */
+void bcm2835_gpio_pud(uint8_t pud)
+{
+    if( pud_type_rpi4 )
+    {
+        pud_compat_setting = pud;
+    }
+    else {
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUD/4;
+    bcm2835_peri_write(paddr, pud);
+}
+}
+
+/* Pullup/down clock
+// Clocks the value of pud into the GPIO pin
+*/
+void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on)
+{
+    if( pud_type_rpi4 )
+    {
+        if( on )
+            bcm2835_gpio_set_pud( pin, pud_compat_setting);
+    }
+    else
+    {
+    volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUDCLK0/4 + pin/32;
+    uint8_t shift = pin % 32;
+    bcm2835_peri_write(paddr, (on ? 1 : 0) << shift);
+}
+}
+
+/* Read GPIO pad behaviour for groups of GPIOs */
+uint32_t bcm2835_gpio_pad(uint8_t group)
+{
+  if (bcm2835_pads == MAP_FAILED){
+    return 0;
+  }
+  
+    volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group;
+    return bcm2835_peri_read(paddr);
+}
+
+/* Set GPIO pad behaviour for groups of GPIOs
+// powerup value for all pads is
+// BCM2835_PAD_SLEW_RATE_UNLIMITED | BCM2835_PAD_HYSTERESIS_ENABLED | BCM2835_PAD_DRIVE_8mA
+*/
+void bcm2835_gpio_set_pad(uint8_t group, uint32_t control)
+{
+  if (bcm2835_pads == MAP_FAILED){
+    return;
+  }
+  
+    volatile uint32_t* paddr = bcm2835_pads + BCM2835_PADS_GPIO_0_27/4 + group;
+    bcm2835_peri_write(paddr, control | BCM2835_PAD_PASSWRD);
+}
+
+/* Some convenient arduino-like functions
+// milliseconds
+*/
+void bcm2835_delay(unsigned int millis)
+{
+    struct timespec sleeper;
+    
+    sleeper.tv_sec  = (time_t)(millis / 1000);
+    sleeper.tv_nsec = (long)(millis % 1000) * 1000000;
+    nanosleep(&sleeper, NULL);
+}
+
+/* microseconds */
+void bcm2835_delayMicroseconds(uint64_t micros)
+{
+    struct timespec t1;
+    uint64_t        start;
+       
+    if (debug)
+    {
+       /* Cant access sytem timers in debug mode */
+       printf("bcm2835_delayMicroseconds %lld\n", (long long int) micros);
+       return;
+    }
+
+    /* Calling nanosleep() takes at least 100-200 us, so use it for
+    // long waits and use a busy wait on the System Timer for the rest.
+    */
+    start =  bcm2835_st_read();
+   
+    /* Not allowed to access timer registers (result is not as precise)*/
+    if (start==0)
+    {
+       t1.tv_sec = 0;
+       t1.tv_nsec = 1000 * (long)(micros);
+       nanosleep(&t1, NULL);
+       return;
+    }
+
+    if (micros > 450)
+    {
+       t1.tv_sec = 0;
+       t1.tv_nsec = 1000 * (long)(micros - 200);
+       nanosleep(&t1, NULL);
+    }    
+  
+    bcm2835_st_delay(start, micros);
+}
+
+/*
+// Higher level convenience functions
+*/
+
+/* Set the state of an output */
+void bcm2835_gpio_write(uint8_t pin, uint8_t on)
+{
+    if (on)
+       bcm2835_gpio_set(pin);
+    else
+       bcm2835_gpio_clr(pin);
+}
+
+/* Set the state of a all 32 outputs in the mask to on or off */
+void bcm2835_gpio_write_multi(uint32_t mask, uint8_t on)
+{
+    if (on)
+       bcm2835_gpio_set_multi(mask);
+    else
+       bcm2835_gpio_clr_multi(mask);
+}
+
+/* Set the state of a all 32 outputs in the mask to the values in value */
+void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask)
+{
+    bcm2835_gpio_set_multi(value & mask);
+    bcm2835_gpio_clr_multi((~value) & mask);
+}
+
+/* Set the pullup/down resistor for a pin
+//
+// The GPIO Pull-up/down Clock Registers control the actuation of internal pull-downs on
+// the respective GPIO pins. These registers must be used in conjunction with the GPPUD
+// register to effect GPIO Pull-up/down changes. The following sequence of events is
+// required:
+// 1. Write to GPPUD to set the required control signal (i.e. Pull-up or Pull-Down or neither
+// to remove the current Pull-up/down)
+// 2. Wait 150 cycles ? this provides the required set-up time for the control signal
+// 3. Write to GPPUDCLK0/1 to clock the control signal into the GPIO pads you wish to
+// modify ? NOTE only the pads which receive a clock will be modified, all others will
+// retain their previous state.
+// 4. Wait 150 cycles ? this provides the required hold time for the control signal
+// 5. Write to GPPUD to remove the control signal
+// 6. Write to GPPUDCLK0/1 to remove the clock
+//
+// RPi has P1-03 and P1-05 with 1k8 pullup resistor
+//
+// RPI 4 uses a different PUD method - no clock
+
+*/
+void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud)
+{
+    if( pud_type_rpi4 )
+    {
+        int shiftbits = (pin & 0xf) << 1;
+        uint32_t bits;
+        uint32_t pull;
+        
+        switch (pud)
+        {
+           case BCM2835_GPIO_PUD_OFF:  pull = 0; break;
+           case BCM2835_GPIO_PUD_UP:   pull = 1; break;
+           case BCM2835_GPIO_PUD_DOWN: pull = 2; break;
+           default: return;
+        }
+                
+        volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUPPDN0/4 + (pin >> 4);
+        
+        bits = bcm2835_peri_read_nb( paddr );
+        bits &= ~(3 << shiftbits);
+        bits |= (pull << shiftbits);
+        
+        bcm2835_peri_write_nb( paddr, bits );
+        
+    } else
+    {
+    bcm2835_gpio_pud(pud);
+    delayMicroseconds(10);
+    bcm2835_gpio_pudclk(pin, 1);
+    delayMicroseconds(10);
+    bcm2835_gpio_pud(BCM2835_GPIO_PUD_OFF);
+    bcm2835_gpio_pudclk(pin, 0);
+}
+
+}
+
+
+uint8_t bcm2835_gpio_get_pud(uint8_t pin)
+{
+    uint8_t ret = BCM2835_GPIO_PUD_ERROR;
+    
+    if( pud_type_rpi4 )
+    {
+        uint32_t bits;
+        volatile uint32_t* paddr = bcm2835_gpio + BCM2835_GPPUPPDN0/4 + (pin >> 4);
+        bits = (bcm2835_peri_read_nb( paddr ) >> ((pin & 0xf)<<1)) & 0x3;
+        
+        switch (bits)
+        {
+            case 0: ret = BCM2835_GPIO_PUD_OFF; break;
+            case 1: ret = BCM2835_GPIO_PUD_UP; break;
+            case 2: ret = BCM2835_GPIO_PUD_DOWN; break;
+            default: ret = BCM2835_GPIO_PUD_ERROR;
+        }   
+    }
+    
+    return ret;
+}
+
+static void bcm2835_aux_spi_reset(void)
+ {
+     volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4;
+     volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4;
+ 
+     bcm2835_peri_write(cntl1, 0);
+     bcm2835_peri_write(cntl0, BCM2835_AUX_SPI_CNTL0_CLEARFIFO);
+}
+
+int bcm2835_spi_begin(void)
+{
+    volatile uint32_t* paddr;
+
+    if (bcm2835_spi0 == MAP_FAILED)
+      return 0; /* bcm2835_init() failed, or not root */
+    
+    /* Set the SPI0 pins to the Alt 0 function to enable SPI0 access on them */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_ALT0); /* CE1 */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_ALT0); /* CE0 */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_ALT0); /* MISO */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_ALT0); /* MOSI */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_ALT0); /* CLK */
+    
+    /* Set the SPI CS register to the some sensible defaults */
+    paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4;
+    bcm2835_peri_write(paddr, 0); /* All 0s */
+    
+    /* Clear TX and RX fifos */
+    bcm2835_peri_write_nb(paddr, BCM2835_SPI0_CS_CLEAR);
+
+    return 1; // OK
+}
+
+void bcm2835_spi_end(void)
+{  
+    /* Set all the SPI0 pins back to input */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_26, BCM2835_GPIO_FSEL_INPT); /* CE1 */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_24, BCM2835_GPIO_FSEL_INPT); /* CE0 */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_21, BCM2835_GPIO_FSEL_INPT); /* MISO */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_19, BCM2835_GPIO_FSEL_INPT); /* MOSI */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_23, BCM2835_GPIO_FSEL_INPT); /* CLK */
+}
+
+void bcm2835_spi_setBitOrder(uint8_t order)
+{
+    bcm2835_spi_bit_order = order;
+}
+
+/* defaults to 0, which means a divider of 65536.
+// The divisor must be a power of 2. Odd numbers
+// rounded down. The maximum SPI clock rate is
+// of the APB clock
+*/
+void bcm2835_spi_setClockDivider(uint16_t divider)
+{
+    volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CLK/4;
+    bcm2835_peri_write(paddr, divider);
+}
+
+void bcm2835_spi_set_speed_hz(uint32_t speed_hz)
+{
+       uint16_t divider = (uint16_t) ((uint32_t) BCM2835_CORE_CLK_HZ / speed_hz);
+       divider &= 0xFFFE;
+       bcm2835_spi_setClockDivider(divider);
+}
+
+void bcm2835_spi_setDataMode(uint8_t mode)
+{
+    volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4;
+    /* Mask in the CPO and CPHA bits of CS */
+    bcm2835_peri_set_bits(paddr, mode << 2, BCM2835_SPI0_CS_CPOL | BCM2835_SPI0_CS_CPHA);
+}
+
+/* Writes (and reads) a single byte to SPI */
+uint8_t bcm2835_spi_transfer(uint8_t value)
+{
+    volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4;
+    volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4;
+    uint32_t ret;
+
+    /* This is Polled transfer as per section 10.6.1
+    // BUG ALERT: what happens if we get interupted in this section, and someone else
+    // accesses a different peripheral? 
+    // Clear TX and RX fifos
+    */
+    bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR);
+
+    /* Set TA = 1 */
+    bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA);
+
+    /* Maybe wait for TXD */
+    while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))
+       ;
+
+    /* Write to FIFO, no barrier */
+    bcm2835_peri_write_nb(fifo, bcm2835_correct_order(value));
+
+    /* Wait for DONE to be set */
+    while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE))
+       ;
+
+    /* Read any byte that was sent back by the slave while we sere sending to it */
+    ret = bcm2835_correct_order(bcm2835_peri_read_nb(fifo));
+
+    /* Set TA = 0, and also set the barrier */
+    bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA);
+
+    return ret;
+}
+
+/* Writes (and reads) an number of bytes to SPI */
+void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len)
+{
+    volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4;
+    volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4;
+    uint32_t TXCnt=0;
+    uint32_t RXCnt=0;
+
+    /* This is Polled transfer as per section 10.6.1
+    // BUG ALERT: what happens if we get interupted in this section, and someone else
+    // accesses a different peripheral? 
+    */
+
+    /* Clear TX and RX fifos */
+    bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR);
+
+    /* Set TA = 1 */
+    bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA);
+
+    /* Use the FIFO's to reduce the interbyte times */
+    while((TXCnt < len)||(RXCnt < len))
+    {
+        /* TX fifo not full, so add some more bytes */
+        while(((bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))&&(TXCnt < len ))
+        {
+           bcm2835_peri_write_nb(fifo, bcm2835_correct_order(tbuf[TXCnt]));
+           TXCnt++;
+        }
+        /* Rx fifo not empty, so get the next received bytes */
+        while(((bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD))&&( RXCnt < len ))
+        {
+           rbuf[RXCnt] = bcm2835_correct_order(bcm2835_peri_read_nb(fifo));
+           RXCnt++;
+        }
+    }
+    /* Wait for DONE to be set */
+    while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE))
+       ;
+
+    /* Set TA = 0, and also set the barrier */
+    bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA);
+}
+
+/* Writes an number of bytes to SPI */
+void bcm2835_spi_writenb(const char* tbuf, uint32_t len)
+{
+    volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4;
+    volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4;
+    uint32_t i;
+
+    /* This is Polled transfer as per section 10.6.1
+    // BUG ALERT: what happens if we get interupted in this section, and someone else
+    // accesses a different peripheral?
+    // Answer: an ISR is required to issue the required memory barriers.
+    */
+
+    /* Clear TX and RX fifos */
+    bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR);
+
+    /* Set TA = 1 */
+    bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA);
+
+    for (i = 0; i < len; i++)
+    {
+       /* Maybe wait for TXD */
+       while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))
+           ;
+       
+       /* Write to FIFO, no barrier */
+       bcm2835_peri_write_nb(fifo, bcm2835_correct_order(tbuf[i]));
+       
+       /* Read from FIFO to prevent stalling */
+       while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD)
+           (void) bcm2835_peri_read_nb(fifo);
+    }
+    
+    /* Wait for DONE to be set */
+    while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE)) {
+       while (bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_RXD)
+               (void) bcm2835_peri_read_nb(fifo);
+    };
+
+    /* Set TA = 0, and also set the barrier */
+    bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA);
+}
+
+/* Writes (and reads) an number of bytes to SPI
+// Read bytes are copied over onto the transmit buffer
+*/
+void bcm2835_spi_transfern(char* buf, uint32_t len)
+{
+    bcm2835_spi_transfernb(buf, buf, len);
+}
+
+void bcm2835_spi_chipSelect(uint8_t cs)
+{
+    volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4;
+    /* Mask in the CS bits of CS */
+    bcm2835_peri_set_bits(paddr, cs, BCM2835_SPI0_CS_CS);
+}
+
+void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active)
+{
+    volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4;
+    uint8_t shift = 21 + cs;
+    /* Mask in the appropriate CSPOLn bit */
+    bcm2835_peri_set_bits(paddr, active << shift, 1 << shift);
+}
+
+void bcm2835_spi_write(uint16_t data)
+{
+#if 0
+       char buf[2];
+
+       buf[0] = data >> 8;
+       buf[1] = data & 0xFF;
+
+       bcm2835_spi_transfern(buf, 2);
+#else
+    volatile uint32_t* paddr = bcm2835_spi0 + BCM2835_SPI0_CS/4;
+    volatile uint32_t* fifo = bcm2835_spi0 + BCM2835_SPI0_FIFO/4;
+
+    /* Clear TX and RX fifos */
+    bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_CLEAR, BCM2835_SPI0_CS_CLEAR);
+
+    /* Set TA = 1 */
+    bcm2835_peri_set_bits(paddr, BCM2835_SPI0_CS_TA, BCM2835_SPI0_CS_TA);
+
+       /* Maybe wait for TXD */
+       while (!(bcm2835_peri_read(paddr) & BCM2835_SPI0_CS_TXD))
+           ;
+
+       /* Write to FIFO */
+       bcm2835_peri_write_nb(fifo,  (uint32_t) data >> 8);
+       bcm2835_peri_write_nb(fifo,  data & 0xFF);
+
+
+    /* Wait for DONE to be set */
+    while (!(bcm2835_peri_read_nb(paddr) & BCM2835_SPI0_CS_DONE))
+       ;
+
+    /* Set TA = 0, and also set the barrier */
+    bcm2835_peri_set_bits(paddr, 0, BCM2835_SPI0_CS_TA);
+#endif
+}
+
+int bcm2835_aux_spi_begin(void)
+{
+    volatile uint32_t* enable = bcm2835_aux + BCM2835_AUX_ENABLE/4;
+    volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4;
+    volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4;
+
+    if (bcm2835_spi1 == MAP_FAILED)
+       return 0; /* bcm2835_init() failed, or not root */
+
+    /* Set the SPI pins to the Alt 4 function to enable SPI1 access on them */
+    bcm2835_gpio_fsel(RPI_V2_GPIO_P1_36, BCM2835_GPIO_FSEL_ALT4);      /* SPI1_CE2_N */
+    bcm2835_gpio_fsel(RPI_V2_GPIO_P1_35, BCM2835_GPIO_FSEL_ALT4);      /* SPI1_MISO */
+    bcm2835_gpio_fsel(RPI_V2_GPIO_P1_38, BCM2835_GPIO_FSEL_ALT4);      /* SPI1_MOSI */
+    bcm2835_gpio_fsel(RPI_V2_GPIO_P1_40, BCM2835_GPIO_FSEL_ALT4);      /* SPI1_SCLK */
+
+    bcm2835_aux_spi_setClockDivider(bcm2835_aux_spi_CalcClockDivider(1000000));        // Default 1MHz SPI
+
+    bcm2835_peri_write(enable, BCM2835_AUX_ENABLE_SPI0);
+    bcm2835_peri_write(cntl1, 0);
+    bcm2835_peri_write(cntl0, BCM2835_AUX_SPI_CNTL0_CLEARFIFO);
+
+    return 1; /* OK */
+}
+
+void bcm2835_aux_spi_end(void)
+{
+    /* Set all the SPI1 pins back to input */
+    bcm2835_gpio_fsel(RPI_V2_GPIO_P1_36, BCM2835_GPIO_FSEL_INPT);      /* SPI1_CE2_N */
+    bcm2835_gpio_fsel(RPI_V2_GPIO_P1_35, BCM2835_GPIO_FSEL_INPT);      /* SPI1_MISO */
+    bcm2835_gpio_fsel(RPI_V2_GPIO_P1_38, BCM2835_GPIO_FSEL_INPT);      /* SPI1_MOSI */
+    bcm2835_gpio_fsel(RPI_V2_GPIO_P1_40, BCM2835_GPIO_FSEL_INPT);      /* SPI1_SCLK */
+}
+
+#define DIV_ROUND_UP(n,d)      (((n) + (d) - 1) / (d))
+
+uint16_t bcm2835_aux_spi_CalcClockDivider(uint32_t speed_hz)
+{
+    uint16_t divider;
+
+    if (speed_hz < (uint32_t) BCM2835_AUX_SPI_CLOCK_MIN) {
+       speed_hz = (uint32_t) BCM2835_AUX_SPI_CLOCK_MIN;
+    } else if (speed_hz > (uint32_t) BCM2835_AUX_SPI_CLOCK_MAX) {
+       speed_hz = (uint32_t) BCM2835_AUX_SPI_CLOCK_MAX;
+    }
+
+    divider = (uint16_t) DIV_ROUND_UP(BCM2835_CORE_CLK_HZ, 2 * speed_hz) - 1;
+
+    if (divider > (uint16_t) BCM2835_AUX_SPI_CNTL0_SPEED_MAX) {
+       return (uint16_t) BCM2835_AUX_SPI_CNTL0_SPEED_MAX;
+    }
+
+    return divider;
+}
+
+static uint32_t spi1_speed;
+
+void bcm2835_aux_spi_setClockDivider(uint16_t divider)
+{
+    spi1_speed = (uint32_t) divider;
+}
+
+void bcm2835_aux_spi_write(uint16_t data)
+{
+    volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4;
+    volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4;
+    volatile uint32_t* stat = bcm2835_spi1 + BCM2835_AUX_SPI_STAT/4;
+    volatile uint32_t* io = bcm2835_spi1 + BCM2835_AUX_SPI_IO/4;
+
+    uint32_t _cntl0 = (spi1_speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT);
+    _cntl0 |= BCM2835_AUX_SPI_CNTL0_CS2_N;
+    _cntl0 |= BCM2835_AUX_SPI_CNTL0_ENABLE;
+    _cntl0 |= BCM2835_AUX_SPI_CNTL0_MSBF_OUT;
+    _cntl0 |= 16; // Shift length
+
+    bcm2835_peri_write(cntl0, _cntl0);
+    bcm2835_peri_write(cntl1, BCM2835_AUX_SPI_CNTL1_MSBF_IN);
+
+    while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL)
+       ;
+
+    bcm2835_peri_write(io, (uint32_t) data << 16);
+}
+
+void bcm2835_aux_spi_writenb(const char *tbuf, uint32_t len) {
+    volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4;
+    volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4;
+    volatile uint32_t* stat = bcm2835_spi1 + BCM2835_AUX_SPI_STAT/4;
+    volatile uint32_t* txhold = bcm2835_spi1 + BCM2835_AUX_SPI_TXHOLD/4;
+    volatile uint32_t* io = bcm2835_spi1 + BCM2835_AUX_SPI_IO/4;
+
+    char *tx = (char *) tbuf;
+    uint32_t tx_len = len;
+    uint32_t count;
+    uint32_t data;
+    uint32_t i;
+    uint8_t byte;
+
+    uint32_t _cntl0 = (spi1_speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT);
+    _cntl0 |= BCM2835_AUX_SPI_CNTL0_CS2_N;
+    _cntl0 |= BCM2835_AUX_SPI_CNTL0_ENABLE;
+    _cntl0 |= BCM2835_AUX_SPI_CNTL0_MSBF_OUT;
+    _cntl0 |= BCM2835_AUX_SPI_CNTL0_VAR_WIDTH;
+
+    bcm2835_peri_write(cntl0, _cntl0);
+    bcm2835_peri_write(cntl1, BCM2835_AUX_SPI_CNTL1_MSBF_IN);
+
+    while (tx_len > 0) {
+
+       while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL)
+           ;
+
+       count = MIN(tx_len, 3);
+       data = 0;
+
+       for (i = 0; i < count; i++) {
+           byte = (tx != NULL) ? (uint8_t) *tx++ : (uint8_t) 0;
+           data |= byte << (8 * (2 - i));
+       }
+
+       data |= (count * 8) << 24;
+       tx_len -= count;
+
+       if (tx_len != 0) {
+           bcm2835_peri_write(txhold, data);
+       } else {
+           bcm2835_peri_write(io, data);
+       }
+
+       while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_BUSY)
+           ;
+
+       (void) bcm2835_peri_read(io);
+    }
+}
+
+void bcm2835_aux_spi_transfernb(const char *tbuf, char *rbuf, uint32_t len) {
+    volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4;
+    volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4;
+    volatile uint32_t* stat = bcm2835_spi1 + BCM2835_AUX_SPI_STAT/4;
+    volatile uint32_t* txhold = bcm2835_spi1 + BCM2835_AUX_SPI_TXHOLD/4;
+    volatile uint32_t* io = bcm2835_spi1 + BCM2835_AUX_SPI_IO/4;
+
+       char *tx = (char *)tbuf;
+       char *rx = (char *)rbuf;
+       uint32_t tx_len = len;
+       uint32_t rx_len = len;
+       uint32_t count;
+       uint32_t data;
+       uint32_t i;
+       uint8_t byte;
+
+       uint32_t _cntl0 = (spi1_speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT);
+       _cntl0 |= BCM2835_AUX_SPI_CNTL0_CS2_N;
+       _cntl0 |= BCM2835_AUX_SPI_CNTL0_ENABLE;
+       _cntl0 |= BCM2835_AUX_SPI_CNTL0_MSBF_OUT;
+       _cntl0 |= BCM2835_AUX_SPI_CNTL0_VAR_WIDTH;
+
+       bcm2835_peri_write(cntl0, _cntl0);
+       bcm2835_peri_write(cntl1, BCM2835_AUX_SPI_CNTL1_MSBF_IN);
+
+       while ((tx_len > 0) || (rx_len > 0)) {
+
+               while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_TX_FULL) && (tx_len > 0)) {
+                       count = MIN(tx_len, 3);
+                       data = 0;
+
+                       for (i = 0; i < count; i++) {
+                               byte = (tx != NULL) ? (uint8_t) *tx++ : (uint8_t) 0;
+                               data |= byte << (8 * (2 - i));
+                       }
+
+                       data |= (count * 8) << 24;
+                       tx_len -= count;
+
+                       if (tx_len != 0) {
+                               bcm2835_peri_write(txhold, data);
+                       } else {
+                               bcm2835_peri_write(io, data);
+                       }
+
+               }
+
+               while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_RX_EMPTY) && (rx_len > 0)) {
+                       count = MIN(rx_len, 3);
+                       data = bcm2835_peri_read(io);
+
+                       if (rbuf != NULL) {
+                               switch (count) {
+                               case 3:
+                                       *rx++ = (char)((data >> 16) & 0xFF);
+                                       /*@fallthrough@*/
+                                       /* no break */
+                               case 2:
+                                       *rx++ = (char)((data >> 8) & 0xFF);
+                                       /*@fallthrough@*/
+                                       /* no break */
+                               case 1:
+                                       *rx++ = (char)((data >> 0) & 0xFF);
+                               }
+                       }
+
+                       rx_len -= count;
+               }
+
+               while (!(bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_BUSY) && (rx_len > 0)) {
+                       count = MIN(rx_len, 3);
+                       data = bcm2835_peri_read(io);
+
+                       if (rbuf != NULL) {
+                               switch (count) {
+                               case 3:
+                                       *rx++ = (char)((data >> 16) & 0xFF);
+                                       /*@fallthrough@*/
+                                       /* no break */
+                               case 2:
+                                       *rx++ = (char)((data >> 8) & 0xFF);
+                                       /*@fallthrough@*/
+                                       /* no break */
+                               case 1:
+                                       *rx++ = (char)((data >> 0) & 0xFF);
+                               }
+                       }
+
+                       rx_len -= count;
+               }
+       }
+}
+
+void bcm2835_aux_spi_transfern(char *buf, uint32_t len) {
+       bcm2835_aux_spi_transfernb(buf, buf, len);
+}
+
+/* Writes (and reads) a single byte to AUX SPI */
+uint8_t bcm2835_aux_spi_transfer(uint8_t value)
+{
+    volatile uint32_t* cntl0 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL0/4;
+    volatile uint32_t* cntl1 = bcm2835_spi1 + BCM2835_AUX_SPI_CNTL1/4;
+    volatile uint32_t* stat = bcm2835_spi1 + BCM2835_AUX_SPI_STAT/4;
+    volatile uint32_t* io = bcm2835_spi1 + BCM2835_AUX_SPI_IO/4;
+
+    uint32_t data;
+
+    uint32_t _cntl0 = (spi1_speed << BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT);
+    _cntl0 |= BCM2835_AUX_SPI_CNTL0_CS2_N;
+    _cntl0 |= BCM2835_AUX_SPI_CNTL0_ENABLE;
+    _cntl0 |= BCM2835_AUX_SPI_CNTL0_MSBF_OUT;
+    _cntl0 |= BCM2835_AUX_SPI_CNTL0_CPHA_IN;
+    _cntl0 |= 8; // Shift length.
+
+    uint32_t _cntl1 = BCM2835_AUX_SPI_CNTL1_MSBF_IN;
+
+    bcm2835_peri_write(cntl1, _cntl1);
+    bcm2835_peri_write(cntl0, _cntl0);
+
+    bcm2835_peri_write(io, (uint32_t) bcm2835_correct_order(value) << 24);
+
+    while (bcm2835_peri_read(stat) & BCM2835_AUX_SPI_STAT_BUSY)
+        ;
+
+    data = bcm2835_correct_order(bcm2835_peri_read(io) & 0xff);
+
+    bcm2835_aux_spi_reset();
+
+    return data;
+}
+
+
+int bcm2835_i2c_begin(void)
+{
+    uint16_t cdiv;
+
+    if (   bcm2835_bsc0 == MAP_FAILED
+       || bcm2835_bsc1 == MAP_FAILED)
+      return 0; /* bcm2835_init() failed, or not root */
+
+#ifdef I2C_V1
+    volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4;
+    /* Set the I2C/BSC0 pins to the Alt 0 function to enable I2C access on them */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); /* SDA */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); /* SCL */
+#else
+    volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4;
+    /* Set the I2C/BSC1 pins to the Alt 0 function to enable I2C access on them */
+    bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_ALT0); /* SDA */
+    bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_ALT0); /* SCL */
+#endif    
+
+    /* Read the clock divider register */
+    cdiv = bcm2835_peri_read(paddr);
+    /* Calculate time for transmitting one byte
+    // 1000000 = micros seconds in a second
+    // 9 = Clocks per byte : 8 bits + ACK
+    */
+    i2c_byte_wait_us = ((float)cdiv / BCM2835_CORE_CLK_HZ) * 1000000 * 9;
+
+    return 1;
+}
+
+void bcm2835_i2c_end(void)
+{
+#ifdef I2C_V1
+    /* Set all the I2C/BSC0 pins back to input */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); /* SDA */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); /* SCL */
+#else
+    /* Set all the I2C/BSC1 pins back to input */
+    bcm2835_gpio_fsel(RPI_V2_GPIO_P1_03, BCM2835_GPIO_FSEL_INPT); /* SDA */
+    bcm2835_gpio_fsel(RPI_V2_GPIO_P1_05, BCM2835_GPIO_FSEL_INPT); /* SCL */
+#endif
+}
+
+void bcm2835_i2c_setSlaveAddress(uint8_t addr)
+{
+    /* Set I2C Device Address */
+#ifdef I2C_V1
+    volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_A/4;
+#else  
+    volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_A/4;
+#endif
+    bcm2835_peri_write(paddr, addr);
+}
+
+/* defaults to 0x5dc, should result in a 166.666 kHz I2C clock frequency.
+// The divisor must be a power of 2. Odd numbers
+// rounded down.
+*/
+void bcm2835_i2c_setClockDivider(uint16_t divider)
+{
+#ifdef I2C_V1
+    volatile uint32_t* paddr = bcm2835_bsc0 + BCM2835_BSC_DIV/4;
+#else
+    volatile uint32_t* paddr = bcm2835_bsc1 + BCM2835_BSC_DIV/4;
+#endif    
+    bcm2835_peri_write(paddr, divider);
+    /* Calculate time for transmitting one byte
+    // 1000000 = micros seconds in a second
+    // 9 = Clocks per byte : 8 bits + ACK
+    */
+    i2c_byte_wait_us = ((float)divider / BCM2835_CORE_CLK_HZ) * 1000000 * 9;
+}
+
+/* set I2C clock divider by means of a baudrate number */
+void bcm2835_i2c_set_baudrate(uint32_t baudrate)
+{
+       uint32_t divider;
+       /* use 0xFFFE mask to limit a max value and round down any odd number */
+       divider = (BCM2835_CORE_CLK_HZ / baudrate) & 0xFFFE;
+       bcm2835_i2c_setClockDivider( (uint16_t)divider );
+}
+
+/* Writes an number of bytes to I2C */
+uint8_t bcm2835_i2c_write(const char * buf, uint32_t len)
+{
+#ifdef I2C_V1
+    volatile uint32_t* dlen    = bcm2835_bsc0 + BCM2835_BSC_DLEN/4;
+    volatile uint32_t* fifo    = bcm2835_bsc0 + BCM2835_BSC_FIFO/4;
+    volatile uint32_t* status  = bcm2835_bsc0 + BCM2835_BSC_S/4;
+    volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4;
+#else
+    volatile uint32_t* dlen    = bcm2835_bsc1 + BCM2835_BSC_DLEN/4;
+    volatile uint32_t* fifo    = bcm2835_bsc1 + BCM2835_BSC_FIFO/4;
+    volatile uint32_t* status  = bcm2835_bsc1 + BCM2835_BSC_S/4;
+    volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4;
+#endif    
+
+    uint32_t remaining = len;
+    uint32_t i = 0;
+    uint8_t reason = BCM2835_I2C_REASON_OK;
+
+    /* Clear FIFO */
+    bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 );
+    /* Clear Status */
+    bcm2835_peri_write(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE);
+    /* Set Data Length */
+    bcm2835_peri_write(dlen, len);
+    /* pre populate FIFO with max buffer */
+    while( remaining && ( i < BCM2835_BSC_FIFO_SIZE ) )
+    {
+        bcm2835_peri_write_nb(fifo, buf[i]);
+        i++;
+        remaining--;
+    }
+    
+    /* Enable device and start transfer */
+    bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST);
+    
+    /* Transfer is over when BCM2835_BSC_S_DONE */
+    while(!(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE ))
+    {
+        while ( remaining && (bcm2835_peri_read(status) & BCM2835_BSC_S_TXD ))
+       {
+           /* Write to FIFO */
+           bcm2835_peri_write(fifo, buf[i]);
+           i++;
+           remaining--;
+       }
+    }
+
+    /* Received a NACK */
+    if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR)
+    {
+       reason = BCM2835_I2C_REASON_ERROR_NACK;
+    }
+
+    /* Received Clock Stretch Timeout */
+    else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT)
+    {
+       reason = BCM2835_I2C_REASON_ERROR_CLKT;
+    }
+
+    /* Not all data is sent */
+    else if (remaining)
+    {
+       reason = BCM2835_I2C_REASON_ERROR_DATA;
+    }
+
+    bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE);
+
+    return reason;
+}
+
+/* Read an number of bytes from I2C */
+uint8_t bcm2835_i2c_read(char* buf, uint32_t len)
+{
+#ifdef I2C_V1
+    volatile uint32_t* dlen    = bcm2835_bsc0 + BCM2835_BSC_DLEN/4;
+    volatile uint32_t* fifo    = bcm2835_bsc0 + BCM2835_BSC_FIFO/4;
+    volatile uint32_t* status  = bcm2835_bsc0 + BCM2835_BSC_S/4;
+    volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4;
+#else
+    volatile uint32_t* dlen    = bcm2835_bsc1 + BCM2835_BSC_DLEN/4;
+    volatile uint32_t* fifo    = bcm2835_bsc1 + BCM2835_BSC_FIFO/4;
+    volatile uint32_t* status  = bcm2835_bsc1 + BCM2835_BSC_S/4;
+    volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4;
+#endif    
+
+    uint32_t remaining = len;
+    uint32_t i = 0;
+    uint8_t reason = BCM2835_I2C_REASON_OK;
+
+    /* Clear FIFO */
+    bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 );
+    /* Clear Status */
+    bcm2835_peri_write_nb(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE);
+    /* Set Data Length */
+    bcm2835_peri_write_nb(dlen, len);
+    /* Start read */
+    bcm2835_peri_write_nb(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST | BCM2835_BSC_C_READ);
+    
+    /* wait for transfer to complete */
+    while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE))
+    {
+        /* we must empty the FIFO as it is populated and not use any delay */
+        while (remaining && bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD)
+       {
+           /* Read from FIFO, no barrier */
+           buf[i] = bcm2835_peri_read_nb(fifo);
+           i++;
+           remaining--;
+       }
+    }
+    
+    /* transfer has finished - grab any remaining stuff in FIFO */
+    while (remaining && (bcm2835_peri_read_nb(status) & BCM2835_BSC_S_RXD))
+    {
+        /* Read from FIFO, no barrier */
+        buf[i] = bcm2835_peri_read_nb(fifo);
+        i++;
+        remaining--;
+    }
+    
+    /* Received a NACK */
+    if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR)
+    {
+       reason = BCM2835_I2C_REASON_ERROR_NACK;
+    }
+
+    /* Received Clock Stretch Timeout */
+    else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT)
+    {
+       reason = BCM2835_I2C_REASON_ERROR_CLKT;
+    }
+
+    /* Not all data is received */
+    else if (remaining)
+    {
+       reason = BCM2835_I2C_REASON_ERROR_DATA;
+    }
+
+    bcm2835_peri_set_bits(status, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE);
+
+    return reason;
+}
+
+/* Read an number of bytes from I2C sending a repeated start after writing
+// the required register. Only works if your device supports this mode
+*/
+uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len)
+{   
+#ifdef I2C_V1
+    volatile uint32_t* dlen    = bcm2835_bsc0 + BCM2835_BSC_DLEN/4;
+    volatile uint32_t* fifo    = bcm2835_bsc0 + BCM2835_BSC_FIFO/4;
+    volatile uint32_t* status  = bcm2835_bsc0 + BCM2835_BSC_S/4;
+    volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4;
+#else
+    volatile uint32_t* dlen    = bcm2835_bsc1 + BCM2835_BSC_DLEN/4;
+    volatile uint32_t* fifo    = bcm2835_bsc1 + BCM2835_BSC_FIFO/4;
+    volatile uint32_t* status  = bcm2835_bsc1 + BCM2835_BSC_S/4;
+    volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4;
+#endif    
+       uint32_t remaining = len;
+    uint32_t i = 0;
+    uint8_t reason = BCM2835_I2C_REASON_OK;
+    
+    /* Clear FIFO */
+    bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 );
+    /* Clear Status */
+    bcm2835_peri_write(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE);
+    /* Set Data Length */
+    bcm2835_peri_write(dlen, 1);
+    /* Enable device and start transfer */
+    bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN);
+    bcm2835_peri_write(fifo, regaddr[0]);
+    bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST);
+    
+    /* poll for transfer has started */
+    while ( !( bcm2835_peri_read(status) & BCM2835_BSC_S_TA ) )
+    {
+        /* Linux may cause us to miss entire transfer stage */
+        if(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE)
+            break;
+    }
+    
+    /* Send a repeated start with read bit set in address */
+    bcm2835_peri_write(dlen, len);
+    bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST  | BCM2835_BSC_C_READ );
+    
+    /* Wait for write to complete and first byte back. */
+    bcm2835_delayMicroseconds(i2c_byte_wait_us * 3);
+    
+    /* wait for transfer to complete */
+    while (!(bcm2835_peri_read(status) & BCM2835_BSC_S_DONE))
+    {
+        /* we must empty the FIFO as it is populated and not use any delay */
+        while (remaining && bcm2835_peri_read(status) & BCM2835_BSC_S_RXD)
+       {
+           /* Read from FIFO */
+           buf[i] = bcm2835_peri_read(fifo);
+           i++;
+           remaining--;
+       }
+    }
+    
+    /* transfer has finished - grab any remaining stuff in FIFO */
+    while (remaining && (bcm2835_peri_read(status) & BCM2835_BSC_S_RXD))
+    {
+        /* Read from FIFO */
+        buf[i] = bcm2835_peri_read(fifo);
+        i++;
+        remaining--;
+    }
+    
+    /* Received a NACK */
+    if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR)
+    {
+               reason = BCM2835_I2C_REASON_ERROR_NACK;
+    }
+
+    /* Received Clock Stretch Timeout */
+    else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT)
+    {
+       reason = BCM2835_I2C_REASON_ERROR_CLKT;
+    }
+
+    /* Not all data is sent */
+    else if (remaining)
+    {
+       reason = BCM2835_I2C_REASON_ERROR_DATA;
+    }
+
+    bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE);
+
+    return reason;
+}
+
+/* Sending an arbitrary number of bytes before issuing a repeated start 
+// (with no prior stop) and reading a response. Some devices require this behavior.
+*/
+uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint32_t buf_len)
+{   
+#ifdef I2C_V1
+    volatile uint32_t* dlen    = bcm2835_bsc0 + BCM2835_BSC_DLEN/4;
+    volatile uint32_t* fifo    = bcm2835_bsc0 + BCM2835_BSC_FIFO/4;
+    volatile uint32_t* status  = bcm2835_bsc0 + BCM2835_BSC_S/4;
+    volatile uint32_t* control = bcm2835_bsc0 + BCM2835_BSC_C/4;
+#else
+    volatile uint32_t* dlen    = bcm2835_bsc1 + BCM2835_BSC_DLEN/4;
+    volatile uint32_t* fifo    = bcm2835_bsc1 + BCM2835_BSC_FIFO/4;
+    volatile uint32_t* status  = bcm2835_bsc1 + BCM2835_BSC_S/4;
+    volatile uint32_t* control = bcm2835_bsc1 + BCM2835_BSC_C/4;
+#endif    
+
+    uint32_t remaining = cmds_len;
+    uint32_t i = 0;
+    uint8_t reason = BCM2835_I2C_REASON_OK;
+    
+    /* Clear FIFO */
+    bcm2835_peri_set_bits(control, BCM2835_BSC_C_CLEAR_1 , BCM2835_BSC_C_CLEAR_1 );
+
+    /* Clear Status */
+    bcm2835_peri_write(status, BCM2835_BSC_S_CLKT | BCM2835_BSC_S_ERR | BCM2835_BSC_S_DONE);
+
+    /* Set Data Length */
+    bcm2835_peri_write(dlen, cmds_len);
+ 
+    /* pre populate FIFO with max buffer */
+    while( remaining && ( i < BCM2835_BSC_FIFO_SIZE ) )
+    {
+        bcm2835_peri_write_nb(fifo, cmds[i]);
+        i++;
+        remaining--;
+    }
+
+    /* Enable device and start transfer */
+    bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST);
+    
+    /* poll for transfer has started (way to do repeated start, from BCM2835 datasheet) */
+    while ( !( bcm2835_peri_read(status) & BCM2835_BSC_S_TA ) )
+    {
+        /* Linux may cause us to miss entire transfer stage */
+        if(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE)
+            break;
+    }
+    
+    remaining = buf_len;
+    i = 0;
+
+    /* Send a repeated start with read bit set in address */
+    bcm2835_peri_write(dlen, buf_len);
+    bcm2835_peri_write(control, BCM2835_BSC_C_I2CEN | BCM2835_BSC_C_ST  | BCM2835_BSC_C_READ );
+    
+    /* Wait for write to complete and first byte back. */
+    bcm2835_delayMicroseconds(i2c_byte_wait_us * (cmds_len + 1));
+    
+    /* wait for transfer to complete */
+    while (!(bcm2835_peri_read_nb(status) & BCM2835_BSC_S_DONE))
+    {
+        /* we must empty the FIFO as it is populated and not use any delay */
+        while (remaining && bcm2835_peri_read(status) & BCM2835_BSC_S_RXD)
+       {
+           /* Read from FIFO, no barrier */
+           buf[i] = bcm2835_peri_read_nb(fifo);
+           i++;
+           remaining--;
+       }
+    }
+    
+    /* transfer has finished - grab any remaining stuff in FIFO */
+    while (remaining && (bcm2835_peri_read(status) & BCM2835_BSC_S_RXD))
+    {
+        /* Read from FIFO */
+        buf[i] = bcm2835_peri_read(fifo);
+        i++;
+        remaining--;
+    }
+    
+    /* Received a NACK */
+    if (bcm2835_peri_read(status) & BCM2835_BSC_S_ERR)
+    {
+       reason = BCM2835_I2C_REASON_ERROR_NACK;
+    }
+
+    /* Received Clock Stretch Timeout */
+    else if (bcm2835_peri_read(status) & BCM2835_BSC_S_CLKT)
+    {
+       reason = BCM2835_I2C_REASON_ERROR_CLKT;
+    }
+
+    /* Not all data is sent */
+    else if (remaining)
+    {
+       reason = BCM2835_I2C_REASON_ERROR_DATA;
+    }
+
+    bcm2835_peri_set_bits(control, BCM2835_BSC_S_DONE , BCM2835_BSC_S_DONE);
+
+    return reason;
+}
+
+/* Read the System Timer Counter (64-bits) */
+uint64_t bcm2835_st_read(void)
+{
+    volatile uint32_t* paddr;
+    uint32_t hi, lo;
+    uint64_t st;
+
+    if (bcm2835_st==MAP_FAILED)
+       return 0;
+
+    paddr = bcm2835_st + BCM2835_ST_CHI/4;
+    hi = bcm2835_peri_read(paddr);
+
+    paddr = bcm2835_st + BCM2835_ST_CLO/4;
+    lo = bcm2835_peri_read(paddr);
+    
+    paddr = bcm2835_st + BCM2835_ST_CHI/4;
+    st = bcm2835_peri_read(paddr);
+    
+    /* Test for overflow */
+    if (st == hi)
+    {
+        st <<= 32;
+        st += lo;
+    }
+    else
+    {
+        st <<= 32;
+        paddr = bcm2835_st + BCM2835_ST_CLO/4;
+        st += bcm2835_peri_read(paddr);
+    }
+    return st;
+}
+
+/* Delays for the specified number of microseconds with offset */
+void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros)
+{
+    uint64_t compare = offset_micros + micros;
+
+    while(bcm2835_st_read() < compare)
+       ;
+}
+
+/* PWM */
+
+void bcm2835_pwm_set_clock(uint32_t divisor)
+{
+    if (   bcm2835_clk == MAP_FAILED
+        || bcm2835_pwm == MAP_FAILED)
+      return; /* bcm2835_init() failed or not root */
+  
+    /* From Gerts code */
+    divisor &= 0xfff;
+    /* Stop PWM clock */
+    bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x01);
+    bcm2835_delay(110); /* Prevents clock going slow */
+    /* Wait for the clock to be not busy */
+    while ((bcm2835_peri_read(bcm2835_clk + BCM2835_PWMCLK_CNTL) & 0x80) != 0)
+       bcm2835_delay(1); 
+    /* set the clock divider and enable PWM clock */
+    bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_DIV, BCM2835_PWM_PASSWRD | (divisor << 12));
+    bcm2835_peri_write(bcm2835_clk + BCM2835_PWMCLK_CNTL, BCM2835_PWM_PASSWRD | 0x11); /* Source=osc and 
enable */
+}
+
+void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled)
+{
+  if (   bcm2835_clk == MAP_FAILED
+       || bcm2835_pwm == MAP_FAILED)
+    return; /* bcm2835_init() failed or not root */
+
+  uint32_t control = bcm2835_peri_read(bcm2835_pwm + BCM2835_PWM_CONTROL);
+
+  if (channel == 0)
+    {
+      if (markspace)
+       control |= BCM2835_PWM0_MS_MODE;
+      else
+       control &= ~BCM2835_PWM0_MS_MODE;
+      if (enabled)
+       control |= BCM2835_PWM0_ENABLE;
+      else
+       control &= ~BCM2835_PWM0_ENABLE;
+    }
+  else if (channel == 1)
+    {
+      if (markspace)
+       control |= BCM2835_PWM1_MS_MODE;
+      else
+       control &= ~BCM2835_PWM1_MS_MODE;
+      if (enabled)
+       control |= BCM2835_PWM1_ENABLE;
+      else
+       control &= ~BCM2835_PWM1_ENABLE;
+    }
+
+  /* If you use the barrier here, wierd things happen, and the commands dont work */
+  bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, control);
+  /*  bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM_CONTROL, BCM2835_PWM0_ENABLE | BCM2835_PWM1_ENABLE | 
BCM2835_PWM0_MS_MODE | BCM2835_PWM1_MS_MODE); */
+
+}
+
+void bcm2835_pwm_set_range(uint8_t channel, uint32_t range)
+{
+  if (   bcm2835_clk == MAP_FAILED
+       || bcm2835_pwm == MAP_FAILED)
+    return; /* bcm2835_init() failed or not root */
+
+  if (channel == 0)
+      bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_RANGE, range);
+  else if (channel == 1)
+      bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_RANGE, range);
+}
+
+void bcm2835_pwm_set_data(uint8_t channel, uint32_t data)
+{
+  if (   bcm2835_clk == MAP_FAILED
+       || bcm2835_pwm == MAP_FAILED)
+    return; /* bcm2835_init() failed or not root */
+
+  if (channel == 0)
+      bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM0_DATA, data);
+  else if (channel == 1)
+      bcm2835_peri_write_nb(bcm2835_pwm + BCM2835_PWM1_DATA, data);
+}
+
+/* Allocate page-aligned memory. */
+void *malloc_aligned(size_t size)
+{
+    void *mem;
+    errno = posix_memalign(&mem, BCM2835_PAGE_SIZE, size);
+    return (errno ? NULL : mem);
+}
+
+/* Map 'size' bytes starting at 'off' in file 'fd' to memory.
+// Return mapped address on success, MAP_FAILED otherwise.
+// On error print message.
+*/
+static void *mapmem(const char *msg, size_t size, int fd, off_t off)
+{
+    void *map = mmap(NULL, size, (PROT_READ | PROT_WRITE), MAP_SHARED, fd, off);
+    if (map == MAP_FAILED)
+       fprintf(stderr, "bcm2835_init: %s mmap failed: %s\n", msg, strerror(errno));
+    return map;
+}
+
+static void unmapmem(void **pmem, size_t size)
+{
+    if (*pmem == MAP_FAILED) return;
+    munmap(*pmem, size);
+    *pmem = MAP_FAILED;
+}
+
+/* Initialise this library. */
+int bcm2835_init(void)
+{
+    int  memfd;
+    int  ok;
+    FILE *fp;
+
+    if (debug) 
+    {
+        bcm2835_peripherals = (uint32_t*)BCM2835_PERI_BASE;
+
+       bcm2835_pads = bcm2835_peripherals + BCM2835_GPIO_PADS/4;
+       bcm2835_clk  = bcm2835_peripherals + BCM2835_CLOCK_BASE/4;
+       bcm2835_gpio = bcm2835_peripherals + BCM2835_GPIO_BASE/4;
+       bcm2835_pwm  = bcm2835_peripherals + BCM2835_GPIO_PWM/4;
+       bcm2835_spi0 = bcm2835_peripherals + BCM2835_SPI0_BASE/4;
+       bcm2835_bsc0 = bcm2835_peripherals + BCM2835_BSC0_BASE/4;
+       bcm2835_bsc1 = bcm2835_peripherals + BCM2835_BSC1_BASE/4;
+       bcm2835_st   = bcm2835_peripherals + BCM2835_ST_BASE/4;
+       bcm2835_aux  = bcm2835_peripherals + BCM2835_AUX_BASE/4;
+       bcm2835_spi1 = bcm2835_peripherals + BCM2835_SPI1_BASE/4;
+
+       return 1; /* Success */
+    }
+
+    /* Figure out the base and size of the peripheral address block
+    // using the device-tree. Required for RPi2/3/4, optional for RPi 1
+    */
+    if ((fp = fopen(BMC2835_RPI2_DT_FILENAME , "rb")))
+    {
+        unsigned char buf[16];
+        uint32_t base_address;
+        uint32_t peri_size;
+        if (fread(buf, 1, sizeof(buf), fp) >= 8)
+        {
+            base_address = (buf[4] << 24) |
+              (buf[5] << 16) |
+              (buf[6] << 8) |
+              (buf[7] << 0);
+            
+            peri_size = (buf[8] << 24) |
+              (buf[9] << 16) |
+              (buf[10] << 8) |
+              (buf[11] << 0);
+            
+            if (!base_address)
+            {
+                /* looks like RPI 4 */
+                base_address = (buf[8] << 24) |
+                      (buf[9] << 16) |
+                      (buf[10] << 8) |
+                      (buf[11] << 0);
+                      
+                peri_size = (buf[12] << 24) |
+                (buf[13] << 16) |
+                (buf[14] << 8) |
+                (buf[15] << 0);
+            }
+            /* check for valid known range formats */
+            if ((buf[0] == 0x7e) &&
+                    (buf[1] == 0x00) &&
+                    (buf[2] == 0x00) &&
+                    (buf[3] == 0x00) &&
+                    ((base_address == BCM2835_PERI_BASE) || (base_address == BCM2835_RPI2_PERI_BASE) || 
(base_address == BCM2835_RPI4_PERI_BASE)))
+            {
+                bcm2835_peripherals_base = (off_t)base_address;
+                bcm2835_peripherals_size = (size_t)peri_size;
+                if( base_address == BCM2835_RPI4_PERI_BASE )
+                {
+                    pud_type_rpi4 = 1;
+                }
+            }
+        
+        }
+        
+       fclose(fp);
+    }
+    /* else we are prob on RPi 1 with BCM2835, and use the hardwired defaults */
+
+    /* Now get ready to map the peripherals block 
+     * If we are not root, try for the new /dev/gpiomem interface and accept
+     * the fact that we can only access GPIO
+     * else try for the /dev/mem interface and get access to everything
+     */
+    memfd = -1;
+    ok = 0;
+    if (geteuid() == 0
+#ifdef BCM2835_HAVE_LIBCAP
+       || bcm2835_has_capability(CAP_SYS_RAWIO)
+#endif
+       )
+    {
+      /* Open the master /dev/mem device */
+      if ((memfd = open("/dev/mem", O_RDWR | O_SYNC) ) < 0) 
+       {
+         fprintf(stderr, "bcm2835_init: Unable to open /dev/mem: %s\n",
+                 strerror(errno)) ;
+         goto exit;
+       }
+      
+      /* Base of the peripherals block is mapped to VM */
+      bcm2835_peripherals = mapmem("gpio", bcm2835_peripherals_size, memfd, bcm2835_peripherals_base);
+      if (bcm2835_peripherals == MAP_FAILED) goto exit;
+      
+      /* Now compute the base addresses of various peripherals, 
+      // which are at fixed offsets within the mapped peripherals block
+      // Caution: bcm2835_peripherals is uint32_t*, so divide offsets by 4
+      */
+      bcm2835_gpio = bcm2835_peripherals + BCM2835_GPIO_BASE/4;
+      bcm2835_pwm  = bcm2835_peripherals + BCM2835_GPIO_PWM/4;
+      bcm2835_clk  = bcm2835_peripherals + BCM2835_CLOCK_BASE/4;
+      bcm2835_pads = bcm2835_peripherals + BCM2835_GPIO_PADS/4;
+      bcm2835_spi0 = bcm2835_peripherals + BCM2835_SPI0_BASE/4;
+      bcm2835_bsc0 = bcm2835_peripherals + BCM2835_BSC0_BASE/4; /* I2C */
+      bcm2835_bsc1 = bcm2835_peripherals + BCM2835_BSC1_BASE/4; /* I2C */
+      bcm2835_st   = bcm2835_peripherals + BCM2835_ST_BASE/4;
+      bcm2835_aux  = bcm2835_peripherals + BCM2835_AUX_BASE/4;
+      bcm2835_spi1 = bcm2835_peripherals + BCM2835_SPI1_BASE/4;
+
+      ok = 1;
+    }
+    else
+    {
+      /* Not root, try /dev/gpiomem */
+      /* Open the master /dev/mem device */
+      if ((memfd = open("/dev/gpiomem", O_RDWR | O_SYNC) ) < 0) 
+       {
+         fprintf(stderr, "bcm2835_init: Unable to open /dev/gpiomem: %s\n",
+                 strerror(errno)) ;
+         goto exit;
+       }
+      
+      /* Base of the peripherals block is mapped to VM */
+      bcm2835_peripherals_base = 0;
+      bcm2835_peripherals = mapmem("gpio", bcm2835_peripherals_size, memfd, bcm2835_peripherals_base);
+      if (bcm2835_peripherals == MAP_FAILED) goto exit;
+      bcm2835_gpio = bcm2835_peripherals;
+      ok = 1;
+    }
+
+exit:
+    if (memfd >= 0)
+        close(memfd);
+
+    if (!ok)
+       bcm2835_close();
+
+    return ok;
+}
+
+/* Close this library and deallocate everything */
+int bcm2835_close(void)
+{
+    if (debug) return 1; /* Success */
+
+    unmapmem((void**) &bcm2835_peripherals, bcm2835_peripherals_size);
+    bcm2835_peripherals = MAP_FAILED;
+    bcm2835_gpio = MAP_FAILED;
+    bcm2835_pwm  = MAP_FAILED;
+    bcm2835_clk  = MAP_FAILED;
+    bcm2835_pads = MAP_FAILED;
+    bcm2835_spi0 = MAP_FAILED;
+    bcm2835_bsc0 = MAP_FAILED;
+    bcm2835_bsc1 = MAP_FAILED;
+    bcm2835_st   = MAP_FAILED;
+    bcm2835_aux  = MAP_FAILED;
+    bcm2835_spi1 = MAP_FAILED;
+    return 1; /* Success */
+}    
+
+#ifdef BCM2835_TEST
+/* this is a simple test program that prints out what it will do rather than 
+// actually doing it
+*/
+int main(int argc, char **argv)
+{
+    /* Be non-destructive */
+    bcm2835_set_debug(1);
+
+    if (!bcm2835_init())
+       return 1;
+
+    /* Configure some GPIO pins fo some testing
+    // Set RPI pin P1-11 to be an output
+    */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_11, BCM2835_GPIO_FSEL_OUTP);
+    /* Set RPI pin P1-15 to be an input */
+    bcm2835_gpio_fsel(RPI_GPIO_P1_15, BCM2835_GPIO_FSEL_INPT);
+    /*  with a pullup */
+    bcm2835_gpio_set_pud(RPI_GPIO_P1_15, BCM2835_GPIO_PUD_UP);
+    /* And a low detect enable */
+    bcm2835_gpio_len(RPI_GPIO_P1_15);
+    /* and input hysteresis disabled on GPIOs 0 to 27 */
+    bcm2835_gpio_set_pad(BCM2835_PAD_GROUP_GPIO_0_27, BCM2835_PAD_SLEW_RATE_UNLIMITED|BCM2835_PAD_DRIVE_8mA);
+
+#if 1
+    /* Blink */
+    while (1)
+    {
+       /* Turn it on */
+       bcm2835_gpio_write(RPI_GPIO_P1_11, HIGH);
+       
+       /* wait a bit */
+       bcm2835_delay(500);
+       
+       /* turn it off */
+       bcm2835_gpio_write(RPI_GPIO_P1_11, LOW);
+       
+       /* wait a bit */
+       bcm2835_delay(500);
+    }
+#endif
+
+#if 0
+    /* Read input */
+    while (1)
+    {
+       /* Read some data */
+       uint8_t value = bcm2835_gpio_lev(RPI_GPIO_P1_15);
+       printf("read from pin 15: %d\n", value);
+       
+       /* wait a bit */
+       bcm2835_delay(500);
+    }
+#endif
+
+#if 0
+    /* Look for a low event detection
+    // eds will be set whenever pin 15 goes low
+    */
+    while (1)
+    {
+       if (bcm2835_gpio_eds(RPI_GPIO_P1_15))
+       {
+           /* Now clear the eds flag by setting it to 1 */
+           bcm2835_gpio_set_eds(RPI_GPIO_P1_15);
+           printf("low event detect for pin 15\n");
+       }
+
+       /* wait a bit */
+       bcm2835_delay(500);
+    }
+#endif
+
+    if (!bcm2835_close())
+       return 1;
+
+    return 0;
+}
+#endif
+
+
+
diff --git a/arduino/libraries/RF24/utility/RPi/bcm2835.h b/arduino/libraries/RF24/utility/RPi/bcm2835.h
new file mode 100644
index 000000000..bd5e340c0
--- /dev/null
+++ b/arduino/libraries/RF24/utility/RPi/bcm2835.h
@@ -0,0 +1,2029 @@
+/* bcm2835.h
+  
+   C and C++ support for Broadcom BCM 2835 as used in Raspberry Pi
+  
+   Author: Mike McCauley
+   Copyright (C) 2011-2013 Mike McCauley
+   $Id: bcm2835.h,v 1.26 2020/01/11 05:07:13 mikem Exp mikem $
+*/
+
+/*! \mainpage C library for Broadcom BCM 2835 as used in Raspberry Pi
+  
+  This is a C library for Raspberry Pi (RPi). It provides access to 
+  GPIO and other IO functions on the Broadcom BCM 2835 chip, as used in the RaspberryPi,
+  allowing access to the GPIO pins on the
+  26 pin IDE plug on the RPi board so you can control and interface with various external devices.
+  
+  It provides functions for reading digital inputs and setting digital outputs, using SPI and I2C,
+  and for accessing the system timers.
+  Pin event detection is supported by polling (interrupts are not supported).
+
+  Works on all versions upt to and including RPI 4. 
+  Works with all versions of Debian up to and including Debian Buster 10.
+  
+  It is C++ compatible, and installs as a header file and non-shared library on 
+  any Linux-based distro (but clearly is no use except on Raspberry Pi or another board with 
+  BCM 2835).
+  
+  The version of the package that this documentation refers to can be downloaded 
+  from http://www.airspayce.com/mikem/bcm2835/bcm2835-1.64.tar.gz
+  You can find the latest version at http://www.airspayce.com/mikem/bcm2835
+  
+  Several example programs are provided.
+  
+  Based on data in http://elinux.org/RPi_Low-level_peripherals and 
+  http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf
+  and http://www.scribd.com/doc/101830961/GPIO-Pads-Control2
+  
+  You can also find online help and discussion at http://groups.google.com/group/bcm2835
+  Please use that group for all questions and discussions on this topic. 
+  Do not contact the author directly, unless it is to discuss commercial licensing.
+  Before asking a question or reporting a bug, please read 
+  - http://en.wikipedia.org/wiki/Wikipedia:Reference_desk/How_to_ask_a_software_question
+  - http://www.catb.org/esr/faqs/smart-questions.html
+  - http://www.chiark.greenend.org.uk/~shgtatham/bugs.html
+  
+  Tested on debian6-19-04-2012, 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian
+  and Occidentalisv01, 2016-02-09 Raspbian Jessie.
+  CAUTION: it has been observed that when detect enables such as bcm2835_gpio_len() 
+  are used and the pin is pulled LOW
+  it can cause temporary hangs on 2012-07-15-wheezy-raspbian, 2013-07-26-wheezy-raspbian
+  and Occidentalisv01.
+  Reason for this is not yet determined, but we suspect that an interrupt handler is
+  hitting a hard loop on those OSs.
+  If you must use bcm2835_gpio_len() and friends, make sure you disable the pins with 
+  bcm2835_gpio_clr_len() and friends after use. 
+  
+  \par Running as root
+
+  Prior to the release of Raspbian Jessie in Feb 2016, access to any
+  peripheral device via /dev/mem on the RPi required the process to
+  run as root. Raspbian Jessie permits non-root users to access the
+  GPIO peripheral (only) via /dev/gpiomem, and this library supports
+  that limited mode of operation.
+
+  If the library runs with effective UID of 0 (ie root), then
+  bcm2835_init() will attempt to open /dev/mem, and, if successful, it
+  will permit use of all peripherals and library functions.
+
+  If the library runs with any other effective UID (ie not root), then
+  bcm2835_init() will attempt to open /dev/gpiomem, and, if
+  successful, will only permit GPIO operations. In particular,
+  bcm2835_spi_begin() and bcm2835_i2c_begin() will return false and all
+  other non-gpio operations may fail silently or crash.
+
+  If your program needs acccess to /dev/mem but not as root, 
+  and if you have the libcap-dev package installed on the target, 
+  you can compile this library to use
+  libcap2 so that it tests whether the exceutable has the cap_sys_rawio capability, and therefore
+  permission to access /dev/mem.
+  To enable this ability, uncomment the #define BCM2835_HAVE_LIBCAP in bcm2835.h or 
+  -DBCM2835_HAVE_LIBCAP on your compiler command line.
+  After your program has been compiled:
+  \code
+  sudo setcap cap_sys_rawio+ep *myprogname*
+  \endcode
+  You also need to do these steps on the host once, to support libcap and not-root read/write access 
+  to /dev/mem:
+  1. Install libcap support
+  \code
+    sudo apt-get install libcap2 libcap-dev
+  2. Add current user to kmem group
+  \code
+    sudo adduser $USER kmem
+  \endcode
+  3. Allow write access to /dev/mem by members of kmem group
+  \code
+    echo 'SUBSYSTEM=="mem", KERNEL=="mem", GROUP="kmem", MODE="0660"' | sudo tee 
/etc/udev/rules.d/98-mem.rules
+  \endcode
+  \code
+    sudo reboot
+  \endcode
+
+  \par Installation
+  
+  This library consists of a single non-shared library and header file, which will be
+  installed in the usual places by make install
+  
+  \code
+  # download the latest version of the library, say bcm2835-1.xx.tar.gz, then:
+  tar zxvf bcm2835-1.xx.tar.gz
+  cd bcm2835-1.xx
+  ./configure
+  make
+  sudo make check
+  sudo make install
+  \endcode
+  
+  \par Physical Addresses
+  
+  The functions bcm2835_peri_read(), bcm2835_peri_write() and bcm2835_peri_set_bits() 
+  are low level peripheral register access functions. They are designed to use
+  physical addresses as described in section 1.2.3 ARM physical addresses
+  of the BCM2835 ARM Peripherals manual. 
+  Physical addresses range from 0x20000000 to 0x20FFFFFF for peripherals. The bus
+  addresses for peripherals are set up to map onto the peripheral bus address range starting at
+  0x7E000000. Thus a peripheral advertised in the manual at bus address 0x7Ennnnnn is available at
+  physical address 0x20nnnnnn.
+  
+  On RPI 2, the peripheral addresses are different and the bcm2835 library gets them 
+  from reading /proc/device-tree/soc/ranges. This is only availble with recent versions of the kernel on RPI 
2.
+  
+  After initialisation, the base address of the various peripheral 
+  registers are available with the following
+  externals:
+  bcm2835_gpio
+  bcm2835_pwm
+  bcm2835_clk
+  bcm2835_pads
+  bcm2835_spio0
+  bcm2835_st
+  bcm2835_bsc0
+  bcm2835_bsc1
+  bcm2835_aux
+  bcm2835_spi1
+
+  \par Raspberry Pi 2 (RPI2)
+
+  For this library to work correctly on RPI2, you MUST have the device tree support enabled in the kernel.
+  You should also ensure you are using the latest version of Linux. The library has been tested on RPI2
+  with 2015-02-16-raspbian-wheezy and ArchLinuxARM-rpi-2 as of 2015-03-29.
+
+  When device tree suport is enabled, the file /proc/device-tree/soc/ranges will appear in the file system, 
+  and the bcm2835 module relies on its presence to correctly run on RPI2 (it is optional for RPI1). 
+  Without device tree support enabled and the presence of this file, it will not work on RPI2.
+
+  To enable device tree support:
+
+  \code
+  sudo raspi-config
+   under Advanced Options - enable Device Tree
+   Reboot.
+  \endcode
+  
+  \par Pin Numbering
+  
+  The GPIO pin numbering as used by RPi is different to and inconsistent with the underlying 
+  BCM 2835 chip pin numbering. http://elinux.org/RPi_BCM2835_GPIOs
+   
+  RPi has a 26 pin IDE header that provides access to some of the GPIO pins on the BCM 2835,
+  as well as power and ground pins. Not all GPIO pins on the BCM 2835 are available on the 
+  IDE header.
+  
+  RPi Version 2 also has a P5 connector with 4 GPIO pins, 5V, 3.3V and Gnd.
+  
+  The functions in this library are designed to be passed the BCM 2835 GPIO pin number and _not_ 
+  the RPi pin number. There are symbolic definitions for each of the available pins
+  that you should use for convenience. See \ref RPiGPIOPin.
+  
+  \par SPI Pins
+   
+  The bcm2835_spi_* functions allow you to control the BCM 2835 SPI0 interface, 
+  allowing you to send and received data by SPI (Serial Peripheral Interface).
+  For more information about SPI, see http://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus
+  
+  When bcm2835_spi_begin() is called it changes the bahaviour of the SPI interface pins from their 
+  default GPIO behaviour in order to support SPI. While SPI is in use, you will not be able 
+  to control the state of the SPI pins through the usual bcm2835_spi_gpio_write().
+  When bcm2835_spi_end() is called, the SPI pins will all revert to inputs, and can then be
+  configured and controled with the usual bcm2835_gpio_* calls.
+  
+  The Raspberry Pi GPIO pins used for SPI are:
+   
+  - P1-19 (MOSI)
+  - P1-21 (MISO) 
+  - P1-23 (CLK) 
+  - P1-24 (CE0) 
+  - P1-26 (CE1)
+
+  Although it is possible to select high speeds for the SPI interface, up to 125MHz (see 
bcm2835_spi_setClockDivider())
+  you should not expect to actually achieve those sorts of speeds with the RPi wiring. Our tests on RPi 2 
show that the
+  SPI CLK line when unloaded has a resonant frequency of about 40MHz, and when loaded, the MOSI and MISO 
lines
+  ring at an even lower frequency. Measurements show that SPI waveforms are very poor and unusable at 62 and 
125MHz.
+  Dont expect any speed faster than 31MHz to work reliably.
+
+  The bcm2835_aux_spi_* functions allow you to control the BCM 2835 SPI1 interface,
+  allowing you to send and received data by SPI (Serial Peripheral Interface).
+
+  The Raspberry Pi GPIO pins used for AUX SPI (SPI1) are:
+
+  - P1-38 (MOSI)
+  - P1-35 (MISO)
+  - P1-40 (CLK)
+  - P1-36 (CE2)
+
+  \par I2C Pins
+  
+  The bcm2835_i2c_* functions allow you to control the BCM 2835 BSC interface,
+  allowing you to send and received data by I2C ("eye-squared cee"; generically referred to as "two-wire 
interface") .
+  For more information about I?C, see http://en.wikipedia.org/wiki/I%C2%B2C
+  
+  The Raspberry Pi V2 GPIO pins used for I2C are:
+  
+  - P1-03 (SDA)
+  - P1-05 (SLC)
+  
+  \par PWM
+  
+  The BCM2835 supports hardware PWM on a limited subset of GPIO pins. This bcm2835 library provides 
+  functions for configuring and controlling PWM output on these pins.
+  
+  The BCM2835 contains 2 independent PWM channels (0 and 1), each of which be connnected to a limited subset 
of 
+  GPIO pins. The following GPIO pins may be connected to the following PWM channels (from section 9.5):
+  \code
+  GPIO PIN    RPi pin  PWM Channel    ALT FUN
+  12                    0            0
+  13                    1            0
+  18         1-12       0            5
+  19                    1            5
+  40                    0            0
+  41                    1            0
+  45                    1            0
+  52                    0            1
+  53                    1            1
+  \endcode
+  In order for a GPIO pin to emit output from its PWM channel, it must be set to the Alt Function given 
above.
+  Note carefully that current versions of the Raspberry Pi only expose one of these pins (GPIO 18 = RPi Pin 
1-12)
+  on the IO headers, and therefore this is the only IO pin on the RPi that can be used for PWM.
+  Further it must be set to ALT FUN 5 to get PWM output.
+  
+  Both PWM channels are driven by the same PWM clock, whose clock dvider can be varied using 
+  bcm2835_pwm_set_clock(). Each channel can be separately enabled with bcm2835_pwm_set_mode().
+  The average output of the PWM channel is determined by the ratio of DATA/RANGE for that channel.
+  Use bcm2835_pwm_set_range() to set the range and bcm2835_pwm_set_data() to set the data in that ratio
+  
+  Each PWM channel can run in either Balanced or Mark-Space mode. In Balanced mode, the hardware 
+  sends a combination of clock pulses that results in an overall DATA pulses per RANGE pulses.
+  In Mark-Space mode, the hardware sets the output HIGH for DATA clock pulses wide, followed by 
+  LOW for RANGE-DATA clock pulses. 
+  
+  The PWM clock can be set to control the PWM pulse widths. The PWM clock is derived from 
+  a 19.2MHz clock. You can set any divider, but some common ones are provided by the 
BCM2835_PWM_CLOCK_DIVIDER_*
+  values of \ref bcm2835PWMClockDivider.
+   
+  For example, say you wanted to drive a DC motor with PWM at about 1kHz, 
+  and control the speed in 1/1024 increments from 
+  0/1024 (stopped) through to 1024/1024 (full on). In that case you might set the 
+  clock divider to be 16, and the RANGE to 1024. The pulse repetition frequency will be
+  1.2MHz/1024 = 1171.875Hz.
+  
+  \par Interactions with other systems
+ 
+  In order for bcm2835 library SPI to work, you may need to disable the SPI kernel module using:
+
+  \code
+  sudo raspi-config
+   under Advanced Options - enable Device Tree
+   under Advanced Options - disable SPI
+   Reboot.
+  \endcode
+
+  Since bcm2835 accesses the lowest level hardware interfaces (in eh intererests of speed and flexibility)
+  there can be intercations with other low level software trying to do similar things.
+
+  It seems that with "latest" 8.0 Jessie 4.9.24-v7+ kernel PWM just won't 
+  work unless you disable audio. There's a line
+  \code
+  dtparam=audio=on
+  \endcode
+  in the /boot/config.txt. 
+  Comment it out like this:
+  \code
+  #dtparam=audio=on
+  \endcode
+
+  \par Real Time performance constraints
+  
+  The bcm2835 is a library for user programs (i.e. they run in 'userland'). 
+  Such programs are not part of the kernel and are usually
+  subject to paging and swapping by the kernel while it does other things besides running your program. 
+  This means that you should not expect to get real-time performance or 
+  real-time timing constraints from such programs. In particular, there is no guarantee that the 
+  bcm2835_delay() and bcm2835_delayMicroseconds() will return after exactly the time requested. 
+  In fact, depending on other activity on the host, IO etc, you might get significantly longer delay times
+  than the one you asked for. So please dont expect to get exactly the time delay you request.
+  
+  Arjan reports that you can prevent swapping on Linux with the following code fragment:
+  
+  \code
+  #define <sched.h>
+  #define <sys/mman.h>
+
+  struct sched_param sp;
+  memset(&sp, 0, sizeof(sp));
+  sp.sched_priority = sched_get_priority_max(SCHED_FIFO);
+  sched_setscheduler(0, SCHED_FIFO, &sp);
+  mlockall(MCL_CURRENT | MCL_FUTURE);
+  \endcode
+  
+  \par Crashing on some versions of Raspbian
+  Some people have reported that various versions of Rasbian will crash or hang 
+  if certain GPIO pins are toggled: https://github.com/raspberrypi/linux/issues/2550
+  when using bcm2835.
+  A workaround is to add this line to your /boot/config.txt:
+  \code
+    dtoverlay=gpio-no-irq
+  \endcode
+
+  \par Bindings to other languages
+  
+  mikem has made Perl bindings available at CPAN:
+  http://search.cpan.org/~mikem/Device-BCM2835-1.9/lib/Device/BCM2835.pm
+  Matthew Baker has kindly made Python bindings available at:
+  https:  github.com/mubeta06/py-libbcm2835
+  Gary Marks has created a Serial Peripheral Interface (SPI) command-line utility 
+  for Raspberry Pi, based on the bcm2835 library. The 
+  utility, spincl, is licensed under Open Source GNU GPLv3 by iP Solutions (http://ipsolutionscorp.com), as 
a 
+  free download with source included: http://ipsolutionscorp.com/raspberry-pi-spi-utility/
+  
+  \par Open Source Licensing GPL V3
+  
+  This is the appropriate option if you want to share the source code of your
+  application with everyone you distribute it to, and you also want to give them
+  the right to share who uses it. If you wish to use this software under Open
+  Source Licensing, you must contribute all your source code to the open source
+  community in accordance with the GPL Version 3 when your application is
+  distributed. See https://www.gnu.org/licenses/gpl-3.0.html and COPYING
+  
+  \par Commercial Licensing
+
+ This is the appropriate option if you are creating proprietary applications
+ and you are not prepared to distribute and share the source code of your
+ application. To purchase a commercial license, contact info airspayce com
+
+  \par Acknowledgements
+  
+  Some of this code has been inspired by Dom and Gert.
+  The I2C code has been inspired by Alan Barr.
+   
+  \par Revision History
+  
+  \version 1.0 Initial release
+
+  \version 1.1 Minor bug fixes
+
+  \version 1.2 Added support for SPI
+
+  \version 1.3 Added bcm2835_spi_transfern()
+
+  \version 1.4 Fixed a problem that prevented SPI CE1 being used. Reported by David Robinson.
+
+  \version 1.5 Added bcm2835_close() to deinit the library. Suggested by C?sar Ortiz
+
+  \version 1.6 Document testing on 2012-07-15-wheezy-raspbian and Occidentalisv01
+  Functions bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen()
+  bcm2835_gpio_len(), bcm2835_gpio_aren() and bcm2835_gpio_afen() now 
+  changes only the pin specified. Other pins that were already previously
+  enabled stay enabled.
+  Added  bcm2835_gpio_clr_ren(), bcm2835_gpio_clr_fen(), bcm2835_gpio_clr_hen()
+  bcm2835_gpio_clr_len(), bcm2835_gpio_clr_aren(), bcm2835_gpio_clr_afen() 
+  to clear the enable for individual pins, suggested by Andreas Sundstrom.
+
+  \version 1.7 Added bcm2835_spi_transfernb to support different buffers for read and write.
+
+  \version 1.8 Improvements to read barrier, as suggested by maddin.
+
+  \version 1.9 Improvements contributed by mikew: 
+  I noticed that it was mallocing memory for the mmaps on /dev/mem.
+  It's not necessary to do that, you can just mmap the file directly,
+  so I've removed the mallocs (and frees).
+  I've also modified delayMicroseconds() to use nanosleep() for long waits,
+  and a busy wait on a high resolution timer for the rest. This is because
+  I've found that calling nanosleep() takes at least 100-200 us.
+  You need to link using '-lrt' using this version.
+  I've added some unsigned casts to the debug prints to silence compiler
+  warnings I was getting, fixed some typos, and changed the value of
+  BCM2835_PAD_HYSTERESIS_ENABLED to 0x08 as per Gert van Loo's doc at
+  http://www.scribd.com/doc/101830961/GPIO-Pads-Control2
+  Also added a define for the passwrd value that Gert says is needed to
+  change pad control settings.
+
+  \version 1.10 Changed the names of the delay functions to bcm2835_delay() 
+  and bcm2835_delayMicroseconds() to prevent collisions with wiringPi.
+  Macros to map delay()-> bcm2835_delay() and
+  Macros to map delayMicroseconds()-> bcm2835_delayMicroseconds(), which
+  can be disabled by defining BCM2835_NO_DELAY_COMPATIBILITY
+
+  \version 1.11 Fixed incorrect link to download file
+
+  \version 1.12 New GPIO pin definitions for RPi version 2 (which has a different GPIO mapping)             
+
+  \version 1.13 New GPIO pin definitions for RPi version 2 plug P5
+  Hardware base pointers are now available (after initialisation) externally as bcm2835_gpio
+  bcm2835_pwm bcm2835_clk bcm2835_pads bcm2835_spi0.
+
+  \version 1.14 Now compiles even if CLOCK_MONOTONIC_RAW is not available, uses CLOCK_MONOTONIC instead.
+  Fixed errors in documentation of SPI divider frequencies based on 250MHz clock. 
+  Reported by Ben Simpson.
+
+  \version 1.15 Added bcm2835_close() to end of examples as suggested by Mark Wolfe.
+
+  \version 1.16 Added bcm2835_gpio_set_multi, bcm2835_gpio_clr_multi and bcm2835_gpio_write_multi
+  to allow a mask of pins to be set all at once. Requested by Sebastian Loncar.
+
+  \version 1.17  Added bcm2835_gpio_write_mask. Requested by Sebastian Loncar.
+
+  \version 1.18 Added bcm2835_i2c_* functions. Changes to bcm2835_delayMicroseconds: 
+  now uses the RPi system timer counter, instead of clock_gettime, for improved accuracy. 
+  No need to link with -lrt now. Contributed by Arjan van Vught.
+  \version 1.19 Removed inlines added by previous patch since they don't seem to work everywhere. 
+  Reported by olly.
+
+  \version 1.20 Patch from Mark Dootson to close /dev/mem after access to the peripherals has been granted.
+
+  \version 1.21 delayMicroseconds is now not susceptible to 32 bit timer overruns. 
+  Patch courtesy Jeremy Mortis.
+
+  \version 1.22 Fixed incorrect definition of BCM2835_GPFEN0 which broke the ability to set 
+  falling edge events. Reported by Mark Dootson.
+
+  \version 1.23 Added bcm2835_i2c_set_baudrate and bcm2835_i2c_read_register_rs. 
+  Improvements to bcm2835_i2c_read and bcm2835_i2c_write functions
+  to fix ocasional reads not completing. Patched by Mark Dootson.
+
+  \version 1.24 Mark Dootson p[atched a problem with his previously submitted code
+  under high load from other processes. 
+
+  \version 1.25 Updated author and distribution location details to airspayce.com
+
+  \version 1.26 Added missing unmapmem for pads in bcm2835_close to prevent a memory leak. 
+  Reported by Hartmut Henkel.
+
+  \version 1.27 bcm2835_gpio_set_pad() no longer needs BCM2835_PAD_PASSWRD: it is
+  now automatically included.
+  Added support for PWM mode with bcm2835_pwm_* functions.
+
+  \version 1.28 Fixed a problem where bcm2835_spi_writenb() would have problems with transfers of more than
+  64 bytes dues to read buffer filling. Patched by Peter Würtz.
+
+  \version 1.29 Further fix to SPI from Peter Würtz.
+
+  \version 1.30 10 microsecond delays from bcm2835_spi_transfer and bcm2835_spi_transfern for
+  significant performance improvements, Patch by Alan Watson.
+
+  \version 1.31 Fix a GCC warning about dummy variable, patched by Alan Watson. Thanks.
+
+  \version 1.32 Added option I2C_V1 definition to compile for version 1 RPi. 
+  By default I2C code is generated for the V2 RPi which has SDA1 and SCL1 connected.
+  Contributed by Malcolm Wiles based on work by Arvi Govindaraj.
+
+  \version 1.33 Added command line utilities i2c and gpio to examples. Contributed by Shahrooz Shahparnia.
+
+  \version 1.34 Added bcm2835_i2c_write_read_rs() which writes an arbitrary number of bytes, 
+  sends a repeat start, and reads from the device. Contributed by Eduardo Steinhorst.
+
+  \version 1.35 Fix build errors when compiled under Qt. Also performance improvements with SPI transfers. 
Contributed b Udo Klaas.
+
+  \version 1.36 Make automake's test runner detect that we're skipping tests when not root, the second
+  one makes us skip the test when using fakeroot (as used when building
+  Debian packages). Contributed by Guido Günther.
+
+  \version 1.37 Moved confiure.in to configure.ac as receommnded by autoreconf.<br>
+  Improvements to bcm2835_st_read to account for possible timer overflow, contributed by 'Ed'.<br>
+  Added definitions for Raspberry Pi B+ J8 header GPIO pins.<br>
+
+  \version 1.38 Added bcm2835_regbase for the benefit of C# wrappers, patch by Frank Hommers <br>
+
+  \version 1.39 Beta version of RPi2 compatibility. Not tested here on RPi2 hardware. 
+  Testers please confirm correct operation on RPi2.<br>
+  Unnecessary 'volatile' qualifiers removed from all variables and signatures.<br>
+  Removed unsupportable PWM dividers, based on a report from Christophe Cecillon.<br>
+  Minor improvements to spi.c example.<br>
+
+  \version 1.40 Correct operation on RPi2 has been confirmed.<br>
+  Fixed a number of compiler errors and warnings that occur when bcm2835.h is included
+  in code compiled with -Wall -Woverflow -Wstrict-overflow -Wshadow -Wextra -pedantic.
+  Reported by tlhackque.<br>
+  Fixed a problem where calling bcm2835_delayMicroseconds loops forever when debug is set. Reported by 
tlhackque.<br>
+  Reinstated use of volatile in 2 functions where there was a danger of lost reads or writes. Reported by 
tlhackque.<br>
+  
+  \version 1.41 Added BCM2835_VERSION macro and new function bcm2835_version(); Requested by tlhackque.<br>
+  Improvements to peripheral memory barriers as suggested by tlhackque.<br>
+  Reinstated some necessary volatile declarations as requested by tlhackque.<br>
+
+  \version 1.42 Further improvements to memory barriers with the patient assistance and patches of 
tlhackque.<br>
+
+  \version 1.43 Fixed problems with compiling barriers on RPI 2 with Arch Linux and gcc 4.9.2. 
+  Reported and patched by Lars Christensen.<br>
+  Testing on RPI 2, with ArchLinuxARM-rpi-2-latest and 2015-02-16-raspbian-wheezy.<br>
+
+  \version 1.44 Added documention about the need for device tree to be enabled on RPI2.<br>
+  Improvements to detection of availability of DMB instruction based on value of __ARM_ARCH macro.<br>
+
+  \version 1.45 Fixed an error in the pad group offsets that would prevent bcm2835_gpio_set_pad() 
+  and bcm2835_gpio_pad() working correctly with non-0 pad groups. Reported by Guido.
+
+  \version 1.46 2015-09-18
+  Added symbolic definitions for remaining pins on 40 pin GPIO header on RPi 2. <br>
+
+  \version 1.47 2015-11-18
+  Fixed possibly incorrect reads in bcm2835_i2c_read_register_rs, patch from Eckhardt Ulrich.<br>
+
+  \version 1.48 2015-12-08
+  Added patch from Eckhardt Ulrich that fixed problems that could cause hanging with 
bcm2835_i2c_read_register_rs
+  and others.
+
+  \version 1.49 2016-01-05
+  Added patch from Jonathan Perkin with new functions bcm2835_gpio_eds_multi() and 
bcm2835_gpio_set_eds_multi().
+
+  \version 1.50 2016-02-28
+  Added support for running as non-root, permitting access to GPIO only. Functions
+  bcm2835_spi_begin() and bcm2835_i2c_begin() will now return 0 if not running as root 
+  (which prevents access to the SPI and I2C peripherals, amongst others). 
+  Testing on Raspbian Jessie.
+
+  \version 1.51 2016-11-03
+  Added documentation about SPI clock divider and resulting SPI speeds on RPi3.
+  Fixed a problem where seg fault could occur in bcm2835_delayMicroseconds() if not running as root. Patch 
from Pok.
+
+  \version 1.52 2017-02-03
+  Added link to commercial license purchasing.
+
+  \version 1.53 2018-01-14
+  Added support for AUX SPI (SPI1)
+  Contributed by Arjan van Vught (http://www.raspberrypi-dmx.org/)
+
+  \version 1.54 2018-01-17
+  Fixed compile errors in new AUX spi code under some circumstances.
+
+  \version 1.55 2018-01-20
+  Fixed version numbers.
+  Fixed some warnings.
+
+  \version 1.56 2018-06-10
+  Supports bcm2835_spi_setBitOrder(BCM2835_SPI_BIT_ORDER_LSBFIRST), after which SPI bytes are reversed on 
read or write.
+  Based on a suggestion by Damiano Benedetti.
+  
+  \version 1.57 2018-08-28
+  Added SPI function bcm2835_spi_set_speed_hz(uint32_t speed_hz);
+  Contributed by Arjan van Vught (http://www.raspberrypi-dmx.org/)
+
+  \version 1.58 2018-11-29
+  Added examples/spiram, which shows how to use the included little library (spiram.c and spiram.h)
+  to read and write SPI RAM chips such as 23K256-I/P
+
+  \version 1.59 2019-05-22
+  Fixed a bug in bcm2835_i2c_read reported by Charles Hayward where a noisy I2C line cold cause a seg fault 
by
+  reading too many characters.
+  
+  \version 1.60 2019-07-23
+  Applied patch from Mark Dootson for RPi 4 compatibility. Thanks Mark. Not tested here on RPi4, but others 
report it works.
+  Tested as still working correctly on earlier RPi models. Tested with Debian Buster on earlier models
+
+  \version 1.61 2020-01-11
+  Fixed errors in the documentation for bcm2835_spi_write.
+  Fixes issue seen on Raspberry Pi 4 boards where 64-bit off_t is used by
+  default via -D_LARGEFILE_SOURCE -D_FILE_OFFSET_BITS=64.  The offset was
+  being incorrectly converted, this way is clearer and fixes the problem. Contributed by Jonathan Perkin.
+
+  \version 1.62 2020-01-12
+  Fixed a problem that could cause compile failures with size_t and off_t
+
+  \version 1.63 2020-03-07
+  Added bcm2835_aux_spi_transfer, contributed by Michivi
+  Adopted GPL V3 licensing
+
+  \version 1.64 2020-04-11
+  Fixed error in definitions of BCM2835_AUX_SPI_STAT_TX_LVL and BCM2835_AUX_SPI_STAT_RX_LVL. Patch from 
+  Eric Marzec. Thanks.
+
+  \version 1.65, 1.66 2020-04-16
+  Added support for use of capability  cap_sys_rawio to determine if access to /dev/mem is available for 
non-root
+  users. Contributed by Doug McFadyen.
+
+  \version 1.67, 1.66 2020-06-11
+  Fixed an error in bcm2835_i2c_read() where the status byte was not correctly updated with 
BCM2835_BSC_S_DONE
+  Reported by Zihan. Thanks.
+
+  \author  Mike McCauley (mikem airspayce com) DO NOT CONTACT THE AUTHOR DIRECTLY: USE THE LISTS
+*/
+
+
+/* Defines for BCM2835 */
+#ifndef BCM2835_H
+#define BCM2835_H
+
+#include <stdint.h>
+
+#define BCM2835_VERSION 10066 /* Version 1.66 */
+
+// Define this if you want to use libcap2 to determine if you have the cap_sys_rawio capability
+// and therefore the capability of opening /dev/mem, even if you are not root.
+// See the comments above in the documentation for 'Running As Root'
+//#define BCM2835_HAVE_LIBCAP
+
+/* RPi 2 is ARM v7, and has DMB instruction for memory barriers.
+   Older RPis are ARM v6 and don't, so a coprocessor instruction must be used instead.
+   However, not all versions of gcc in all distros support the dmb assembler instruction even on compatible 
processors.
+   This test is so any ARMv7 or higher processors with suitable GCC will use DMB.
+*/
+#if __ARM_ARCH >= 7
+#define BCM2835_HAVE_DMB
+#endif
+
+/*! \defgroup constants Constants for passing to and from library functions
+  The values here are designed to be passed to various functions in the bcm2835 library.
+  @{
+*/
+
+/*! This means pin HIGH, true, 3.3volts on a pin. */
+#define HIGH 0x1
+/*! This means pin LOW, false, 0volts on a pin. */
+#define LOW  0x0
+
+/*! Return the minimum of 2 numbers */
+#ifndef MIN
+#define MIN(a, b) (a < b ? a : b)
+#endif
+
+/*! Speed of the core clock core_clk */
+#define BCM2835_CORE_CLK_HZ            250000000       /*!< 250 MHz */
+
+/*! On all recent OSs, the base of the peripherals is read from a /proc file */
+#define BMC2835_RPI2_DT_FILENAME "/proc/device-tree/soc/ranges"
+
+/*! Physical addresses for various peripheral register sets
+  Base Physical Address of the BCM 2835 peripheral registers
+  Note this is different for the RPi2 BCM2836, where this is derived from /proc/device-tree/soc/ranges
+  If /proc/device-tree/soc/ranges exists on a RPi 1 OS, it would be expected to contain the
+  following numbers:
+*/
+/*! Peripherals block base address on RPi 1 */
+#define BCM2835_PERI_BASE               0x20000000
+/*! Size of the peripherals block on RPi 1 */
+#define BCM2835_PERI_SIZE               0x01000000
+/*! Alternate base address for RPI  2 / 3 */
+#define BCM2835_RPI2_PERI_BASE          0x3F000000
+/*! Alternate base address for RPI  4 */
+#define BCM2835_RPI4_PERI_BASE          0xFE000000
+/*! Alternate size for RPI  4 */
+#define BCM2835_RPI4_PERI_SIZE          0x01800000
+
+/*! Offsets for the bases of various peripherals within the peripherals block
+  /   Base Address of the System Timer registers
+*/
+#define BCM2835_ST_BASE                                        0x3000
+/*! Base Address of the Pads registers */
+#define BCM2835_GPIO_PADS               0x100000
+/*! Base Address of the Clock/timer registers */
+#define BCM2835_CLOCK_BASE              0x101000
+/*! Base Address of the GPIO registers */
+#define BCM2835_GPIO_BASE               0x200000
+/*! Base Address of the SPI0 registers */
+#define BCM2835_SPI0_BASE               0x204000
+/*! Base Address of the BSC0 registers */
+#define BCM2835_BSC0_BASE                              0x205000
+/*! Base Address of the PWM registers */
+#define BCM2835_GPIO_PWM                0x20C000
+/*! Base Address of the AUX registers */
+#define BCM2835_AUX_BASE                               0x215000
+/*! Base Address of the AUX_SPI1 registers */
+#define BCM2835_SPI1_BASE                              0x215080
+/*! Base Address of the AUX_SPI2 registers */
+#define BCM2835_SPI2_BASE                              0x2150C0
+/*! Base Address of the BSC1 registers */
+#define BCM2835_BSC1_BASE                              0x804000
+
+#include <stdlib.h>
+
+/*! Physical address and size of the peripherals block
+  May be overridden on RPi2
+*/
+extern off_t bcm2835_peripherals_base;
+/*! Size of the peripherals block to be mapped */
+extern size_t bcm2835_peripherals_size;
+
+/*! Virtual memory address of the mapped peripherals block */
+extern uint32_t *bcm2835_peripherals;
+
+/*! Base of the ST (System Timer) registers.
+  Available after bcm2835_init has been called (as root)
+*/
+extern volatile uint32_t *bcm2835_st;
+
+/*! Base of the GPIO registers.
+  Available after bcm2835_init has been called
+*/
+extern volatile uint32_t *bcm2835_gpio;
+
+/*! Base of the PWM registers.
+  Available after bcm2835_init has been called (as root)
+*/
+extern volatile uint32_t *bcm2835_pwm;
+
+/*! Base of the CLK registers.
+  Available after bcm2835_init has been called (as root)
+*/
+extern volatile uint32_t *bcm2835_clk;
+
+/*! Base of the PADS registers.
+  Available after bcm2835_init has been called (as root)
+*/
+extern volatile uint32_t *bcm2835_pads;
+
+/*! Base of the SPI0 registers.
+  Available after bcm2835_init has been called (as root)
+*/
+extern volatile uint32_t *bcm2835_spi0;
+
+/*! Base of the BSC0 registers.
+  Available after bcm2835_init has been called (as root)
+*/
+extern volatile uint32_t *bcm2835_bsc0;
+
+/*! Base of the BSC1 registers.
+  Available after bcm2835_init has been called (as root)
+*/
+extern volatile uint32_t *bcm2835_bsc1;
+
+/*! Base of the AUX registers.
+  Available after bcm2835_init has been called (as root)
+*/
+extern volatile uint32_t *bcm2835_aux;
+
+/*! Base of the SPI1 registers.
+  Available after bcm2835_init has been called (as root)
+*/
+extern volatile uint32_t *bcm2835_spi1;
+
+
+/*! \brief bcm2835RegisterBase
+  Register bases for bcm2835_regbase()
+*/
+typedef enum
+{
+    BCM2835_REGBASE_ST   = 1, /*!< Base of the ST (System Timer) registers. */
+    BCM2835_REGBASE_GPIO = 2, /*!< Base of the GPIO registers. */
+    BCM2835_REGBASE_PWM  = 3, /*!< Base of the PWM registers. */
+    BCM2835_REGBASE_CLK  = 4, /*!< Base of the CLK registers. */
+    BCM2835_REGBASE_PADS = 5, /*!< Base of the PADS registers. */
+    BCM2835_REGBASE_SPI0 = 6, /*!< Base of the SPI0 registers. */
+    BCM2835_REGBASE_BSC0 = 7, /*!< Base of the BSC0 registers. */
+    BCM2835_REGBASE_BSC1 = 8,  /*!< Base of the BSC1 registers. */
+       BCM2835_REGBASE_AUX  = 9,  /*!< Base of the AUX registers. */
+       BCM2835_REGBASE_SPI1 = 10  /*!< Base of the SPI1 registers. */
+} bcm2835RegisterBase;
+
+/*! Size of memory page on RPi */
+#define BCM2835_PAGE_SIZE               (4*1024)
+/*! Size of memory block on RPi */
+#define BCM2835_BLOCK_SIZE              (4*1024)
+
+
+/* Defines for GPIO
+   The BCM2835 has 54 GPIO pins.
+   BCM2835 data sheet, Page 90 onwards.
+*/
+/*! GPIO register offsets from BCM2835_GPIO_BASE. 
+  Offsets into the GPIO Peripheral block in bytes per 6.1 Register View 
+*/
+#define BCM2835_GPFSEL0                      0x0000 /*!< GPIO Function Select 0 */
+#define BCM2835_GPFSEL1                      0x0004 /*!< GPIO Function Select 1 */
+#define BCM2835_GPFSEL2                      0x0008 /*!< GPIO Function Select 2 */
+#define BCM2835_GPFSEL3                      0x000c /*!< GPIO Function Select 3 */
+#define BCM2835_GPFSEL4                      0x0010 /*!< GPIO Function Select 4 */
+#define BCM2835_GPFSEL5                      0x0014 /*!< GPIO Function Select 5 */
+#define BCM2835_GPSET0                       0x001c /*!< GPIO Pin Output Set 0 */
+#define BCM2835_GPSET1                       0x0020 /*!< GPIO Pin Output Set 1 */
+#define BCM2835_GPCLR0                       0x0028 /*!< GPIO Pin Output Clear 0 */
+#define BCM2835_GPCLR1                       0x002c /*!< GPIO Pin Output Clear 1 */
+#define BCM2835_GPLEV0                       0x0034 /*!< GPIO Pin Level 0 */
+#define BCM2835_GPLEV1                       0x0038 /*!< GPIO Pin Level 1 */
+#define BCM2835_GPEDS0                       0x0040 /*!< GPIO Pin Event Detect Status 0 */
+#define BCM2835_GPEDS1                       0x0044 /*!< GPIO Pin Event Detect Status 1 */
+#define BCM2835_GPREN0                       0x004c /*!< GPIO Pin Rising Edge Detect Enable 0 */
+#define BCM2835_GPREN1                       0x0050 /*!< GPIO Pin Rising Edge Detect Enable 1 */
+#define BCM2835_GPFEN0                       0x0058 /*!< GPIO Pin Falling Edge Detect Enable 0 */
+#define BCM2835_GPFEN1                       0x005c /*!< GPIO Pin Falling Edge Detect Enable 1 */
+#define BCM2835_GPHEN0                       0x0064 /*!< GPIO Pin High Detect Enable 0 */
+#define BCM2835_GPHEN1                       0x0068 /*!< GPIO Pin High Detect Enable 1 */
+#define BCM2835_GPLEN0                       0x0070 /*!< GPIO Pin Low Detect Enable 0 */
+#define BCM2835_GPLEN1                       0x0074 /*!< GPIO Pin Low Detect Enable 1 */
+#define BCM2835_GPAREN0                      0x007c /*!< GPIO Pin Async. Rising Edge Detect 0 */
+#define BCM2835_GPAREN1                      0x0080 /*!< GPIO Pin Async. Rising Edge Detect 1 */
+#define BCM2835_GPAFEN0                      0x0088 /*!< GPIO Pin Async. Falling Edge Detect 0 */
+#define BCM2835_GPAFEN1                      0x008c /*!< GPIO Pin Async. Falling Edge Detect 1 */
+#define BCM2835_GPPUD                        0x0094 /*!< GPIO Pin Pull-up/down Enable */
+#define BCM2835_GPPUDCLK0                    0x0098 /*!< GPIO Pin Pull-up/down Enable Clock 0 */
+#define BCM2835_GPPUDCLK1                    0x009c /*!< GPIO Pin Pull-up/down Enable Clock 1 */
+
+/* 2711 has a different method for pin pull-up/down/enable  */
+#define BCM2835_GPPUPPDN0                    0x00e4 /* Pin pull-up/down for pins 15:0  */
+#define BCM2835_GPPUPPDN1                    0x00e8 /* Pin pull-up/down for pins 31:16 */
+#define BCM2835_GPPUPPDN2                    0x00ec /* Pin pull-up/down for pins 47:32 */
+#define BCM2835_GPPUPPDN3                    0x00f0 /* Pin pull-up/down for pins 57:48 */
+
+/*!   \brief bcm2835PortFunction
+  Port function select modes for bcm2835_gpio_fsel()
+*/
+typedef enum
+{
+    BCM2835_GPIO_FSEL_INPT  = 0x00,   /*!< Input 0b000 */
+    BCM2835_GPIO_FSEL_OUTP  = 0x01,   /*!< Output 0b001 */
+    BCM2835_GPIO_FSEL_ALT0  = 0x04,   /*!< Alternate function 0 0b100 */
+    BCM2835_GPIO_FSEL_ALT1  = 0x05,   /*!< Alternate function 1 0b101 */
+    BCM2835_GPIO_FSEL_ALT2  = 0x06,   /*!< Alternate function 2 0b110, */
+    BCM2835_GPIO_FSEL_ALT3  = 0x07,   /*!< Alternate function 3 0b111 */
+    BCM2835_GPIO_FSEL_ALT4  = 0x03,   /*!< Alternate function 4 0b011 */
+    BCM2835_GPIO_FSEL_ALT5  = 0x02,   /*!< Alternate function 5 0b010 */
+    BCM2835_GPIO_FSEL_MASK  = 0x07    /*!< Function select bits mask 0b111 */
+} bcm2835FunctionSelect;
+
+/*! \brief bcm2835PUDControl
+  Pullup/Pulldown defines for bcm2835_gpio_pud()
+*/
+typedef enum
+{
+    BCM2835_GPIO_PUD_OFF     = 0x00,   /*!< Off ? disable pull-up/down 0b00 */
+    BCM2835_GPIO_PUD_DOWN    = 0x01,   /*!< Enable Pull Down control 0b01 */
+    BCM2835_GPIO_PUD_UP      = 0x02    /*!< Enable Pull Up control 0b10  */
+} bcm2835PUDControl;
+
+/* need a value for pud functions that can't work unless RPI 4 */
+#define BCM2835_GPIO_PUD_ERROR  0x08 
+
+/*! Pad control register offsets from BCM2835_GPIO_PADS */
+#define BCM2835_PADS_GPIO_0_27               0x002c /*!< Pad control register for pads 0 to 27 */
+#define BCM2835_PADS_GPIO_28_45              0x0030 /*!< Pad control register for pads 28 to 45 */
+#define BCM2835_PADS_GPIO_46_53              0x0034 /*!< Pad control register for pads 46 to 53 */
+
+/*! Pad Control masks */
+#define BCM2835_PAD_PASSWRD                  (0x5A << 24)  /*!< Password to enable setting pad mask */
+#define BCM2835_PAD_SLEW_RATE_UNLIMITED      0x10 /*!< Slew rate unlimited */
+#define BCM2835_PAD_HYSTERESIS_ENABLED       0x08 /*!< Hysteresis enabled */
+#define BCM2835_PAD_DRIVE_2mA                0x00 /*!< 2mA drive current */
+#define BCM2835_PAD_DRIVE_4mA                0x01 /*!< 4mA drive current */
+#define BCM2835_PAD_DRIVE_6mA                0x02 /*!< 6mA drive current */
+#define BCM2835_PAD_DRIVE_8mA                0x03 /*!< 8mA drive current */
+#define BCM2835_PAD_DRIVE_10mA               0x04 /*!< 10mA drive current */
+#define BCM2835_PAD_DRIVE_12mA               0x05 /*!< 12mA drive current */
+#define BCM2835_PAD_DRIVE_14mA               0x06 /*!< 14mA drive current */
+#define BCM2835_PAD_DRIVE_16mA               0x07 /*!< 16mA drive current */
+
+/*! \brief bcm2835PadGroup
+  Pad group specification for bcm2835_gpio_pad()
+*/
+typedef enum
+{
+    BCM2835_PAD_GROUP_GPIO_0_27         = 0, /*!< Pad group for GPIO pads 0 to 27 */
+    BCM2835_PAD_GROUP_GPIO_28_45        = 1, /*!< Pad group for GPIO pads 28 to 45 */
+    BCM2835_PAD_GROUP_GPIO_46_53        = 2  /*!< Pad group for GPIO pads 46 to 53 */
+} bcm2835PadGroup;
+
+/*! \brief GPIO Pin Numbers
+  
+  Here we define Raspberry Pin GPIO pins on P1 in terms of the underlying BCM GPIO pin numbers.
+  These can be passed as a pin number to any function requiring a pin.
+  Not all pins on the RPi 26 bin IDE plug are connected to GPIO pins
+  and some can adopt an alternate function.
+  RPi version 2 has some slightly different pinouts, and these are values RPI_V2_*.
+  RPi B+ has yet differnet pinouts and these are defined in RPI_BPLUS_*.
+  At bootup, pins 8 and 10 are set to UART0_TXD, UART0_RXD (ie the alt0 function) respectively
+  When SPI0 is in use (ie after bcm2835_spi_begin()), SPI0 pins are dedicated to SPI
+  and cant be controlled independently.
+  If you are using the RPi Compute Module, just use the GPIO number: there is no need to use one of these
+  symbolic names
+*/
+typedef enum
+{
+    RPI_GPIO_P1_03        =  0,  /*!< Version 1, Pin P1-03 */
+    RPI_GPIO_P1_05        =  1,  /*!< Version 1, Pin P1-05 */
+    RPI_GPIO_P1_07        =  4,  /*!< Version 1, Pin P1-07 */
+    RPI_GPIO_P1_08        = 14,  /*!< Version 1, Pin P1-08, defaults to alt function 0 UART0_TXD */
+    RPI_GPIO_P1_10        = 15,  /*!< Version 1, Pin P1-10, defaults to alt function 0 UART0_RXD */
+    RPI_GPIO_P1_11        = 17,  /*!< Version 1, Pin P1-11 */
+    RPI_GPIO_P1_12        = 18,  /*!< Version 1, Pin P1-12, can be PWM channel 0 in ALT FUN 5 */
+    RPI_GPIO_P1_13        = 21,  /*!< Version 1, Pin P1-13 */
+    RPI_GPIO_P1_15        = 22,  /*!< Version 1, Pin P1-15 */
+    RPI_GPIO_P1_16        = 23,  /*!< Version 1, Pin P1-16 */
+    RPI_GPIO_P1_18        = 24,  /*!< Version 1, Pin P1-18 */
+    RPI_GPIO_P1_19        = 10,  /*!< Version 1, Pin P1-19, MOSI when SPI0 in use */
+    RPI_GPIO_P1_21        =  9,  /*!< Version 1, Pin P1-21, MISO when SPI0 in use */
+    RPI_GPIO_P1_22        = 25,  /*!< Version 1, Pin P1-22 */
+    RPI_GPIO_P1_23        = 11,  /*!< Version 1, Pin P1-23, CLK when SPI0 in use */
+    RPI_GPIO_P1_24        =  8,  /*!< Version 1, Pin P1-24, CE0 when SPI0 in use */
+    RPI_GPIO_P1_26        =  7,  /*!< Version 1, Pin P1-26, CE1 when SPI0 in use */
+
+    /* RPi Version 2 */
+    RPI_V2_GPIO_P1_03     =  2,  /*!< Version 2, Pin P1-03 */
+    RPI_V2_GPIO_P1_05     =  3,  /*!< Version 2, Pin P1-05 */
+    RPI_V2_GPIO_P1_07     =  4,  /*!< Version 2, Pin P1-07 */
+    RPI_V2_GPIO_P1_08     = 14,  /*!< Version 2, Pin P1-08, defaults to alt function 0 UART0_TXD */
+    RPI_V2_GPIO_P1_10     = 15,  /*!< Version 2, Pin P1-10, defaults to alt function 0 UART0_RXD */
+    RPI_V2_GPIO_P1_11     = 17,  /*!< Version 2, Pin P1-11 */
+    RPI_V2_GPIO_P1_12     = 18,  /*!< Version 2, Pin P1-12, can be PWM channel 0 in ALT FUN 5 */
+    RPI_V2_GPIO_P1_13     = 27,  /*!< Version 2, Pin P1-13 */
+    RPI_V2_GPIO_P1_15     = 22,  /*!< Version 2, Pin P1-15 */
+    RPI_V2_GPIO_P1_16     = 23,  /*!< Version 2, Pin P1-16 */
+    RPI_V2_GPIO_P1_18     = 24,  /*!< Version 2, Pin P1-18 */
+    RPI_V2_GPIO_P1_19     = 10,  /*!< Version 2, Pin P1-19, MOSI when SPI0 in use */
+    RPI_V2_GPIO_P1_21     =  9,  /*!< Version 2, Pin P1-21, MISO when SPI0 in use */
+    RPI_V2_GPIO_P1_22     = 25,  /*!< Version 2, Pin P1-22 */
+    RPI_V2_GPIO_P1_23     = 11,  /*!< Version 2, Pin P1-23, CLK when SPI0 in use */
+    RPI_V2_GPIO_P1_24     =  8,  /*!< Version 2, Pin P1-24, CE0 when SPI0 in use */
+    RPI_V2_GPIO_P1_26     =  7,  /*!< Version 2, Pin P1-26, CE1 when SPI0 in use */
+    RPI_V2_GPIO_P1_29     =  5,  /*!< Version 2, Pin P1-29 */
+    RPI_V2_GPIO_P1_31     =  6,  /*!< Version 2, Pin P1-31 */
+    RPI_V2_GPIO_P1_32     = 12,  /*!< Version 2, Pin P1-32 */
+    RPI_V2_GPIO_P1_33     = 13,  /*!< Version 2, Pin P1-33 */
+    RPI_V2_GPIO_P1_35     = 19,  /*!< Version 2, Pin P1-35, can be PWM channel 1 in ALT FUN 5  */
+    RPI_V2_GPIO_P1_36     = 16,  /*!< Version 2, Pin P1-36 */
+    RPI_V2_GPIO_P1_37     = 26,  /*!< Version 2, Pin P1-37 */
+    RPI_V2_GPIO_P1_38     = 20,  /*!< Version 2, Pin P1-38 */
+    RPI_V2_GPIO_P1_40     = 21,  /*!< Version 2, Pin P1-40 */
+
+    /* RPi Version 2, new plug P5 */
+    RPI_V2_GPIO_P5_03     = 28,  /*!< Version 2, Pin P5-03 */
+    RPI_V2_GPIO_P5_04     = 29,  /*!< Version 2, Pin P5-04 */
+    RPI_V2_GPIO_P5_05     = 30,  /*!< Version 2, Pin P5-05 */
+    RPI_V2_GPIO_P5_06     = 31,  /*!< Version 2, Pin P5-06 */
+
+    /* RPi B+ J8 header, also RPi 2 40 pin GPIO header */
+    RPI_BPLUS_GPIO_J8_03     =  2,  /*!< B+, Pin J8-03 */
+    RPI_BPLUS_GPIO_J8_05     =  3,  /*!< B+, Pin J8-05 */
+    RPI_BPLUS_GPIO_J8_07     =  4,  /*!< B+, Pin J8-07 */
+    RPI_BPLUS_GPIO_J8_08     = 14,  /*!< B+, Pin J8-08, defaults to alt function 0 UART0_TXD */
+    RPI_BPLUS_GPIO_J8_10     = 15,  /*!< B+, Pin J8-10, defaults to alt function 0 UART0_RXD */
+    RPI_BPLUS_GPIO_J8_11     = 17,  /*!< B+, Pin J8-11 */
+    RPI_BPLUS_GPIO_J8_12     = 18,  /*!< B+, Pin J8-12, can be PWM channel 0 in ALT FUN 5 */
+    RPI_BPLUS_GPIO_J8_13     = 27,  /*!< B+, Pin J8-13 */
+    RPI_BPLUS_GPIO_J8_15     = 22,  /*!< B+, Pin J8-15 */
+    RPI_BPLUS_GPIO_J8_16     = 23,  /*!< B+, Pin J8-16 */
+    RPI_BPLUS_GPIO_J8_18     = 24,  /*!< B+, Pin J8-18 */
+    RPI_BPLUS_GPIO_J8_19     = 10,  /*!< B+, Pin J8-19, MOSI when SPI0 in use */
+    RPI_BPLUS_GPIO_J8_21     =  9,  /*!< B+, Pin J8-21, MISO when SPI0 in use */
+    RPI_BPLUS_GPIO_J8_22     = 25,  /*!< B+, Pin J8-22 */
+    RPI_BPLUS_GPIO_J8_23     = 11,  /*!< B+, Pin J8-23, CLK when SPI0 in use */
+    RPI_BPLUS_GPIO_J8_24     =  8,  /*!< B+, Pin J8-24, CE0 when SPI0 in use */
+    RPI_BPLUS_GPIO_J8_26     =  7,  /*!< B+, Pin J8-26, CE1 when SPI0 in use */
+    RPI_BPLUS_GPIO_J8_29     =  5,  /*!< B+, Pin J8-29,  */
+    RPI_BPLUS_GPIO_J8_31     =  6,  /*!< B+, Pin J8-31,  */
+    RPI_BPLUS_GPIO_J8_32     = 12,  /*!< B+, Pin J8-32,  */
+    RPI_BPLUS_GPIO_J8_33     = 13,  /*!< B+, Pin J8-33,  */
+    RPI_BPLUS_GPIO_J8_35     = 19,  /*!< B+, Pin J8-35, can be PWM channel 1 in ALT FUN 5 */
+    RPI_BPLUS_GPIO_J8_36     = 16,  /*!< B+, Pin J8-36,  */
+    RPI_BPLUS_GPIO_J8_37     = 26,  /*!< B+, Pin J8-37,  */
+    RPI_BPLUS_GPIO_J8_38     = 20,  /*!< B+, Pin J8-38,  */
+    RPI_BPLUS_GPIO_J8_40     = 21   /*!< B+, Pin J8-40,  */
+} RPiGPIOPin;
+
+/* Defines for AUX
+  GPIO register offsets from BCM2835_AUX_BASE.
+*/
+#define BCM2835_AUX_IRQ                        0x0000  /*!< xxx */
+#define BCM2835_AUX_ENABLE             0x0004  /*!< */
+
+#define BCM2835_AUX_ENABLE_UART1       0x01    /*!<  */
+#define BCM2835_AUX_ENABLE_SPI0                0x02    /*!< SPI0 (SPI1 in the device) */
+#define BCM2835_AUX_ENABLE_SPI1                0x04    /*!< SPI1 (SPI2 in the device) */
+
+
+#define BCM2835_AUX_SPI_CNTL0          0x0000  /*!< */
+#define BCM2835_AUX_SPI_CNTL1          0x0004  /*!< */
+#define BCM2835_AUX_SPI_STAT           0x0008  /*!< */
+#define BCM2835_AUX_SPI_PEEK           0x000C  /*!< Read but do not take from FF */
+#define BCM2835_AUX_SPI_IO             0x0020  /*!< Write = TX, read=RX */
+#define BCM2835_AUX_SPI_TXHOLD         0x0030  /*!< Write = TX keep CS, read=RX */
+
+#define BCM2835_AUX_SPI_CLOCK_MIN      30500           /*!< 30,5kHz */
+#define BCM2835_AUX_SPI_CLOCK_MAX      125000000       /*!< 125Mhz */
+
+#define BCM2835_AUX_SPI_CNTL0_SPEED    0xFFF00000  /*!< */
+#define BCM2835_AUX_SPI_CNTL0_SPEED_MAX        0xFFF      /*!< */
+#define BCM2835_AUX_SPI_CNTL0_SPEED_SHIFT 20        /*!< */
+
+#define BCM2835_AUX_SPI_CNTL0_CS0_N     0x000C0000 /*!< CS 0 low */
+#define BCM2835_AUX_SPI_CNTL0_CS1_N     0x000A0000 /*!< CS 1 low */
+#define BCM2835_AUX_SPI_CNTL0_CS2_N    0x00060000 /*!< CS 2 low */
+
+#define BCM2835_AUX_SPI_CNTL0_POSTINPUT        0x00010000  /*!< */
+#define BCM2835_AUX_SPI_CNTL0_VAR_CS   0x00008000  /*!< */
+#define BCM2835_AUX_SPI_CNTL0_VAR_WIDTH        0x00004000  /*!< */
+#define BCM2835_AUX_SPI_CNTL0_DOUTHOLD 0x00003000  /*!< */
+#define BCM2835_AUX_SPI_CNTL0_ENABLE   0x00000800  /*!< */
+#define BCM2835_AUX_SPI_CNTL0_CPHA_IN  0x00000400  /*!< */
+#define BCM2835_AUX_SPI_CNTL0_CLEARFIFO        0x00000200  /*!< */
+#define BCM2835_AUX_SPI_CNTL0_CPHA_OUT 0x00000100  /*!< */
+#define BCM2835_AUX_SPI_CNTL0_CPOL     0x00000080  /*!< */
+#define BCM2835_AUX_SPI_CNTL0_MSBF_OUT 0x00000040  /*!< */
+#define BCM2835_AUX_SPI_CNTL0_SHIFTLEN 0x0000003F  /*!< */
+
+#define BCM2835_AUX_SPI_CNTL1_CSHIGH   0x00000700  /*!< */
+#define BCM2835_AUX_SPI_CNTL1_IDLE     0x00000080  /*!< */
+#define BCM2835_AUX_SPI_CNTL1_TXEMPTY  0x00000040  /*!< */
+#define BCM2835_AUX_SPI_CNTL1_MSBF_IN  0x00000002  /*!< */
+#define BCM2835_AUX_SPI_CNTL1_KEEP_IN  0x00000001  /*!< */
+
+#define BCM2835_AUX_SPI_STAT_TX_LVL    0xF0000000  /*!< */
+#define BCM2835_AUX_SPI_STAT_RX_LVL    0x00F00000  /*!< */
+#define BCM2835_AUX_SPI_STAT_TX_FULL   0x00000400  /*!< */
+#define BCM2835_AUX_SPI_STAT_TX_EMPTY  0x00000200  /*!< */
+#define BCM2835_AUX_SPI_STAT_RX_FULL   0x00000100  /*!< */
+#define BCM2835_AUX_SPI_STAT_RX_EMPTY  0x00000080  /*!< */
+#define BCM2835_AUX_SPI_STAT_BUSY      0x00000040  /*!< */
+#define BCM2835_AUX_SPI_STAT_BITCOUNT  0x0000003F  /*!< */
+
+/* Defines for SPI
+   GPIO register offsets from BCM2835_SPI0_BASE. 
+   Offsets into the SPI Peripheral block in bytes per 10.5 SPI Register Map
+*/
+#define BCM2835_SPI0_CS                      0x0000 /*!< SPI Master Control and Status */
+#define BCM2835_SPI0_FIFO                    0x0004 /*!< SPI Master TX and RX FIFOs */
+#define BCM2835_SPI0_CLK                     0x0008 /*!< SPI Master Clock Divider */
+#define BCM2835_SPI0_DLEN                    0x000c /*!< SPI Master Data Length */
+#define BCM2835_SPI0_LTOH                    0x0010 /*!< SPI LOSSI mode TOH */
+#define BCM2835_SPI0_DC                      0x0014 /*!< SPI DMA DREQ Controls */
+
+/* Register masks for SPI0_CS */
+#define BCM2835_SPI0_CS_LEN_LONG             0x02000000 /*!< Enable Long data word in Lossi mode if DMA_LEN 
is set */
+#define BCM2835_SPI0_CS_DMA_LEN              0x01000000 /*!< Enable DMA mode in Lossi mode */
+#define BCM2835_SPI0_CS_CSPOL2               0x00800000 /*!< Chip Select 2 Polarity */
+#define BCM2835_SPI0_CS_CSPOL1               0x00400000 /*!< Chip Select 1 Polarity */
+#define BCM2835_SPI0_CS_CSPOL0               0x00200000 /*!< Chip Select 0 Polarity */
+#define BCM2835_SPI0_CS_RXF                  0x00100000 /*!< RXF - RX FIFO Full */
+#define BCM2835_SPI0_CS_RXR                  0x00080000 /*!< RXR RX FIFO needs Reading (full) */
+#define BCM2835_SPI0_CS_TXD                  0x00040000 /*!< TXD TX FIFO can accept Data */
+#define BCM2835_SPI0_CS_RXD                  0x00020000 /*!< RXD RX FIFO contains Data */
+#define BCM2835_SPI0_CS_DONE                 0x00010000 /*!< Done transfer Done */
+#define BCM2835_SPI0_CS_TE_EN                0x00008000 /*!< Unused */
+#define BCM2835_SPI0_CS_LMONO                0x00004000 /*!< Unused */
+#define BCM2835_SPI0_CS_LEN                  0x00002000 /*!< LEN LoSSI enable */
+#define BCM2835_SPI0_CS_REN                  0x00001000 /*!< REN Read Enable */
+#define BCM2835_SPI0_CS_ADCS                 0x00000800 /*!< ADCS Automatically Deassert Chip Select */
+#define BCM2835_SPI0_CS_INTR                 0x00000400 /*!< INTR Interrupt on RXR */
+#define BCM2835_SPI0_CS_INTD                 0x00000200 /*!< INTD Interrupt on Done */
+#define BCM2835_SPI0_CS_DMAEN                0x00000100 /*!< DMAEN DMA Enable */
+#define BCM2835_SPI0_CS_TA                   0x00000080 /*!< Transfer Active */
+#define BCM2835_SPI0_CS_CSPOL                0x00000040 /*!< Chip Select Polarity */
+#define BCM2835_SPI0_CS_CLEAR                0x00000030 /*!< Clear FIFO Clear RX and TX */
+#define BCM2835_SPI0_CS_CLEAR_RX             0x00000020 /*!< Clear FIFO Clear RX  */
+#define BCM2835_SPI0_CS_CLEAR_TX             0x00000010 /*!< Clear FIFO Clear TX  */
+#define BCM2835_SPI0_CS_CPOL                 0x00000008 /*!< Clock Polarity */
+#define BCM2835_SPI0_CS_CPHA                 0x00000004 /*!< Clock Phase */
+#define BCM2835_SPI0_CS_CS                   0x00000003 /*!< Chip Select */
+
+/*! \brief bcm2835SPIBitOrder SPI Bit order
+  Specifies the SPI data bit ordering for bcm2835_spi_setBitOrder()
+*/
+typedef enum
+{
+    BCM2835_SPI_BIT_ORDER_LSBFIRST = 0,  /*!< LSB First */
+    BCM2835_SPI_BIT_ORDER_MSBFIRST = 1   /*!< MSB First */
+}bcm2835SPIBitOrder;
+
+/*! \brief SPI Data mode
+  Specify the SPI data mode to be passed to bcm2835_spi_setDataMode()
+*/
+typedef enum
+{
+    BCM2835_SPI_MODE0 = 0,  /*!< CPOL = 0, CPHA = 0 */
+    BCM2835_SPI_MODE1 = 1,  /*!< CPOL = 0, CPHA = 1 */
+    BCM2835_SPI_MODE2 = 2,  /*!< CPOL = 1, CPHA = 0 */
+    BCM2835_SPI_MODE3 = 3   /*!< CPOL = 1, CPHA = 1 */
+}bcm2835SPIMode;
+
+/*! \brief bcm2835SPIChipSelect
+  Specify the SPI chip select pin(s)
+*/
+typedef enum
+{
+    BCM2835_SPI_CS0 = 0,     /*!< Chip Select 0 */
+    BCM2835_SPI_CS1 = 1,     /*!< Chip Select 1 */
+    BCM2835_SPI_CS2 = 2,     /*!< Chip Select 2 (ie pins CS1 and CS2 are asserted) */
+    BCM2835_SPI_CS_NONE = 3  /*!< No CS, control it yourself */
+} bcm2835SPIChipSelect;
+
+/*! \brief bcm2835SPIClockDivider
+  Specifies the divider used to generate the SPI clock from the system clock.
+  Figures below give the divider, clock period and clock frequency.
+  Clock divided is based on nominal core clock rate of 250MHz on RPi1 and RPi2, and 400MHz on RPi3.
+  It is reported that (contrary to the documentation) any even divider may used.
+  The frequencies shown for each divider have been confirmed by measurement on RPi1 and RPi2.
+  The system clock frequency on RPi3 is different, so the frequency you get from a given divider will be 
different.
+  See comments in 'SPI Pins' for information about reliable SPI speeds.
+  Note: it is possible to change the core clock rate of the RPi 3 back to 250MHz, by putting 
+  \code
+  core_freq=250
+  \endcode
+  in the config.txt
+*/
+typedef enum
+{
+    BCM2835_SPI_CLOCK_DIVIDER_65536 = 0,       /*!< 65536 = 3.814697260kHz on Rpi2, 6.1035156kHz on RPI3 */
+    BCM2835_SPI_CLOCK_DIVIDER_32768 = 32768,   /*!< 32768 = 7.629394531kHz on Rpi2, 12.20703125kHz on RPI3 */
+    BCM2835_SPI_CLOCK_DIVIDER_16384 = 16384,   /*!< 16384 = 15.25878906kHz on Rpi2, 24.4140625kHz on RPI3 */
+    BCM2835_SPI_CLOCK_DIVIDER_8192  = 8192,    /*!< 8192 = 30.51757813kHz on Rpi2, 48.828125kHz on RPI3 */
+    BCM2835_SPI_CLOCK_DIVIDER_4096  = 4096,    /*!< 4096 = 61.03515625kHz on Rpi2, 97.65625kHz on RPI3 */
+    BCM2835_SPI_CLOCK_DIVIDER_2048  = 2048,    /*!< 2048 = 122.0703125kHz on Rpi2, 195.3125kHz on RPI3 */
+    BCM2835_SPI_CLOCK_DIVIDER_1024  = 1024,    /*!< 1024 = 244.140625kHz on Rpi2, 390.625kHz on RPI3 */
+    BCM2835_SPI_CLOCK_DIVIDER_512   = 512,     /*!< 512 = 488.28125kHz on Rpi2, 781.25kHz on RPI3 */
+    BCM2835_SPI_CLOCK_DIVIDER_256   = 256,     /*!< 256 = 976.5625kHz on Rpi2, 1.5625MHz on RPI3 */
+    BCM2835_SPI_CLOCK_DIVIDER_128   = 128,     /*!< 128 = 1.953125MHz on Rpi2, 3.125MHz on RPI3 */
+    BCM2835_SPI_CLOCK_DIVIDER_64    = 64,      /*!< 64 = 3.90625MHz on Rpi2, 6.250MHz on RPI3 */
+    BCM2835_SPI_CLOCK_DIVIDER_32    = 32,      /*!< 32 = 7.8125MHz on Rpi2, 12.5MHz on RPI3 */
+    BCM2835_SPI_CLOCK_DIVIDER_16    = 16,      /*!< 16 = 15.625MHz on Rpi2, 25MHz on RPI3 */
+    BCM2835_SPI_CLOCK_DIVIDER_8     = 8,       /*!< 8 = 31.25MHz on Rpi2, 50MHz on RPI3 */
+    BCM2835_SPI_CLOCK_DIVIDER_4     = 4,       /*!< 4 = 62.5MHz on Rpi2, 100MHz on RPI3. Dont expect this 
speed to work reliably. */
+    BCM2835_SPI_CLOCK_DIVIDER_2     = 2,       /*!< 2 = 125MHz on Rpi2, 200MHz on RPI3, fastest you can get. 
Dont expect this speed to work reliably.*/
+    BCM2835_SPI_CLOCK_DIVIDER_1     = 1        /*!< 1 = 3.814697260kHz on Rpi2, 6.1035156kHz on RPI3, same 
as 0/65536 */
+} bcm2835SPIClockDivider;
+
+/* Defines for I2C
+   GPIO register offsets from BCM2835_BSC*_BASE.
+   Offsets into the BSC Peripheral block in bytes per 3.1 BSC Register Map
+*/
+#define BCM2835_BSC_C                  0x0000 /*!< BSC Master Control */
+#define BCM2835_BSC_S                  0x0004 /*!< BSC Master Status */
+#define BCM2835_BSC_DLEN               0x0008 /*!< BSC Master Data Length */
+#define BCM2835_BSC_A                  0x000c /*!< BSC Master Slave Address */
+#define BCM2835_BSC_FIFO               0x0010 /*!< BSC Master Data FIFO */
+#define BCM2835_BSC_DIV                        0x0014 /*!< BSC Master Clock Divider */
+#define BCM2835_BSC_DEL                        0x0018 /*!< BSC Master Data Delay */
+#define BCM2835_BSC_CLKT               0x001c /*!< BSC Master Clock Stretch Timeout */
+
+/* Register masks for BSC_C */
+#define BCM2835_BSC_C_I2CEN            0x00008000 /*!< I2C Enable, 0 = disabled, 1 = enabled */
+#define BCM2835_BSC_C_INTR             0x00000400 /*!< Interrupt on RX */
+#define BCM2835_BSC_C_INTT             0x00000200 /*!< Interrupt on TX */
+#define BCM2835_BSC_C_INTD             0x00000100 /*!< Interrupt on DONE */
+#define BCM2835_BSC_C_ST               0x00000080 /*!< Start transfer, 1 = Start a new transfer */
+#define BCM2835_BSC_C_CLEAR_1          0x00000020 /*!< Clear FIFO Clear */
+#define BCM2835_BSC_C_CLEAR_2          0x00000010 /*!< Clear FIFO Clear */
+#define BCM2835_BSC_C_READ             0x00000001 /*!< Read transfer */
+
+/* Register masks for BSC_S */
+#define BCM2835_BSC_S_CLKT             0x00000200 /*!< Clock stretch timeout */
+#define BCM2835_BSC_S_ERR              0x00000100 /*!< ACK error */
+#define BCM2835_BSC_S_RXF              0x00000080 /*!< RXF FIFO full, 0 = FIFO is not full, 1 = FIFO is full 
*/
+#define BCM2835_BSC_S_TXE              0x00000040 /*!< TXE FIFO full, 0 = FIFO is not full, 1 = FIFO is full 
*/
+#define BCM2835_BSC_S_RXD              0x00000020 /*!< RXD FIFO contains data */
+#define BCM2835_BSC_S_TXD              0x00000010 /*!< TXD FIFO can accept data */
+#define BCM2835_BSC_S_RXR              0x00000008 /*!< RXR FIFO needs reading (full) */
+#define BCM2835_BSC_S_TXW              0x00000004 /*!< TXW FIFO needs writing (full) */
+#define BCM2835_BSC_S_DONE             0x00000002 /*!< Transfer DONE */
+#define BCM2835_BSC_S_TA               0x00000001 /*!< Transfer Active */
+
+#define BCM2835_BSC_FIFO_SIZE          16 /*!< BSC FIFO size */
+
+/*! \brief bcm2835I2CClockDivider
+  Specifies the divider used to generate the I2C clock from the system clock.
+  Clock divided is based on nominal base clock rate of 250MHz
+*/
+typedef enum
+{
+    BCM2835_I2C_CLOCK_DIVIDER_2500   = 2500,      /*!< 2500 = 10us = 100 kHz */
+    BCM2835_I2C_CLOCK_DIVIDER_626    = 626,       /*!< 622 = 2.504us = 399.3610 kHz */
+    BCM2835_I2C_CLOCK_DIVIDER_150    = 150,       /*!< 150 = 60ns = 1.666 MHz (default at reset) */
+    BCM2835_I2C_CLOCK_DIVIDER_148    = 148        /*!< 148 = 59ns = 1.689 MHz */
+} bcm2835I2CClockDivider;
+
+/*! \brief bcm2835I2CReasonCodes
+  Specifies the reason codes for the bcm2835_i2c_write and bcm2835_i2c_read functions.
+*/
+typedef enum
+{
+    BCM2835_I2C_REASON_OK           = 0x00,      /*!< Success */
+    BCM2835_I2C_REASON_ERROR_NACK    = 0x01,      /*!< Received a NACK */
+    BCM2835_I2C_REASON_ERROR_CLKT    = 0x02,      /*!< Received Clock Stretch Timeout */
+    BCM2835_I2C_REASON_ERROR_DATA    = 0x04       /*!< Not all data is sent / received */
+} bcm2835I2CReasonCodes;
+
+/* Defines for ST
+   GPIO register offsets from BCM2835_ST_BASE.
+   Offsets into the ST Peripheral block in bytes per 12.1 System Timer Registers
+   The System Timer peripheral provides four 32-bit timer channels and a single 64-bit free running counter.
+   BCM2835_ST_CLO is the System Timer Counter Lower bits register.
+   The system timer free-running counter lower register is a read-only register that returns the current 
value
+   of the lower 32-bits of the free running counter.
+   BCM2835_ST_CHI is the System Timer Counter Upper bits register.
+   The system timer free-running counter upper register is a read-only register that returns the current 
value
+   of the upper 32-bits of the free running counter.
+*/
+#define BCM2835_ST_CS                  0x0000 /*!< System Timer Control/Status */
+#define BCM2835_ST_CLO                         0x0004 /*!< System Timer Counter Lower 32 bits */
+#define BCM2835_ST_CHI                         0x0008 /*!< System Timer Counter Upper 32 bits */
+
+/*! @} */
+
+
+/* Defines for PWM, word offsets (ie 4 byte multiples) */
+#define BCM2835_PWM_CONTROL 0
+#define BCM2835_PWM_STATUS  1
+#define BCM2835_PWM_DMAC    2
+#define BCM2835_PWM0_RANGE  4
+#define BCM2835_PWM0_DATA   5
+#define BCM2835_PWM_FIF1    6
+#define BCM2835_PWM1_RANGE  8
+#define BCM2835_PWM1_DATA   9
+
+/* Defines for PWM Clock, word offsets (ie 4 byte multiples) */
+#define BCM2835_PWMCLK_CNTL     40
+#define BCM2835_PWMCLK_DIV      41
+#define BCM2835_PWM_PASSWRD     (0x5A << 24)  /*!< Password to enable setting PWM clock */
+
+#define BCM2835_PWM1_MS_MODE    0x8000  /*!< Run in Mark/Space mode */
+#define BCM2835_PWM1_USEFIFO    0x2000  /*!< Data from FIFO */
+#define BCM2835_PWM1_REVPOLAR   0x1000  /*!< Reverse polarity */
+#define BCM2835_PWM1_OFFSTATE   0x0800  /*!< Ouput Off state */
+#define BCM2835_PWM1_REPEATFF   0x0400  /*!< Repeat last value if FIFO empty */
+#define BCM2835_PWM1_SERIAL     0x0200  /*!< Run in serial mode */
+#define BCM2835_PWM1_ENABLE     0x0100  /*!< Channel Enable */
+
+#define BCM2835_PWM0_MS_MODE    0x0080  /*!< Run in Mark/Space mode */
+#define BCM2835_PWM_CLEAR_FIFO  0x0040  /*!< Clear FIFO */
+#define BCM2835_PWM0_USEFIFO    0x0020  /*!< Data from FIFO */
+#define BCM2835_PWM0_REVPOLAR   0x0010  /*!< Reverse polarity */
+#define BCM2835_PWM0_OFFSTATE   0x0008  /*!< Ouput Off state */
+#define BCM2835_PWM0_REPEATFF   0x0004  /*!< Repeat last value if FIFO empty */
+#define BCM2835_PWM0_SERIAL     0x0002  /*!< Run in serial mode */
+#define BCM2835_PWM0_ENABLE     0x0001  /*!< Channel Enable */
+
+/*! \brief bcm2835PWMClockDivider
+  Specifies the divider used to generate the PWM clock from the system clock.
+  Figures below give the divider, clock period and clock frequency.
+  Clock divided is based on nominal PWM base clock rate of 19.2MHz
+  The frequencies shown for each divider have been confirmed by measurement
+*/
+typedef enum
+{
+    BCM2835_PWM_CLOCK_DIVIDER_2048  = 2048,    /*!< 2048 = 9.375kHz */
+    BCM2835_PWM_CLOCK_DIVIDER_1024  = 1024,    /*!< 1024 = 18.75kHz */
+    BCM2835_PWM_CLOCK_DIVIDER_512   = 512,     /*!< 512 = 37.5kHz */
+    BCM2835_PWM_CLOCK_DIVIDER_256   = 256,     /*!< 256 = 75kHz */
+    BCM2835_PWM_CLOCK_DIVIDER_128   = 128,     /*!< 128 = 150kHz */
+    BCM2835_PWM_CLOCK_DIVIDER_64    = 64,      /*!< 64 = 300kHz */
+    BCM2835_PWM_CLOCK_DIVIDER_32    = 32,      /*!< 32 = 600.0kHz */
+    BCM2835_PWM_CLOCK_DIVIDER_16    = 16,      /*!< 16 = 1.2MHz */
+    BCM2835_PWM_CLOCK_DIVIDER_8     = 8,       /*!< 8 = 2.4MHz */
+    BCM2835_PWM_CLOCK_DIVIDER_4     = 4,       /*!< 4 = 4.8MHz */
+    BCM2835_PWM_CLOCK_DIVIDER_2     = 2,       /*!< 2 = 9.6MHz, fastest you can get */
+    BCM2835_PWM_CLOCK_DIVIDER_1     = 1        /*!< 1 = 4.6875kHz, same as divider 4096 */
+} bcm2835PWMClockDivider;
+
+/* Historical name compatibility */
+#ifndef BCM2835_NO_DELAY_COMPATIBILITY
+#define delay(x) bcm2835_delay(x)
+#define delayMicroseconds(x) bcm2835_delayMicroseconds(x)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+    /*! \defgroup init Library initialisation and management
+      These functions allow you to intialise and control the bcm2835 library
+      @{
+    */
+
+    /*! Initialise the library by opening /dev/mem (if you are root) 
+      or /dev/gpiomem (if you are not)
+      and getting pointers to the 
+      internal memory for BCM 2835 device registers. You must call this (successfully)
+      before calling any other 
+      functions in this library (except bcm2835_set_debug). 
+      If bcm2835_init() fails by returning 0, 
+      calling any other function may result in crashes or other failures.
+      If bcm2835_init() succeeds but you are not running as root, then only gpio operations
+      are permitted, and calling any other functions may result in crashes or other failures. .
+      Prints messages to stderr in case of errors.
+      \return 1 if successful else 0
+    */
+    extern int bcm2835_init(void);
+
+    /*! Close the library, deallocating any allocated memory and closing /dev/mem
+      \return 1 if successful else 0
+    */
+    extern int bcm2835_close(void);
+
+    /*! Sets the debug level of the library.
+      A value of 1 prevents mapping to /dev/mem, and makes the library print out
+      what it would do, rather than accessing the GPIO registers.
+      A value of 0, the default, causes normal operation.
+      Call this before calling bcm2835_init();
+      \param[in] debug The new debug level. 1 means debug
+    */
+    extern void  bcm2835_set_debug(uint8_t debug);
+
+    /*! Returns the version number of the library, same as BCM2835_VERSION
+       \return the current library version number
+    */
+    extern unsigned int bcm2835_version(void);
+
+    /*! @} */
+
+    /*! \defgroup lowlevel Low level register access
+      These functions provide low level register access, and should not generally
+      need to be used 
+       
+      @{
+    */
+
+    /*! Gets the base of a register
+      \param[in] regbase You can use one of the common values BCM2835_REGBASE_*
+      in \ref bcm2835RegisterBase
+      \return the register base
+      \sa Physical Addresses
+    */
+    extern uint32_t* bcm2835_regbase(uint8_t regbase);
+
+    /*! Reads 32 bit value from a peripheral address WITH a memory barrier before and after each read.
+      This is safe, but slow.  The MB before protects this read from any in-flight reads that didn't
+      use a MB.  The MB after protects subsequent reads from another peripheral.
+
+      \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc.
+      \return the value read from the 32 bit register
+      \sa Physical Addresses
+    */
+    extern uint32_t bcm2835_peri_read(volatile uint32_t* paddr);
+
+    /*! Reads 32 bit value from a peripheral address WITHOUT the read barriers
+      You should only use this when:
+      o your code has previously called bcm2835_peri_read() for a register
+      within the same peripheral, and no read or write to another peripheral has occurred since.
+      o your code has called bcm2835_memory_barrier() since the last access to ANOTHER peripheral.
+
+      \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc.
+      \return the value read from the 32 bit register
+      \sa Physical Addresses
+    */
+    extern uint32_t bcm2835_peri_read_nb(volatile uint32_t* paddr);
+
+
+    /*! Writes 32 bit value from a peripheral address WITH a memory barrier before and after each write
+      This is safe, but slow.  The MB before ensures that any in-flight write to another peripheral
+      completes before this write is issued.  The MB after ensures that subsequent reads and writes
+      to another peripheral will see the effect of this write.
+
+      This is a tricky optimization; if you aren't sure, use the barrier version.
+
+      \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc.
+      \param[in] value The 32 bit value to write
+      \sa Physical Addresses
+    */
+    extern void bcm2835_peri_write(volatile uint32_t* paddr, uint32_t value);
+
+    /*! Writes 32 bit value from a peripheral address without the write barrier
+      You should only use this when:
+      o your code has previously called bcm2835_peri_write() for a register
+      within the same peripheral, and no other peripheral access has occurred since.
+      o your code has called bcm2835_memory_barrier() since the last access to ANOTHER peripheral.
+
+      This is a tricky optimization; if you aren't sure, use the barrier version.
+
+      \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc.
+      \param[in] value The 32 bit value to write
+      \sa Physical Addresses
+    */
+    extern void bcm2835_peri_write_nb(volatile uint32_t* paddr, uint32_t value);
+
+    /*! Alters a number of bits in a 32 peripheral regsiter.
+      It reads the current valu and then alters the bits defines as 1 in mask, 
+      according to the bit value in value. 
+      All other bits that are 0 in the mask are unaffected.
+      Use this to alter a subset of the bits in a register.
+      Memory barriers are used.  Note that this is not atomic; an interrupt
+      routine can cause unexpected results.
+      \param[in] paddr Physical address to read from. See BCM2835_GPIO_BASE etc.
+      \param[in] value The 32 bit value to write, masked in by mask.
+      \param[in] mask Bitmask that defines the bits that will be altered in the register.
+      \sa Physical Addresses
+    */
+    extern void bcm2835_peri_set_bits(volatile uint32_t* paddr, uint32_t value, uint32_t mask);
+    /*! @}    end of lowlevel */
+
+    /*! \defgroup gpio GPIO register access
+      These functions allow you to control the GPIO interface. You can set the 
+      function of each GPIO pin, read the input state and set the output state.
+      @{
+    */
+
+    /*! Sets the Function Select register for the given pin, which configures
+      the pin as Input, Output or one of the 6 alternate functions.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+      \param[in] mode Mode to set the pin to, one of BCM2835_GPIO_FSEL_* from \ref bcm2835FunctionSelect
+    */
+    extern void bcm2835_gpio_fsel(uint8_t pin, uint8_t mode);
+
+    /*! Sets the specified pin output to 
+      HIGH.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+      \sa bcm2835_gpio_write()
+    */
+    extern void bcm2835_gpio_set(uint8_t pin);
+
+    /*! Sets the specified pin output to 
+      LOW.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+      \sa bcm2835_gpio_write()
+    */
+    extern void bcm2835_gpio_clr(uint8_t pin);
+
+    /*! Sets any of the first 32 GPIO output pins specified in the mask to 
+      HIGH.
+      \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05)
+      \sa bcm2835_gpio_write_multi()
+    */
+    extern void bcm2835_gpio_set_multi(uint32_t mask);
+
+    /*! Sets any of the first 32 GPIO output pins specified in the mask to 
+      LOW.
+      \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05)
+      \sa bcm2835_gpio_write_multi()
+    */
+    extern void bcm2835_gpio_clr_multi(uint32_t mask);
+
+    /*! Reads the current level on the specified 
+      pin and returns either HIGH or LOW. Works whether or not the pin
+      is an input or an output.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+      \return the current level  either HIGH or LOW
+    */
+    extern uint8_t bcm2835_gpio_lev(uint8_t pin);
+
+    /*! Event Detect Status.
+      Tests whether the specified pin has detected a level or edge
+      as requested by bcm2835_gpio_ren(), bcm2835_gpio_fen(), bcm2835_gpio_hen(), 
+      bcm2835_gpio_len(), bcm2835_gpio_aren(), bcm2835_gpio_afen().
+      Clear the flag for a given pin by calling bcm2835_gpio_set_eds(pin);
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+      \return HIGH if the event detect status for the given pin is true.
+    */
+    extern uint8_t bcm2835_gpio_eds(uint8_t pin);
+
+    /*! Same as bcm2835_gpio_eds() but checks if any of the pins specified in
+      the mask have detected a level or edge.
+      \param[in] mask Mask of pins to check. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05)
+      \return Mask of pins HIGH if the event detect status for the given pin is true.
+    */
+    extern uint32_t bcm2835_gpio_eds_multi(uint32_t mask);
+
+    /*! Sets the Event Detect Status register for a given pin to 1, 
+      which has the effect of clearing the flag. Use this afer seeing
+      an Event Detect Status on the pin.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+    */
+    extern void bcm2835_gpio_set_eds(uint8_t pin);
+
+    /*! Same as bcm2835_gpio_set_eds() but clears the flag for any pin which
+      is set in the mask.
+      \param[in] mask Mask of pins to clear. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05)
+    */
+    extern void bcm2835_gpio_set_eds_multi(uint32_t mask);
+    
+    /*! Enable Rising Edge Detect Enable for the specified pin.
+      When a rising edge is detected, sets the appropriate pin in Event Detect Status.
+      The GPRENn registers use
+      synchronous edge detection. This means the input signal is sampled using the
+      system clock and then it is looking for a ?011? pattern on the sampled signal. This
+      has the effect of suppressing glitches.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+    */
+    extern void bcm2835_gpio_ren(uint8_t pin);
+
+    /*! Disable Rising Edge Detect Enable for the specified pin.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+    */
+    extern void bcm2835_gpio_clr_ren(uint8_t pin);
+
+    /*! Enable Falling Edge Detect Enable for the specified pin.
+      When a falling edge is detected, sets the appropriate pin in Event Detect Status.
+      The GPRENn registers use
+      synchronous edge detection. This means the input signal is sampled using the
+      system clock and then it is looking for a ?100? pattern on the sampled signal. This
+      has the effect of suppressing glitches.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+    */
+    extern void bcm2835_gpio_fen(uint8_t pin);
+
+    /*! Disable Falling Edge Detect Enable for the specified pin.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+    */
+    extern void bcm2835_gpio_clr_fen(uint8_t pin);
+
+    /*! Enable High Detect Enable for the specified pin.
+      When a HIGH level is detected on the pin, sets the appropriate pin in Event Detect Status.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+    */
+    extern void bcm2835_gpio_hen(uint8_t pin);
+
+    /*! Disable High Detect Enable for the specified pin.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+    */
+    extern void bcm2835_gpio_clr_hen(uint8_t pin);
+
+    /*! Enable Low Detect Enable for the specified pin.
+      When a LOW level is detected on the pin, sets the appropriate pin in Event Detect Status.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+    */
+    extern void bcm2835_gpio_len(uint8_t pin);
+
+    /*! Disable Low Detect Enable for the specified pin.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+    */
+    extern void bcm2835_gpio_clr_len(uint8_t pin);
+
+    /*! Enable Asynchronous Rising Edge Detect Enable for the specified pin.
+      When a rising edge is detected, sets the appropriate pin in Event Detect Status.
+      Asynchronous means the incoming signal is not sampled by the system clock. As such
+      rising edges of very short duration can be detected.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+    */
+    extern void bcm2835_gpio_aren(uint8_t pin);
+
+    /*! Disable Asynchronous Rising Edge Detect Enable for the specified pin.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+    */
+    extern void bcm2835_gpio_clr_aren(uint8_t pin);
+
+    /*! Enable Asynchronous Falling Edge Detect Enable for the specified pin.
+      When a falling edge is detected, sets the appropriate pin in Event Detect Status.
+      Asynchronous means the incoming signal is not sampled by the system clock. As such
+      falling edges of very short duration can be detected.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+    */
+    extern void bcm2835_gpio_afen(uint8_t pin);
+
+    /*! Disable Asynchronous Falling Edge Detect Enable for the specified pin.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+    */
+    extern void bcm2835_gpio_clr_afen(uint8_t pin);
+
+    /*! Sets the Pull-up/down register for the given pin. This is
+      used with bcm2835_gpio_pudclk() to set the  Pull-up/down resistor for the given pin.
+      However, it is usually more convenient to use bcm2835_gpio_set_pud().
+      \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl
+      On the RPI 4, although this function and bcm2835_gpio_pudclk() are supported for backward
+      compatibility, new code should always use bcm2835_gpio_set_pud().
+      \sa bcm2835_gpio_set_pud()
+    */
+    extern void bcm2835_gpio_pud(uint8_t pud);
+
+    /*! Clocks the Pull-up/down value set earlier by bcm2835_gpio_pud() into the pin.
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+      \param[in] on HIGH to clock the value from bcm2835_gpio_pud() into the pin. 
+      LOW to remove the clock. 
+      
+      On the RPI 4, although this function and bcm2835_gpio_pud() are supported for backward
+      compatibility, new code should always use bcm2835_gpio_set_pud().
+      
+      \sa bcm2835_gpio_set_pud()
+    */
+    extern void bcm2835_gpio_pudclk(uint8_t pin, uint8_t on);
+
+    /*! Reads and returns the Pad Control for the given GPIO group.
+      Caution: requires root access.
+      \param[in] group The GPIO pad group number, one of BCM2835_PAD_GROUP_GPIO_*
+      \return Mask of bits from BCM2835_PAD_* from \ref bcm2835PadGroup
+    */
+    extern uint32_t bcm2835_gpio_pad(uint8_t group);
+
+    /*! Sets the Pad Control for the given GPIO group.
+      Caution: requires root access.
+      \param[in] group The GPIO pad group number, one of BCM2835_PAD_GROUP_GPIO_*
+      \param[in] control Mask of bits from BCM2835_PAD_* from \ref bcm2835PadGroup. Note 
+      that it is not necessary to include BCM2835_PAD_PASSWRD in the mask as this
+      is automatically included.
+    */
+    extern void bcm2835_gpio_set_pad(uint8_t group, uint32_t control);
+
+    /*! Delays for the specified number of milliseconds.
+      Uses nanosleep(), and therefore does not use CPU until the time is up.
+      However, you are at the mercy of nanosleep(). From the manual for nanosleep():
+      If the interval specified in req is not an exact multiple of the granularity  
+      underlying  clock  (see  time(7)),  then the interval will be
+      rounded up to the next multiple. Furthermore, after the sleep completes, 
+      there may still be a delay before the CPU becomes free to once
+      again execute the calling thread.
+      \param[in] millis Delay in milliseconds
+    */
+    extern void bcm2835_delay (unsigned int millis);
+
+    /*! Delays for the specified number of microseconds.
+      Uses a combination of nanosleep() and a busy wait loop on the BCM2835 system timers,
+      However, you are at the mercy of nanosleep(). From the manual for nanosleep():
+      If the interval specified in req is not an exact multiple of the granularity  
+      underlying  clock  (see  time(7)),  then the interval will be
+      rounded up to the next multiple. Furthermore, after the sleep completes, 
+      there may still be a delay before the CPU becomes free to once
+      again execute the calling thread.
+      For times less than about 450 microseconds, uses a busy wait on the System Timer.
+      It is reported that a delay of 0 microseconds on RaspberryPi will in fact
+      result in a delay of about 80 microseconds. Your mileage may vary.
+      \param[in] micros Delay in microseconds
+    */
+    extern void bcm2835_delayMicroseconds (uint64_t micros);
+
+    /*! Sets the output state of the specified pin
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+      \param[in] on HIGH sets the output to HIGH and LOW to LOW.
+    */
+    extern void bcm2835_gpio_write(uint8_t pin, uint8_t on);
+
+    /*! Sets any of the first 32 GPIO output pins specified in the mask to the state given by on
+      \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05)
+      \param[in] on HIGH sets the output to HIGH and LOW to LOW.
+    */
+    extern void bcm2835_gpio_write_multi(uint32_t mask, uint8_t on);
+
+    /*! Sets the first 32 GPIO output pins specified in the mask to the value given by value
+      \param[in] value values required for each bit masked in by mask, eg: (1 << RPI_GPIO_P1_03) | (1 << 
RPI_GPIO_P1_05)
+      \param[in] mask Mask of pins to affect. Use eg: (1 << RPI_GPIO_P1_03) | (1 << RPI_GPIO_P1_05)
+    */
+    extern void bcm2835_gpio_write_mask(uint32_t value, uint32_t mask);
+
+    /*! Sets the Pull-up/down mode for the specified pin. This is more convenient than
+      clocking the mode in with bcm2835_gpio_pud() and bcm2835_gpio_pudclk().
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+      \param[in] pud The desired Pull-up/down mode. One of BCM2835_GPIO_PUD_* from bcm2835PUDControl
+    */
+    extern void bcm2835_gpio_set_pud(uint8_t pin, uint8_t pud);
+
+    /*! On the BCM2711 based RPI 4, gets the current Pull-up/down mode for the specified pin.
+      Returns one of BCM2835_GPIO_PUD_* from bcm2835PUDControl.
+      On earlier RPI versions not based on the BCM2711, returns BCM2835_GPIO_PUD_ERROR
+      \param[in] pin GPIO number, or one of RPI_GPIO_P1_* from \ref RPiGPIOPin.
+    */
+    
+    extern uint8_t bcm2835_gpio_get_pud(uint8_t pin);
+
+    /*! @}  */
+
+    /*! \defgroup spi SPI access
+      These functions let you use SPI0 (Serial Peripheral Interface) to 
+      interface with an external SPI device.
+      @{
+    */
+
+    /*! Start SPI operations.
+      Forces RPi SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1)
+      to alternate function ALT0, which enables those pins for SPI interface.
+      You should call bcm2835_spi_end() when all SPI funcitons are complete to return the pins to 
+      their default functions.
+      \sa  bcm2835_spi_end()
+      \return 1 if successful, 0 otherwise (perhaps because you are not running as root)
+    */
+    extern int bcm2835_spi_begin(void);
+
+    /*! End SPI operations.
+      SPI0 pins P1-19 (MOSI), P1-21 (MISO), P1-23 (CLK), P1-24 (CE0) and P1-26 (CE1)
+      are returned to their default INPUT behaviour.
+    */
+    extern void bcm2835_spi_end(void);
+
+    /*! Sets the SPI bit order
+      Set the bit order to be used for transmit and receive. The bcm2835 SPI0 only supports 
BCM2835_SPI_BIT_ORDER_MSB,
+      so if you select BCM2835_SPI_BIT_ORDER_LSB, the bytes will be reversed in software.
+      The library defaults to BCM2835_SPI_BIT_ORDER_MSB.
+      \param[in] order The desired bit order, one of BCM2835_SPI_BIT_ORDER_*, 
+      see \ref bcm2835SPIBitOrder
+    */
+    extern void bcm2835_spi_setBitOrder(uint8_t order);
+
+    /*! Sets the SPI clock divider and therefore the 
+      SPI clock speed. 
+      \param[in] divider The desired SPI clock divider, one of BCM2835_SPI_CLOCK_DIVIDER_*, 
+      see \ref bcm2835SPIClockDivider
+    */
+    extern void bcm2835_spi_setClockDivider(uint16_t divider);
+
+    /*! Sets the SPI clock divider by converting the speed parameter to
+      the equivalent SPI clock divider. ( see \sa bcm2835_spi_setClockDivider)
+      \param[in] speed_hz The desired SPI clock speed in Hz
+    */
+   extern void bcm2835_spi_set_speed_hz(uint32_t speed_hz);
+
+    /*! Sets the SPI data mode
+      Sets the clock polariy and phase
+      \param[in] mode The desired data mode, one of BCM2835_SPI_MODE*, 
+      see \ref bcm2835SPIMode
+    */
+    extern void bcm2835_spi_setDataMode(uint8_t mode);
+
+    /*! Sets the chip select pin(s)
+      When an bcm2835_spi_transfer() is made, the selected pin(s) will be asserted during the
+      transfer.
+      \param[in] cs Specifies the CS pins(s) that are used to activate the desired slave. 
+      One of BCM2835_SPI_CS*, see \ref bcm2835SPIChipSelect
+    */
+    extern void bcm2835_spi_chipSelect(uint8_t cs);
+
+    /*! Sets the chip select pin polarity for a given pin
+      When an bcm2835_spi_transfer() occurs, the currently selected chip select pin(s) 
+      will be asserted to the 
+      value given by active. When transfers are not happening, the chip select pin(s) 
+      return to the complement (inactive) value.
+      \param[in] cs The chip select pin to affect
+      \param[in] active Whether the chip select pin is to be active HIGH
+    */
+    extern void bcm2835_spi_setChipSelectPolarity(uint8_t cs, uint8_t active);
+
+    /*! Transfers one byte to and from the currently selected SPI slave.
+      Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) 
+      during the transfer.
+      Clocks the 8 bit value out on MOSI, and simultaneously clocks in data from MISO. 
+      Returns the read data byte from the slave.
+      Uses polled transfer as per section 10.6.1 of the BCM 2835 ARM Peripherls manual
+      \param[in] value The 8 bit data byte to write to MOSI
+      \return The 8 bit byte simultaneously read from  MISO
+      \sa bcm2835_spi_transfern()
+    */
+    extern uint8_t bcm2835_spi_transfer(uint8_t value);
+    
+    /*! Transfers any number of bytes to and from the currently selected SPI slave.
+      Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect) 
+      during the transfer.
+      Clocks the len 8 bit bytes out on MOSI, and simultaneously clocks in data from MISO. 
+      The data read read from the slave is placed into rbuf. rbuf must be at least len bytes long
+      Uses polled transfer as per section 10.6.1 of the BCM 2835 ARM Peripherls manual
+      \param[in] tbuf Buffer of bytes to send. 
+      \param[out] rbuf Received bytes will by put in this buffer
+      \param[in] len Number of bytes in the tbuf buffer, and the number of bytes to send/received
+      \sa bcm2835_spi_transfer()
+    */
+    extern void bcm2835_spi_transfernb(char* tbuf, char* rbuf, uint32_t len);
+
+    /*! Transfers any number of bytes to and from the currently selected SPI slave
+      using bcm2835_spi_transfernb.
+      The returned data from the slave replaces the transmitted data in the buffer.
+      \param[in,out] buf Buffer of bytes to send. Received bytes will replace the contents
+      \param[in] len Number of bytes int eh buffer, and the number of bytes to send/received
+      \sa bcm2835_spi_transfer()
+    */
+    extern void bcm2835_spi_transfern(char* buf, uint32_t len);
+
+    /*! Transfers any number of bytes to the currently selected SPI slave.
+      Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect)
+      during the transfer.
+      \param[in] buf Buffer of bytes to send.
+      \param[in] len Number of bytes in the buf buffer, and the number of bytes to send
+    */
+    extern void bcm2835_spi_writenb(const char* buf, uint32_t len);
+
+    /*! Transfers half-word to the currently selected SPI slave.
+      Asserts the currently selected CS pins (as previously set by bcm2835_spi_chipSelect)
+      during the transfer.
+      Clocks the 8 bit value out on MOSI, and simultaneously clocks in data from MISO.
+      Uses polled transfer as per section 10.6.1 of the BCM 2835 ARM Peripherls manual
+      \param[in] data The 8 bit data byte to write to MOSI
+      \sa bcm2835_spi_writenb()
+    */
+    extern void bcm2835_spi_write(uint16_t data);
+
+    /*! Start AUX SPI operations.
+      Forces RPi AUX SPI pins P1-38 (MOSI), P1-38 (MISO), P1-40 (CLK) and P1-36 (CE2)
+      to alternate function ALT4, which enables those pins for SPI interface.
+      \return 1 if successful, 0 otherwise (perhaps because you are not running as root)
+    */
+    extern int bcm2835_aux_spi_begin(void);
+
+    /*! End AUX SPI operations.
+       SPI1 pins P1-38 (MOSI), P1-38 (MISO), P1-40 (CLK) and P1-36 (CE2)
+       are returned to their default INPUT behaviour.
+     */
+    extern void bcm2835_aux_spi_end(void);
+
+    /*! Sets the AUX SPI clock divider and therefore the AUX SPI clock speed.
+      \param[in] divider The desired AUX SPI clock divider.
+    */
+    extern void bcm2835_aux_spi_setClockDivider(uint16_t divider);
+
+    /*!
+     * Calculates the input for \sa bcm2835_aux_spi_setClockDivider
+     * @param speed_hz A value between \sa BCM2835_AUX_SPI_CLOCK_MIN and \sa BCM2835_AUX_SPI_CLOCK_MAX
+     * @return Input for \sa bcm2835_aux_spi_setClockDivider
+     */
+    extern uint16_t bcm2835_aux_spi_CalcClockDivider(uint32_t speed_hz);
+
+    /*! Transfers half-word to the AUX SPI slave.
+      Asserts the currently selected CS pins during the transfer.
+      \param[in] data The 8 bit data byte to write to MOSI
+      \return The 16 bit byte simultaneously read from  MISO
+      \sa bcm2835_spi_transfern()
+    */
+    extern void bcm2835_aux_spi_write(uint16_t data);
+
+    /*! Transfers any number of bytes to the AUX SPI slave.
+      Asserts the CE2 pin during the transfer.
+      \param[in] buf Buffer of bytes to send.
+      \param[in] len Number of bytes in the tbuf buffer, and the number of bytes to send
+    */
+    extern void bcm2835_aux_spi_writenb(const char *buf, uint32_t len);
+
+    /*! Transfers any number of bytes to and from the AUX SPI slave
+      using bcm2835_aux_spi_transfernb.
+      The returned data from the slave replaces the transmitted data in the buffer.
+      \param[in,out] buf Buffer of bytes to send. Received bytes will replace the contents
+      \param[in] len Number of bytes in the buffer, and the number of bytes to send/received
+      \sa bcm2835_aux_spi_transfer()
+    */
+    extern void bcm2835_aux_spi_transfern(char *buf, uint32_t len);
+
+    /*! Transfers any number of bytes to and from the AUX SPI slave.
+      Asserts the CE2 pin during the transfer.
+      Clocks the len 8 bit bytes out on MOSI, and simultaneously clocks in data from MISO.
+      The data read read from the slave is placed into rbuf. rbuf must be at least len bytes long
+      \param[in] tbuf Buffer of bytes to send.
+      \param[out] rbuf Received bytes will by put in this buffer
+      \param[in] len Number of bytes in the tbuf buffer, and the number of bytes to send/received
+    */
+    extern void bcm2835_aux_spi_transfernb(const char *tbuf, char *rbuf, uint32_t len);
+
+    /*! Transfers one byte to and from the AUX SPI slave.
+      Clocks the 8 bit value out on MOSI, and simultaneously clocks in data from MISO. 
+      Returns the read data byte from the slave.
+      \param[in] value The 8 bit data byte to write to MOSI
+      \return The 8 bit byte simultaneously read from MISO
+      \sa bcm2835_aux_spi_transfern()
+    */
+    extern uint8_t bcm2835_aux_spi_transfer(uint8_t value);
+    
+    /*! @} */
+
+    /*! \defgroup i2c I2C access
+      These functions let you use I2C (The Broadcom Serial Control bus with the Philips
+      I2C bus/interface version 2.1 January 2000.) to interface with an external I2C device.
+      @{
+    */
+
+    /*! Start I2C operations.
+      Forces RPi I2C pins P1-03 (SDA) and P1-05 (SCL)
+      to alternate function ALT0, which enables those pins for I2C interface.
+      You should call bcm2835_i2c_end() when all I2C functions are complete to return the pins to
+      their default functions
+      \return 1 if successful, 0 otherwise (perhaps because you are not running as root)
+      \sa  bcm2835_i2c_end()
+    */
+    extern int bcm2835_i2c_begin(void);
+
+    /*! End I2C operations.
+      I2C pins P1-03 (SDA) and P1-05 (SCL)
+      are returned to their default INPUT behaviour.
+    */
+    extern void bcm2835_i2c_end(void);
+
+    /*! Sets the I2C slave address.
+      \param[in] addr The I2C slave address.
+    */
+    extern void bcm2835_i2c_setSlaveAddress(uint8_t addr);
+
+    /*! Sets the I2C clock divider and therefore the I2C clock speed.
+      \param[in] divider The desired I2C clock divider, one of BCM2835_I2C_CLOCK_DIVIDER_*,
+      see \ref bcm2835I2CClockDivider
+    */
+    extern void bcm2835_i2c_setClockDivider(uint16_t divider);
+
+    /*! Sets the I2C clock divider by converting the baudrate parameter to
+      the equivalent I2C clock divider. ( see \sa bcm2835_i2c_setClockDivider)
+      For the I2C standard 100khz you would set baudrate to 100000
+      The use of baudrate corresponds to its use in the I2C kernel device
+      driver. (Of course, bcm2835 has nothing to do with the kernel driver)
+    */
+    extern void bcm2835_i2c_set_baudrate(uint32_t baudrate);
+
+    /*! Transfers any number of bytes to the currently selected I2C slave.
+      (as previously set by \sa bcm2835_i2c_setSlaveAddress)
+      \param[in] buf Buffer of bytes to send.
+      \param[in] len Number of bytes in the buf buffer, and the number of bytes to send.
+      \return reason see \ref bcm2835I2CReasonCodes
+    */
+    extern uint8_t bcm2835_i2c_write(const char * buf, uint32_t len);
+
+    /*! Transfers any number of bytes from the currently selected I2C slave.
+      (as previously set by \sa bcm2835_i2c_setSlaveAddress)
+      \param[in] buf Buffer of bytes to receive.
+      \param[in] len Number of bytes in the buf buffer, and the number of bytes to received.
+      \return reason see \ref bcm2835I2CReasonCodes
+    */
+    extern uint8_t bcm2835_i2c_read(char* buf, uint32_t len);
+
+    /*! Allows reading from I2C slaves that require a repeated start (without any prior stop)
+      to read after the required slave register has been set. For example, the popular
+      MPL3115A2 pressure and temperature sensor. Note that your device must support or
+      require this mode. If your device does not require this mode then the standard
+      combined:
+      \sa bcm2835_i2c_write
+      \sa bcm2835_i2c_read
+      are a better choice.
+      Will read from the slave previously set by \sa bcm2835_i2c_setSlaveAddress
+      \param[in] regaddr Buffer containing the slave register you wish to read from.
+      \param[in] buf Buffer of bytes to receive.
+      \param[in] len Number of bytes in the buf buffer, and the number of bytes to received.
+      \return reason see \ref bcm2835I2CReasonCodes
+    */
+    extern uint8_t bcm2835_i2c_read_register_rs(char* regaddr, char* buf, uint32_t len);
+
+    /*! Allows sending an arbitrary number of bytes to I2C slaves before issuing a repeated
+      start (with no prior stop) and reading a response.
+      Necessary for devices that require such behavior, such as the MLX90620.
+      Will write to and read from the slave previously set by \sa bcm2835_i2c_setSlaveAddress
+      \param[in] cmds Buffer containing the bytes to send before the repeated start condition.
+      \param[in] cmds_len Number of bytes to send from cmds buffer
+      \param[in] buf Buffer of bytes to receive.
+      \param[in] buf_len Number of bytes to receive in the buf buffer.
+      \return reason see \ref bcm2835I2CReasonCodes
+    */
+    extern uint8_t bcm2835_i2c_write_read_rs(char* cmds, uint32_t cmds_len, char* buf, uint32_t buf_len);
+
+    /*! @} */
+
+    /*! \defgroup st System Timer access
+      Allows access to and delays using the System Timer Counter.
+      @{
+    */
+
+    /*! Read the System Timer Counter register.
+      \return the value read from the System Timer Counter Lower 32 bits register
+    */
+    extern uint64_t bcm2835_st_read(void);
+
+    /*! Delays for the specified number of microseconds with offset.
+      \param[in] offset_micros Offset in microseconds
+      \param[in] micros Delay in microseconds
+    */
+    extern void bcm2835_st_delay(uint64_t offset_micros, uint64_t micros);
+
+    /*! @}  */
+
+    /*! \defgroup pwm Pulse Width Modulation
+      Allows control of 2 independent PWM channels. A limited subset of GPIO pins
+      can be connected to one of these 2 channels, allowing PWM control of GPIO pins.
+      You have to set the desired pin into a particular Alt Fun to PWM output. See the PWM
+      documentation on the Main Page.
+      @{
+    */
+
+    /*! Sets the PWM clock divisor, 
+      to control the basic PWM pulse widths.
+      \param[in] divisor Divides the basic 19.2MHz PWM clock. You can use one of the common
+      values BCM2835_PWM_CLOCK_DIVIDER_* in \ref bcm2835PWMClockDivider
+    */
+    extern void bcm2835_pwm_set_clock(uint32_t divisor);
+    
+    /*! Sets the mode of the given PWM channel,
+      allowing you to control the PWM mode and enable/disable that channel
+      \param[in] channel The PWM channel. 0 or 1.
+      \param[in] markspace Set true if you want Mark-Space mode. 0 for Balanced mode.
+      \param[in] enabled Set true to enable this channel and produce PWM pulses.
+    */
+    extern void bcm2835_pwm_set_mode(uint8_t channel, uint8_t markspace, uint8_t enabled);
+
+    /*! Sets the maximum range of the PWM output.
+      The data value can vary between 0 and this range to control PWM output
+      \param[in] channel The PWM channel. 0 or 1.
+      \param[in] range The maximum value permitted for DATA.
+    */
+    extern void bcm2835_pwm_set_range(uint8_t channel, uint32_t range);
+    
+    /*! Sets the PWM pulse ratio to emit to DATA/RANGE, 
+      where RANGE is set by bcm2835_pwm_set_range().
+      \param[in] channel The PWM channel. 0 or 1.
+      \param[in] data Controls the PWM output ratio as a fraction of the range. 
+      Can vary from 0 to RANGE.
+    */
+    extern void bcm2835_pwm_set_data(uint8_t channel, uint32_t data);
+
+    /*! @}  */
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BCM2835_H */
+
+/*! @example blink.c
+  Blinks RPi GPIO pin 11 on and off
+*/
+
+/*! @example input.c
+  Reads the state of an RPi input pin
+*/
+
+/*! @example event.c
+  Shows how to use event detection on an input pin
+*/
+
+/*! @example spi.c
+  Shows how to use SPI interface to transfer a byte to and from an SPI device
+*/
+
+/*! @example spin.c
+  Shows how to use SPI interface to transfer a number of bytes to and from an SPI device
+*/
+
+/*! @example pwm.c
+  Shows how to use PWM to control GPIO pins
+*/
+
+/*! @example i2c.c
+Command line utility for executing i2c commands with the 
+Broadcom bcm2835. Contributed by Shahrooz Shahparnia.
+*/
+
+/*! example gpio.c
+  Command line utility for executing gpio commands with the 
+  Broadcom bcm2835. Contributed by Shahrooz Shahparnia.
+*/
+
+/*! example spimem_test.c
+  Shows how to use the included little library (spiram.c and spiram.h)
+  to read and write SPI RAM chips such as 23K256-I/P
+*/
diff --git a/arduino/libraries/RF24/utility/RPi/compatibility.cpp 
b/arduino/libraries/RF24/utility/RPi/compatibility.cpp
new file mode 100644
index 000000000..bca533b7c
--- /dev/null
+++ b/arduino/libraries/RF24/utility/RPi/compatibility.cpp
@@ -0,0 +1,11 @@
+#include "compatibility.h"
+#include <chrono>
+
+auto start = std::chrono::steady_clock::now();
+
+uint32_t millis(void)
+{
+    auto end = std::chrono::steady_clock::now();
+       
+       return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
+}
diff --git a/arduino/libraries/RF24/utility/RPi/compatibility.h 
b/arduino/libraries/RF24/utility/RPi/compatibility.h
new file mode 100644
index 000000000..00fa53151
--- /dev/null
+++ b/arduino/libraries/RF24/utility/RPi/compatibility.h
@@ -0,0 +1,17 @@
+#ifndef COMPATIBLITY_H
+#define COMPATIBLITY_H
+
+#include <sys/time.h>
+#include <stdint.h>
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+extern uint32_t millis(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif //Compatibility.h
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/RPi/includes.h b/arduino/libraries/RF24/utility/RPi/includes.h
new file mode 100644
index 000000000..7d84a9480
--- /dev/null
+++ b/arduino/libraries/RF24/utility/RPi/includes.h
@@ -0,0 +1,10 @@
+#ifndef __RF24_INCLUDES_H__
+#define __RF24_INCLUDES_H__
+
+#define RF24_RPi
+
+#include "RPi/bcm2835.h"
+#include "RPi/RF24_arch_config.h"
+#include "RPi/interrupt.h"
+
+#endif
diff --git a/arduino/libraries/RF24/utility/RPi/interrupt.cpp 
b/arduino/libraries/RF24/utility/RPi/interrupt.cpp
new file mode 100644
index 000000000..b942f63f0
--- /dev/null
+++ b/arduino/libraries/RF24/utility/RPi/interrupt.cpp
@@ -0,0 +1,221 @@
+/*
+Interrupts functions extruded from wiringPi library by Oitzu.
+
+wiringPi Copyright (c) 2012 Gordon Henderson
+https://projects.drogon.net/raspberry-pi/wiringpi
+wiringPi is free software: GNU Lesser General Public License
+see <http://www.gnu.org/licenses/>
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <poll.h>
+#include <sys/stat.h>
+#include "interrupt.h"
+#include <pthread.h>
+
+#define delay(x) bcm2835_delay(x)
+
+static pthread_mutex_t pinMutex = PTHREAD_MUTEX_INITIALIZER;
+static volatile int pinPass = -1;
+
+pthread_t threadId[64];
+
+// sysFds:
+//      Map a file descriptor from the /sys/class/gpio/gpioX/value
+static int sysFds[64] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
-1, -1, -1, -1, -1, -1, -1, -1,
+                         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
-1, -1, -1, -1, -1, -1, -1, -1,
+                         -1, -1, -1, -1, -1, -1,};
+
+// ISR Data
+static void (* isrFunctions[64])(void);
+
+int waitForInterrupt(int pin, int mS)
+{
+    int fd, x;
+    uint8_t c;
+    struct pollfd polls;
+
+    if ((fd = sysFds[pin]) == -1) {
+        return -2;
+    }
+
+    // Setup poll structure
+
+    polls.fd = fd;
+    polls.events = POLLPRI;      // Urgent data!
+
+    // Wait for it ...
+    x = poll(&polls, 1, mS);
+
+    // Do a dummy read to clear the interrupt
+    //      A one character read appars to be enough.
+    //      Followed by a seek to reset it.
+
+    (void) (read(fd, &c, 1) + 1);
+    lseek(fd, 0, SEEK_SET);
+
+    return x;
+}
+
+int piHiPri(const int pri)
+{
+    struct sched_param sched;
+
+    memset(&sched, 0, sizeof(sched));
+
+    if (pri > sched_get_priority_max(SCHED_RR)) {
+        sched.sched_priority = sched_get_priority_max(SCHED_RR);
+    } else {
+        sched.sched_priority = pri;
+    }
+
+    return sched_setscheduler(0, SCHED_RR, &sched);
+}
+
+void* interruptHandler(void* arg)
+{
+    int myPin;
+
+    (void) piHiPri(55);  // Only effective if we run as root
+
+    myPin = pinPass;
+    pinPass = -1;
+
+    for (;;) {
+        if (waitForInterrupt(myPin, -1) > 0) {
+            pthread_mutex_lock(&pinMutex);
+            isrFunctions[myPin]();
+            pthread_mutex_unlock(&pinMutex);
+            pthread_testcancel(); //Cancel at this point if we have an cancellation request.
+        }
+    }
+
+    return NULL;
+}
+
+int attachInterrupt(int pin, int mode, void (* function)(void))
+{
+    const char* modeS;
+    char fName[64];
+    char pinS[8];
+    pid_t pid;
+    int count, i;
+    char c;
+    int bcmGpioPin;
+
+    bcmGpioPin = pin;
+
+    if (mode != INT_EDGE_SETUP) {
+        /**/ if (mode == INT_EDGE_FALLING) {
+            modeS = "falling";
+        } else if (mode == INT_EDGE_RISING) {
+            modeS = "rising";
+        } else {
+            modeS = "both";
+        }
+
+        sprintf(pinS, "%d", bcmGpioPin);
+
+        if ((pid = fork()) < 0) {    // Fail
+            return printf("wiringPiISR: fork failed: %s\n", strerror(errno));
+        }
+
+        if (pid == 0)       // Child, exec
+        {
+            /**/ if (access("/usr/local/bin/gpio", X_OK) == 0) {
+                execl("/usr/local/bin/gpio", "gpio", "edge", pinS, modeS, (char*) NULL);
+                return printf("wiringPiISR: execl failed: %s\n", strerror(errno));
+            } else if (access("/usr/bin/gpio", X_OK) == 0) {
+                execl("/usr/bin/gpio", "gpio", "edge", pinS, modeS, (char*) NULL);
+                return printf("wiringPiISR: execl failed: %s\n", strerror(errno));
+            } else {
+                return printf("wiringPiISR: Can't find gpio program\n");
+            }
+        } else {                // Parent, wait
+            wait(NULL);
+        }
+    }
+
+    if (sysFds[bcmGpioPin] == -1) {
+        sprintf(fName, "/sys/class/gpio/gpio%d/value", bcmGpioPin);
+        if ((sysFds[bcmGpioPin] = open(fName, O_RDWR)) < 0) {
+            return printf("wiringPiISR: unable to open %s: %s\n", fName, strerror(errno));
+        }
+    }
+
+    ioctl(sysFds[bcmGpioPin], FIONREAD, &count);
+    for (i = 0; i < count; ++i) {
+        (void) (read(sysFds[bcmGpioPin], &c, 1) + 1);
+    }
+
+    isrFunctions[pin] = function;
+
+    pthread_mutex_lock(&pinMutex);
+    pinPass = pin;
+    pthread_create(&threadId[bcmGpioPin], NULL, interruptHandler, NULL);
+    while (pinPass != -1)
+        delay (1);
+    pthread_mutex_unlock(&pinMutex);
+
+    return 0;
+}
+
+int detachInterrupt(int pin)
+{
+    char pinS[8];
+    const char* modeS = "none";
+    pid_t pid;
+
+    if (pthread_cancel(threadId[pin]) != 0) //Cancel the thread
+    {
+        return 0;
+    }
+
+    if (close(sysFds[pin]) != 0) //Close filehandle
+    {
+        return 0;
+    }
+
+    /* Set wiringPi to 'none' interrupt mode */
+
+    sprintf(pinS, "%d", pin);
+
+    if ((pid = fork()) < 0) {    // Fail
+        return printf("wiringPiISR: fork failed: %s\n", strerror(errno));
+    }
+
+    if (pid == 0)       // Child, exec
+    {
+        /**/ if (access("/usr/local/bin/gpio", X_OK) == 0) {
+            execl("/usr/local/bin/gpio", "gpio", "edge", pinS, modeS, (char*) NULL);
+            return printf("wiringPiISR: execl failed: %s\n", strerror(errno));
+        } else if (access("/usr/bin/gpio", X_OK) == 0) {
+            execl("/usr/bin/gpio", "gpio", "edge", pinS, modeS, (char*) NULL);
+            return printf("wiringPiISR: execl failed: %s\n", strerror(errno));
+        } else {
+            return printf("wiringPiISR: Can't find gpio program\n");
+        }
+    } else {                // Parent, wait
+        wait(NULL);
+    }
+
+    return 1;
+}
+
+void rfNoInterrupts()
+{
+    pthread_mutex_lock(&pinMutex);
+}
+
+void rfInterrupts()
+{
+    pthread_mutex_unlock(&pinMutex);
+}
diff --git a/arduino/libraries/RF24/utility/RPi/interrupt.h b/arduino/libraries/RF24/utility/RPi/interrupt.h
new file mode 100644
index 000000000..e76d34e7d
--- /dev/null
+++ b/arduino/libraries/RF24/utility/RPi/interrupt.h
@@ -0,0 +1,72 @@
+/*
+Interrupts functions extruded from wiringPi library by Oitzu.
+
+wiringPi Copyright (c) 2012 Gordon Henderson
+https://projects.drogon.net/raspberry-pi/wiringpi
+wiringPi is free software: GNU Lesser General Public License
+see <http://www.gnu.org/licenses/>
+*/
+
+#include "RF24_arch_config.h"
+
+#define INT_EDGE_SETUP          0
+#define INT_EDGE_FALLING        1
+#define INT_EDGE_RISING         2
+#define INT_EDGE_BOTH           3
+
+/*
+ * interruptHandler:
+ *      This is a thread and gets started to wait for the interrupt we're
+ *      hoping to catch. It will call the user-function when the interrupt
+ *      fires.
+ *********************************************************************************
+ */
+void* interruptHandler(void* arg);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * waitForInterrupt:
+ *      Pi Specific.
+ *      Wait for Interrupt on a GPIO pin.
+ *      This is actually done via the /sys/class/gpio interface regardless of
+ *      the wiringPi access mode in-use. Maybe sometime it might get a better
+ *      way for a bit more efficiency.
+ *********************************************************************************
+ */
+extern int waitForInterrupt(int pin, int mS);
+
+/*
+ * piHiPri:
+ *      Attempt to set a high priority schedulling for the running program
+ *********************************************************************************
+ */
+extern int piHiPri(const int pri);
+
+/*
+ * attachInterrupt (Original: wiringPiISR):
+ *      Pi Specific.
+ *      Take the details and create an interrupt handler that will do a call-
+ *      back to the user supplied function.
+ *********************************************************************************
+ */
+extern int attachInterrupt(int pin, int mode, void (* function)(void));
+
+/*
+ * detachInterrupt:
+ *      Pi Specific detachInterrupt.
+ *      Will cancel the interrupt thread, close the filehandle and 
+ *             setting wiringPi back to 'none' mode.
+ *********************************************************************************
+ */
+extern int detachInterrupt(int pin);
+
+extern void rfNoInterrupts();
+
+extern void rfInterrupts();
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/RPi/spi.cpp b/arduino/libraries/RF24/utility/RPi/spi.cpp
new file mode 100644
index 000000000..5e5dfc9eb
--- /dev/null
+++ b/arduino/libraries/RF24/utility/RPi/spi.cpp
@@ -0,0 +1,66 @@
+#include "spi.h"
+#include <pthread.h>
+#include <unistd.h>
+#include <stdexcept>
+
+static pthread_mutex_t spiMutex = PTHREAD_MUTEX_INITIALIZER;
+bool bcmIsInitialized = false;
+
+SPI::SPI()
+{
+
+}
+
+void SPI::begin(int busNo, uint32_t spi_speed)
+{
+    if (!bcmIsInitialized) {
+        if (!bcm2835_init()) {
+            return;
+        }
+    }
+    bcmIsInitialized = true;
+    bcm2835_spi_begin();
+}
+
+void SPI::beginTransaction(SPISettings settings)
+{
+    if (geteuid() != 0) {
+        throw std::runtime_error("Process should run as root");
+    }
+    pthread_mutex_lock(&spiMutex);
+    setBitOrder(settings.border);
+    setDataMode(settings.dmode);
+    setClockDivider(settings.clck);
+}
+
+void SPI::endTransaction()
+{
+    pthread_mutex_unlock(&spiMutex);
+}
+
+void SPI::setBitOrder(uint8_t bit_order)
+{
+    bcm2835_spi_setBitOrder(bit_order);
+}
+
+void SPI::setDataMode(uint8_t data_mode)
+{
+    bcm2835_spi_setDataMode(data_mode);
+}
+
+void SPI::setClockDivider(uint32_t spi_speed)
+{
+    //bcm2835_spi_setClockDivider(spi_speed);
+    bcm2835_spi_set_speed_hz(spi_speed);
+}
+
+void SPI::chipSelect(int csn_pin)
+{
+    bcm2835_spi_chipSelect(csn_pin);
+    delayMicroseconds(5);
+}
+
+SPI::~SPI()
+{
+
+}
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/RPi/spi.h b/arduino/libraries/RF24/utility/RPi/spi.h
new file mode 100644
index 000000000..83864bfba
--- /dev/null
+++ b/arduino/libraries/RF24/utility/RPi/spi.h
@@ -0,0 +1,100 @@
+/*
+ * TMRh20 2015
+ * SPI layer for RF24 <-> BCM2835
+ */
+/**
+ * @file spi.h
+ * \cond HIDDEN_SYMBOLS
+ * Class declaration for SPI helper files
+ */
+#ifndef _SPI_H_INCLUDED
+#define _SPI_H_INCLUDED
+
+#include <stdio.h>
+#include "bcm2835.h"
+#include "interrupt.h"
+#include "../../RF24_config.h"
+
+#define SPI_HAS_TRANSACTION
+#define MSBFIRST BCM2835_SPI_BIT_ORDER_MSBFIRST
+#define SPI_MODE0 BCM2835_SPI_MODE0
+//#define RF24_SPI_SPEED 10000000 //BCM2835_SPI_SPEED_4MHZ
+
+class SPISettings {
+public:
+    SPISettings(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
+    {
+        init(clock, bitOrder, dataMode);
+    }
+
+    SPISettings()
+    {
+        init(RF24_SPI_SPEED, MSBFIRST, SPI_MODE0);
+    }
+
+    uint32_t clck;
+    uint8_t border;
+    uint8_t dmode;
+private:
+
+    void init(uint32_t clock, uint8_t bitOrder, uint8_t dataMode)
+    {
+        clck = clock;
+        border = bitOrder;
+        dmode = dataMode;
+    }
+
+    friend class SPIClass;
+};
+
+class SPI {
+public:
+
+    SPI();
+
+    virtual ~SPI();
+
+    inline static uint8_t transfer(uint8_t _data);
+
+    inline static void transfernb(char* tbuf, char* rbuf, uint32_t len);
+
+    inline static void transfern(char* buf, uint32_t len);
+
+    static void begin(int busNo, uint32_t spi_speed = RF24_SPI_SPEED);
+
+    static void end();
+
+    static void setBitOrder(uint8_t bit_order);
+
+    static void setDataMode(uint8_t data_mode);
+
+    static void setClockDivider(uint32_t spi_speed);
+
+    static void chipSelect(int csn_pin);
+
+    static void beginTransaction(SPISettings settings);
+
+    static void endTransaction();
+
+
+};
+
+uint8_t SPI::transfer(uint8_t _data)
+{
+    uint8_t data = bcm2835_spi_transfer(_data);
+    return data;
+}
+
+void SPI::transfernb(char* tbuf, char* rbuf, uint32_t len)
+{
+    bcm2835_spi_transfernb(tbuf, rbuf, len);
+}
+
+void SPI::transfern(char* buf, uint32_t len)
+{
+    transfernb(buf, buf, len);
+}
+/**
+ * \endcond
+ */
+#endif
diff --git a/arduino/libraries/RF24/utility/SPIDEV/RF24_arch_config.h 
b/arduino/libraries/RF24/utility/SPIDEV/RF24_arch_config.h
new file mode 100644
index 000000000..0e967dc38
--- /dev/null
+++ b/arduino/libraries/RF24/utility/SPIDEV/RF24_arch_config.h
@@ -0,0 +1,68 @@
+/*
+ Copyright (C) 2011 J. Coliz <maniacbug ymail com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+
+ */
+#ifndef __ARCH_CONFIG_H__
+#define __ARCH_CONFIG_H__
+
+#define RF24_LINUX
+
+#include <stddef.h>
+#include "spi.h"
+#include "gpio.h"
+#include "compatibility.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <sys/time.h>
+
+//#define RF24_SPI_SPEED RF24_SPIDEV_SPEED
+
+#define _BV(x) (1<<(x))
+#define _SPI spi
+
+//#undef SERIAL_DEBUG
+#ifdef SERIAL_DEBUG
+    #define IF_SERIAL_DEBUG(x) ({x;})
+#else
+    #define IF_SERIAL_DEBUG(x)
+#endif
+
+// Avoid spurious warnings
+#if 1
+    #if !defined( NATIVE ) && defined( ARDUINO )
+        #undef PROGMEM
+        #define PROGMEM __attribute__(( section(".progmem.data") ))
+        #undef PSTR
+        #define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))
+    #endif
+#endif
+
+typedef uint16_t prog_uint16_t;
+#define PSTR(x) (x)
+#define printf_P printf
+#define strlen_P strlen
+#define PROGMEM
+#define pgm_read_word(p) (*(p))
+#define PRIPSTR "%s"
+#define pgm_read_byte(p) (*(p))
+#define pgm_read_ptr(p) (*(p))
+
+// Function, constant map as a result of migrating from Arduino
+#define LOW GPIO::OUTPUT_LOW
+#define HIGH GPIO::OUTPUT_HIGH
+#define INPUT GPIO::DIRECTION_IN
+#define OUTPUT GPIO::DIRECTION_OUT
+#define digitalWrite(pin, value) GPIO::write(pin, value)
+#define pinMode(pin, direction) GPIO::open(pin, direction)
+#define delay(milisec) __msleep(milisec)
+#define delayMicroseconds(usec) __usleep(usec)
+#define millis() __millis()
+
+#endif // __ARCH_CONFIG_H__
+// vim:ai:cin:sts=2 sw=2 ft=cpp
diff --git a/arduino/libraries/RF24/utility/SPIDEV/compatibility.cpp 
b/arduino/libraries/RF24/utility/SPIDEV/compatibility.cpp
new file mode 100644
index 000000000..6ba649233
--- /dev/null
+++ b/arduino/libraries/RF24/utility/SPIDEV/compatibility.cpp
@@ -0,0 +1,50 @@
+#include "compatibility.h"
+
+long long mtime, seconds, useconds;
+//static struct timeval start, end;
+//struct timespec start, end;
+#include <time.h>
+#include <chrono>
+/**********************************************************************/
+/**
+ * This function is added in order to simulate arduino delay() function
+ * @param milisec
+ */
+void __msleep(int milisec)
+{
+    struct timespec req;// = {0};
+    req.tv_sec = (time_t) milisec / 1000;
+    req.tv_nsec = (milisec % 1000) * 1000000L;
+    //nanosleep(&req, (struct timespec *)NULL);
+    clock_nanosleep(CLOCK_REALTIME, 0, &req, NULL);
+}
+
+void __usleep(int microsec)
+{
+    struct timespec req;// = {0};
+    req.tv_sec = (time_t) microsec / 1000000;
+    req.tv_nsec = (microsec / 1000000) * 1000;
+    //nanosleep(&req, (struct timespec *)NULL);
+    clock_nanosleep(CLOCK_REALTIME, 0, &req, NULL);
+}
+
+/**
+ * This function is added in order to simulate arduino millis() function
+ */
+
+bool timerStarted = false;
+
+void __start_timer()
+{
+
+}
+
+auto start = std::chrono::steady_clock::now();
+
+uint32_t __millis()
+{
+
+       auto end = std::chrono::steady_clock::now();
+
+       return std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
+}
diff --git a/arduino/libraries/RF24/utility/SPIDEV/compatibility.h 
b/arduino/libraries/RF24/utility/SPIDEV/compatibility.h
new file mode 100644
index 000000000..2a2167c85
--- /dev/null
+++ b/arduino/libraries/RF24/utility/SPIDEV/compatibility.h
@@ -0,0 +1,34 @@
+/*
+ * File:   compatiblity.h
+ * Author: purinda
+ *
+ * Created on 24 June 2012, 3:08 PM
+ * patch for safer monotonic clock & millis() correction for 64bit LDV 2018
+ */
+
+#ifndef COMPATIBLITY_H
+#define COMPATIBLITY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stdint.h>  // for uintXX_t types
+#include <stddef.h>
+#include <time.h>
+#include <sys/time.h>
+
+void __msleep(int milisec);
+
+void __usleep(int milisec);
+
+void __start_timer();
+
+uint32_t __millis();
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif    /* COMPATIBLITY_H */
+
diff --git a/arduino/libraries/RF24/utility/SPIDEV/gpio.cpp b/arduino/libraries/RF24/utility/SPIDEV/gpio.cpp
new file mode 100644
index 000000000..1745a7b8b
--- /dev/null
+++ b/arduino/libraries/RF24/utility/SPIDEV/gpio.cpp
@@ -0,0 +1,167 @@
+/*
+ * https://github.com/mrshu/GPIOlib
+ * Copyright (c) 2011, Copyright (c) 2011 mr.Shu
+ * All rights reserved.
+ *
+ * Modified on 24 June 2012, 11:06 AM
+ * File:   gpio.cpp
+ * Author: purinda (purinda gmail com)
+ *
+ *  Patched for filedescriptor catching and error control by L Diaz 2018
+ */
+
+#include "gpio.h"
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+std::map<int, GPIOfdCache_t> GPIO::cache;
+
+GPIO::GPIO()
+{
+}
+
+GPIO::~GPIO()
+{
+}
+
+void GPIO::open(int port, int DDR)
+{
+    FILE* f;
+    f = fopen("/sys/class/gpio/export", "w");
+    if (f == NULL) {
+        throw GPIOException("can't export GPIO pin .check access rights");
+    }
+    fprintf(f, "%d\n", port);
+    fclose(f);
+
+    int counter = 0;
+    char file[128];
+    sprintf(file, "/sys/class/gpio/gpio%d/direction", port);
+
+    while ((f = fopen(file, "w")) == NULL) { //Wait 10 seconds for the file to be accessible if not open on 
first attempt
+        sleep(1);
+        counter++;
+        if (counter > 10) {
+            throw GPIOException("can't access /sys/class/gpio/gpio%d/direction GPIO pin. check access 
rights");
+            /*perror("Could not open /sys/class/gpio/gpio%d/direction");
+            exit(0); */
+        }
+    }
+    int l = (DDR == 0) ? fprintf(f, "in\n") : fprintf(f, "out\n");
+    if (!(l == 3 || l == 4)) {
+        fclose(f);
+        throw GPIOException("can't set direction on GPIO pin. check access rights");
+    }
+    /*
+    if (DDR == 0)
+          fprintf(f, "in\n");
+    else  printf(f, "out\n");
+  */
+    fclose(f);
+
+    // Caches the GPIO descriptor;
+    sprintf(file, "/sys/class/gpio/gpio%d/value", port);
+    int flags = (DDR == 0) ? O_RDONLY : O_WRONLY;
+    int fd = ::open(file, flags);
+    if (fd < 0) {
+        throw GPIOException("Can't open the GPIO");
+    } else {
+        cache[port] = fd;  // cache the fd;
+        lseek(fd, SEEK_SET, 0);
+    }
+
+}
+
+void GPIO::close(int port)
+{
+    std::map<int, GPIOfdCache_t>::iterator i;
+    i = cache.find(port);
+    if (i != cache.end()) {
+        close(i->second); // close the cached fd
+        cache.erase(i); // Delete cache entry
+    }
+    // Do unexport
+    FILE* f;
+    f = fopen("/sys/class/gpio/unexport", "w");
+    if (f != NULL) {
+        fprintf(f, "%d\n", port);
+        fclose(f);
+    }
+}
+
+int GPIO::read(int port)
+{
+    std::map<int, GPIOfdCache_t>::iterator i;
+    int fd;
+    i = cache.find(port);
+    if (i == cache.end()) { // Fallback to open the gpio
+        GPIO::open(port, GPIO::DIRECTION_IN);
+        i = cache.find(port);
+        if (i == cache.end()) {
+            throw GPIOException("can't access to GPIO");
+        } else {
+            fd = i->second;
+        }
+    } else {
+        fd = i->second;
+    }
+
+    char c;
+    if (lseek(fd, 0, SEEK_SET) == 0 && ::read(fd, &c, 1) == 1) {
+        return (c == '0') ? 0 : 1;
+    } else {
+        throw GPIOException("can't access to GPIO");
+    }
+
+    /*FILE *f;
+
+    char file[128];
+    sprintf(file, "/sys/class/gpio/gpio%d/value", port);
+    f = fopen(file, "r");
+
+    int i;
+    fscanf(f, "%d", &i);
+    fclose(f);
+    return i;
+*/
+}
+
+void GPIO::write(int port, int value)
+{
+    std::map<int, GPIOfdCache_t>::iterator i;
+    int fd;
+    i = cache.find(port);
+    if (i == cache.end()) { // Fallback to open the gpio
+        GPIO::open(port, GPIO::DIRECTION_OUT);
+        i = cache.find(port);
+        if (i == cache.end()) {
+            throw GPIOException("can't access to GPIO");
+        } else {
+            fd = i->second;
+        }
+    } else {
+        fd = i->second;
+    }
+
+    if (lseek(fd, 0, SEEK_SET) != 0) {
+        throw GPIOException("can't access to GPIO");
+    }
+    int l = (value == 0) ? ::write(fd, "0\n", 2) : ::write(fd, "1\n", 2);
+    if (l != 2) {
+        throw GPIOException("can't access to GPIO");
+    }
+
+    /*FILE *f;
+
+    char file[128];
+    sprintf(file, "/sys/class/gpio/gpio%d/value", port);
+    f = fopen(file, "w");
+
+    if (value == 0)    fprintf(f, "0\n");
+    else               fprintf(f, "1\n");
+
+    fclose(f);*/
+}
diff --git a/arduino/libraries/RF24/utility/SPIDEV/gpio.h b/arduino/libraries/RF24/utility/SPIDEV/gpio.h
new file mode 100644
index 000000000..cc5a7b208
--- /dev/null
+++ b/arduino/libraries/RF24/utility/SPIDEV/gpio.h
@@ -0,0 +1,94 @@
+/*
+ * https://github.com/mrshu/GPIOlib
+ * Copyright (c) 2011, Copyright (c) 2011 mr.Shu
+ * All rights reserved.
+ *
+ * Modified on 24 June 2012, 11:06 AM
+ * File:   gpio.h
+ * Author: purinda (purinda gmail com)
+ *
+ */
+
+#ifndef H
+#define H
+
+#include <cstdio>
+#include <map>
+#include <stdexcept>
+
+/** Specific excpetion for SPI errors */
+class GPIOException : public std::runtime_error {
+public:
+    explicit GPIOException(const std::string& msg)
+            :std::runtime_error(msg)
+    {
+    }
+};
+
+/**
+ * @file gpio.h
+ * @cond HIDDEN_SYMBOLS
+ * Class declaration for GPIO helper files
+ */
+
+
+/**
+ * Example GPIO.h file
+ *
+ * @defgroup GPIO GPIO Example
+ *
+ * See RF24_arch_config.h for additional information
+ * @{
+ */
+
+typedef int GPIOfdCache_t;
+
+class GPIO {
+public:
+
+    /* Constants */
+    static const int DIRECTION_OUT = 1;
+    static const int DIRECTION_IN = 0;
+
+    static const int OUTPUT_HIGH = 1;
+    static const int OUTPUT_LOW = 0;
+
+    GPIO();
+
+    /**
+     * Similar to Arduino pinMode(pin,mode);
+     * @param port
+     * @param DDR
+     */
+    static void open(int port, int DDR);
+
+    /**
+     *
+     * @param port
+     */
+    static void close(int port);
+
+    /**
+     * Similar to Arduino digitalRead(pin);
+     * @param port
+     */
+    static int read(int port);
+
+    /**
+    * Similar to Arduino digitalWrite(pin,state);
+    * @param port
+    * @param value
+    */
+    static void write(int port, int value);
+
+    virtual ~GPIO();
+
+private:
+    /* fd cache */
+    static std::map<int, GPIOfdCache_t> cache;
+};
+/**
+ * @endcond
+ */
+/**@}*/
+#endif    /* H */
diff --git a/arduino/libraries/RF24/utility/SPIDEV/includes.h 
b/arduino/libraries/RF24/utility/SPIDEV/includes.h
new file mode 100644
index 000000000..bc9008c28
--- /dev/null
+++ b/arduino/libraries/RF24/utility/SPIDEV/includes.h
@@ -0,0 +1,9 @@
+#ifndef __RF24_INCLUDES_H__
+#define __RF24_INCLUDES_H__
+
+#define RF24_SPIDEV
+
+#include "SPIDEV/RF24_arch_config.h"
+#include "SPIDEV/interrupt.h"
+
+#endif
diff --git a/arduino/libraries/RF24/utility/SPIDEV/interrupt.cpp 
b/arduino/libraries/RF24/utility/SPIDEV/interrupt.cpp
new file mode 100644
index 000000000..4411bbe9a
--- /dev/null
+++ b/arduino/libraries/RF24/utility/SPIDEV/interrupt.cpp
@@ -0,0 +1,221 @@
+/*
+Interrupts functions extruded from wiringPi library by Oitzu.
+
+wiringPi Copyright (c) 2012 Gordon Henderson
+https://projects.drogon.net/raspberry-pi/wiringpi
+wiringPi is free software: GNU Lesser General Public License
+see <http://www.gnu.org/licenses/>
+*/
+
+#include <stdio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <poll.h>
+#include <sys/stat.h>
+#include "interrupt.h"
+#include <pthread.h>
+
+//#define delay(x) bcm2835_delay(x)
+
+static pthread_mutex_t pinMutex = PTHREAD_MUTEX_INITIALIZER;
+static volatile int pinPass = -1;
+
+pthread_t threadId[64];
+
+// sysFds:
+//      Map a file descriptor from the /sys/class/gpio/gpioX/value
+static int sysFds[64] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
-1, -1, -1, -1, -1, -1, -1, -1,
+                         -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 
-1, -1, -1, -1, -1, -1, -1, -1,
+                         -1, -1, -1, -1, -1, -1,};
+
+// ISR Data
+static void (* isrFunctions[64])(void);
+
+int waitForInterrupt(int pin, int mS)
+{
+    int fd, x;
+    uint8_t c;
+    struct pollfd polls;
+
+    if ((fd = sysFds[pin]) == -1) {
+        return -2;
+    }
+
+    // Setup poll structure
+
+    polls.fd = fd;
+    polls.events = POLLPRI;      // Urgent data!
+
+    // Wait for it ...
+    x = poll(&polls, 1, mS);
+
+    // Do a dummy read to clear the interrupt
+    //      A one character read appars to be enough.
+    //      Followed by a seek to reset it.
+
+    (void) (read(fd, &c, 1) + 1);
+    lseek(fd, 0, SEEK_SET);
+
+    return x;
+}
+
+int piHiPri(const int pri)
+{
+    struct sched_param sched;
+
+    memset(&sched, 0, sizeof(sched));
+
+    if (pri > sched_get_priority_max(SCHED_RR)) {
+        sched.sched_priority = sched_get_priority_max(SCHED_RR);
+    } else {
+        sched.sched_priority = pri;
+    }
+
+    return sched_setscheduler(0, SCHED_RR, &sched);
+}
+
+void* interruptHandler(void* arg)
+{
+    int myPin;
+
+    (void) piHiPri(55);  // Only effective if we run as root
+
+    myPin = pinPass;
+    pinPass = -1;
+
+    for (;;) {
+        if (waitForInterrupt(myPin, -1) > 0) {
+            pthread_mutex_lock(&pinMutex);
+            isrFunctions[myPin]();
+            pthread_mutex_unlock(&pinMutex);
+            pthread_testcancel(); //Cancel at this point if we have an cancellation request.
+        }
+    }
+
+    return NULL;
+}
+
+int attachInterrupt(int pin, int mode, void (* function)(void))
+{
+    const char* modeS;
+    char fName[64];
+    char pinS[8];
+    pid_t pid;
+    int count, i;
+    char c;
+    int bcmGpioPin;
+
+    bcmGpioPin = pin;
+
+    if (mode != INT_EDGE_SETUP) {
+        /**/ if (mode == INT_EDGE_FALLING) {
+            modeS = "falling";
+        } else if (mode == INT_EDGE_RISING) {
+            modeS = "rising";
+        } else {
+            modeS = "both";
+        }
+
+        sprintf(pinS, "%d", bcmGpioPin);
+
+        if ((pid = fork()) < 0) {    // Fail
+            return printf("wiringPiISR: fork failed: %s\n", strerror(errno));
+        }
+
+        if (pid == 0)       // Child, exec
+        {
+            /**/ if (access("/usr/local/bin/gpio", X_OK) == 0) {
+                execl("/usr/local/bin/gpio", "gpio", "edge", pinS, modeS, (char*) NULL);
+                return printf("wiringPiISR: execl failed: %s\n", strerror(errno));
+            } else if (access("/usr/bin/gpio", X_OK) == 0) {
+                execl("/usr/bin/gpio", "gpio", "edge", pinS, modeS, (char*) NULL);
+                return printf("wiringPiISR: execl failed: %s\n", strerror(errno));
+            } else {
+                return printf("wiringPiISR: Can't find gpio program\n");
+            }
+        } else {                // Parent, wait
+            wait(NULL);
+        }
+    }
+
+    if (sysFds[bcmGpioPin] == -1) {
+        sprintf(fName, "/sys/class/gpio/gpio%d/value", bcmGpioPin);
+        if ((sysFds[bcmGpioPin] = open(fName, O_RDWR)) < 0) {
+            return printf("wiringPiISR: unable to open %s: %s\n", fName, strerror(errno));
+        }
+    }
+
+    ioctl(sysFds[bcmGpioPin], FIONREAD, &count);
+    for (i = 0; i < count; ++i) {
+        (void) (read(sysFds[bcmGpioPin], &c, 1) + 1);
+    }
+
+    isrFunctions[pin] = function;
+
+    pthread_mutex_lock(&pinMutex);
+    pinPass = pin;
+    pthread_create(&threadId[bcmGpioPin], NULL, interruptHandler, NULL);
+    while (pinPass != -1)
+        delay (1);
+    pthread_mutex_unlock(&pinMutex);
+
+    return 0;
+}
+
+int detachInterrupt(int pin)
+{
+    char pinS[8];
+    const char* modeS = "none";
+    pid_t pid;
+
+    if (!pthread_cancel(threadId[pin])) //Cancel the thread
+    {
+        return 0;
+    }
+
+    if (!close(sysFds[pin])) //Close filehandle
+    {
+        return 0;
+    }
+
+    /* Set wiringPi to 'none' interrupt mode */
+
+    sprintf(pinS, "%d", pin);
+
+    if ((pid = fork()) < 0) {    // Fail
+        return printf("wiringPiISR: fork failed: %s\n", strerror(errno));
+    }
+
+    if (pid == 0)       // Child, exec
+    {
+        /**/ if (access("/usr/local/bin/gpio", X_OK) == 0) {
+            execl("/usr/local/bin/gpio", "gpio", "edge", pinS, modeS, (char*) NULL);
+            return printf("wiringPiISR: execl failed: %s\n", strerror(errno));
+        } else if (access("/usr/bin/gpio", X_OK) == 0) {
+            execl("/usr/bin/gpio", "gpio", "edge", pinS, modeS, (char*) NULL);
+            return printf("wiringPiISR: execl failed: %s\n", strerror(errno));
+        } else {
+            return printf("wiringPiISR: Can't find gpio program\n");
+        }
+    } else {                // Parent, wait
+        wait(NULL);
+    }
+
+    return 1;
+}
+
+void rfNoInterrupts()
+{
+    pthread_mutex_lock(&pinMutex);
+}
+
+void rfInterrupts()
+{
+    pthread_mutex_unlock(&pinMutex);
+}
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/SPIDEV/interrupt.h 
b/arduino/libraries/RF24/utility/SPIDEV/interrupt.h
new file mode 100644
index 000000000..e76d34e7d
--- /dev/null
+++ b/arduino/libraries/RF24/utility/SPIDEV/interrupt.h
@@ -0,0 +1,72 @@
+/*
+Interrupts functions extruded from wiringPi library by Oitzu.
+
+wiringPi Copyright (c) 2012 Gordon Henderson
+https://projects.drogon.net/raspberry-pi/wiringpi
+wiringPi is free software: GNU Lesser General Public License
+see <http://www.gnu.org/licenses/>
+*/
+
+#include "RF24_arch_config.h"
+
+#define INT_EDGE_SETUP          0
+#define INT_EDGE_FALLING        1
+#define INT_EDGE_RISING         2
+#define INT_EDGE_BOTH           3
+
+/*
+ * interruptHandler:
+ *      This is a thread and gets started to wait for the interrupt we're
+ *      hoping to catch. It will call the user-function when the interrupt
+ *      fires.
+ *********************************************************************************
+ */
+void* interruptHandler(void* arg);
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * waitForInterrupt:
+ *      Pi Specific.
+ *      Wait for Interrupt on a GPIO pin.
+ *      This is actually done via the /sys/class/gpio interface regardless of
+ *      the wiringPi access mode in-use. Maybe sometime it might get a better
+ *      way for a bit more efficiency.
+ *********************************************************************************
+ */
+extern int waitForInterrupt(int pin, int mS);
+
+/*
+ * piHiPri:
+ *      Attempt to set a high priority schedulling for the running program
+ *********************************************************************************
+ */
+extern int piHiPri(const int pri);
+
+/*
+ * attachInterrupt (Original: wiringPiISR):
+ *      Pi Specific.
+ *      Take the details and create an interrupt handler that will do a call-
+ *      back to the user supplied function.
+ *********************************************************************************
+ */
+extern int attachInterrupt(int pin, int mode, void (* function)(void));
+
+/*
+ * detachInterrupt:
+ *      Pi Specific detachInterrupt.
+ *      Will cancel the interrupt thread, close the filehandle and 
+ *             setting wiringPi back to 'none' mode.
+ *********************************************************************************
+ */
+extern int detachInterrupt(int pin);
+
+extern void rfNoInterrupts();
+
+extern void rfInterrupts();
+
+#ifdef __cplusplus
+}
+#endif
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/SPIDEV/spi.cpp b/arduino/libraries/RF24/utility/SPIDEV/spi.cpp
new file mode 100644
index 000000000..0a08a6a90
--- /dev/null
+++ b/arduino/libraries/RF24/utility/SPIDEV/spi.cpp
@@ -0,0 +1,191 @@
+/*
+ * File:   spi.cpp
+ * Author: Purinda Gunasekara <purinda gmail com>
+ *
+ * Created on 24 June 2012, 11:00 AM
+ *
+ * Patched for exception handling and selectable SPI SPEED by ldiaz 2018.
+ *
+ * Inspired from spidev test in linux kernel documentation
+ * www.kernel.org/doc/Documentation/spi/spidev_test.c
+ */
+
+#include "spi.h"
+
+#include <fcntl.h>
+#include <linux/spi/spidev.h>
+#include <memory.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#define RF24_SPIDEV_BITS 8
+
+SPI::SPI()
+        :fd(-1), _spi_speed(RF24_SPI_SPEED)
+{
+}
+
+
+void SPI::begin(int busNo, uint32_t spi_speed)
+{
+
+    if (this->spiIsInitialized) {
+        return;
+    }
+
+    /* set spidev accordingly to busNo like:
+     * busNo = 23 -> /dev/spidev2.3
+     *
+     * a bit messy but simple
+     * */
+    char device[] = "/dev/spidev0.0";
+    device[11] += (busNo / 10) % 10;
+    device[13] += busNo % 10;
+
+    if (this->fd >= 0) // check whether spi is already open
+    {
+        close(this->fd);
+        this->fd = -1;
+    }
+
+    this->fd = open(device, O_RDWR);
+    if (this->fd < 0) {
+        throw SPIException("can't open device");
+    }
+    /*
+  {
+        perror("can't open device");
+        abort();
+
+  }*/
+    this->spiIsInitialized = true;
+    init(spi_speed);
+}
+
+void SPI::init(uint32_t speed)
+{
+    uint8_t bits = RF24_SPIDEV_BITS;
+    uint8_t mode = 0;
+
+    int ret;
+    /*
+     * spi mode
+     */
+    ret = ioctl(this->fd, SPI_IOC_WR_MODE, &mode);
+    if (ret == -1) {
+        throw SPIException("cant set WR spi mode");
+    }
+    /*{
+        perror("can't set spi mode");
+        abort();
+    }*/
+
+    ret = ioctl(this->fd, SPI_IOC_RD_MODE, &mode);
+    if (ret == -1) {
+        throw SPIException("can't set RD spi mode");
+    }
+    /*{
+        perror("can't set spi mode");
+        abort();
+    }*/
+
+    /*
+     * bits per word
+     */
+    ret = ioctl(this->fd, SPI_IOC_WR_BITS_PER_WORD, &bits);
+    if (ret == -1) {
+        throw SPIException("can't set WR bits per word");
+    }
+    /*{
+        perror("can't set bits per word");
+        abort();
+    }*/
+
+    ret = ioctl(this->fd, SPI_IOC_RD_BITS_PER_WORD, &bits);
+    if (ret == -1) {
+        throw SPIException("can't set RD bits per word");
+    }
+    /*{
+        perror("can't set bits per word");
+        abort();
+    }*/
+    /*
+     * max speed hz
+     */
+    ret = ioctl(this->fd, SPI_IOC_WR_MAX_SPEED_HZ, &speed);
+    if (ret == -1) {
+        throw SPIException("can't WR set max speed hz");
+    }
+    /*{
+        perror("can't set max speed hz");
+        abort();
+    }*/
+
+    ret = ioctl(this->fd, SPI_IOC_RD_MAX_SPEED_HZ, &speed);
+    if (ret == -1) {
+        throw SPIException("can't RD set max speed hz");
+    }
+    /*{
+        perror("can't set max speed hz");
+        abort();
+    }*/
+    _spi_speed = speed;
+}
+
+uint8_t SPI::transfer(uint8_t tx)
+{
+    struct spi_ioc_transfer tr;
+    memset(&tr, 0, sizeof(tr));
+    tr.tx_buf = (unsigned long) &tx;
+    uint8_t rx;
+    tr.rx_buf = (unsigned long) &rx;
+    tr.len = sizeof(tx);
+    tr.speed_hz = _spi_speed; //RF24_SPI_SPEED;
+    tr.delay_usecs = 0;
+    tr.bits_per_word = RF24_SPIDEV_BITS;
+    tr.cs_change = 0;
+
+    int ret;
+    ret = ioctl(this->fd, SPI_IOC_MESSAGE(1), &tr);
+    if (ret < 1) {
+        throw SPIException("can't send spi message");
+    }
+    /*{
+        perror("can't send spi message");
+        abort();
+    }*/
+
+    return rx;
+}
+
+void SPI::transfernb(char* tbuf, char* rbuf, uint32_t len)
+{
+    struct spi_ioc_transfer tr;
+    memset(&tr, 0, sizeof(tr));
+    tr.tx_buf = (unsigned long) tbuf;
+    tr.rx_buf = (unsigned long) rbuf;
+    tr.len = len;
+    tr.speed_hz = _spi_speed; //RF24_SPI_SPEED;
+    tr.delay_usecs = 0;
+    tr.bits_per_word = RF24_SPIDEV_BITS;
+    tr.cs_change = 0;
+
+    int ret;
+    ret = ioctl(this->fd, SPI_IOC_MESSAGE(1), &tr);
+    if (ret < 1) {
+        throw SPIException("can't send spi message");
+    }
+    /*{
+        perror("can't send spi message");
+        abort();
+    }*/
+}
+
+SPI::~SPI()
+{
+    if (this->fd >= 0) {
+        close(this->fd);
+    }
+}
diff --git a/arduino/libraries/RF24/utility/SPIDEV/spi.h b/arduino/libraries/RF24/utility/SPIDEV/spi.h
new file mode 100644
index 000000000..75e503d37
--- /dev/null
+++ b/arduino/libraries/RF24/utility/SPIDEV/spi.h
@@ -0,0 +1,93 @@
+/*
+ * File:   spi.h
+ * Author: Purinda Gunasekara <purinda gmail com>
+ *
+ * Created on 24 June 2012, 11:00 AM
+ */
+
+#ifndef SPI_H
+#define    SPI_H
+
+/**
+ * @file spi.h
+ * \cond HIDDEN_SYMBOLS
+ * Class declaration for SPI helper files
+ */
+
+/**
+* Example GPIO.h file
+*
+* @defgroup SPI SPI Example
+*
+* See RF24_arch_config.h for additional information
+* @{
+*/
+
+#include <inttypes.h>
+#include <stdexcept>
+
+#include "../../RF24_config.h"
+
+/** Specific excpetion for SPI errors */
+class SPIException : public std::runtime_error {
+public:
+    explicit SPIException(const std::string& msg)
+            :std::runtime_error(msg)
+    {
+    }
+};
+
+class SPI {
+
+public:
+
+    /**
+    * SPI constructor
+    */
+    SPI();
+
+    /**
+    * Start SPI
+    */
+    void begin(int busNo, uint32_t spi_speed = RF24_SPI_SPEED);
+
+    /**
+    * Transfer a single byte
+    * @param tx Byte to send
+    * @return Data returned via spi
+    */
+    uint8_t transfer(uint8_t tx);
+
+    /**
+    * Transfer a buffer of data
+    * @param tbuf Transmit buffer
+    * @param rbuf Receive buffer
+    * @param len Length of the data
+    */
+    void transfernb(char* tbuf, char* rbuf, uint32_t len);
+
+    /**
+    * Transfer a buffer of data without an rx buffer
+    * @param buf Pointer to a buffer of data
+    * @param len Length of the data
+    */
+    void transfern(char* buf, uint32_t len)
+    {
+        transfernb(buf, buf, len);
+    }
+
+    ~SPI();
+
+private:
+
+    int fd;
+    uint32_t _spi_speed;
+    bool spiIsInitialized = false;
+    void init(uint32_t spi_speed = RF24_SPI_SPEED);
+};
+
+/**
+ * \endcond
+ */
+/*@}*/
+#endif    /* SPI_H */
diff --git a/arduino/libraries/RF24/utility/Teensy/RF24_arch_config.h 
b/arduino/libraries/RF24/utility/Teensy/RF24_arch_config.h
new file mode 100644
index 000000000..19177a4bc
--- /dev/null
+++ b/arduino/libraries/RF24/utility/Teensy/RF24_arch_config.h
@@ -0,0 +1,30 @@
+
+
+#if ARDUINO < 100
+
+    #include <WProgram.h>
+
+#else
+    #include <Arduino.h>
+#endif
+
+#include <stddef.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <SPI.h>
+
+#define _SPI SPIClass
+#define RF24_SPI_PTR
+
+#define printf Serial.printf
+
+#ifdef SERIAL_DEBUG
+    #define IF_SERIAL_DEBUG(x) ({x;})
+#else
+    #define IF_SERIAL_DEBUG(x)
+#endif
+
+#define PRIPSTR "%s"
diff --git a/arduino/libraries/RF24/utility/Template/RF24_arch_config.h 
b/arduino/libraries/RF24/utility/Template/RF24_arch_config.h
new file mode 100644
index 000000000..50e1b4d25
--- /dev/null
+++ b/arduino/libraries/RF24/utility/Template/RF24_arch_config.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2011 J. Coliz <maniacbug ymail com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ */
+
+/**
+ * @file RF24_arch_config.h
+ * General defines and includes for RF24/Linux
+ */
+
+/**
+ * Example of RF24_arch_config.h for RF24 portability
+ *
+ * @defgroup Porting_General Porting: General
+ *
+ * @{
+ */
+
+
+#ifndef __ARCH_CONFIG_H__
+#define __ARCH_CONFIG_H__
+
+#define RF24_LINUX
+
+#include <stddef.h>
+#include "spi.h"
+#include "gpio.h"
+#include "compatibility.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <sys/time.h>
+
+#define _BV(x) (1 << (x))
+#define _SPI spi
+
+#undef SERIAL_DEBUG
+#ifdef SERIAL_DEBUG
+    #define IF_SERIAL_DEBUG(x) ({x;})
+#else
+    #define IF_SERIAL_DEBUG(x)
+#endif
+
+// Avoid spurious warnings
+#if 1
+    #if !defined(NATIVE) && defined(ARDUINO)
+        #undef PROGMEM
+        #define PROGMEM __attribute__(( section(".progmem.data") ))
+        #undef PSTR
+        #define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))
+    #endif
+#endif
+
+typedef uint16_t prog_uint16_t;
+#define PSTR(x) (x)
+#define printf_P printf
+#define strlen_P strlen
+#define PROGMEM
+#define pgm_read_word(p) (*(p))
+#define PRIPSTR "%s"
+#define pgm_read_byte(p) (*(p))
+
+// Function, constant map as a result of migrating from Arduino
+#define LOW GPIO::OUTPUT_LOW
+#define HIGH GPIO::OUTPUT_HIGH
+#define INPUT GPIO::DIRECTION_IN
+#define OUTPUT GPIO::DIRECTION_OUT
+#define digitalWrite(pin, value) GPIO::write(pin, value)
+#define pinMode(pin, direction) GPIO::open(pin, direction)
+#define delay(milisec) __msleep(milisec)
+#define delayMicroseconds(usec) __usleep(usec)
+#define millis() __millis()
+
+#endif // __ARCH_CONFIG_H__
+
+
+/**@}*/
diff --git a/arduino/libraries/RF24/utility/Template/compatibility.h 
b/arduino/libraries/RF24/utility/Template/compatibility.h
new file mode 100644
index 000000000..6c53a7c88
--- /dev/null
+++ b/arduino/libraries/RF24/utility/Template/compatibility.h
@@ -0,0 +1,46 @@
+/*
+ * File:   compatiblity.h
+ * Author: purinda
+ *
+ * Created on 24 June 2012, 3:08 PM
+ */
+
+/**
+ * @file compatibility.h
+ * Class declaration for SPI helper files
+ */
+
+/**
+ * Example of compatibility.h class declaration for timing functions portability
+ *
+ * @defgroup Porting_Timing Porting: Timing
+ *
+ * @{
+ */
+
+#ifndef COMPATIBLITY_H
+#define COMPATIBLITY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <stddef.h>
+#include <time.h>
+#include <sys/time.h>
+
+void __msleep(int milisec);
+
+void __usleep(int milisec);
+
+void __start_timer();
+
+long __millis();
+
+#ifdef    __cplusplus
+}
+#endif
+
+#endif    /* COMPATIBLITY_H */
+
+/**@}*/
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/Template/gpio.h b/arduino/libraries/RF24/utility/Template/gpio.h
new file mode 100644
index 000000000..1f03a0ab3
--- /dev/null
+++ b/arduino/libraries/RF24/utility/Template/gpio.h
@@ -0,0 +1,65 @@
+
+
+/**
+ * @file gpio.h
+ * Class declaration for SPI helper files
+ */
+
+/**
+ * Example of gpio.h class declaration for GPIO portability
+ *
+ * @defgroup Porting_GPIO Porting: GPIO
+ *
+ * @{
+ */
+
+#include <cstdio>
+
+#ifndef DOXYGEN_FORCED
+// exclude this line from the docs to prevent displaying in the list of classes
+class GPIO
+#endif
+{
+public:
+    /* Constants */
+    static const int DIRECTION_OUT = 1;
+    static const int DIRECTION_IN = 0;
+
+    static const int OUTPUT_HIGH = 1;
+    static const int OUTPUT_LOW = 0;
+
+    GPIO();
+
+    /**
+     * Similar to Arduino pinMode(pin,mode);
+     * @param port
+     * @param DDR
+     */
+    static void open(int port, int DDR);
+
+    /**
+     *
+     * @param port
+     */
+    static void close(int port);
+
+    /**
+     * Similar to Arduino digitalRead(pin);
+     * @param port
+     */
+    static int read(int port);
+
+    /**
+    * Similar to Arduino digitalWrite(pin,state);
+    * @param port
+    * @param value
+    */
+    static void write(int port, int value);
+
+#ifndef DOXYGEN_FORCED
+// exclude this line from the docs to prevent warnings docs generators
+    virtual ~ GPIO();
+#endif
+};
+
+/**@}*/
diff --git a/arduino/libraries/RF24/utility/Template/includes.h 
b/arduino/libraries/RF24/utility/Template/includes.h
new file mode 100644
index 000000000..04c67a7a8
--- /dev/null
+++ b/arduino/libraries/RF24/utility/Template/includes.h
@@ -0,0 +1,29 @@
+/**
+ * @file includes.h
+ * Configuration defines for RF24/Linux
+ */
+
+/**
+ * Example of includes.h for RF24 Linux portability
+ *
+ * @defgroup Porting_Includes Porting: Includes
+ *
+ * @{
+ */
+
+#ifndef __RF24_INCLUDES_H__
+#define __RF24_INCLUDES_H__
+
+/**
+ * Define a specific platform for this configuration
+ */
+#define RF24_BBB
+
+/**
+ * Load the correct configuration for this platform
+ */
+#include "BBB/RF24_arch_config.h"
+
+#endif
+
+/**@}*/
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/Template/spi.h b/arduino/libraries/RF24/utility/Template/spi.h
new file mode 100644
index 000000000..d0813a120
--- /dev/null
+++ b/arduino/libraries/RF24/utility/Template/spi.h
@@ -0,0 +1,86 @@
+/**
+ * @file spi.h
+ * Class declaration for SPI helper files
+ */
+
+/**
+ * Example of spi.h class declaration for SPI portability
+ *
+ * @defgroup Porting_SPI Porting: SPI
+ *
+ * @{
+ */
+#include <string>
+#include <stdint.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <inttypes.h>
+#include <linux/types.h>
+#include <linux/spi/spidev.h>
+
+using namespace std;
+
+#ifndef DOXYGEN_FORCED
+// exclude this line from the docs to prevent displaying in the list of classes
+class SPI
+#endif
+{
+public:
+
+       /**
+        * SPI constructor
+        */
+       SPI();
+
+       /**
+        * Start SPI
+        */
+       void begin(int busNo);
+
+       /**
+        * Transfer a single byte
+        * @param tx_ Byte to send
+        * @return Data returned via spi
+        */
+       uint8_t transfer(uint8_t tx_);
+
+       /**
+        * Transfer a buffer of data
+        * @param tbuf Transmit buffer
+        * @param rbuf Receive buffer
+        * @param len Length of the data
+        */
+       void transfernb(char* tbuf, char* rbuf, uint32_t len);
+
+       /**
+        * Transfer a buffer of data without an rx buffer
+        * @param buf Pointer to a buffer of data
+        * @param len Length of the data
+        */
+       void transfern(char* buf, uint32_t len);
+
+#ifndef DOXYGEN_FORCED
+// exclude this line from the docs to prevent warnings docs generators
+       virtual ~ SPI();
+#endif
+private:
+
+       /** Default SPI device */
+       string device;
+       /** SPI Mode set */
+       uint8_t mode;
+       /** word size*/
+       uint8_t bits;
+       /** Set SPI speed*/
+       uint32_t speed;
+       int fd;
+
+       void init();
+
+};
+
+/**@}*/
diff --git a/arduino/libraries/RF24/utility/rp2/CMakeLists.txt 
b/arduino/libraries/RF24/utility/rp2/CMakeLists.txt
new file mode 100644
index 000000000..ccc115cf5
--- /dev/null
+++ b/arduino/libraries/RF24/utility/rp2/CMakeLists.txt
@@ -0,0 +1,21 @@
+## Include this file if you want to use the RF24 library
+## in YOUR (Pico) project.
+## See examples_pico/CMakeLists.txt to see how it could be done
+
+cmake_minimum_required(VERSION 3.12)
+
+set(CMAKE_C_STANDARD 11)
+set(CMAKE_CXX_STANDARD 17)
+
+# Define the RF24 core library
+add_library(RF24 INTERFACE)
+
+target_sources(RF24 INTERFACE
+    ${CMAKE_CURRENT_LIST_DIR}/../../RF24.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/gpio.cpp
+    ${CMAKE_CURRENT_LIST_DIR}/spi.cpp
+)
+
+target_include_directories(RF24 INTERFACE
+    ${CMAKE_CURRENT_LIST_DIR}/../../
+)
diff --git a/arduino/libraries/RF24/utility/rp2/RF24_arch_config.h 
b/arduino/libraries/RF24/utility/rp2/RF24_arch_config.h
new file mode 100644
index 000000000..13c16aa49
--- /dev/null
+++ b/arduino/libraries/RF24/utility/rp2/RF24_arch_config.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C)
+ *  2021 Jannis Achstetter (kripton)
+ *  2021 Brendan Doherty (2bndy5)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ */
+
+/**
+ * @file RF24_arch_config.h
+ * General defines and includes for RF24 using The Pico SDK
+ */
+
+#ifndef __ARCH_CONFIG_H__
+#define __ARCH_CONFIG_H__
+
+#include "spi.h"
+#include "gpio.h"
+#include <string.h>
+
+/** Define a specific platform name for this configuration */
+#define RF24_RP2
+
+#define _BV(x) (1 << (x))
+#define _SPI SPI
+#define RF24_SPI_PTR
+
+static SPI spi;
+
+#ifdef SERIAL_DEBUG
+    #define IF_SERIAL_DEBUG(x) ({x;})
+#else
+    #define IF_SERIAL_DEBUG(x)
+#endif
+
+typedef uint16_t prog_uint16_t;
+#define PSTR(x) (x)
+#define printf_P printf
+#define strlen_P strlen
+#define PROGMEM
+#define pgm_read_word(p) (*(p))
+#define PRIPSTR "%s"
+#define pgm_read_byte(p) (*(p))
+
+#define pgm_read_ptr(p) (*(p))
+
+// Function, constant map as a result of migrating from Arduino
+#define LOW GPIO::OUTPUT_LOW
+#define HIGH GPIO::OUTPUT_HIGH
+#define INPUT GPIO::DIRECTION_IN
+#define OUTPUT GPIO::DIRECTION_OUT
+#define digitalWrite(pin, value) GPIO::write(pin, value)
+#define pinMode(pin, direction) GPIO::open(pin, direction)
+#define delay(milisec) sleep_ms(milisec)
+#define delayMicroseconds(usec) sleep_us(usec)
+#define millis() to_ms_since_boot(get_absolute_time())
+
+#endif // __ARCH_CONFIG_H__
+
+/**@}*/
diff --git a/arduino/libraries/RF24/utility/rp2/gpio.cpp b/arduino/libraries/RF24/utility/rp2/gpio.cpp
new file mode 100644
index 000000000..b6d11d4fe
--- /dev/null
+++ b/arduino/libraries/RF24/utility/rp2/gpio.cpp
@@ -0,0 +1,26 @@
+#include "gpio.h"
+
+GPIO::GPIO()
+{
+}
+
+void GPIO::open(int port, int DDR) {
+    gpio_init(port);
+    gpio_set_dir(port, DDR);
+}
+
+void GPIO::close(int port) {
+    gpio_init(port);
+}
+
+int GPIO::read(int port) {
+    return gpio_get(port);
+}
+
+void GPIO::write(int port, int value) {
+    gpio_put(port, value);
+}
+
+GPIO::~GPIO()
+{
+}
diff --git a/arduino/libraries/RF24/utility/rp2/gpio.h b/arduino/libraries/RF24/utility/rp2/gpio.h
new file mode 100644
index 000000000..3ef265f64
--- /dev/null
+++ b/arduino/libraries/RF24/utility/rp2/gpio.h
@@ -0,0 +1,57 @@
+/**
+ * @file gpio.h
+ * Class declaration for SPI helper files
+ */
+
+/**
+ * Example of gpio.h class declaration for GPIO portability
+ *
+ * @defgroup Porting_GPIO Porting: GPIO
+ *
+ * @{
+ */
+
+#include <stdio.h>
+#include "pico/stdlib.h"
+
+class GPIO {
+public:
+    /* Constants */
+    static const int DIRECTION_OUT = 1;
+    static const int DIRECTION_IN = 0;
+
+    static const int OUTPUT_HIGH = 1;
+    static const int OUTPUT_LOW = 0;
+
+    GPIO();
+
+    /**
+     * Similar to Arduino pinMode(pin,mode);
+     * @param port
+     * @param DDR
+     */
+    static void open(int port, int DDR);
+
+    /**
+     *
+     * @param port
+     */
+    static void close(int port);
+
+    /**
+     * Similar to Arduino digitalRead(pin);
+     * @param port
+     */
+    static int read(int port);
+
+    /**
+    * Similar to Arduino digitalWrite(pin,state);
+    * @param port
+    * @param value
+    */
+    static void write(int port, int value);
+
+    virtual ~ GPIO();
+};
+
+/**@}*/
diff --git a/arduino/libraries/RF24/utility/rp2/spi.cpp b/arduino/libraries/RF24/utility/rp2/spi.cpp
new file mode 100644
index 000000000..f60945198
--- /dev/null
+++ b/arduino/libraries/RF24/utility/rp2/spi.cpp
@@ -0,0 +1,52 @@
+#include "spi.h"
+
+spi_inst_t* SPI::_hw_id;
+
+SPI::SPI()
+{
+}
+
+void SPI::begin(spi_inst_t* hw_id)
+{
+    begin(hw_id, PICO_DEFAULT_SPI_SCK_PIN, PICO_DEFAULT_SPI_TX_PIN, PICO_DEFAULT_SPI_RX_PIN);
+}
+
+void SPI::begin(spi_inst_t* hw_id, uint8_t _sck, uint8_t _tx, uint8_t _rx)
+{
+    _hw_id = hw_id;
+    gpio_set_function(_sck, GPIO_FUNC_SPI);
+    gpio_set_function(_tx, GPIO_FUNC_SPI);
+    gpio_set_function(_rx, GPIO_FUNC_SPI);
+}
+
+uint8_t SPI::transfer(uint8_t tx_)
+{
+    uint8_t recv = 0;
+    spi_write_read_blocking(_hw_id, &tx_, &recv, 1);
+    return recv;
+}
+
+void SPI::transfernb(const uint8_t* tbuf, uint8_t* rbuf, uint32_t len)
+{
+    spi_write_read_blocking(_hw_id, tbuf, rbuf, len);
+}
+
+void SPI::transfern(const uint8_t* buf, uint32_t len)
+{
+    spi_write_blocking(_hw_id, buf, len);
+}
+
+void SPI::beginTransaction(uint32_t _spi_speed)
+{
+    spi_init(_hw_id, _spi_speed);
+    spi_set_format(_hw_id, RF24_SPI_BYTE_SIZE, RF24_SPI_CPOL, RF24_SPI_CPHA, RF24_SPI_ENDIAN);
+}
+
+void SPI::endTransaction()
+{
+    spi_deinit(_hw_id);
+}
+
+SPI::~SPI()
+{
+}
diff --git a/arduino/libraries/RF24/utility/rp2/spi.h b/arduino/libraries/RF24/utility/rp2/spi.h
new file mode 100644
index 000000000..4c545a692
--- /dev/null
+++ b/arduino/libraries/RF24/utility/rp2/spi.h
@@ -0,0 +1,91 @@
+/**
+ * @file spi.h
+ * Class declaration for SPI wrapping the Pico SDK
+ */
+#include <stdio.h>
+#include "pico/stdlib.h"
+#include "hardware/spi.h"
+
+#define RF24_SPI_BYTE_SIZE 8
+#define RF24_SPI_ENDIAN SPI_MSB_FIRST
+#define RF24_SPI_CPHA SPI_CPHA_0
+#define RF24_SPI_CPOL SPI_CPOL_0
+
+// this SPI class uses beginTransaction() & endTransaction() to
+// implement spi_init() & spi_deinit()
+#define SPI_HAS_TRANSACTION 1
+
+class SPI {
+public:
+
+    /**
+     * SPI constructor
+     */
+    SPI();
+
+    /**
+     * Start SPI
+     * @param hw_id This is either `spi0` or `spi1` (provided by the Pico SDK)
+     *
+     * @note this function assumes using the default SPI pins defined for your
+     * board in "pico-sdk/src/boards/include/boards/*.h" files of the Pico SDK.
+     * @see begin(spi_inst_t, uint8_t, uint8_t, uint8_t) for using other pins as
+     * your SPI bus.
+     */
+    static void begin(spi_inst_t* hw_id);
+
+    /**
+     * Start SPI
+     * @param hw_id This is either `spi0` or `spi1` (provided by the Pico SDK)
+     * @param _sck The pin to be used as the SPI bus' sck
+     * @param _tx The pin to be used as the SPI bus' tx (MOSI)
+     * @param _rx The pin to be used as the SPI bus' rx (MISO)
+     *
+     * @note this function assumes using the default SPI pins defined for your
+     * board in "pico-sdk/src/boards/include/boards/*.h" files of the Pico SDK.
+     * @see The [Pico SDK has a chart of applicable 
pins](https://datasheets.raspberrypi.org/pico/raspberry-pi-pico-c-sdk.pdf#%5B%7B%22num%22%3A106%2C%22gen%22%3A0%7D%2C%7B%22name%22%3A%22XYZ%22%7D%2C115%2C377.118%2Cnull%5D)
+     * that can be used for hardware driven SPI transactions.
+     */
+    static void begin(spi_inst_t* hw_id, uint8_t _sck, uint8_t _tx, uint8_t _rx);
+
+    /**
+     * Transfer a single byte
+     * @param tx_ Byte to send
+     * @return Data returned via spi
+     */
+    static uint8_t transfer(uint8_t tx_);
+
+    /**
+     * Transfer a buffer of data
+     * @param tbuf Transmit buffer
+     * @param rbuf Receive buffer
+     * @param len Length of the data
+     */
+    static void transfernb(const uint8_t* tbuf, uint8_t* rbuf, uint32_t len);
+
+    /**
+     * Transfer a buffer of data without an rx buffer
+     * @param buf Pointer to a buffer of data
+     * @param len Length of the data
+     */
+    static void transfern(const uint8_t* buf, uint32_t len);
+
+    /**
+     * init the SPI bus (using hw_id passed to begin())
+     * @param _spi_speed The frequency to use for SPI transactions with the radio.
+     */
+    static void beginTransaction(uint32_t _spi_speed);
+
+    /** deinit the SPI bus (using hw_id passed to begin()) */
+    static void endTransaction();
+
+    virtual ~ SPI();
+
+private:
+
+    /** the ID of the hardware driven SPI bus */
+    static spi_inst_t* _hw_id;
+
+};
+
+/**@}*/
diff --git a/arduino/libraries/RF24/utility/wiringPi/RF24_arch_config.h 
b/arduino/libraries/RF24/utility/wiringPi/RF24_arch_config.h
new file mode 100644
index 000000000..a60214ac1
--- /dev/null
+++ b/arduino/libraries/RF24/utility/wiringPi/RF24_arch_config.h
@@ -0,0 +1,55 @@
+/*
+ Copyright (C) 2011 J. Coliz <maniacbug ymail com>
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License
+ version 2 as published by the Free Software Foundation.
+
+ */
+#ifndef __ARCH_CONFIG_H__
+#define __ARCH_CONFIG_H__
+
+#define RF24_LINUX
+
+#include <stddef.h>
+#include "spi.h"
+#include "wiringPi.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <time.h>
+#include <string.h>
+#include <sys/time.h>
+
+#define _BV(x) (1<<(x))
+#define _SPI spi
+
+#undef SERIAL_DEBUG
+#ifdef SERIAL_DEBUG
+    #define IF_SERIAL_DEBUG(x) ({x;})
+#else
+    #define IF_SERIAL_DEBUG(x)
+#endif
+
+// Avoid spurious warnings
+#if 1
+    #if !defined( NATIVE ) && defined( ARDUINO )
+        #undef PROGMEM
+        #define PROGMEM __attribute__(( section(".progmem.data") ))
+        #undef PSTR
+        #define PSTR(s) (__extension__({static const char __c[] PROGMEM = (s); &__c[0];}))
+    #endif
+#endif
+
+typedef uint16_t prog_uint16_t;
+#define PSTR(x) (x)
+#define printf_P printf
+#define strlen_P strlen
+#define PROGMEM
+#define pgm_read_word(p) (*(p))
+#define PRIPSTR "%s"
+#define pgm_read_byte(p) (*(p))
+#define pgm_read_ptr(p) (*(p))
+#endif // __ARCH_CONFIG_H__
+
+
+/*@}*/
diff --git a/arduino/libraries/RF24/utility/wiringPi/includes.h 
b/arduino/libraries/RF24/utility/wiringPi/includes.h
new file mode 100644
index 000000000..b11c94a1b
--- /dev/null
+++ b/arduino/libraries/RF24/utility/wiringPi/includes.h
@@ -0,0 +1,29 @@
+/**
+* @file includes.h
+* Configuration defines for RF24/Linux
+*/
+
+/**
+* Example of includes.h for RF24 Linux portability
+*
+* @defgroup Porting_Includes Porting: Includes
+*
+*
+* @{
+*/
+
+#ifndef __RF24_INCLUDES_H__
+#define __RF24_INCLUDES_H__
+
+/**
+ * Define RF24_WIRINGPI configuration for RaspberryPi platform
+ */
+#define RF24_WIRINGPI
+
+/**
+ * Load the correct configuration for this platform
+ */
+#include "wiringPi/RF24_arch_config.h"
+
+#endif
+/*@}*/
\ No newline at end of file
diff --git a/arduino/libraries/RF24/utility/wiringPi/spi.cpp b/arduino/libraries/RF24/utility/wiringPi/spi.cpp
new file mode 100644
index 000000000..eb59ae79c
--- /dev/null
+++ b/arduino/libraries/RF24/utility/wiringPi/spi.cpp
@@ -0,0 +1,88 @@
+/*
+ * File:   spi.cpp
+ * Author:
+ *
+ * Created on
+ *
+ * Inspired from spi speed test from wiringPi
+ * wiringPi/examples/spiSpeed.c
+ */
+
+#include "spi.h"
+
+#include <wiringPi.h>
+#include <wiringPiSPI.h>
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+
+#define RF24_SPI_CHANNEL    0
+
+SPI::SPI():fd(-1)
+{
+    printf("wiringPi RF24 DRIVER\n");
+}
+
+void SPI::begin(int csn_pin, uint32_t spi_speed)
+{
+    // initialize the wiringPiSPI
+    wiringPiSetup();
+    if ((this->fd = wiringPiSPISetup(RF24_SPI_CHANNEL, spi_speed)) < 0) {
+        printf("Cannot configure the SPI device!\n");
+        fflush(stdout);
+        abort();
+    } else {
+        printf("Configured SPI fd: %d - pin: %d\n", fd, csn_pin);
+    }
+}
+
+uint8_t SPI::transfer(uint8_t tx)
+{
+    memset(&msgByte, 0, sizeof(msgByte));
+    memcpy(&msgByte, &tx, sizeof(tx));
+
+    if (wiringPiSPIDataRW(RF24_SPI_CHANNEL, &msgByte, sizeof(tx)) < 0) {
+        printf("transfer(): Cannot send data: %s\n", strerror(errno));
+        fflush(stdout);
+        abort();
+    }
+
+    return msgByte;
+}
+
+void SPI::transfern(char* buf, uint32_t len)
+{
+    printf("transfern(tx: %s)\n", buf);
+
+    if (wiringPiSPIDataRW(RF24_SPI_CHANNEL, (uint8_t*) buf, len) < 0) {
+        printf("transfern(): Cannot send data %s\n", strerror(errno));
+        fflush(stdout);
+        abort();
+    }
+}
+
+void SPI::transfernb(char* tbuf, char* rbuf, uint32_t len)
+{
+    // using an auxiliary buffer to keep tx and rx different
+    memset(msg, 0, sizeof(msg));
+    memcpy(msg, tbuf, len);
+
+    if (wiringPiSPIDataRW(RF24_SPI_CHANNEL, msg, len) < 0) {
+        printf("transfernb() Cannot send data %s\n", strerror(errno));
+        fflush(stdout);
+        abort();
+    }
+
+    memcpy(rbuf, msg, len);
+}
+
+SPI::~SPI()
+{
+    if (this->fd >= 0) {
+        close(this->fd);
+        this->fd = -1;
+    }
+}
diff --git a/arduino/libraries/RF24/utility/wiringPi/spi.h b/arduino/libraries/RF24/utility/wiringPi/spi.h
new file mode 100644
index 000000000..8083ad03b
--- /dev/null
+++ b/arduino/libraries/RF24/utility/wiringPi/spi.h
@@ -0,0 +1,72 @@
+/**
+ * @file spi.h
+ * Class declaration for SPI helper files
+ */
+
+#ifndef SPI_H
+#define    SPI_H
+
+/**
+* Example of spi.h class declaration for SPI portability
+*
+* @defgroup Porting_SPI Porting: SPI
+*
+* @{
+*/
+
+#include <stdio.h>
+#include <inttypes.h>
+
+#include "../../RF24_config.h"
+
+using namespace std;
+
+class SPI {
+public:
+    /**
+    * SPI default constructor
+    */
+    SPI();
+
+    /**
+    * Start SPI communication
+    * @param pin used for SPI
+    */
+    void begin(int csn_pin, uint32_t spi_speed = RF24_SPI_SPEED);
+
+    /**
+    * Transfer a single byte of data
+    * @param tx Byte to send
+    * @return Data returned via spi
+    */
+    uint8_t transfer(uint8_t);
+
+    /**
+    * Transfer a buffer of data using rx and tx buffer
+    * @param tbuf Transmit buffer
+    * @param rbuf Receive buffer
+    * @param len Length of the data
+    */
+    void transfernb(char*, char*, uint32_t);
+
+    /**
+    * Transfer a buffer of data without using an rx buffer
+    * @param buf Pointer to a buffer of data
+    * @param len Length of the data
+    */
+    void transfern(char*, const uint32_t);
+
+    /**
+    * SPI destructor
+    */
+    virtual ~SPI();
+
+private:
+    int fd;
+    uint8_t msg[32 + 1];
+    uint8_t msgByte;
+};
+
+/*@}*/
+#endif    /* SPI_H */
+
diff --git a/arduino/libraries/SD/README.adoc b/arduino/libraries/SD/README.adoc
new file mode 100644
index 000000000..fb3ed030c
--- /dev/null
+++ b/arduino/libraries/SD/README.adoc
@@ -0,0 +1,26 @@
+= SD Library for Arduino =
+
+image:https://travis-ci.org/arduino-libraries/SD.svg?branch=master[Build Status, 
link=https://travis-ci.org/arduino-libraries/SD]
+
+The SD library allows for reading from and writing to SD cards.
+
+For more information about this library please visit us at
+http://www.arduino.cc/en/Reference/SD
+
+== License ==
+
+ Copyright (C) 2009 by William Greiman
+Copyright (c) 2010 SparkFun Electronics
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program.  If not, see <http://www.gnu.org/licenses/>.
diff --git a/arduino/libraries/SD/examples/CardInfo/CardInfo.ino 
b/arduino/libraries/SD/examples/CardInfo/CardInfo.ino
new file mode 100644
index 000000000..97913efde
--- /dev/null
+++ b/arduino/libraries/SD/examples/CardInfo/CardInfo.ino
@@ -0,0 +1,116 @@
+/*
+  SD card test
+
+  This example shows how use the utility libraries on which the'
+  SD library is based in order to get info about your SD card.
+  Very useful for testing a card when you're not sure whether its working or not.
+
+  The circuit:
+    SD card attached to SPI bus as follows:
+ ** MOSI - pin 11 on Arduino Uno/Duemilanove/Diecimila
+ ** MISO - pin 12 on Arduino Uno/Duemilanove/Diecimila
+ ** CLK - pin 13 on Arduino Uno/Duemilanove/Diecimila
+ ** CS - depends on your SD card shield or module.
+               Pin 4 used here for consistency with other Arduino examples
+
+
+  created  28 Mar 2011
+  by Limor Fried
+  modified 9 Apr 2012
+  by Tom Igoe
+*/
+// include the SD library:
+#include <SPI.h>
+#include <SD.h>
+
+// set up variables using the SD utility library functions:
+Sd2Card card;
+SdVolume volume;
+SdFile root;
+
+// change this to match your SD shield or module;
+// Arduino Ethernet shield: pin 4
+// Adafruit SD shields and modules: pin 10
+// Sparkfun SD shield: pin 8
+// MKRZero SD: SDCARD_SS_PIN
+const int chipSelect = 4;
+
+void setup() {
+  // Open serial communications and wait for port to open:
+  Serial.begin(9600);
+  while (!Serial) {
+    ; // wait for serial port to connect. Needed for native USB port only
+  }
+
+
+  Serial.print("\nInitializing SD card...");
+
+  // we'll use the initialization code from the utility libraries
+  // since we're just testing if the card is working!
+  if (!card.init(SPI_HALF_SPEED, chipSelect)) {
+    Serial.println("initialization failed. Things to check:");
+    Serial.println("* is a card inserted?");
+    Serial.println("* is your wiring correct?");
+    Serial.println("* did you change the chipSelect pin to match your shield or module?");
+    while (1);
+  } else {
+    Serial.println("Wiring is correct and a card is present.");
+  }
+
+  // print the type of card
+  Serial.println();
+  Serial.print("Card type:         ");
+  switch (card.type()) {
+    case SD_CARD_TYPE_SD1:
+      Serial.println("SD1");
+      break;
+    case SD_CARD_TYPE_SD2:
+      Serial.println("SD2");
+      break;
+    case SD_CARD_TYPE_SDHC:
+      Serial.println("SDHC");
+      break;
+    default:
+      Serial.println("Unknown");
+  }
+
+  // Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
+  if (!volume.init(card)) {
+    Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
+    while (1);
+  }
+
+  Serial.print("Clusters:          ");
+  Serial.println(volume.clusterCount());
+  Serial.print("Blocks x Cluster:  ");
+  Serial.println(volume.blocksPerCluster());
+
+  Serial.print("Total Blocks:      ");
+  Serial.println(volume.blocksPerCluster() * volume.clusterCount());
+  Serial.println();
+
+  // print the type and size of the first FAT-type volume
+  uint32_t volumesize;
+  Serial.print("Volume type is:    FAT");
+  Serial.println(volume.fatType(), DEC);
+
+  volumesize = volume.blocksPerCluster();    // clusters are collections of blocks
+  volumesize *= volume.clusterCount();       // we'll have a lot of clusters
+  volumesize /= 2;                           // SD card blocks are always 512 bytes (2 blocks are 1KB)
+  Serial.print("Volume size (Kb):  ");
+  Serial.println(volumesize);
+  Serial.print("Volume size (Mb):  ");
+  volumesize /= 1024;
+  Serial.println(volumesize);
+  Serial.print("Volume size (Gb):  ");
+  Serial.println((float)volumesize / 1024.0);
+
+  Serial.println("\nFiles found on the card (name, date and size in bytes): ");
+  root.openRoot(volume);
+
+  // list all files in the card with date and size
+  root.ls(LS_R | LS_DATE | LS_SIZE);
+}
+
+void loop(void) {
+}
diff --git a/arduino/libraries/SD/examples/Datalogger/Datalogger.ino 
b/arduino/libraries/SD/examples/Datalogger/Datalogger.ino
new file mode 100644
index 000000000..c2631c0a4
--- /dev/null
+++ b/arduino/libraries/SD/examples/Datalogger/Datalogger.ino
@@ -0,0 +1,84 @@
+/*
+  SD card datalogger
+
+  This example shows how to log data from three analog sensors
+  to an SD card using the SD library.
+
+  The circuit:
+   analog sensors on analog ins 0, 1, and 2
+   SD card attached to SPI bus as follows:
+ ** MOSI - pin 11
+ ** MISO - pin 12
+ ** CLK - pin 13
+ ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)
+
+  created  24 Nov 2010
+  modified 9 Apr 2012
+  by Tom Igoe
+
+  This example code is in the public domain.
+
+*/
+
+#include <SPI.h>
+#include <SD.h>
+
+const int chipSelect = 4;
+
+void setup() {
+  // Open serial communications and wait for port to open:
+  Serial.begin(9600);
+  while (!Serial) {
+    ; // wait for serial port to connect. Needed for native USB port only
+  }
+
+
+  Serial.print("Initializing SD card...");
+
+  // see if the card is present and can be initialized:
+  if (!SD.begin(chipSelect)) {
+    Serial.println("Card failed, or not present");
+    // don't do anything more:
+    while (1);
+  }
+  Serial.println("card initialized.");
+}
+
+void loop() {
+  // make a string for assembling the data to log:
+  String dataString = "";
+
+  // read three sensors and append to the string:
+  for (int analogPin = 0; analogPin < 3; analogPin++) {
+    int sensor = analogRead(analogPin);
+    dataString += String(sensor);
+    if (analogPin < 2) {
+      dataString += ",";
+    }
+  }
+
+  // open the file. note that only one file can be open at a time,
+  // so you have to close this one before opening another.
+  File dataFile = SD.open("datalog.txt", FILE_WRITE);
+
+  // if the file is available, write to it:
+  if (dataFile) {
+    dataFile.println(dataString);
+    dataFile.close();
+    // print to the serial port too:
+    Serial.println(dataString);
+  }
+  // if the file isn't open, pop up an error:
+  else {
+    Serial.println("error opening datalog.txt");
+  }
+}
+
+
+
+
+
+
+
+
+
diff --git a/arduino/libraries/SD/examples/DumpFile/DumpFile.ino 
b/arduino/libraries/SD/examples/DumpFile/DumpFile.ino
new file mode 100644
index 000000000..74919991c
--- /dev/null
+++ b/arduino/libraries/SD/examples/DumpFile/DumpFile.ino
@@ -0,0 +1,65 @@
+/*
+  SD card file dump
+
+  This example shows how to read a file from the SD card using the
+  SD library and send it over the serial port.
+
+  The circuit:
+   SD card attached to SPI bus as follows:
+ ** MOSI - pin 11
+ ** MISO - pin 12
+ ** CLK - pin 13
+ ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)
+
+  created  22 December 2010
+  by Limor Fried
+  modified 9 Apr 2012
+  by Tom Igoe
+
+  This example code is in the public domain.
+
+*/
+
+#include <SPI.h>
+#include <SD.h>
+
+const int chipSelect = 4;
+
+void setup() {
+  // Open serial communications and wait for port to open:
+  Serial.begin(9600);
+  while (!Serial) {
+    ; // wait for serial port to connect. Needed for native USB port only
+  }
+
+
+  Serial.print("Initializing SD card...");
+
+  // see if the card is present and can be initialized:
+  if (!SD.begin(chipSelect)) {
+    Serial.println("Card failed, or not present");
+    // don't do anything more:
+    while (1);
+  }
+  Serial.println("card initialized.");
+
+  // open the file. note that only one file can be open at a time,
+  // so you have to close this one before opening another.
+  File dataFile = SD.open("datalog.txt");
+
+  // if the file is available, write to it:
+  if (dataFile) {
+    while (dataFile.available()) {
+      Serial.write(dataFile.read());
+    }
+    dataFile.close();
+  }
+  // if the file isn't open, pop up an error:
+  else {
+    Serial.println("error opening datalog.txt");
+  }
+}
+
+void loop() {
+}
+
diff --git a/arduino/libraries/SD/examples/Files/Files.ino b/arduino/libraries/SD/examples/Files/Files.ino
new file mode 100644
index 000000000..cabebba51
--- /dev/null
+++ b/arduino/libraries/SD/examples/Files/Files.ino
@@ -0,0 +1,75 @@
+/*
+  SD card basic file example
+
+  This example shows how to create and destroy an SD card file
+  The circuit:
+   SD card attached to SPI bus as follows:
+ ** MOSI - pin 11
+ ** MISO - pin 12
+ ** CLK - pin 13
+ ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)
+
+  created   Nov 2010
+  by David A. Mellis
+  modified 9 Apr 2012
+  by Tom Igoe
+
+  This example code is in the public domain.
+
+*/
+#include <SPI.h>
+#include <SD.h>
+
+File myFile;
+
+void setup() {
+  // Open serial communications and wait for port to open:
+  Serial.begin(9600);
+  while (!Serial) {
+    ; // wait for serial port to connect. Needed for native USB port only
+  }
+
+
+  Serial.print("Initializing SD card...");
+
+  if (!SD.begin(4)) {
+    Serial.println("initialization failed!");
+    while (1);
+  }
+  Serial.println("initialization done.");
+
+  if (SD.exists("example.txt")) {
+    Serial.println("example.txt exists.");
+  } else {
+    Serial.println("example.txt doesn't exist.");
+  }
+
+  // open a new file and immediately close it:
+  Serial.println("Creating example.txt...");
+  myFile = SD.open("example.txt", FILE_WRITE);
+  myFile.close();
+
+  // Check to see if the file exists:
+  if (SD.exists("example.txt")) {
+    Serial.println("example.txt exists.");
+  } else {
+    Serial.println("example.txt doesn't exist.");
+  }
+
+  // delete the file:
+  Serial.println("Removing example.txt...");
+  SD.remove("example.txt");
+
+  if (SD.exists("example.txt")) {
+    Serial.println("example.txt exists.");
+  } else {
+    Serial.println("example.txt doesn't exist.");
+  }
+}
+
+void loop() {
+  // nothing happens after setup finishes.
+}
+
+
+
diff --git a/arduino/libraries/SD/examples/NonBlockingWrite/NonBlockingWrite.ino 
b/arduino/libraries/SD/examples/NonBlockingWrite/NonBlockingWrite.ino
new file mode 100644
index 000000000..29d8ec66d
--- /dev/null
+++ b/arduino/libraries/SD/examples/NonBlockingWrite/NonBlockingWrite.ino
@@ -0,0 +1,91 @@
+/*
+  Non-blocking Write
+
+  This example demonstrates how to perform non-blocking writes
+  to a file on a SD card. The file will contain the current millis()
+  value every 10ms. If the SD card is busy, the data will be buffered
+  in order to not block the sketch.
+
+  NOTE: myFile.availableForWrite() will automatically sync the
+        file contents as needed. You may lose some unsynced data
+        still if myFile.sync() or myFile.close() is not called.
+
+  The circuit:
+  - Arduino MKR Zero board
+  - micro SD card attached
+
+  This example code is in the public domain.
+*/
+
+#include <SD.h>
+
+// file name to use for writing
+const char filename[] = "demo.txt";
+
+// File object to represent file
+File txtFile;
+
+// string to buffer output
+String buffer;
+
+unsigned long lastMillis = 0;
+
+void setup() {
+  Serial.begin(9600);
+  while (!Serial);
+
+  // reserve 1kB for String used as a buffer
+  buffer.reserve(1024);
+
+  // set LED pin to output, used to blink when writing
+  pinMode(LED_BUILTIN, OUTPUT);
+
+  // init the SD card
+  if (!SD.begin()) {
+    Serial.println("Card failed, or not present");
+    // don't do anything more:
+    while (1);
+  }
+
+  // If you want to start from an empty file,
+  // uncomment the next line:
+  // SD.remove(filename);
+
+  // try to open the file for writing
+  txtFile = SD.open(filename, FILE_WRITE);
+  if (!txtFile) {
+    Serial.print("error opening ");
+    Serial.println(filename);
+    while (1);
+  }
+
+  // add some new lines to start
+  txtFile.println();
+  txtFile.println("Hello World!");
+}
+
+void loop() {
+  // check if it's been over 10 ms since the last line added
+  unsigned long now = millis();
+  if ((now - lastMillis) >= 10) {
+    // add a new line to the buffer
+    buffer += "Hello ";
+    buffer += now;
+    buffer += "\r\n";
+
+    lastMillis = now;
+  }
+
+  // check if the SD card is available to write data without blocking
+  // and if the buffered data is enough for the full chunk size
+  unsigned int chunkSize = txtFile.availableForWrite();
+  if (chunkSize && buffer.length() >= chunkSize) {
+    // write to file and blink LED
+    digitalWrite(LED_BUILTIN, HIGH);
+    txtFile.write(buffer.c_str(), chunkSize);
+    digitalWrite(LED_BUILTIN, LOW);
+
+    // remove written data from buffer
+    buffer.remove(0, chunkSize);
+  }
+}
diff --git a/arduino/libraries/SD/examples/ReadWrite/ReadWrite.ino 
b/arduino/libraries/SD/examples/ReadWrite/ReadWrite.ino
new file mode 100644
index 000000000..d964668c4
--- /dev/null
+++ b/arduino/libraries/SD/examples/ReadWrite/ReadWrite.ino
@@ -0,0 +1,79 @@
+/*
+  SD card read/write
+
+  This example shows how to read and write data to and from an SD card file
+  The circuit:
+   SD card attached to SPI bus as follows:
+ ** MOSI - pin 11
+ ** MISO - pin 12
+ ** CLK - pin 13
+ ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)
+
+  created   Nov 2010
+  by David A. Mellis
+  modified 9 Apr 2012
+  by Tom Igoe
+
+  This example code is in the public domain.
+
+*/
+
+#include <SPI.h>
+#include <SD.h>
+
+File myFile;
+
+void setup() {
+  // Open serial communications and wait for port to open:
+  Serial.begin(9600);
+  while (!Serial) {
+    ; // wait for serial port to connect. Needed for native USB port only
+  }
+
+
+  Serial.print("Initializing SD card...");
+
+  if (!SD.begin(4)) {
+    Serial.println("initialization failed!");
+    while (1);
+  }
+  Serial.println("initialization done.");
+
+  // open the file. note that only one file can be open at a time,
+  // so you have to close this one before opening another.
+  myFile = SD.open("test.txt", FILE_WRITE);
+
+  // if the file opened okay, write to it:
+  if (myFile) {
+    Serial.print("Writing to test.txt...");
+    myFile.println("testing 1, 2, 3.");
+    // close the file:
+    myFile.close();
+    Serial.println("done.");
+  } else {
+    // if the file didn't open, print an error:
+    Serial.println("error opening test.txt");
+  }
+
+  // re-open the file for reading:
+  myFile = SD.open("test.txt");
+  if (myFile) {
+    Serial.println("test.txt:");
+
+    // read from the file until there's nothing else in it:
+    while (myFile.available()) {
+      Serial.write(myFile.read());
+    }
+    // close the file:
+    myFile.close();
+  } else {
+    // if the file didn't open, print an error:
+    Serial.println("error opening test.txt");
+  }
+}
+
+void loop() {
+  // nothing happens after setup
+}
+
+
diff --git a/arduino/libraries/SD/examples/listfiles/listfiles.ino 
b/arduino/libraries/SD/examples/listfiles/listfiles.ino
new file mode 100644
index 000000000..48f84fb99
--- /dev/null
+++ b/arduino/libraries/SD/examples/listfiles/listfiles.ino
@@ -0,0 +1,80 @@
+/*
+  Listfiles
+
+  This example shows how print out the files in a
+  directory on a SD card
+
+  The circuit:
+   SD card attached to SPI bus as follows:
+ ** MOSI - pin 11
+ ** MISO - pin 12
+ ** CLK - pin 13
+ ** CS - pin 4 (for MKRZero SD: SDCARD_SS_PIN)
+
+  created   Nov 2010
+  by David A. Mellis
+  modified 9 Apr 2012
+  by Tom Igoe
+  modified 2 Feb 2014
+  by Scott Fitzgerald
+
+  This example code is in the public domain.
+
+*/
+#include <SPI.h>
+#include <SD.h>
+
+File root;
+
+void setup() {
+  // Open serial communications and wait for port to open:
+  Serial.begin(9600);
+  while (!Serial) {
+    ; // wait for serial port to connect. Needed for native USB port only
+  }
+
+  Serial.print("Initializing SD card...");
+
+  if (!SD.begin(4)) {
+    Serial.println("initialization failed!");
+    while (1);
+  }
+  Serial.println("initialization done.");
+
+  root = SD.open("/");
+
+  printDirectory(root, 0);
+
+  Serial.println("done!");
+}
+
+void loop() {
+  // nothing happens after setup finishes.
+}
+
+void printDirectory(File dir, int numTabs) {
+  while (true) {
+
+    File entry =  dir.openNextFile();
+    if (! entry) {
+      // no more files
+      break;
+    }
+    for (uint8_t i = 0; i < numTabs; i++) {
+      Serial.print('\t');
+    }
+    Serial.print(entry.name());
+    if (entry.isDirectory()) {
+      Serial.println("/");
+      printDirectory(entry, numTabs + 1);
+    } else {
+      // files have sizes, directories do not
+      Serial.print("\t\t");
+      Serial.println(entry.size(), DEC);
+    }
+    entry.close();
+  }
+}
+
+
+
diff --git a/arduino/libraries/SD/extras/codespell-ignore-words-list.txt 
b/arduino/libraries/SD/extras/codespell-ignore-words-list.txt
new file mode 100644
index 000000000..e69de29bb
diff --git a/arduino/libraries/SD/keywords.txt b/arduino/libraries/SD/keywords.txt
new file mode 100644
index 000000000..91e74b830
--- /dev/null
+++ b/arduino/libraries/SD/keywords.txt
@@ -0,0 +1,31 @@
+#######################################
+# Syntax Coloring Map SD
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+SD     KEYWORD1        SD
+File   KEYWORD1        SD
+SDFile KEYWORD1        SD
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+begin  KEYWORD2
+exists KEYWORD2
+mkdir  KEYWORD2
+remove KEYWORD2
+rmdir  KEYWORD2
+open   KEYWORD2
+close  KEYWORD2
+seek   KEYWORD2
+position       KEYWORD2
+size   KEYWORD2        
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+FILE_READ      LITERAL1
+FILE_WRITE     LITERAL1
diff --git a/arduino/libraries/SD/library.properties b/arduino/libraries/SD/library.properties
new file mode 100644
index 000000000..60e885928
--- /dev/null
+++ b/arduino/libraries/SD/library.properties
@@ -0,0 +1,9 @@
+name=SD
+version=1.2.4
+author=Arduino, SparkFun
+maintainer=Arduino <info arduino cc>
+sentence=Enables reading and writing on SD cards. 
+paragraph=Once an SD memory card is connected to the SPI interface of the Arduino or Genuino board you can 
create files and read/write on them. You can also move through directories on the SD card.
+category=Data Storage
+url=http://www.arduino.cc/en/Reference/SD
+architectures=*
diff --git a/arduino/libraries/SD/src/File.cpp b/arduino/libraries/SD/src/File.cpp
new file mode 100644
index 000000000..5e37166b5
--- /dev/null
+++ b/arduino/libraries/SD/src/File.cpp
@@ -0,0 +1,168 @@
+/*
+
+  SD - a slightly more friendly wrapper for sdfatlib
+
+  This library aims to expose a subset of SD card functionality
+  in the form of a higher level "wrapper" object.
+
+  License: GNU General Public License V3
+          (Because sdfatlib is licensed with this.)
+
+  (C) Copyright 2010 SparkFun Electronics
+
+*/
+
+#include <SD.h>
+
+/* for debugging file open/close leaks
+   uint8_t nfilecount=0;
+*/
+
+File::File(SdFile f, const char *n) {
+  // oh man you are kidding me, new() doesn't exist? Ok we do it by hand!
+  _file = (SdFile *)malloc(sizeof(SdFile));
+  if (_file) {
+    memcpy(_file, &f, sizeof(SdFile));
+
+    strncpy(_name, n, 12);
+    _name[12] = 0;
+
+    /* for debugging file open/close leaks
+       nfilecount++;
+       Serial.print("Created \"");
+       Serial.print(n);
+       Serial.print("\": ");
+       Serial.println(nfilecount, DEC);
+    */
+  }
+}
+
+File::File(void) {
+  _file = 0;
+  _name[0] = 0;
+  //Serial.print("Created empty file object");
+}
+
+// returns a pointer to the file name
+char *File::name(void) {
+  return _name;
+}
+
+// a directory is a special type of file
+boolean File::isDirectory(void) {
+  return (_file && _file->isDir());
+}
+
+
+size_t File::write(uint8_t val) {
+  return write(&val, 1);
+}
+
+size_t File::write(const uint8_t *buf, size_t size) {
+  size_t t;
+  if (!_file) {
+    setWriteError();
+    return 0;
+  }
+  _file->clearWriteError();
+  t = _file->write(buf, size);
+  if (_file->getWriteError()) {
+    setWriteError();
+    return 0;
+  }
+  return t;
+}
+
+int File::availableForWrite() {
+  if (_file) {
+    return _file->availableForWrite();
+  }
+  return 0;
+}
+
+int File::peek() {
+  if (! _file) {
+    return 0;
+  }
+
+  int c = _file->read();
+  if (c != -1) {
+    _file->seekCur(-1);
+  }
+  return c;
+}
+
+int File::read() {
+  if (_file) {
+    return _file->read();
+  }
+  return -1;
+}
+
+// buffered read for more efficient, high speed reading
+int File::read(void *buf, uint16_t nbyte) {
+  if (_file) {
+    return _file->read(buf, nbyte);
+  }
+  return 0;
+}
+
+int File::available() {
+  if (! _file) {
+    return 0;
+  }
+
+  uint32_t n = size() - position();
+
+  return n > 0X7FFF ? 0X7FFF : n;
+}
+
+void File::flush() {
+  if (_file) {
+    _file->sync();
+  }
+}
+
+boolean File::seek(uint32_t pos) {
+  if (! _file) {
+    return false;
+  }
+
+  return _file->seekSet(pos);
+}
+
+uint32_t File::position() {
+  if (! _file) {
+    return -1;
+  }
+  return _file->curPosition();
+}
+
+uint32_t File::size() {
+  if (! _file) {
+    return 0;
+  }
+  return _file->fileSize();
+}
+
+void File::close() {
+  if (_file) {
+    _file->close();
+    free(_file);
+    _file = 0;
+
+    /* for debugging file open/close leaks
+      nfilecount--;
+      Serial.print("Deleted ");
+      Serial.println(nfilecount, DEC);
+    */
+  }
+}
+
+File::operator bool() {
+  if (_file) {
+    return  _file->isOpen();
+  }
+  return false;
+}
+
diff --git a/arduino/libraries/SD/src/README.txt b/arduino/libraries/SD/src/README.txt
new file mode 100644
index 000000000..495ea4c79
--- /dev/null
+++ b/arduino/libraries/SD/src/README.txt
@@ -0,0 +1,13 @@
+
+** SD - a slightly more friendly wrapper for sdfatlib **
+
+This library aims to expose a subset of SD card functionality in the
+form of a higher level "wrapper" object.
+
+License: GNU General Public License V3
+         (Because sdfatlib is licensed with this.)
+
+(C) Copyright 2010 SparkFun Electronics
+
+Now better than ever with optimization, multiple file support, directory handling, etc - ladyada!
+
diff --git a/arduino/libraries/SD/src/SD.cpp b/arduino/libraries/SD/src/SD.cpp
new file mode 100644
index 000000000..8c9a29e71
--- /dev/null
+++ b/arduino/libraries/SD/src/SD.cpp
@@ -0,0 +1,637 @@
+/*
+
+  SD - a slightly more friendly wrapper for sdfatlib
+
+  This library aims to expose a subset of SD card functionality
+  in the form of a higher level "wrapper" object.
+
+  License: GNU General Public License V3
+          (Because sdfatlib is licensed with this.)
+
+  (C) Copyright 2010 SparkFun Electronics
+
+
+  This library provides four key benefits:
+
+     Including `SD.h` automatically creates a global
+     `SD` object which can be interacted with in a similar
+     manner to other standard global objects like `Serial` and `Ethernet`.
+
+     Boilerplate initialisation code is contained in one method named
+     `begin` and no further objects need to be created in order to access
+     the SD card.
+
+     Calls to `open` can supply a full path name including parent
+     directories which simplifies interacting with files in subdirectories.
+
+     Utility methods are provided to determine whether a file exists
+     and to create a directory hierarchy.
+
+
+  Note however that not all functionality provided by the underlying
+  sdfatlib library is exposed.
+
+*/
+
+/*
+
+  Implementation Notes
+
+  In order to handle multi-directory path traversal, functionality that
+  requires this ability is implemented as callback functions.
+
+  Individual methods call the `walkPath` function which performs the actual
+  directory traversal (swapping between two different directory/file handles
+  along the way) and at each level calls the supplied callback function.
+
+  Some types of functionality will take an action at each level (e.g. exists
+  or make directory) which others will only take an action at the bottom
+  level (e.g. open).
+
+*/
+
+#include "SD.h"
+
+namespace SDLib {
+
+  // Used by `getNextPathComponent`
+#define MAX_COMPONENT_LEN 12 // What is max length?
+#define PATH_COMPONENT_BUFFER_LEN MAX_COMPONENT_LEN+1
+
+  bool getNextPathComponent(const char *path, unsigned int *p_offset,
+                            char *buffer) {
+    /*
+
+      Parse individual path components from a path.
+
+        e.g. after repeated calls '/foo/bar/baz' will be split
+             into 'foo', 'bar', 'baz'.
+
+      This is similar to `strtok()` but copies the component into the
+      supplied buffer rather than modifying the original string.
+
+
+      `buffer` needs to be PATH_COMPONENT_BUFFER_LEN in size.
+
+      `p_offset` needs to point to an integer of the offset at
+      which the previous path component finished.
+
+      Returns `true` if more components remain.
+
+      Returns `false` if this is the last component.
+        (This means path ended with 'foo' or 'foo/'.)
+
+    */
+
+    // TODO: Have buffer local to this function, so we know it's the
+    //       correct length?
+
+    int bufferOffset = 0;
+
+    int offset = *p_offset;
+
+    // Skip root or other separator
+    if (path[offset] == '/') {
+      offset++;
+    }
+
+    // Copy the next next path segment
+    while (bufferOffset < MAX_COMPONENT_LEN
+           && (path[offset] != '/')
+           && (path[offset] != '\0')) {
+      buffer[bufferOffset++] = path[offset++];
+    }
+
+    buffer[bufferOffset] = '\0';
+
+    // Skip trailing separator so we can determine if this
+    // is the last component in the path or not.
+    if (path[offset] == '/') {
+      offset++;
+    }
+
+    *p_offset = offset;
+
+    return (path[offset] != '\0');
+  }
+
+
+
+  boolean walkPath(const char *filepath, SdFile& parentDir,
+                   boolean(*callback)(SdFile& parentDir,
+                                      const char *filePathComponent,
+                                      boolean isLastComponent,
+                                      void *object),
+                   void *object = NULL) {
+    /*
+
+       When given a file path (and parent directory--normally root),
+       this function traverses the directories in the path and at each
+       level calls the supplied callback function while also providing
+       the supplied object for context if required.
+
+         e.g. given the path '/foo/bar/baz'
+              the callback would be called at the equivalent of
+        '/foo', '/foo/bar' and '/foo/bar/baz'.
+
+       The implementation swaps between two different directory/file
+       handles as it traverses the directories and does not use recursion
+       in an attempt to use memory efficiently.
+
+       If a callback wishes to stop the directory traversal it should
+       return false--in this case the function will stop the traversal,
+       tidy up and return false.
+
+       If a directory path doesn't exist at some point this function will
+       also return false and not subsequently call the callback.
+
+       If a directory path specified is complete, valid and the callback
+       did not indicate the traversal should be interrupted then this
+       function will return true.
+
+    */
+
+
+    SdFile subfile1;
+    SdFile subfile2;
+
+    char buffer[PATH_COMPONENT_BUFFER_LEN];
+
+    unsigned int offset = 0;
+
+    SdFile *p_parent;
+    SdFile *p_child;
+
+    SdFile *p_tmp_sdfile;
+
+    p_child = &subfile1;
+
+    p_parent = &parentDir;
+
+    while (true) {
+
+      boolean moreComponents = getNextPathComponent(filepath, &offset, buffer);
+
+      boolean shouldContinue = callback((*p_parent), buffer, !moreComponents, object);
+
+      if (!shouldContinue) {
+        // TODO: Don't repeat this code?
+        // If it's one we've created then we
+        // don't need the parent handle anymore.
+        if (p_parent != &parentDir) {
+          (*p_parent).close();
+        }
+        return false;
+      }
+
+      if (!moreComponents) {
+        break;
+      }
+
+      boolean exists = (*p_child).open(*p_parent, buffer, O_RDONLY);
+
+      // If it's one we've created then we
+      // don't need the parent handle anymore.
+      if (p_parent != &parentDir) {
+        (*p_parent).close();
+      }
+
+      // Handle case when it doesn't exist and we can't continue...
+      if (exists) {
+        // We alternate between two file handles as we go down
+        // the path.
+        if (p_parent == &parentDir) {
+          p_parent = &subfile2;
+        }
+
+        p_tmp_sdfile = p_parent;
+        p_parent = p_child;
+        p_child = p_tmp_sdfile;
+      } else {
+        return false;
+      }
+    }
+
+    if (p_parent != &parentDir) {
+      (*p_parent).close(); // TODO: Return/ handle different?
+    }
+
+    return true;
+  }
+
+
+
+  /*
+
+     The callbacks used to implement various functionality follow.
+
+     Each callback is supplied with a parent directory handle,
+     character string with the name of the current file path component,
+     a flag indicating if this component is the last in the path and
+     a pointer to an arbitrary object used for context.
+
+  */
+
+  boolean callback_pathExists(SdFile& parentDir, const char *filePathComponent,
+                              boolean /* isLastComponent */, void * /* object */) {
+    /*
+
+      Callback used to determine if a file/directory exists in parent
+      directory.
+
+      Returns true if file path exists.
+
+    */
+    SdFile child;
+
+    boolean exists = child.open(parentDir, filePathComponent, O_RDONLY);
+
+    if (exists) {
+      child.close();
+    }
+
+    return exists;
+  }
+
+
+
+  boolean callback_makeDirPath(SdFile& parentDir, const char *filePathComponent,
+                               boolean isLastComponent, void *object) {
+    /*
+
+      Callback used to create a directory in the parent directory if
+      it does not already exist.
+
+      Returns true if a directory was created or it already existed.
+
+    */
+    boolean result = false;
+    SdFile child;
+
+    result = callback_pathExists(parentDir, filePathComponent, isLastComponent, object);
+    if (!result) {
+      result = child.makeDir(parentDir, filePathComponent);
+    }
+
+    return result;
+  }
+
+
+  /*
+
+    boolean callback_openPath(SdFile& parentDir, char *filePathComponent,
+                 boolean isLastComponent, void *object) {
+
+    Callback used to open a file specified by a filepath that may
+    specify one or more directories above it.
+
+    Expects the context object to be an instance of `SDClass` and
+    will use the `file` property of the instance to open the requested
+    file/directory with the associated file open mode property.
+
+    Always returns true if the directory traversal hasn't reached the
+    bottom of the directory hierarchy.
+
+    Returns false once the file has been opened--to prevent the traversal
+    from descending further. (This may be unnecessary.)
+
+    if (isLastComponent) {
+    SDClass *p_SD = static_cast<SDClass*>(object);
+    p_SD->file.open(parentDir, filePathComponent, p_SD->fileOpenMode);
+    if (p_SD->fileOpenMode == FILE_WRITE) {
+      p_SD->file.seekSet(p_SD->file.fileSize());
+    }
+    // TODO: Return file open result?
+    return false;
+    }
+    return true;
+    }
+  */
+
+
+
+  boolean callback_remove(SdFile& parentDir, const char *filePathComponent,
+                          boolean isLastComponent, void * /* object */) {
+    if (isLastComponent) {
+      return SdFile::remove(parentDir, filePathComponent);
+    }
+    return true;
+  }
+
+  boolean callback_rmdir(SdFile& parentDir, const char *filePathComponent,
+                         boolean isLastComponent, void * /* object */) {
+    if (isLastComponent) {
+      SdFile f;
+      if (!f.open(parentDir, filePathComponent, O_READ)) {
+        return false;
+      }
+      return f.rmDir();
+    }
+    return true;
+  }
+
+
+
+  /* Implementation of class used to create `SDCard` object. */
+
+
+
+  boolean SDClass::begin(uint8_t csPin) {
+    if (root.isOpen()) {
+      root.close();
+    }
+
+    /*
+
+      Performs the initialisation required by the sdfatlib library.
+
+      Return true if initialization succeeds, false otherwise.
+
+    */
+    return card.init(SPI_HALF_SPEED, csPin) &&
+           volume.init(card) &&
+           root.openRoot(volume);
+  }
+
+  boolean SDClass::begin(uint32_t clock, uint8_t csPin) {
+    if (root.isOpen()) {
+      root.close();
+    }
+
+    return card.init(SPI_HALF_SPEED, csPin) &&
+           card.setSpiClock(clock) &&
+           volume.init(card) &&
+           root.openRoot(volume);
+  }
+
+  //call this when a card is removed. It will allow you to insert and initialise a new card.
+  void SDClass::end() {
+    root.close();
+  }
+
+  // this little helper is used to traverse paths
+  SdFile SDClass::getParentDir(const char *filepath, int *index) {
+    // get parent directory
+    SdFile d1;
+    SdFile d2;
+
+    d1.openRoot(volume); // start with the mostparent, root!
+
+    // we'll use the pointers to swap between the two objects
+    SdFile *parent = &d1;
+    SdFile *subdir = &d2;
+
+    const char *origpath = filepath;
+
+    while (strchr(filepath, '/')) {
+
+      // get rid of leading /'s
+      if (filepath[0] == '/') {
+        filepath++;
+        continue;
+      }
+
+      if (! strchr(filepath, '/')) {
+        // it was in the root directory, so leave now
+        break;
+      }
+
+      // extract just the name of the next subdirectory
+      uint8_t idx = strchr(filepath, '/') - filepath;
+      if (idx > 12) {
+        idx = 12;  // don't let them specify long names
+      }
+      char subdirname[13];
+      strncpy(subdirname, filepath, idx);
+      subdirname[idx] = 0;
+
+      // close the subdir (we reuse them) if open
+      subdir->close();
+      if (! subdir->open(parent, subdirname, O_READ)) {
+        // failed to open one of the subdirectories
+        return SdFile();
+      }
+      // move forward to the next subdirectory
+      filepath += idx;
+
+      // we reuse the objects, close it.
+      parent->close();
+
+      // swap the pointers
+      SdFile *t = parent;
+      parent = subdir;
+      subdir = t;
+    }
+
+    *index = (int)(filepath - origpath);
+    // parent is now the parent directory of the file!
+    return *parent;
+  }
+
+
+  File SDClass::open(const char *filepath, uint8_t mode) {
+    /*
+
+       Open the supplied file path for reading or writing.
+
+       The file content can be accessed via the `file` property of
+       the `SDClass` object--this property is currently
+       a standard `SdFile` object from `sdfatlib`.
+
+       Defaults to read only.
+
+       If `write` is true, default action (when `append` is true) is to
+       append data to the end of the file.
+
+       If `append` is false then the file will be truncated first.
+
+       If the file does not exist and it is opened for writing the file
+       will be created.
+
+       An attempt to open a file for reading that does not exist is an
+       error.
+
+    */
+
+    int pathidx;
+
+    // do the interactive search
+    SdFile parentdir = getParentDir(filepath, &pathidx);
+    // no more subdirs!
+
+    filepath += pathidx;
+
+    if (! filepath[0]) {
+      // it was the directory itself!
+      return File(parentdir, "/");
+    }
+
+    // Open the file itself
+    SdFile file;
+
+    // failed to open a subdir!
+    if (!parentdir.isOpen()) {
+      return File();
+    }
+
+    if (! file.open(parentdir, filepath, mode)) {
+      return File();
+    }
+    // close the parent
+    parentdir.close();
+
+    if ((mode & (O_APPEND | O_WRITE)) == (O_APPEND | O_WRITE)) {
+      file.seekSet(file.fileSize());
+    }
+    return File(file, filepath);
+  }
+
+
+  /*
+    File SDClass::open(char *filepath, uint8_t mode) {
+    //
+
+       Open the supplied file path for reading or writing.
+
+       The file content can be accessed via the `file` property of
+       the `SDClass` object--this property is currently
+       a standard `SdFile` object from `sdfatlib`.
+
+       Defaults to read only.
+
+       If `write` is true, default action (when `append` is true) is to
+       append data to the end of the file.
+
+       If `append` is false then the file will be truncated first.
+
+       If the file does not exist and it is opened for writing the file
+       will be created.
+
+       An attempt to open a file for reading that does not exist is an
+       error.
+
+     //
+
+    // TODO: Allow for read&write? (Possibly not, as it requires seek.)
+
+    fileOpenMode = mode;
+    walkPath(filepath, root, callback_openPath, this);
+
+    return File();
+
+    }
+  */
+
+
+  //boolean SDClass::close() {
+  //  /*
+  //
+  //    Closes the file opened by the `open` method.
+  //
+  //   */
+  //  file.close();
+  //}
+
+
+  boolean SDClass::exists(const char *filepath) {
+    /*
+
+       Returns true if the supplied file path exists.
+
+    */
+    return walkPath(filepath, root, callback_pathExists);
+  }
+
+
+  //boolean SDClass::exists(char *filepath, SdFile& parentDir) {
+  //  /*
+  //
+  //     Returns true if the supplied file path rooted at `parentDir`
+  //     exists.
+  //
+  //   */
+  //  return walkPath(filepath, parentDir, callback_pathExists);
+  //}
+
+
+  boolean SDClass::mkdir(const char *filepath) {
+    /*
+
+      Makes a single directory or a hierarchy of directories.
+
+      A rough equivalent to `mkdir -p`.
+
+    */
+    return walkPath(filepath, root, callback_makeDirPath);
+  }
+
+  boolean SDClass::rmdir(const char *filepath) {
+    /*
+
+      Remove a single directory or a hierarchy of directories.
+
+      A rough equivalent to `rm -rf`.
+
+    */
+    return walkPath(filepath, root, callback_rmdir);
+  }
+
+  boolean SDClass::remove(const char *filepath) {
+    return walkPath(filepath, root, callback_remove);
+  }
+
+
+  // allows you to recurse into a directory
+  File File::openNextFile(uint8_t mode) {
+    dir_t p;
+
+    //Serial.print("\t\treading dir...");
+    while (_file->readDir(&p) > 0) {
+
+      // done if past last used entry
+      if (p.name[0] == DIR_NAME_FREE) {
+        //Serial.println("end");
+        return File();
+      }
+
+      // skip deleted entry and entries for . and  ..
+      if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') {
+        //Serial.println("dots");
+        continue;
+      }
+
+      // only list subdirectories and files
+      if (!DIR_IS_FILE_OR_SUBDIR(&p)) {
+        //Serial.println("notafile");
+        continue;
+      }
+
+      // print file name with possible blank fill
+      SdFile f;
+      char name[13];
+      _file->dirName(p, name);
+      //Serial.print("try to open file ");
+      //Serial.println(name);
+
+      if (f.open(_file, name, mode)) {
+        //Serial.println("OK!");
+        return File(f, name);
+      } else {
+        //Serial.println("ugh");
+        return File();
+      }
+    }
+
+    //Serial.println("nothing");
+    return File();
+  }
+
+  void File::rewindDirectory(void) {
+    if (isDirectory()) {
+      _file->rewind();
+    }
+  }
+
+  SDClass SD;
+
+};
diff --git a/arduino/libraries/SD/src/SD.h b/arduino/libraries/SD/src/SD.h
new file mode 100644
index 000000000..fa34ccd5d
--- /dev/null
+++ b/arduino/libraries/SD/src/SD.h
@@ -0,0 +1,138 @@
+/*
+
+  SD - a slightly more friendly wrapper for sdfatlib
+
+  This library aims to expose a subset of SD card functionality
+  in the form of a higher level "wrapper" object.
+
+  License: GNU General Public License V3
+          (Because sdfatlib is licensed with this.)
+
+  (C) Copyright 2010 SparkFun Electronics
+
+*/
+
+#ifndef __SD_H__
+#define __SD_H__
+
+#include <Arduino.h>
+
+#include "utility/SdFat.h"
+#include "utility/SdFatUtil.h"
+
+#define FILE_READ O_READ
+#define FILE_WRITE (O_READ | O_WRITE | O_CREAT | O_APPEND)
+
+namespace SDLib {
+
+  class File : public Stream {
+    private:
+      char _name[13]; // our name
+      SdFile *_file;  // underlying file pointer
+
+    public:
+      File(SdFile f, const char *name);     // wraps an underlying SdFile
+      File(void);      // 'empty' constructor
+      virtual size_t write(uint8_t);
+      virtual size_t write(const uint8_t *buf, size_t size);
+      virtual int availableForWrite();
+      virtual int read();
+      virtual int peek();
+      virtual int available();
+      virtual void flush();
+      int read(void *buf, uint16_t nbyte);
+      boolean seek(uint32_t pos);
+      uint32_t position();
+      uint32_t size();
+      void close();
+      operator bool();
+      char * name();
+
+      boolean isDirectory(void);
+      File openNextFile(uint8_t mode = O_RDONLY);
+      void rewindDirectory(void);
+
+      using Print::write;
+  };
+
+  class SDClass {
+
+    private:
+      // These are required for initialisation and use of sdfatlib
+      Sd2Card card;
+      SdVolume volume;
+      SdFile root;
+
+      // my quick&dirty iterator, should be replaced
+      SdFile getParentDir(const char *filepath, int *indx);
+    public:
+      // This needs to be called to set up the connection to the SD card
+      // before other methods are used.
+      boolean begin(uint8_t csPin = SD_CHIP_SELECT_PIN);
+      boolean begin(uint32_t clock, uint8_t csPin);
+
+      //call this when a card is removed. It will allow you to insert and initialise a new card.
+      void end();
+
+      // Open the specified file/directory with the supplied mode (e.g. read or
+      // write, etc). Returns a File object for interacting with the file.
+      // Note that currently only one file can be open at a time.
+      File open(const char *filename, uint8_t mode = FILE_READ);
+      File open(const String &filename, uint8_t mode = FILE_READ) {
+        return open(filename.c_str(), mode);
+      }
+
+      // Methods to determine if the requested file path exists.
+      boolean exists(const char *filepath);
+      boolean exists(const String &filepath) {
+        return exists(filepath.c_str());
+      }
+
+      // Create the requested directory heirarchy--if intermediate directories
+      // do not exist they will be created.
+      boolean mkdir(const char *filepath);
+      boolean mkdir(const String &filepath) {
+        return mkdir(filepath.c_str());
+      }
+
+      // Delete the file.
+      boolean remove(const char *filepath);
+      boolean remove(const String &filepath) {
+        return remove(filepath.c_str());
+      }
+
+      boolean rmdir(const char *filepath);
+      boolean rmdir(const String &filepath) {
+        return rmdir(filepath.c_str());
+      }
+
+    private:
+
+      // This is used to determine the mode used to open a file
+      // it's here because it's the easiest place to pass the
+      // information through the directory walking function. But
+      // it's probably not the best place for it.
+      // It shouldn't be set directly--it is set via the parameters to `open`.
+      int fileOpenMode;
+
+      friend class File;
+      friend boolean callback_openPath(SdFile&, const char *, boolean, void *);
+  };
+
+  extern SDClass SD;
+
+};
+
+// We enclose File and SD classes in namespace SDLib to avoid conflicts
+// with others legacy libraries that redefines File class.
+
+// This ensure compatibility with sketches that uses only SD library
+using namespace SDLib;
+
+// This allows sketches to use SDLib::File with other libraries (in the
+// sketch you must use SDFile instead of File to disambiguate)
+typedef SDLib::File    SDFile;
+typedef SDLib::SDClass SDFileSystemClass;
+#define SDFileSystem   SDLib::SD
+
+#endif
diff --git a/arduino/libraries/SD/src/utility/FatStructs.h b/arduino/libraries/SD/src/utility/FatStructs.h
new file mode 100644
index 000000000..84c1cc71b
--- /dev/null
+++ b/arduino/libraries/SD/src/utility/FatStructs.h
@@ -0,0 +1,418 @@
+/* Arduino SdFat Library
+   Copyright (C) 2009 by William Greiman
+
+   This file is part of the Arduino SdFat Library
+
+   This Library is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the Arduino SdFat Library.  If not, see
+   <http://www.gnu.org/licenses/>.
+*/
+#ifndef FatStructs_h
+#define FatStructs_h
+/**
+   \file
+   FAT file structures
+*/
+/*
+   mostly from Microsoft document fatgen103.doc
+   http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx
+*/
+//------------------------------------------------------------------------------
+/** Value for byte 510 of boot block or MBR */
+uint8_t const BOOTSIG0 = 0X55;
+/** Value for byte 511 of boot block or MBR */
+uint8_t const BOOTSIG1 = 0XAA;
+//------------------------------------------------------------------------------
+/**
+   \struct partitionTable
+   \brief MBR partition table entry
+
+   A partition table entry for a MBR formatted storage device.
+   The MBR partition table has four entries.
+*/
+struct partitionTable {
+  /**
+     Boot Indicator . Indicates whether the volume is the active
+     partition.  Legal values include: 0X00. Do not use for booting.
+     0X80 Active partition.
+  */
+  uint8_t  boot;
+  /**
+      Head part of Cylinder-head-sector address of the first block in
+      the partition. Legal values are 0-255. Only used in old PC BIOS.
+  */
+  uint8_t  beginHead;
+  /**
+     Sector part of Cylinder-head-sector address of the first block in
+     the partition. Legal values are 1-63. Only used in old PC BIOS.
+  */
+  unsigned beginSector : 6;
+  /** High bits cylinder for first block in partition. */
+  unsigned beginCylinderHigh : 2;
+  /**
+     Combine beginCylinderLow with beginCylinderHigh. Legal values
+     are 0-1023.  Only used in old PC BIOS.
+  */
+  uint8_t  beginCylinderLow;
+  /**
+     Partition type. See defines that begin with PART_TYPE_ for
+     some Microsoft partition types.
+  */
+  uint8_t  type;
+  /**
+     head part of cylinder-head-sector address of the last sector in the
+     partition.  Legal values are 0-255. Only used in old PC BIOS.
+  */
+  uint8_t  endHead;
+  /**
+     Sector part of cylinder-head-sector address of the last sector in
+     the partition.  Legal values are 1-63. Only used in old PC BIOS.
+  */
+  unsigned endSector : 6;
+  /** High bits of end cylinder */
+  unsigned endCylinderHigh : 2;
+  /**
+     Combine endCylinderLow with endCylinderHigh. Legal values
+     are 0-1023.  Only used in old PC BIOS.
+  */
+  uint8_t  endCylinderLow;
+  /** Logical block address of the first block in the partition. */
+  uint32_t firstSector;
+  /** Length of the partition, in blocks. */
+  uint32_t totalSectors;
+} __attribute__((packed));
+/** Type name for partitionTable */
+typedef struct partitionTable part_t;
+//------------------------------------------------------------------------------
+/**
+   \struct masterBootRecord
+
+   \brief Master Boot Record
+
+   The first block of a storage device that is formatted with a MBR.
+*/
+struct masterBootRecord {
+  /** Code Area for master boot program. */
+  uint8_t  codeArea[440];
+  /** Optional WindowsNT disk signature. May contain more boot code. */
+  uint32_t diskSignature;
+  /** Usually zero but may be more boot code. */
+  uint16_t usuallyZero;
+  /** Partition tables. */
+  part_t   part[4];
+  /** First MBR signature byte. Must be 0X55 */
+  uint8_t  mbrSig0;
+  /** Second MBR signature byte. Must be 0XAA */
+  uint8_t  mbrSig1;
+} __attribute__((packed));
+/** Type name for masterBootRecord */
+typedef struct masterBootRecord mbr_t;
+//------------------------------------------------------------------------------
+/**
+   \struct biosParmBlock
+
+   \brief BIOS parameter block
+
+    The BIOS parameter block describes the physical layout of a FAT volume.
+*/
+struct biosParmBlock {
+  /**
+     Count of bytes per sector. This value may take on only the
+     following values: 512, 1024, 2048 or 4096
+  */
+  uint16_t bytesPerSector;
+  /**
+     Number of sectors per allocation unit. This value must be a
+     power of 2 that is greater than 0. The legal values are
+     1, 2, 4, 8, 16, 32, 64, and 128.
+  */
+  uint8_t  sectorsPerCluster;
+  /**
+     Number of sectors before the first FAT.
+     This value must not be zero.
+  */
+  uint16_t reservedSectorCount;
+  /** The count of FAT data structures on the volume. This field should
+      always contain the value 2 for any FAT volume of any type.
+  */
+  uint8_t  fatCount;
+  /**
+    For FAT12 and FAT16 volumes, this field contains the count of
+    32-byte directory entries in the root directory. For FAT32 volumes,
+    this field must be set to 0. For FAT12 and FAT16 volumes, this
+    value should always specify a count that when multiplied by 32
+    results in a multiple of bytesPerSector.  FAT16 volumes should
+    use the value 512.
+  */
+  uint16_t rootDirEntryCount;
+  /**
+     This field is the old 16-bit total count of sectors on the volume.
+     This count includes the count of all sectors in all four regions
+     of the volume. This field can be 0; if it is 0, then totalSectors32
+     must be non-zero.  For FAT32 volumes, this field must be 0. For
+     FAT12 and FAT16 volumes, this field contains the sector count, and
+     totalSectors32 is 0 if the total sector count fits
+     (is less than 0x10000).
+  */
+  uint16_t totalSectors16;
+  /**
+     This dates back to the old MS-DOS 1.x media determination and is
+     no longer usually used for anything.  0xF8 is the standard value
+     for fixed (non-removable) media. For removable media, 0xF0 is
+     frequently used. Legal values are 0xF0 or 0xF8-0xFF.
+  */
+  uint8_t  mediaType;
+  /**
+     Count of sectors occupied by one FAT on FAT12/FAT16 volumes.
+     On FAT32 volumes this field must be 0, and sectorsPerFat32
+     contains the FAT size count.
+  */
+  uint16_t sectorsPerFat16;
+  /** Sectors per track for interrupt 0x13. Not used otherwise. */
+  uint16_t sectorsPerTrtack;
+  /** Number of heads for interrupt 0x13.  Not used otherwise. */
+  uint16_t headCount;
+  /**
+     Count of hidden sectors preceding the partition that contains this
+     FAT volume. This field is generally only relevant for media
+      visible on interrupt 0x13.
+  */
+  uint32_t hidddenSectors;
+  /**
+     This field is the new 32-bit total count of sectors on the volume.
+     This count includes the count of all sectors in all four regions
+     of the volume.  This field can be 0; if it is 0, then
+     totalSectors16 must be non-zero.
+  */
+  uint32_t totalSectors32;
+  /**
+     Count of sectors occupied by one FAT on FAT32 volumes.
+  */
+  uint32_t sectorsPerFat32;
+  /**
+     This field is only defined for FAT32 media and does not exist on
+     FAT12 and FAT16 media.
+     Bits 0-3 -- Zero-based number of active FAT.
+                 Only valid if mirroring is disabled.
+     Bits 4-6 -- Reserved.
+     Bit 7     -- 0 means the FAT is mirrored at runtime into all FATs.
+            -- 1 means only one FAT is active; it is the one referenced in bits 0-3.
+     Bits 8-15         -- Reserved.
+  */
+  uint16_t fat32Flags;
+  /**
+     FAT32 version. High byte is major revision number.
+     Low byte is minor revision number. Only 0.0 define.
+  */
+  uint16_t fat32Version;
+  /**
+     Cluster number of the first cluster of the root directory for FAT32.
+     This usually 2 but not required to be 2.
+  */
+  uint32_t fat32RootCluster;
+  /**
+     Sector number of FSINFO structure in the reserved area of the
+     FAT32 volume. Usually 1.
+  */
+  uint16_t fat32FSInfo;
+  /**
+     If non-zero, indicates the sector number in the reserved area
+     of the volume of a copy of the boot record. Usually 6.
+     No value other than 6 is recommended.
+  */
+  uint16_t fat32BackBootBlock;
+  /**
+     Reserved for future expansion. Code that formats FAT32 volumes
+     should always set all of the bytes of this field to 0.
+  */
+  uint8_t  fat32Reserved[12];
+} __attribute__((packed));
+/** Type name for biosParmBlock */
+typedef struct biosParmBlock bpb_t;
+//------------------------------------------------------------------------------
+/**
+   \struct fat32BootSector
+
+   \brief Boot sector for a FAT16 or FAT32 volume.
+
+*/
+struct fat32BootSector {
+  /** X86 jmp to boot program */
+  uint8_t  jmpToBootCode[3];
+  /** informational only - don't depend on it */
+  char     oemName[8];
+  /** BIOS Parameter Block */
+  bpb_t    bpb;
+  /** for int0x13 use value 0X80 for hard drive */
+  uint8_t  driveNumber;
+  /** used by Windows NT - should be zero for FAT */
+  uint8_t  reserved1;
+  /** 0X29 if next three fields are valid */
+  uint8_t  bootSignature;
+  /** usually generated by combining date and time */
+  uint32_t volumeSerialNumber;
+  /** should match volume label in root dir */
+  char     volumeLabel[11];
+  /** informational only - don't depend on it */
+  char     fileSystemType[8];
+  /** X86 boot code */
+  uint8_t  bootCode[420];
+  /** must be 0X55 */
+  uint8_t  bootSectorSig0;
+  /** must be 0XAA */
+  uint8_t  bootSectorSig1;
+} __attribute__((packed));
+//------------------------------------------------------------------------------
+// End Of Chain values for FAT entries
+/** FAT16 end of chain value used by Microsoft. */
+uint16_t const FAT16EOC = 0XFFFF;
+/** Minimum value for FAT16 EOC.  Use to test for EOC. */
+uint16_t const FAT16EOC_MIN = 0XFFF8;
+/** FAT32 end of chain value used by Microsoft. */
+uint32_t const FAT32EOC = 0X0FFFFFFF;
+/** Minimum value for FAT32 EOC.  Use to test for EOC. */
+uint32_t const FAT32EOC_MIN = 0X0FFFFFF8;
+/** Mask a for FAT32 entry. Entries are 28 bits. */
+uint32_t const FAT32MASK = 0X0FFFFFFF;
+
+/** Type name for fat32BootSector */
+typedef struct fat32BootSector fbs_t;
+//------------------------------------------------------------------------------
+/**
+   \struct directoryEntry
+   \brief FAT short directory entry
+
+   Short means short 8.3 name, not the entry size.
+
+   Date Format. A FAT directory entry date stamp is a 16-bit field that is
+   basically a date relative to the MS-DOS epoch of 01/01/1980. Here is the
+   format (bit 0 is the LSB of the 16-bit word, bit 15 is the MSB of the
+   16-bit word):
+
+   Bits 9-15: Count of years from 1980, valid value range 0-127
+   inclusive (1980-2107).
+
+   Bits 5-8: Month of year, 1 = January, valid value range 1-12 inclusive.
+
+   Bits 0-4: Day of month, valid value range 1-31 inclusive.
+
+   Time Format. A FAT directory entry time stamp is a 16-bit field that has
+   a granularity of 2 seconds. Here is the format (bit 0 is the LSB of the
+   16-bit word, bit 15 is the MSB of the 16-bit word).
+
+   Bits 11-15: Hours, valid value range 0-23 inclusive.
+
+   Bits 5-10: Minutes, valid value range 0-59 inclusive.
+
+   Bits 0-4: 2-second count, valid value range 0-29 inclusive (0 - 58 seconds).
+
+   The valid time range is from Midnight 00:00:00 to 23:59:58.
+*/
+struct directoryEntry {
+  /**
+     Short 8.3 name.
+     The first eight bytes contain the file name with blank fill.
+     The last three bytes contain the file extension with blank fill.
+  */
+  uint8_t  name[11];
+  /** Entry attributes.
+
+     The upper two bits of the attribute byte are reserved and should
+     always be set to 0 when a file is created and never modified or
+     looked at after that.  See defines that begin with DIR_ATT_.
+  */
+  uint8_t  attributes;
+  /**
+     Reserved for use by Windows NT. Set value to 0 when a file is
+     created and never modify or look at it after that.
+  */
+  uint8_t  reservedNT;
+  /**
+     The granularity of the seconds part of creationTime is 2 seconds
+     so this field is a count of tenths of a second and its valid
+     value range is 0-199 inclusive. (WHG note - seems to be hundredths)
+  */
+  uint8_t  creationTimeTenths;
+  /** Time file was created. */
+  uint16_t creationTime;
+  /** Date file was created. */
+  uint16_t creationDate;
+  /**
+     Last access date. Note that there is no last access time, only
+     a date.  This is the date of last read or write. In the case of
+     a write, this should be set to the same date as lastWriteDate.
+  */
+  uint16_t lastAccessDate;
+  /**
+     High word of this entry's first cluster number (always 0 for a
+     FAT12 or FAT16 volume).
+  */
+  uint16_t firstClusterHigh;
+  /** Time of last write. File creation is considered a write. */
+  uint16_t lastWriteTime;
+  /** Date of last write. File creation is considered a write. */
+  uint16_t lastWriteDate;
+  /** Low word of this entry's first cluster number. */
+  uint16_t firstClusterLow;
+  /** 32-bit unsigned holding this file's size in bytes. */
+  uint32_t fileSize;
+} __attribute__((packed));
+//------------------------------------------------------------------------------
+// Definitions for directory entries
+//
+/** Type name for directoryEntry */
+typedef struct directoryEntry dir_t;
+/** escape for name[0] = 0XE5 */
+uint8_t const DIR_NAME_0XE5 = 0X05;
+/** name[0] value for entry that is free after being "deleted" */
+uint8_t const DIR_NAME_DELETED = 0XE5;
+/** name[0] value for entry that is free and no allocated entries follow */
+uint8_t const DIR_NAME_FREE = 0X00;
+/** file is read-only */
+uint8_t const DIR_ATT_READ_ONLY = 0X01;
+/** File should hidden in directory listings */
+uint8_t const DIR_ATT_HIDDEN = 0X02;
+/** Entry is for a system file */
+uint8_t const DIR_ATT_SYSTEM = 0X04;
+/** Directory entry contains the volume label */
+uint8_t const DIR_ATT_VOLUME_ID = 0X08;
+/** Entry is for a directory */
+uint8_t const DIR_ATT_DIRECTORY = 0X10;
+/** Old DOS archive bit for backup support */
+uint8_t const DIR_ATT_ARCHIVE = 0X20;
+/** Test value for long name entry.  Test is
+  (d->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME. */
+uint8_t const DIR_ATT_LONG_NAME = 0X0F;
+/** Test mask for long name entry */
+uint8_t const DIR_ATT_LONG_NAME_MASK = 0X3F;
+/** defined attribute bits */
+uint8_t const DIR_ATT_DEFINED_BITS = 0X3F;
+/** Directory entry is part of a long name */
+static inline uint8_t DIR_IS_LONG_NAME(const dir_t* dir) {
+  return (dir->attributes & DIR_ATT_LONG_NAME_MASK) == DIR_ATT_LONG_NAME;
+}
+/** Mask for file/subdirectory tests */
+uint8_t const DIR_ATT_FILE_TYPE_MASK = (DIR_ATT_VOLUME_ID | DIR_ATT_DIRECTORY);
+/** Directory entry is for a file */
+static inline uint8_t DIR_IS_FILE(const dir_t* dir) {
+  return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == 0;
+}
+/** Directory entry is for a subdirectory */
+static inline uint8_t DIR_IS_SUBDIR(const dir_t* dir) {
+  return (dir->attributes & DIR_ATT_FILE_TYPE_MASK) == DIR_ATT_DIRECTORY;
+}
+/** Directory entry is for a file or subdirectory */
+static inline uint8_t DIR_IS_FILE_OR_SUBDIR(const dir_t* dir) {
+  return (dir->attributes & DIR_ATT_VOLUME_ID) == 0;
+}
+#endif  // FatStructs_h
diff --git a/arduino/libraries/SD/src/utility/Sd2Card.cpp b/arduino/libraries/SD/src/utility/Sd2Card.cpp
new file mode 100644
index 000000000..7cfbe73ee
--- /dev/null
+++ b/arduino/libraries/SD/src/utility/Sd2Card.cpp
@@ -0,0 +1,777 @@
+/* Arduino Sd2Card Library
+   Copyright (C) 2009 by William Greiman
+
+   This file is part of the Arduino Sd2Card Library
+
+   This Library is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the Arduino Sd2Card Library.  If not, see
+   <http://www.gnu.org/licenses/>.
+*/
+#define USE_SPI_LIB
+#include <Arduino.h>
+#include "Sd2Card.h"
+//------------------------------------------------------------------------------
+#ifndef SOFTWARE_SPI
+#ifdef USE_SPI_LIB
+
+  #ifndef SDCARD_SPI
+    #define SDCARD_SPI SPI
+  #endif
+
+  #include <SPI.h>
+  static SPISettings settings;
+#endif
+// functions for hardware SPI
+/** Send a byte to the card */
+static void spiSend(uint8_t b) {
+  #ifndef USE_SPI_LIB
+  SPDR = b;
+  while (!(SPSR & (1 << SPIF)))
+    ;
+  #else
+  SDCARD_SPI.transfer(b);
+  #endif
+}
+/** Receive a byte from the card */
+static  uint8_t spiRec(void) {
+  #ifndef USE_SPI_LIB
+  spiSend(0XFF);
+  return SPDR;
+  #else
+  return SDCARD_SPI.transfer(0xFF);
+  #endif
+}
+#else  // SOFTWARE_SPI
+//------------------------------------------------------------------------------
+/** nop to tune soft SPI timing */
+#define nop asm volatile ("nop\n\t")
+//------------------------------------------------------------------------------
+/** Soft SPI receive */
+uint8_t spiRec(void) {
+  uint8_t data = 0;
+  // no interrupts during byte receive - about 8 us
+  cli();
+  // output pin high - like sending 0XFF
+  fastDigitalWrite(SPI_MOSI_PIN, HIGH);
+
+  for (uint8_t i = 0; i < 8; i++) {
+    fastDigitalWrite(SPI_SCK_PIN, HIGH);
+
+    // adjust so SCK is nice
+    nop;
+    nop;
+
+    data <<= 1;
+
+    if (fastDigitalRead(SPI_MISO_PIN)) {
+      data |= 1;
+    }
+
+    fastDigitalWrite(SPI_SCK_PIN, LOW);
+  }
+  // enable interrupts
+  sei();
+  return data;
+}
+//------------------------------------------------------------------------------
+/** Soft SPI send */
+void spiSend(uint8_t data) {
+  // no interrupts during byte send - about 8 us
+  cli();
+  for (uint8_t i = 0; i < 8; i++) {
+    fastDigitalWrite(SPI_SCK_PIN, LOW);
+
+    fastDigitalWrite(SPI_MOSI_PIN, data & 0X80);
+
+    data <<= 1;
+
+    fastDigitalWrite(SPI_SCK_PIN, HIGH);
+  }
+  // hold SCK high for a few ns
+  nop;
+  nop;
+  nop;
+  nop;
+
+  fastDigitalWrite(SPI_SCK_PIN, LOW);
+  // enable interrupts
+  sei();
+}
+#endif  // SOFTWARE_SPI
+//------------------------------------------------------------------------------
+// send command and return error code.  Return zero for OK
+uint8_t Sd2Card::cardCommand(uint8_t cmd, uint32_t arg) {
+  // end read if in partialBlockRead mode
+  readEnd();
+
+  // select card
+  chipSelectLow();
+
+  // wait up to 300 ms if busy
+  waitNotBusy(300);
+
+  // send command
+  spiSend(cmd | 0x40);
+
+  // send argument
+  for (int8_t s = 24; s >= 0; s -= 8) {
+    spiSend(arg >> s);
+  }
+
+  // send CRC
+  uint8_t crc = 0XFF;
+  if (cmd == CMD0) {
+    crc = 0X95;  // correct crc for CMD0 with arg 0
+  }
+  if (cmd == CMD8) {
+    crc = 0X87;  // correct crc for CMD8 with arg 0X1AA
+  }
+  spiSend(crc);
+
+  // wait for response
+  for (uint8_t i = 0; ((status_ = spiRec()) & 0X80) && i != 0XFF; i++)
+    ;
+  return status_;
+}
+//------------------------------------------------------------------------------
+/**
+   Determine the size of an SD flash memory card.
+
+   \return The number of 512 byte data blocks in the card
+           or zero if an error occurs.
+*/
+uint32_t Sd2Card::cardSize(void) {
+  csd_t csd;
+  if (!readCSD(&csd)) {
+    return 0;
+  }
+  if (csd.v1.csd_ver == 0) {
+    uint8_t read_bl_len = csd.v1.read_bl_len;
+    uint16_t c_size = (csd.v1.c_size_high << 10)
+                      | (csd.v1.c_size_mid << 2) | csd.v1.c_size_low;
+    uint8_t c_size_mult = (csd.v1.c_size_mult_high << 1)
+                          | csd.v1.c_size_mult_low;
+    return (uint32_t)(c_size + 1) << (c_size_mult + read_bl_len - 7);
+  } else if (csd.v2.csd_ver == 1) {
+    uint32_t c_size = ((uint32_t)csd.v2.c_size_high << 16)
+                      | (csd.v2.c_size_mid << 8) | csd.v2.c_size_low;
+    return (c_size + 1) << 10;
+  } else {
+    error(SD_CARD_ERROR_BAD_CSD);
+    return 0;
+  }
+}
+//------------------------------------------------------------------------------
+static uint8_t chip_select_asserted = 0;
+
+void Sd2Card::chipSelectHigh(void) {
+  digitalWrite(chipSelectPin_, HIGH);
+  #ifdef USE_SPI_LIB
+  if (chip_select_asserted) {
+    chip_select_asserted = 0;
+    SDCARD_SPI.endTransaction();
+  }
+  #endif
+}
+//------------------------------------------------------------------------------
+void Sd2Card::chipSelectLow(void) {
+  #ifdef USE_SPI_LIB
+  if (!chip_select_asserted) {
+    chip_select_asserted = 1;
+    SDCARD_SPI.beginTransaction(settings);
+  }
+  #endif
+  digitalWrite(chipSelectPin_, LOW);
+}
+//------------------------------------------------------------------------------
+/** Erase a range of blocks.
+
+   \param[in] firstBlock The address of the first block in the range.
+   \param[in] lastBlock The address of the last block in the range.
+
+   \note This function requests the SD card to do a flash erase for a
+   range of blocks.  The data on the card after an erase operation is
+   either 0 or 1, depends on the card vendor.  The card must support
+   single block erase.
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+*/
+uint8_t Sd2Card::erase(uint32_t firstBlock, uint32_t lastBlock) {
+  if (!eraseSingleBlockEnable()) {
+    error(SD_CARD_ERROR_ERASE_SINGLE_BLOCK);
+    goto fail;
+  }
+  if (type_ != SD_CARD_TYPE_SDHC) {
+    firstBlock <<= 9;
+    lastBlock <<= 9;
+  }
+  if (cardCommand(CMD32, firstBlock)
+      || cardCommand(CMD33, lastBlock)
+      || cardCommand(CMD38, 0)) {
+    error(SD_CARD_ERROR_ERASE);
+    goto fail;
+  }
+  if (!waitNotBusy(SD_ERASE_TIMEOUT)) {
+    error(SD_CARD_ERROR_ERASE_TIMEOUT);
+    goto fail;
+  }
+  chipSelectHigh();
+  return true;
+
+fail:
+  chipSelectHigh();
+  return false;
+}
+//------------------------------------------------------------------------------
+/** Determine if card supports single block erase.
+
+   \return The value one, true, is returned if single block erase is supported.
+   The value zero, false, is returned if single block erase is not supported.
+*/
+uint8_t Sd2Card::eraseSingleBlockEnable(void) {
+  csd_t csd;
+  return readCSD(&csd) ? csd.v1.erase_blk_en : 0;
+}
+//------------------------------------------------------------------------------
+/**
+   Initialize an SD flash memory card.
+
+   \param[in] sckRateID SPI clock rate selector. See setSckRate().
+   \param[in] chipSelectPin SD chip select pin number.
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.  The reason for failure
+   can be determined by calling errorCode() and errorData().
+*/
+uint8_t Sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin) {
+  errorCode_ = inBlock_ = partialBlockRead_ = type_ = 0;
+  chipSelectPin_ = chipSelectPin;
+  // 16-bit init start time allows over a minute
+  unsigned int t0 = millis();
+  uint32_t arg;
+
+  // set pin modes
+  pinMode(chipSelectPin_, OUTPUT);
+  digitalWrite(chipSelectPin_, HIGH);
+  #ifndef USE_SPI_LIB
+  pinMode(SPI_MISO_PIN, INPUT);
+  pinMode(SPI_MOSI_PIN, OUTPUT);
+  pinMode(SPI_SCK_PIN, OUTPUT);
+  #endif
+
+  #ifndef SOFTWARE_SPI
+  #ifndef USE_SPI_LIB
+  // SS must be in output mode even it is not chip select
+  pinMode(SS_PIN, OUTPUT);
+  digitalWrite(SS_PIN, HIGH); // disable any SPI device using hardware SS pin
+  // Enable SPI, Master, clock rate f_osc/128
+  SPCR = (1 << SPE) | (1 << MSTR) | (1 << SPR1) | (1 << SPR0);
+  // clear double speed
+  SPSR &= ~(1 << SPI2X);
+  #else // USE_SPI_LIB
+  SDCARD_SPI.begin();
+  settings = SPISettings(250000, MSBFIRST, SPI_MODE0);
+  #endif // USE_SPI_LIB
+  #endif // SOFTWARE_SPI
+
+  // must supply min of 74 clock cycles with CS high.
+  #ifdef USE_SPI_LIB
+  SDCARD_SPI.beginTransaction(settings);
+  #endif
+  for (uint8_t i = 0; i < 10; i++) {
+    spiSend(0XFF);
+  }
+  #ifdef USE_SPI_LIB
+  SDCARD_SPI.endTransaction();
+  #endif
+
+  chipSelectLow();
+
+  // command to go idle in SPI mode
+  while ((status_ = cardCommand(CMD0, 0)) != R1_IDLE_STATE) {
+    unsigned int d = millis() - t0;
+    if (d > SD_INIT_TIMEOUT) {
+      error(SD_CARD_ERROR_CMD0);
+      goto fail;
+    }
+  }
+  // check SD version
+  if ((cardCommand(CMD8, 0x1AA) & R1_ILLEGAL_COMMAND)) {
+    type(SD_CARD_TYPE_SD1);
+  } else {
+    // only need last byte of r7 response
+    for (uint8_t i = 0; i < 4; i++) {
+      status_ = spiRec();
+    }
+    if (status_ != 0XAA) {
+      error(SD_CARD_ERROR_CMD8);
+      goto fail;
+    }
+    type(SD_CARD_TYPE_SD2);
+  }
+  // initialize card and send host supports SDHC if SD2
+  arg = type() == SD_CARD_TYPE_SD2 ? 0X40000000 : 0;
+
+  while ((status_ = cardAcmd(ACMD41, arg)) != R1_READY_STATE) {
+    // check for timeout
+    unsigned int d = millis() - t0;
+    if (d > SD_INIT_TIMEOUT) {
+      error(SD_CARD_ERROR_ACMD41);
+      goto fail;
+    }
+  }
+  // if SD2 read OCR register to check for SDHC card
+  if (type() == SD_CARD_TYPE_SD2) {
+    if (cardCommand(CMD58, 0)) {
+      error(SD_CARD_ERROR_CMD58);
+      goto fail;
+    }
+    if ((spiRec() & 0XC0) == 0XC0) {
+      type(SD_CARD_TYPE_SDHC);
+    }
+    // discard rest of ocr - contains allowed voltage range
+    for (uint8_t i = 0; i < 3; i++) {
+      spiRec();
+    }
+  }
+  chipSelectHigh();
+
+  #ifndef SOFTWARE_SPI
+  return setSckRate(sckRateID);
+  #else  // SOFTWARE_SPI
+  return true;
+  #endif  // SOFTWARE_SPI
+
+fail:
+  chipSelectHigh();
+  return false;
+}
+//------------------------------------------------------------------------------
+/**
+   Enable or disable partial block reads.
+
+   Enabling partial block reads improves performance by allowing a block
+   to be read over the SPI bus as several sub-blocks.  Errors may occur
+   if the time between reads is too long since the SD card may timeout.
+   The SPI SS line will be held low until the entire block is read or
+   readEnd() is called.
+
+   Use this for applications like the Adafruit Wave Shield.
+
+   \param[in] value The value TRUE (non-zero) or FALSE (zero).)
+*/
+void Sd2Card::partialBlockRead(uint8_t value) {
+  readEnd();
+  partialBlockRead_ = value;
+}
+//------------------------------------------------------------------------------
+/**
+   Read a 512 byte block from an SD card device.
+
+   \param[in] block Logical block to be read.
+   \param[out] dst Pointer to the location that will receive the data.
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+*/
+uint8_t Sd2Card::readBlock(uint32_t block, uint8_t* dst) {
+  return readData(block, 0, 512, dst);
+}
+//------------------------------------------------------------------------------
+/**
+   Read part of a 512 byte block from an SD card.
+
+   \param[in] block Logical block to be read.
+   \param[in] offset Number of bytes to skip at start of block
+   \param[out] dst Pointer to the location that will receive the data.
+   \param[in] count Number of bytes to read
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+*/
+uint8_t Sd2Card::readData(uint32_t block,
+                          uint16_t offset, uint16_t count, uint8_t* dst) {
+  if (count == 0) {
+    return true;
+  }
+  if ((count + offset) > 512) {
+    goto fail;
+  }
+  if (!inBlock_ || block != block_ || offset < offset_) {
+    block_ = block;
+    // use address if not SDHC card
+    if (type() != SD_CARD_TYPE_SDHC) {
+      block <<= 9;
+    }
+    if (cardCommand(CMD17, block)) {
+      error(SD_CARD_ERROR_CMD17);
+      goto fail;
+    }
+    if (!waitStartBlock()) {
+      goto fail;
+    }
+    offset_ = 0;
+    inBlock_ = 1;
+  }
+
+  #ifdef OPTIMIZE_HARDWARE_SPI
+  // start first spi transfer
+  SPDR = 0XFF;
+
+  // skip data before offset
+  for (; offset_ < offset; offset_++) {
+    while (!(SPSR & (1 << SPIF)))
+      ;
+    SPDR = 0XFF;
+  }
+  // transfer data
+  n = count - 1;
+  for (uint16_t i = 0; i < n; i++) {
+    while (!(SPSR & (1 << SPIF)))
+      ;
+    dst[i] = SPDR;
+    SPDR = 0XFF;
+  }
+  // wait for last byte
+  while (!(SPSR & (1 << SPIF)))
+    ;
+  dst[n] = SPDR;
+
+  #else  // OPTIMIZE_HARDWARE_SPI
+
+  // skip data before offset
+  for (; offset_ < offset; offset_++) {
+    spiRec();
+  }
+  // transfer data
+  for (uint16_t i = 0; i < count; i++) {
+    dst[i] = spiRec();
+  }
+  #endif  // OPTIMIZE_HARDWARE_SPI
+
+  offset_ += count;
+  if (!partialBlockRead_ || offset_ >= 512) {
+    // read rest of data, checksum and set chip select high
+    readEnd();
+  }
+  return true;
+
+fail:
+  chipSelectHigh();
+  return false;
+}
+//------------------------------------------------------------------------------
+/** Skip remaining data in a block when in partial block read mode. */
+void Sd2Card::readEnd(void) {
+  if (inBlock_) {
+    // skip data and crc
+    #ifdef OPTIMIZE_HARDWARE_SPI
+    // optimize skip for hardware
+    SPDR = 0XFF;
+    while (offset_++ < 513) {
+      while (!(SPSR & (1 << SPIF)))
+        ;
+      SPDR = 0XFF;
+    }
+    // wait for last crc byte
+    while (!(SPSR & (1 << SPIF)))
+      ;
+    #else  // OPTIMIZE_HARDWARE_SPI
+    while (offset_++ < 514) {
+      spiRec();
+    }
+    #endif  // OPTIMIZE_HARDWARE_SPI
+    chipSelectHigh();
+    inBlock_ = 0;
+  }
+}
+//------------------------------------------------------------------------------
+/** read CID or CSR register */
+uint8_t Sd2Card::readRegister(uint8_t cmd, void* buf) {
+  uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
+  if (cardCommand(cmd, 0)) {
+    error(SD_CARD_ERROR_READ_REG);
+    goto fail;
+  }
+  if (!waitStartBlock()) {
+    goto fail;
+  }
+  // transfer data
+  for (uint16_t i = 0; i < 16; i++) {
+    dst[i] = spiRec();
+  }
+  spiRec();  // get first crc byte
+  spiRec();  // get second crc byte
+  chipSelectHigh();
+  return true;
+
+fail:
+  chipSelectHigh();
+  return false;
+}
+//------------------------------------------------------------------------------
+/**
+   Set the SPI clock rate.
+
+   \param[in] sckRateID A value in the range [0, 6].
+
+   The SPI clock will be set to F_CPU/pow(2, 1 + sckRateID). The maximum
+   SPI rate is F_CPU/2 for \a sckRateID = 0 and the minimum rate is F_CPU/128
+   for \a scsRateID = 6.
+
+   \return The value one, true, is returned for success and the value zero,
+   false, is returned for an invalid value of \a sckRateID.
+*/
+uint8_t Sd2Card::setSckRate(uint8_t sckRateID) {
+  if (sckRateID > 6) {
+    error(SD_CARD_ERROR_SCK_RATE);
+    return false;
+  }
+  #ifndef USE_SPI_LIB
+  // see avr processor datasheet for SPI register bit definitions
+  if ((sckRateID & 1) || sckRateID == 6) {
+    SPSR &= ~(1 << SPI2X);
+  } else {
+    SPSR |= (1 << SPI2X);
+  }
+  SPCR &= ~((1 << SPR1) | (1 << SPR0));
+  SPCR |= (sckRateID & 4 ? (1 << SPR1) : 0)
+          | (sckRateID & 2 ? (1 << SPR0) : 0);
+  #else // USE_SPI_LIB
+  switch (sckRateID) {
+    case 0:  settings = SPISettings(25000000, MSBFIRST, SPI_MODE0); break;
+    case 1:  settings = SPISettings(4000000, MSBFIRST, SPI_MODE0); break;
+    case 2:  settings = SPISettings(2000000, MSBFIRST, SPI_MODE0); break;
+    case 3:  settings = SPISettings(1000000, MSBFIRST, SPI_MODE0); break;
+    case 4:  settings = SPISettings(500000, MSBFIRST, SPI_MODE0); break;
+    case 5:  settings = SPISettings(250000, MSBFIRST, SPI_MODE0); break;
+    default: settings = SPISettings(125000, MSBFIRST, SPI_MODE0);
+  }
+  #endif // USE_SPI_LIB
+  return true;
+}
+#ifdef USE_SPI_LIB
+//------------------------------------------------------------------------------
+// set the SPI clock frequency
+uint8_t Sd2Card::setSpiClock(uint32_t clock) {
+  settings = SPISettings(clock, MSBFIRST, SPI_MODE0);
+  return true;
+}
+#endif
+//------------------------------------------------------------------------------
+// wait for card to go not busy
+uint8_t Sd2Card::waitNotBusy(unsigned int timeoutMillis) {
+  unsigned int t0 = millis();
+  unsigned int d;
+  do {
+    if (spiRec() == 0XFF) {
+      return true;
+    }
+    d = millis() - t0;
+  } while (d < timeoutMillis);
+  return false;
+}
+//------------------------------------------------------------------------------
+/** Wait for start block token */
+uint8_t Sd2Card::waitStartBlock(void) {
+  unsigned int t0 = millis();
+  while ((status_ = spiRec()) == 0XFF) {
+    unsigned int d = millis() - t0;
+    if (d > SD_READ_TIMEOUT) {
+      error(SD_CARD_ERROR_READ_TIMEOUT);
+      goto fail;
+    }
+  }
+  if (status_ != DATA_START_BLOCK) {
+    error(SD_CARD_ERROR_READ);
+    goto fail;
+  }
+  return true;
+
+fail:
+  chipSelectHigh();
+  return false;
+}
+//------------------------------------------------------------------------------
+/**
+   Writes a 512 byte block to an SD card.
+
+   \param[in] blockNumber Logical block to be written.
+   \param[in] src Pointer to the location of the data to be written.
+   \param[in] blocking If the write should be blocking.
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+*/
+uint8_t Sd2Card::writeBlock(uint32_t blockNumber, const uint8_t* src, uint8_t blocking) {
+  #if SD_PROTECT_BLOCK_ZERO
+  // don't allow write to first block
+  if (blockNumber == 0) {
+    error(SD_CARD_ERROR_WRITE_BLOCK_ZERO);
+    goto fail;
+  }
+  #endif  // SD_PROTECT_BLOCK_ZERO
+
+  // use address if not SDHC card
+  if (type() != SD_CARD_TYPE_SDHC) {
+    blockNumber <<= 9;
+  }
+  if (cardCommand(CMD24, blockNumber)) {
+    error(SD_CARD_ERROR_CMD24);
+    goto fail;
+  }
+  if (!writeData(DATA_START_BLOCK, src)) {
+    goto fail;
+  }
+  if (blocking) {
+    // wait for flash programming to complete
+    if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
+      error(SD_CARD_ERROR_WRITE_TIMEOUT);
+      goto fail;
+    }
+    // response is r2 so get and check two bytes for nonzero
+    if (cardCommand(CMD13, 0) || spiRec()) {
+      error(SD_CARD_ERROR_WRITE_PROGRAMMING);
+      goto fail;
+    }
+  }
+  chipSelectHigh();
+  return true;
+
+fail:
+  chipSelectHigh();
+  return false;
+}
+//------------------------------------------------------------------------------
+/** Write one data block in a multiple block write sequence */
+uint8_t Sd2Card::writeData(const uint8_t* src) {
+  // wait for previous write to finish
+  if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
+    error(SD_CARD_ERROR_WRITE_MULTIPLE);
+    chipSelectHigh();
+    return false;
+  }
+  return writeData(WRITE_MULTIPLE_TOKEN, src);
+}
+//------------------------------------------------------------------------------
+// send one block of data for write block or write multiple blocks
+uint8_t Sd2Card::writeData(uint8_t token, const uint8_t* src) {
+  #ifdef OPTIMIZE_HARDWARE_SPI
+
+  // send data - optimized loop
+  SPDR = token;
+
+  // send two byte per iteration
+  for (uint16_t i = 0; i < 512; i += 2) {
+    while (!(SPSR & (1 << SPIF)))
+      ;
+    SPDR = src[i];
+    while (!(SPSR & (1 << SPIF)))
+      ;
+    SPDR = src[i + 1];
+  }
+
+  // wait for last data byte
+  while (!(SPSR & (1 << SPIF)))
+    ;
+
+  #else  // OPTIMIZE_HARDWARE_SPI
+  spiSend(token);
+  for (uint16_t i = 0; i < 512; i++) {
+    spiSend(src[i]);
+  }
+  #endif  // OPTIMIZE_HARDWARE_SPI
+  spiSend(0xff);  // dummy crc
+  spiSend(0xff);  // dummy crc
+
+  status_ = spiRec();
+  if ((status_ & DATA_RES_MASK) != DATA_RES_ACCEPTED) {
+    error(SD_CARD_ERROR_WRITE);
+    chipSelectHigh();
+    return false;
+  }
+  return true;
+}
+//------------------------------------------------------------------------------
+/** Start a write multiple blocks sequence.
+
+   \param[in] blockNumber Address of first block in sequence.
+   \param[in] eraseCount The number of blocks to be pre-erased.
+
+   \note This function is used with writeData() and writeStop()
+   for optimized multiple block writes.
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+*/
+uint8_t Sd2Card::writeStart(uint32_t blockNumber, uint32_t eraseCount) {
+  #if SD_PROTECT_BLOCK_ZERO
+  // don't allow write to first block
+  if (blockNumber == 0) {
+    error(SD_CARD_ERROR_WRITE_BLOCK_ZERO);
+    goto fail;
+  }
+  #endif  // SD_PROTECT_BLOCK_ZERO
+  // send pre-erase count
+  if (cardAcmd(ACMD23, eraseCount)) {
+    error(SD_CARD_ERROR_ACMD23);
+    goto fail;
+  }
+  // use address if not SDHC card
+  if (type() != SD_CARD_TYPE_SDHC) {
+    blockNumber <<= 9;
+  }
+  if (cardCommand(CMD25, blockNumber)) {
+    error(SD_CARD_ERROR_CMD25);
+    goto fail;
+  }
+  return true;
+
+fail:
+  chipSelectHigh();
+  return false;
+}
+//------------------------------------------------------------------------------
+/** End a write multiple blocks sequence.
+
+  \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+*/
+uint8_t Sd2Card::writeStop(void) {
+  if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
+    goto fail;
+  }
+  spiSend(STOP_TRAN_TOKEN);
+  if (!waitNotBusy(SD_WRITE_TIMEOUT)) {
+    goto fail;
+  }
+  chipSelectHigh();
+  return true;
+
+fail:
+  error(SD_CARD_ERROR_STOP_TRAN);
+  chipSelectHigh();
+  return false;
+}
+//------------------------------------------------------------------------------
+/** Check if the SD card is busy
+
+  \return The value one, true, is returned when is busy and
+   the value zero, false, is returned for when is NOT busy.
+*/
+uint8_t Sd2Card::isBusy(void) {
+  chipSelectLow();
+  byte b = spiRec();
+  chipSelectHigh();
+
+  return (b != 0XFF);
+}
diff --git a/arduino/libraries/SD/src/utility/Sd2Card.h b/arduino/libraries/SD/src/utility/Sd2Card.h
new file mode 100644
index 000000000..5d91ebf51
--- /dev/null
+++ b/arduino/libraries/SD/src/utility/Sd2Card.h
@@ -0,0 +1,273 @@
+/* Arduino Sd2Card Library
+   Copyright (C) 2009 by William Greiman
+
+   This file is part of the Arduino Sd2Card Library
+
+   This Library is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the Arduino Sd2Card Library.  If not, see
+   <http://www.gnu.org/licenses/>.
+*/
+#ifndef Sd2Card_h
+#define Sd2Card_h
+/**
+   \file
+   Sd2Card class
+*/
+#include "Sd2PinMap.h"
+#include "SdInfo.h"
+/** Set SCK to max rate of F_CPU/2. See Sd2Card::setSckRate(). */
+uint8_t const SPI_FULL_SPEED = 0;
+/** Set SCK rate to F_CPU/4. See Sd2Card::setSckRate(). */
+uint8_t const SPI_HALF_SPEED = 1;
+/** Set SCK rate to F_CPU/8. Sd2Card::setSckRate(). */
+uint8_t const SPI_QUARTER_SPEED = 2;
+/**
+   USE_SPI_LIB: if set, use the SPI library bundled with Arduino IDE, otherwise
+   run with a standalone driver for AVR.
+*/
+#define USE_SPI_LIB
+/**
+   Define MEGA_SOFT_SPI non-zero to use software SPI on Mega Arduinos.
+   Pins used are SS 10, MOSI 11, MISO 12, and SCK 13.
+
+   MEGA_SOFT_SPI allows an unmodified Adafruit GPS Shield to be used
+   on Mega Arduinos.  Software SPI works well with GPS Shield V1.1
+   but many SD cards will fail with GPS Shield V1.0.
+*/
+#define MEGA_SOFT_SPI 0
+//------------------------------------------------------------------------------
+#if MEGA_SOFT_SPI && (defined(__AVR_ATmega1280__)||defined(__AVR_ATmega2560__))
+  #define SOFTWARE_SPI
+#endif  // MEGA_SOFT_SPI
+//------------------------------------------------------------------------------
+// SPI pin definitions
+//
+#ifndef SOFTWARE_SPI
+  // hardware pin defs
+
+  // include pins_arduino.h or variant.h depending on architecture, via Arduino.h
+  #include <Arduino.h>
+
+  /**
+  SD Chip Select pin
+
+  Warning if this pin is redefined the hardware SS will pin will be enabled
+  as an output by init().  An avr processor will not function as an SPI
+  master unless SS is set to output mode.
+  */
+  #ifndef SDCARD_SS_PIN
+    /** The default chip select pin for the SD card is SS. */
+    uint8_t const  SD_CHIP_SELECT_PIN = SS;
+  #else
+    uint8_t const  SD_CHIP_SELECT_PIN = SDCARD_SS_PIN;
+  #endif
+
+  // The following three pins must not be redefined for hardware SPI,
+  // so ensure that they are taken from pins_arduino.h or variant.h, depending on architecture.
+  #ifndef SDCARD_MOSI_PIN
+    /** SPI Master Out Slave In pin */
+    uint8_t const  SPI_MOSI_PIN = MOSI;
+    /** SPI Master In Slave Out pin */
+    uint8_t const  SPI_MISO_PIN = MISO;
+    /** SPI Clock pin */
+    uint8_t const  SPI_SCK_PIN = SCK;
+  #else
+    uint8_t const  SPI_MOSI_PIN = SDCARD_MOSI_PIN;
+    uint8_t const  SPI_MISO_PIN = SDCARD_MISO_PIN;
+    uint8_t const  SPI_SCK_PIN = SDCARD_SCK_PIN;
+  #endif
+
+  /** optimize loops for hardware SPI */
+  #ifndef USE_SPI_LIB
+    #define OPTIMIZE_HARDWARE_SPI
+  #endif
+
+#else  // SOFTWARE_SPI
+  // define software SPI pins so Mega can use unmodified GPS Shield
+  /** SPI chip select pin */
+  uint8_t const SD_CHIP_SELECT_PIN = 10;
+  /** SPI Master Out Slave In pin */
+  uint8_t const SPI_MOSI_PIN = 11;
+  /** SPI Master In Slave Out pin */
+  uint8_t const SPI_MISO_PIN = 12;
+  /** SPI Clock pin */
+  uint8_t const SPI_SCK_PIN = 13;
+#endif  // SOFTWARE_SPI
+//------------------------------------------------------------------------------
+/** Protect block zero from write if nonzero */
+#define SD_PROTECT_BLOCK_ZERO 1
+/** init timeout ms */
+unsigned int const SD_INIT_TIMEOUT = 2000;
+/** erase timeout ms */
+unsigned int const SD_ERASE_TIMEOUT = 10000;
+/** read timeout ms */
+unsigned int const SD_READ_TIMEOUT = 300;
+/** write time out ms */
+unsigned int const SD_WRITE_TIMEOUT = 600;
+//------------------------------------------------------------------------------
+// SD card errors
+/** timeout error for command CMD0 */
+uint8_t const SD_CARD_ERROR_CMD0 = 0X1;
+/** CMD8 was not accepted - not a valid SD card*/
+uint8_t const SD_CARD_ERROR_CMD8 = 0X2;
+/** card returned an error response for CMD17 (read block) */
+uint8_t const SD_CARD_ERROR_CMD17 = 0X3;
+/** card returned an error response for CMD24 (write block) */
+uint8_t const SD_CARD_ERROR_CMD24 = 0X4;
+/**  WRITE_MULTIPLE_BLOCKS command failed */
+uint8_t const SD_CARD_ERROR_CMD25 = 0X05;
+/** card returned an error response for CMD58 (read OCR) */
+uint8_t const SD_CARD_ERROR_CMD58 = 0X06;
+/** SET_WR_BLK_ERASE_COUNT failed */
+uint8_t const SD_CARD_ERROR_ACMD23 = 0X07;
+/** card's ACMD41 initialization process timeout */
+uint8_t const SD_CARD_ERROR_ACMD41 = 0X08;
+/** card returned a bad CSR version field */
+uint8_t const SD_CARD_ERROR_BAD_CSD = 0X09;
+/** erase block group command failed */
+uint8_t const SD_CARD_ERROR_ERASE = 0X0A;
+/** card not capable of single block erase */
+uint8_t const SD_CARD_ERROR_ERASE_SINGLE_BLOCK = 0X0B;
+/** Erase sequence timed out */
+uint8_t const SD_CARD_ERROR_ERASE_TIMEOUT = 0X0C;
+/** card returned an error token instead of read data */
+uint8_t const SD_CARD_ERROR_READ = 0X0D;
+/** read CID or CSD failed */
+uint8_t const SD_CARD_ERROR_READ_REG = 0X0E;
+/** timeout while waiting for start of read data */
+uint8_t const SD_CARD_ERROR_READ_TIMEOUT = 0X0F;
+/** card did not accept STOP_TRAN_TOKEN */
+uint8_t const SD_CARD_ERROR_STOP_TRAN = 0X10;
+/** card returned an error token as a response to a write operation */
+uint8_t const SD_CARD_ERROR_WRITE = 0X11;
+/** attempt to write protected block zero */
+uint8_t const SD_CARD_ERROR_WRITE_BLOCK_ZERO = 0X12;
+/** card did not go ready for a multiple block write */
+uint8_t const SD_CARD_ERROR_WRITE_MULTIPLE = 0X13;
+/** card returned an error to a CMD13 status check after a write */
+uint8_t const SD_CARD_ERROR_WRITE_PROGRAMMING = 0X14;
+/** timeout occurred during write programming */
+uint8_t const SD_CARD_ERROR_WRITE_TIMEOUT = 0X15;
+/** incorrect rate selected */
+uint8_t const SD_CARD_ERROR_SCK_RATE = 0X16;
+//------------------------------------------------------------------------------
+// card types
+/** Standard capacity V1 SD card */
+uint8_t const SD_CARD_TYPE_SD1 = 1;
+/** Standard capacity V2 SD card */
+uint8_t const SD_CARD_TYPE_SD2 = 2;
+/** High Capacity SD card */
+uint8_t const SD_CARD_TYPE_SDHC = 3;
+//------------------------------------------------------------------------------
+/**
+   \class Sd2Card
+   \brief Raw access to SD and SDHC flash memory cards.
+*/
+class Sd2Card {
+  public:
+    /** Construct an instance of Sd2Card. */
+    Sd2Card(void) : errorCode_(0), inBlock_(0), partialBlockRead_(0), type_(0) {}
+    uint32_t cardSize(void);
+    uint8_t erase(uint32_t firstBlock, uint32_t lastBlock);
+    uint8_t eraseSingleBlockEnable(void);
+    /**
+       \return error code for last error. See Sd2Card.h for a list of error codes.
+    */
+    uint8_t errorCode(void) const {
+      return errorCode_;
+    }
+    /** \return error data for last error. */
+    uint8_t errorData(void) const {
+      return status_;
+    }
+    /**
+       Initialize an SD flash memory card with default clock rate and chip
+       select pin.  See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin).
+    */
+    uint8_t init(void) {
+      return init(SPI_FULL_SPEED, SD_CHIP_SELECT_PIN);
+    }
+    /**
+       Initialize an SD flash memory card with the selected SPI clock rate
+       and the default SD chip select pin.
+       See sd2Card::init(uint8_t sckRateID, uint8_t chipSelectPin).
+    */
+    uint8_t init(uint8_t sckRateID) {
+      return init(sckRateID, SD_CHIP_SELECT_PIN);
+    }
+    uint8_t init(uint8_t sckRateID, uint8_t chipSelectPin);
+    void partialBlockRead(uint8_t value);
+    /** Returns the current value, true or false, for partial block read. */
+    uint8_t partialBlockRead(void) const {
+      return partialBlockRead_;
+    }
+    uint8_t readBlock(uint32_t block, uint8_t* dst);
+    uint8_t readData(uint32_t block,
+                     uint16_t offset, uint16_t count, uint8_t* dst);
+    /**
+       Read a cards CID register. The CID contains card identification
+       information such as Manufacturer ID, Product name, Product serial
+       number and Manufacturing date. */
+    uint8_t readCID(cid_t* cid) {
+      return readRegister(CMD10, cid);
+    }
+    /**
+       Read a cards CSD register. The CSD contains Card-Specific Data that
+       provides information regarding access to the card's contents. */
+    uint8_t readCSD(csd_t* csd) {
+      return readRegister(CMD9, csd);
+    }
+    void readEnd(void);
+    uint8_t setSckRate(uint8_t sckRateID);
+    #ifdef USE_SPI_LIB
+    uint8_t setSpiClock(uint32_t clock);
+    #endif
+    /** Return the card type: SD V1, SD V2 or SDHC */
+    uint8_t type(void) const {
+      return type_;
+    }
+    uint8_t writeBlock(uint32_t blockNumber, const uint8_t* src, uint8_t blocking = 1);
+    uint8_t writeData(const uint8_t* src);
+    uint8_t writeStart(uint32_t blockNumber, uint32_t eraseCount);
+    uint8_t writeStop(void);
+    uint8_t isBusy(void);
+  private:
+    uint32_t block_;
+    uint8_t chipSelectPin_;
+    uint8_t errorCode_;
+    uint8_t inBlock_;
+    uint16_t offset_;
+    uint8_t partialBlockRead_;
+    uint8_t status_;
+    uint8_t type_;
+    // private functions
+    uint8_t cardAcmd(uint8_t cmd, uint32_t arg) {
+      cardCommand(CMD55, 0);
+      return cardCommand(cmd, arg);
+    }
+    uint8_t cardCommand(uint8_t cmd, uint32_t arg);
+    void error(uint8_t code) {
+      errorCode_ = code;
+    }
+    uint8_t readRegister(uint8_t cmd, void* buf);
+    uint8_t sendWriteCommand(uint32_t blockNumber, uint32_t eraseCount);
+    void chipSelectHigh(void);
+    void chipSelectLow(void);
+    void type(uint8_t value) {
+      type_ = value;
+    }
+    uint8_t waitNotBusy(unsigned int timeoutMillis);
+    uint8_t writeData(uint8_t token, const uint8_t* src);
+    uint8_t waitStartBlock(void);
+};
+#endif  // Sd2Card_h
diff --git a/arduino/libraries/SD/src/utility/Sd2PinMap.h b/arduino/libraries/SD/src/utility/Sd2PinMap.h
new file mode 100644
index 000000000..0609ffe33
--- /dev/null
+++ b/arduino/libraries/SD/src/utility/Sd2PinMap.h
@@ -0,0 +1,525 @@
+/* Arduino SdFat Library
+   Copyright (C) 2010 by William Greiman
+
+   This file is part of the Arduino SdFat Library
+
+   This Library is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the Arduino SdFat Library.  If not, see
+   <http://www.gnu.org/licenses/>.
+*/
+#if defined(__arm__) // Arduino Due Board follows
+
+#ifndef Sd2PinMap_h
+  #define Sd2PinMap_h
+
+  #include <Arduino.h>
+
+  uint8_t const SS_PIN = SS;
+  uint8_t const MOSI_PIN = MOSI;
+  uint8_t const MISO_PIN = MISO;
+  uint8_t const SCK_PIN = SCK;
+
+#endif // Sd2PinMap_h
+
+#elif defined(__AVR_ATmega4809__) // Arduino UNO WiFI Rev2 follows
+
+#ifndef Sd2PinMap_h
+  #define Sd2PinMap_h
+
+  #include <Arduino.h>
+
+  uint8_t const SS_PIN = SS;
+  uint8_t const MOSI_PIN = MOSI;
+  uint8_t const MISO_PIN = MISO;
+  uint8_t const SCK_PIN = SCK;
+
+#endif // Sd2PinMap_h
+
+#elif defined(__AVR__) // Other AVR based Boards follows
+
+// Warning this file was generated by a program.
+#ifndef Sd2PinMap_h
+#define Sd2PinMap_h
+#include <avr/io.h>
+
+//------------------------------------------------------------------------------
+/** struct for mapping digital pins */
+struct pin_map_t {
+  volatile uint8_t* ddr;
+  volatile uint8_t* pin;
+  volatile uint8_t* port;
+  uint8_t bit;
+};
+//------------------------------------------------------------------------------
+#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+// Mega
+
+// Two Wire (aka I2C) ports
+uint8_t const SDA_PIN = 20;
+uint8_t const SCL_PIN = 21;
+
+// SPI port
+uint8_t const SS_PIN = 53;
+uint8_t const MOSI_PIN = 51;
+uint8_t const MISO_PIN = 50;
+uint8_t const SCK_PIN = 52;
+
+static const pin_map_t digitalPinMap[] = {
+  {&DDRE, &PINE, &PORTE, 0},  // E0  0
+  {&DDRE, &PINE, &PORTE, 1},  // E1  1
+  {&DDRE, &PINE, &PORTE, 4},  // E4  2
+  {&DDRE, &PINE, &PORTE, 5},  // E5  3
+  {&DDRG, &PING, &PORTG, 5},  // G5  4
+  {&DDRE, &PINE, &PORTE, 3},  // E3  5
+  {&DDRH, &PINH, &PORTH, 3},  // H3  6
+  {&DDRH, &PINH, &PORTH, 4},  // H4  7
+  {&DDRH, &PINH, &PORTH, 5},  // H5  8
+  {&DDRH, &PINH, &PORTH, 6},  // H6  9
+  {&DDRB, &PINB, &PORTB, 4},  // B4 10
+  {&DDRB, &PINB, &PORTB, 5},  // B5 11
+  {&DDRB, &PINB, &PORTB, 6},  // B6 12
+  {&DDRB, &PINB, &PORTB, 7},  // B7 13
+  {&DDRJ, &PINJ, &PORTJ, 1},  // J1 14
+  {&DDRJ, &PINJ, &PORTJ, 0},  // J0 15
+  {&DDRH, &PINH, &PORTH, 1},  // H1 16
+  {&DDRH, &PINH, &PORTH, 0},  // H0 17
+  {&DDRD, &PIND, &PORTD, 3},  // D3 18
+  {&DDRD, &PIND, &PORTD, 2},  // D2 19
+  {&DDRD, &PIND, &PORTD, 1},  // D1 20
+  {&DDRD, &PIND, &PORTD, 0},  // D0 21
+  {&DDRA, &PINA, &PORTA, 0},  // A0 22
+  {&DDRA, &PINA, &PORTA, 1},  // A1 23
+  {&DDRA, &PINA, &PORTA, 2},  // A2 24
+  {&DDRA, &PINA, &PORTA, 3},  // A3 25
+  {&DDRA, &PINA, &PORTA, 4},  // A4 26
+  {&DDRA, &PINA, &PORTA, 5},  // A5 27
+  {&DDRA, &PINA, &PORTA, 6},  // A6 28
+  {&DDRA, &PINA, &PORTA, 7},  // A7 29
+  {&DDRC, &PINC, &PORTC, 7},  // C7 30
+  {&DDRC, &PINC, &PORTC, 6},  // C6 31
+  {&DDRC, &PINC, &PORTC, 5},  // C5 32
+  {&DDRC, &PINC, &PORTC, 4},  // C4 33
+  {&DDRC, &PINC, &PORTC, 3},  // C3 34
+  {&DDRC, &PINC, &PORTC, 2},  // C2 35
+  {&DDRC, &PINC, &PORTC, 1},  // C1 36
+  {&DDRC, &PINC, &PORTC, 0},  // C0 37
+  {&DDRD, &PIND, &PORTD, 7},  // D7 38
+  {&DDRG, &PING, &PORTG, 2},  // G2 39
+  {&DDRG, &PING, &PORTG, 1},  // G1 40
+  {&DDRG, &PING, &PORTG, 0},  // G0 41
+  {&DDRL, &PINL, &PORTL, 7},  // L7 42
+  {&DDRL, &PINL, &PORTL, 6},  // L6 43
+  {&DDRL, &PINL, &PORTL, 5},  // L5 44
+  {&DDRL, &PINL, &PORTL, 4},  // L4 45
+  {&DDRL, &PINL, &PORTL, 3},  // L3 46
+  {&DDRL, &PINL, &PORTL, 2},  // L2 47
+  {&DDRL, &PINL, &PORTL, 1},  // L1 48
+  {&DDRL, &PINL, &PORTL, 0},  // L0 49
+  {&DDRB, &PINB, &PORTB, 3},  // B3 50
+  {&DDRB, &PINB, &PORTB, 2},  // B2 51
+  {&DDRB, &PINB, &PORTB, 1},  // B1 52
+  {&DDRB, &PINB, &PORTB, 0},  // B0 53
+  {&DDRF, &PINF, &PORTF, 0},  // F0 54
+  {&DDRF, &PINF, &PORTF, 1},  // F1 55
+  {&DDRF, &PINF, &PORTF, 2},  // F2 56
+  {&DDRF, &PINF, &PORTF, 3},  // F3 57
+  {&DDRF, &PINF, &PORTF, 4},  // F4 58
+  {&DDRF, &PINF, &PORTF, 5},  // F5 59
+  {&DDRF, &PINF, &PORTF, 6},  // F6 60
+  {&DDRF, &PINF, &PORTF, 7},  // F7 61
+  {&DDRK, &PINK, &PORTK, 0},  // K0 62
+  {&DDRK, &PINK, &PORTK, 1},  // K1 63
+  {&DDRK, &PINK, &PORTK, 2},  // K2 64
+  {&DDRK, &PINK, &PORTK, 3},  // K3 65
+  {&DDRK, &PINK, &PORTK, 4},  // K4 66
+  {&DDRK, &PINK, &PORTK, 5},  // K5 67
+  {&DDRK, &PINK, &PORTK, 6},  // K6 68
+  {&DDRK, &PINK, &PORTK, 7}   // K7 69
+};
+//------------------------------------------------------------------------------
+#elif (defined(__AVR_ATmega644P__) || defined(__AVR_ATmega1284P__)) && defined(CORE_MICRODUINO)
+// Microduino Core+
+
+// Two Wire (aka I2C) ports
+uint8_t const SDA_PIN = 20;
+uint8_t const SCL_PIN = 21;
+
+// SPI port
+uint8_t const SS_PIN = 10;
+uint8_t const MOSI_PIN = 11;
+uint8_t const MISO_PIN = 12;
+uint8_t const SCK_PIN = 13;
+
+static const pin_map_t digitalPinMap[] = {
+  {&DDRD, &PIND, &PORTD, 0},  // D0 PD0
+  {&DDRD, &PIND, &PORTD, 1},  // D1 PD1
+  {&DDRD, &PIND, &PORTD, 2},  // D2 PD2
+  {&DDRD, &PIND, &PORTD, 3},  // D3 PD3
+  {&DDRB, &PINB, &PORTB, 0},  // D4 PB0
+  {&DDRB, &PINB, &PORTB, 1},  // D5 PB1
+  {&DDRB, &PINB, &PORTB, 2},  // D6 PB2
+  {&DDRB, &PINB, &PORTB, 3},  // D7 PB3
+  {&DDRD, &PIND, &PORTD, 6},  // D8 PD6
+  {&DDRD, &PIND, &PORTD, 5},  // D9 PD5
+  {&DDRB, &PINB, &PORTB, 4},  // D10 PB4
+  {&DDRB, &PINB, &PORTB, 5},  // D11 PB5
+  {&DDRB, &PINB, &PORTB, 6},  // D12 PB6
+  {&DDRB, &PINB, &PORTB, 7},  // D13 PB7
+  {&DDRC, &PINC, &PORTC, 7},  // D14 PC7
+  {&DDRC, &PINC, &PORTC, 6},  // D15 PC6
+  {&DDRC, &PINC, &PORTC, 5},  // D16 PC5
+  {&DDRC, &PINC, &PORTC, 4},  // D17 PC4
+  {&DDRC, &PINC, &PORTC, 3},  // D18 PC3
+  {&DDRC, &PINC, &PORTC, 2},  // D19 PC2
+  {&DDRC, &PINC, &PORTC, 1},  // D20 PC1
+  {&DDRC, &PINC, &PORTC, 0},  // D21 PC0
+  {&DDRD, &PIND, &PORTD, 4},  // D22 PD4
+  {&DDRD, &PIND, &PORTD, 7},  // D23 PD7
+  {&DDRA, &PINA, &PORTA, 7},  // D24 PA7
+  {&DDRA, &PINA, &PORTA, 6},  // D25 PA6
+  {&DDRA, &PINA, &PORTA, 5},  // D26 PA5
+  {&DDRA, &PINA, &PORTA, 4},  // D27 PA4
+  {&DDRA, &PINA, &PORTA, 3},  // D28 PA3
+  {&DDRA, &PINA, &PORTA, 2},  // D29 PA2
+  {&DDRA, &PINA, &PORTA, 1},  // D30 PA1
+  {&DDRA, &PINA, &PORTA, 0}   // D31 PA0
+};
+//------------------------------------------------------------------------------
+#elif defined(__AVR_ATmega128RFA1__) && defined(CORE_MICRODUINO)
+// Microduino Core RF
+
+// Two Wire (aka I2C) ports
+uint8_t const SDA_PIN = 18;
+uint8_t const SCL_PIN = 19;
+
+// SPI port
+uint8_t const SS_PIN = 10;
+uint8_t const MOSI_PIN = 11;
+uint8_t const MISO_PIN = 12;
+uint8_t const SCK_PIN = 13;
+
+static const pin_map_t digitalPinMap[] = {
+  {&DDRD, &PINE, &PORTE, 0},  // D0 PE0
+  {&DDRD, &PINE, &PORTE, 1},  // D1 PE1
+  {&DDRD, &PIND, &PORTD, 2},  // D2 PD2
+  {&DDRD, &PIND, &PORTD, 3},  // D3 PD3
+  {&DDRB, &PINE, &PORTE, 3},  // D4 PE3
+  {&DDRB, &PINE, &PORTE, 4},  // D5 PE4
+  {&DDRB, &PINE, &PORTE, 5},  // D6 PE5
+  {&DDRB, &PINB, &PORTB, 7},  // D7 PB7
+  {&DDRD, &PINB, &PORTB, 6},  // D8 PB6
+  {&DDRD, &PINB, &PORTB, 5},  // D9 PB5
+  {&DDRB, &PINB, &PORTB, 4},  // D10 PB4
+  {&DDRB, &PINB, &PORTB, 2},  // D11 PB2
+  {&DDRB, &PINB, &PORTB, 3},  // D12 PB3
+  {&DDRB, &PINB, &PORTB, 1},  // D13 PB1
+  {&DDRF, &PINF, &PORTF, 7},  // D14 PF7
+  {&DDRF, &PINF, &PORTF, 6},  // D15 PF6
+  {&DDRF, &PINF, &PORTF, 5},  // D16 PF5
+  {&DDRF, &PINF, &PORTF, 4},  // D17 PF4
+  {&DDRD, &PIND, &PORTD, 1},  // D18 PD1
+  {&DDRD, &PIND, &PORTD, 0},  // D19 PD0
+  {&DDRF, &PINF, &PORTF, 3},  // D20 PF3
+  {&DDRF, &PINF, &PORTF, 2},  // D21 PF2
+};
+//------------------------------------------------------------------------------
+#elif defined(__AVR_ATmega32U4__) && defined(CORE_MICRODUINO)
+// Microduino Core USB
+
+// Two Wire (aka I2C) ports
+uint8_t const SDA_PIN = 18;
+uint8_t const SCL_PIN = 19;
+
+// SPI port
+uint8_t const SS_PIN = 10;
+uint8_t const MOSI_PIN = 11;
+uint8_t const MISO_PIN = 12;
+uint8_t const SCK_PIN = 13;
+
+static const pin_map_t digitalPinMap[] = {
+  {&DDRD, &PIND, &PORTD, 2},  // D0 - PD2
+  {&DDRD, &PIND, &PORTD, 3},  // D1 - PD3
+  {&DDRE, &PINE, &PORTE, 6},  // D2 - PE6
+  {&DDRD, &PIND, &PORTD, 6},  // D3 - PD6
+  {&DDRD, &PIND, &PORTD, 7},  // D4 - PD7
+  {&DDRC, &PINC, &PORTC, 6},  // D5 - PC6
+  {&DDRC, &PINC, &PORTC, 7},  // D6 - PC7
+  {&DDRE, &PINE, &PORTE, 7},  // D7 - PE7
+  {&DDRB, &PINB, &PORTB, 6},  // D8 - PB6
+  {&DDRB, &PINB, &PORTB, 5},  // D9 - PB5
+  {&DDRB, &PINB, &PORTB, 0},  // D10 - PB0
+  {&DDRB, &PINB, &PORTB, 2},  // D11 - MOSI - PB2
+  {&DDRB, &PINB, &PORTB, 3},  // D12 -MISO -  PB3
+  {&DDRB, &PINB, &PORTB, 1},  // D13 -SCK -  PB1
+  {&DDRF, &PINF, &PORTF, 7},  // D14 - A0 - PF7
+  {&DDRF, &PINF, &PORTF, 6},  // D15 - A1 - PF6
+  {&DDRF, &PINF, &PORTF, 5},  // D16 - A2 - PF5
+  {&DDRF, &PINF, &PORTF, 4},  // D17 - A3 - PF4
+  {&DDRD, &PIND, &PORTD, 1},  // D18 - PD1
+  {&DDRD, &PIND, &PORTD, 0},  // D19 - PD0
+  {&DDRF, &PINF, &PORTF, 1},  // D20 - A6 - PF1
+  {&DDRF, &PINF, &PORTF, 0},  // D21 - A7 - PF0
+};
+//------------------------------------------------------------------------------
+#elif defined(__AVR_ATmega644P__) || defined(__AVR_ATmega644__)
+// Sanguino
+
+// Two Wire (aka I2C) ports
+uint8_t const SDA_PIN = 17;
+uint8_t const SCL_PIN = 18;
+
+// SPI port
+uint8_t const SS_PIN = 4;
+uint8_t const MOSI_PIN = 5;
+uint8_t const MISO_PIN = 6;
+uint8_t const SCK_PIN = 7;
+
+static const pin_map_t digitalPinMap[] = {
+  {&DDRB, &PINB, &PORTB, 0},  // B0  0
+  {&DDRB, &PINB, &PORTB, 1},  // B1  1
+  {&DDRB, &PINB, &PORTB, 2},  // B2  2
+  {&DDRB, &PINB, &PORTB, 3},  // B3  3
+  {&DDRB, &PINB, &PORTB, 4},  // B4  4
+  {&DDRB, &PINB, &PORTB, 5},  // B5  5
+  {&DDRB, &PINB, &PORTB, 6},  // B6  6
+  {&DDRB, &PINB, &PORTB, 7},  // B7  7
+  {&DDRD, &PIND, &PORTD, 0},  // D0  8
+  {&DDRD, &PIND, &PORTD, 1},  // D1  9
+  {&DDRD, &PIND, &PORTD, 2},  // D2 10
+  {&DDRD, &PIND, &PORTD, 3},  // D3 11
+  {&DDRD, &PIND, &PORTD, 4},  // D4 12
+  {&DDRD, &PIND, &PORTD, 5},  // D5 13
+  {&DDRD, &PIND, &PORTD, 6},  // D6 14
+  {&DDRD, &PIND, &PORTD, 7},  // D7 15
+  {&DDRC, &PINC, &PORTC, 0},  // C0 16
+  {&DDRC, &PINC, &PORTC, 1},  // C1 17
+  {&DDRC, &PINC, &PORTC, 2},  // C2 18
+  {&DDRC, &PINC, &PORTC, 3},  // C3 19
+  {&DDRC, &PINC, &PORTC, 4},  // C4 20
+  {&DDRC, &PINC, &PORTC, 5},  // C5 21
+  {&DDRC, &PINC, &PORTC, 6},  // C6 22
+  {&DDRC, &PINC, &PORTC, 7},  // C7 23
+  {&DDRA, &PINA, &PORTA, 7},  // A7 24
+  {&DDRA, &PINA, &PORTA, 6},  // A6 25
+  {&DDRA, &PINA, &PORTA, 5},  // A5 26
+  {&DDRA, &PINA, &PORTA, 4},  // A4 27
+  {&DDRA, &PINA, &PORTA, 3},  // A3 28
+  {&DDRA, &PINA, &PORTA, 2},  // A2 29
+  {&DDRA, &PINA, &PORTA, 1},  // A1 30
+  {&DDRA, &PINA, &PORTA, 0}   // A0 31
+};
+//------------------------------------------------------------------------------
+#elif defined(__AVR_ATmega32U4__)
+// Leonardo
+
+// Two Wire (aka I2C) ports
+uint8_t const SDA_PIN = 2;
+uint8_t const SCL_PIN = 3;
+
+// SPI port
+uint8_t const SS_PIN = 17;
+uint8_t const MOSI_PIN = 16;
+uint8_t const MISO_PIN = 14;
+uint8_t const SCK_PIN = 15;
+
+static const pin_map_t digitalPinMap[] = {
+  {&DDRD, &PIND, &PORTD, 2},  // D2  0
+  {&DDRD, &PIND, &PORTD, 3},  // D3  1
+  {&DDRD, &PIND, &PORTD, 1},  // D1  2
+  {&DDRD, &PIND, &PORTD, 0},  // D0  3
+  {&DDRD, &PIND, &PORTD, 4},  // D4  4
+  {&DDRC, &PINC, &PORTC, 6},  // C6  5
+  {&DDRD, &PIND, &PORTD, 7},  // D7  6
+  {&DDRE, &PINE, &PORTE, 6},  // E6  7
+  {&DDRB, &PINB, &PORTB, 4},  // B4  8
+  {&DDRB, &PINB, &PORTB, 5},  // B5  9
+  {&DDRB, &PINB, &PORTB, 6},  // B6 10
+  {&DDRB, &PINB, &PORTB, 7},  // B7 11
+  {&DDRD, &PIND, &PORTD, 6},  // D6 12
+  {&DDRC, &PINC, &PORTC, 7},  // C7 13
+  {&DDRB, &PINB, &PORTB, 3},  // B3 14
+  {&DDRB, &PINB, &PORTB, 1},  // B1 15
+  {&DDRB, &PINB, &PORTB, 2},  // B2 16
+  {&DDRB, &PINB, &PORTB, 0},  // B0 17
+  {&DDRF, &PINF, &PORTF, 7},  // F7 18
+  {&DDRF, &PINF, &PORTF, 6},  // F6 19
+  {&DDRF, &PINF, &PORTF, 5},  // F5 20
+  {&DDRF, &PINF, &PORTF, 4},  // F4 21
+  {&DDRF, &PINF, &PORTF, 1},  // F1 22
+  {&DDRF, &PINF, &PORTF, 0},  // F0 23
+};
+//------------------------------------------------------------------------------
+#elif defined(__AVR_AT90USB646__) || defined(__AVR_AT90USB1286__)
+// Teensy++ 1.0 & 2.0
+
+// Two Wire (aka I2C) ports
+uint8_t const SDA_PIN = 1;
+uint8_t const SCL_PIN = 0;
+
+// SPI port
+uint8_t const SS_PIN = 20;
+uint8_t const MOSI_PIN = 22;
+uint8_t const MISO_PIN = 23;
+uint8_t const SCK_PIN = 21;
+
+static const pin_map_t digitalPinMap[] = {
+  {&DDRD, &PIND, &PORTD, 0},  // D0  0
+  {&DDRD, &PIND, &PORTD, 1},  // D1  1
+  {&DDRD, &PIND, &PORTD, 2},  // D2  2
+  {&DDRD, &PIND, &PORTD, 3},  // D3  3
+  {&DDRD, &PIND, &PORTD, 4},  // D4  4
+  {&DDRD, &PIND, &PORTD, 5},  // D5  5
+  {&DDRD, &PIND, &PORTD, 6},  // D6  6
+  {&DDRD, &PIND, &PORTD, 7},  // D7  7
+  {&DDRE, &PINE, &PORTE, 0},  // E0  8
+  {&DDRE, &PINE, &PORTE, 1},  // E1  9
+  {&DDRC, &PINC, &PORTC, 0},  // C0 10
+  {&DDRC, &PINC, &PORTC, 1},  // C1 11
+  {&DDRC, &PINC, &PORTC, 2},  // C2 12
+  {&DDRC, &PINC, &PORTC, 3},  // C3 13
+  {&DDRC, &PINC, &PORTC, 4},  // C4 14
+  {&DDRC, &PINC, &PORTC, 5},  // C5 15
+  {&DDRC, &PINC, &PORTC, 6},  // C6 16
+  {&DDRC, &PINC, &PORTC, 7},  // C7 17
+  {&DDRE, &PINE, &PORTE, 6},  // E6 18
+  {&DDRE, &PINE, &PORTE, 7},  // E7 19
+  {&DDRB, &PINB, &PORTB, 0},  // B0 20
+  {&DDRB, &PINB, &PORTB, 1},  // B1 21
+  {&DDRB, &PINB, &PORTB, 2},  // B2 22
+  {&DDRB, &PINB, &PORTB, 3},  // B3 23
+  {&DDRB, &PINB, &PORTB, 4},  // B4 24
+  {&DDRB, &PINB, &PORTB, 5},  // B5 25
+  {&DDRB, &PINB, &PORTB, 6},  // B6 26
+  {&DDRB, &PINB, &PORTB, 7},  // B7 27
+  {&DDRA, &PINA, &PORTA, 0},  // A0 28
+  {&DDRA, &PINA, &PORTA, 1},  // A1 29
+  {&DDRA, &PINA, &PORTA, 2},  // A2 30
+  {&DDRA, &PINA, &PORTA, 3},  // A3 31
+  {&DDRA, &PINA, &PORTA, 4},  // A4 32
+  {&DDRA, &PINA, &PORTA, 5},  // A5 33
+  {&DDRA, &PINA, &PORTA, 6},  // A6 34
+  {&DDRA, &PINA, &PORTA, 7},  // A7 35
+  {&DDRE, &PINE, &PORTE, 4},  // E4 36
+  {&DDRE, &PINE, &PORTE, 5},  // E5 37
+  {&DDRF, &PINF, &PORTF, 0},  // F0 38
+  {&DDRF, &PINF, &PORTF, 1},  // F1 39
+  {&DDRF, &PINF, &PORTF, 2},  // F2 40
+  {&DDRF, &PINF, &PORTF, 3},  // F3 41
+  {&DDRF, &PINF, &PORTF, 4},  // F4 42
+  {&DDRF, &PINF, &PORTF, 5},  // F5 43
+  {&DDRF, &PINF, &PORTF, 6},  // F6 44
+  {&DDRF, &PINF, &PORTF, 7}   // F7 45
+};
+//------------------------------------------------------------------------------
+#else  // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+// 168 and 328 Arduinos
+
+// Two Wire (aka I2C) ports
+uint8_t const SDA_PIN = 18;
+uint8_t const SCL_PIN = 19;
+
+// SPI port
+uint8_t const SS_PIN = 10;
+uint8_t const MOSI_PIN = 11;
+uint8_t const MISO_PIN = 12;
+uint8_t const SCK_PIN = 13;
+
+static const pin_map_t digitalPinMap[] = {
+  {&DDRD, &PIND, &PORTD, 0},  // D0  0
+  {&DDRD, &PIND, &PORTD, 1},  // D1  1
+  {&DDRD, &PIND, &PORTD, 2},  // D2  2
+  {&DDRD, &PIND, &PORTD, 3},  // D3  3
+  {&DDRD, &PIND, &PORTD, 4},  // D4  4
+  {&DDRD, &PIND, &PORTD, 5},  // D5  5
+  {&DDRD, &PIND, &PORTD, 6},  // D6  6
+  {&DDRD, &PIND, &PORTD, 7},  // D7  7
+  {&DDRB, &PINB, &PORTB, 0},  // B0  8
+  {&DDRB, &PINB, &PORTB, 1},  // B1  9
+  {&DDRB, &PINB, &PORTB, 2},  // B2 10
+  {&DDRB, &PINB, &PORTB, 3},  // B3 11
+  {&DDRB, &PINB, &PORTB, 4},  // B4 12
+  {&DDRB, &PINB, &PORTB, 5},  // B5 13
+  {&DDRC, &PINC, &PORTC, 0},  // C0 14
+  {&DDRC, &PINC, &PORTC, 1},  // C1 15
+  {&DDRC, &PINC, &PORTC, 2},  // C2 16
+  {&DDRC, &PINC, &PORTC, 3},  // C3 17
+  {&DDRC, &PINC, &PORTC, 4},  // C4 18
+  {&DDRC, &PINC, &PORTC, 5}   // C5 19
+};
+#endif  // defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
+//------------------------------------------------------------------------------
+static const uint8_t digitalPinCount = sizeof(digitalPinMap) / sizeof(pin_map_t);
+
+uint8_t badPinNumber(void)
+__attribute__((error("Pin number is too large or not a constant")));
+
+static inline __attribute__((always_inline))
+uint8_t getPinMode(uint8_t pin) {
+  if (__builtin_constant_p(pin) && pin < digitalPinCount) {
+    return (*digitalPinMap[pin].ddr >> digitalPinMap[pin].bit) & 1;
+  } else {
+    return badPinNumber();
+  }
+}
+static inline __attribute__((always_inline))
+void setPinMode(uint8_t pin, uint8_t mode) {
+  if (__builtin_constant_p(pin) && pin < digitalPinCount) {
+    if (mode) {
+      *digitalPinMap[pin].ddr |= 1 << digitalPinMap[pin].bit;
+    } else {
+      *digitalPinMap[pin].ddr &= ~(1 << digitalPinMap[pin].bit);
+    }
+  } else {
+    badPinNumber();
+  }
+}
+static inline __attribute__((always_inline))
+uint8_t fastDigitalRead(uint8_t pin) {
+  if (__builtin_constant_p(pin) && pin < digitalPinCount) {
+    return (*digitalPinMap[pin].pin >> digitalPinMap[pin].bit) & 1;
+  } else {
+    return badPinNumber();
+  }
+}
+static inline __attribute__((always_inline))
+void fastDigitalWrite(uint8_t pin, uint8_t value) {
+  if (__builtin_constant_p(pin) && pin < digitalPinCount) {
+    if (value) {
+      *digitalPinMap[pin].port |= 1 << digitalPinMap[pin].bit;
+    } else {
+      *digitalPinMap[pin].port &= ~(1 << digitalPinMap[pin].bit);
+    }
+  } else {
+    badPinNumber();
+  }
+}
+#endif  // Sd2PinMap_h
+
+#elif defined (__CPU_ARC__)
+
+#if defined (__ARDUINO_ARC__)
+  // Two Wire (aka I2C) ports
+  uint8_t const SDA_PIN = 18;
+  uint8_t const SCL_PIN = 19;
+
+  // SPI port
+  uint8_t const SS_PIN = 10;
+  uint8_t const MOSI_PIN = 11;
+  uint8_t const MISO_PIN = 12;
+  uint8_t const SCK_PIN = 13;
+
+#endif // Arduino ARC
+
+#else
+#error Architecture or board not supported.
+#endif
diff --git a/arduino/libraries/SD/src/utility/SdFat.h b/arduino/libraries/SD/src/utility/SdFat.h
new file mode 100644
index 000000000..e57974df8
--- /dev/null
+++ b/arduino/libraries/SD/src/utility/SdFat.h
@@ -0,0 +1,641 @@
+/* Arduino SdFat Library
+   Copyright (C) 2009 by William Greiman
+
+   This file is part of the Arduino SdFat Library
+
+   This Library is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the Arduino SdFat Library.  If not, see
+   <http://www.gnu.org/licenses/>.
+*/
+#ifndef SdFat_h
+#define SdFat_h
+/**
+   \file
+   SdFile and SdVolume classes
+*/
+#if defined (__AVR__) || defined (__CPU_ARC__)
+  #include <avr/pgmspace.h>
+#endif
+#include "Sd2Card.h"
+#include "FatStructs.h"
+#include <Print.h>
+//------------------------------------------------------------------------------
+/**
+   Allow use of deprecated functions if non-zero
+*/
+#define ALLOW_DEPRECATED_FUNCTIONS 1
+//------------------------------------------------------------------------------
+// forward declaration since SdVolume is used in SdFile
+class SdVolume;
+//==============================================================================
+// SdFile class
+
+#ifdef O_RDONLY //ARDUINO_ARCH_MBED
+#undef O_READ
+#undef O_RDONLY
+#undef O_WRITE
+#undef O_WRONLY
+#undef O_RDWR
+#undef O_ACCMODE
+#undef O_APPEND
+#undef O_SYNC
+#undef O_CREAT
+#undef O_EXCL
+#undef O_TRUNC
+#endif
+
+// flags for ls()
+/** ls() flag to print modify date */
+uint8_t const LS_DATE = 1;
+/** ls() flag to print file size */
+uint8_t const LS_SIZE = 2;
+/** ls() flag for recursive list of subdirectories */
+uint8_t const LS_R = 4;
+
+// use the gnu style oflag in open()
+/** open() oflag for reading */
+uint8_t const O_READ = 0X01;
+/** open() oflag - same as O_READ */
+uint8_t const O_RDONLY = O_READ;
+/** open() oflag for write */
+uint8_t const O_WRITE = 0X02;
+/** open() oflag - same as O_WRITE */
+uint8_t const O_WRONLY = O_WRITE;
+/** open() oflag for reading and writing */
+uint8_t const O_RDWR = (O_READ | O_WRITE);
+/** open() oflag mask for access modes */
+uint8_t const O_ACCMODE = (O_READ | O_WRITE);
+/** The file offset shall be set to the end of the file prior to each write. */
+uint8_t const O_APPEND = 0X04;
+/** synchronous writes - call sync() after each write */
+uint8_t const O_SYNC = 0X08;
+/** create the file if nonexistent */
+uint8_t const O_CREAT = 0X10;
+/** If O_CREAT and O_EXCL are set, open() shall fail if the file exists */
+uint8_t const O_EXCL = 0X20;
+/** truncate the file to zero length */
+uint8_t const O_TRUNC = 0X40;
+
+// flags for timestamp
+/** set the file's last access date */
+uint8_t const T_ACCESS = 1;
+/** set the file's creation date and time */
+uint8_t const T_CREATE = 2;
+/** Set the file's write date and time */
+uint8_t const T_WRITE = 4;
+// values for type_
+/** This SdFile has not been opened. */
+uint8_t const FAT_FILE_TYPE_CLOSED = 0;
+/** SdFile for a file */
+uint8_t const FAT_FILE_TYPE_NORMAL = 1;
+/** SdFile for a FAT16 root directory */
+uint8_t const FAT_FILE_TYPE_ROOT16 = 2;
+/** SdFile for a FAT32 root directory */
+uint8_t const FAT_FILE_TYPE_ROOT32 = 3;
+/** SdFile for a subdirectory */
+uint8_t const FAT_FILE_TYPE_SUBDIR = 4;
+/** Test value for directory type */
+uint8_t const FAT_FILE_TYPE_MIN_DIR = FAT_FILE_TYPE_ROOT16;
+
+/** date field for FAT directory entry */
+static inline uint16_t FAT_DATE(uint16_t year, uint8_t month, uint8_t day) {
+  return (year - 1980) << 9 | month << 5 | day;
+}
+/** year part of FAT directory date field */
+static inline uint16_t FAT_YEAR(uint16_t fatDate) {
+  return 1980 + (fatDate >> 9);
+}
+/** month part of FAT directory date field */
+static inline uint8_t FAT_MONTH(uint16_t fatDate) {
+  return (fatDate >> 5) & 0XF;
+}
+/** day part of FAT directory date field */
+static inline uint8_t FAT_DAY(uint16_t fatDate) {
+  return fatDate & 0X1F;
+}
+/** time field for FAT directory entry */
+static inline uint16_t FAT_TIME(uint8_t hour, uint8_t minute, uint8_t second) {
+  return hour << 11 | minute << 5 | second >> 1;
+}
+/** hour part of FAT directory time field */
+static inline uint8_t FAT_HOUR(uint16_t fatTime) {
+  return fatTime >> 11;
+}
+/** minute part of FAT directory time field */
+static inline uint8_t FAT_MINUTE(uint16_t fatTime) {
+  return (fatTime >> 5) & 0X3F;
+}
+/** second part of FAT directory time field */
+static inline uint8_t FAT_SECOND(uint16_t fatTime) {
+  return 2 * (fatTime & 0X1F);
+}
+/** Default date for file timestamps is 1 Jan 2000 */
+uint16_t const FAT_DEFAULT_DATE = ((2000 - 1980) << 9) | (1 << 5) | 1;
+/** Default time for file timestamp is 1 am */
+uint16_t const FAT_DEFAULT_TIME = (1 << 11);
+//------------------------------------------------------------------------------
+/**
+   \class SdFile
+   \brief Access FAT16 and FAT32 files on SD and SDHC cards.
+*/
+class SdFile : public Print {
+  public:
+    /** Create an instance of SdFile. */
+    SdFile(void) : type_(FAT_FILE_TYPE_CLOSED) {}
+    /**
+       writeError is set to true if an error occurs during a write().
+       Set writeError to false before calling print() and/or write() and check
+       for true after calls to print() and/or write().
+    */
+    //bool writeError;
+    /**
+       Cancel unbuffered reads for this file.
+       See setUnbufferedRead()
+    */
+    void clearUnbufferedRead(void) {
+      flags_ &= ~F_FILE_UNBUFFERED_READ;
+    }
+    uint8_t close(void);
+    uint8_t contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
+    uint8_t createContiguous(SdFile* dirFile,
+                             const char* fileName, uint32_t size);
+    /** \return The current cluster number for a file or directory. */
+    uint32_t curCluster(void) const {
+      return curCluster_;
+    }
+    /** \return The current position for a file or directory. */
+    uint32_t curPosition(void) const {
+      return curPosition_;
+    }
+    /**
+       Set the date/time callback function
+
+       \param[in] dateTime The user's call back function.  The callback
+       function is of the form:
+
+       \code
+       void dateTime(uint16_t* date, uint16_t* time) {
+         uint16_t year;
+         uint8_t month, day, hour, minute, second;
+
+         // User gets date and time from GPS or real-time clock here
+
+         // return date using FAT_DATE macro to format fields
+     *   *date = FAT_DATE(year, month, day);
+
+         // return time using FAT_TIME macro to format fields
+     *   *time = FAT_TIME(hour, minute, second);
+       }
+       \endcode
+
+       Sets the function that is called when a file is created or when
+       a file's directory entry is modified by sync(). All timestamps,
+       access, creation, and modify, are set when a file is created.
+       sync() maintains the last access date and last modify date/time.
+
+       See the timestamp() function.
+    */
+    static void dateTimeCallback(
+      void (*dateTime)(uint16_t* date, uint16_t* time)) {
+      dateTime_ = dateTime;
+    }
+    /**
+       Cancel the date/time callback function.
+    */
+    static void dateTimeCallbackCancel(void) {
+      // use explicit zero since NULL is not defined for Sanguino
+      dateTime_ = 0;
+    }
+    /** \return Address of the block that contains this file's directory. */
+    uint32_t dirBlock(void) const {
+      return dirBlock_;
+    }
+    uint8_t dirEntry(dir_t* dir);
+    /** \return Index of this file's directory in the block dirBlock. */
+    uint8_t dirIndex(void) const {
+      return dirIndex_;
+    }
+    static void dirName(const dir_t& dir, char* name);
+    /** \return The total number of bytes in a file or directory. */
+    uint32_t fileSize(void) const {
+      return fileSize_;
+    }
+    /** \return The first cluster number for a file or directory. */
+    uint32_t firstCluster(void) const {
+      return firstCluster_;
+    }
+    /** \return True if this is a SdFile for a directory else false. */
+    uint8_t isDir(void) const {
+      return type_ >= FAT_FILE_TYPE_MIN_DIR;
+    }
+    /** \return True if this is a SdFile for a file else false. */
+    uint8_t isFile(void) const {
+      return type_ == FAT_FILE_TYPE_NORMAL;
+    }
+    /** \return True if this is a SdFile for an open file/directory else false. */
+    uint8_t isOpen(void) const {
+      return type_ != FAT_FILE_TYPE_CLOSED;
+    }
+    /** \return True if this is a SdFile for a subdirectory else false. */
+    uint8_t isSubDir(void) const {
+      return type_ == FAT_FILE_TYPE_SUBDIR;
+    }
+    /** \return True if this is a SdFile for the root directory. */
+    uint8_t isRoot(void) const {
+      return type_ == FAT_FILE_TYPE_ROOT16 || type_ == FAT_FILE_TYPE_ROOT32;
+    }
+    void ls(uint8_t flags = 0, uint8_t indent = 0);
+    uint8_t makeDir(SdFile* dir, const char* dirName);
+    uint8_t open(SdFile* dirFile, uint16_t index, uint8_t oflag);
+    uint8_t open(SdFile* dirFile, const char* fileName, uint8_t oflag);
+
+    uint8_t openRoot(SdVolume* vol);
+    static void printDirName(const dir_t& dir, uint8_t width);
+    static void printFatDate(uint16_t fatDate);
+    static void printFatTime(uint16_t fatTime);
+    static void printTwoDigits(uint8_t v);
+    /**
+       Read the next byte from a file.
+
+       \return For success read returns the next byte in the file as an int.
+       If an error occurs or end of file is reached -1 is returned.
+    */
+    int16_t read(void) {
+      uint8_t b;
+      return read(&b, 1) == 1 ? b : -1;
+    }
+    int16_t read(void* buf, uint16_t nbyte);
+    int8_t readDir(dir_t* dir);
+    static uint8_t remove(SdFile* dirFile, const char* fileName);
+    uint8_t remove(void);
+    /** Set the file's current position to zero. */
+    void rewind(void) {
+      curPosition_ = curCluster_ = 0;
+    }
+    uint8_t rmDir(void);
+    uint8_t rmRfStar(void);
+    /** Set the files position to current position + \a pos. See seekSet(). */
+    uint8_t seekCur(uint32_t pos) {
+      return seekSet(curPosition_ + pos);
+    }
+    /**
+        Set the files current position to end of file.  Useful to position
+        a file for append. See seekSet().
+    */
+    uint8_t seekEnd(void) {
+      return seekSet(fileSize_);
+    }
+    uint8_t seekSet(uint32_t pos);
+    /**
+       Use unbuffered reads to access this file.  Used with Wave
+       Shield ISR.  Used with Sd2Card::partialBlockRead() in WaveRP.
+
+       Not recommended for normal applications.
+    */
+    void setUnbufferedRead(void) {
+      if (isFile()) {
+        flags_ |= F_FILE_UNBUFFERED_READ;
+      }
+    }
+    uint8_t timestamp(uint8_t flag, uint16_t year, uint8_t month, uint8_t day,
+                      uint8_t hour, uint8_t minute, uint8_t second);
+    uint8_t sync(uint8_t blocking = 1);
+    /** Type of this SdFile.  You should use isFile() or isDir() instead of type()
+       if possible.
+
+       \return The file or directory type.
+    */
+    uint8_t type(void) const {
+      return type_;
+    }
+    uint8_t truncate(uint32_t size);
+    /** \return Unbuffered read flag. */
+    uint8_t unbufferedRead(void) const {
+      return flags_ & F_FILE_UNBUFFERED_READ;
+    }
+    /** \return SdVolume that contains this file. */
+    SdVolume* volume(void) const {
+      return vol_;
+    }
+    size_t write(uint8_t b);
+    size_t write(const void* buf, uint16_t nbyte);
+    size_t write(const char* str);
+    #ifdef __AVR__
+    void write_P(PGM_P str);
+    void writeln_P(PGM_P str);
+    #endif
+    int availableForWrite(void);
+    //------------------------------------------------------------------------------
+    #if ALLOW_DEPRECATED_FUNCTIONS
+    // Deprecated functions  - suppress cpplint warnings with NOLINT comment
+    /** \deprecated Use:
+       uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock);
+    */
+    uint8_t contiguousRange(uint32_t& bgnBlock, uint32_t& endBlock) {  // NOLINT
+      return contiguousRange(&bgnBlock, &endBlock);
+    }
+    /** \deprecated Use:
+        uint8_t SdFile::createContiguous(SdFile* dirFile,
+          const char* fileName, uint32_t size)
+    */
+    uint8_t createContiguous(SdFile& dirFile,  // NOLINT
+                             const char* fileName, uint32_t size) {
+      return createContiguous(&dirFile, fileName, size);
+    }
+
+    /**
+       \deprecated Use:
+       static void SdFile::dateTimeCallback(
+         void (*dateTime)(uint16_t* date, uint16_t* time));
+    */
+    static void dateTimeCallback(
+      void (*dateTime)(uint16_t& date, uint16_t& time)) {  // NOLINT
+      oldDateTime_ = dateTime;
+      dateTime_ = dateTime ? oldToNew : 0;
+    }
+    /** \deprecated Use: uint8_t SdFile::dirEntry(dir_t* dir); */
+    uint8_t dirEntry(dir_t& dir) {
+      return dirEntry(&dir); // NOLINT
+    }
+    /** \deprecated Use:
+       uint8_t SdFile::makeDir(SdFile* dir, const char* dirName);
+    */
+    uint8_t makeDir(SdFile& dir, const char* dirName) {  // NOLINT
+      return makeDir(&dir, dirName);
+    }
+    /** \deprecated Use:
+       uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag);
+    */
+    uint8_t open(SdFile& dirFile, // NOLINT
+                 const char* fileName, uint8_t oflag) {
+      return open(&dirFile, fileName, oflag);
+    }
+    /** \deprecated  Do not use in new apps */
+    uint8_t open(SdFile& dirFile, const char* fileName) {  // NOLINT
+      return open(dirFile, fileName, O_RDWR);
+    }
+    /** \deprecated Use:
+       uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag);
+    */
+    uint8_t open(SdFile& dirFile, uint16_t index, uint8_t oflag) {  // NOLINT
+      return open(&dirFile, index, oflag);
+    }
+    /** \deprecated Use: uint8_t SdFile::openRoot(SdVolume* vol); */
+    uint8_t openRoot(SdVolume& vol) {
+      return openRoot(&vol); // NOLINT
+    }
+
+    /** \deprecated Use: int8_t SdFile::readDir(dir_t* dir); */
+    int8_t readDir(dir_t& dir) {
+      return readDir(&dir); // NOLINT
+    }
+    /** \deprecated Use:
+       static uint8_t SdFile::remove(SdFile* dirFile, const char* fileName);
+    */
+    static uint8_t remove(SdFile& dirFile, const char* fileName) {  // NOLINT
+      return remove(&dirFile, fileName);
+    }
+    //------------------------------------------------------------------------------
+    // rest are private
+  private:
+    static void (*oldDateTime_)(uint16_t& date, uint16_t& time);  // NOLINT
+    static void oldToNew(uint16_t* date, uint16_t* time) {
+      uint16_t d;
+      uint16_t t;
+      oldDateTime_(d, t);
+      *date = d;
+      *time = t;
+    }
+    #endif  // ALLOW_DEPRECATED_FUNCTIONS
+  private:
+    // bits defined in flags_
+    // should be 0XF
+    static uint8_t const F_OFLAG = (O_ACCMODE | O_APPEND | O_SYNC);
+    // available bits
+    static uint8_t const F_FILE_NON_BLOCKING_WRITE = 0X10;
+    // a new cluster was added to the file
+    static uint8_t const F_FILE_CLUSTER_ADDED = 0X20;
+    // use unbuffered SD read
+    static uint8_t const F_FILE_UNBUFFERED_READ = 0X40;
+    // sync of directory entry required
+    static uint8_t const F_FILE_DIR_DIRTY = 0X80;
+
+    // make sure F_OFLAG is ok
+    #if ((F_FILE_NON_BLOCKING_WRITE | F_FILE_CLUSTER_ADDED | F_FILE_UNBUFFERED_READ | F_FILE_DIR_DIRTY) & 
F_OFLAG)
+#error flags_ bits conflict
+    #endif  // flags_ bits
+
+    // private data
+    uint8_t   flags_;         // See above for definition of flags_ bits
+    uint8_t   type_;          // type of file see above for values
+    uint32_t  curCluster_;    // cluster for current file position
+    uint32_t  curPosition_;   // current file position in bytes from beginning
+    uint32_t  dirBlock_;      // SD block that contains directory entry for file
+    uint8_t   dirIndex_;      // index of entry in dirBlock 0 <= dirIndex_ <= 0XF
+    uint32_t  fileSize_;      // file size in bytes
+    uint32_t  firstCluster_;  // first cluster of file
+    SdVolume* vol_;           // volume where file is located
+
+    // private functions
+    uint8_t addCluster(void);
+    uint8_t addDirCluster(void);
+    dir_t* cacheDirEntry(uint8_t action);
+    static void (*dateTime_)(uint16_t* date, uint16_t* time);
+    static uint8_t make83Name(const char* str, uint8_t* name);
+    uint8_t openCachedEntry(uint8_t cacheIndex, uint8_t oflags);
+    dir_t* readDirCache(void);
+};
+//==============================================================================
+// SdVolume class
+/**
+   \brief Cache for an SD data block
+*/
+union cache_t {
+  /** Used to access cached file data blocks. */
+  uint8_t  data[512];
+  /** Used to access cached FAT16 entries. */
+  uint16_t fat16[256];
+  /** Used to access cached FAT32 entries. */
+  uint32_t fat32[128];
+  /** Used to access cached directory entries. */
+  dir_t    dir[16];
+  /** Used to access a cached MasterBoot Record. */
+  mbr_t    mbr;
+  /** Used to access to a cached FAT boot sector. */
+  fbs_t    fbs;
+};
+//------------------------------------------------------------------------------
+/**
+   \class SdVolume
+   \brief Access FAT16 and FAT32 volumes on SD and SDHC cards.
+*/
+class SdVolume {
+  public:
+    /** Create an instance of SdVolume */
+    SdVolume(void) : allocSearchStart_(2), fatType_(0) {}
+    /** Clear the cache and returns a pointer to the cache.  Used by the WaveRP
+        recorder to do raw write to the SD card.  Not for normal apps.
+    */
+    static uint8_t* cacheClear(void) {
+      cacheFlush();
+      cacheBlockNumber_ = 0XFFFFFFFF;
+      return cacheBuffer_.data;
+    }
+    /**
+       Initialize a FAT volume.  Try partition one first then try super
+       floppy format.
+
+       \param[in] dev The Sd2Card where the volume is located.
+
+       \return The value one, true, is returned for success and
+       the value zero, false, is returned for failure.  Reasons for
+       failure include not finding a valid partition, not finding a valid
+       FAT file system or an I/O error.
+    */
+    uint8_t init(Sd2Card* dev) {
+      return init(dev, 1) ? true : init(dev, 0);
+    }
+    uint8_t init(Sd2Card* dev, uint8_t part);
+
+    // inline functions that return volume info
+    /** \return The volume's cluster size in blocks. */
+    uint8_t blocksPerCluster(void) const {
+      return blocksPerCluster_;
+    }
+    /** \return The number of blocks in one FAT. */
+    uint32_t blocksPerFat(void)  const {
+      return blocksPerFat_;
+    }
+    /** \return The total number of clusters in the volume. */
+    uint32_t clusterCount(void) const {
+      return clusterCount_;
+    }
+    /** \return The shift count required to multiply by blocksPerCluster. */
+    uint8_t clusterSizeShift(void) const {
+      return clusterSizeShift_;
+    }
+    /** \return The logical block number for the start of file data. */
+    uint32_t dataStartBlock(void) const {
+      return dataStartBlock_;
+    }
+    /** \return The number of FAT structures on the volume. */
+    uint8_t fatCount(void) const {
+      return fatCount_;
+    }
+    /** \return The logical block number for the start of the first FAT. */
+    uint32_t fatStartBlock(void) const {
+      return fatStartBlock_;
+    }
+    /** \return The FAT type of the volume. Values are 12, 16 or 32. */
+    uint8_t fatType(void) const {
+      return fatType_;
+    }
+    /** \return The number of entries in the root directory for FAT16 volumes. */
+    uint32_t rootDirEntryCount(void) const {
+      return rootDirEntryCount_;
+    }
+    /** \return The logical block number for the start of the root directory
+         on FAT16 volumes or the first cluster number on FAT32 volumes. */
+    uint32_t rootDirStart(void) const {
+      return rootDirStart_;
+    }
+    /** return a pointer to the Sd2Card object for this volume */
+    static Sd2Card* sdCard(void) {
+      return sdCard_;
+    }
+    //------------------------------------------------------------------------------
+    #if ALLOW_DEPRECATED_FUNCTIONS
+    // Deprecated functions  - suppress cpplint warnings with NOLINT comment
+    /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev); */
+    uint8_t init(Sd2Card& dev) {
+      return init(&dev); // NOLINT
+    }
+
+    /** \deprecated Use: uint8_t SdVolume::init(Sd2Card* dev, uint8_t vol); */
+    uint8_t init(Sd2Card& dev, uint8_t part) {  // NOLINT
+      return init(&dev, part);
+    }
+    #endif  // ALLOW_DEPRECATED_FUNCTIONS
+    //------------------------------------------------------------------------------
+  private:
+    // Allow SdFile access to SdVolume private data.
+    friend class SdFile;
+
+    // value for action argument in cacheRawBlock to indicate read from cache
+    static uint8_t const CACHE_FOR_READ = 0;
+    // value for action argument in cacheRawBlock to indicate cache dirty
+    static uint8_t const CACHE_FOR_WRITE = 1;
+
+    static cache_t cacheBuffer_;        // 512 byte cache for device blocks
+    static uint32_t cacheBlockNumber_;  // Logical number of block in the cache
+    static Sd2Card* sdCard_;            // Sd2Card object for cache
+    static uint8_t cacheDirty_;         // cacheFlush() will write block if true
+    static uint32_t cacheMirrorBlock_;  // block number for mirror FAT
+    //
+    uint32_t allocSearchStart_;   // start cluster for alloc search
+    uint8_t blocksPerCluster_;    // cluster size in blocks
+    uint32_t blocksPerFat_;       // FAT size in blocks
+    uint32_t clusterCount_;       // clusters in one FAT
+    uint8_t clusterSizeShift_;    // shift to convert cluster count to block count
+    uint32_t dataStartBlock_;     // first data block number
+    uint8_t fatCount_;            // number of FATs on volume
+    uint32_t fatStartBlock_;      // start block for first FAT
+    uint8_t fatType_;             // volume type (12, 16, OR 32)
+    uint16_t rootDirEntryCount_;  // number of entries in FAT16 root dir
+    uint32_t rootDirStart_;       // root start block for FAT16, cluster for FAT32
+    //----------------------------------------------------------------------------
+    uint8_t allocContiguous(uint32_t count, uint32_t* curCluster);
+    uint8_t blockOfCluster(uint32_t position) const {
+      return (position >> 9) & (blocksPerCluster_ - 1);
+    }
+    uint32_t clusterStartBlock(uint32_t cluster) const {
+      return dataStartBlock_ + ((cluster - 2) << clusterSizeShift_);
+    }
+    uint32_t blockNumber(uint32_t cluster, uint32_t position) const {
+      return clusterStartBlock(cluster) + blockOfCluster(position);
+    }
+    static uint8_t cacheFlush(uint8_t blocking = 1);
+    static uint8_t cacheMirrorBlockFlush(uint8_t blocking);
+    static uint8_t cacheRawBlock(uint32_t blockNumber, uint8_t action);
+    static void cacheSetDirty(void) {
+      cacheDirty_ |= CACHE_FOR_WRITE;
+    }
+    static uint8_t cacheZeroBlock(uint32_t blockNumber);
+    uint8_t chainSize(uint32_t beginCluster, uint32_t* size) const;
+    uint8_t fatGet(uint32_t cluster, uint32_t* value) const;
+    uint8_t fatPut(uint32_t cluster, uint32_t value);
+    uint8_t fatPutEOC(uint32_t cluster) {
+      return fatPut(cluster, 0x0FFFFFFF);
+    }
+    uint8_t freeChain(uint32_t cluster);
+    uint8_t isEOC(uint32_t cluster) const {
+      return  cluster >= (fatType_ == 16 ? FAT16EOC_MIN : FAT32EOC_MIN);
+    }
+    uint8_t readBlock(uint32_t block, uint8_t* dst) {
+      return sdCard_->readBlock(block, dst);
+    }
+    uint8_t readData(uint32_t block, uint16_t offset,
+                     uint16_t count, uint8_t* dst) {
+      return sdCard_->readData(block, offset, count, dst);
+    }
+    uint8_t writeBlock(uint32_t block, const uint8_t* dst, uint8_t blocking = 1) {
+      return sdCard_->writeBlock(block, dst, blocking);
+    }
+    uint8_t isBusy(void) {
+      return sdCard_->isBusy();
+    }
+    uint8_t isCacheMirrorBlockDirty(void) {
+      return (cacheMirrorBlock_ != 0);
+    }
+};
+#endif  // SdFat_h
diff --git a/arduino/libraries/SD/src/utility/SdFatUtil.h b/arduino/libraries/SD/src/utility/SdFatUtil.h
new file mode 100644
index 000000000..4b93de8a8
--- /dev/null
+++ b/arduino/libraries/SD/src/utility/SdFatUtil.h
@@ -0,0 +1,77 @@
+/* Arduino SdFat Library
+   Copyright (C) 2008 by William Greiman
+
+   This file is part of the Arduino SdFat Library
+
+   This Library is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the Arduino SdFat Library.  If not, see
+   <http://www.gnu.org/licenses/>.
+*/
+#ifndef SdFatUtil_h
+#define SdFatUtil_h
+/**
+   \file
+   Useful utility functions.
+*/
+#include <Arduino.h>
+#ifdef __AVR__
+  #include <avr/pgmspace.h>
+  /** Store and print a string in flash memory.*/
+  #define PgmPrint(x) SerialPrint_P(PSTR(x))
+  /** Store and print a string in flash memory followed by a CR/LF.*/
+  #define PgmPrintln(x) SerialPrintln_P(PSTR(x))
+  /** Defined so doxygen works for function definitions. */
+#endif
+#define NOINLINE __attribute__((noinline,unused))
+#define UNUSEDOK __attribute__((unused))
+//------------------------------------------------------------------------------
+/** Return the number of bytes currently free in RAM. */
+static UNUSEDOK int FreeRam(void) {
+  extern int  __bss_end;
+  extern int* __brkval;
+  int free_memory;
+  if (reinterpret_cast<int>(__brkval) == 0) {
+    // if no heap use from end of bss section
+    free_memory = reinterpret_cast<int>(&free_memory)
+                  - reinterpret_cast<int>(&__bss_end);
+  } else {
+    // use from top of stack to heap
+    free_memory = reinterpret_cast<int>(&free_memory)
+                  - reinterpret_cast<int>(__brkval);
+  }
+  return free_memory;
+}
+#ifdef __AVR__
+//------------------------------------------------------------------------------
+/**
+   %Print a string in flash memory to the serial port.
+
+   \param[in] str Pointer to string stored in flash memory.
+*/
+static NOINLINE void SerialPrint_P(PGM_P str) {
+  for (uint8_t c; (c = pgm_read_byte(str)); str++) {
+    Serial.write(c);
+  }
+}
+//------------------------------------------------------------------------------
+/**
+   %Print a string in flash memory followed by a CR/LF.
+
+   \param[in] str Pointer to string stored in flash memory.
+*/
+static NOINLINE void SerialPrintln_P(PGM_P str) {
+  SerialPrint_P(str);
+  Serial.println();
+}
+#endif  // __AVR__
+#endif  // #define SdFatUtil_h
diff --git a/arduino/libraries/SD/src/utility/SdFatmainpage.h 
b/arduino/libraries/SD/src/utility/SdFatmainpage.h
new file mode 100644
index 000000000..1e6d96f9c
--- /dev/null
+++ b/arduino/libraries/SD/src/utility/SdFatmainpage.h
@@ -0,0 +1,202 @@
+/* Arduino SdFat Library
+   Copyright (C) 2009 by William Greiman
+
+   This file is part of the Arduino SdFat Library
+
+   This Library is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the Arduino SdFat Library.  If not, see
+   <http://www.gnu.org/licenses/>.
+*/
+
+/**
+  \mainpage Arduino SdFat Library
+  <CENTER>Copyright &copy; 2009 by William Greiman
+  </CENTER>
+
+  \section Intro Introduction
+  The Arduino SdFat Library is a minimal implementation of FAT16 and FAT32
+  file systems on SD flash memory cards.  Standard SD and high capacity
+  SDHC cards are supported.
+
+  The SdFat only supports short 8.3 names.
+
+  The main classes in SdFat are Sd2Card, SdVolume, and SdFile.
+
+  The Sd2Card class supports access to standard SD cards and SDHC cards.  Most
+  applications will only need to call the Sd2Card::init() member function.
+
+  The SdVolume class supports FAT16 and FAT32 partitions.  Most applications
+  will only need to call the SdVolume::init() member function.
+
+  The SdFile class provides file access functions such as open(), read(),
+  remove(), write(), close() and sync(). This class supports access to the root
+  directory and subdirectories.
+
+  A number of example are provided in the SdFat/examples folder.  These were
+  developed to test SdFat and illustrate its use.
+
+  SdFat was developed for high speed data recording.  SdFat was used to implement
+  an audio record/play class, WaveRP, for the Adafruit Wave Shield.  This
+  application uses special Sd2Card calls to write to contiguous files in raw mode.
+  These functions reduce write latency so that audio can be recorded with the
+  small amount of RAM in the Arduino.
+
+  \section SDcard SD\SDHC Cards
+
+  Arduinos access SD cards using the cards SPI protocol.  PCs, Macs, and
+  most consumer devices use the 4-bit parallel SD protocol.  A card that
+  functions well on A PC or Mac may not work well on the Arduino.
+
+  Most cards have good SPI read performance but cards vary widely in SPI
+  write performance.  Write performance is limited by how efficiently the
+  card manages internal erase/remapping operations.  The Arduino cannot
+  optimize writes to reduce erase operations because of its limit RAM.
+
+  SanDisk cards generally have good write performance.  They seem to have
+  more internal RAM buffering than other cards and therefore can limit
+  the number of flash erase operations that the Arduino forces due to its
+  limited RAM.
+
+  \section Hardware Hardware Configuration
+
+  SdFat was developed using an
+  <A HREF = "http://www.adafruit.com/";> Adafruit Industries</A>
+  <A HREF = "http://www.ladyada.net/make/waveshield/";> Wave Shield</A>.
+
+  The hardware interface to the SD card should not use a resistor based level
+  shifter.  SdFat sets the SPI bus frequency to 8 MHz which results in signal
+  rise times that are too slow for the edge detectors in many newer SD card
+  controllers when resistor voltage dividers are used.
+
+  The 5 to 3.3 V level shifter for 5 V Arduinos should be IC based like the
+  74HC4050N based circuit shown in the file SdLevel.png.  The Adafruit Wave Shield
+  uses a 74AHC125N.  Gravitech sells SD and MicroSD Card Adapters based on the
+  74LCX245.
+
+  If you are using a resistor based level shifter and are having problems try
+  setting the SPI bus frequency to 4 MHz.  This can be done by using
+  card.init(SPI_HALF_SPEED) to initialize the SD card.
+
+  \section comment Bugs and Comments
+
+  If you wish to report bugs or have comments, send email to fat16lib sbcglobal net.
+
+  \section SdFatClass SdFat Usage
+
+  SdFat uses a slightly restricted form of short names.
+  Only printable ASCII characters are supported. No characters with code point
+  values greater than 127 are allowed.  Space is not allowed even though space
+  was allowed in the API of early versions of DOS.
+
+  Short names are limited to 8 characters followed by an optional period (.)
+  and extension of up to 3 characters.  The characters may be any combination
+  of letters and digits.  The following special characters are also allowed:
+
+  $ % ' - _ @ ~ ` ! ( ) { } ^ # &
+
+  Short names are always converted to upper case and their original case
+  value is lost.
+
+  \note
+  The Arduino Print class uses character
+  at a time writes so it was necessary to use a \link SdFile::sync() sync() \endlink
+  function to control when data is written to the SD card.
+
+  \par
+  An application which writes to a file using \link Print::print() print()\endlink,
+  \link Print::println() println() \endlink
+  or \link SdFile::write write() \endlink must call \link SdFile::sync() sync() \endlink
+  at the appropriate time to force data and directory information to be written
+  to the SD Card.  Data and directory information are also written to the SD card
+  when \link SdFile::close() close() \endlink is called.
+
+  \par
+  Applications must use care calling \link SdFile::sync() sync() \endlink
+  since 2048 bytes of I/O is required to update file and
+  directory information.  This includes writing the current data block, reading
+  the block that contains the directory entry for update, writing the directory
+  block back and reading back the current data block.
+
+  It is possible to open a file with two or more instances of SdFile.  A file may
+  be corrupted if data is written to the file by more than one instance of SdFile.
+
+  \section HowTo How to format SD Cards as FAT Volumes
+
+  You should use a freshly formatted SD card for best performance.  FAT
+  file systems become slower if many files have been created and deleted.
+  This is because the directory entry for a deleted file is marked as deleted,
+  but is not deleted.  When a new file is created, these entries must be scanned
+  before creating the file, a flaw in the FAT design.  Also files can become
+  fragmented which causes reads and writes to be slower.
+
+  Microsoft operating systems support removable media formatted with a
+  Master Boot Record, MBR, or formatted as a super floppy with a FAT Boot Sector
+  in block zero.
+
+  Microsoft operating systems expect MBR formatted removable media
+  to have only one partition. The first partition should be used.
+
+  Microsoft operating systems do not support partitioning SD flash cards.
+  If you erase an SD card with a program like KillDisk, Most versions of
+  Windows will format the card as a super floppy.
+
+  The best way to restore an SD card's format is to use SDFormatter
+  which can be downloaded from:
+
+  http://www.sdcard.org/consumers/formatter/
+
+  SDFormatter aligns flash erase boundaries with file
+  system structures which reduces write latency and file system overhead.
+
+  SDFormatter does not have an option for FAT type so it may format
+  small cards as FAT12.
+
+  After the MBR is restored by SDFormatter you may need to reformat small
+  cards that have been formatted FAT12 to force the volume type to be FAT16.
+
+  If you reformat the SD card with an OS utility, choose a cluster size that
+  will result in:
+
+  4084 < CountOfClusters && CountOfClusters < 65525
+
+  The volume will then be FAT16.
+
+  If you are formatting an SD card on OS X or Linux, be sure to use the first
+  partition. Format this partition with a cluster count in above range.
+
+  \section  References References
+
+  Adafruit Industries:
+
+  http://www.adafruit.com/
+
+  http://www.ladyada.net/make/waveshield/
+
+  The Arduino site:
+
+  http://www.arduino.cc/
+
+  For more information about FAT file systems see:
+
+  http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx
+
+  For information about using SD cards as SPI devices see:
+
+  http://www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf
+
+  The ATmega328 datasheet:
+
+  http://www.atmel.com/dyn/resources/prod_documents/doc8161.pdf
+
+
+*/
diff --git a/arduino/libraries/SD/src/utility/SdFile.cpp b/arduino/libraries/SD/src/utility/SdFile.cpp
new file mode 100644
index 000000000..18a1db6e7
--- /dev/null
+++ b/arduino/libraries/SD/src/utility/SdFile.cpp
@@ -0,0 +1,1527 @@
+/* Arduino SdFat Library
+   Copyright (C) 2009 by William Greiman
+
+   This file is part of the Arduino SdFat Library
+
+   This Library is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the Arduino SdFat Library.  If not, see
+   <http://www.gnu.org/licenses/>.
+*/
+#include "SdFat.h"
+#ifdef __AVR__
+  #include <avr/pgmspace.h>
+#endif
+#include <Arduino.h>
+//------------------------------------------------------------------------------
+// callback function for date/time
+void (*SdFile::dateTime_)(uint16_t* date, uint16_t* time) = NULL;
+
+#if ALLOW_DEPRECATED_FUNCTIONS
+  // suppress cpplint warnings with NOLINT comment
+  void (*SdFile::oldDateTime_)(uint16_t& date, uint16_t& time) = NULL;  // NOLINT
+#endif  // ALLOW_DEPRECATED_FUNCTIONS
+//------------------------------------------------------------------------------
+// add a cluster to a file
+uint8_t SdFile::addCluster() {
+  if (!vol_->allocContiguous(1, &curCluster_)) {
+    return false;
+  }
+
+  // if first cluster of file link to directory entry
+  if (firstCluster_ == 0) {
+    firstCluster_ = curCluster_;
+    flags_ |= F_FILE_DIR_DIRTY;
+  }
+  flags_ |= F_FILE_CLUSTER_ADDED;
+  return true;
+}
+//------------------------------------------------------------------------------
+// Add a cluster to a directory file and zero the cluster.
+// return with first block of cluster in the cache
+uint8_t SdFile::addDirCluster(void) {
+  if (!addCluster()) {
+    return false;
+  }
+
+  // zero data in cluster insure first cluster is in cache
+  uint32_t block = vol_->clusterStartBlock(curCluster_);
+  for (uint8_t i = vol_->blocksPerCluster_; i != 0; i--) {
+    if (!SdVolume::cacheZeroBlock(block + i - 1)) {
+      return false;
+    }
+  }
+  // Increase directory file size by cluster size
+  fileSize_ += 512UL << vol_->clusterSizeShift_;
+  return true;
+}
+//------------------------------------------------------------------------------
+// cache a file's directory entry
+// return pointer to cached entry or null for failure
+dir_t* SdFile::cacheDirEntry(uint8_t action) {
+  if (!SdVolume::cacheRawBlock(dirBlock_, action)) {
+    return NULL;
+  }
+  return SdVolume::cacheBuffer_.dir + dirIndex_;
+}
+//------------------------------------------------------------------------------
+/**
+    Close a file and force cached data and directory information
+    to be written to the storage device.
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+   Reasons for failure include no file is open or an I/O error.
+*/
+uint8_t SdFile::close(void) {
+  if (!sync()) {
+    return false;
+  }
+  type_ = FAT_FILE_TYPE_CLOSED;
+  return true;
+}
+//------------------------------------------------------------------------------
+/**
+   Check for contiguous file and return its raw block range.
+
+   \param[out] bgnBlock the first block address for the file.
+   \param[out] endBlock the last  block address for the file.
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+   Reasons for failure include file is not contiguous, file has zero length
+   or an I/O error occurred.
+*/
+uint8_t SdFile::contiguousRange(uint32_t* bgnBlock, uint32_t* endBlock) {
+  // error if no blocks
+  if (firstCluster_ == 0) {
+    return false;
+  }
+
+  for (uint32_t c = firstCluster_; ; c++) {
+    uint32_t next;
+    if (!vol_->fatGet(c, &next)) {
+      return false;
+    }
+
+    // check for contiguous
+    if (next != (c + 1)) {
+      // error if not end of chain
+      if (!vol_->isEOC(next)) {
+        return false;
+      }
+      *bgnBlock = vol_->clusterStartBlock(firstCluster_);
+      *endBlock = vol_->clusterStartBlock(c)
+                  + vol_->blocksPerCluster_ - 1;
+      return true;
+    }
+  }
+}
+//------------------------------------------------------------------------------
+/**
+   Create and open a new contiguous file of a specified size.
+
+   \note This function only supports short DOS 8.3 names.
+   See open() for more information.
+
+   \param[in] dirFile The directory where the file will be created.
+   \param[in] fileName A valid DOS 8.3 file name.
+   \param[in] size The desired file size.
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+   Reasons for failure include \a fileName contains
+   an invalid DOS 8.3 file name, the FAT volume has not been initialized,
+   a file is already open, the file already exists, the root
+   directory is full or an I/O error.
+
+*/
+uint8_t SdFile::createContiguous(SdFile* dirFile,
+                                 const char* fileName, uint32_t size) {
+  // don't allow zero length file
+  if (size == 0) {
+    return false;
+  }
+  if (!open(dirFile, fileName, O_CREAT | O_EXCL | O_RDWR)) {
+    return false;
+  }
+
+  // calculate number of clusters needed
+  uint32_t count = ((size - 1) >> (vol_->clusterSizeShift_ + 9)) + 1;
+
+  // allocate clusters
+  if (!vol_->allocContiguous(count, &firstCluster_)) {
+    remove();
+    return false;
+  }
+  fileSize_ = size;
+
+  // insure sync() will update dir entry
+  flags_ |= F_FILE_DIR_DIRTY;
+  return sync();
+}
+//------------------------------------------------------------------------------
+/**
+   Return a files directory entry
+
+   \param[out] dir Location for return of the files directory entry.
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+*/
+uint8_t SdFile::dirEntry(dir_t* dir) {
+  // make sure fields on SD are correct
+  if (!sync()) {
+    return false;
+  }
+
+  // read entry
+  dir_t* p = cacheDirEntry(SdVolume::CACHE_FOR_READ);
+  if (!p) {
+    return false;
+  }
+
+  // copy to caller's struct
+  memcpy(dir, p, sizeof(dir_t));
+  return true;
+}
+//------------------------------------------------------------------------------
+/**
+   Format the name field of \a dir into the 13 byte array
+   \a name in standard 8.3 short name format.
+
+   \param[in] dir The directory structure containing the name.
+   \param[out] name A 13 byte char array for the formatted name.
+*/
+void SdFile::dirName(const dir_t& dir, char* name) {
+  uint8_t j = 0;
+  for (uint8_t i = 0; i < 11; i++) {
+    if (dir.name[i] == ' ') {
+      continue;
+    }
+    if (i == 8) {
+      name[j++] = '.';
+    }
+    name[j++] = dir.name[i];
+  }
+  name[j] = 0;
+}
+//------------------------------------------------------------------------------
+/** List directory contents to Serial.
+
+   \param[in] flags The inclusive OR of
+
+   LS_DATE - %Print file modification date
+
+   LS_SIZE - %Print file size.
+
+   LS_R - Recursive list of subdirectories.
+
+   \param[in] indent Amount of space before file name. Used for recursive
+   list to indicate subdirectory level.
+*/
+void SdFile::ls(uint8_t flags, uint8_t indent) {
+  dir_t* p;
+
+  rewind();
+  while ((p = readDirCache())) {
+    // done if past last used entry
+    if (p->name[0] == DIR_NAME_FREE) {
+      break;
+    }
+
+    // skip deleted entry and entries for . and  ..
+    if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') {
+      continue;
+    }
+
+    // only list subdirectories and files
+    if (!DIR_IS_FILE_OR_SUBDIR(p)) {
+      continue;
+    }
+
+    // print any indent spaces
+    for (int8_t i = 0; i < indent; i++) {
+      Serial.print(' ');
+    }
+
+    // print file name with possible blank fill
+    printDirName(*p, flags & (LS_DATE | LS_SIZE) ? 14 : 0);
+
+    // print modify date/time if requested
+    if (flags & LS_DATE) {
+      printFatDate(p->lastWriteDate);
+      Serial.print(' ');
+      printFatTime(p->lastWriteTime);
+    }
+    // print size if requested
+    if (!DIR_IS_SUBDIR(p) && (flags & LS_SIZE)) {
+      Serial.print(' ');
+      Serial.print(p->fileSize);
+    }
+    Serial.println();
+
+    // list subdirectory content if requested
+    if ((flags & LS_R) && DIR_IS_SUBDIR(p)) {
+      uint16_t index = curPosition() / 32 - 1;
+      SdFile s;
+      if (s.open(this, index, O_READ)) {
+        s.ls(flags, indent + 2);
+      }
+      seekSet(32 * (index + 1));
+    }
+  }
+}
+//------------------------------------------------------------------------------
+// format directory name field from a 8.3 name string
+uint8_t SdFile::make83Name(const char* str, uint8_t* name) {
+  uint8_t c;
+  uint8_t n = 7;  // max index for part before dot
+  uint8_t i = 0;
+  // blank fill name and extension
+  while (i < 11) {
+    name[i++] = ' ';
+  }
+  i = 0;
+  while ((c = *str++) != '\0') {
+    if (c == '.') {
+      if (n == 10) {
+        return false;  // only one dot allowed
+      }
+      n = 10;  // max index for full 8.3 name
+      i = 8;   // place for extension
+    } else {
+      // illegal FAT characters
+      uint8_t b;
+      #if defined(__AVR__)
+      PGM_P p = PSTR("|<>^+=?/[];,*\"\\");
+      while ((b = pgm_read_byte(p++))) if (b == c) {
+          return false;
+        }
+      #elif defined(__arm__)
+      const uint8_t valid[] = "|<>^+=?/[];,*\"\\";
+      const uint8_t *p = valid;
+      while ((b = *p++)) if (b == c) {
+          return false;
+        }
+      #endif
+      // check size and only allow ASCII printable characters
+      if (i > n || c < 0X21 || c > 0X7E) {
+        return false;
+      }
+      // only upper case allowed in 8.3 names - convert lower to upper
+      name[i++] = c < 'a' || c > 'z' ?  c : c + ('A' - 'a');
+    }
+  }
+  // must have a file name, extension is optional
+  return name[0] != ' ';
+}
+//------------------------------------------------------------------------------
+/** Make a new directory.
+
+   \param[in] dir An open SdFat instance for the directory that will containing
+   the new directory.
+
+   \param[in] dirName A valid 8.3 DOS name for the new directory.
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+   Reasons for failure include this SdFile is already open, \a dir is not a
+   directory, \a dirName is invalid or already exists in \a dir.
+*/
+uint8_t SdFile::makeDir(SdFile* dir, const char* dirName) {
+  dir_t d;
+
+  // create a normal file
+  if (!open(dir, dirName, O_CREAT | O_EXCL | O_RDWR)) {
+    return false;
+  }
+
+  // convert SdFile to directory
+  flags_ = O_READ;
+  type_ = FAT_FILE_TYPE_SUBDIR;
+
+  // allocate and zero first cluster
+  if (!addDirCluster()) {
+    return false;
+  }
+
+  // force entry to SD
+  if (!sync()) {
+    return false;
+  }
+
+  // cache entry - should already be in cache due to sync() call
+  dir_t* p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
+  if (!p) {
+    return false;
+  }
+
+  // change directory entry  attribute
+  p->attributes = DIR_ATT_DIRECTORY;
+
+  // make entry for '.'
+  memcpy(&d, p, sizeof(d));
+  for (uint8_t i = 1; i < 11; i++) {
+    d.name[i] = ' ';
+  }
+  d.name[0] = '.';
+
+  // cache block for '.'  and '..'
+  uint32_t block = vol_->clusterStartBlock(firstCluster_);
+  if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) {
+    return false;
+  }
+
+  // copy '.' to block
+  memcpy(&SdVolume::cacheBuffer_.dir[0], &d, sizeof(d));
+
+  // make entry for '..'
+  d.name[1] = '.';
+  if (dir->isRoot()) {
+    d.firstClusterLow = 0;
+    d.firstClusterHigh = 0;
+  } else {
+    d.firstClusterLow = dir->firstCluster_ & 0XFFFF;
+    d.firstClusterHigh = dir->firstCluster_ >> 16;
+  }
+  // copy '..' to block
+  memcpy(&SdVolume::cacheBuffer_.dir[1], &d, sizeof(d));
+
+  // set position after '..'
+  curPosition_ = 2 * sizeof(d);
+
+  // write first block
+  return SdVolume::cacheFlush();
+}
+//------------------------------------------------------------------------------
+/**
+   Open a file or directory by name.
+
+   \param[in] dirFile An open SdFat instance for the directory containing the
+   file to be opened.
+
+   \param[in] fileName A valid 8.3 DOS name for a file to be opened.
+
+   \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
+   OR of flags from the following list
+
+   O_READ - Open for reading.
+
+   O_RDONLY - Same as O_READ.
+
+   O_WRITE - Open for writing.
+
+   O_WRONLY - Same as O_WRITE.
+
+   O_RDWR - Open for reading and writing.
+
+   O_APPEND - If set, the file offset shall be set to the end of the
+   file prior to each write.
+
+   O_CREAT - If the file exists, this flag has no effect except as noted
+   under O_EXCL below. Otherwise, the file shall be created
+
+   O_EXCL - If O_CREAT and O_EXCL are set, open() shall fail if the file exists.
+
+   O_SYNC - Call sync() after each write.  This flag should not be used with
+   write(uint8_t), write_P(PGM_P), writeln_P(PGM_P), or the Arduino Print class.
+   These functions do character at a time writes so sync() will be called
+   after each byte.
+
+   O_TRUNC - If the file exists and is a regular file, and the file is
+   successfully opened and is not read only, its length shall be truncated to 0.
+
+   \note Directory files must be opened read only.  Write and truncation is
+   not allowed for directory files.
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+   Reasons for failure include this SdFile is already open, \a difFile is not
+   a directory, \a fileName is invalid, the file does not exist
+   or can't be opened in the access mode specified by oflag.
+*/
+uint8_t SdFile::open(SdFile* dirFile, const char* fileName, uint8_t oflag) {
+  uint8_t dname[11];
+  dir_t* p;
+
+  // error if already open
+  if (isOpen()) {
+    return false;
+  }
+
+  if (!make83Name(fileName, dname)) {
+    return false;
+  }
+  vol_ = dirFile->vol_;
+  dirFile->rewind();
+
+  // bool for empty entry found
+  uint8_t emptyFound = false;
+
+  // search for file
+  while (dirFile->curPosition_ < dirFile->fileSize_) {
+    uint8_t index = 0XF & (dirFile->curPosition_ >> 5);
+    p = dirFile->readDirCache();
+    if (p == NULL) {
+      return false;
+    }
+
+    if (p->name[0] == DIR_NAME_FREE || p->name[0] == DIR_NAME_DELETED) {
+      // remember first empty slot
+      if (!emptyFound) {
+        emptyFound = true;
+        dirIndex_ = index;
+        dirBlock_ = SdVolume::cacheBlockNumber_;
+      }
+      // done if no entries follow
+      if (p->name[0] == DIR_NAME_FREE) {
+        break;
+      }
+    } else if (!memcmp(dname, p->name, 11)) {
+      // don't open existing file if O_CREAT and O_EXCL
+      if ((oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) {
+        return false;
+      }
+
+      // open found file
+      return openCachedEntry(0XF & index, oflag);
+    }
+  }
+  // only create file if O_CREAT and O_WRITE
+  if ((oflag & (O_CREAT | O_WRITE)) != (O_CREAT | O_WRITE)) {
+    return false;
+  }
+
+  // cache found slot or add cluster if end of file
+  if (emptyFound) {
+    p = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
+    if (!p) {
+      return false;
+    }
+  } else {
+    if (dirFile->type_ == FAT_FILE_TYPE_ROOT16) {
+      return false;
+    }
+
+    // add and zero cluster for dirFile - first cluster is in cache for write
+    if (!dirFile->addDirCluster()) {
+      return false;
+    }
+
+    // use first entry in cluster
+    dirIndex_ = 0;
+    p = SdVolume::cacheBuffer_.dir;
+  }
+  // initialize as empty file
+  memset(p, 0, sizeof(dir_t));
+  memcpy(p->name, dname, 11);
+
+  // set timestamps
+  if (dateTime_) {
+    // call user function
+    dateTime_(&p->creationDate, &p->creationTime);
+  } else {
+    // use default date/time
+    p->creationDate = FAT_DEFAULT_DATE;
+    p->creationTime = FAT_DEFAULT_TIME;
+  }
+  p->lastAccessDate = p->creationDate;
+  p->lastWriteDate = p->creationDate;
+  p->lastWriteTime = p->creationTime;
+
+  // force write of entry to SD
+  if (!SdVolume::cacheFlush()) {
+    return false;
+  }
+
+  // open entry in cache
+  return openCachedEntry(dirIndex_, oflag);
+}
+//------------------------------------------------------------------------------
+/**
+   Open a file by index.
+
+   \param[in] dirFile An open SdFat instance for the directory.
+
+   \param[in] index The \a index of the directory entry for the file to be
+   opened.  The value for \a index is (directory file position)/32.
+
+   \param[in] oflag Values for \a oflag are constructed by a bitwise-inclusive
+   OR of flags O_READ, O_WRITE, O_TRUNC, and O_SYNC.
+
+   See open() by fileName for definition of flags and return values.
+
+*/
+uint8_t SdFile::open(SdFile* dirFile, uint16_t index, uint8_t oflag) {
+  // error if already open
+  if (isOpen()) {
+    return false;
+  }
+
+  // don't open existing file if O_CREAT and O_EXCL - user call error
+  if ((oflag & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) {
+    return false;
+  }
+
+  vol_ = dirFile->vol_;
+
+  // seek to location of entry
+  if (!dirFile->seekSet(32 * index)) {
+    return false;
+  }
+
+  // read entry into cache
+  dir_t* p = dirFile->readDirCache();
+  if (p == NULL) {
+    return false;
+  }
+
+  // error if empty slot or '.' or '..'
+  if (p->name[0] == DIR_NAME_FREE ||
+      p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') {
+    return false;
+  }
+  // open cached entry
+  return openCachedEntry(index & 0XF, oflag);
+}
+//------------------------------------------------------------------------------
+// open a cached directory entry. Assumes vol_ is initializes
+uint8_t SdFile::openCachedEntry(uint8_t dirIndex, uint8_t oflag) {
+  // location of entry in cache
+  dir_t* p = SdVolume::cacheBuffer_.dir + dirIndex;
+
+  // write or truncate is an error for a directory or read-only file
+  if (p->attributes & (DIR_ATT_READ_ONLY | DIR_ATT_DIRECTORY)) {
+    if (oflag & (O_WRITE | O_TRUNC)) {
+      return false;
+    }
+  }
+  // remember location of directory entry on SD
+  dirIndex_ = dirIndex;
+  dirBlock_ = SdVolume::cacheBlockNumber_;
+
+  // copy first cluster number for directory fields
+  firstCluster_ = (uint32_t)p->firstClusterHigh << 16;
+  firstCluster_ |= p->firstClusterLow;
+
+  // make sure it is a normal file or subdirectory
+  if (DIR_IS_FILE(p)) {
+    fileSize_ = p->fileSize;
+    type_ = FAT_FILE_TYPE_NORMAL;
+  } else if (DIR_IS_SUBDIR(p)) {
+    if (!vol_->chainSize(firstCluster_, &fileSize_)) {
+      return false;
+    }
+    type_ = FAT_FILE_TYPE_SUBDIR;
+  } else {
+    return false;
+  }
+  // save open flags for read/write
+  flags_ = oflag & (O_ACCMODE | O_SYNC | O_APPEND);
+
+  // set to start of file
+  curCluster_ = 0;
+  curPosition_ = 0;
+
+  // truncate file to zero length if requested
+  if (oflag & O_TRUNC) {
+    return truncate(0);
+  }
+  return true;
+}
+//------------------------------------------------------------------------------
+/**
+   Open a volume's root directory.
+
+   \param[in] vol The FAT volume containing the root directory to be opened.
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+   Reasons for failure include the FAT volume has not been initialized
+   or it a FAT12 volume.
+*/
+uint8_t SdFile::openRoot(SdVolume* vol) {
+  // error if file is already open
+  if (isOpen()) {
+    return false;
+  }
+
+  if (vol->fatType() == 16) {
+    type_ = FAT_FILE_TYPE_ROOT16;
+    firstCluster_ = 0;
+    fileSize_ = 32 * vol->rootDirEntryCount();
+  } else if (vol->fatType() == 32) {
+    type_ = FAT_FILE_TYPE_ROOT32;
+    firstCluster_ = vol->rootDirStart();
+    if (!vol->chainSize(firstCluster_, &fileSize_)) {
+      return false;
+    }
+  } else {
+    // volume is not initialized or FAT12
+    return false;
+  }
+  vol_ = vol;
+  // read only
+  flags_ = O_READ;
+
+  // set to start of file
+  curCluster_ = 0;
+  curPosition_ = 0;
+
+  // root has no directory entry
+  dirBlock_ = 0;
+  dirIndex_ = 0;
+  return true;
+}
+//------------------------------------------------------------------------------
+/** %Print the name field of a directory entry in 8.3 format to Serial.
+
+   \param[in] dir The directory structure containing the name.
+   \param[in] width Blank fill name if length is less than \a width.
+*/
+void SdFile::printDirName(const dir_t& dir, uint8_t width) {
+  uint8_t w = 0;
+  for (uint8_t i = 0; i < 11; i++) {
+    if (dir.name[i] == ' ') {
+      continue;
+    }
+    if (i == 8) {
+      Serial.print('.');
+      w++;
+    }
+    Serial.write(dir.name[i]);
+    w++;
+  }
+  if (DIR_IS_SUBDIR(&dir)) {
+    Serial.print('/');
+    w++;
+  }
+  while (w < width) {
+    Serial.print(' ');
+    w++;
+  }
+}
+//------------------------------------------------------------------------------
+/** %Print a directory date field to Serial.
+
+    Format is yyyy-mm-dd.
+
+   \param[in] fatDate The date field from a directory entry.
+*/
+void SdFile::printFatDate(uint16_t fatDate) {
+  Serial.print(FAT_YEAR(fatDate));
+  Serial.print('-');
+  printTwoDigits(FAT_MONTH(fatDate));
+  Serial.print('-');
+  printTwoDigits(FAT_DAY(fatDate));
+}
+//------------------------------------------------------------------------------
+/** %Print a directory time field to Serial.
+
+   Format is hh:mm:ss.
+
+   \param[in] fatTime The time field from a directory entry.
+*/
+void SdFile::printFatTime(uint16_t fatTime) {
+  printTwoDigits(FAT_HOUR(fatTime));
+  Serial.print(':');
+  printTwoDigits(FAT_MINUTE(fatTime));
+  Serial.print(':');
+  printTwoDigits(FAT_SECOND(fatTime));
+}
+//------------------------------------------------------------------------------
+/** %Print a value as two digits to Serial.
+
+   \param[in] v Value to be printed, 0 <= \a v <= 99
+*/
+void SdFile::printTwoDigits(uint8_t v) {
+  char str[3];
+  str[0] = '0' + v / 10;
+  str[1] = '0' + v % 10;
+  str[2] = 0;
+  Serial.print(str);
+}
+//------------------------------------------------------------------------------
+/**
+   Read data from a file starting at the current position.
+
+   \param[out] buf Pointer to the location that will receive the data.
+
+   \param[in] nbyte Maximum number of bytes to read.
+
+   \return For success read() returns the number of bytes read.
+   A value less than \a nbyte, including zero, will be returned
+   if end of file is reached.
+   If an error occurs, read() returns -1.  Possible errors include
+   read() called before a file has been opened, corrupt file system
+   or an I/O error occurred.
+*/
+int16_t SdFile::read(void* buf, uint16_t nbyte) {
+  uint8_t* dst = reinterpret_cast<uint8_t*>(buf);
+
+  // error if not open or write only
+  if (!isOpen() || !(flags_ & O_READ)) {
+    return -1;
+  }
+
+  // max bytes left in file
+  if (nbyte > (fileSize_ - curPosition_)) {
+    nbyte = fileSize_ - curPosition_;
+  }
+
+  // amount left to read
+  uint16_t toRead = nbyte;
+  while (toRead > 0) {
+    uint32_t block;  // raw device block number
+    uint16_t offset = curPosition_ & 0X1FF;  // offset in block
+    if (type_ == FAT_FILE_TYPE_ROOT16) {
+      block = vol_->rootDirStart() + (curPosition_ >> 9);
+    } else {
+      uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_);
+      if (offset == 0 && blockOfCluster == 0) {
+        // start of new cluster
+        if (curPosition_ == 0) {
+          // use first cluster in file
+          curCluster_ = firstCluster_;
+        } else {
+          // get next cluster from FAT
+          if (!vol_->fatGet(curCluster_, &curCluster_)) {
+            return -1;
+          }
+        }
+      }
+      block = vol_->clusterStartBlock(curCluster_) + blockOfCluster;
+    }
+    uint16_t n = toRead;
+
+    // amount to be read from current block
+    if (n > (512 - offset)) {
+      n = 512 - offset;
+    }
+
+    // no buffering needed if n == 512 or user requests no buffering
+    if ((unbufferedRead() || n == 512) &&
+        block != SdVolume::cacheBlockNumber_) {
+      if (!vol_->readData(block, offset, n, dst)) {
+        return -1;
+      }
+      dst += n;
+    } else {
+      // read block to cache and copy data to caller
+      if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_READ)) {
+        return -1;
+      }
+      uint8_t* src = SdVolume::cacheBuffer_.data + offset;
+      uint8_t* end = src + n;
+      while (src != end) {
+        *dst++ = *src++;
+      }
+    }
+    curPosition_ += n;
+    toRead -= n;
+  }
+  return nbyte;
+}
+//------------------------------------------------------------------------------
+/**
+   Read the next directory entry from a directory file.
+
+   \param[out] dir The dir_t struct that will receive the data.
+
+   \return For success readDir() returns the number of bytes read.
+   A value of zero will be returned if end of file is reached.
+   If an error occurs, readDir() returns -1.  Possible errors include
+   readDir() called before a directory has been opened, this is not
+   a directory file or an I/O error occurred.
+*/
+int8_t SdFile::readDir(dir_t* dir) {
+  int8_t n;
+  // if not a directory file or miss-positioned return an error
+  if (!isDir() || (0X1F & curPosition_)) {
+    return -1;
+  }
+
+  while ((n = read(dir, sizeof(dir_t))) == sizeof(dir_t)) {
+    // last entry if DIR_NAME_FREE
+    if (dir->name[0] == DIR_NAME_FREE) {
+      break;
+    }
+    // skip empty entries and entry for .  and ..
+    if (dir->name[0] == DIR_NAME_DELETED || dir->name[0] == '.') {
+      continue;
+    }
+    // return if normal file or subdirectory
+    if (DIR_IS_FILE_OR_SUBDIR(dir)) {
+      return n;
+    }
+  }
+  // error, end of file, or past last entry
+  return n < 0 ? -1 : 0;
+}
+//------------------------------------------------------------------------------
+// Read next directory entry into the cache
+// Assumes file is correctly positioned
+dir_t* SdFile::readDirCache(void) {
+  // error if not directory
+  if (!isDir()) {
+    return NULL;
+  }
+
+  // index of entry in cache
+  uint8_t i = (curPosition_ >> 5) & 0XF;
+
+  // use read to locate and cache block
+  if (read() < 0) {
+    return NULL;
+  }
+
+  // advance to next entry
+  curPosition_ += 31;
+
+  // return pointer to entry
+  return (SdVolume::cacheBuffer_.dir + i);
+}
+//------------------------------------------------------------------------------
+/**
+   Remove a file.
+
+   The directory entry and all data for the file are deleted.
+
+   \note This function should not be used to delete the 8.3 version of a
+   file that has a long name. For example if a file has the long name
+   "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT".
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+   Reasons for failure include the file read-only, is a directory,
+   or an I/O error occurred.
+*/
+uint8_t SdFile::remove(void) {
+  // free any clusters - will fail if read-only or directory
+  if (!truncate(0)) {
+    return false;
+  }
+
+  // cache directory entry
+  dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
+  if (!d) {
+    return false;
+  }
+
+  // mark entry deleted
+  d->name[0] = DIR_NAME_DELETED;
+
+  // set this SdFile closed
+  type_ = FAT_FILE_TYPE_CLOSED;
+
+  // write entry to SD
+  return SdVolume::cacheFlush();
+}
+//------------------------------------------------------------------------------
+/**
+   Remove a file.
+
+   The directory entry and all data for the file are deleted.
+
+   \param[in] dirFile The directory that contains the file.
+   \param[in] fileName The name of the file to be removed.
+
+   \note This function should not be used to delete the 8.3 version of a
+   file that has a long name. For example if a file has the long name
+   "New Text Document.txt" you should not delete the 8.3 name "NEWTEX~1.TXT".
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+   Reasons for failure include the file is a directory, is read only,
+   \a dirFile is not a directory, \a fileName is not found
+   or an I/O error occurred.
+*/
+uint8_t SdFile::remove(SdFile* dirFile, const char* fileName) {
+  SdFile file;
+  if (!file.open(dirFile, fileName, O_WRITE)) {
+    return false;
+  }
+  return file.remove();
+}
+//------------------------------------------------------------------------------
+/** Remove a directory file.
+
+   The directory file will be removed only if it is empty and is not the
+   root directory.  rmDir() follows DOS and Windows and ignores the
+   read-only attribute for the directory.
+
+   \note This function should not be used to delete the 8.3 version of a
+   directory that has a long name. For example if a directory has the
+   long name "New folder" you should not delete the 8.3 name "NEWFOL~1".
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+   Reasons for failure include the file is not a directory, is the root
+   directory, is not empty, or an I/O error occurred.
+*/
+uint8_t SdFile::rmDir(void) {
+  // must be open subdirectory
+  if (!isSubDir()) {
+    return false;
+  }
+
+  rewind();
+
+  // make sure directory is empty
+  while (curPosition_ < fileSize_) {
+    dir_t* p = readDirCache();
+    if (p == NULL) {
+      return false;
+    }
+    // done if past last used entry
+    if (p->name[0] == DIR_NAME_FREE) {
+      break;
+    }
+    // skip empty slot or '.' or '..'
+    if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') {
+      continue;
+    }
+    // error not empty
+    if (DIR_IS_FILE_OR_SUBDIR(p)) {
+      return false;
+    }
+  }
+  // convert empty directory to normal file for remove
+  type_ = FAT_FILE_TYPE_NORMAL;
+  flags_ |= O_WRITE;
+  return remove();
+}
+//------------------------------------------------------------------------------
+/** Recursively delete a directory and all contained files.
+
+   This is like the Unix/Linux 'rm -rf *' if called with the root directory
+   hence the name.
+
+   Warning - This will remove all contents of the directory including
+   subdirectories.  The directory will then be removed if it is not root.
+   The read-only attribute for files will be ignored.
+
+   \note This function should not be used to delete the 8.3 version of
+   a directory that has a long name.  See remove() and rmDir().
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+*/
+uint8_t SdFile::rmRfStar(void) {
+  rewind();
+  while (curPosition_ < fileSize_) {
+    SdFile f;
+
+    // remember position
+    uint16_t index = curPosition_ / 32;
+
+    dir_t* p = readDirCache();
+    if (!p) {
+      return false;
+    }
+
+    // done if past last entry
+    if (p->name[0] == DIR_NAME_FREE) {
+      break;
+    }
+
+    // skip empty slot or '.' or '..'
+    if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') {
+      continue;
+    }
+
+    // skip if part of long file name or volume label in root
+    if (!DIR_IS_FILE_OR_SUBDIR(p)) {
+      continue;
+    }
+
+    if (!f.open(this, index, O_READ)) {
+      return false;
+    }
+    if (f.isSubDir()) {
+      // recursively delete
+      if (!f.rmRfStar()) {
+        return false;
+      }
+    } else {
+      // ignore read-only
+      f.flags_ |= O_WRITE;
+      if (!f.remove()) {
+        return false;
+      }
+    }
+    // position to next entry if required
+    if (curPosition_ != (32u * (index + 1))) {
+      if (!seekSet(32u * (index + 1))) {
+        return false;
+      }
+    }
+  }
+  // don't try to delete root
+  if (isRoot()) {
+    return true;
+  }
+  return rmDir();
+}
+//------------------------------------------------------------------------------
+/**
+   Sets a file's position.
+
+   \param[in] pos The new position in bytes from the beginning of the file.
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+*/
+uint8_t SdFile::seekSet(uint32_t pos) {
+  // error if file not open or seek past end of file
+  if (!isOpen() || pos > fileSize_) {
+    return false;
+  }
+
+  if (type_ == FAT_FILE_TYPE_ROOT16) {
+    curPosition_ = pos;
+    return true;
+  }
+  if (pos == 0) {
+    // set position to start of file
+    curCluster_ = 0;
+    curPosition_ = 0;
+    return true;
+  }
+  // calculate cluster index for cur and new position
+  uint32_t nCur = (curPosition_ - 1) >> (vol_->clusterSizeShift_ + 9);
+  uint32_t nNew = (pos - 1) >> (vol_->clusterSizeShift_ + 9);
+
+  if (nNew < nCur || curPosition_ == 0) {
+    // must follow chain from first cluster
+    curCluster_ = firstCluster_;
+  } else {
+    // advance from curPosition
+    nNew -= nCur;
+  }
+  while (nNew--) {
+    if (!vol_->fatGet(curCluster_, &curCluster_)) {
+      return false;
+    }
+  }
+  curPosition_ = pos;
+  return true;
+}
+//------------------------------------------------------------------------------
+/**
+   The sync() call causes all modified data and directory fields
+   to be written to the storage device.
+
+   \param[in] blocking If the sync should block until fully complete.
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+   Reasons for failure include a call to sync() before a file has been
+   opened or an I/O error.
+*/
+uint8_t SdFile::sync(uint8_t blocking) {
+  // only allow open files and directories
+  if (!isOpen()) {
+    return false;
+  }
+
+  if (flags_ & F_FILE_DIR_DIRTY) {
+    dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
+    if (!d) {
+      return false;
+    }
+
+    // do not set filesize for dir files
+    if (!isDir()) {
+      d->fileSize = fileSize_;
+    }
+
+    // update first cluster fields
+    d->firstClusterLow = firstCluster_ & 0XFFFF;
+    d->firstClusterHigh = firstCluster_ >> 16;
+
+    // set modify time if user supplied a callback date/time function
+    if (dateTime_) {
+      dateTime_(&d->lastWriteDate, &d->lastWriteTime);
+      d->lastAccessDate = d->lastWriteDate;
+    }
+    // clear directory dirty
+    flags_ &= ~F_FILE_DIR_DIRTY;
+  }
+
+  if (!blocking) {
+    flags_ &= ~F_FILE_NON_BLOCKING_WRITE;
+  }
+
+  return SdVolume::cacheFlush(blocking);
+}
+//------------------------------------------------------------------------------
+/**
+   Set a file's timestamps in its directory entry.
+
+   \param[in] flags Values for \a flags are constructed by a bitwise-inclusive
+   OR of flags from the following list
+
+   T_ACCESS - Set the file's last access date.
+
+   T_CREATE - Set the file's creation date and time.
+
+   T_WRITE - Set the file's last write/modification date and time.
+
+   \param[in] year Valid range 1980 - 2107 inclusive.
+
+   \param[in] month Valid range 1 - 12 inclusive.
+
+   \param[in] day Valid range 1 - 31 inclusive.
+
+   \param[in] hour Valid range 0 - 23 inclusive.
+
+   \param[in] minute Valid range 0 - 59 inclusive.
+
+   \param[in] second Valid range 0 - 59 inclusive
+
+   \note It is possible to set an invalid date since there is no check for
+   the number of days in a month.
+
+   \note
+   Modify and access timestamps may be overwritten if a date time callback
+   function has been set by dateTimeCallback().
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+*/
+uint8_t SdFile::timestamp(uint8_t flags, uint16_t year, uint8_t month,
+                          uint8_t day, uint8_t hour, uint8_t minute, uint8_t second) {
+  if (!isOpen()
+      || year < 1980
+      || year > 2107
+      || month < 1
+      || month > 12
+      || day < 1
+      || day > 31
+      || hour > 23
+      || minute > 59
+      || second > 59) {
+    return false;
+  }
+  dir_t* d = cacheDirEntry(SdVolume::CACHE_FOR_WRITE);
+  if (!d) {
+    return false;
+  }
+
+  uint16_t dirDate = FAT_DATE(year, month, day);
+  uint16_t dirTime = FAT_TIME(hour, minute, second);
+  if (flags & T_ACCESS) {
+    d->lastAccessDate = dirDate;
+  }
+  if (flags & T_CREATE) {
+    d->creationDate = dirDate;
+    d->creationTime = dirTime;
+    // seems to be units of 1/100 second not 1/10 as Microsoft states
+    d->creationTimeTenths = second & 1 ? 100 : 0;
+  }
+  if (flags & T_WRITE) {
+    d->lastWriteDate = dirDate;
+    d->lastWriteTime = dirTime;
+  }
+  SdVolume::cacheSetDirty();
+  return sync();
+}
+//------------------------------------------------------------------------------
+/**
+   Truncate a file to a specified length.  The current file position
+   will be maintained if it is less than or equal to \a length otherwise
+   it will be set to end of file.
+
+   \param[in] length The desired length for the file.
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.
+   Reasons for failure include file is read only, file is a directory,
+   \a length is greater than the current file size or an I/O error occurs.
+*/
+uint8_t SdFile::truncate(uint32_t length) {
+  // error if not a normal file or read-only
+  if (!isFile() || !(flags_ & O_WRITE)) {
+    return false;
+  }
+
+  // error if length is greater than current size
+  if (length > fileSize_) {
+    return false;
+  }
+
+  // fileSize and length are zero - nothing to do
+  if (fileSize_ == 0) {
+    return true;
+  }
+
+  // remember position for seek after truncation
+  uint32_t newPos = curPosition_ > length ? length : curPosition_;
+
+  // position to last cluster in truncated file
+  if (!seekSet(length)) {
+    return false;
+  }
+
+  if (length == 0) {
+    // free all clusters
+    if (!vol_->freeChain(firstCluster_)) {
+      return false;
+    }
+    firstCluster_ = 0;
+  } else {
+    uint32_t toFree;
+    if (!vol_->fatGet(curCluster_, &toFree)) {
+      return false;
+    }
+
+    if (!vol_->isEOC(toFree)) {
+      // free extra clusters
+      if (!vol_->freeChain(toFree)) {
+        return false;
+      }
+
+      // current cluster is end of chain
+      if (!vol_->fatPutEOC(curCluster_)) {
+        return false;
+      }
+    }
+  }
+  fileSize_ = length;
+
+  // need to update directory entry
+  flags_ |= F_FILE_DIR_DIRTY;
+
+  if (!sync()) {
+    return false;
+  }
+
+  // set file to correct position
+  return seekSet(newPos);
+}
+//------------------------------------------------------------------------------
+/**
+   Write data to an open file.
+
+   \note Data is moved to the cache but may not be written to the
+   storage device until sync() is called.
+
+   \param[in] buf Pointer to the location of the data to be written.
+
+   \param[in] nbyte Number of bytes to write.
+
+   \return For success write() returns the number of bytes written, always
+   \a nbyte.  If an error occurs, write() returns 0.  Possible errors
+   include write() is called before a file has been opened, write is called
+   for a read-only file, device is full, a corrupt file system or an I/O error.
+
+*/
+size_t SdFile::write(const void* buf, uint16_t nbyte) {
+  // convert void* to uint8_t*  -  must be before goto statements
+  const uint8_t* src = reinterpret_cast<const uint8_t*>(buf);
+
+  // number of bytes left to write  -  must be before goto statements
+  uint16_t nToWrite = nbyte;
+  // if blocking writes should be used
+  uint8_t blocking = (flags_ & F_FILE_NON_BLOCKING_WRITE) == 0x00;
+
+  // error if not a normal file or is read-only
+  if (!isFile() || !(flags_ & O_WRITE)) {
+    goto writeErrorReturn;
+  }
+
+  // seek to end of file if append flag
+  if ((flags_ & O_APPEND) && curPosition_ != fileSize_) {
+    if (!seekEnd()) {
+      goto writeErrorReturn;
+    }
+  }
+
+  while (nToWrite > 0) {
+    uint8_t blockOfCluster = vol_->blockOfCluster(curPosition_);
+    uint16_t blockOffset = curPosition_ & 0X1FF;
+    if (blockOfCluster == 0 && blockOffset == 0) {
+      // start of new cluster
+      if (curCluster_ == 0) {
+        if (firstCluster_ == 0) {
+          // allocate first cluster of file
+          if (!addCluster()) {
+            goto writeErrorReturn;
+          }
+        } else {
+          curCluster_ = firstCluster_;
+        }
+      } else {
+        uint32_t next;
+        if (!vol_->fatGet(curCluster_, &next)) {
+          return false;
+        }
+        if (vol_->isEOC(next)) {
+          // add cluster if at end of chain
+          if (!addCluster()) {
+            goto writeErrorReturn;
+          }
+        } else {
+          curCluster_ = next;
+        }
+      }
+    }
+    // max space in block
+    uint16_t n = 512 - blockOffset;
+
+    // lesser of space and amount to write
+    if (n > nToWrite) {
+      n = nToWrite;
+    }
+
+    // block for data write
+    uint32_t block = vol_->clusterStartBlock(curCluster_) + blockOfCluster;
+    if (n == 512) {
+      // full block - don't need to use cache
+      // invalidate cache if block is in cache
+      if (SdVolume::cacheBlockNumber_ == block) {
+        SdVolume::cacheBlockNumber_ = 0XFFFFFFFF;
+      }
+      if (!vol_->writeBlock(block, src, blocking)) {
+        goto writeErrorReturn;
+      }
+      src += 512;
+    } else {
+      if (blockOffset == 0 && curPosition_ >= fileSize_) {
+        // start of new block don't need to read into cache
+        if (!SdVolume::cacheFlush()) {
+          goto writeErrorReturn;
+        }
+        SdVolume::cacheBlockNumber_ = block;
+        SdVolume::cacheSetDirty();
+      } else {
+        // rewrite part of block
+        if (!SdVolume::cacheRawBlock(block, SdVolume::CACHE_FOR_WRITE)) {
+          goto writeErrorReturn;
+        }
+      }
+      uint8_t* dst = SdVolume::cacheBuffer_.data + blockOffset;
+      uint8_t* end = dst + n;
+      while (dst != end) {
+        *dst++ = *src++;
+      }
+    }
+    nToWrite -= n;
+    curPosition_ += n;
+  }
+  if (curPosition_ > fileSize_) {
+    // update fileSize and insure sync will update dir entry
+    fileSize_ = curPosition_;
+    flags_ |= F_FILE_DIR_DIRTY;
+  } else if (dateTime_ && nbyte) {
+    // insure sync will update modified date and time
+    flags_ |= F_FILE_DIR_DIRTY;
+  }
+
+  if (flags_ & O_SYNC) {
+    if (!sync()) {
+      goto writeErrorReturn;
+    }
+  }
+  return nbyte;
+
+writeErrorReturn:
+  // return for write error
+  //writeError = true;
+  setWriteError();
+  return 0;
+}
+//------------------------------------------------------------------------------
+/**
+   Write a byte to a file. Required by the Arduino Print class.
+
+   Use SdFile::writeError to check for errors.
+*/
+size_t SdFile::write(uint8_t b) {
+  return write(&b, 1);
+}
+//------------------------------------------------------------------------------
+/**
+   Write a string to a file. Used by the Arduino Print class.
+
+   Use SdFile::writeError to check for errors.
+*/
+size_t SdFile::write(const char* str) {
+  return write(str, strlen(str));
+}
+#ifdef __AVR__
+//------------------------------------------------------------------------------
+/**
+   Write a PROGMEM string to a file.
+
+   Use SdFile::writeError to check for errors.
+*/
+void SdFile::write_P(PGM_P str) {
+  for (uint8_t c; (c = pgm_read_byte(str)); str++) {
+    write(c);
+  }
+}
+//------------------------------------------------------------------------------
+/**
+   Write a PROGMEM string followed by CR/LF to a file.
+
+   Use SdFile::writeError to check for errors.
+*/
+void SdFile::writeln_P(PGM_P str) {
+  write_P(str);
+  println();
+}
+#endif
+//------------------------------------------------------------------------------
+/**
+   Check how many bytes can be written without blocking.
+
+   \return The number of bytes that can be written without blocking.
+*/
+int SdFile::availableForWrite() {
+  if (!isFile() || !(flags_ & O_WRITE)) {
+    return 0;
+  }
+
+  // seek to end of file if append flag
+  if ((flags_ & O_APPEND) && curPosition_ != fileSize_) {
+    if (!seekEnd()) {
+      return 0;
+    }
+  }
+
+  if (vol_->isBusy()) {
+    return 0;
+  }
+
+  if (flags_ & F_FILE_CLUSTER_ADDED) {
+    // new cluster added, trigger a non-blocking sync
+    sync(0);
+    flags_ &= ~F_FILE_CLUSTER_ADDED;
+    return 0;
+  }
+
+  if (vol_->isCacheMirrorBlockDirty()) {
+    // cache mirror block is dirty, trigger a non-blocking sync
+    vol_->cacheMirrorBlockFlush(0);
+    return 0;
+  }
+
+  flags_ |= F_FILE_NON_BLOCKING_WRITE;
+
+  uint16_t blockOffset = curPosition_ & 0X1FF;
+  uint16_t n = 512 - blockOffset;
+
+  return n;
+}
diff --git a/arduino/libraries/SD/src/utility/SdInfo.h b/arduino/libraries/SD/src/utility/SdInfo.h
new file mode 100644
index 000000000..6a0e087f4
--- /dev/null
+++ b/arduino/libraries/SD/src/utility/SdInfo.h
@@ -0,0 +1,232 @@
+/* Arduino Sd2Card Library
+   Copyright (C) 2009 by William Greiman
+
+   This file is part of the Arduino Sd2Card Library
+
+   This Library is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the Arduino Sd2Card Library.  If not, see
+   <http://www.gnu.org/licenses/>.
+*/
+#ifndef SdInfo_h
+#define SdInfo_h
+#include <stdint.h>
+// Based on the document:
+//
+// SD Specifications
+// Part 1
+// Physical Layer
+// Simplified Specification
+// Version 2.00
+// September 25, 2006
+//
+// www.sdcard.org/developers/tech/sdcard/pls/Simplified_Physical_Layer_Spec.pdf
+//------------------------------------------------------------------------------
+// SD card commands
+/** GO_IDLE_STATE - init card in spi mode if CS low */
+uint8_t const CMD0 = 0X00;
+/** SEND_IF_COND - verify SD Memory Card interface operating condition.*/
+uint8_t const CMD8 = 0X08;
+/** SEND_CSD - read the Card Specific Data (CSD register) */
+uint8_t const CMD9 = 0X09;
+/** SEND_CID - read the card identification information (CID register) */
+uint8_t const CMD10 = 0X0A;
+/** SEND_STATUS - read the card status register */
+uint8_t const CMD13 = 0X0D;
+/** READ_BLOCK - read a single data block from the card */
+uint8_t const CMD17 = 0X11;
+/** WRITE_BLOCK - write a single data block to the card */
+uint8_t const CMD24 = 0X18;
+/** WRITE_MULTIPLE_BLOCK - write blocks of data until a STOP_TRANSMISSION */
+uint8_t const CMD25 = 0X19;
+/** ERASE_WR_BLK_START - sets the address of the first block to be erased */
+uint8_t const CMD32 = 0X20;
+/** ERASE_WR_BLK_END - sets the address of the last block of the continuous
+    range to be erased*/
+uint8_t const CMD33 = 0X21;
+/** ERASE - erase all previously selected blocks */
+uint8_t const CMD38 = 0X26;
+/** APP_CMD - escape for application specific command */
+uint8_t const CMD55 = 0X37;
+/** READ_OCR - read the OCR register of a card */
+uint8_t const CMD58 = 0X3A;
+/** SET_WR_BLK_ERASE_COUNT - Set the number of write blocks to be
+     pre-erased before writing */
+uint8_t const ACMD23 = 0X17;
+/** SD_SEND_OP_COMD - Sends host capacity support information and
+    activates the card's initialization process */
+uint8_t const ACMD41 = 0X29;
+//------------------------------------------------------------------------------
+/** status for card in the ready state */
+uint8_t const R1_READY_STATE = 0X00;
+/** status for card in the idle state */
+uint8_t const R1_IDLE_STATE = 0X01;
+/** status bit for illegal command */
+uint8_t const R1_ILLEGAL_COMMAND = 0X04;
+/** start data token for read or write single block*/
+uint8_t const DATA_START_BLOCK = 0XFE;
+/** stop token for write multiple blocks*/
+uint8_t const STOP_TRAN_TOKEN = 0XFD;
+/** start data token for write multiple blocks*/
+uint8_t const WRITE_MULTIPLE_TOKEN = 0XFC;
+/** mask for data response tokens after a write block operation */
+uint8_t const DATA_RES_MASK = 0X1F;
+/** write data accepted token */
+uint8_t const DATA_RES_ACCEPTED = 0X05;
+//------------------------------------------------------------------------------
+typedef struct CID {
+  // byte 0
+  uint8_t mid;  // Manufacturer ID
+  // byte 1-2
+  char oid[2];  // OEM/Application ID
+  // byte 3-7
+  char pnm[5];  // Product name
+  // byte 8
+  unsigned prv_m : 4;  // Product revision n.m
+  unsigned prv_n : 4;
+  // byte 9-12
+  uint32_t psn;  // Product serial number
+  // byte 13
+  unsigned mdt_year_high : 4;  // Manufacturing date
+  unsigned reserved : 4;
+  // byte 14
+  unsigned mdt_month : 4;
+  unsigned mdt_year_low : 4;
+  // byte 15
+  unsigned always1 : 1;
+  unsigned crc : 7;
+} cid_t;
+//------------------------------------------------------------------------------
+// CSD for version 1.00 cards
+typedef struct CSDV1 {
+  // byte 0
+  unsigned reserved1 : 6;
+  unsigned csd_ver : 2;
+  // byte 1
+  uint8_t taac;
+  // byte 2
+  uint8_t nsac;
+  // byte 3
+  uint8_t tran_speed;
+  // byte 4
+  uint8_t ccc_high;
+  // byte 5
+  unsigned read_bl_len : 4;
+  unsigned ccc_low : 4;
+  // byte 6
+  unsigned c_size_high : 2;
+  unsigned reserved2 : 2;
+  unsigned dsr_imp : 1;
+  unsigned read_blk_misalign : 1;
+  unsigned write_blk_misalign : 1;
+  unsigned read_bl_partial : 1;
+  // byte 7
+  uint8_t c_size_mid;
+  // byte 8
+  unsigned vdd_r_curr_max : 3;
+  unsigned vdd_r_curr_min : 3;
+  unsigned c_size_low : 2;
+  // byte 9
+  unsigned c_size_mult_high : 2;
+  unsigned vdd_w_cur_max : 3;
+  unsigned vdd_w_curr_min : 3;
+  // byte 10
+  unsigned sector_size_high : 6;
+  unsigned erase_blk_en : 1;
+  unsigned c_size_mult_low : 1;
+  // byte 11
+  unsigned wp_grp_size : 7;
+  unsigned sector_size_low : 1;
+  // byte 12
+  unsigned write_bl_len_high : 2;
+  unsigned r2w_factor : 3;
+  unsigned reserved3 : 2;
+  unsigned wp_grp_enable : 1;
+  // byte 13
+  unsigned reserved4 : 5;
+  unsigned write_partial : 1;
+  unsigned write_bl_len_low : 2;
+  // byte 14
+  unsigned reserved5: 2;
+  unsigned file_format : 2;
+  unsigned tmp_write_protect : 1;
+  unsigned perm_write_protect : 1;
+  unsigned copy : 1;
+  unsigned file_format_grp : 1;
+  // byte 15
+  unsigned always1 : 1;
+  unsigned crc : 7;
+} csd1_t;
+//------------------------------------------------------------------------------
+// CSD for version 2.00 cards
+typedef struct CSDV2 {
+  // byte 0
+  unsigned reserved1 : 6;
+  unsigned csd_ver : 2;
+  // byte 1
+  uint8_t taac;
+  // byte 2
+  uint8_t nsac;
+  // byte 3
+  uint8_t tran_speed;
+  // byte 4
+  uint8_t ccc_high;
+  // byte 5
+  unsigned read_bl_len : 4;
+  unsigned ccc_low : 4;
+  // byte 6
+  unsigned reserved2 : 4;
+  unsigned dsr_imp : 1;
+  unsigned read_blk_misalign : 1;
+  unsigned write_blk_misalign : 1;
+  unsigned read_bl_partial : 1;
+  // byte 7
+  unsigned reserved3 : 2;
+  unsigned c_size_high : 6;
+  // byte 8
+  uint8_t c_size_mid;
+  // byte 9
+  uint8_t c_size_low;
+  // byte 10
+  unsigned sector_size_high : 6;
+  unsigned erase_blk_en : 1;
+  unsigned reserved4 : 1;
+  // byte 11
+  unsigned wp_grp_size : 7;
+  unsigned sector_size_low : 1;
+  // byte 12
+  unsigned write_bl_len_high : 2;
+  unsigned r2w_factor : 3;
+  unsigned reserved5 : 2;
+  unsigned wp_grp_enable : 1;
+  // byte 13
+  unsigned reserved6 : 5;
+  unsigned write_partial : 1;
+  unsigned write_bl_len_low : 2;
+  // byte 14
+  unsigned reserved7: 2;
+  unsigned file_format : 2;
+  unsigned tmp_write_protect : 1;
+  unsigned perm_write_protect : 1;
+  unsigned copy : 1;
+  unsigned file_format_grp : 1;
+  // byte 15
+  unsigned always1 : 1;
+  unsigned crc : 7;
+} csd2_t;
+//------------------------------------------------------------------------------
+// union of old and new style CSD register
+union csd_t {
+  csd1_t v1;
+  csd2_t v2;
+};
+#endif  // SdInfo_h
diff --git a/arduino/libraries/SD/src/utility/SdVolume.cpp b/arduino/libraries/SD/src/utility/SdVolume.cpp
new file mode 100644
index 000000000..60375c471
--- /dev/null
+++ b/arduino/libraries/SD/src/utility/SdVolume.cpp
@@ -0,0 +1,351 @@
+/* Arduino SdFat Library
+   Copyright (C) 2009 by William Greiman
+
+   This file is part of the Arduino SdFat Library
+
+   This Library is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This Library is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with the Arduino SdFat Library.  If not, see
+   <http://www.gnu.org/licenses/>.
+*/
+#include "SdFat.h"
+//------------------------------------------------------------------------------
+// raw block cache
+// init cacheBlockNumber_to invalid SD block number
+uint32_t SdVolume::cacheBlockNumber_ = 0XFFFFFFFF;
+cache_t  SdVolume::cacheBuffer_;     // 512 byte cache for Sd2Card
+Sd2Card* SdVolume::sdCard_;          // pointer to SD card object
+uint8_t  SdVolume::cacheDirty_ = 0;  // cacheFlush() will write block if true
+uint32_t SdVolume::cacheMirrorBlock_ = 0;  // mirror  block for second FAT
+//------------------------------------------------------------------------------
+// find a contiguous group of clusters
+uint8_t SdVolume::allocContiguous(uint32_t count, uint32_t* curCluster) {
+  // start of group
+  uint32_t bgnCluster;
+
+  // flag to save place to start next search
+  uint8_t setStart;
+
+  // set search start cluster
+  if (*curCluster) {
+    // try to make file contiguous
+    bgnCluster = *curCluster + 1;
+
+    // don't save new start location
+    setStart = false;
+  } else {
+    // start at likely place for free cluster
+    bgnCluster = allocSearchStart_;
+
+    // save next search start if one cluster
+    setStart = 1 == count;
+  }
+  // end of group
+  uint32_t endCluster = bgnCluster;
+
+  // last cluster of FAT
+  uint32_t fatEnd = clusterCount_ + 1;
+
+  // search the FAT for free clusters
+  for (uint32_t n = 0;; n++, endCluster++) {
+    // can't find space checked all clusters
+    if (n >= clusterCount_) {
+      return false;
+    }
+
+    // past end - start from beginning of FAT
+    if (endCluster > fatEnd) {
+      bgnCluster = endCluster = 2;
+    }
+    uint32_t f;
+    if (!fatGet(endCluster, &f)) {
+      return false;
+    }
+
+    if (f != 0) {
+      // cluster in use try next cluster as bgnCluster
+      bgnCluster = endCluster + 1;
+    } else if ((endCluster - bgnCluster + 1) == count) {
+      // done - found space
+      break;
+    }
+  }
+  // mark end of chain
+  if (!fatPutEOC(endCluster)) {
+    return false;
+  }
+
+  // link clusters
+  while (endCluster > bgnCluster) {
+    if (!fatPut(endCluster - 1, endCluster)) {
+      return false;
+    }
+    endCluster--;
+  }
+  if (*curCluster != 0) {
+    // connect chains
+    if (!fatPut(*curCluster, bgnCluster)) {
+      return false;
+    }
+  }
+  // return first cluster number to caller
+  *curCluster = bgnCluster;
+
+  // remember possible next free cluster
+  if (setStart) {
+    allocSearchStart_ = bgnCluster + 1;
+  }
+
+  return true;
+}
+//------------------------------------------------------------------------------
+uint8_t SdVolume::cacheFlush(uint8_t blocking) {
+  if (cacheDirty_) {
+    if (!sdCard_->writeBlock(cacheBlockNumber_, cacheBuffer_.data, blocking)) {
+      return false;
+    }
+
+    if (!blocking) {
+      return true;
+    }
+
+    // mirror FAT tables
+    if (!cacheMirrorBlockFlush(blocking)) {
+      return false;
+    }
+    cacheDirty_ = 0;
+  }
+  return true;
+}
+//------------------------------------------------------------------------------
+uint8_t SdVolume::cacheMirrorBlockFlush(uint8_t blocking) {
+  if (cacheMirrorBlock_) {
+    if (!sdCard_->writeBlock(cacheMirrorBlock_, cacheBuffer_.data, blocking)) {
+      return false;
+    }
+    cacheMirrorBlock_ = 0;
+  }
+  return true;
+}
+//------------------------------------------------------------------------------
+uint8_t SdVolume::cacheRawBlock(uint32_t blockNumber, uint8_t action) {
+  if (cacheBlockNumber_ != blockNumber) {
+    if (!cacheFlush()) {
+      return false;
+    }
+    if (!sdCard_->readBlock(blockNumber, cacheBuffer_.data)) {
+      return false;
+    }
+    cacheBlockNumber_ = blockNumber;
+  }
+  cacheDirty_ |= action;
+  return true;
+}
+//------------------------------------------------------------------------------
+// cache a zero block for blockNumber
+uint8_t SdVolume::cacheZeroBlock(uint32_t blockNumber) {
+  if (!cacheFlush()) {
+    return false;
+  }
+
+  // loop take less flash than memset(cacheBuffer_.data, 0, 512);
+  for (uint16_t i = 0; i < 512; i++) {
+    cacheBuffer_.data[i] = 0;
+  }
+  cacheBlockNumber_ = blockNumber;
+  cacheSetDirty();
+  return true;
+}
+//------------------------------------------------------------------------------
+// return the size in bytes of a cluster chain
+uint8_t SdVolume::chainSize(uint32_t cluster, uint32_t* size) const {
+  uint32_t s = 0;
+  do {
+    if (!fatGet(cluster, &cluster)) {
+      return false;
+    }
+    s += 512UL << clusterSizeShift_;
+  } while (!isEOC(cluster));
+  *size = s;
+  return true;
+}
+//------------------------------------------------------------------------------
+// Fetch a FAT entry
+uint8_t SdVolume::fatGet(uint32_t cluster, uint32_t* value) const {
+  if (cluster > (clusterCount_ + 1)) {
+    return false;
+  }
+  uint32_t lba = fatStartBlock_;
+  lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7;
+  if (lba != cacheBlockNumber_) {
+    if (!cacheRawBlock(lba, CACHE_FOR_READ)) {
+      return false;
+    }
+  }
+  if (fatType_ == 16) {
+    *value = cacheBuffer_.fat16[cluster & 0XFF];
+  } else {
+    *value = cacheBuffer_.fat32[cluster & 0X7F] & FAT32MASK;
+  }
+  return true;
+}
+//------------------------------------------------------------------------------
+// Store a FAT entry
+uint8_t SdVolume::fatPut(uint32_t cluster, uint32_t value) {
+  // error if reserved cluster
+  if (cluster < 2) {
+    return false;
+  }
+
+  // error if not in FAT
+  if (cluster > (clusterCount_ + 1)) {
+    return false;
+  }
+
+  // calculate block address for entry
+  uint32_t lba = fatStartBlock_;
+  lba += fatType_ == 16 ? cluster >> 8 : cluster >> 7;
+
+  if (lba != cacheBlockNumber_) {
+    if (!cacheRawBlock(lba, CACHE_FOR_READ)) {
+      return false;
+    }
+  }
+  // store entry
+  if (fatType_ == 16) {
+    cacheBuffer_.fat16[cluster & 0XFF] = value;
+  } else {
+    cacheBuffer_.fat32[cluster & 0X7F] = value;
+  }
+  cacheSetDirty();
+
+  // mirror second FAT
+  if (fatCount_ > 1) {
+    cacheMirrorBlock_ = lba + blocksPerFat_;
+  }
+  return true;
+}
+//------------------------------------------------------------------------------
+// free a cluster chain
+uint8_t SdVolume::freeChain(uint32_t cluster) {
+  // clear free cluster location
+  allocSearchStart_ = 2;
+
+  do {
+    uint32_t next;
+    if (!fatGet(cluster, &next)) {
+      return false;
+    }
+
+    // free cluster
+    if (!fatPut(cluster, 0)) {
+      return false;
+    }
+
+    cluster = next;
+  } while (!isEOC(cluster));
+
+  return true;
+}
+//------------------------------------------------------------------------------
+/**
+   Initialize a FAT volume.
+
+   \param[in] dev The SD card where the volume is located.
+
+   \param[in] part The partition to be used.  Legal values for \a part are
+   1-4 to use the corresponding partition on a device formatted with
+   a MBR, Master Boot Record, or zero if the device is formatted as
+   a super floppy with the FAT boot sector in block zero.
+
+   \return The value one, true, is returned for success and
+   the value zero, false, is returned for failure.  Reasons for
+   failure include not finding a valid partition, not finding a valid
+   FAT file system in the specified partition or an I/O error.
+*/
+uint8_t SdVolume::init(Sd2Card* dev, uint8_t part) {
+  uint32_t volumeStartBlock = 0;
+  sdCard_ = dev;
+  // if part == 0 assume super floppy with FAT boot sector in block zero
+  // if part > 0 assume mbr volume with partition table
+  if (part) {
+    if (part > 4) {
+      return false;
+    }
+    if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) {
+      return false;
+    }
+    part_t* p = &cacheBuffer_.mbr.part[part - 1];
+    if ((p->boot & 0X7F) != 0  ||
+        p->totalSectors < 100 ||
+        p->firstSector == 0) {
+      // not a valid partition
+      return false;
+    }
+    volumeStartBlock = p->firstSector;
+  }
+  if (!cacheRawBlock(volumeStartBlock, CACHE_FOR_READ)) {
+    return false;
+  }
+  bpb_t* bpb = &cacheBuffer_.fbs.bpb;
+  if (bpb->bytesPerSector != 512 ||
+      bpb->fatCount == 0 ||
+      bpb->reservedSectorCount == 0 ||
+      bpb->sectorsPerCluster == 0) {
+    // not valid FAT volume
+    return false;
+  }
+  fatCount_ = bpb->fatCount;
+  blocksPerCluster_ = bpb->sectorsPerCluster;
+
+  // determine shift that is same as multiply by blocksPerCluster_
+  clusterSizeShift_ = 0;
+  while (blocksPerCluster_ != (1 << clusterSizeShift_)) {
+    // error if not power of 2
+    if (clusterSizeShift_++ > 7) {
+      return false;
+    }
+  }
+  blocksPerFat_ = bpb->sectorsPerFat16 ?
+                  bpb->sectorsPerFat16 : bpb->sectorsPerFat32;
+
+  fatStartBlock_ = volumeStartBlock + bpb->reservedSectorCount;
+
+  // count for FAT16 zero for FAT32
+  rootDirEntryCount_ = bpb->rootDirEntryCount;
+
+  // directory start for FAT16 dataStart for FAT32
+  rootDirStart_ = fatStartBlock_ + bpb->fatCount * blocksPerFat_;
+
+  // data start for FAT16 and FAT32
+  dataStartBlock_ = rootDirStart_ + ((32 * bpb->rootDirEntryCount + 511) / 512);
+
+  // total blocks for FAT16 or FAT32
+  uint32_t totalBlocks = bpb->totalSectors16 ?
+                         bpb->totalSectors16 : bpb->totalSectors32;
+  // total data blocks
+  clusterCount_ = totalBlocks - (dataStartBlock_ - volumeStartBlock);
+
+  // divide by cluster size to get cluster count
+  clusterCount_ >>= clusterSizeShift_;
+
+  // FAT type is determined by cluster count
+  if (clusterCount_ < 4085) {
+    fatType_ = 12;
+  } else if (clusterCount_ < 65525) {
+    fatType_ = 16;
+  } else {
+    rootDirStart_ = bpb->fat32RootCluster;
+    fatType_ = 32;
+  }
+  return true;
+}
diff --git a/arduino/libraries/elapsedMillis/LICENSE b/arduino/libraries/elapsedMillis/LICENSE
new file mode 100644
index 000000000..e8e922656
--- /dev/null
+++ b/arduino/libraries/elapsedMillis/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2011 PJRC.COM, LLC 
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/arduino/libraries/elapsedMillis/README.md b/arduino/libraries/elapsedMillis/README.md
new file mode 100644
index 000000000..692a993f3
--- /dev/null
+++ b/arduino/libraries/elapsedMillis/README.md
@@ -0,0 +1,12 @@
+elapsedMillis 
+=============
+![Latest Release](https://img.shields.io/github/v/release/pfeerick/elapsedMillis) [![Examples Build 
Status](https://github.com/pfeerick/elapsedMillis/workflows/build/badge.svg)](https://github.com/pfeerick/elapsedMillis/actions)
+
+
+These special variable types (*objects*) automatically increase as time elapses. This makes it easy to check 
if a certain time has elapsed, while allowing your program to perform other work or checks for user input. It 
is also very to handle multiple tasks requiring different delays.
+
+Documentation on how to use this library is located in the 
[wiki](https://github.com/pfeerick/elapsedMillis/wiki) and also on the [Arduino 
Playground](http://playground.arduino.cc//Code/ElapsedMillis).
+
+Please report any bugs or issues you find on the [issue 
tracker](https://github.com/pfeerick/elapsedMillis/issues).
+
+Initial code derived from Paul Stoffregen's elapsedMillis and elapsedMicros helper code for the Teensy USB 
developer board. Code contributes and initial examples created by John Plocher.
diff --git a/arduino/libraries/elapsedMillis/elapsedMillis.h b/arduino/libraries/elapsedMillis/elapsedMillis.h
new file mode 100644
index 000000000..dbb494cf3
--- /dev/null
+++ b/arduino/libraries/elapsedMillis/elapsedMillis.h
@@ -0,0 +1,104 @@
+/* Elapsed time types - for easy-to-use measurements of elapsed time
+ * http://www.pjrc.com/teensy/
+ * Copyright (c) 2011 PJRC.COM, LLC
+ * 
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ * 
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ * 
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#ifndef elapsedMillis_h
+#define elapsedMillis_h
+#ifdef __cplusplus
+
+#if ARDUINO >= 100
+#include "Arduino.h"
+#else
+#include "WProgram.h"
+#endif
+
+class elapsedMicros
+{
+private:
+       unsigned long us;
+public:
+       elapsedMicros(void) { us = micros(); }
+       elapsedMicros(unsigned long val) { us = micros() - val; }
+       elapsedMicros(const elapsedMicros &orig) { us = orig.us; }
+       operator unsigned long () const { return micros() - us; }
+       elapsedMicros & operator = (const elapsedMicros &rhs) { us = rhs.us; return *this; }
+       elapsedMicros & operator = (unsigned long val) { us = micros() - val; return *this; }
+       elapsedMicros & operator -= (unsigned long val)      { us += val ; return *this; }
+       elapsedMicros & operator += (unsigned long val)      { us -= val ; return *this; }
+       elapsedMicros operator - (int val) const           { elapsedMicros r(*this); r.us += val; return r; }
+       elapsedMicros operator - (unsigned int val) const  { elapsedMicros r(*this); r.us += val; return r; }
+       elapsedMicros operator - (long val) const          { elapsedMicros r(*this); r.us += val; return r; }
+       elapsedMicros operator - (unsigned long val) const { elapsedMicros r(*this); r.us += val; return r; }
+       elapsedMicros operator + (int val) const           { elapsedMicros r(*this); r.us -= val; return r; }
+       elapsedMicros operator + (unsigned int val) const  { elapsedMicros r(*this); r.us -= val; return r; }
+       elapsedMicros operator + (long val) const          { elapsedMicros r(*this); r.us -= val; return r; }
+       elapsedMicros operator + (unsigned long val) const { elapsedMicros r(*this); r.us -= val; return r; }
+};
+
+class elapsedMillis
+{
+private:
+       unsigned long ms;
+public:
+       elapsedMillis(void) { ms = millis(); }
+       elapsedMillis(unsigned long val) { ms = millis() - val; }
+       elapsedMillis(const elapsedMillis &orig) { ms = orig.ms; }
+       operator unsigned long () const { return millis() - ms; }
+       elapsedMillis & operator = (const elapsedMillis &rhs) { ms = rhs.ms; return *this; }
+       elapsedMillis & operator = (unsigned long val) { ms = millis() - val; return *this; }
+       elapsedMillis & operator -= (unsigned long val)      { ms += val ; return *this; }
+       elapsedMillis & operator += (unsigned long val)      { ms -= val ; return *this; }
+       elapsedMillis operator - (int val) const           { elapsedMillis r(*this); r.ms += val; return r; }
+       elapsedMillis operator - (unsigned int val) const  { elapsedMillis r(*this); r.ms += val; return r; }
+       elapsedMillis operator - (long val) const          { elapsedMillis r(*this); r.ms += val; return r; }
+       elapsedMillis operator - (unsigned long val) const { elapsedMillis r(*this); r.ms += val; return r; }
+       elapsedMillis operator + (int val) const           { elapsedMillis r(*this); r.ms -= val; return r; }
+       elapsedMillis operator + (unsigned int val) const  { elapsedMillis r(*this); r.ms -= val; return r; }
+       elapsedMillis operator + (long val) const          { elapsedMillis r(*this); r.ms -= val; return r; }
+       elapsedMillis operator + (unsigned long val) const { elapsedMillis r(*this); r.ms -= val; return r; }
+};
+
+class elapsedSeconds
+{
+private:
+       unsigned long s;
+public:
+       elapsedSeconds(void) { s = millis()*0.001; }
+       elapsedSeconds(unsigned long val) { s = millis()*0.001 - val; }
+       elapsedSeconds(const elapsedSeconds &orig) { s = orig.s; }
+       operator unsigned long () const { return millis()*0.001 - s; }
+       elapsedSeconds & operator = (const elapsedSeconds &rhs) { s = rhs.s; return *this; }
+       elapsedSeconds & operator = (unsigned long val) { s = millis()*0.001 - val; return *this; }
+       elapsedSeconds & operator -= (unsigned long val)      { s += val ; return *this; }
+       elapsedSeconds & operator += (unsigned long val)      { s -= val ; return *this; }
+       elapsedSeconds operator - (int val) const           { elapsedSeconds r(*this); r.s += val; return r; }
+       elapsedSeconds operator - (unsigned int val) const  { elapsedSeconds r(*this); r.s += val; return r; }
+       elapsedSeconds operator - (long val) const          { elapsedSeconds r(*this); r.s += val; return r; }
+       elapsedSeconds operator - (unsigned long val) const { elapsedSeconds r(*this); r.s += val; return r; }
+       elapsedSeconds operator + (int val) const           { elapsedSeconds r(*this); r.s -= val; return r; }
+       elapsedSeconds operator + (unsigned int val) const  { elapsedSeconds r(*this); r.s -= val; return r; }
+       elapsedSeconds operator + (long val) const          { elapsedSeconds r(*this); r.s -= val; return r; }
+       elapsedSeconds operator + (unsigned long val) const { elapsedSeconds r(*this); r.s -= val; return r; }
+};
+
+#endif // __cplusplus
+#endif // elapsedMillis_h
diff --git a/arduino/libraries/elapsedMillis/examples/GettingStarted/GettingStarted.ino 
b/arduino/libraries/elapsedMillis/examples/GettingStarted/GettingStarted.ino
new file mode 100644
index 000000000..54b0856b8
--- /dev/null
+++ b/arduino/libraries/elapsedMillis/examples/GettingStarted/GettingStarted.ino
@@ -0,0 +1,41 @@
+/*  
+  Simple example of using elapsedMillis library
+  
+  This example does nothing more than blinking a LED... but the difference 
+  here to just using delay(1000) to control the delay between blinks is that 
+  you can do more in the loop - like get user input, check buttons, get sensor
+  input and other tasks that take time - while you wait for the next time the 
+  LED is to change state. All you need to do is add it before or after the 
+  'if' statement that controls the check of the time elapsed. 
+  
+  This example code is in the public domain.
+*/
+
+#include <elapsedMillis.h>
+
+elapsedMillis timeElapsed; //declare global if you don't want it reset every time loop runs
+
+// Pin 13 has an LED connected on most Arduino boards.
+int led = 13;
+
+// delay in milliseconds between blinks of the LED
+unsigned int interval = 1000;
+
+// state of the LED = LOW is off, HIGH is on
+boolean ledState = LOW;
+
+void setup()
+{
+  // initialize the digital pin as an output.
+  pinMode(led, OUTPUT);
+}
+
+void loop()
+{
+  if (timeElapsed > interval)
+  {
+    ledState = !ledState; // toggle the state from HIGH to LOW to HIGH to LOW ...
+    digitalWrite(led, ledState);
+    timeElapsed = 0; // reset the counter to 0 so the counting starts over...
+  }
+}
diff --git a/arduino/libraries/elapsedMillis/examples/blinkingLeds/blinkingLeds.ino 
b/arduino/libraries/elapsedMillis/examples/blinkingLeds/blinkingLeds.ino
new file mode 100644
index 000000000..acd92eae7
--- /dev/null
+++ b/arduino/libraries/elapsedMillis/examples/blinkingLeds/blinkingLeds.ino
@@ -0,0 +1,85 @@
+/*  
+  Simple demo of using all the timing helpers elapsedMillis makes available.
+
+  Either attach LEDs with series resistors to the indicated pins, or a
+  six led / six bit 'Chartreuse' module plugged into pins 8 through GND. 
+  
+  Wired up in order, the leds have a nice walking/counting effect.
+  
+  This example code is in the public domain.
+*/
+
+#include <elapsedMillis.h>
+
+//declare these global if you don't want them reset every time loop runs
+elapsedMicros LED1micro;
+elapsedMicros LED2micro;
+elapsedMillis LED3millis;
+elapsedMillis LED4millis;
+elapsedSeconds LED5seconds;
+elapsedSeconds LED6seconds;
+
+const int LED1 = 8;
+const int LED2 = 9;
+const int LED3 = 10;
+const int LED4 = 11;
+const int LED5 = 12;
+const int LED6 = 13;
+
+// delay between blinks of the LED
+unsigned long LED1_Interval = 62500;
+unsigned long LED2_Interval = 125000;
+unsigned int LED3_Interval = 250;
+unsigned int LED4_Interval = 500;
+unsigned int LED5_Interval = 1;
+unsigned int LED6_Interval = 2;
+
+void setup()
+{
+  // initialize the LED pins as outputs
+  pinMode(LED1, OUTPUT);
+  pinMode(LED2, OUTPUT);
+  pinMode(LED3, OUTPUT);
+  pinMode(LED4, OUTPUT);
+  pinMode(LED5, OUTPUT);
+  pinMode(LED6, OUTPUT);
+}
+
+void loop()
+{
+  if (LED1micro >= LED1_Interval)
+  {
+    digitalWrite(LED1, !(digitalRead(LED1))); // toggle the LED state
+    LED1micro = 0;                            // reset the counter to 0 so the counting starts over...
+  }
+
+  if (LED2micro >= LED2_Interval)
+  {
+    digitalWrite(LED2, !(digitalRead(LED2))); // toggle the LED state
+    LED2micro = 0;                            // reset the counter to 0 so the counting starts over...
+  }
+
+  if (LED3millis >= LED3_Interval)
+  {
+    digitalWrite(LED3, !(digitalRead(LED3))); // toggle the LED state
+    LED3millis = 0;                           // reset the counter to 0 so the counting starts over...
+  }
+
+  if (LED4millis >= LED4_Interval)
+  {
+    digitalWrite(LED4, !(digitalRead(LED4))); // toggle the LED state
+    LED4millis = 0;                           // reset the counter to 0 so the counting starts over...
+  }
+
+  if (LED5seconds >= LED5_Interval)
+  {
+    digitalWrite(LED5, !(digitalRead(LED5))); // toggle the LED state
+    LED5seconds = 0;                          // reset the counter to 0 so the counting starts over...
+  }
+
+  if (LED6seconds >= LED6_Interval)
+  {
+    digitalWrite(LED6, !(digitalRead(LED6))); // toggle the LED state
+    LED6seconds = 0;                          // reset the counter to 0 so the counting starts over...
+  }
+}
diff --git a/arduino/libraries/elapsedMillis/examples/timeouts/timeouts.ino 
b/arduino/libraries/elapsedMillis/examples/timeouts/timeouts.ino
new file mode 100644
index 000000000..1290e99b9
--- /dev/null
+++ b/arduino/libraries/elapsedMillis/examples/timeouts/timeouts.ino
@@ -0,0 +1,145 @@
+/*
+  Using timeouts for user interaction
+  
+  A more "real" example than blinking a LED - demonstrate waiting for user input while 
+  flashing a LED in an arbitrary sequence.
+ 
+  This example code is in the public domain.
+
+  The elapsedMillis() and elapsedMicros() code was originally developed for
+  the Teensy USB development board (http://www.pjrc.com/teensy/)
+
+  More examples can be found at http://www.pjrc.com/teensy/td_timing.html
+
+  Note that elapsedMillis variables should usually have global scope.  If they're 
+  local, they restart from zero every time the function runs, which might be useful 
+  in some cases, but usually you want them to retain their value over longer time.  
+ */
+
+#include <elapsedMillis.h>
+#include <ctype.h>
+
+#define LEDPIN 13         // The usual Arduino LED port...
+#define USERWAITTIME 6000 // milliseconds
+#define LEDWAITTIME 35    // milliseconds
+
+// Many non-trivial Arduino programs benefit from being expressed as a state machine.
+// This one encodes the various nuances of accepting user input while still doing
+// background work (flashing the LED...)
+//
+// State machine:  a device that can be in one of a set number of stable conditions
+//                 depending on its previous condition and on the present values of
+//                 its inputs.
+//
+// Alan Skorkin wrote a nice blog about using state machines:
+//    http://www.skorks.com/2011/09/why-developers-never-use-state-machines/
+// in response to this (non-Arduino) post:
+//    
http://www.shopify.com/technology/3383012-why-developers-should-be-force-fed-state-machines#axzz2jvAUuwUj
+//
+enum State
+{
+    INIT,
+    NEEDINPUT,
+    WAITING,
+    TIMEOUT,
+    PROCESSING,
+    FINISHED,
+    ERROR
+} state;
+
+// The LED would be really boring if it just sat there being either always on or always off
+// The array encodes a flash pattern to let you know that nothing is hung...
+boolean flashPattern[] = {1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0};
+unsigned int ledIndex = 0;
+
+elapsedMillis timeout; // Globally scoped - see comment above
+elapsedMillis pulserate;
+char userinput;
+boolean ledBrightness = HIGH;
+String validInput = "YN";
+
+void setup()
+{
+    //set the LED pin as an output
+    pinMode(LEDPIN, OUTPUT);
+
+    //Initialize serial and wait for port to open:
+    Serial.begin(9600);
+    while (!Serial)
+    {
+        ; // wait for serial port to connect. Needed for Leonardo only
+    }
+    state = INIT;
+}
+
+void loop()
+{
+    // Every time through loop(), the state variable controls what we do.
+    // As things get done, the state variable is updated to hold the next state.
+    // The power of a state machine is that it can clearly describe the program
+    // flow through a complicated decision tree as opposed to a monoithic structure.
+
+    switch (state)
+    {
+    case INIT:
+        Serial.println("Welcome to the world of elapsed time");
+        Serial.println("This is a Simple light sequence controller.");
+        Serial.print("Watch the LED on pin");
+        Serial.print(LEDPIN, DEC);
+        Serial.println("as you interact with the program.");
+        state = NEEDINPUT;
+        break;
+    case NEEDINPUT:
+        Serial.print("Toggle LED? (Y/n): ");
+        timeout = 0;
+        state = WAITING;
+        break;
+    case WAITING:
+        if (Serial.available())
+        {
+            userinput = toupper(Serial.read());
+            if (validInput.indexOf(userinput) == -1)
+            {
+                state = ERROR;
+            }
+            else
+            {
+                state = PROCESSING;
+            }
+            break;
+        }
+        else if (timeout > USERWAITTIME)
+        {
+            state = TIMEOUT;
+        }
+        break;
+    case TIMEOUT: // If you wanted to be obnoxious, you could ring a bell to get the user's attention at 
this point
+        Serial.println("<took too long>");
+        state = NEEDINPUT;
+        break;
+    case PROCESSING:
+        Serial.println(userinput);
+        // The whole point is to make the flashpattern invert when the user says "Y"
+        if (userinput == 'Y')
+            ledBrightness = !ledBrightness;
+        state = NEEDINPUT;
+        break;
+    default:
+    case ERROR:
+        Serial.println(" Braap!");
+        Serial.print("Expected [");
+        Serial.print(validInput);
+        Serial.println("], please follow directions!");
+        state = NEEDINPUT;
+        break;
+    }
+
+    if (pulserate > LEDWAITTIME)
+    {
+        // Demonstrate doing 2 things at once - waiting for input (above) while flashing a LED in a sequence 
(here)
+        digitalWrite(LEDPIN, flashPattern[ledIndex++] ? ledBrightness : !ledBrightness);
+        if (ledIndex > sizeof(flashPattern))
+            ledIndex = 0;
+        pulserate = 0;
+    }
+}
diff --git a/arduino/libraries/elapsedMillis/examples/timingComparison/timingComparison.ino 
b/arduino/libraries/elapsedMillis/examples/timingComparison/timingComparison.ino
new file mode 100644
index 000000000..97ef67c68
--- /dev/null
+++ b/arduino/libraries/elapsedMillis/examples/timingComparison/timingComparison.ino
@@ -0,0 +1,163 @@
+/*
+  Blink with elapsed time
+  Shows how to use elapsed time to free up your sketch to do interesting work
+ 
+  This example code is in the public domain.
+
+  The elapsedMillis() and elapsedMicros() code was originally developed for
+  the Teensy USB development board (http://www.pjrc.com/teensy/)
+
+  Using timers in your code introduces a new concept - variables that
+  change their value "by themselves"
+
+  The advantage of using timers or elapsed time counters is that you
+  can use them to free yourself from the busy-wait limitations of
+  delay(), allowing you to do other work in your sketch.
+
+  Unlike the "int led" variable used below, which is initialized once
+  and then assumed to always hold the same value, or the ledState
+  variable that is explicitly set to a new value every time the LED is
+  turned on or off, the elapsed time datatypes introduced here
+  automatically update themselves as time passes; their "value" is the
+  amount of time that has passed since the variable was created or
+  set. When you set an elapsedMillis variable to 0 (zero), 1
+  millisecond later it will contain 1, not 0.  1385 milliseconds
+  later, it will contain 1385.
+
+  You can set/reset these variables to whatever value you want,
+  whenever you want, and they will be incremented by 1 after each
+  milli/micro-second of time has passed.  You can use them to easily
+  decide in your program when to do stuff.
+
+  But be careful!  Most of the time you will probably want to declare
+  variables of the elapsedMillis/elpasedMicros data-type globally, at
+  the top of your sketch.  If you define them inside a function() or
+  in loop(), they will be reset to 0 (zero) each time the function is
+  called; there they will do a good (but probably unintended) job of
+  counting the only elapsed time taken since the function was called.
+
+  Both milliseconds and mimicrosecond based interval counters are 
+  supported:
+     elapsedmillis em1;
+        elapsedmicros em2;
+       
+  All the standard operations are supported on them.
+
+  There are 3 examples below that blink a LED every second.  
+    * The first is the venerable blink sketch that uses delay() to 
+      "do nothing" while waiting for time to pass.
+    * The second uses calls to millis() to calculate if enough 
+      time has passed, and
+    * The third is similar, but uses an elapsedmillis variable.
+
+  All three of these are examples of an explicit polling mechanism
+  - your code continually asks itself "is it time yet", and if so,
+  does something.  The alternative, in situations where much more
+  exact timing is needed, or if further decoupling is required
+  between your time-aware code and the rest of your application is
+  an interrupt handler that takes advantage of the various TIMER
+  mechanisms that are supported directly by your microcontroller.
+  For more on using the TIMER hardware directly, see the Arduino
+  Timer examples on playground:
+        http://playground.arduino.cc/code/timer1
+
+  More examples can be found at http://www.pjrc.com/teensy/td_timing.html
+
+ */
+
+#include <elapsedMillis.h>
+
+// Pin 13 has an LED connected on most Arduino boards.
+int led = 13;
+
+// the setup routine runs once when you press reset:
+void setup()
+{
+  // initialize the digital pin as an output.
+  pinMode(led, OUTPUT);
+}
+
+elapsedMillis elapsedTime; // used by elapsedmilis example
+                           // Globally scoped - see comment above
+
+unsigned long lasttime = millis(); // used by millis() example...
+
+unsigned int interval = 1000;
+boolean ledState = LOW;
+
+void loop()
+{
+  // delayloop();
+  // millisloop();
+  elapsedmillisloop();
+}
+
+// ##########################  delay() based loop ###########################
+//
+// the loop routine runs over and over again forever,
+// but that is all it can do, since the delay() calls preclude
+// doing other work...
+//
+// In this example, the LED blinks on and off once per invocation of loop()
+
+void delayloop()
+{
+  // Note that any "real code" you put here will cause the time between blinks to grow
+  delay(1000); // wait for a second
+  // and, finally, blink the lights:
+
+  ledState = !ledState; // toggle the state from HIGH to LOW to HIGH to LOW ...
+  digitalWrite(led, ledState);
+}
+
+// ##########################  millis() based loop ###########################
+//
+// the loop routine runs over and over again forever,
+// with the blink rate decoupled from however much time the "real code" of the sketch takes
+//
+// In this example, the blink rate of the LED is independent of the number of times loop()
+// is invoked as well as how long it takes to the rest of loop()'s code to run (presuming,
+// of course, that the desired blink interval is significantly larger than than that "other"
+// time)
+
+void millisloop()
+{
+
+  // your "real" code goes here:
+  //      read sensors
+  //      compute new servo positions
+  //      update motor driver output
+  // and, finally, blink the lights:
+
+  if (millis() - lasttime >= interval)
+  {
+    ledState = !ledState; // toggle the state from HIGH to LOW to HIGH to LOW ...
+    digitalWrite(led, ledState);
+    lasttime = millis();
+  }
+}
+
+// ##########################  elapsedmillis() based loop ###########################
+//
+// the loop routine runs over and over again forever,
+// with the blink rate decoupled from however much time the "real code" of the sketch takes
+//
+// This example is almost the same as the millis() one, with the various accounting details
+// abstracted by the new datatype.
+
+void elapsedmillisloop()
+{
+
+  // your "real" code goes here:
+  //      read sensors
+  //      compute new servo positions
+  //      update motor driver output
+  // and, finally, blink the lights:
+
+  if (elapsedTime > interval)
+  {                       // Again, note that "elapsedTime" is global scope
+    ledState = !ledState; // toggle the state from HIGH to LOW to HIGH to LOW ...
+    digitalWrite(led, ledState);
+    elapsedTime = 0; // reset the counter to 0 so the counting starts over...
+  }
+}
diff --git a/arduino/libraries/elapsedMillis/keywords.txt b/arduino/libraries/elapsedMillis/keywords.txt
new file mode 100644
index 000000000..6ecb3caa5
--- /dev/null
+++ b/arduino/libraries/elapsedMillis/keywords.txt
@@ -0,0 +1,19 @@
+#######################################
+# Syntax Coloring Map For elapsedMillis 
+#######################################
+
+#######################################
+# Datatypes (KEYWORD1)
+#######################################
+
+elapsedMillis  KEYWORD1
+elapsedMicros  KEYWORD1
+
+#######################################
+# Methods and Functions (KEYWORD2)
+#######################################
+
+#######################################
+# Constants (LITERAL1)
+#######################################
+
diff --git a/arduino/libraries/elapsedMillis/library.json b/arduino/libraries/elapsedMillis/library.json
new file mode 100644
index 000000000..ed59f5363
--- /dev/null
+++ b/arduino/libraries/elapsedMillis/library.json
@@ -0,0 +1,26 @@
+{
+       "name": "elapsedMillis",
+       "version": "1.0.5",
+       "keywords": "timing, delay",
+       "description": "Library for making responsive code easier using elapsed time objects",
+       "authors":
+       [
+               {
+               "name": "Paul Stoffregen",
+               "email": "paul pjrc com",
+               "url": "http://pjrc.com/about/contact.html";
+               },
+               {
+               "name": "Peter Feerick",
+               "email": "peter feerick gmail com",
+               "url": "https://blog.peterfeerick.com.au/contact/";,
+               "maintainer": true
+               }
+       ],
+       "repository":
+       {
+               "type": "git",
+               "url": "https://github.com/pfeerick/elapsedMillis.git";
+       },
+       "frameworks": "arduino"
+}
diff --git a/arduino/libraries/elapsedMillis/library.properties 
b/arduino/libraries/elapsedMillis/library.properties
new file mode 100644
index 000000000..04d5e006b
--- /dev/null
+++ b/arduino/libraries/elapsedMillis/library.properties
@@ -0,0 +1,9 @@
+name=elapsedMillis
+version=1.0.5
+author=Paul Stoffregen <paul pjrc com>
+maintainer=Peter Feerick <peter feerick gmail com>
+sentence=Makes coding responsive sketches easier.
+paragraph=When using delay(), your code can not (easily) respond to user input while the delay is happening 
(unless you use interrupts or complex timer code). This library makes this easy by allowing you to create 
variables (objects) that automatically increase as time elapses. It is easy to check if a certain time has 
elapsed, while your program performs other work or checks for user input.
+category=Timing
+url=http://github.com/pfeerick/elapsedMillis/wiki
+architectures=*


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]