From 2165daa49d3b4caa8fd80cf2841183f8db218c8c Mon Sep 17 00:00:00 2001 From: Brendan <2bndy5@gmail.com> Date: Wed, 21 Jun 2023 12:47:50 -0700 Subject: [PATCH] Template layer for radio HW abstraction (#223) * apply template to RF24Mesh lib in accordance with discussion at nRF24/RF24Network#204 uses type definition for RF24Mesh and renames class to ESB_Mesh for backwards compatibility. updates CI to check RF24Network changes specifically for the template-attempt2 branch * Update docs for v2.0 * bump major version --------- Co-authored-by: TMRh20 --- .github/workflows/build_arduino.yml | 3 +- .github/workflows/build_platformIO.yml | 4 +- .gitignore | 3 ++ README.md | 28 ++++++++++ RF24Mesh.cpp | 74 ++++++++++++++++++-------- RF24Mesh.h | 66 +++++++++++++++++++---- docs/main_page.md | 34 ++++++++++-- docs/sphinx/classRF24Mesh.rst | 63 +++++++++++++--------- docs/sphinx/conf.py | 1 - library.json | 2 +- library.properties | 2 +- 11 files changed, 211 insertions(+), 69 deletions(-) diff --git a/.github/workflows/build_arduino.yml b/.github/workflows/build_arduino.yml index 67d4bbad..2b675bfc 100644 --- a/.github/workflows/build_arduino.yml +++ b/.github/workflows/build_arduino.yml @@ -35,6 +35,7 @@ jobs: - source-url: https://github.com/nRF24/RF24.git - source-url: https://github.com/nRF24/RF24Network.git - source-path: ./ + - name: nrf_to_nrf fqbn: ${{ matrix.fqbn }} enable-deltas-report: ${{ matrix.enable-deltas-report }} platforms: | @@ -70,7 +71,7 @@ jobs: - "arduino:avr:chiwawa" - "arduino:avr:one" - "arduino:avr:unowifi" - # - "arduino:mbed:nano33ble" # pending nRF5x integration + - "arduino:mbed:nano33ble" - "arduino:samd:mkr1000" - "arduino:samd:mkrwifi1010" - "arduino:samd:nano_33_iot" diff --git a/.github/workflows/build_platformIO.yml b/.github/workflows/build_platformIO.yml index d1e4d683..d63af5a4 100644 --- a/.github/workflows/build_platformIO.yml +++ b/.github/workflows/build_platformIO.yml @@ -35,8 +35,8 @@ jobs: needs: [check_formatting, validate_lib_json] uses: nRF24/.github/.github/workflows/build_platformio.yaml@main with: - example-path: ${{ matrix.example }} - board-id: ${{ matrix.board }} + example-path: ${{ matrix.example }} + board-id: ${{ matrix.board }} strategy: fail-fast: false matrix: diff --git a/.gitignore b/.gitignore index 1db888b5..a79ed1aa 100644 --- a/.gitignore +++ b/.gitignore @@ -25,6 +25,9 @@ build/ .env/ .venv/ +# ignore python build artifacts +*.egg-info/ + # Cmake build-in-source generated stuff CMakeCache.txt CPackConfig.cmake diff --git a/README.md b/README.md index aa235d33..e11285ef 100644 --- a/README.md +++ b/README.md @@ -9,4 +9,32 @@ Mesh Networking for RF24Network +Introducing **RF24Network & RF24Mesh v2.0** with some *significant API changes*, adding the use of [C++ Templates](https://cplusplus.com/doc/oldtutorial/templates/) +in order to support a range of ESB enabled radios, most recently NRF52x radios. + +**Important Notes:** +- Any network layer that uses v2 needs to have RF24Network/RF24Mesh dependencies of v2 or newer. RF24 v1.x is an exception here. +- General usage should remain backward compatible, see the included examples of the related libraries for more info +- Any third party libs that extend the network/mesh layer may also need to be updated to incorporate the new templated class prototypes: +```cpp +template +class ESBNetwork; + +template +class ESBMesh; +``` +- Third party libs should also be able to use the backward-compatible typedef in their template: + - ESBGateway.h: + ```cpp + template + class ESBGateway + ``` + and inform the compiler what types they intend to support: + - ESBGateway.cpp: + ```cpp + template class ESBGateway; + ``` +- The auto installers do not perform a version check like package managers, so having the correct versions of the software is important. +- We *will* be maintaining the v1.x versions with bugfixes etc for those who cannot or do not wish to migrate to the newer template approach. + https://nRF24.github.io/RF24Mesh diff --git a/RF24Mesh.cpp b/RF24Mesh.cpp index 6c6306ff..6bae0bf5 100644 --- a/RF24Mesh.cpp +++ b/RF24Mesh.cpp @@ -1,7 +1,7 @@ /** * @file RF24Mesh.cpp * - * Class definitions for RF24Mesh + * Class definitions for ESBMesh */ #include "RF24Mesh.h" @@ -10,7 +10,8 @@ #include #endif -RF24Mesh::RF24Mesh(RF24& _radio, RF24Network& _network) : radio(_radio), network(_network) +template +ESBMesh::ESBMesh(radio_t& _radio, network_t& _network) : radio(_radio), network(_network) { setCallback(NULL); meshStarted = false; @@ -21,7 +22,8 @@ RF24Mesh::RF24Mesh(RF24& _radio, RF24Network& _network) : radio(_radio), network /*****************************************************/ -bool RF24Mesh::begin(uint8_t channel, rf24_datarate_e data_rate, uint32_t timeout) +template +bool ESBMesh::begin(uint8_t channel, rf24_datarate_e data_rate, uint32_t timeout) { //delay(1); // Found problems w/SPIDEV & ncurses. Without this, getch() returns a stream of garbage if (meshStarted) { @@ -57,7 +59,8 @@ bool RF24Mesh::begin(uint8_t channel, rf24_datarate_e data_rate, uint32_t timeou /*****************************************************/ -uint8_t RF24Mesh::update() +template +uint8_t ESBMesh::update() { uint8_t type = network.update(); if (mesh_address == MESH_DEFAULT_ADDRESS) return type; @@ -100,7 +103,8 @@ uint8_t RF24Mesh::update() /*****************************************************/ -bool RF24Mesh::write(uint16_t to_node, const void* data, uint8_t msg_type, size_t size) +template +bool ESBMesh::write(uint16_t to_node, const void* data, uint8_t msg_type, size_t size) { if (mesh_address == MESH_DEFAULT_ADDRESS) return 0; @@ -110,7 +114,8 @@ bool RF24Mesh::write(uint16_t to_node, const void* data, uint8_t msg_type, size_ /*****************************************************/ -bool RF24Mesh::write(const void* data, uint8_t msg_type, size_t size, uint8_t nodeID) +template +bool ESBMesh::write(const void* data, uint8_t msg_type, size_t size, uint8_t nodeID) { if (mesh_address == MESH_DEFAULT_ADDRESS) return 0; @@ -132,7 +137,8 @@ bool RF24Mesh::write(const void* data, uint8_t msg_type, size_t size, uint8_t no /*****************************************************/ -void RF24Mesh::setChannel(uint8_t _channel) +template +void ESBMesh::setChannel(uint8_t _channel) { radio.stopListening(); radio.setChannel(_channel); @@ -141,14 +147,16 @@ void RF24Mesh::setChannel(uint8_t _channel) /*****************************************************/ -void RF24Mesh::setChild(bool allow) +template +void ESBMesh::setChild(bool allow) { network.networkFlags = allow ? network.networkFlags & ~FLAG_NO_POLL : network.networkFlags | FLAG_NO_POLL; } /*****************************************************/ -bool RF24Mesh::checkConnection() +template +bool ESBMesh::checkConnection() { // getAddress() doesn't use auto-ack; do a double-check to manually retry 1 more time if (getAddress(_nodeID) < 1) { @@ -161,7 +169,8 @@ bool RF24Mesh::checkConnection() /*****************************************************/ -int16_t RF24Mesh::getAddress(uint8_t nodeID) +template +int16_t ESBMesh::getAddress(uint8_t nodeID) { // Master will return and send 00 address for a nodeID with address 0, -1 if not found //if (nodeID == _nodeID) return mesh_address; @@ -198,7 +207,8 @@ int16_t RF24Mesh::getAddress(uint8_t nodeID) /*****************************************************/ -int16_t RF24Mesh::getNodeID(uint16_t address) +template +int16_t ESBMesh::getNodeID(uint16_t address) { if (address == MESH_BLANK_ID) return _nodeID; if (address == 0) return 0; @@ -231,7 +241,8 @@ int16_t RF24Mesh::getNodeID(uint16_t address) /*****************************************************/ -uint8_t RF24Mesh::getLevel(uint16_t address) +template +uint8_t ESBMesh::getLevel(uint16_t address) { uint8_t count = 0; while (address) { @@ -243,7 +254,8 @@ uint8_t RF24Mesh::getLevel(uint16_t address) /*****************************************************/ -void RF24Mesh::beginDefault() +template +void ESBMesh::beginDefault() { radio.stopListening(); network.begin(MESH_DEFAULT_ADDRESS); @@ -252,7 +264,8 @@ void RF24Mesh::beginDefault() /*****************************************************/ -bool RF24Mesh::releaseAddress() +template +bool ESBMesh::releaseAddress() { if (mesh_address == MESH_DEFAULT_ADDRESS) return 0; @@ -266,7 +279,8 @@ bool RF24Mesh::releaseAddress() /*****************************************************/ -uint16_t RF24Mesh::renewAddress(uint32_t timeout) +template +uint16_t ESBMesh::renewAddress(uint32_t timeout) { if (radio.available()) network.update(); @@ -295,7 +309,8 @@ uint16_t RF24Mesh::renewAddress(uint32_t timeout) /*****************************************************/ -bool RF24Mesh::requestAddress(uint8_t level) +template +bool ESBMesh::requestAddress(uint8_t level) { RF24NetworkHeader header(MESH_MULTICAST_ADDRESS, NETWORK_POLL); //Find another radio, starting with level 0 multicast @@ -397,7 +412,8 @@ bool RF24Mesh::requestAddress(uint8_t level) /*****************************************************/ -void RF24Mesh::setNodeID(uint8_t nodeID) +template +void ESBMesh::setNodeID(uint8_t nodeID) { _nodeID = nodeID; } @@ -405,14 +421,16 @@ void RF24Mesh::setNodeID(uint8_t nodeID) /*****************************************************/ #if !defined(MESH_NOMASTER) -void RF24Mesh::setStaticAddress(uint8_t nodeID, uint16_t address) +template +void ESBMesh::setStaticAddress(uint8_t nodeID, uint16_t address) { setAddress(nodeID, address); } /*****************************************************/ -void RF24Mesh::setAddress(uint8_t nodeID, uint16_t address, bool searchBy) +template +void ESBMesh::setAddress(uint8_t nodeID, uint16_t address, bool searchBy) { //Look for the node in the list for (uint8_t i = 0; i < addrListTop; i++) { @@ -449,7 +467,8 @@ void RF24Mesh::setAddress(uint8_t nodeID, uint16_t address, bool searchBy) /*****************************************************/ -void RF24Mesh::loadDHCP() +template +void ESBMesh::loadDHCP() { #if defined(__linux) && !defined(__ARDUINO_X86__) @@ -472,7 +491,8 @@ void RF24Mesh::loadDHCP() /*****************************************************/ -void RF24Mesh::saveDHCP() +template +void ESBMesh::saveDHCP() { #if defined(__linux) && !defined(__ARDUINO_X86__) std::ofstream outfile("dhcplist.txt", std::ofstream::binary | std::ofstream::trunc); @@ -486,7 +506,8 @@ void RF24Mesh::saveDHCP() /*****************************************************/ -void RF24Mesh::DHCP() +template +void ESBMesh::DHCP() { if (doDHCP) doDHCP = false; @@ -576,10 +597,17 @@ void RF24Mesh::DHCP() #endif // !MESH_NOMASTER -void RF24Mesh::setCallback(void (*meshCallback)(void)) +template +void ESBMesh::setCallback(void (*meshCallback)(void)) { this->meshCallback = meshCallback; } /*****************************************************/ + +// ensure the compiler is aware of the possible datatype for the template class +template class ESBMesh, RF24>; +#if defined(ARDUINO_ARCH_NRF52) || defined(ARDUINO_ARCH_NRF52840) +template class ESBMesh, nrf_to_nrf>; +#endif diff --git a/RF24Mesh.h b/RF24Mesh.h index 27bf4eba..1df13053 100644 --- a/RF24Mesh.h +++ b/RF24Mesh.h @@ -41,15 +41,34 @@ #else #include #include + #if defined(ARDUINO_ARCH_NRF52) || defined(ARDUINO_ARCH_NRF52840) + #include + #endif #endif #include #include class RF24; -class RF24Network; +#if defined(ARDUINO_ARCH_NRF52) || defined(ARDUINO_ARCH_NRF52840) || defined(ARDUINO_ARCH_NRF52833) +class nrf_to_nrf; +#endif + +template +class ESBNetwork; -class RF24Mesh +/** + * @tparam network_t The `network` object's type. Defaults to `RF24Network` for legacy behavior. + * This new abstraction is really meant for using the nRF52840 SoC as a drop-in replacement + * for the nRF24L01 radio. For more detail, see the + * [nrf_to_nrf Arduino library](https://github.com/TMRh20/nrf_to_nrf). + * @tparam radio_t The `radio` object's type. Defaults to `RF24` for legacy behavior. + * This new abstraction is really meant for using the nRF52840 SoC as a drop-in replacement + * for the nRF24L01 radio. For more detail, see the + * [nrf_to_nrf Arduino library](https://github.com/TMRh20/nrf_to_nrf). + */ +template, class radio_t = RF24> +class ESBMesh { /** * @name RF24Mesh @@ -60,17 +79,25 @@ class RF24Mesh public: /** - * Construct the mesh: - * + * Construct the mesh. + * + * v2.0 supports a backward compatible constructor: * @code * RF24 radio(7, 8); * RF24Network network(radio); - * RF24Mesh mesh(radio, network); + * RF24Mesh mesh(radio, network); // for nRF24L01 + * + * nrf_to_nrf radio1; + * RF52Network network1(radio1); + * RF52Mesh mesh1(network1, radio1); // for nRF52xxx family * @endcode + * + * @see v2.0 supports [nrf_to_nrf Arduino library](https://github.com/TMRh20/nrf_to_nrf) + * for nrf52 chips' internal radio. * @param _radio The underlying radio driver instance * @param _network The underlying network instance */ - RF24Mesh(RF24& _radio, RF24Network& _network); + ESBMesh(radio_t& _radio, network_t& _network); /** * Call this in setup() to configure the mesh and request an address.
@@ -110,7 +137,7 @@ class RF24Mesh /** * Set a unique @ref _nodeID "nodeID" for this node. * - * This needs to be called before RF24Mesh::begin(). The parameter value passed can be fetched + * This needs to be called before ESBMesh::begin(). The parameter value passed can be fetched * via serial connection, EEPROM, etc when configuring a large number of nodes. * @note If using RF24Gateway and/or RF24Ethernet, nodeIDs 0 & 1 are used by the master node. * @param nodeID Can be any unique value ranging from 1 to 255 (reserving 0 for the master node). @@ -138,7 +165,7 @@ class RF24Mesh * for any requesting (non-master) node's ID, similar to DHCP. * * @warning On master nodes, It is required to call this function immediately after calling - * RF24Mesh::update() to ensure address requests are handled appropriately. + * ESBMesh::update() to ensure address requests are handled appropriately. */ void DHCP(); @@ -287,7 +314,7 @@ class RF24Mesh #if !defined(MESH_NOMASTER) /** * @brief A struct for storing a @ref _nodeID "nodeID" and an address in a single element of - * the RF24Mesh::addrList array. + * the ESBMesh::addrList array. * * @note This array only exists on the mesh network's master node. */ @@ -318,8 +345,8 @@ class RF24Mesh /**@}*/ private: - RF24& radio; - RF24Network& network; + radio_t& radio; + network_t& network; /** Function pointer for customized callback usage in long running algorithms. */ void (*meshCallback)(void); @@ -342,6 +369,23 @@ class RF24Mesh uint8_t getLevel(uint16_t address); }; +/** + * A type definition of the template class `ESBMesh` to maintain backward compatibility. + * + * ```.cpp + * RF24 radio(7, 8); + * RF24Network network(radio); + * + * RF24Mesh mesh(radio, network); + * // is equivalent to + * ESBMesh, RF24> mesh(radio, network); + * ``` + */ +typedef ESBMesh, RF24> RF24Mesh; +#if defined(ARDUINO_ARCH_NRF52) || defined(ARDUINO_ARCH_NRF52840) || defined(ARDUINO_ARCH_NRF52833) +typedef ESBMesh, nrf_to_nrf> RF52Mesh; +#endif + /** * @example RF24Mesh_Example.ino * Arduino Example Sketch
diff --git a/docs/main_page.md b/docs/main_page.md index c8e9bf10..11648c0f 100644 --- a/docs/main_page.md +++ b/docs/main_page.md @@ -6,15 +6,43 @@ This class intends to provide a simple and seamless 'mesh' layer for sensor netw allowing automatic and dynamic configuration that can be customized to suit many scenarios. It is currently designed to interface directly with with the [RF24Network library](http://nRF24.github.io/RF24Network), an -[OSI Network Layer](http://en.wikipedia.org/wiki/Network_layer) using nRF24L01(+) radios -driven by the newly optimized [RF24 library](http://nRF24.github.io/RF24) fork. +[OSI Network Layer](http://en.wikipedia.org/wiki/Network_layer) using nRF24L01(+) or NRF52x radios +driven by the [RF24 library](http://nRF24.github.io/RF24) or [nrf_to_nrf library](https://github.com/TMRh20/nrf_to_nrf). ## Purpose/Goals - Provide a simple user interface for creating dynamic sensor networks with the RF24 and RF24Network libraries. - Create stable, fully automated/self-managed networks -## News +## News - 2023 API Changes + +Introducing **RF24Network & RF24Mesh v2.0** with some *significant API changes*, adding the use of [C++ Templates](https://cplusplus.com/doc/oldtutorial/templates/) +in order to support a range of ESB enabled radios, most recently NRF52x radios. + +**Important Notes:** +- Any network layer that uses v2 needs to have RF24Network/RF24Mesh dependencies of v2 or newer. RF24 v1.x is an exception here. +- General usage should remain backward compatible, see the included examples of the related libraries for more info +- Any third party libs that extend the network/mesh layer may also need to be updated to incorporate the new templated class prototypes: +```cpp +template +class ESBNetwork; + +template +class ESBMesh; +``` +- Third party libs should also be able to use the backward-compatible typedef in their template: + - ESBGateway.h: + ```cpp + template + class ESBGateway + ``` + and inform the compiler what types they intend to support: + - ESBGateway.cpp: + ```cpp + template class ESBGateway; + ``` +- The auto installers do not perform a version check like package managers, so having the correct versions of the software is important. +- We *will* be maintaining the v1.x versions with bugfixes etc for those who cannot or do not wish to migrate to the newer template approach. See a the list of changes on [the Github releases page](https://github.com/nRF24/RF24Mesh/releases/) diff --git a/docs/sphinx/classRF24Mesh.rst b/docs/sphinx/classRF24Mesh.rst index 91d89df7..3ab81039 100644 --- a/docs/sphinx/classRF24Mesh.rst +++ b/docs/sphinx/classRF24Mesh.rst @@ -1,9 +1,20 @@ -RF24Mesh class +ESBMesh class ~~~~~~~~~~~~~~ -.. cpp:class:: RF24Mesh +.. doxygentypedef:: RF24Mesh - .. doxygenfunction:: RF24Mesh::RF24Mesh +.. cpp:class:: template, radio_t = RF24> ESBMesh + + :tparam network_t: The ``network`` object's type. Defaults to the :class:`RF24Network` specialization + for legacy behavior. This new abstraction is really meant for using the nRF52840 SoC as a + drop-in replacement for the nRF24L01 radio. For more detail, see the + `nrf_to_nrf Arduino library `_. + :tparam radio_t: The ``radio`` object's type. Defaults to :class:`RF24` for legacy behavior. + This new abstraction is really meant for using the nRF52840 SoC as a drop-in replacement + for the nRF24L01 radio. For more detail, see the + `nrf_to_nrf Arduino library `_. + + .. doxygenfunction:: ESBMesh::ESBMesh .. seealso:: - :cpp:class:`RF24` for the ``radio`` object @@ -12,51 +23,51 @@ RF24Mesh class Basic API ============ -.. doxygenfunction:: RF24Mesh::begin +.. doxygenfunction:: ESBMesh::begin .. seealso:: :cpp:enum:`rf24_datarate_e`, :external:cpp:func:`RF24::setChannel()`, :c:macro:`MESH_DEFAULT_CHANNEL`, :c:macro:`MESH_RENEWAL_TIMEOUT` -.. doxygenfunction:: RF24Mesh::update +.. doxygenfunction:: ESBMesh::update .. seealso:: Review :cpp:func:`RF24Network::update()` for more details. -.. doxygenfunction:: RF24Mesh::write (const void *data, uint8_t msg_type, size_t size, uint8_t nodeID=0) +.. doxygenfunction:: ESBMesh::write (const void *data, uint8_t msg_type, size_t size, uint8_t nodeID=0) .. seealso:: Review :cpp:var:`RF24NetworkHeader::type` for more details about available message types. -.. doxygenfunction:: RF24Mesh::renewAddress -.. doxygenfunction:: RF24Mesh::setNodeID -.. doxygenvariable:: RF24Mesh::_nodeID +.. doxygenfunction:: ESBMesh::renewAddress +.. doxygenfunction:: ESBMesh::setNodeID +.. doxygenvariable:: ESBMesh::_nodeID Advanced API ============ -.. doxygenvariable:: RF24Mesh::mesh_address -.. doxygenfunction:: RF24Mesh::getNodeID -.. doxygenfunction:: RF24Mesh::checkConnection -.. doxygenfunction:: RF24Mesh::releaseAddress -.. doxygenfunction:: RF24Mesh::getAddress -.. doxygenfunction:: RF24Mesh::write (uint16_t to_node, const void *data, uint8_t msg_type, size_t size) -.. doxygenfunction:: RF24Mesh::setChannel +.. doxygenvariable:: ESBMesh::mesh_address +.. doxygenfunction:: ESBMesh::getNodeID +.. doxygenfunction:: ESBMesh::checkConnection +.. doxygenfunction:: ESBMesh::releaseAddress +.. doxygenfunction:: ESBMesh::getAddress +.. doxygenfunction:: ESBMesh::write (uint16_t to_node, const void *data, uint8_t msg_type, size_t size) +.. doxygenfunction:: ESBMesh::setChannel .. seealso:: :external:cpp:func:`RF24::setChannel()` -.. doxygenfunction:: RF24Mesh::setChild -.. doxygenfunction:: RF24Mesh::setCallback -.. doxygenfunction:: RF24Mesh::setAddress -.. doxygenfunction:: RF24Mesh::setStaticAddress -.. doxygenfunction:: RF24Mesh::DHCP -.. doxygenfunction:: RF24Mesh::saveDHCP -.. doxygenfunction:: RF24Mesh::loadDHCP +.. doxygenfunction:: ESBMesh::setChild +.. doxygenfunction:: ESBMesh::setCallback +.. doxygenfunction:: ESBMesh::setAddress +.. doxygenfunction:: ESBMesh::setStaticAddress +.. doxygenfunction:: ESBMesh::DHCP +.. doxygenfunction:: ESBMesh::saveDHCP +.. doxygenfunction:: ESBMesh::loadDHCP Address List Struct =================== -.. doxygenvariable:: RF24Mesh::addrList -.. doxygenvariable:: RF24Mesh::addrListTop +.. doxygenvariable:: ESBMesh::addrList +.. doxygenvariable:: ESBMesh::addrListTop -.. doxygenstruct:: RF24Mesh::addrListStruct +.. doxygenstruct:: ESBMesh::addrListStruct :members: diff --git a/docs/sphinx/conf.py b/docs/sphinx/conf.py index e76f566a..02584c99 100644 --- a/docs/sphinx/conf.py +++ b/docs/sphinx/conf.py @@ -127,7 +127,6 @@ # Set the repo location to get a badge with stats "repo_url": "https://github.com/nRF24/RF24Mesh/", "repo_name": "RF24Mesh", - "repo_type": "github", # If False, expand all TOC entries "globaltoc_collapse": False, } diff --git a/library.json b/library.json index 161fcbb0..c9daa460 100644 --- a/library.json +++ b/library.json @@ -7,7 +7,7 @@ "type": "git", "url": "https://github.com/nRF24/RF24Mesh.git" }, - "version": "1.1.9", + "version": "2.0.0", "dependencies": [ { diff --git a/library.properties b/library.properties index 74d55d05..73d8eb6d 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=RF24Mesh -version=1.1.9 +version=2.0.0 author=TMRh20 maintainer=TMRh20,Avamander sentence=OSI Layer 7, Automated 'mesh' style networking for nrf24L01(+) radios.