diff --git a/.github/workflows/build_arduino.yml b/.github/workflows/build_arduino.yml new file mode 100644 index 0000000..97e53ba --- /dev/null +++ b/.github/workflows/build_arduino.yml @@ -0,0 +1,103 @@ +name: Arduino CLI build + +on: + pull_request: + types: [opened, reopened] + paths: + - ".github/workflows/build_arduino.yml" + - "examples/**" + + push: + paths: + - ".github/workflows/build_arduino.yml" + - "examples/**" + +jobs: + check_formatting: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Check code formatting + uses: per1234/artistic-style-action@main + with: + options-file-path: ./examples/examples_formatter.conf + name-patterns: | + - '*.ino' + - '*.cpp' + - '*.hpp' + - '*.h' + target-paths: | + - examples + build: + needs: check_formatting + runs-on: ubuntu-latest + + strategy: + fail-fast: false + + matrix: + fqbn: + - "arduino:avr:yun" + - "arduino:avr:uno" + - "arduino:avr:diecimila" + - "arduino:avr:nano" + - "arduino:avr:mega" + - "arduino:avr:megaADK" + - "arduino:avr:leonardo" + - "arduino:avr:micro" + - "arduino:avr:esplora" + - "arduino:avr:mini" + - "arduino:avr:ethernet" + - "arduino:avr:fio" + - "arduino:avr:bt" + # - "arduino:avr:LilyPad" # board not found + - "arduino:avr:LilyPadUSB" + - "arduino:avr:pro" + - "arduino:avr:atmegang" + - "arduino:avr:robotControl" + # - "arduino:avr:gemma" # does not support SPI + - "arduino:avr:circuitplay32u4cat" + - "arduino:avr:yunmini" + - "arduino:avr:chiwawa" + - "arduino:avr:one" + - "arduino:avr:unowifi" + - "arduino:mbed:nano33ble" + - "arduino:samd:mkr1000" + - "arduino:samd:mkrzero" + - "arduino:samd:mkrwifi1010" + - "arduino:samd:nano_33_iot" + - "arduino:samd:mkrfox1200" + - "arduino:samd:mkrwan1300" + - "arduino:samd:mkrwan1310" + - "arduino:samd:mkrgsm1400" + - "arduino:samd:mkrnb1500" + - "arduino:samd:mkrvidor4000" + - "arduino:samd:adafruit_circuitplayground_m0" + - "arduino:samd:mzero_pro_bl" + - "arduino:samd:mzero_bl" + - "arduino:samd:tian" + - "arduino:megaavr:uno2018" + # - "arduino:megaavr:nano4809" # board not found + + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Compile examples + uses: arduino/compile-sketches@main + with: + sketch-paths: | + - examples/RF24Mesh_Example + - examples/RF24Mesh_Example_Master_Statics + - examples/RF24Mesh_Example_Master_To_Nodes + - examples/RF24Mesh_Example_Node2Node + - examples/RF24Mesh_Example_Node2NodeExtra + - examples/RF24Mesh_SerialConfig + - examples/RF24Mesh_Example_Master + libraries: | + - name: RF24 + - source-url: https://github.com/nRF24/RF24Network.git + - source-path: ./ + # - name: RF24Network + fqbn: ${{ matrix.fqbn }} diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml new file mode 100644 index 0000000..db26e2e --- /dev/null +++ b/.github/workflows/build_linux.yml @@ -0,0 +1,190 @@ +name: Linux build + +on: + pull_request: + types: [opened, reopened] + paths: + - "*.h" + - "*.cpp" + - "CMakeLists.txt" + - "cmake/**" + - "library.properties" # CMake gets lib info from here + - "examples_RPi/**" + - "!examples_RPi/*.py" + - "!**Makefile" # old build system is not tested in this workflow + - "pyRF24Mesh/*" + - ".github/workflows/build_linux.yml" + push: + paths: + - "*.h" + - "*.cpp" + - "CMakeLists.txt" + - "cmake/**" + - "library.properties" # CMake gets lib info from here + - "examples_RPi/**" + - "!examples_RPi/*.py" + - "!**Makefile" # old build system is not tested in this workflow + - "pyRF24Mesh/*" + - ".github/workflows/build_linux.yml" + release: + types: [published, edited] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + using_cmake: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + + matrix: + toolchain: + - compiler: "armhf" + usr_dir: "arm-linux-gnueabihf" + - compiler: "arm64" + usr_dir: "aarch64-linux-gnu" + # - compiler: "x86_64" + # usr_dir: "x86_64-linux-gnux32" + # - compiler: "i686" + # usr_dir: "i686-linux-gnu" + - compiler: "default" # github runner is hosted on a "amd64" + usr_dir: "local" + + steps: + # - name: provide toolchain (for x86_64) + # if: ${{ matrix.toolchain.compiler == 'x86_64' }} + # run: | + # sudo apt-get update + # sudo apt-get install gcc-x86-64-linux-gnux32 g++-x86-64-linux-gnux32 + + # - name: provide toolchain (for i686) + # if: ${{ matrix.toolchain.compiler == 'i686' }} + # run: | + # sudo apt-get update + # sudo apt-get install gcc-i686-linux-gnu g++-i686-linux-gnu + + - name: provide toolchain (for arm64) + if: ${{ matrix.toolchain.compiler == 'arm64' }} + run: | + sudo apt-get update + sudo apt-get install gcc-aarch64-linux-gnu g++-aarch64-linux-gnu + + - name: provide toolchain (for armhf) + if: ${{ matrix.toolchain.compiler == 'armhf' }} + run: | + sudo apt-get update + sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf + + - name: checkout RF24 + uses: actions/checkout@v2 + with: + repository: nRF24/RF24 + + - name: build & install RF24 + run: | + mkdir build + cd build + cmake .. -D CMAKE_BUILD_TYPE=$BUILD_TYPE -D RF24_DRIVER=SPIDEV \ + -D CMAKE_INSTALL_PREFIX=/usr/${{ matrix.toolchain.usr_dir }} \ + -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/${{ matrix.toolchain.compiler }}.cmake + sudo make install + + - name: checkout RF24Network + uses: actions/checkout@v2 + with: + repository: nRF24/RF24Network + + - name: build & install RF24Network + run: | + mkdir build + cd build + cmake .. -D CMAKE_BUILD_TYPE=$BUILD_TYPE \ + -D CMAKE_INSTALL_PREFIX=/usr/${{ matrix.toolchain.usr_dir }} \ + -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/${{ matrix.toolchain.compiler }}.cmake + sudo make install + + - name: checkout RF24Mesh repo + uses: actions/checkout@v2 + + - name: create CMake build environment + run: cmake -E make_directory ${{ github.workspace }}/build + + - name: configure lib + working-directory: ${{ github.workspace }}/build + run: | + cmake .. -D CMAKE_BUILD_TYPE=$BUILD_TYPE \ + -D CMAKE_INSTALL_PREFIX=/usr/${{ matrix.toolchain.usr_dir }} \ + -D CMAKE_TOOLCHAIN_FILE=cmake/toolchains/${{ matrix.toolchain.compiler }}.cmake + + - name: build lib + working-directory: ${{ github.workspace }}/build + run: cmake --build . + + - name: install lib + working-directory: ${{ github.workspace }}/build + run: sudo cmake --install . + + - name: package lib + working-directory: ${{ github.workspace }}/build + run: sudo cpack + + - name: Save artifact + uses: actions/upload-artifact@v2 + with: + name: "pkg_RF24Mesh" + path: | + ${{ github.workspace }}/build/pkgs/*.deb + ${{ github.workspace }}/build/pkgs/*.rpm + + - name: Upload Release assets + if: github.event_name == 'release' && (matrix.toolchain.compiler == 'armhf' || matrix.toolchain.compiler == 'arm64') + uses: csexton/release-asset-action@master + with: + pattern: "${{ github.workspace }}/build/pkgs/librf24*" + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: clean build environment + working-directory: ${{ github.workspace }}/build + run: sudo rm -r ./* + + - name: configure examples + working-directory: ${{ github.workspace }}/build + run: | + cmake ../examples_RPi \ + -D CMAKE_TOOLCHAIN_FILE=../cmake/toolchains/${{ matrix.toolchain.compiler }}.cmake + + - name: build examples + working-directory: ${{ github.workspace }}/build + # doesn't build the RF24Mesh_Ncurses_Master example because we haven't cross-compiled it in this workflow + run: | + cmake --build . + file ./RF24Mesh_Example + + # cross-compiling a python C extension is better done with pypa/cibuildwheel action + - name: Set up Python 3.7 + if: ${{ matrix.toolchain.compiler == 'default' }} + uses: actions/setup-python@v1 + with: + python-version: 3.7 + + - name: provide python wrapper prerequisites + if: ${{ matrix.toolchain.compiler == 'default' }} + # python3-rpi.gpio is only required for physical hardware (namely the IRQ example) + run: sudo apt-get install python3-dev libboost-python-dev python3-setuptools + + - name: create alias symlink to libboost_python3*.so + if: ${{ matrix.toolchain.compiler == 'default' }} + run: 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 + + - name: build python wrapper + if: ${{ matrix.toolchain.compiler == 'default' }} + working-directory: ${{ github.workspace }}/pyRF24Mesh + run: python3 setup.py build + + - name: install python wrapper + if: ${{ matrix.toolchain.compiler == 'default' }} + working-directory: ${{ github.workspace }}/pyRF24Mesh + run: sudo python3 setup.py install diff --git a/.github/workflows/build_platformIO.yml b/.github/workflows/build_platformIO.yml new file mode 100644 index 0000000..07a7c01 --- /dev/null +++ b/.github/workflows/build_platformIO.yml @@ -0,0 +1,142 @@ +name: PlatformIO build + +on: + pull_request: + types: [opened, reopened] + paths: + - ".github/workflows/build_platformIO.yml" + - "library.json" + - "examples/**" + - "!examples/old_backups/**" + - "!examples/rf24_ATTiny/**" + push: + paths: + - ".github/workflows/build_platformIO.yml" + - "library.json" + - "examples/**" + - "!examples/old_backups/**" + - "!examples/rf24_ATTiny/**" + release: + types: [published, edited] + +jobs: + validate_lib_json: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v2 + + - name: get latest release version number + id: latest_ver + run: echo "::set-output name=release::$(awk -F "=" '/version/ {print $2}' library.properties)" + + - name: Set up Python + uses: actions/setup-python@v2 + + - name: Install PlatformIO + run: | + python -m pip install --upgrade pip + pip install --upgrade platformio + + - name: package lib + run: pio package pack -o PlatformIO-RF24Mesh-${{ steps.latest_ver.outputs.release }}.tar.gz + + - name: Save artifact + uses: actions/upload-artifact@v2 + with: + name: "PIO_pkg_RF24Mesh" + path: PlatformIO*.tar.gz + + - name: Upload Release assets + if: github.event_name == 'release' + uses: csexton/release-asset-action@master + with: + pattern: "PlatformIO*.tar.gz" + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: upload package to PlatformIO Registry + if: github.event_name == 'release' && github.event_type != 'edited' + # PIO lib packages cannot be re-published under the same tag + env: + PLATFORMIO_AUTH_TOKEN: ${{ secrets.PLATFORMIO_AUTH_TOKEN }} + run: pio package publish --owner nrf24 --non-interactive + + check_formatting: + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Check code formatting + uses: per1234/artistic-style-action@main + with: + options-file-path: ./examples/examples_formatter.conf + name-patterns: | + - '*.ino' + - '*.cpp' + - '*.hpp' + - '*.h' + target-paths: | + - examples + + build: + needs: [check_formatting, validate_lib_json] + runs-on: ubuntu-latest + + strategy: + fail-fast: false + + matrix: + example: + - "examples/RF24Mesh_Example/RF24Mesh_Example.ino" + - "examples/RF24Mesh_Example_Master_Statics/RF24Mesh_Example_Master_Statics.ino" + - "examples/RF24Mesh_Example_Master_To_Nodes/RF24Mesh_Example_Master_To_Nodes.ino" + - "examples/RF24Mesh_Example_Node2Node/RF24Mesh_Example_Node2Node.ino" + - "examples/RF24Mesh_Example_Node2NodeExtra/RF24Mesh_Example_Node2NodeExtra.ino" + - "examples/RF24Mesh_SerialConfig/RF24Mesh_SerialConfig.ino" + - "examples/RF24Mesh_Example_Master/RF24Mesh_Example_Master.ino" + board: + - "teensy31" + - "teensy35" + - "teensy36" + - "teensy40" + - "teensy41" + - "teensylc" + + steps: + - uses: actions/checkout@v2 + + - name: Cache pip + uses: actions/cache@v2 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Cache PlatformIO + uses: actions/cache@v2 + with: + path: ~/.platformio + key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }} + + - name: Set up Python + uses: actions/setup-python@v2 + + - name: Install PlatformIO + run: | + python -m pip install --upgrade pip + pip install --upgrade platformio + + # "dependencies" field in JSON should automatically install RF24 & RF24Network. + # Because we run this CI test against the local repo, the JSON seems neglected + - name: Install library dependencies + run: | + pio lib -g install nrf24/RF24 + pio lib -g install https://github.com/nrf24/RF24Network.git + # pio lib -g install nrf24/RF24Network + + - name: Run PlatformIO + run: pio ci --lib="." --board=${{ matrix.board }} + env: + PLATFORMIO_CI_SRC: ${{ matrix.example }} diff --git a/.github/workflows/build_rp2xxx.yml b/.github/workflows/build_rp2xxx.yml new file mode 100644 index 0000000..b8f7b72 --- /dev/null +++ b/.github/workflows/build_rp2xxx.yml @@ -0,0 +1,110 @@ +name: Pico SDK build + +on: + push: + paths: + - ".github/workflows/build_rp2xxx.yml" + - "*.h" + - "*.cpp" + - "CMakeLists.txt" + - "cmake/" + - "examples_pico/*" + pull_request: + types: [opened, reopened] + paths: + - ".github/workflows/build_rp2xxx.yml" + - "*.h" + - "*.cpp" + - "CMakeLists.txt" + - "cmake/**" + - "examples_pico/*" + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + + matrix: + board: + - "pico" + - "adafruit_feather_rp2040" + - "adafruit_itsybitsy_rp2040" + - "adafruit_qtpy_rp2040" + - "pimoroni_tiny2040" # examples require PicoSDK v1.2.0 + - "sparkfun_micromod" # examples require PicoSDK v1.2.0 + - "sparkfun_promicro" # examples require PicoSDK v1.2.0 + - "sparkfun_thingplus" # examples require PicoSDK v1.2.0 + # - "vgaboard" # examples require PicoSDK v1.2.0 (this can be enabled on request) + - "arduino_nano_rp2040_connect" # requires PicoSDK v1.2.0 + - "pimoroni_picolipo_4mb" # requires PicoSDK v1.2.0 + - "pimoroni_picolipo_16mb" # requires PicoSDK v1.2.0 + - "pimoroni_pga2040" # requires PicoSDK v1.2.0 + # - "pimoroni_keybow2040" # no SPI bus exposed + # - "pimoroni_picosystem" # SPI is reserved for LCD + + steps: + - name: checkout RF24Mesh lib + uses: actions/checkout@v2 + with: + path: RF24Mesh + + - name: checkout RF24Network lib + uses: actions/checkout@v2 + with: + repository: nRF24/RF24Network + path: RF24Network + + - name: checkout RF24 lib + uses: actions/checkout@v2 + with: + repository: nRF24/RF24 + path: RF24 + + - name: Install toolchain + run: sudo apt update && sudo apt install gcc-arm-none-eabi libnewlib-arm-none-eabi build-essential + + - name: Clone pico-sdk + uses: actions/checkout@v2 + with: + repository: raspberrypi/pico-sdk + # master branch is latest stable release + path: pico-sdk + clean: false + submodules: true + + - name: Checkout pico-sdk submodules + working-directory: ${{ github.workspace }}/pico-sdk + run: git submodule update --init + + - name: Create Build Environment + working-directory: ${{ github.workspace }}/RF24Mesh + env: + PICO_SDK_PATH: ${{ github.workspace }}/pico-sdk + run: cmake -E make_directory ${{ github.workspace }}/RF24Mesh/build + + - name: Configure CMake + working-directory: ${{ github.workspace }}/RF24Mesh/build + env: + PICO_SDK_PATH: ${{ github.workspace }}/pico-sdk + run: cmake ../examples_pico -DCMAKE_BUILD_TYPE=$BUILD_TYPE -DPICO_BOARD=${{ matrix.board }} + + - name: Build + working-directory: ${{ github.workspace }}/RF24Mesh/build + # Execute the build. You can specify a specific target with "--target " + run: cmake --build . --config $BUILD_TYPE + + - name: Save artifact + uses: actions/upload-artifact@v2 + with: + name: examples_pico_${{ matrix.board }} + path: | + ${{ github.workspace }}/RF24Mesh/build/*.uf2 + ${{ github.workspace }}/RF24Mesh/build/*.elf + # ${{ github.workspace }}/RF24Mesh/build/*.hex + # ${{ github.workspace }}/RF24Mesh/build/*.bin diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index da74cd3..743e980 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -1,44 +1,75 @@ -name: DoxyGen build +name: build Docs on: pull_request: - branches: - - master + types: [opened, reopened] + paths: + - "*.h" + - "docs/**" + - "!docs/README.md" + - "*.md" + - "examples**.cpp" + - "examples**.ino" + - "images/**" + - ".github/workflows/doxygen.yml" + - "Doxyfile" + - "library.properties" # get lib version from here push: - branches: - - master + paths: + - "*.h" + - "docs/**" + - "!docs/README.md" + - "*.md" + - "examples**.cpp" + - "examples**.ino" + - "images/**" + - ".github/workflows/doxygen.yml" + - "Doxyfile" + - "library.properties" # get lib version from here release: - branches: - - master - types: - - published - - edited + branches: [master] + types: [published, edited] jobs: build-doxygen: runs-on: ubuntu-latest steps: - - name: get latest release version number - id: latest_ver - uses: pozetroninc/github-action-get-latest-release@master - with: - repository: nRF24/RF24Mesh - - name: checkout - uses: actions/checkout@v2 - - name: overwrite doxygen tags - run: | - touch doxygenAction - echo "PROJECT_NUMBER = ${{ steps.latest_ver.outputs.release }}" >> doxygenAction - echo "@INCLUDE = doxygenAction" >> Doxyfile - - name: build doxygen - uses: mattnotmitt/doxygen-action@v1 - with: - working-directory: '.' - doxyfile-path: './Doxyfile' - - name: upload to github pages - if: ${{ github.event_name == 'release'}} - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: ./docs/html \ No newline at end of file + - uses: actions/checkout@v2 + - name: get latest release version number + id: latest_ver + run: echo "::set-output name=release::$(awk -F "=" '/version/ {print $2}' library.properties)" + - name: overwrite doxygen tags + run: | + touch doxygenAction + echo "PROJECT_NUMBER = ${{ steps.latest_ver.outputs.release }}" >> doxygenAction + echo "@INCLUDE = doxygenAction" >> Doxyfile + - name: build doxygen + uses: mattnotmitt/doxygen-action@v1 + with: + working-directory: '.' + doxyfile-path: './Doxyfile' + - name: Save doxygen docs as artifact + uses: actions/upload-artifact@v2 + with: + name: "RF24Mesh_doxygen_docs" + path: ${{ github.workspace }}/docs/html + - name: upload to github pages + if: ${{ github.event_name == 'release'}} + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs/html + + # build pretty docs using doxygen XML output with Sphinx + - uses: actions/setup-python@v2 + - name: Install sphinx deps + run: python -m pip install -r docs/sphinx/requirements.txt + - name: build docs with Sphinx + working-directory: docs + run: sphinx-build sphinx _build + - name: Save sphinx docs as artifact + uses: actions/upload-artifact@v2 + with: + name: "RF24Mesh_sphinx_docs" + path: ${{ github.workspace }}/docs/_build diff --git a/.gitignore b/.gitignore index 7e95313..4cff0cb 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Executables from examples */bin/* +*.exe # Generated library files *.so @@ -7,4 +8,26 @@ # ignore docs folder docs/html/ -docs/xml/ \ No newline at end of file +docs/sphinx/xml/ +docs/_build + +# ignore CMake stuff +build/ +*CMakeUserPresets.json + +# ignore local vscode folder +.vscode/ + +# ignore PlatformIO packages +*.tar.gz + +# Cmake build-in-source generated stuff +CMakeCache.txt +CPackConfig.cmake +CPackSourceConfig.cmake +CMakeFiles +DEBIAN +cmake_install.cmake +compile_commands.json +# Makefile is modified when `cmake .` is run +# Makefile # preserve old/traditional build system diff --git a/.readthedocs.yaml b/.readthedocs.yaml new file mode 100644 index 0000000..c843775 --- /dev/null +++ b/.readthedocs.yaml @@ -0,0 +1,24 @@ +# .readthedocs.yaml +# Read the Docs configuration file +# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details + +# Required +version: 2 + +build: + os: "ubuntu-20.04" + tools: + python: "3" + +# Build documentation in the docs/ directory with Sphinx +sphinx: + configuration: docs/sphinx/conf.py + +# Optionally build your docs in additional formats such as PDF +formats: + - pdf + +# install Python requirements required to build docs +python: + install: + - requirements: docs/sphinx/requirements.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 50109cb..a241604 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,8 +2,162 @@ # (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}/usePicoSDK.cmake) + include(${CMAKE_CURRENT_LIST_DIR}/cmake/usePicoSDK.cmake) return() endif() -## TODO: Non-PicoSDK builds +############################ +# for non-RPi-Pico platforms +############################ +cmake_minimum_required(VERSION 3.15) + +# Set the project name to your project name +project(RF24Mesh 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) # sets the variable LibTargetName + +# allow using CMake options to adjust RF24Network_config.h without modiying source code +option(MESH_NOMASTER "exclude compiling code that is strictly for master nodes" OFF) +option(MESH_DEBUG "enable/disable debugging output" OFF) +option(MESH_DEBUG_MINIMAL "enable/disable minimal debugging output" 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) + +# detect CPU and add compiler flags accordingly +include(cmake/detectCPU.cmake) + +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) + +# setup CPack options +include(${CMAKE_CURRENT_LIST_DIR}/cmake/CPackInfo.cmake) +if(NOT DEFINED USE_RF24_LIB_SRC) + find_library(RF24 rf24 REQUIRED) + message(STATUS "using RF24 library: ${RF24}") + + find_library(RF24Network rf24network REQUIRED) + message(STATUS "using RF24Network library: ${RF24Network}") +endif() + +########################### +# create target for bulding the RF24Log lib +########################### +add_library(${LibTargetName} SHARED RF24Mesh.cpp) +target_include_directories(${LibTargetName} PUBLIC ${CMAKE_CURRENT_LIST_DIR}) + +# python wrapper builds from source +if(DEFINED USE_RF24_LIB_SRC OR pybind11_FOUND OR SKBUILD) + message(STATUS "Building lib from RF24 source") + target_compile_definitions(${LibTargetName} PUBLIC USE_RF24_LIB_SRC) + + if(NOT DEFINED RF24_LIB_PATH) + target_include_directories(${LibTargetName} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../RF24) + else() + target_include_directories(${LibTargetName} PUBLIC ${RF24_LIB_PATH}) + endif() + + if(NOT DEFINED RF24NETWORK_LIB_PATH) + target_include_directories(${LibTargetName} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../RF24) + else() + target_include_directories(${LibTargetName} PUBLIC ${RF24NETWORK_LIB_PATH}) + endif() + + target_link_libraries(${LibTargetName} + INTERFACE ${LibTargetName}_project_options + INTERFACE ${LibTargetName}_project_warnings + ) +else() + + target_link_libraries(${LibTargetName} + INTERFACE ${LibTargetName}_project_options + INTERFACE ${LibTargetName}_project_warnings + SHARED ${RF24} + SHARED ${RF24Network} + ) +endif() + +set_target_properties( + ${LibTargetName} + PROPERTIES + SOVERSION ${${LibName}_VERSION_MAJOR} + VERSION ${${LibName}_VERSION_STRING} +) +# assert the appropriate preprocessor macros for RF24Network_config.h +if(MESH_NOMASTER) + message(STATUS "MESH_NOMASTER asserted") + target_compile_definitions(${LibTargetName} PUBLIC MESH_NOMASTER) +endif() +if(MESH_DEBUG) + message(STATUS "MESH_DEBUG asserted") + target_compile_definitions(${LibTargetName} PUBLIC MESH_DEBUG) +endif() +if(MESH_DEBUG_MINIMAL) + message(STATUS "MESH_DEBUG_MINIMAL asserted") + target_compile_definitions(${LibTargetName} PUBLIC MESH_DEBUG_MINIMAL) +endif() +# for the following, we let the default be configured in source code +if(DEFINED MESH_MAX_CHILDREN) + message(STATUS "MESH_MAX_CHILDREN set to ${MESH_MAX_CHILDREN}") + target_compile_definitions(${LibTargetName} PUBLIC MESH_MAX_CHILDREN=${MESH_MAX_CHILDREN}) +endif() +if(DEFINED MESH_DEFAULT_CHANNEL) + message(STATUS "MESH_DEFAULT_CHANNEL set to ${MESH_DEFAULT_CHANNEL}") + target_compile_definitions(${LibTargetName} PUBLIC MESH_DEFAULT_CHANNEL=${MESH_DEFAULT_CHANNEL}) +endif() +if(DEFINED MESH_RENEWAL_TIMEOUT) + message(STATUS "MESH_RENEWAL_TIMEOUT set to ${MESH_RENEWAL_TIMEOUT}") + target_compile_definitions(${LibTargetName} PUBLIC MESH_RENEWAL_TIMEOUT=${MESH_RENEWAL_TIMEOUT}) +endif() +if(DEFINED MESH_MEM_ALLOC_SIZE) + message(STATUS "MESH_MEM_ALLOC_SIZE set to ${MESH_MEM_ALLOC_SIZE}") + target_compile_definitions(${LibTargetName} PUBLIC MESH_MEM_ALLOC_SIZE=${MESH_MEM_ALLOC_SIZE}) +endif() +if(DEFINED MESH_LOOKUP_TIMEOUT) + message(STATUS "MESH_LOOKUP_TIMEOUT set to ${MESH_LOOKUP_TIMEOUT}") + target_compile_definitions(${LibTargetName} PUBLIC MESH_LOOKUP_TIMEOUT=${MESH_LOOKUP_TIMEOUT}) +endif() +if(DEFINED MESH_WRITE_TIMEOUT) + message(STATUS "MESH_WRITE_TIMEOUT set to ${MESH_WRITE_TIMEOUT}") + target_compile_definitions(${LibTargetName} PUBLIC MESH_WRITE_TIMEOUT=${MESH_WRITE_TIMEOUT}) +endif() +# MESH_DEFAULT_ADDRESS is an alias of the RF24Network's NETWORK_DEFAULT_ADDRESS definition + + +########################### +# target install rules for the RF24Log lib +########################### +install(TARGETS ${LibTargetName} + DESTINATION lib +) + +install(FILES + RF24Mesh.h + RF24Mesh_config.h + DESTINATION include/RF24Mesh +) + +# CMAKE_CROSSCOMPILING is only TRUE when CMAKE_TOOLCHAIN_FILE is specified via CLI +if(CMAKE_HOST_UNIX AND "${CMAKE_CROSSCOMPILING}" STREQUAL "FALSE") + install(CODE "message(STATUS \"Updating ldconfig\")") + install(CODE "execute_process(COMMAND ldconfig)") +endif() diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8e3c5cc..9d99c0b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,8 +2,13 @@ These are the current requirements for getting your code included in RF24Mesh: * Try your best to follow the rest of the code, if you're unsure then the NASA C style can help as it's closest to the current style: https://ntrs.nasa.gov/archive/nasa/casi.ntrs.nasa.gov/19950022400.pdf -* Definetly follow PEP-8 if it's Python code. +* Definetly follow [PEP-8](https://www.python.org/dev/peps/pep-0008/) if it's Python code. -* Follow the Arduino IDE formatting style for Arduino examples +* Follow the [Arduino IDE formatting style](https://www.arduino.cc/en/Reference/StyleGuide) for Arduino examples -* Add doxygen-compatible documentation to any new functions you add, or update existing documentation if you change behaviour +* 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/Doxyfile b/Doxyfile index c352cd0..c32f7c6 100644 --- a/Doxyfile +++ b/Doxyfile @@ -1,4 +1,4 @@ -# Doxyfile 1.8.18 +# Doxyfile 1.9.1 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. @@ -227,6 +227,14 @@ QT_AUTOBRIEF = NO MULTILINE_CPP_IS_BRIEF = NO +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + # 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. @@ -261,7 +269,12 @@ TAB_SIZE = 4 # commands \{ and \} for these it is advised to use the version @{ and @} or use # a double escape (\\{ and \\}) -ALIASES = +ALIASES = "rst=\xmlonlyembed:rst:leading-asterisk^^" +ALIASES += "endrst=\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 @@ -315,7 +328,10 @@ OPTIMIZE_OUTPUT_SLICE = NO # 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. +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. EXTENSION_MAPPING = ino=c \ pde=c @@ -450,6 +466,19 @@ TYPEDEF_HIDES_STRUCT = NO LOOKUP_CACHE_SIZE = 0 +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which efficively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- @@ -513,6 +542,13 @@ EXTRACT_LOCAL_METHODS = NO EXTRACT_ANON_NSPACES = NO +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + # 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 @@ -550,11 +586,18 @@ HIDE_IN_BODY_DOCS = 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. +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. # The default value is: system dependent. CASE_SENSE_NAMES = YES @@ -793,10 +836,13 @@ WARN_IF_DOC_ERROR = YES WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when -# a warning is encountered. +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. # The default value is: NO. -WARN_AS_ERROR = 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 @@ -830,8 +876,8 @@ INPUT = ./ \ # 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. +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 @@ -844,13 +890,15 @@ INPUT_ENCODING = UTF-8 # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# # 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. +# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, +# *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.cc \ @@ -1108,16 +1156,22 @@ USE_HTAGS = NO 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. +# 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 and the CLANG_ADD_INC_PATHS tag is set to +# YES then doxygen will add the directory of each input to the include path. +# The default value is: YES. + +CLANG_ADD_INC_PATHS = YES + # 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 @@ -1127,10 +1181,13 @@ CLANG_ASSISTED_PARSING = NO 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. +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source 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. Any options specified with CLANG_OPTIONS +# will be added as well. # Note: The availability of this option depends on whether or not doxygen was # generated with the -Duse_libclang=ON option for CMake. @@ -1147,13 +1204,6 @@ CLANG_DATABASE_PATH = ALPHABETICAL_INDEX = YES -# The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in -# which the alphabetical index list will be split. -# Minimum value: 1, maximum value: 20, default value: 5. -# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. - -COLS_IN_ALPHA_INDEX = 10 - # 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 @@ -1324,10 +1374,11 @@ 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 +# 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. @@ -1369,8 +1420,8 @@ 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. +# (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 @@ -1400,7 +1451,7 @@ CHM_FILE = 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). +# (YES) or that it should be included in the main .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. @@ -1445,7 +1496,8 @@ 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). +# (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. @@ -1453,8 +1505,8 @@ 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). +# 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. @@ -1462,16 +1514,16 @@ 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). +# 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). +# 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 = @@ -1483,9 +1535,9 @@ QHP_CUST_FILTER_ATTRS = 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. +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) 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 = @@ -1566,8 +1618,8 @@ EXT_LINKS_IN_WINDOW = NO # 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. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). # The default value is: png. # This tag requires that the tag GENERATE_HTML is set to YES. @@ -1612,7 +1664,7 @@ 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. +# http://docs.mathjax.org/en/v2.7-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. @@ -1642,7 +1694,8 @@ 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 +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. @@ -1689,7 +1742,8 @@ SERVER_BASED_SEARCH = NO # # 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/). +# Xapian (see: +# https://xapian.org/). # # See the section "External Indexing and Searching" for details. # The default value is: NO. @@ -1702,8 +1756,9 @@ EXTERNAL_SEARCH = NO # # 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. +# 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 = @@ -1867,9 +1922,11 @@ LATEX_EXTRA_FILES = 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. +# If the USE_PDFLATEX tag is set to YES, doxygen will use the engine as +# specified with LATEX_CMD_NAME to generate the PDF file directly from the LaTeX +# files. Set this option to YES, to get a higher quality PDF documentation. +# +# See also section LATEX_CMD_NAME for selecting the engine. # The default value is: YES. # This tag requires that the tag GENERATE_LATEX is set to YES. @@ -2043,7 +2100,7 @@ MAN_LINKS = NO # captures the structure of the code including all documentation. # The default value is: NO. -GENERATE_XML = 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 @@ -2051,7 +2108,7 @@ GENERATE_XML = NO # The default directory is: xml. # This tag requires that the tag GENERATE_XML is set to YES. -XML_OUTPUT = xml +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 @@ -2261,14 +2318,14 @@ ALLEXTERNALS = NO # listed. # The default value is: YES. -EXTERNAL_GROUPS = YES +EXTERNAL_GROUPS = NO # 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 +EXTERNAL_PAGES = NO #--------------------------------------------------------------------------- # Configuration options related to the dot tool @@ -2380,10 +2437,32 @@ UML_LOOK = NO # 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. +# This tag requires that the tag UML_LOOK is set to YES. UML_LIMIT_NUM_FIELDS = 10 +# If the DOT_UML_DETAILS tag is set to NO, doxygen will show attributes and +# methods without types and arguments in the UML graphs. If the DOT_UML_DETAILS +# tag is set to YES, doxygen will add type and arguments for attributes and +# methods in the UML graphs. If the DOT_UML_DETAILS tag is set to NONE, doxygen +# will not generate fields with class member information in the UML graphs. The +# class diagrams will look similar to the default class diagrams but using UML +# notation for the relationships. +# Possible values are: NO, YES and NONE. +# The default value is: NO. +# This tag requires that the tag UML_LOOK is set to YES. + +DOT_UML_DETAILS = NO + +# The DOT_WRAP_THRESHOLD tag can be used to set the maximum number of characters +# to display on a single line. If the actual line length exceeds this threshold +# significantly it will wrapped across multiple lines. Some heuristics are apply +# to avoid ugly line breaks. +# Minimum value: 0, maximum value: 1000, default value: 17. +# This tag requires that the tag HAVE_DOT is set to YES. + +DOT_WRAP_THRESHOLD = 17 + # If the TEMPLATE_RELATIONS tag is set to YES then the inheritance and # collaboration graphs will show the relations between templates and their # instances. @@ -2573,9 +2652,11 @@ DOT_MULTI_TARGETS = NO GENERATE_LEGEND = YES -# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate dot +# If the DOT_CLEANUP tag is set to YES, doxygen will remove the intermediate # files that are used to generate the various graphs. +# +# Note: This setting is not only used for dot files but also for msc and +# plantuml temporary files. # The default value is: YES. -# This tag requires that the tag HAVE_DOT is set to YES. DOT_CLEANUP = YES diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..23cb790 --- /dev/null +++ b/LICENSE @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 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/README.md b/README.md index d5004fe..aa235d3 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,12 @@ -RF24Mesh -======== + +[![Linux build](https://github.com/nRF24/RF24Mesh/actions/workflows/build_linux.yml/badge.svg)](https://github.com/nRF24/RF24Mesh/actions/workflows/build_linux.yml) +[![Arduino CLI build](https://github.com/nRF24/RF24Mesh/actions/workflows/build_arduino.yml/badge.svg)](https://github.com/nRF24/RF24Mesh/actions/workflows/build_arduino.yml) +[![PlatformIO build](https://github.com/nRF24/RF24Mesh/actions/workflows/build_platformIO.yml/badge.svg)](https://github.com/nRF24/RF24Mesh/actions/workflows/build_platformIO.yml) +[![Pico SDK build](https://github.com/nRF24/RF24Mesh/actions/workflows/build_rp2xxx.yml/badge.svg)](https://github.com/nRF24/RF24Mesh/actions/workflows/build_rp2xxx.yml) +[![Documentation Status](https://readthedocs.org/projects/rf24mesh/badge/?version=latest)](https://rf24mesh.readthedocs.io/en/latest/?badge=latest) + +# RF24Mesh + Mesh Networking for RF24Network -https://nRF24.github.io/RF24Mesh \ No newline at end of file +https://nRF24.github.io/RF24Mesh diff --git a/RF24Mesh.cpp b/RF24Mesh.cpp index 9e62f62..5400d11 100644 --- a/RF24Mesh.cpp +++ b/RF24Mesh.cpp @@ -67,20 +67,20 @@ uint8_t RF24Mesh::update() header->to_node = header->from_node; int16_t returnAddr = 0; - if (type==MESH_ADDR_LOOKUP) { + if (type == MESH_ADDR_LOOKUP) { returnAddr = getAddress(network.frame_buffer[sizeof(RF24NetworkHeader)]); network.write(*header, &returnAddr, sizeof(returnAddr)); } else { int16_t addr = 0; - memcpy(&addr,&network.frame_buffer[sizeof(RF24NetworkHeader)], sizeof(addr)); + memcpy(&addr, &network.frame_buffer[sizeof(RF24NetworkHeader)], sizeof(addr)); returnAddr = getNodeID(addr); network.write(*header, &returnAddr, sizeof(returnAddr)); } } else if (type == MESH_ADDR_RELEASE) { uint16_t *fromAddr = (uint16_t*)network.frame_buffer; - for(uint8_t i = 0; i lookupTimeout || toNode == -2) { return 0; } - retryDelay+=10; + retryDelay += 10; delay(retryDelay); } } @@ -144,6 +144,7 @@ void RF24Mesh::setChild(bool allow) bool RF24Mesh::checkConnection() { + // getAddress() doesn't use auto-ack; do a double-check to manually retry 1 more time if (getAddress(_nodeID) < 1) { if (getAddress(_nodeID) < 1) { return false; @@ -199,7 +200,7 @@ int16_t RF24Mesh::getNodeID(uint16_t address) #if !defined(MESH_NOMASTER) if (!mesh_address) { //Master Node - for (uint8_t i = 0; i 0 && contactNode[pollCount] != contactNode[pollCount - 1]) { //Drop duplicate polls to help prevent dupliacate requests + if (pollCount > 0 && contactNode[pollCount] != contactNode[pollCount - 1]) { //Drop duplicate polls to help prevent duplicate requests ++pollCount; } else{ ++pollCount; } - #if defined (MESH_DEBUG_SERIAL) || defined (MESH_DEBUG_PRINTF) - if (goodSignal) { - // This response was better than -64dBm - #if defined (MESH_DEBUG_SERIAL) - Serial.print(millis()); Serial.println(F(" MSH: Poll > -64dbm ")); - #elif defined (MESH_DEBUG_PRINTF) - printf("%u MSH: Poll > -64dbm\n", millis()); - #endif - } - else{ - #if defined (MESH_DEBUG_SERIAL) - Serial.print( millis() ); Serial.println(F(" MSH: Poll < -64dbm ")); - #elif defined (MESH_DEBUG_PRINTF) - printf( "%u MSH: Poll < -64dbm\n", millis() ); - #endif - } - #endif // defined (MESH_DEBUG_SERIAL) || defined (MESH_DEBUG_PRINTF) + IF_MESH_DEBUG(printf_P(PSTR("%u: MSH Poll %c -64dbm\n"), millis(), (goodSignal ? '>' : '<'))); } // end if if (millis() - timr > 55 || pollCount >= MESH_MAXPOLLS ) { if (!pollCount) { - #if defined (MESH_DEBUG_SERIAL) - Serial.print( millis() ); Serial.print(F(" MSH: No poll from level ")); Serial.println(level); - #elif defined (MESH_DEBUG_PRINTF) - printf( "%u MSH: No poll from level %d\n", millis(), level); - #endif + IF_MESH_DEBUG(printf_P(PSTR("%u: MSH No poll from level %d\n"), millis(), level)); return 0; } else { - #if defined (MESH_DEBUG_SERIAL) - Serial.print(millis()); Serial.println(F(" MSH: Poll OK ")); - #elif defined (MESH_DEBUG_PRINTF) - printf("%u MSH: Poll OK\n", millis()); - #endif + IF_MESH_DEBUG(printf_P(PSTR("%u: MSH Poll OK\n"), millis())); break; } } @@ -358,13 +333,7 @@ bool RF24Mesh::requestAddress(uint8_t level) } // end while - #ifdef MESH_DEBUG_SERIAL - Serial.print(millis()); Serial.print(F(" MSH: Got poll from level ")); Serial.print(level); - Serial.print(F(" count ")); Serial.print(pollCount); - Serial.print(F(" node ")); Serial.println(contactNode[pollCount-1], OCT); // #ML# - #elif defined MESH_DEBUG_PRINTF - printf("%u MSH: Got poll from level %d count %d\n", millis(), level, pollCount); - #endif + IF_MESH_DEBUG(printf_P(PSTR("%u: MSH Got poll from level %d count %d\n"), millis(), level, pollCount)); bool gotResponse = 0; for (uint8_t i = 0; i < pollCount; i++) { @@ -375,23 +344,20 @@ bool RF24Mesh::requestAddress(uint8_t level) // Do a direct write (no ack) to the contact node. Include the nodeId and address. network.write(header, 0, 0, contactNode[i]); - #ifdef MESH_DEBUG_SERIAL - Serial.print( millis() ); Serial.print(F(" MSH: Req addr from ")); Serial.println(contactNode[i],OCT); - #elif defined MESH_DEBUG_PRINTF - printf("%u MSH: Request address from: 0%o\n",millis(),contactNode[i]); - #endif + + IF_MESH_DEBUG(printf_P(PSTR("%u: MSH Request address from: 0%o\n"), millis(), contactNode[i])); timr = millis(); while (millis() - timr < 225) { if (network.update() == NETWORK_ADDR_RESPONSE) { if (network.frame_buffer[7] == _nodeID ) { - uint16_t newAddy = 0; - memcpy(&newAddy, &network.frame_buffer[sizeof(RF24NetworkHeader)], sizeof(newAddy)); - uint16_t mask = 0xFFFF; - newAddy &= ~(mask << (3 * getLevel(contactNode[i]))); // Get the level of contact node. Multiply by 3 to get the number of bits to shift (3 per digit) - if (newAddy == contactNode[i]) { // Then shift the mask by this much, and invert it bitwise. Apply the mask to the newly received - i=pollCount; // address to evalute whether 'subnet' of the assigned address matches the contact node address. + uint16_t newAddy = 0; + memcpy(&newAddy, &network.frame_buffer[sizeof(RF24NetworkHeader)], sizeof(newAddy)); + uint16_t mask = 0xFFFF; + newAddy &= ~(mask << (3 * getLevel(contactNode[i]))); // Get the level of contact node. Multiply by 3 to get the number of bits to shift (3 per digit) + if (newAddy == contactNode[i]) { // Then shift the mask by this much, and invert it bitwise. Apply the mask to the newly received + i = pollCount; // address to evalute whether 'subnet' of the assigned address matches the contact node address. gotResponse = 1; break; } @@ -405,27 +371,16 @@ bool RF24Mesh::requestAddress(uint8_t level) return 0; } - #ifdef MESH_DEBUG_SERIAL - uint8_t mask = 7, count=3; - char addrs[5] = " "; - uint16_t newAddr; - #endif - uint16_t newAddress=0; memcpy(&newAddress, network.frame_buffer + sizeof(RF24NetworkHeader), sizeof(newAddress)); - #ifdef MESH_DEBUG_SERIAL - Serial.print(millis()); - Serial.print(F(" Set address: ")); - Serial.println(newAddress, OCT); - #elif defined (MESH_DEBUG_PRINTF) - printf("Set address 0%o rcvd 0%o\n", mesh_address, newAddress); - #endif + IF_MESH_DEBUG(printf_P(PSTR("Set address 0%o rcvd 0%o\n"), mesh_address, newAddress)); mesh_address = newAddress; radio.stopListening(); network.begin(mesh_address); + // getNodeID() doesn't use auto-ack; do a double-check to manually retry 1 more time if (getNodeID(mesh_address) != _nodeID) { if (getNodeID(mesh_address) != _nodeID) { beginDefault(); @@ -435,19 +390,6 @@ bool RF24Mesh::requestAddress(uint8_t level) return 1; } -/*****************************************************/ -/* -bool RF24Mesh::waitForAvailable(uint32_t timeout) { - - unsigned long timer = millis(); - while(millis()-timer < timeout) { - network.update(); - if (network.available()) { return 1; } - } - if (network.available()) { return 1; } - else{ return 0; } -} -*/ /*****************************************************/ void RF24Mesh::setNodeID(uint8_t nodeID) @@ -468,7 +410,7 @@ void RF24Mesh::setStaticAddress(uint8_t nodeID, uint16_t address) void RF24Mesh::setAddress(uint8_t nodeID, uint16_t address, bool searchBy) { //Look for the node in the list - for(uint8_t i=0; i>= 3; //Find out how many digits are in the octal address - count+=3; + count += 3; } shiftVal = count; //Now we know how many bits to shift when adding a child node 1-5 (B001 to B101) to any address } @@ -577,9 +517,7 @@ void RF24Mesh::DHCP() extraChild = 1; } - #ifdef MESH_DEBUG_PRINTF - // printf("%u MSH: Rcv addr req from_id %d \n",millis(),header.reserved); - #endif + // IF_MESH_DEBUG(printf_P(PSTR("%u: MSH Rcv addr req from_id %d\n"), millis(), header.reserved)); for (int i = MESH_MAX_CHILDREN + extraChild; i > 0; i--) { // For each of the possible addresses (5 max) @@ -589,23 +527,8 @@ void RF24Mesh::DHCP() if (newAddress == MESH_DEFAULT_ADDRESS) { continue; } for (uint8_t i = 0; i < addrListTop; i++) { - #if defined (MESH_DEBUG_MINIMAL) - #if !defined (__linux) && !defined ARDUINO_SAM_DUE || defined TEENSY || defined(__ARDUINO_X86__) - Serial.print("ID: ");Serial.print(addrList[i].nodeID,DEC);Serial.print(" ADDR: "); - uint16_t newAddr = addrList[i].address; - char addr[5] = " "; - uint8_t count=3,mask=7; - while(newAddr) { - addr[count] = (newAddr & mask)+48; //get the individual Octal numbers, specified in chunks of 3 bits, convert to ASCII by adding 48 - newAddr >>= 3; - count--; - } - Serial.println(addr); - #else - printf("ID: %d ADDR: 0%o\n", addrList[i].nodeID,addrList[i].address); - #endif - #endif - if ((addrList[i].address == newAddress) && (addrList[i].nodeID != header.reserved)) { + IF_MESH_DEBUG_MINIMAL(printf_P(PSTR("ID: %d ADDR: 0%o\n"), addrList[i].nodeID, addrList[i].address)); + if (addrList[i].address == newAddress && addrList[i].nodeID != header.reserved) { found = true; break; } @@ -617,7 +540,10 @@ void RF24Mesh::DHCP() //This is a routed request to 00 setAddress(header.reserved, newAddress); - //delay(10); // ML: without this delay, address renewal fails + // without this delay, address renewal fails for children with slower execution speed + #if defined (SLOW_ADDR_POLL_RESPONSE) + delay(SLOW_ADDR_POLL_RESPONSE); + #endif // defined (SLOW_ADDR_POLL_RESPONSE) if (header.from_node != MESH_DEFAULT_ADDRESS) { //Is NOT node 01 to 05 //delay(2); if (!network.write(header, &newAddress, sizeof(newAddress))) { @@ -626,18 +552,14 @@ void RF24Mesh::DHCP() } else { //delay(2); - network.write(header,&newAddress,sizeof(newAddress),header.to_node); + network.write(header, &newAddress, sizeof(newAddress), header.to_node); } - #ifdef MESH_DEBUG_PRINTF - printf("Sent to 0%o phys: 0%o new: 0%o id: %d\n", header.to_node,MESH_DEFAULT_ADDRESS,newAddress,header.reserved); - #endif + IF_MESH_DEBUG(printf_P(PSTR("Sent to 0%o phys: 0%o new: 0%o id: %d\n"), header.to_node, MESH_DEFAULT_ADDRESS, newAddress, header.reserved)); break; } else { - #if defined (MESH_DEBUG_PRINTF) - printf("not allocated\n"); - #endif + IF_MESH_DEBUG(printf_P(PSTR("not allocated\n"))); } } // end for } diff --git a/RF24Mesh.h b/RF24Mesh.h index 2ba167b..b53ae3f 100644 --- a/RF24Mesh.h +++ b/RF24Mesh.h @@ -34,7 +34,7 @@ #include "RF24Mesh_config.h" -#if defined(__linux) && !defined(__ARDUINO_X86__) +#if defined(__linux) && !defined(__ARDUINO_X86__) && !defined(USE_RF24_LIB_SRC) #include #include #define RF24_LINUX @@ -54,7 +54,7 @@ class RF24Mesh /** * @name RF24Mesh * - * The mesh library and class documentation is currently in active development and usage may change. + * The mesh library and class documentation is currently in active development and usage may change. */ /**@{*/ public: @@ -62,9 +62,9 @@ class RF24Mesh * Construct the mesh: * * @code - * RF24 radio(7,8); + * RF24 radio(7, 8); * RF24Network network(radio); - * RF24Mesh mesh(radio,network); + * RF24Mesh mesh(radio, network); * @endcode * @param _radio The underlying radio driver instance * @param _network The underlying network instance @@ -78,9 +78,9 @@ class RF24Mesh * This may take a few moments to complete. * * The following parameters are optional: - * @param channel The radio channel (1-127) default:97 - * @param data_rate The data rate (RF24_250KBPS,RF24_1MBPS,RF24_2MBPS) default:RF24_1MBPS - * @param timeout How long to attempt address renewal in milliseconds default:7500 + * @param channel The radio channel (0 - 125). Default is 97. + * @param data_rate The data rate (RF24_250KBPS, RF24_1MBPS, RF24_2MBPS). Default is RF24_1MBPS. + * @param timeout How long to attempt address renewal in milliseconds. Default is 7500. */ bool begin(uint8_t channel = MESH_DEFAULT_CHANNEL, rf24_datarate_e data_rate = RF24_1MBPS, uint32_t timeout = MESH_RENEWAL_TIMEOUT); @@ -91,47 +91,52 @@ class RF24Mesh uint8_t update(); /** - * Automatically construct a header and send a payload + * Automatically construct a header and send a payload. * Very similar to the standard network.write() function, which can be used directly. * - * @note Including the nodeID parameter will result in an automatic address lookup being performed. - * @note Message types 1-64 (decimal) will NOT be acknowledged by the network, types 65-127 will be. Use as appropriate to manage traffic: + * @note Including the @ref _nodeID "nodeID" parameter will result in an automatic address lookup being performed. + * @note Message types 1 - 64 (decimal) will NOT be acknowledged by the network, types 65 - 127 will be. Use as appropriate to manage traffic: * if expecting a response, no ack is needed. * - * @param data Send any type of data of any length (Max length determined by RF24Network layer) - * @param msg_type The user-defined (1-127) message header_type to send. Used to distinguish between different types of data being transmitted. + * @param data Send any type of data of any length (maximum length determined by RF24Network layer). + * @param msg_type The user-defined (1 - 127) message header_type to send. Used to distinguish between different types of data being transmitted. * @param size The size of the data being sent - * @param nodeID **Optional**: The nodeID of the recipient if not sending to master - * @return True if success, False if failed + * @param nodeID **Optional**: The @ref _nodeID "nodeID" of the recipient if not sending to master. + * @return True if success; false if failed */ bool write(const void *data, uint8_t msg_type, size_t size, uint8_t nodeID = 0); /** - * Set a unique nodeID for this node. + * Set a unique @ref _nodeID "nodeID" for this node. * - * This needs to be called before mesh.begin(), can be via serial connection, eeprom etc if configuring a large number of nodes... + * This needs to be called before RF24Mesh::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 253 + * @param nodeID Can be any unique value ranging from 1 to 255 (reserving 0 for the master node). */ void setNodeID(uint8_t nodeID); /** - * Reconnect to the mesh and renew the current RF24Network address. Used to re-establish a connection to the mesh if physical location etc. has changed, or - * a routing node goes down. - * @note Currently times out after 7.5 seconds if address renewal fails. + * @brief Reconnect to the mesh and renew the current RF24Network address. * - * @note If all nodes are set to verify connectivity/reconnect at a specified period, restarting the master (and deleting dhcplist.txt on Linux) will result - * in complete network/mesh reconvergence. - * @param timeout How long to attempt address renewal in milliseconds default:7500 + * This is used to re-establish a connection to the mesh network if physical location of a node + * or surrounding nodes has changed (or a routing node becomes unavailable). * - * @return Returns the newly assigned RF24Network address + * @note If all nodes are set to verify connectivity and reconnect at a specified period, then + * restarting the master (and deleting dhcplist.txt on Linux) will result in complete + * network/mesh reconvergence. + * @param timeout How long to attempt address renewal in milliseconds. Default is 7500 + * @return The newly assigned RF24Network address */ uint16_t renewAddress(uint32_t timeout = MESH_RENEWAL_TIMEOUT); #if !defined(MESH_NOMASTER) /** - * Only to be used on the master node. Provides automatic configuration for sensor nodes, similar to DHCP. - * Call immediately after calling network.update() to ensure address requests are handled appropriately + * This is only to be used on the master node because it manages allocation of network addresses + * 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. */ void DHCP(); @@ -147,49 +152,49 @@ class RF24Mesh /** * Convert an RF24Network address into a nodeId. - * @param address If no address is provided, returns the local nodeID, otherwise a lookup request is sent to the master node - * @return Returns the unique identifier (1-255) or -1 if not found. + * @param address If no address is provided, returns the local @ref _nodeID "nodeID", + * otherwise a lookup request is sent to the master node + * @return The unique identifier of the node in the range [1, 255] or -1 if node was not found. */ int16_t getNodeID(uint16_t address = MESH_BLANK_ID); /** * Tests connectivity of this node to the mesh. * @note If this function fails, address renewal should typically be done. - * @return Return 1 if connected, 0 if mesh not responding + * @return 1 if connected, 0 if mesh not responding */ bool checkConnection(); /** * Releases the currently assigned address lease. Useful for nodes that will be sleeping etc. * @note Nodes should ensure that addresses are released successfully prior to going offline. - * @return Returns 1 if successfully released, 0 if not + * @return True if successfully released, otherwise false. */ bool releaseAddress(); /** * The assigned RF24Network (Octal) address of this node - * @return Returns an unsigned 16-bit integer containing the RF24Network address in octal format + * @return An unsigned 16-bit integer containing the RF24Network address in octal format. */ uint16_t mesh_address; /** - * Convert a nodeID into an RF24Network address - * @note If printing or displaying the address, it needs to be converted to octal format: Serial.println(address,OCT); + * @brief Convert a @ref _nodeID "nodeID" into an RF24Network address + * @note If printing or displaying the address, it needs to be converted to octal format: + * @code Serial.println(address, OCT); @endcode * * Results in a lookup request being sent to the master node. - * @param nodeID - The unique identifier (1-253) of the node - * @return Returns the RF24Network address of the node, -2 if successful but not in list, -1 if failed + * @param nodeID The unique identifier of the node in the range [1, 255]. + * @return The RF24Network address of the node, -2 if successful but not in list, -1 if failed. */ int16_t getAddress(uint8_t nodeID); - /** - * Write to a specific node by RF24Network address. - * - */ + /** @brief Write to a specific node by RF24Network address. */ bool write(uint16_t to_node, const void *data, uint8_t msg_type, size_t size); /** * Change the active radio channel after the mesh has been started. + * @param _channel The value passed to `RF24::setChannel()` */ void setChannel(uint8_t _channel); @@ -210,7 +215,7 @@ class RF24Mesh * mesh.setCallback(myCallbackFunction); * @endcode * - * @param meshCallback The name of a function to call + * @param meshCallback The name of a function to call. This function should consume no required input parameters. */ void setCallback(void (*meshCallback)(void)); @@ -218,30 +223,38 @@ class RF24Mesh #if !defined(MESH_NOMASTER) /** - * Set/change a nodeID/RF24Network Address pair manually on the master node. + * Set or change a @ref _nodeID "nodeID" : node address (key : value) pair manually. + * This function is for use on the master node only. * * @code - * Set a static address for node 02, with nodeID 23, since it will just be a static routing node for example - * running on an ATTiny chip. - * - * mesh.setAddress(23,02); + * // Set a static address for node 02, with nodeID 23, since it will just be + * // a static routing node for example running on an ATTiny chip. + * mesh.setAddress(23, 02); * @endcode - * * @code - * Change/set the nodeID for an existing address - * + * // Change or set the nodeID for an existing address * uint16_t address = 012; - * mesh.setAddress(3,address,true); + * mesh.setAddress(3, address, true); * @endcode * - * @param nodeID The nodeID to assign + * @param nodeID The @ref _nodeID "nodeID" to assign * @param address The octal RF24Network address to assign - * @param searchBy Optional parameter. Default is search by nodeID and set the address. True allows searching by address and setting nodeID. - * @return If the nodeID exists in the list, + * @param searchBy Optional parameter. Default is search by @ref _nodeID "nodeID" and + * set the address. True allows searching by address and setting @ref _nodeID "nodeID". + * @return If the @ref _nodeID "nodeID" exists in the list, */ void setAddress(uint8_t nodeID, uint16_t address, bool searchBy = false); + /** + * Save the @ref addrList to a binary file named "dhcplist.txt". + * @note This function is for use on the master node only and only on Linux or x86 platforms. + */ void saveDHCP(); + + /** + * Load the @ref addrList from a binary file named "dhcplist.txt". + * @note This function is for use on the master node only and only on Linux or x86 platforms. + */ void loadDHCP(); /** @@ -251,52 +264,79 @@ class RF24Mesh */ /**@{*/ - /** - * Calls setAddress() - */ + /** @deprecated For backward compatibility with older code. Use the synonomous setAddress() instead. */ void setStaticAddress(uint8_t nodeID, uint16_t address); #endif // !defined(MESH_NOMASTER) - /**@}*/ + /** - * @name Address list struct + * The unique identifying number used to differentiate mesh nodes' from their assigned network + * address. Ideally, this is set before calling begin() or renewAddress(). It is up to the + * network administrator to make sure that this number is unique to each mesh/network node. * - * See the list struct class reference + * This nodeID number is typically in the range [0, 255], but remember that `0` is reserved for + * the master node. Other external systems may reserve other node ID numbers, for instance + * RF24Gateway/RF24Ethernet reserves the node ID number `1` in addition to the master node ID + * `0`. */ - /**@{*/ - - /**@}*/ - uint8_t _nodeID; #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. + * + * @note This array only exists on the mesh network's master node. + */ typedef struct { - uint8_t nodeID; /** NodeIDs and addresses are stored in the addrList array using this structure */ - uint16_t address; /** NodeIDs and addresses are stored in the addrList array using this structure */ + /** @brief The @ref _nodeID "nodeID" of an network node (child) */ + uint8_t nodeID; + /** @brief The logical address of an network node (child) */ + uint16_t address; } addrListStruct; + /** + * @name Address list struct + * @brief helping members for managing the list of assigned addresses + * @see the addrListStruct struct reference + */ + /**@{*/ + // Pointer used for dynamic memory allocation of address list - addrListStruct *addrList; /** See the addrListStruct class reference */ - uint8_t addrListTop; /** The number of entries in the assigned address list */ + /** + * @brief A array of addrListStruct elements for assigned addresses. + * @see addrListStruct class reference + */ + addrListStruct *addrList; + /** @brief The number of entries in the addrListStruct of assigned addresses. */ + uint8_t addrListTop; #endif + /**@}*/ private: RF24 &radio; RF24Network &network; + /** Function pionter for customized callback usage in long running algorithms. */ void (*meshCallback)(void); - bool requestAddress(uint8_t level); /** Actual requesting of the address once a contact node is discovered or supplied **/ - bool waitForAvailable(uint32_t timeout); /** Waits for data to become available */ + + /** Actual requesting of the address once a contact node is discovered or supplied **/ + bool requestAddress(uint8_t level); #if !defined(MESH_NOMASTER) - bool doDHCP; /** Indicator that an address request is available */ - bool addrMemAllocated; /** Just ensures we don't re-allocate the memory buffer if restarting the mesh on master **/ + /** Indicator that an address request is available. */ + bool doDHCP; + /** Just ensures we don't re-allocate the memory buffer if restarting the mesh on master. **/ + bool addrMemAllocated; #endif - void beginDefault(); /** Starts up the network layer with default address **/ + /** Starts up the network layer with default address. **/ + void beginDefault(); + /** A flag asserted in begin() afer putting the radio in TX mode. */ bool meshStarted; - uint8_t getLevel(uint16_t address); /** Returns the number of digits in the specified address **/ + /** Returns the number of octal digits in the specified address. **/ + uint8_t getLevel(uint16_t address); }; #endif // define __RF24MESH_H__ diff --git a/RF24Mesh_config.h b/RF24Mesh_config.h index 7e4879c..193141b 100644 --- a/RF24Mesh_config.h +++ b/RF24Mesh_config.h @@ -16,13 +16,7 @@ #ifndef MESH_MAX_CHILDREN #define MESH_MAX_CHILDREN 4 #endif // MESH_MAX_CHILDREN -#if defined (RF24_TINY) || defined (DOXYGEN_FORCED) - /** - * @brief Save on resources for non-master nodes - * - * This can be optionally set to 0 for all nodes except the master (nodeID 0) to save program space. - * Enabled automatically on ATTiny devices - */ +#if defined (RF24_TINY) #define MESH_NOMASTER #endif @@ -80,7 +74,7 @@ #define MESH_LOOKUP_TIMEOUT 135 #endif // MESH_LOOKUP_TIMEOUT -/** @brief How long RF24Mesh::write retries address lookups before timing out. Allows multiple attempts */ +/** @brief How long RF24Mesh::write() retries address lookups before timing out. Allows multiple attempts */ #ifndef MESH_WRITE_TIMEOUT #define MESH_WRITE_TIMEOUT 115 #endif // MESH_WRITE_TIMEOUT @@ -89,15 +83,32 @@ #define MESH_DEFAULT_ADDRESS NETWORK_DEFAULT_ADDRESS #endif // MESH_DEFAULT_ADDRESS -//#define MESH_MAX_ADDRESSES 255 /** UNUSED Determines the max size of the array used for storing addresses on the Master Node */ -//#define MESH_ADDRESS_HOLD_TIME 30000 /**UNUSED How long before a released address becomes available */ +#define MESH_MULTICAST_ADDRESS NETWORK_MULTICAST_ADDRESS + +//#define MESH_MAX_ADDRESSES 255 /* UNUSED Determines the max size of the array used for storing addresses on the Master Node */ +//#define MESH_ADDRESS_HOLD_TIME 30000 /* UNUSED How long before a released address becomes available */ + +#if (defined (__linux) || defined (linux)) && !defined (__ARDUINO_X86__) && !defined (USE_RF24_LIB_SRC) + #include + +//ATXMega +#elif defined(XMEGA) + #include "../../rf24lib/rf24lib/RF24_config.h" +#else + #include +#endif + +#if defined (MESH_DEBUG_MINIMAL) + #define IF_MESH_DEBUG_MINIMAL(x) ({x;}) +#else + #define IF_MESH_DEBUG_MINIMAL(x) +#endif + #if defined (MESH_DEBUG) - #if !defined (__linux) && !defined ARDUINO_SAM_DUE || defined TEENSY || defined(__ARDUINO_X86__) - #define MESH_DEBUG_SERIAL - #else - #define MESH_DEBUG_PRINTF - #endif // platform ostream def -#endif // defined (MESH_DEBUG) + #define IF_MESH_DEBUG(x) ({x;}) +#else + #define IF_MESH_DEBUG(x) +#endif #endif // __RF24MESH_CONFIG_H__ diff --git a/cmake/CPackInfo.cmake b/cmake/CPackInfo.cmake new file mode 100644 index 0000000..f5c9e3a --- /dev/null +++ b/cmake/CPackInfo.cmake @@ -0,0 +1,73 @@ +# 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_.cmake files + if(WIN32) + set(TARGET_ARCH $ENV{PROCESSOR_ARCHITECTURE}) + else() + execute_process(COMMAND dpkg --print-architecture + OUTPUT_VARIABLE TARGET_ARCH + ) + endif() + string(STRIP "${TARGET_ARCH}" TARGET_ARCH) +endif() + +# set the Cpack generators (specific to types of packages to create) +if(NOT WIN32) + set(CPACK_GENERATOR DEB RPM) # RPM requires rpmbuild executable +else() + set(CPACK_GENERATOR "") # should find out how to build vcpkg packages +endif() + +# assemble a debian package filename from known info +include(InstallRequiredSystemLibraries) +set(CPACK_PACKAGE_FILE_NAME "lib${LibTargetName}_${${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 + +if(NOT WIN32) + ############################### + # 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) + set(CPACK_DEBIAN_PACKAGE_GENERATE_SHLIBS ON) + + ############################### + # info specific rpm (fedora) packages + ############################### + set(CPACK_RPM_FILE_NAME "lib${LibTargetName}-${${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") + + # 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} /sbin/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}") +endif() + +include(CPack) diff --git a/cmake/Cache.cmake b/cmake/Cache.cmake new file mode 100644 index 0000000..d02f4b5 --- /dev/null +++ b/cmake/Cache.cmake @@ -0,0 +1,31 @@ +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/cmake/CompilerWarnings.cmake b/cmake/CompilerWarnings.cmake new file mode 100644 index 0000000..9031160 --- /dev/null +++ b/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/cmake/GetLibInfo.cmake b/cmake/GetLibInfo.cmake new file mode 100644 index 0000000..3871395 --- /dev/null +++ b/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/cmake/PreventInSourceBuilds.cmake b/cmake/PreventInSourceBuilds.cmake new file mode 100644 index 0000000..dc4fd81 --- /dev/null +++ b/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/cmake/StandardProjectSettings.cmake b/cmake/StandardProjectSettings.cmake new file mode 100644 index 0000000..75a379f --- /dev/null +++ b/cmake/StandardProjectSettings.cmake @@ -0,0 +1,35 @@ +# Set a default build type if none was specified +if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) + message(STATUS "Setting build type to 'Release' as none was specified.") + set(CMAKE_BUILD_TYPE + Release + CACHE STRING "Choose the type of build." FORCE + ) + # 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/cmake/detectCPU.cmake b/cmake/detectCPU.cmake new file mode 100644 index 0000000..bd702f9 --- /dev/null +++ b/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 + 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 is ${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/cmake/enableNcursesExample.cmake b/cmake/enableNcursesExample.cmake new file mode 100644 index 0000000..b016787 --- /dev/null +++ b/cmake/enableNcursesExample.cmake @@ -0,0 +1,15 @@ +find_package(Curses) +if(Curses_FOUND) + include_directories(${CURSES_INCLUDE_DIR}) + option(BUILD_NCURSES_EXAMPLE + "Enable/Disable building the ncurses example (requires libncurses5-dev installed)" + ON + ) +else() + message(STATUS "libncurses5-dev not found. Skipping ncurses example") + option(BUILD_NCURSES_EXAMPLE + "Enable/Disable building the ncurses example (requires libncurses5-dev installed)" + OFF + ) +endif() +message(STATUS "BUILD_NCURSES_EXAMPLE set to ${BUILD_NCURSES_EXAMPLE}") diff --git a/cmake/toolchains/arm64.cmake b/cmake/toolchains/arm64.cmake new file mode 100644 index 0000000..23e41ca --- /dev/null +++ b/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/.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/cmake/toolchains/armhf.cmake b/cmake/toolchains/armhf.cmake new file mode 100644 index 0000000..488b81a --- /dev/null +++ b/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/.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/cmake/toolchains/default.cmake b/cmake/toolchains/default.cmake new file mode 100644 index 0000000..e67d723 --- /dev/null +++ b/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/cmake/toolchains/i686.cmake b/cmake/toolchains/i686.cmake new file mode 100644 index 0000000..f3c42be --- /dev/null +++ b/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/.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/cmake/toolchains/x86_64.cmake b/cmake/toolchains/x86_64.cmake new file mode 100644 index 0000000..e4ff737 --- /dev/null +++ b/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/.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/usePicoSDK.cmake b/cmake/usePicoSDK.cmake similarity index 80% rename from usePicoSDK.cmake rename to cmake/usePicoSDK.cmake index 9eeb9b1..bab2b49 100644 --- a/usePicoSDK.cmake +++ b/cmake/usePicoSDK.cmake @@ -10,9 +10,9 @@ set(CMAKE_CXX_STANDARD 17) add_library(RF24Mesh INTERFACE) target_sources(RF24Mesh INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/RF24Mesh.cpp + ${CMAKE_CURRENT_LIST_DIR}/../RF24Mesh.cpp ) target_include_directories(RF24Mesh INTERFACE - ${CMAKE_CURRENT_LIST_DIR}/ + ${CMAKE_CURRENT_LIST_DIR}/../ ) diff --git a/docs/general_usage.md b/docs/general_usage.md index aec34d0..403d7f3 100644 --- a/docs/general_usage.md +++ b/docs/general_usage.md @@ -1,59 +1,80 @@ # General Usage - + ## Network Design Options -1. **Static Network** (No Mesh)
+1. **Static Network** (No Mesh) + RF24Network can be configured manually, with a static design. RF24Mesh is not used at all. See [Network addressing](http://nRF24.github.io/RF24Network/md_docs_addressing.html) -2. **Static Network w/Dynamic Assignment**
+2. **Static Network w/Dynamic Assignment** + RF24Mesh is only used to acquire an address on startup. Nodes are generally expected to remain stationary. Changes to the network would be addressed manually, by adding, removing, or resetting nodes. Users can choose to use RF24Network functions directly, or use RF24Mesh. -3. **Dynamic Network & Assignment**
+3. **Dynamic Network & Assignment** + Nodes join the mesh automatically and re-attach as required. This is the default and how the examples work. -4. **Hybrid Network**
+4. **Hybrid Network** + Utilizes a combination of static & dynamic nodes. Requires initial planning and deployment, but can result in a more stable network, easing the use of sleeping nodes. ## Network Management -RF24Network addresses can be viewed as MAC addresses, and RF24Mesh nodeIDs viewed as static IP addresses. When joining or re-attaching to the network, nodes -will request a RF24Network address, and are identified via nodeID. + +RF24Network addresses can be viewed as MAC addresses, and RF24Mesh nodeIDs +viewed as static IP addresses. When joining or re-attaching to the network, +nodes will request a RF24Network address, and are identified via nodeID. ### Raspberry Pi/Linux -On Linux devices, the RF24Gateway will save address assignments to file (dhcplist.txt) so they will be restored, even if the -gateway is restarted. To force network re-convergence, delete the dhcplist.txt file and restart the gateway. If nodes are configured to verify their connection at a set -interval, they will come back online in time. + +On Linux devices, the RF24Gateway will save address assignments to file +(dhcplist.txt) so they will be restored, even if the gateway is restarted. +To force network re-convergence, delete the dhcplist.txt file and restart the +gateway. If nodes are configured to verify their connection at a set interval, +they will come back online in time. ### Arduino/AVR -On all other devices, the address list is not saved. To force network re-convergence, restart the gateway. If nodes are configured to verify their -connection at a set interval, they will come back online in time. -If a node/nodeID is removed from the network permanently, the address should be released prior to removal. If it is not, the assigned RF24Network address can be +On all other devices, the address list is not saved. To force network re-convergence, +restart the gateway. If nodes are configured to verify their connection at a set +interval, they will come back online in time. + +If a node/nodeID is removed from the network permanently, the address should be +released prior to removal. If it is not, the assigned RF24Network address can be written to 0 in the RF24Mesh address list. ## Mesh Communication -RF24Mesh nodeIDs are unique identifiers, while RF24Network addresses change dynamically within a statically defined structure. Due to this structure, it is simple -for any node to communicate with the master node, since the RF24Network address is always known (00). Conversely, the master node maintains a list of every node on the -network, so address 'lookups' return immediately. - -Communication from node-to-node requires address queries to be sent to the master node, since individual nodes may change RF24Network & radio address at any time. -Due to the extra data transmissions, node-to-node communication is less efficient. -## General Usage -One thing to keep in mind is the dynamic nature of RF24Mesh, and the need to verify connectivity to the network. For nodes that are constantly transmitting, -(every few seconds at most) it is suitable to check the connection, and/or renew the address when connectivity fails. Since data is not saved by the master -node, if the master node goes down, all child nodes must renew their address. In this case, as long as the master node is down for a few seconds, the nodes -will all begin requesting an address. +RF24Mesh nodeIDs are unique identifiers, while RF24Network addresses change +dynamically within a statically defined structure. Due to this structure, it is +simple for any node to communicate with the master node, since the RF24Network +address is always known (00). Conversely, the master node maintains a list of +every node on the network, so address 'lookups' return immediately. +Communication from node-to-node requires address queries to be sent to the master +node, since individual nodes may change RF24Network & radio address at any time. +Due to the extra data transmissions, node-to-node communication is less efficient. -Nodes that are not actively transmitting, should be configured to test their connection at predefined intervals, to allow them to reconnect as necessary. +## Tricks of Trade +One thing to keep in mind is the dynamic nature of RF24Mesh, and the need to +verify connectivity to the network. For nodes that are constantly transmitting, +(every few seconds at most) it is suitable to check the connection, and/or renew +the address when connectivity fails. Since data is not saved by the master +node, if the master node goes down, all child nodes must renew their address. +In this case, as long as the master node is down for a few seconds, the nodes +will all begin requesting an address. -In the case of sleeping nodes, or nodes that will only be online temporarily, it is generally suitable to release the address prior to going offline, and -requesting an address upon waking. Keep in mind, address requests can generally take anywhere from 10-15ms, up to few seconds in most cases. +Nodes that are not actively transmitting, should be configured to test their +connection at predefined intervals, to allow them to reconnect as necessary. +In the case of sleeping nodes, or nodes that will only be online temporarily, +it is generally suitable to release the address prior to going offline, and +requesting an address upon waking. Keep in mind, address requests can generally +take anywhere from 10 - 15ms, up to few seconds in most cases. One of the recently introduced features is the ability to transmit payloads without the network returning a network-ack response. If solely using this method -of transmission, the node should also be configured to verify its connection via `mesh.checkConnection();` periodically, to ensure connectivity. +of transmission, the node should also be configured to verify its connection via `RF24Mesh::checkConnection()` periodically, to ensure connectivity. ## RF24Network -Beyond requesting and releasing addresses, usage is outlined in the class documentation, and further information regarding RF24Network is available at -http://nRF24.github.io/RF24Network + +Beyond requesting and releasing addresses, usage is outlined in the RF24Mesh class documentation, and further information regarding RF24Network is available at +[RF24Network documentation](http://nRF24.github.io/RF24Network). diff --git a/docs/main_page.md b/docs/main_page.md index 2fb2593..4e1115d 100644 --- a/docs/main_page.md +++ b/docs/main_page.md @@ -1,36 +1,54 @@ # Mesh Networking Layer for RF24 Radios -This class intends to provide a simple and seamless 'mesh' layer for sensor networks, 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://tmrh20.github.com/RF24Network/), an [OSI Network Layer](http://en.wikipedia.org/wiki/Network_layer) using nRF24L01(+) radios driven -by the newly optimized [RF24 library](http://tmrh20.github.com/RF24/) fork. + +This class intends to provide a simple and seamless 'mesh' layer for sensor networks, +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. ## 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 + See a the list of changes on [the Github releases page](https://github.com/nRF24/RF24Mesh/releases/) ## RF24Mesh Overview -The RF24Network library provides a system of addressing and routing for RF24 radio modules that allows large wireless sensor networks to be constructed.
RF24Mesh -provides extended features, including automatic addressing and dynamic configuration of wireless sensors. + +The RF24Network library provides a system of addressing and routing for RF24 radio modules +that allows large wireless sensor networks to be constructed. + +RF24Mesh provides extended features, including automatic addressing and dynamic configuration +of wireless sensors. ### How does it work? -Nodes are assigned a unique number ranging from 1 to 253, and just about everything else, addressing, routing, etc. is managed by the library. -The unique identifier is like an IP address, used to communicate at a high level within the RF24 communication stack and will generally remain static. At the network layer, -the physical radio addresses, similar to MAC addresses, are allocated as nodes move around and establish connections within the network. +Nodes are assigned a unique number ranging from 1 to 255, and just about everything else, addressing, routing, etc. is managed by the library. + +The unique identifier is like an IP address, used to communicate at a high level within the +RF24 communication stack and will generally remain static. At the network layer, the physical +radio addresses, similar to MAC addresses, are allocated as nodes move around and establish +connections within the network. -The 'master' node keeps track of the unique nodeIDs and the assigned RF24Network addresses. When a node is moved physically, or just loses its connection to the network, +The 'master' node keeps track of the unique nodeIDs and the assigned RF24Network addresses. +When a node is moved physically, or just loses its connection to the network, it can automatically re-join the mesh and reconfigure itself within the network. -In the mesh configuration sensors/nodes can move around physically, far from the 'master node' using other nodes to route traffic over extended distances. Addressing and -topology is reconfigured as connections are broken and re-established within different areas of the network. +In the mesh configuration sensors/nodes can move around physically, far from the 'master +node' using other nodes to route traffic over extended distances. Addressing and +topology is reconfigured as connections are broken and re-established within different areas +of the network. -RF24Mesh takes advantage of functionality and features within the RF24 and RF24Network libraries, so everything from addressing, routing, fragmentation/re-assembly -(very large payloads) are handled automatically with processes designed to support a multi-node radio network. +RF24Mesh takes advantage of functionality and features within the RF24 and RF24Network +libraries, so everything from addressing, routing, fragmentation/re-assembly +(very large payloads) are handled automatically with processes designed to support a +multi-node radio network. ## How to learn more + - Try it out! - [Setup and Configuration](md_docs_setup_config.html) - [Usage & Overview](md_docs_general_usage.html) @@ -39,4 +57,4 @@ RF24Mesh takes advantage of functionality and features within the RF24 and RF24N - [RF24Ethernet: TCP/IP based Mesh over RF24](http://nRF24.github.io/RF24Ethernet/) - [RF24Gateway: A TCP/IP and RF24 Gateway for RF24 nodes](http://nRF24.github.io/RF24Gateway/) - [All Documentation and Downloads](https://tmrh20.github.io) -- [Source Code](https://github.com/TMRh20/RF24Mesh) +- [Source Code](https://github.com/nRF24/RF24Mesh) diff --git a/docs/setup_config.md b/docs/setup_config.md index 25ab14e..daacdfd 100644 --- a/docs/setup_config.md +++ b/docs/setup_config.md @@ -1,22 +1,28 @@ # Setup And Config -The initial testing version of RF24Mesh is built as a simple overlay for RF24Network. Users currently need to be familiar with the basics of sending and receiving data via -RF24Network, but do not need to understand the topology, routing or addressing systems. RF24Mesh will attempt to construct and maintain a mesh network, keeping all nodes +The initial testing version of RF24Mesh is built as a simple overlay for RF24Network. Users +currently need to be familiar with the basics of sending and receiving data via +RF24Network, but do not need to understand the topology, routing or addressing systems. +RF24Mesh will attempt to construct and maintain a mesh network, keeping all nodes connected together. ## Requirements + ### Hardware Requirements + - 1 Raspberry Pi or Arduino to act as the Master Node - 1 or more Arduino, Raspberry Pi, etc. (Sensor Nodes) - 2 or more NRF24L01+ radio modules - 1 or more various sensors for your sensor nodes ### Software Requirements + - [Download RF24 Core Radio Library](https://github.com/TMRh20/RF24/archive/master.zip) - [Download RF24Network Library](https://github.com/TMRh20/RF24Network/archive/master.zip) - [Download RF24Mesh - Dynamic Mesh Library](https://github.com/TMRh20/RF24Mesh/archive/master.zip) ## Installation + 1. Use the Arduino Library Manager. Selecting RF24Mesh should also install RF24Network and RF24 Core libraries 2. Configure and test the hardware using examples from RF24 and RF24Network prior to attempting to use RF24Mesh - In Arduino IDE @@ -34,20 +40,27 @@ connected together. display incoming data from the sensor examples. Usage is very much the same as RF24Network, except for address assignment and network management. ## Configuration -As per the examples, nodes are configured with a unique value between 1 and 253. This allows them to change positions on the network while still being identified. -For pre-configuration of the mesh, some options are available by editing RF24Mesh_config.h prior to compiling: +As per the examples, nodes are configured with a unique value between 1 and 253. This allows +them to change positions on the network while still being identified. + +For pre-configuration of the mesh, some options are available by editing RF24Mesh_config.h +prior to compiling: ### Restrict number of children + ```cpp #define MESH_MAX_CHILDREN 4 ``` + The @ref MESH_MAX_CHILDREN option restricts the maximum number of child nodes/node and limits the number of available addresses on the network. Max: 4 ### Reduce resource consumption + ```cpp #define MESH_NOMASTER ``` -The @ref MESH_NOMASTER option reduces program space and memory usage. Can be used on any node except for the master (nodeID 0) + +The MESH_NOMASTER macro optionally reduces program space and memory usage. Can be used on any node except for the master (nodeID 0) @see [General Usage](md_docs_general_usage.html) for information on how to work with the mesh once connected diff --git a/docs/sphinx/Makefile b/docs/sphinx/Makefile new file mode 100644 index 0000000..bbe765a --- /dev/null +++ b/docs/sphinx/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = ../sphinx +BUILDDIR = ../_build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/sphinx/README.md b/docs/sphinx/README.md new file mode 100644 index 0000000..5ca5047 --- /dev/null +++ b/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/docs/sphinx/RF24Mesh_8h.rst b/docs/sphinx/RF24Mesh_8h.rst new file mode 100644 index 0000000..aa584d3 --- /dev/null +++ b/docs/sphinx/RF24Mesh_8h.rst @@ -0,0 +1,5 @@ +RF24Mesh.h +============ + +.. doxygenfile:: RF24Mesh.h + :sections: define diff --git a/docs/sphinx/RF24Mesh__config_8h.rst b/docs/sphinx/RF24Mesh__config_8h.rst new file mode 100644 index 0000000..689495a --- /dev/null +++ b/docs/sphinx/RF24Mesh__config_8h.rst @@ -0,0 +1,4 @@ +RF24Mesh_config.h +================= + +.. doxygenfile:: RF24Mesh_config.h diff --git a/docs/sphinx/_static/Logo large.png b/docs/sphinx/_static/Logo large.png new file mode 100644 index 0000000..25e4215 Binary files /dev/null and b/docs/sphinx/_static/Logo large.png differ diff --git a/docs/sphinx/_static/custom_material.css b/docs/sphinx/_static/custom_material.css new file mode 100644 index 0000000..aef855b --- /dev/null +++ b/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,'); +} + +.md-typeset .admonition.seealso>.admonition-title::before { + mask-image: url('data:image/svg+xml;charset=utf-8,'); + 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,'); + 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/docs/sphinx/_static/new_favicon.ico b/docs/sphinx/_static/new_favicon.ico new file mode 100644 index 0000000..c15a165 Binary files /dev/null and b/docs/sphinx/_static/new_favicon.ico differ diff --git a/docs/sphinx/classRF24Mesh.rst b/docs/sphinx/classRF24Mesh.rst new file mode 100644 index 0000000..d0013c7 --- /dev/null +++ b/docs/sphinx/classRF24Mesh.rst @@ -0,0 +1,62 @@ +RF24Mesh class +~~~~~~~~~~~~~~ + +.. cpp:class:: RF24Mesh + + .. doxygenfunction:: RF24Mesh::RF24Mesh + + .. seealso:: + - :cpp:class:`RF24` for the ``radio`` object + - :cpp:class:`RF24Network` for the ``network`` object. + +Basic API +============ + +.. doxygenfunction:: RF24Mesh::begin + +.. seealso:: + :cpp:enum:`rf24_datarate_e`, :cpp:func:`RF24::setChannel()`, :c:macro:`MESH_DEFAULT_CHANNEL`, :c:macro:`MESH_RENEWAL_TIMEOUT` + +.. doxygenfunction:: RF24Mesh::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) + +.. seealso:: + Review :cpp:var:`RF24NetworkHeader::type` for more details about available message types. + +.. doxygenfunction:: RF24Mesh::renewAddress +.. doxygenfunction:: RF24Mesh::setNodeID +.. doxygenvariable:: RF24Mesh::_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 + +.. seealso:: :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 + +Address List Struct +=================== + +.. doxygenvariable:: RF24Mesh::addrList +.. doxygenvariable:: RF24Mesh::addrListTop + +.. doxygenstruct:: RF24Mesh::addrListStruct + :members: diff --git a/docs/sphinx/conf.py b/docs/sphinx/conf.py new file mode 100644 index 0000000..feffb42 --- /dev/null +++ b/docs/sphinx/conf.py @@ -0,0 +1,156 @@ +# 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 = "RF24Mesh library" +copyright = "2021, nRF24 org" +author = "nRF24" + +# The full version, including alpha/beta/rc tags +release = "1.1.6" # 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", +] + +intersphinx_mapping = { + "python": ("https://rf24.readthedocs.io/en/latest/", None), + "RF24 library": ("https://rf24.readthedocs.io/en/latest", None), + "RF24Network library": ("https://rf24network.readthedocs.io/en/latest", None), +} + +# 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++" + +# If true, '()' will be appended to :func: etc. cross-reference text. +add_function_parentheses = True + +# -- Options for breathe (XML output from doxygen) --------------------------- + +breathe_projects = {"RF24Mesh": "xml"} +breathe_default_project = "RF24Mesh" +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/RF24Mesh/", + "repo_name": "RF24Mesh", + "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 = "RF24Mesh 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/docs/sphinx/deprecated.rst b/docs/sphinx/deprecated.rst new file mode 100644 index 0000000..3e1b5a0 --- /dev/null +++ b/docs/sphinx/deprecated.rst @@ -0,0 +1,5 @@ +Deprecated API +============== + +.. doxygenpage:: deprecated + :content-only: diff --git a/docs/sphinx/examples.rst b/docs/sphinx/examples.rst new file mode 100644 index 0000000..0fd5469 --- /dev/null +++ b/docs/sphinx/examples.rst @@ -0,0 +1,44 @@ +Examples +========== + +Arduino Examples +---------------- + +.. toctree:: + :maxdepth: 1 + + examples/Arduino/RF24Mesh_Example + examples/Arduino/RF24Mesh_Example_Master + examples/Arduino/RF24Mesh_Example_Node2Node + examples/Arduino/RF24Mesh_Example_Node2NodeExtra + examples/Arduino/RF24Mesh_SerialConfig + +Linux Examples +---------------- + +.. toctree:: + :maxdepth: 1 + + examples/Linux/RF24Mesh_Example + examples/Linux/RF24Mesh_Example_Master + examples/Linux/RF24Mesh_Ncurses_Master + +PicoSDK Examples +---------------- + +.. toctree:: + :maxdepth: 1 + + examples/PicoSDK/RF24Mesh_Example_Master + examples/PicoSDK/RF24Mesh_Example + examples/PicoSDK/default_pins + + +Python Examples +---------------- + +.. toctree:: + :maxdepth: 1 + + examples/Python/RF24Mesh_Example_Master + examples/Python/RF24Mesh_Example diff --git a/docs/sphinx/examples/Arduino/RF24Mesh_Example.rst b/docs/sphinx/examples/Arduino/RF24Mesh_Example.rst new file mode 100644 index 0000000..04327d3 --- /dev/null +++ b/docs/sphinx/examples/Arduino/RF24Mesh_Example.rst @@ -0,0 +1,5 @@ +RF24Mesh_Example.ino +=========================== + +.. literalinclude:: ../../../../examples/RF24Mesh_Example/RF24Mesh_Example.ino + :linenos: diff --git a/docs/sphinx/examples/Arduino/RF24Mesh_Example_Master.rst b/docs/sphinx/examples/Arduino/RF24Mesh_Example_Master.rst new file mode 100644 index 0000000..0b702e9 --- /dev/null +++ b/docs/sphinx/examples/Arduino/RF24Mesh_Example_Master.rst @@ -0,0 +1,5 @@ +RF24Mesh_Example_Master.ino +=========================== + +.. literalinclude:: ../../../../examples/RF24Mesh_Example_Master/RF24Mesh_Example_Master.ino + :linenos: diff --git a/docs/sphinx/examples/Arduino/RF24Mesh_Example_Node2Node.rst b/docs/sphinx/examples/Arduino/RF24Mesh_Example_Node2Node.rst new file mode 100644 index 0000000..6bc84a6 --- /dev/null +++ b/docs/sphinx/examples/Arduino/RF24Mesh_Example_Node2Node.rst @@ -0,0 +1,5 @@ +RF24Mesh_Example_Node2Node.ino +============================== + +.. literalinclude:: ../../../../examples/RF24Mesh_Example_Node2Node/RF24Mesh_Example_Node2Node.ino + :linenos: diff --git a/docs/sphinx/examples/Arduino/RF24Mesh_Example_Node2NodeExtra.rst b/docs/sphinx/examples/Arduino/RF24Mesh_Example_Node2NodeExtra.rst new file mode 100644 index 0000000..f23763f --- /dev/null +++ b/docs/sphinx/examples/Arduino/RF24Mesh_Example_Node2NodeExtra.rst @@ -0,0 +1,5 @@ +RF24Mesh_Example_Node2NodeExtra.ino +=================================== + +.. literalinclude:: ../../../../examples/RF24Mesh_Example_Node2NodeExtra/RF24Mesh_Example_Node2NodeExtra.ino + :linenos: diff --git a/docs/sphinx/examples/Arduino/RF24Mesh_SerialConfig.rst b/docs/sphinx/examples/Arduino/RF24Mesh_SerialConfig.rst new file mode 100644 index 0000000..bf2d82c --- /dev/null +++ b/docs/sphinx/examples/Arduino/RF24Mesh_SerialConfig.rst @@ -0,0 +1,5 @@ +RF24Mesh_SerialConfig.ino +========================= + +.. literalinclude:: ../../../../examples/RF24Mesh_SerialConfig/RF24Mesh_SerialConfig.ino + :linenos: diff --git a/docs/sphinx/examples/Linux/RF24Mesh_Example.rst b/docs/sphinx/examples/Linux/RF24Mesh_Example.rst new file mode 100644 index 0000000..f24e5dd --- /dev/null +++ b/docs/sphinx/examples/Linux/RF24Mesh_Example.rst @@ -0,0 +1,6 @@ +RF24Mesh_Example.cpp +=========================== + +.. literalinclude:: ../../../../examples_RPi/RF24Mesh_Example.cpp + :caption: examples_RPi/RF24Mesh_Example.cpp + :linenos: diff --git a/docs/sphinx/examples/Linux/RF24Mesh_Example_Master.rst b/docs/sphinx/examples/Linux/RF24Mesh_Example_Master.rst new file mode 100644 index 0000000..6099457 --- /dev/null +++ b/docs/sphinx/examples/Linux/RF24Mesh_Example_Master.rst @@ -0,0 +1,6 @@ +RF24Mesh_Example_Master.cpp +=========================== + +.. literalinclude:: ../../../../examples_RPi/RF24Mesh_Example_Master.cpp + :caption: examples_RPi/RF24Mesh_Example_Master.cpp + :linenos: diff --git a/docs/sphinx/examples/Linux/RF24Mesh_Ncurses_Master.rst b/docs/sphinx/examples/Linux/RF24Mesh_Ncurses_Master.rst new file mode 100644 index 0000000..e044fe7 --- /dev/null +++ b/docs/sphinx/examples/Linux/RF24Mesh_Ncurses_Master.rst @@ -0,0 +1,10 @@ +RF24Mesh_Ncurses_Master.cpp +=========================== + +A very limited ncurses interface used for initial monitoring/testing of RF24Mesh. + +.. image:: ../../../../images/RF24Mesh_Ncurses.JPG + +.. literalinclude:: ../../../../examples_RPi/ncurses/RF24Mesh_Ncurses_Master.cpp + :caption: examples_RPi/RF24Mesh_Ncurses_Master.cpp + :linenos: diff --git a/docs/sphinx/examples/PicoSDK/RF24Mesh_Example.rst b/docs/sphinx/examples/PicoSDK/RF24Mesh_Example.rst new file mode 100644 index 0000000..90b61cc --- /dev/null +++ b/docs/sphinx/examples/PicoSDK/RF24Mesh_Example.rst @@ -0,0 +1,8 @@ +RF24Mesh_Example +=========================== + +.. seealso:: + `defaultPins.h `_ + +.. literalinclude:: ../../../../examples_pico/RF24Mesh_Example.cpp + :linenos: diff --git a/docs/sphinx/examples/PicoSDK/RF24Mesh_Example_Master.rst b/docs/sphinx/examples/PicoSDK/RF24Mesh_Example_Master.rst new file mode 100644 index 0000000..6fc89fe --- /dev/null +++ b/docs/sphinx/examples/PicoSDK/RF24Mesh_Example_Master.rst @@ -0,0 +1,8 @@ +RF24Mesh_Example_Master +=========================== + +.. seealso:: + `defaultPins.h `_ + +.. literalinclude:: ../../../../examples_pico/RF24Mesh_Example_Master.cpp + :linenos: diff --git a/docs/sphinx/examples/PicoSDK/default_pins.rst b/docs/sphinx/examples/PicoSDK/default_pins.rst new file mode 100644 index 0000000..5408726 --- /dev/null +++ b/docs/sphinx/examples/PicoSDK/default_pins.rst @@ -0,0 +1,5 @@ +PicoSDK Examples' Default Pins +============================== + +.. literalinclude:: ../../../../examples_pico/defaultPins.h + :linenos: diff --git a/docs/sphinx/examples/Python/RF24Mesh_Example.rst b/docs/sphinx/examples/Python/RF24Mesh_Example.rst new file mode 100644 index 0000000..43a09e1 --- /dev/null +++ b/docs/sphinx/examples/Python/RF24Mesh_Example.rst @@ -0,0 +1,7 @@ +RF24Mesh_Example.py +=========================== + +.. literalinclude:: ../../../../examples_RPi/RF24Mesh_Example.py + :language: python + :caption: examples_RPi/RF24Mesh_Example.py + :linenos: diff --git a/docs/sphinx/examples/Python/RF24Mesh_Example_Master.rst b/docs/sphinx/examples/Python/RF24Mesh_Example_Master.rst new file mode 100644 index 0000000..8b8f354 --- /dev/null +++ b/docs/sphinx/examples/Python/RF24Mesh_Example_Master.rst @@ -0,0 +1,7 @@ +RF24Mesh_Example_Master.py +=========================== + +.. literalinclude:: ../../../../examples_RPi/RF24Mesh_Example_Master.py + :language: python + :caption: examples_RPi/RF24Mesh_Example_Master.py + :linenos: diff --git a/docs/sphinx/index.rst b/docs/sphinx/index.rst new file mode 100644 index 0000000..716d1e2 --- /dev/null +++ b/docs/sphinx/index.rst @@ -0,0 +1,37 @@ + +:hero: Automated Networking for nrf24L01 radios + +Introduction +============= + +.. doxygenpage:: index + :content-only: + +Site Index +----------- + +:ref:`Site index` + +.. toctree:: + :maxdepth: 2 + :caption: API Reference + :hidden: + + classRF24Mesh + RF24Mesh_8h + RF24Mesh__config_8h + deprecated + + +.. toctree:: + :maxdepth: 1 + :hidden: + + pages + + +.. toctree:: + :maxdepth: 2 + :hidden: + + examples diff --git a/docs/sphinx/make.bat b/docs/sphinx/make.bat new file mode 100644 index 0000000..bba1007 --- /dev/null +++ b/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/docs/sphinx/md_contributing.rst b/docs/sphinx/md_contributing.rst new file mode 100644 index 0000000..8a9b00d --- /dev/null +++ b/docs/sphinx/md_contributing.rst @@ -0,0 +1,5 @@ +Contributing +============= + +.. doxygenpage:: md_CONTRIBUTING + :content-only: diff --git a/docs/sphinx/md_docs_general_usage.rst b/docs/sphinx/md_docs_general_usage.rst new file mode 100644 index 0000000..5a53a1a --- /dev/null +++ b/docs/sphinx/md_docs_general_usage.rst @@ -0,0 +1,5 @@ +General Usage +================ + +.. doxygenpage:: md_docs_general_usage + :content-only: diff --git a/docs/sphinx/md_docs_setup_config.rst b/docs/sphinx/md_docs_setup_config.rst new file mode 100644 index 0000000..81cc4df --- /dev/null +++ b/docs/sphinx/md_docs_setup_config.rst @@ -0,0 +1,5 @@ +Setup And Config +================ + +.. doxygenpage:: md_docs_setup_config + :content-only: diff --git a/docs/sphinx/pages.rst b/docs/sphinx/pages.rst new file mode 100644 index 0000000..a8b4b11 --- /dev/null +++ b/docs/sphinx/pages.rst @@ -0,0 +1,9 @@ +Related Pages +============= + +.. toctree:: + :maxdepth: 1 + + md_contributing + md_docs_general_usage + md_docs_setup_config diff --git a/docs/sphinx/requirements.txt b/docs/sphinx/requirements.txt new file mode 100644 index 0000000..1707fa2 --- /dev/null +++ b/docs/sphinx/requirements.txt @@ -0,0 +1,2 @@ +breathe +sphinx-immaterial diff --git a/examples/RF24Mesh_Example/RF24Mesh_Example.ino b/examples/RF24Mesh_Example/RF24Mesh_Example.ino index e5bf6d5..d72cd6a 100644 --- a/examples/RF24Mesh_Example/RF24Mesh_Example.ino +++ b/examples/RF24Mesh_Example/RF24Mesh_Example.ino @@ -19,15 +19,14 @@ RF24 radio(7, 8); RF24Network network(radio); RF24Mesh mesh(radio, network); -/** - User Configuration: nodeID - A unique identifier for each radio. Allows addressing - to change dynamically with physical changes to the mesh. - - In this example, configuration takes place below, prior to uploading the sketch to the device - A unique value from 1-255 must be configured for each node. - This will be stored in EEPROM on AVR devices, so remains persistent between further uploads, loss of power, etc. - - **/ +/* + * User Configuration: nodeID - A unique identifier for each radio. Allows addressing + * to change dynamically with physical changes to the mesh. + * + * In this example, configuration takes place below, prior to uploading the sketch to the device + * A unique value from 1-255 must be configured for each node. + * This will be stored in EEPROM on AVR devices, so remains persistent between further uploads, loss of power, etc. + */ #define nodeID 1 @@ -41,12 +40,21 @@ struct payload_t { void setup() { Serial.begin(115200); - //printf_begin(); + while (!Serial) { + // some boards need this because of native USB capability + } + // Set the nodeID manually mesh.setNodeID(nodeID); + // Connect to the mesh Serial.println(F("Connecting to the mesh...")); - mesh.begin(); + if (!mesh.begin()) { + Serial.println(F("Radio hardware not responding or could not connect to network.")); + while (1) { + // hold in an infinite loop + } + } } @@ -66,7 +74,7 @@ void loop() { if ( ! mesh.checkConnection() ) { //refresh the network address Serial.println("Renewing Address"); - if(!mesh.renewAddress()){ + if (!mesh.renewAddress()) { //If address renewal fails, reconfigure the radio and restart the mesh //This allows recovery from most if not all radio errors mesh.begin(); @@ -89,9 +97,3 @@ void loop() { Serial.println(payload.ms); } } - - - - - - diff --git a/examples/RF24Mesh_Example_Master/RF24Mesh_Example_Master.ino b/examples/RF24Mesh_Example_Master/RF24Mesh_Example_Master.ino index 2d21735..e626e70 100644 --- a/examples/RF24Mesh_Example_Master/RF24Mesh_Example_Master.ino +++ b/examples/RF24Mesh_Example_Master/RF24Mesh_Example_Master.ino @@ -1,77 +1,83 @@ - - - /** RF24Mesh_Example_Master.ino by TMRh20 - * - * - * This example sketch shows how to manually configure a node via RF24Mesh as a master node, which - * will receive all data from sensor nodes. - * - * The nodes can change physical or logical position in the network, and reconnect through different - * routing nodes as required. The master node manages the address assignments for the individual nodes - * in a manner similar to DHCP. - * - */ - - + + +/** RF24Mesh_Example_Master.ino by TMRh20 + * + * + * This example sketch shows how to manually configure a node via RF24Mesh as a master node, which + * will receive all data from sensor nodes. + * + * The nodes can change physical or logical position in the network, and reconnect through different + * routing nodes as required. The master node manages the address assignments for the individual nodes + * in a manner similar to DHCP. + * + */ + + #include "RF24Network.h" #include "RF24.h" #include "RF24Mesh.h" #include -//Include eeprom.h for AVR (Uno, Nano) etc. except ATTiny -#include /***** Configure the chosen CE,CS pins *****/ -RF24 radio(7,8); +RF24 radio(7, 8); RF24Network network(radio); -RF24Mesh mesh(radio,network); +RF24Mesh mesh(radio, network); uint32_t displayTimer = 0; void setup() { Serial.begin(115200); + while (!Serial) { + // some boards need this because of native USB capability + } // Set the nodeID to 0 for the master node mesh.setNodeID(0); Serial.println(mesh.getNodeID()); // Connect to the mesh - mesh.begin(); + if (!mesh.begin()) { + Serial.println(F("Radio hardware not responding or could not connect to network.")); + while (1) { + // hold in an infinite loop + } + } } -void loop() { +void loop() { // Call mesh.update to keep the network updated mesh.update(); - + // In addition, keep the 'DHCP service' running on the master node so addresses will // be assigned to the sensor nodes mesh.DHCP(); - - + + // Check for incoming data from the sensors - if(network.available()){ + if (network.available()) { RF24NetworkHeader header; network.peek(header); - - uint32_t dat=0; - switch(header.type){ + + uint32_t dat = 0; + switch (header.type) { // Display the incoming millis() values from the sensor nodes - case 'M': network.read(header,&dat,sizeof(dat)); Serial.println(dat); break; - default: network.read(header,0,0); Serial.println(header.type);break; + case 'M': network.read(header, &dat, sizeof(dat)); Serial.println(dat); break; + default: network.read(header, 0, 0); Serial.println(header.type); break; } } - - if(millis() - displayTimer > 5000){ + + if (millis() - displayTimer > 5000) { displayTimer = millis(); Serial.println(" "); Serial.println(F("********Assigned Addresses********")); - for(int i=0; i 0){ - Serial.println(_ID); - }else{ - Serial.println("Mesh ID Lookup Failed"); - } + if (network.available()) { + RF24NetworkHeader header; + uint32_t mills; + network.read(header, &mills, sizeof(mills)); + Serial.print("Rcv "); Serial.print(mills); + Serial.print(" from nodeID "); + int _ID = mesh.getNodeID(header.from_node); + if ( _ID > 0) { + Serial.println(_ID); + } else { + Serial.println("Mesh ID Lookup Failed"); + } } - - + + // Send to the other node every second - if(millis() - millisTimer >= 1000){ + if (millis() - millisTimer >= 1000) { millisTimer = millis(); - + // Send an 'M' type to other Node containing the current millis() - if(!mesh.write(&millisTimer,'M',sizeof(millisTimer),otherNodeID)){ - if( ! mesh.checkConnection() ){ - Serial.println("Renewing Address"); - mesh.renewAddress(); - }else{ - Serial.println("Send fail, Test OK"); - } + if (!mesh.write(&millisTimer, 'M', sizeof(millisTimer), otherNodeID)) { + if ( ! mesh.checkConnection() ) { + Serial.println("Renewing Address"); + mesh.renewAddress(); + } else { + Serial.println("Send fail, Test OK"); + } } } - -} +} diff --git a/examples/RF24Mesh_Example_Node2NodeExtra/RF24Mesh_Example_Node2NodeExtra.ino b/examples/RF24Mesh_Example_Node2NodeExtra/RF24Mesh_Example_Node2NodeExtra.ino index 0c8d04c..98e5cf8 100644 --- a/examples/RF24Mesh_Example_Node2NodeExtra/RF24Mesh_Example_Node2NodeExtra.ino +++ b/examples/RF24Mesh_Example_Node2NodeExtra/RF24Mesh_Example_Node2NodeExtra.ino @@ -44,12 +44,21 @@ uint32_t delayTime = 120; void setup() { Serial.begin(115200); - //printf_begin(); + while (!Serial) { + // some boards need this because of native USB capability + } + // Set the nodeID manually mesh.setNodeID(nodeID); + // Connect to the mesh Serial.println(F("Connecting to the mesh...")); - mesh.begin(); + if (!mesh.begin()) { + Serial.println(F("Radio hardware not responding or could not connect to network.")); + while (1) { + // hold in an infinite loop + } + } } unsigned int sizeCtr = 2; diff --git a/examples/RF24Mesh_SerialConfig/RF24Mesh_SerialConfig.ino b/examples/RF24Mesh_SerialConfig/RF24Mesh_SerialConfig.ino index 5e8926b..df23d6c 100644 --- a/examples/RF24Mesh_SerialConfig/RF24Mesh_SerialConfig.ino +++ b/examples/RF24Mesh_SerialConfig/RF24Mesh_SerialConfig.ino @@ -32,7 +32,9 @@ RF24Mesh mesh(radio, network); void setup() { Serial.begin(115200); - printf_begin(); + while (!Serial) { + // some boards need this because of native USB capability + } // If this is a new node, the nodeID will return 0. Once the node is configured with an ID other than 0, this // bit will no longer run. @@ -46,7 +48,12 @@ void setup() { } // Now that this node has a unique ID, connect to the mesh - mesh.begin(); + if (!mesh.begin()) { + Serial.println(F("Radio hardware not responding or could not connect to network.")); + while (1) { + // hold in an infinite loop + } + } } diff --git a/examples/examples_formatter.conf b/examples/examples_formatter.conf new file mode 100644 index 0000000..79fd9a6 --- /dev/null +++ b/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/examples_RPi/CMakeLists.txt b/examples_RPi/CMakeLists.txt new file mode 100644 index 0000000..a31e5e2 --- /dev/null +++ b/examples_RPi/CMakeLists.txt @@ -0,0 +1,35 @@ +cmake_minimum_required(VERSION 3.12) + +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 + +find_library(RF24 rf24 REQUIRED) +message(STATUS "using RF24 library: ${RF24}") + +find_library(RF24Network rf24network REQUIRED) +message(STATUS "using RF24Network library: ${RF24Network}") + +find_library(RF24Mesh rf24mesh REQUIRED) +message(STATUS "using RF24Mesh library: ${RF24Mesh}") + +# iterate over a list of examples by filename +set(EXAMPLES_LIST + RF24Mesh_Example + RF24Mesh_Example_Master +) + +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 + target_link_libraries(${example} PUBLIC ${RF24} pthread ${RF24Network} ${RF24Mesh}) +endforeach() + +include(../cmake/enableNcursesExample.cmake) +if(BUILD_NCURSES_EXAMPLE) + add_subdirectory(ncurses) +endif() diff --git a/examples_RPi/RF24Mesh_Example.cpp b/examples_RPi/RF24Mesh_Example.cpp index ad813ef..454a709 100644 --- a/examples_RPi/RF24Mesh_Example.cpp +++ b/examples_RPi/RF24Mesh_Example.cpp @@ -1,7 +1,5 @@ - - - /** RF24Mesh_Example.cpp by TMRh20 - * +/** RF24Mesh_Example.cpp by TMRh20 + * * Note: This sketch only functions on -RPi- * * This example sketch shows how to manually configure a node via RF24Mesh, and send data to the @@ -10,56 +8,59 @@ * nodes to change position in relation to each other and the master node. * */ - -#include "RF24Mesh/RF24Mesh.h" +#include "RF24Mesh/RF24Mesh.h" #include #include - -RF24 radio(22,0); +RF24 radio(22, 0); RF24Network network(radio); -RF24Mesh mesh(radio,network); +RF24Mesh mesh(radio, network); + +uint32_t displayTimer = 0; -uint32_t displayTimer=0; +int main(int argc, char **argv) +{ -int main(int argc, char** argv) { - // Set the nodeID to 0 for the master node mesh.setNodeID(4); // Connect to the mesh - printf("start nodeID %d\n",mesh.getNodeID()); - mesh.begin(); + printf("start nodeID %d\n", mesh.getNodeID()); + if (!mesh.begin()) { + printf("Radio hardware not responding or could not connect to network.\n"); + return 0; + } radio.printDetails(); -while(1) -{ - - // Call mesh.update to keep the network updated - mesh.update(); - - // Send the current millis() to the master node every second - if(millis() - displayTimer >= 1000){ - displayTimer = millis(); - - if(!mesh.write(&displayTimer,'M',sizeof(displayTimer))){ - - // If a write fails, check connectivity to the mesh network - if( ! mesh.checkConnection() ){ - // The address could be refreshed per a specified timeframe or only when sequential writes fail, etc. - printf("Renewing Address\n"); - mesh.renewAddress(); - }else{ - printf("Send fail, Test OK\n"); + while (1) + { + // Call mesh.update to keep the network updated + mesh.update(); + + // Send the current millis() to the master node every second + if (millis() - displayTimer >= 1000) + { + displayTimer = millis(); + + if (!mesh.write(&displayTimer, 'M', sizeof(displayTimer))) + { + // If a write fails, check connectivity to the mesh network + if (!mesh.checkConnection()) + { + // The address could be refreshed per a specified timeframe or only when sequential writes fail, etc. + printf("Renewing Address\n"); + mesh.renewAddress(); + } + else + { + printf("Send fail, Test OK\n"); + } } - }else{ - printf("Send OK: %u\n",displayTimer); - } - } - delay(1); + else + { + printf("Send OK: %u\n", displayTimer); + } + } + delay(1); } -return 0; + return 0; } - - - - diff --git a/examples_RPi/RF24Mesh_Example.py b/examples_RPi/RF24Mesh_Example.py index ae41944..14e5989 100755 --- a/examples_RPi/RF24Mesh_Example.py +++ b/examples_RPi/RF24Mesh_Example.py @@ -1,30 +1,31 @@ #!/usr/bin/env python3 -from RF24 import * -from RF24Network import * -from RF24Mesh import * from time import sleep, time from struct import pack +from RF24 import * +from RF24Network import * +from RF24Mesh import * start = time() def millis(): - - return int((time()-start)*1000) % (2 ** 32) + return int((time() - start) * 1000) % (2 ** 32) def delay(ms): - ms = ms % (2**32) - sleep(ms/1000.0) + ms = ms % (2 ** 32) + sleep(ms / 1000.0) # radio setup for RPi B Rev2: CS0=Pin 24 -radio = RF24(22,0); +radio = RF24(22, 0) network = RF24Network(radio) mesh = RF24Mesh(radio, network) mesh.setNodeID(4) -print("start nodeID", mesh.getNodeID()); -mesh.begin() +print("start nodeID", mesh.getNodeID()) +if not mesh.begin(): + print("Radio hardware not responding or could not connect to network.") + exit() radio.setPALevel(RF24_PA_MAX) # Power Amplifier radio.printDetails() @@ -47,5 +48,4 @@ def delay(ms): print("Send fail, Test OK") else: print("Send OK:", displayTimer) - delay(1); - \ No newline at end of file + delay(1) diff --git a/examples_RPi/RF24Mesh_Example_Master.cpp b/examples_RPi/RF24Mesh_Example_Master.cpp index e0bf957..42d1583 100644 --- a/examples_RPi/RF24Mesh_Example_Master.cpp +++ b/examples_RPi/RF24Mesh_Example_Master.cpp @@ -1,7 +1,5 @@ - - - /** RF24Mesh_Example_Master.ino by TMRh20 - * +/** RF24Mesh_Example_Master.ino by TMRh20 + * * Note: This sketch only functions on -Arduino Due- * * This example sketch shows how to manually configure a node via RF24Mesh as a master node, which @@ -12,60 +10,56 @@ * in a manner similar to DHCP. * */ - -#include "RF24Mesh/RF24Mesh.h" +#include "RF24Mesh/RF24Mesh.h" #include #include - -RF24 radio(22,0); +RF24 radio(22, 0); RF24Network network(radio); -RF24Mesh mesh(radio,network); - +RF24Mesh mesh(radio, network); - -int main(int argc, char** argv) { - +int main(int argc, char **argv) +{ // Set the nodeID to 0 for the master node mesh.setNodeID(0); // Connect to the mesh printf("start\n"); - mesh.begin(); + if (!mesh.begin()) { + printf("Radio hardware not responding or could not connect to network.\n"); + return 0; + } radio.printDetails(); -while(1) -{ - - // Call network.update as usual to keep the network updated - mesh.update(); + while (1) + { + // Call network.update as usual to keep the network updated + mesh.update(); + + // In addition, keep the 'DHCP service' running on the master node so addresses will + // be assigned to the sensor nodes + mesh.DHCP(); + + // Check for incoming data from the sensors + while (network.available()) + { + // printf("rcv\n"); + RF24NetworkHeader header; + network.peek(header); - // In addition, keep the 'DHCP service' running on the master node so addresses will - // be assigned to the sensor nodes - mesh.DHCP(); - - - // Check for incoming data from the sensors - while(network.available()){ -// printf("rcv\n"); - RF24NetworkHeader header; - network.peek(header); - - uint32_t dat=0; - switch(header.type){ - // Display the incoming millis() values from the sensor nodes - case 'M': network.read(header,&dat,sizeof(dat)); - printf("Rcv %u from 0%o\n",dat,header.from_node); - break; - default: network.read(header,0,0); - printf("Rcv bad type %d from 0%o\n",header.type,header.from_node); - break; + uint32_t dat = 0; + switch (header.type) + { // Display the incoming millis() values from the sensor nodes + case 'M': + network.read(header, &dat, sizeof(dat)); + printf("Rcv %u from 0%o\n", dat, header.from_node); + break; + default: + network.read(header, 0, 0); + printf("Rcv bad type %d from 0%o\n", header.type, header.from_node); + break; + } } + delay(2); } -delay(2); - } -return 0; + return 0; } - - - - diff --git a/examples_RPi/RF24Mesh_Example_Master.py b/examples_RPi/RF24Mesh_Example_Master.py index 963269b..7f58508 100755 --- a/examples_RPi/RF24Mesh_Example_Master.py +++ b/examples_RPi/RF24Mesh_Example_Master.py @@ -1,19 +1,21 @@ #!/usr/bin/env python3 + +from struct import unpack from RF24 import * from RF24Network import * from RF24Mesh import * -from struct import unpack - # radio setup for RPi B Rev2: CS0=Pin 24 -radio = RF24(22,0); +radio = RF24(22, 0) network = RF24Network(radio) mesh = RF24Mesh(radio, network) mesh.setNodeID(0) -mesh.begin() +if not mesh.begin(): + print("Radio hardware not responding or could not connect to network.") + exit() radio.setPALevel(RF24_PA_MAX) # Power Amplifier radio.printDetails() @@ -24,6 +26,6 @@ while network.available(): header, payload = network.read(10) if chr(header.type) == 'M': - print("Rcv {} from 0{:o}".format(unpack("L",payload)[0], header.from_node)) + print("Rcv {} from 0{:o}".format(unpack("L", payload)[0], header.from_node)) else: - print("Rcv bad type {} from 0{:o}".format(header.type,header.from_node)); \ No newline at end of file + print("Rcv bad type {} from 0{:o}".format(header.type, header.from_node)) \ No newline at end of file diff --git a/examples_RPi/ncurses/CMakeLists.txt b/examples_RPi/ncurses/CMakeLists.txt new file mode 100644 index 0000000..e621453 --- /dev/null +++ b/examples_RPi/ncurses/CMakeLists.txt @@ -0,0 +1,17 @@ +# this example needs the ncurses package installed +find_package(Curses REQUIRED) +include_directories(${CURSES_INCLUDE_DIR}) + +set(example RF24Mesh_Ncurses_Master) + +# make a target +add_executable(${example} ${example}.cpp) + +# link the RF24 lib to the target. Notice we specify pthread as a linked lib here +target_link_libraries(${example} PUBLIC + ${RF24} + pthread + ${RF24Network} + ${RF24Mesh} + ${CURSES_LIBRARIES} +) diff --git a/examples_RPi/ncurses/RF24Mesh_Ncurses_Master.cpp b/examples_RPi/ncurses/RF24Mesh_Ncurses_Master.cpp index 4aacbc0..d3fdd04 100644 --- a/examples_RPi/ncurses/RF24Mesh_Ncurses_Master.cpp +++ b/examples_RPi/ncurses/RF24Mesh_Ncurses_Master.cpp @@ -5,15 +5,15 @@ * assignments, and information regarding incoming data, regardless of the specific * configuration details. * -* Requirements: NCurses +* Requirements: NCurses * Install NCurses: apt-get install libncurses5-dev * Setup: * 1: make * 2: sudo ./RF24Mesh_Ncurses_Master -* +* * NOTE: DEBUG MUST BE DISABLED IN RF24Mesh_config.h * -* Once configured and running, the interface will display the header information, data rate, +* Once configured and running, the interface will display the header information, data rate, * and address assignments for all connected nodes.* * The master node will also continuously ping each of the child nodes, one per second, while indicating * the results. @@ -21,13 +21,14 @@ */ #include -#include "RF24Mesh/RF24Mesh.h" +#include "RF24Mesh/RF24Mesh.h" #include #include -RF24 radio(22,0); +RF24 radio(22, 0); + RF24Network network(radio); -RF24Mesh mesh(radio,network); +RF24Mesh mesh(radio, network); void printNodes(uint8_t boldID); void pingNode(uint8_t listNo); @@ -37,11 +38,14 @@ uint8_t nodeCounter; uint16_t failID = 0; int main() -{ +{ printf("Establishing mesh...\n"); mesh.setNodeID(0); - mesh.begin(); + if (!mesh.begin()) { + printf("Radio hardware not responding or could not connect to network.\n"); + return 0; + } radio.printDetails(); initscr(); /* Start curses mode */ @@ -54,8 +58,8 @@ int main() printw("RF24Mesh Master Node Monitoring Interface by TMRh20 - 2014\n"); attroff(COLOR_PAIR(1)); refresh(); /* Print it on to the real screen */ - - uint32_t kbTimer = 0,kbCount = 0, pingTimer=millis(); + + uint32_t kbTimer = 0,kbCount = 0, pingTimer=millis(); //std::map::iterator it = mesh.addrMap.begin(); unsigned long totalPayloads = 0; @@ -69,26 +73,26 @@ while(1) mesh.DHCP(); // Wait until a sensor node is connected if(sizeof(mesh.addrList) < 1){continue; } - + // Check for incoming data from the sensors - while(network.available()){ + while(network.available()){ RF24NetworkHeader header; network.peek(header); - + uint8_t boldID = 0; - + // Print the total number of received payloads mvprintw(9,0," Total: %lu\n",totalPayloads++); kbCount++; - + attron(A_BOLD | COLOR_PAIR(1)); mvprintw(2,0,"[Last Payload Info]\n"); - attroff(A_BOLD | COLOR_PAIR(1)); - + attroff(A_BOLD | COLOR_PAIR(1)); + // Read the network payload network.read(header,0,0); - + // Display the header info mvprintw(3,0," HeaderID: %u \n Type: %d \n From: 0%o \n ",header.id,header.type,header.from_node); @@ -99,11 +103,11 @@ while(1) boldID = mesh.addrList[i].nodeID; } } - printNodes(boldID); + printNodes(boldID); } //refresh(); - + if(millis()-kbTimer > 1000 && kbCount > 0){ kbTimer = millis(); attron(A_BOLD | COLOR_PAIR(1)); @@ -111,9 +115,9 @@ while(1) attroff(A_BOLD | COLOR_PAIR(1)); mvprintw(8,0," Kbps: %.2f",(kbCount * 32 * 8)/1000.00); kbCount = 0; - + } - + // Ping each connected node, one per second if(millis()-pingTimer>1003 && sizeof(mesh.addrList) > 0){ pingTimer=millis(); @@ -121,9 +125,9 @@ while(1) nodeCounter = 0; } pingNode(nodeCounter); - nodeCounter++; + nodeCounter++; } - + /*uint32_t nOK,nFails; network.failures(&nFails,&nOK); attron(A_BOLD | COLOR_PAIR(1)); @@ -131,12 +135,12 @@ while(1) attroff(A_BOLD | COLOR_PAIR(1)); mvprintw(3,25," #OK: %u ",nOK); mvprintw(4,25," #Fail: %u ",nFails);*/ - - + + refresh(); delay(2); }//while 1 - + endwin(); /* End curses mode */ return 0; } @@ -184,7 +188,7 @@ void pingNode(uint8_t listNo){ mvprintw(12,0," ID:%d ",mesh.addrList[listNo].nodeID); mvprintw(13,0," Net:0%o ",mesh.addrList[listNo].address); mvprintw(14,0," Time:%ums ",pingtime); - + if(ok || !headers.to_node){ mvprintw(15,0," OK "); } else{ attron(A_BOLD); mvprintw(15,0," FAIL"); attron(A_BOLD); } } diff --git a/examples_pico/CMakeLists.txt b/examples_pico/CMakeLists.txt new file mode 100644 index 0000000..903e16d --- /dev/null +++ b/examples_pico/CMakeLists.txt @@ -0,0 +1,57 @@ +cmake_minimum_required(VERSION 3.12) + +#[[ + for these examples we are assuming the following structure: + path/to/github/repos/ + RF24/ + RF24Network/ + RF24Mesh/ + examples_pico/ <-- you are here + pico-sdk/ + + Be sure to set the PICO_SDK_PATH environment variable + `export PICO_SDK_PATH=/path/to/github/repos/pico-sdk/` +]] + +# Pull in SDK (must be before project) +include(../../RF24/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(../../RF24/CMakeLists.txt) # for RF24 lib +include(../../RF24Network/CMakeLists.txt) # for RF24Network lib +include(../CMakeLists.txt) # for RF24Mesh lib + +# iterate over a list of examples by filename +set(EXAMPLES_LIST + RF24Mesh_Example + RF24Mesh_Example_Master +) + +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 + RF24Network + RF24Mesh + 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/examples_pico/RF24Mesh_Example.cpp b/examples_pico/RF24Mesh_Example.cpp new file mode 100644 index 0000000..f205596 --- /dev/null +++ b/examples_pico/RF24Mesh_Example.cpp @@ -0,0 +1,84 @@ +/** RF24Mesh_Example.cpp by TMRh20 + * + * Note: This sketch only functions on RP2040 boards + * + * This example sketch shows how to manually configure a node via RF24Mesh, and send data to the + * master node. + * In this sketch, the nodes will refresh their network address as soon as a single write fails. This allows the + * nodes to change position in relation to each other and the master node. + * + */ +#include "pico/stdlib.h" // printf(), sleep_ms(), to_ms_since_boot(), get_absolute_time() +#include // tud_cdc_connected() +#include // RF24 radio object +#include // RF24Network network object +#include // RF24Mesh mesh 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); + +RF24Network network(radio); + +RF24Mesh mesh(radio, network); + +uint32_t displayTimer = 0; + +bool setup() +{ + // wait here until the CDC ACM (serial port emulation) is connected + while (!tud_cdc_connected()) { + sleep_ms(10); + } + + // Set the nodeID to 0 for the master node + mesh.setNodeID(4); + + // Connect to the mesh + printf("start nodeID %d\n", mesh.getNodeID()); + if (!mesh.begin()) + { + printf("Radio hardware not responding or could not connect to network.\n"); + return 0; + } + return true; +} + +void loop() +{ + // Call mesh.update to keep the network updated + mesh.update(); + + // Send the current millis() to the master node every second + if (to_ms_since_boot(get_absolute_time()) - displayTimer >= 1000) { + displayTimer = to_ms_since_boot(get_absolute_time()); + + if (!mesh.write(&displayTimer, 'M', sizeof(displayTimer))) { + // If a write fails, check connectivity to the mesh network + if (!mesh.checkConnection()) { + // The address could be refreshed per a specified timeframe or only when sequential writes fail, etc. + printf("Renewing Address\n"); + mesh.renewAddress(); + } + else { + printf("Send fail, Test OK\n"); + } + } + else { + printf("Send OK: %u\n", displayTimer); + } + } +} + +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/examples_pico/RF24Mesh_Example_Master.cpp b/examples_pico/RF24Mesh_Example_Master.cpp new file mode 100644 index 0000000..b337407 --- /dev/null +++ b/examples_pico/RF24Mesh_Example_Master.cpp @@ -0,0 +1,87 @@ +/** + * This example sketch shows how to manually configure a node via RF24Mesh as a master node, which + * will receive all data from sensor nodes. + * + * The nodes can change physical or logical position in the network, and reconnect through different + * routing nodes as required. The master node manages the address assignments for the individual nodes + * in a manner similar to DHCP. + */ +#include "pico/stdlib.h" // printf(), sleep_ms(), to_ms_since_boot(), get_absolute_time() +#include // tud_cdc_connected() +#include // RF24 radio object +#include // RF24Network network object +#include // RF24Mesh mesh 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); + +RF24Network network(radio); + +RF24Mesh mesh(radio, network); + +bool setup() +{ + // wait here until the CDC ACM (serial port emulation) is connected + while (!tud_cdc_connected()) { + sleep_ms(10); + } + + // Set the nodeID to 0 for the master node + mesh.setNodeID(0); + + // Connect to the mesh + printf("start\n"); + if (!mesh.begin()) { + printf("Radio hardware not responding or could not connect to network.\n"); + return 0; + } + + radio.printDetails(); + return true; +} + + +void loop() +{ + // Call network.update as usual to keep the network updated + mesh.update(); + + // In addition, keep the 'DHCP service' running on the master node so addresses will + // be assigned to the sensor nodes + mesh.DHCP(); + + // Check for incoming data from the sensors + while (network.available()) { + // printf("rcv\n"); + RF24NetworkHeader header; + network.peek(header); + + uint32_t dat = 0; + switch (header.type) + { // Display the incoming millis() values from the sensor nodes + case 'M': + network.read(header, &dat, sizeof(dat)); + printf("Rcv %u from 0%o\n", dat, header.from_node); + break; + default: + network.read(header, 0, 0); + printf("Rcv bad type %d from 0%o\n", header.type, header.from_node); + break; + } + } +} + + +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/examples_pico/defaultPins.h b/examples_pico/defaultPins.h new file mode 100644 index 0000000..e6819ca --- /dev/null +++ b/examples_pico/defaultPins.h @@ -0,0 +1,29 @@ +// 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 + +#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 + + +#elif defined (SPARFUN_THINGPLUS) +#define CSN_PIN 16 // the pin labeled 16 +#define CE_PIN 7 // the pin labeled SCL + +#else +// pins available on (ADAFRUIT_ITSYBITSY_RP2040 || ADAFRUIT_FEATHER_RP2040 || Pico_board || Sparkfun_ProMicro || SparkFun MicroMod) + +#define CE_PIN 7 +#define CSN_PIN 8 +#endif // board detection macro defs + +#endif // DEFAULTPINS_H diff --git a/library.json b/library.json index 813adf0..d9b18f3 100644 --- a/library.json +++ b/library.json @@ -1,22 +1,46 @@ { - "name": "RF24Mesh", - "keywords": "rf, radio, wireless, spi, mesh, network", - "description": "A simple and seamless 'mesh' layer for sensor networks. Designed to interface directly with with the RF24Network Development library, an OSI Network Layer using nRF24L01(+) radios driven by the newly optimized RF24 library fork.", - "repository": - { - "type": "git", - "url": "https://github.com/TMRh20/RF24Mesh.git" - }, - "version": "1.1.5", - "dependencies": [ - { "name": "RF24", "authors": "TMRh20", "frameworks": "arduino" }, - { "name": "RF24Network", "authors": "TMRh20", "frameworks": "arduino" } - ], - "exclude": "examples_RPi", - "frameworks": "arduino", - "platforms": [ - "atmelavr", - "atmelsam", - "teensy" - ] + "name": "RF24Mesh", + "keywords": "rf, radio, wireless, spi, mesh, network", + "description": "A simple and seamless 'mesh' layer for sensor networks. Designed to interface directly with with the RF24Network Development library, an OSI Network Layer using nRF24L01(+) radios driven by the newly optimized RF24 library fork.", + "license": "GPL-2.0-only", + "repository": { + "type": "git", + "url": "https://github.com/nRF24/RF24Mesh.git" + }, + "version": "1.1.6", + "dependencies": + [ + { + "name": "RF24", + "authors": "nRF24", + "frameworks": "arduino" + }, + { + "name": "RF24Network", + "authors": "nRF24", + "frameworks": "arduino" + } + ], + "export": + { + "exclude": [ + ".github/*", + "keywords.txt", + "examples_RPi/*", + "pyRF24Mesh/*", + "cmake/*", + "CMakeLists.txt", + "Makefile" + ] + }, + "frameworks": "arduino", + "platforms": [ + "atmelavr", + "atmelsam", + "teensy", + "atmelmegaavr", + "espressif32", + "espressif8266", + "ststm32" + ] } diff --git a/library.properties b/library.properties index 36f83dc..6ebcb80 100644 --- a/library.properties +++ b/library.properties @@ -1,5 +1,5 @@ name=RF24Mesh -version=1.1.5 +version=1.1.6 author=TMRh20 maintainer=TMRh20,Avamander sentence=OSI Layer 7, Automated 'mesh' style networking for nrf24L01(+) radios. diff --git a/pyRF24Mesh/pyRF24Mesh.cpp b/pyRF24Mesh/pyRF24Mesh.cpp index 2c7ba20..2d04214 100644 --- a/pyRF24Mesh/pyRF24Mesh.cpp +++ b/pyRF24Mesh/pyRF24Mesh.cpp @@ -25,6 +25,7 @@ char *get_bytes_or_bytearray_str(bp::object buf) return PyBytes_AsString(py_ba); else throw_ba_exception(); + return NULL; } @@ -38,6 +39,7 @@ int get_bytes_or_bytearray_ln(bp::object buf) return PyBytes_Size(py_ba); else throw_ba_exception(); + return 0; } diff --git a/pyRF24Mesh/setup.py b/pyRF24Mesh/setup.py index 8d5eb72..48c2362 100755 --- a/pyRF24Mesh/setup.py +++ b/pyRF24Mesh/setup.py @@ -1,17 +1,50 @@ #!/usr/bin/env python -from distutils.core import setup, Extension -import sys +import os +from setuptools import setup, Extension +from sys import version_info -if sys.version_info >= (3,): - BOOST_LIB = 'boost_python3' -else: - BOOST_LIB = 'boost_python' +if version_info >= (3,): + BOOST_LIB = "boost_python3" +else: + BOOST_LIB = "boost_python" -module_RF24Mesh = Extension('RF24Mesh', - libraries = ['rf24mesh', 'rf24network', BOOST_LIB], - sources = ['pyRF24Mesh.cpp']) +# NOTE can't access "../../LICENSE.inc" from working dir because +# it's relative. Brute force absolute path dynamically. +git_dir = os.path.split(os.path.abspath(os.getcwd()))[0] -setup(name='RF24Mesh', - version='1.0', - ext_modules=[module_RF24Mesh]) +# get LIB_VERSION from library.properties file for Arduino IDE +version = "1.0" +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] + + +long_description = """ +.. warning:: This python wrapper for the RF24Mesh 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="RF24Mesh", + 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( + "RF24Mesh", + sources=["pyRF24Mesh.cpp"], + libraries=["rf24", "rf24network", "rf24mesh", BOOST_LIB], + ) + ], +)