From 7441eefbfb9c2750649d76de70b1d80fec013c66 Mon Sep 17 00:00:00 2001 From: Brendan <2bndy5@gmail.com> Date: Tue, 26 Mar 2024 11:49:58 -0700 Subject: [PATCH] update python wrapper/examples/doc (#967) - expose selected `RF24_DRIVER` in the python wrapper (for programmatically specifying the right pins) - update for the python examples - interrupt_configure.py usees gpiod instead of RPi.GPIO - removed argparse from examples for simplicity - use the appropriate pin numbers for the selected `RF24_DRIVER` - revised the python wrapper's install doc --- .github/workflows/doxygen.yml | 1 + docs/Doxyfile | 13 +- docs/doxygen-custom.css | 104 ++++++ docs/python_wrapper.md | 82 +++-- examples_linux/acknowledgementPayloads.cpp | 2 +- examples_linux/acknowledgement_payloads.py | 136 +++----- examples_linux/getting_started.py | 138 +++----- examples_linux/gettingstarted.cpp | 2 +- examples_linux/interruptConfigure.cpp | 2 +- examples_linux/interrupt_configure.py | 320 ++++++++---------- .../interrupts/transfer_interrupt.cpp | 2 +- examples_linux/manualAcknowledgements.cpp | 2 +- examples_linux/manual_acknowledgements.py | 135 +++----- examples_linux/multiceiverDemo.cpp | 2 +- examples_linux/multiceiver_demo.py | 99 +++--- examples_linux/ncurses/scanner_curses.cpp | 2 +- examples_linux/scanner.cpp | 2 +- examples_linux/scanner.py | 14 +- examples_linux/streamingData.cpp | 2 +- examples_linux/streaming_data.py | 138 +++----- pyRF24/pyRF24.cpp | 13 + 21 files changed, 603 insertions(+), 608 deletions(-) diff --git a/.github/workflows/doxygen.yml b/.github/workflows/doxygen.yml index 7824d83c5..86b695d11 100644 --- a/.github/workflows/doxygen.yml +++ b/.github/workflows/doxygen.yml @@ -40,4 +40,5 @@ jobs: uses: nRF24/.github/.github/workflows/build_docs.yaml@main with: deploy-gh-pages: ${{ github.event_name == 'release' || (github.event_name == 'workflow_dispatch' && github.ref == 'refs/heads/master') }} + doxygen-version: '1.10.0' secrets: inherit diff --git a/docs/Doxyfile b/docs/Doxyfile index d5015a11d..de8a00450 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -332,14 +332,15 @@ HTML_EXTRA_STYLESHEET = doxygen-custom.css HTML_COLORSTYLE = TOGGLE -# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML -# page will contain the date and time when the page was generated. Setting this -# to YES can help to show when doxygen was last run and thus if the -# documentation is up to date. +# If the TIMESTAMP tag is set different from NO then each generated page will contain +# the date or date and time when the page was generated. Setting this to NO can help +# when comparing the output of multiple runs. +# +# Possible values are: YES, NO, DATETIME and DATE. +# # The default value is: NO. -# This tag requires that the tag GENERATE_HTML is set to YES. -HTML_TIMESTAMP = YES +TIMESTAMP = DATE # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that diff --git a/docs/doxygen-custom.css b/docs/doxygen-custom.css index 7d56191d7..2b5ef5a1b 100644 --- a/docs/doxygen-custom.css +++ b/docs/doxygen-custom.css @@ -1,3 +1,107 @@ table.markdownTable th { color: unset; } + +/* overrides from default CSSS for some admonitions */ +dl.note, dl.remark, +dl.warning, dl.attention { + color: unset; +} +dl.remark { + background: var(--remark-color-bg); + border-left: 8px solid var(--remark-color-hl); +} +dl.remark dt { + color: var(--remark-color-hl); +} + +/* special rules to accent `/see` or `/sa` command output */ +dl.see { + background: var(--seealso-color-bg); + border-left: 8px solid var(--seealso-color-hl); +} +dl.see dt { + color: var(--seealso-color-hl); +} +dl.see { + padding: 10px; + margin: 10px 0px; + overflow: hidden; + margin-left: 0; + border-radius: 4px; +} + +/* admonition icons */ +dl.note dt::before { + background-color: var(--note-color-hl); + mask-image: var(--note-icon); +} +dl.see dt::before { + background-color: var(--seealso-color-hl); + mask-image: var(--seealso-icon); +} +dl.remark dt::before { + background-color: var(--remark-color-hl); + mask-image: var(--remark-icon); +} +dl.warning dt::before { + background-color: var(--warning-color-hl); + mask-image: var(--warning-icon); +} +dl.deprecated dt::before { + background-color: var(--deprecated-color-hl); + mask-image: var(--deprecated-icon); +} +dl.note dt::before, +dl.see dt::before, +dl.warning dt::before, +dl.remark dt::before, +dl.deprecated dt::before { + vertical-align: middle; + background-repeat: no-repeat; + content: ""; + display: inline-block; + height: 2em; + width: 2em; + margin-right: 0.25rem; +} +dl.note dt, +dl.see dt, +dl.warning dt, +dl.remark dt, +dl.deprecated dt { + margin-top: -0.35em; + margin-bottom: 0.5em; +} + +/* icon SVG data */ +*:root { + --note-icon: url('data:image/svg+xml;utf8,'); + --seealso-icon: url('data:image/svg+xml;utf8,'); + --warning-icon: url('data:image/svg+xml;utf8,'); + --remark-icon: url('data:image/svg+xml;utf8,'); + --deprecated-icon: url('data:image/svg+xml;utf8,'); +} + +/* color overrides */ +html { + /* light theme CSS variables */ + --note-color-bg: hsla(47.6, 77.3%, 91.4%, 65%); + --warning-color-bg: hsla(6.8, 75.9%, 88.6%, 65%); + --deprecated-color-bg: hsla(205.7, 22.6%, 93.9%, 65%); + --seealso-color-bg: hsla(215, 76%, 89%, 65%); + --seealso-color-hl: hsl(215, 98%, 48%); + --remark-color-bg: hsla(133, 75%, 89%, 65%); + --remark-color-hl: hsl(133, 98.9%, 35.3%); +} + +html.dark-mode { + /* dark theme CSS variables */ + --note-color-bg: hsla(45.8, 87.3%, 12.4%, 65%); + --warning-color-bg: hsla(5.2, 33.3%, 13.5%, 65%); + --deprecated-color-bg: hsla(221.5, 12.4%, 20.6%, 65%); + --seealso-color-bg: hsla(215, 33%, 14%, 0.65); + --seealso-color-hl: hsl(215, 98%, 48%); + --remark-color-bg: hsla(133, 32%, 14%, 65%); + --remark-color-hl: hsl(133, 98%, 48%); +} \ No newline at end of file diff --git a/docs/python_wrapper.md b/docs/python_wrapper.md index 7bcadee74..60e73a42c 100644 --- a/docs/python_wrapper.md +++ b/docs/python_wrapper.md @@ -2,22 +2,41 @@ @tableofcontents - -By [mz-fuzzy](https://github.com/mz-fuzzy) +@remark +@parblock +We recommend using the newer [pyRF24 package](https://github.com/nRF24/pyRF24) +[available from pypi](https://pypi.org/project/pyrf24/) because + +1. it is [practically drop-in compatible](https://nrf24.github.io/pyRF24/#migrating-to-pyrf24) +2. easier to install or get updates with popular package managers like pip +3. does not require the C++ libraries to be installed -- it uses its own isolated binaries +4. includes wrappers for RF24, RF24Network, RF24Mesh libraries +5. includes a new [fake BLE implementation](https://nrf24.github.io/pyRF24/ble_api.html) +6. has its own [dedicated documentation](https://nRF24.github.io/pyRF24) +7. is compatible with python's builtin `help()` +8. includes typing stub files for type checking tools like mypy + +The only reason that you should need to keep using these older individual python +wrappers is if you must to use python v3.6 or older. + +You **cannot** use these individual wrappers in combination with the pyRF24 package. +@endparblock ## Python Wrapper Prerequisites -### RF24 +These instructions work for the RF24, RF24Network, and RF24Mesh libraries, but +the C++ source code needs to be built and installed for the corresponding +python wrapper(s) to work. -The RF24 lib needs to be built in C++ & installed for the python wrapper to wrap it. +@see Review [installing with CMake](md_docs_using_cmake.html) and [Linux/RPi General](md_docs_rpi_general.html). -See [Linux Installation](md_docs_linux_install.html) (or [installing with CMake](md_docs_using_cmake.html) -alternatively) and [Linux/RPi General](md_docs_rpi_general.html) +@note The interrupt_configure.py example uses the +[gpiod library](https://pypi.org/project/gpiod) to watch the radio's IRQ pin. ### Python2 ```shell -sudo apt-get install python-dev libboost-python-dev python-pip python-rpi.gpio +sudo apt-get install python-dev libboost-python-dev python-pip ``` Next, install some up-to-date python packages. @@ -29,7 +48,7 @@ python -m pip install --upgrade pip setuptools ### Python3 ```shell -sudo apt-get install python3-dev libboost-python-dev python3-pip python3-rpi.gpio +sudo apt-get install python3-dev libboost-python-dev python3-pip ``` Next, install some up-to-date python3 packages. @@ -40,7 +59,7 @@ python3 -m pip install --upgrade pip setuptools ## Installation -@note Steps 2 and 3 have to be repeated if installing the python wrappers for +@note Only step 2 has to be repeated if installing the python wrappers for RF24Network and RF24Mesh libraries. The prerequisites stated above still apply to each library. @@ -48,9 +67,9 @@ to each library. ```shell sudo ln -s $(ls /usr/lib/$(ls /usr/lib/gcc | tail -1)/libboost_python3*.so | tail -1) /usr/lib/$(ls /usr/lib/gcc | tail -1)/libboost_python3.so ``` -2. Build the library. +2. Install the library. - This step and the next step need to be executed from the appropriate directory of + This step needs to be executed from the appropriate directory of the cloned RF24* repository: - navigate to *pyRF24* directory in the RF24 cloned repository - navigate to *RPi/pyRF24Network* directory in the RF24Network cloned repository @@ -58,25 +77,20 @@ to each library. When in the correct directory, run the following command: ```shell - ./setup.py build + python setup.py install ``` or for python3 ```shell - python3 setup.py build - ``` - @note Build takes several minutes on arm-based machines. Machines with RAM less than 1GB may need to increase amount of swap for build. -3. Install the library - ```shell - sudo ./setup.py install - ``` - or for python3 - ```shell - sudo python3 setup.py install + python3 -m pip install -v . ``` + @note Building/installing takes several minutes on arm-based machines. + Machines with RAM less than 1GB may need to increase amount of swap for build. + The `-v` option enables pip's verbose output to show that the process has not frozen. + See the additional [Platform Support pages](pages.html) for information on connecting your hardware. See the included [\*.py files in the "examples_linux" folder](examples.html) for usage information. -4. Running the Example +3. Running the Example The python examples location differ for each RF24* resopitories. - navigate to *examples_linux* directory in the RF24 cloned repository @@ -95,9 +109,27 @@ to each library. Run the example ```shell - sudo python getting_started.py + python getting_started.py ``` or for python3 ```shell - sudo python3 getting_started.py + python3 getting_started.py ``` + + @note + @parblock + Running the python wrappers built with 'pigpio' or 'RPi' drivers requires `sudo` permission. + + If you are working in a python virtual environment (aka "venv"), then the + virtual environment's python executable must be specified after `sudo`. Otherwise, + `sudo` may invoke the system-installed python executable which can lead to errors. + + Assuming the python virtual environment is located in `~/venv`, use the following command: + ``` + sudo ~/venv/bin/python getting_started.py + ``` + This `sudo` advice must be observed even while the virtual environment is activated. + + See more information about python virtual environments in the + [python documentation](https://docs.python.org/3/library/venv.html). + @endparblock diff --git a/examples_linux/acknowledgementPayloads.cpp b/examples_linux/acknowledgementPayloads.cpp index 2138c19a0..1c784548a 100644 --- a/examples_linux/acknowledgementPayloads.cpp +++ b/examples_linux/acknowledgementPayloads.cpp @@ -36,7 +36,7 @@ using namespace std; RF24 radio(CE_PIN, CSN_PIN); /****************** Linux (BBB,x86,etc) ***********************/ // See http://nRF24.github.io/RF24/pages.html for more information on usage -// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA +// See https://github.com/eclipse/mraa/ for more information on MRAA // See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV // For this example, we'll be using a payload containing diff --git a/examples_linux/acknowledgement_payloads.py b/examples_linux/acknowledgement_payloads.py index b0afb3741..8a2bf9a8a 100644 --- a/examples_linux/acknowledgement_payloads.py +++ b/examples_linux/acknowledgement_payloads.py @@ -3,30 +3,14 @@ with Acknowledgement (ACK) payloads attached to ACK packets. This example was written to be used on 2 devices acting as 'nodes'. + +See documentation at https://nRF24.github.io/RF24 """ -import sys -import argparse -import time -from RF24 import RF24, RF24_PA_LOW +import time +from RF24 import RF24, RF24_PA_LOW, RF24_DRIVER -parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter -) -parser.add_argument( - "-n", - "--node", - type=int, - choices=range(2), - help="the identifying radio number (or node ID number)", -) -parser.add_argument( - "-r", - "--role", - type=int, - choices=range(2), - help="'1' specifies the TX role. '0' specifies the RX role.", -) +print(__file__) # print example name ########### USER CONFIGURATION ########### # See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md @@ -35,14 +19,50 @@ # their own pin numbering # CS Pin addresses the SPI bus number at /dev/spidev. # ie: RF24 radio(, *10+); spidev1.0 is 10, spidev1.1 is 11 etc.. -CSN_PIN = 0 # connected to GPIO8 -CE_PIN = 22 # connected to GPIO22 +CSN_PIN = 0 # GPIO8 aka CE0 on SPI bus 0: /dev/spidev0.0 +if RF24_DRIVER == "MRAA": + CE_PIN = 15 # for GPIO22 +elif RF24_DRIVER == "wiringPi": + CE_PIN = 3 # for GPIO22 +else: + CE_PIN = 22 radio = RF24(CE_PIN, CSN_PIN) -################## Linux (BBB,x86,etc) ######################### -# See http://nRF24.github.io/RF24/pages.html for more information on usage -# See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA -# See https://www.kernel.org/doc/Documentation/spi/spidev for more -# information on SPIDEV + +# initialize the nRF24L01 on the spi bus +if not radio.begin(): + raise RuntimeError("radio hardware is not responding") + +# For this example, we will use different addresses +# An address need to be a buffer protocol object (bytearray) +address = [b"1Node", b"2Node"] +# It is very helpful to think of an address as a path instead of as +# an identifying device destination + +radio_number = bool( + int(input("Which radio is this? Enter '0' or '1'. Defaults to '0' ") or 0) +) + +# ACK payloads are dynamically sized. +radio.enableDynamicPayloads() # to use ACK payloads + +# to enable the custom ACK payload feature +radio.enableAckPayload() + +# set the Power Amplifier level to -12 dBm since this test example is +# usually run with nRF24L01 transceivers in close proximity of each other +radio.setPALevel(RF24_PA_LOW) # RF24_PA_MAX is default + +# set the TX address of the RX node into the TX pipe +radio.openWritingPipe(address[radio_number]) # always uses pipe 0 + +# set the RX address of the TX node into a RX pipe +radio.openReadingPipe(1, address[not radio_number]) # using pipe 1 + +# for debugging, we have 2 options that print a large block of details +# (smaller) function that prints raw register values +# radio.printDetails() +# (larger) function that prints human readable data +# radio.printPrettyDetails() # using the python keyword global is bad practice. Instead we'll use a # 1 item list to store our integer number for the payloads' counter @@ -166,63 +186,11 @@ def set_role() -> bool: if __name__ == "__main__": - - args = parser.parse_args() # parse any CLI args - - # initialize the nRF24L01 on the spi bus - if not radio.begin(): - raise RuntimeError("radio hardware is not responding") - - # For this example, we will use different addresses - # An address need to be a buffer protocol object (bytearray) - address = [b"1Node", b"2Node"] - # It is very helpful to think of an address as a path instead of as - # an identifying device destination - - print(sys.argv[0]) # print example name - - # to use different addresses on a pair of radios, we need a variable to - # uniquely identify which address this radio will use to transmit - # 0 uses address[0] to transmit, 1 uses address[1] to transmit - radio_number = args.node # uses default value from `parser` - if args.node is None: # if '--node' arg wasn't specified - radio_number = bool( - int(input("Which radio is this? Enter '0' or '1'. Defaults to '0' ") or 0) - ) - - # ACK payloads are dynamically sized. - radio.enableDynamicPayloads() # to use ACK payloads - - # to enable the custom ACK payload feature - radio.enableAckPayload() - - # set the Power Amplifier level to -12 dBm since this test example is - # usually run with nRF24L01 transceivers in close proximity of each other - radio.setPALevel(RF24_PA_LOW) # RF24_PA_MAX is default - - # set the TX address of the RX node into the TX pipe - radio.openWritingPipe(address[radio_number]) # always uses pipe 0 - - # set the RX address of the TX node into a RX pipe - radio.openReadingPipe(1, address[not radio_number]) # using pipe 1 - - # for debugging, we have 2 options that print a large block of details - # (smaller) function that prints raw register values - # radio.printDetails() - # (larger) function that prints human readable data - # radio.printPrettyDetails() - try: - if args.role is None: # if not specified with CLI arg '-r' - while set_role(): - pass # continue example until 'Q' is entered - else: # if role was set using CLI args - # run role once and exit - if bool(args.role): - master() - else: - slave() + while set_role(): + pass # continue example until 'Q' is entered except KeyboardInterrupt: print(" Keyboard Interrupt detected. Exiting...") radio.powerDown() - sys.exit() +else: + print(" Run slave() on receiver\n Run master() on transmitter") diff --git a/examples_linux/getting_started.py b/examples_linux/getting_started.py index 52199bbf8..6701b88d8 100644 --- a/examples_linux/getting_started.py +++ b/examples_linux/getting_started.py @@ -1,32 +1,15 @@ """ A simple example of sending data from 1 nRF24L01 transceiver to another. This example was written to be used on 2 devices acting as 'nodes'. + +See documentation at https://nRF24.github.io/RF24 """ -import sys -import argparse + import time import struct -from RF24 import RF24, RF24_PA_LOW - - -parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter -) -parser.add_argument( - "-n", - "--node", - type=int, - choices=range(2), - help="the identifying radio number (or node ID number)", -) -parser.add_argument( - "-r", - "--role", - type=int, - choices=range(2), - help="'1' specifies the TX role. '0' specifies the RX role.", -) +from RF24 import RF24, RF24_PA_LOW, RF24_DRIVER +print(__file__) # print example name ########### USER CONFIGURATION ########### # See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md @@ -35,14 +18,53 @@ # their own pin numbering # CS Pin addresses the SPI bus number at /dev/spidev. # ie: RF24 radio(, *10+); spidev1.0 is 10, spidev1.1 is 11 etc.. -CSN_PIN = 0 # connected to GPIO8 -CE_PIN = 22 # connected to GPIO22 +CSN_PIN = 0 # GPIO8 aka CE0 on SPI bus 0: /dev/spidev0.0 +if RF24_DRIVER == "MRAA": + CE_PIN = 15 # for GPIO22 +elif RF24_DRIVER == "wiringPi": + CE_PIN = 3 # for GPIO22 +else: + CE_PIN = 22 radio = RF24(CE_PIN, CSN_PIN) -################## Linux (BBB,x86,etc) ######################### -# See http://nRF24.github.io/RF24/pages.html for more information on usage -# See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA -# See https://www.kernel.org/doc/Documentation/spi/spidev for more -# information on SPIDEV + +# initialize the nRF24L01 on the spi bus +if not radio.begin(): + raise RuntimeError("radio hardware is not responding") + +# For this example, we will use different addresses +# An address need to be a buffer protocol object (bytearray) +address = [b"1Node", b"2Node"] +# It is very helpful to think of an address as a path instead of as +# an identifying device destination + + +# to use different addresses on a pair of radios, we need a variable to +# uniquely identify which address this radio will use to transmit +# 0 uses address[0] to transmit, 1 uses address[1] to transmit +radio_number = bool( + int(input("Which radio is this? Enter '0' or '1'. Defaults to '0' ") or 0) +) + +# set the Power Amplifier level to -12 dBm since this test example is +# usually run with nRF24L01 transceivers in close proximity of each other +radio.setPALevel(RF24_PA_LOW) # RF24_PA_MAX is default + +# set the TX address of the RX node into the TX pipe +radio.openWritingPipe(address[radio_number]) # always uses pipe 0 + +# set the RX address of the TX node into a RX pipe +radio.openReadingPipe(1, address[not radio_number]) # using pipe 1 + +# To save time during transmission, we'll set the payload size to be only +# what we need. A float value occupies 4 bytes in memory using +# struct.pack(); "f" means an unsigned float +radio.payloadSize = struct.calcsize("f") + +# for debugging, we have 2 options that print a large block of details +# (smaller) function that prints raw register values +# radio.printDetails() +# (larger) function that prints human readable data +# radio.printPrettyDetails() # using the python keyword global is bad practice. Instead we'll use a 1 item # list to store our float number for the payloads sent/received @@ -137,61 +159,11 @@ def set_role() -> bool: if __name__ == "__main__": - - args = parser.parse_args() # parse any CLI args - - # initialize the nRF24L01 on the spi bus - if not radio.begin(): - raise RuntimeError("radio hardware is not responding") - - # For this example, we will use different addresses - # An address need to be a buffer protocol object (bytearray) - address = [b"1Node", b"2Node"] - # It is very helpful to think of an address as a path instead of as - # an identifying device destination - - print(sys.argv[0]) # print example name - - # to use different addresses on a pair of radios, we need a variable to - # uniquely identify which address this radio will use to transmit - # 0 uses address[0] to transmit, 1 uses address[1] to transmit - radio_number = args.node # uses default value from `parser` - if args.node is None: # if '--node' arg wasn't specified - radio_number = bool( - int(input("Which radio is this? Enter '0' or '1'. Defaults to '0' ") or 0) - ) - - # set the Power Amplifier level to -12 dBm since this test example is - # usually run with nRF24L01 transceivers in close proximity of each other - radio.setPALevel(RF24_PA_LOW) # RF24_PA_MAX is default - - # set the TX address of the RX node into the TX pipe - radio.openWritingPipe(address[radio_number]) # always uses pipe 0 - - # set the RX address of the TX node into a RX pipe - radio.openReadingPipe(1, address[not radio_number]) # using pipe 1 - - # To save time during transmission, we'll set the payload size to be only - # what we need. A float value occupies 4 bytes in memory using - # struct.pack(); "f" means an unsigned float - radio.payloadSize = struct.calcsize("f") - - # for debugging, we have 2 options that print a large block of details - # (smaller) function that prints raw register values - # radio.printDetails() - # (larger) function that prints human readable data - # radio.printPrettyDetails() - try: - if args.role is None: # if not specified with CLI arg '-r' - while set_role(): - pass # continue example until 'Q' is entered - else: # if role was set using CLI args - # run role once and exit - if bool(args.role): - master() - else: - slave() + while set_role(): + pass # continue example until 'Q' is entered except KeyboardInterrupt: print(" Keyboard Interrupt detected. Powering down radio.") radio.powerDown() +else: + print(" Run slave() on receiver\n Run master() on transmitter") diff --git a/examples_linux/gettingstarted.cpp b/examples_linux/gettingstarted.cpp index 88991177c..c5916a390 100644 --- a/examples_linux/gettingstarted.cpp +++ b/examples_linux/gettingstarted.cpp @@ -35,7 +35,7 @@ using namespace std; RF24 radio(CE_PIN, CSN_PIN); /****************** Linux (BBB,x86,etc) ***********************/ // See http://nRF24.github.io/RF24/pages.html for more information on usage -// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA +// See https://github.com/eclipse/mraa/ for more information on MRAA // See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV // For this example, we'll be using a payload containing diff --git a/examples_linux/interruptConfigure.cpp b/examples_linux/interruptConfigure.cpp index f032e163a..916868465 100644 --- a/examples_linux/interruptConfigure.cpp +++ b/examples_linux/interruptConfigure.cpp @@ -44,7 +44,7 @@ volatile bool got_interrupt = false; // used to signify that the event started RF24 radio(CE_PIN, CSN_PIN); /****************** Linux (BBB,x86,etc) ***********************/ // See http://nRF24.github.io/RF24/pages.html for more information on usage -// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA +// See https://github.com/eclipse/mraa/ for more information on MRAA // See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV // For this example, we'll be using a payload containing diff --git a/examples_linux/interrupt_configure.py b/examples_linux/interrupt_configure.py index 9c635db49..b42930a6f 100644 --- a/examples_linux/interrupt_configure.py +++ b/examples_linux/interrupt_configure.py @@ -1,35 +1,34 @@ """ -This example uses Acknowledgement (ACK) payloads attached to ACK packets to -demonstrate how the nRF24L01's IRQ (Interrupt Request) pin can be -configured to detect when data is received, or when data has transmitted -successfully, or when data has failed to transmit. +Simple example of detecting (and verifying) the IRQ (interrupt) pin on the +nRF24L01 -This example was written to be used on 2 devices acting as "nodes". +See documentation at https://nRF24.github.io/RF24 """ -import sys -import argparse -import time -import RPi.GPIO as GPIO -from RF24 import RF24, RF24_PA_LOW +import time +from RF24 import RF24, RF24_PA_LOW, RF24_DRIVER + +try: + import gpiod + from gpiod.line import Edge +except ImportError as exc: + raise ImportError( + "This script requires gpiod installed for observing the IRQ pin. Please run\n" + "\n pip install gpiod\n\nMore details at https://pypi.org/project/gpiod/" + ) from exc + +try: # try RPi5 gpio chip first + chip_path = "/dev/gpiochip4" + chip = gpiod.Chip(chip_path) +except FileNotFoundError: # fall back to gpio chip for RPi4 or older + chip_path = "/dev/gpiochip0" + chip = gpiod.Chip(chip_path) +finally: + print(__file__) # print example name + # print gpio chip info + info = chip.get_info() + print(f"Using {info.name} [{info.label}] ({info.num_lines} lines)") -parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter -) -parser.add_argument( - "-n", - "--node", - type=int, - choices=range(2), - help="the identifying radio number (or node ID number)", -) -parser.add_argument( - "-r", - "--role", - type=int, - choices=range(2), - help="'1' specifies the TX role. '0' specifies the RX role.", -) ########### USER CONFIGURATION ########### # See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md @@ -38,17 +37,55 @@ # their own pin numbering # CS Pin addresses the SPI bus number at /dev/spidev. # ie: RF24 radio(, *10+); spidev1.0 is 10, spidev1.1 is 11 etc.. -CSN_PIN = 0 # connected to GPIO8 -CE_PIN = 22 # connected to GPIO22 +CSN_PIN = 0 # GPIO8 aka CE0 on SPI bus 0: /dev/spidev0.0 +if RF24_DRIVER == "MRAA": + CE_PIN = 15 # for GPIO22 +elif RF24_DRIVER == "wiringPi": + CE_PIN = 3 # for GPIO22 +else: + CE_PIN = 22 radio = RF24(CE_PIN, CSN_PIN) -################## Linux (BBB,x86,etc) ######################### -# See http://nRF24.github.io/RF24/pages.html for more information on usage -# See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA -# See https://www.kernel.org/doc/Documentation/spi/spidev for more -# information on SPIDEV # select your digital input pin that's connected to the IRQ pin on the nRF24L01 -IRQ_PIN = 12 +IRQ_PIN = 24 + +# For this example, we will use different addresses +# An address need to be a buffer protocol object (bytearray) +address = [b"1Node", b"2Node"] +# It is very helpful to think of an address as a path instead of as +# an identifying device destination + +# to use different addresses on a pair of radios, we need a variable to +# uniquely identify which address this radio will use to transmit +# 0 uses address[0] to transmit, 1 uses address[1] to transmit +radio_number = bool( + int(input("Which radio is this? Enter '0' or '1'. Defaults to '0' ") or 0) +) + +# initialize the nRF24L01 on the spi bus +if not radio.begin(): + raise OSError("nRF24L01 hardware isn't responding") + +# this example uses the ACK payload to trigger the IRQ pin active for +# the "on data received" event +radio.enableDynamicPayloads() # ACK payloads are dynamically sized +radio.enableAckPayload() # enable ACK payloads + +# set the Power Amplifier level to -12 dBm since this test example is +# usually run with nRF24L01 transceivers in close proximity of each other +radio.setPALevel(RF24_PA_LOW) # RF24_PA_MAX is default + +# set the TX address of the RX node into the TX pipe +radio.openWritingPipe(address[radio_number]) # always uses pipe 0 + +# set the RX address of the TX node into a RX pipe +radio.openReadingPipe(1, address[not radio_number]) # using pipe 1 + +# for debugging, we have 2 options that print a large block of details +# (smaller) function that prints raw register values +# radio.printDetails() +# (larger) function that prints human readable data +# radio.printPrettyDetails() # For this example, we'll be using a payload containing # a string that changes on every transmission. (successful or not) @@ -58,61 +95,40 @@ ack_payloads = (b"Yak ", b"Back", b" ACK") -def interrupt_handler(channel): +def interrupt_handler(): """This function is called when IRQ pin is detected active LOW""" - print("IRQ pin", channel, "went active LOW.") - tx_ds, tx_df, rx_dr = radio.whatHappened() # get IRQ status flags - if tx_df: - radio.flush_tx() + print("\tIRQ pin went active LOW.") + tx_ds, tx_df, rx_dr = radio.whatHappened() # update IRQ status flags print(f"\ttx_ds: {tx_ds}, tx_df: {tx_df}, rx_dr: {rx_dr}") if pl_iterator[0] == 0: print(" 'data ready' event test", ("passed" if rx_dr else "failed")) elif pl_iterator[0] == 1: print(" 'data sent' event test", ("passed" if tx_ds else "failed")) - elif pl_iterator[0] == 3: + elif pl_iterator[0] == 2: print(" 'data fail' event test", ("passed" if tx_df else "failed")) # setup IRQ GPIO pin -GPIO.setmode(GPIO.BCM) -GPIO.setup(IRQ_PIN, GPIO.IN, pull_up_down=GPIO.PUD_UP) -GPIO.add_event_detect(IRQ_PIN, GPIO.FALLING, callback=interrupt_handler) -# IMPORTANT: do not call radio.available() before calling -# radio.whatHappened() when the interruptHandler() is triggered by the -# IRQ pin FALLING event. According to the datasheet, the pipe information -# is unreliable during the IRQ pin FALLING transition. - - -def _ping_n_wait(pl_iter): - """private function to ping RX node and wait for IRQ pin to be handled - - :param int pl_iter: The index of the buffer in `tx_payloads` tuple to - send. This number is also used to determine if event test was - successful or not. - """ - # set pl_iterator[0] so interrupt_handler() can determine if test was - # successful or not - pl_iterator[0] = pl_iter - # the following False parameter means we're expecting an ACK packet - radio.startFastWrite(tx_payloads[pl_iter], False) - time.sleep(0.1) # wait 100 ms for interrupt_handler() to complete - +irq_line = gpiod.request_lines( + path=chip_path, + consumer="RF24_interrupt_py-example", # optional + config={IRQ_PIN: gpiod.LineSettings(edge_detection=Edge.FALLING)}, +) -def print_rx_fifo(pl_size: int): - """Flush RX FIFO by printing all available payloads with 1 buffer - :param int pl_size: the expected size of each payload +def _wait_for_irq(timeout: float = 5): + """Wait till IRQ_PIN goes active (LOW). + IRQ pin is LOW when activated. Otherwise it is always HIGH """ - if radio.rxFifoFull(): - # all 3 payloads received were 5 bytes each, and RX FIFO is full - # so, fetching 15 bytes from the RX FIFO also flushes RX FIFO - print("Complete RX FIFO:", radio.read(pl_size * 3).decode("utf-8")) - else: - buffer = bytearray() - while radio.available(): - buffer += radio.read(pl_size) - if buffer: # if any payloads were read from the RX FIFO - print("Complete RX FIFO:", buffer.decode("utf-8")) + # wait up to ``timeout`` seconds for event to be detected. + if not irq_line.wait_edge_events(timeout): + print(f"\tInterrupt event not detected for {timeout} seconds!") + return False + # read event from kernel buffer + for event in irq_line.read_edge_events(): + if event.line_offset == IRQ_PIN and event.event_type is event.Type.FALLING_EDGE: + return True + return False def master(): @@ -129,65 +145,70 @@ def master(): # on data ready test print("\nConfiguring IRQ pin to only ignore 'on data sent' event") radio.maskIRQ(True, False, False) # args = tx_ds, tx_df, rx_dr - print(" Pinging slave node for an ACK payload...", end=" ") - _ping_n_wait(0) + print(" Pinging slave node for an ACK payload...") + pl_iterator[0] = 0 + radio.startFastWrite(tx_payloads[0], False) # False means expecting an ACK + if _wait_for_irq(): + interrupt_handler() # on "data sent" test print("\nConfiguring IRQ pin to only ignore 'on data ready' event") radio.maskIRQ(False, False, True) # args = tx_ds, tx_df, rx_dr - print(" Pinging slave node again... ", end=" ") - _ping_n_wait(1) + print(" Pinging slave node again...") + pl_iterator[0] = 1 + radio.startFastWrite(tx_payloads[1], False) # False means expecting an ACK + if _wait_for_irq(): + interrupt_handler() - # trigger slave node to stopListening() by filling slave node's RX FIFO + # trigger slave node to exit by filling the slave node's RX FIFO print("\nSending one extra payload to fill RX FIFO on slave node.") - radio.maskIRQ(1, 1, 1) # disable IRQ pin for this step + print("Disabling IRQ pin for all events.") + radio.maskIRQ(True, True, True) # args = tx_ds, tx_df, rx_dr if radio.write(tx_payloads[2]): - # when send_only parameter is True, send() ignores RX FIFO usage - if radio.rxFifoFull(): - print("RX node's FIFO is full; it is not listening any more") - else: - print( - "Transmission successful, but the RX node might still be listening." - ) + print("Slave node should not be listening anymore.") else: - radio.flush_tx() - print("Transmission failed or timed out. Continuing anyway.") + print("Slave node was unresponsive.") # on "data fail" test print("\nConfiguring IRQ pin to go active for all events.") radio.maskIRQ(False, False, False) # args = tx_ds, tx_df, rx_dr - print(" Sending a ping to inactive slave node...", end=" ") - _ping_n_wait(3) - - # CE pin is still HIGH which consumes more power. Example is now idling so... - radio.stopListening() # ensure CE pin is LOW - # stopListening() also calls flush_tx() when ACK payloads are enabled - - print_rx_fifo(len(ack_payloads[0])) # empty RX FIFO - - -def slave(timeout: int = 6): - """Only listen for 3 payload from the master node - - :param int timeout: The number of seconds to wait (with no transmission) - until exiting function. - """ - pl_iterator[0] = 0 # reset this to indicate event is a 'data_ready' event + print(" Sending a ping to inactive slave node...") + radio.flush_tx() # just in case any previous tests failed + pl_iterator[0] = 2 + radio.startFastWrite(tx_payloads[3], False) # False means expecting an ACK + if _wait_for_irq(): + interrupt_handler() + radio.flush_tx() # flush artifact payload in TX FIFO from last test + # all 3 ACK payloads received were 4 bytes each, and RX FIFO is full + # so, fetching 12 bytes from the RX FIFO also flushes RX FIFO + print("\nComplete RX FIFO:", radio.read(12)) + + +def slave(timeout=6): # will listen for 6 seconds before timing out + """Only listen for 3 payload from the master node""" + # the "data ready" event will trigger in RX mode + # the "data sent" or "data fail" events will trigger when we + # receive with ACK payloads enabled (& loaded in TX FIFO) + print("\nDisabling IRQ pin for all events.") + radio.maskIRQ(True, True, True) # args = tx_ds, tx_df, rx_dr # setup radio to receive pings, fill TX FIFO with ACK payloads radio.writeAckPayload(1, ack_payloads[0]) radio.writeAckPayload(1, ack_payloads[1]) radio.writeAckPayload(1, ack_payloads[2]) - radio.startListening() # start listening & clear status flags + radio.startListening() # start listening & clear irq_dr flag start_timer = time.monotonic() # start timer now while not radio.rxFifoFull() and time.monotonic() - start_timer < timeout: # if RX FIFO is not full and timeout is not reached, then keep waiting pass - time.sleep(0.1) # wait for last ACK payload to transmit + time.sleep(0.5) # wait for last ACK payload to transmit radio.stopListening() # put radio in TX mode & discard any ACK payloads - print_rx_fifo(len(tx_payloads[0])) + if radio.available(): # if RX FIFO is not empty (timeout did not occur) + # all 3 payloads received were 5 bytes each, and RX FIFO is full + # so, fetching 15 bytes from the RX FIFO also flushes RX FIFO + print("Complete RX FIFO:", radio.read(15)) -def set_role() -> bool: +def set_role(): """Set the role using stdin stream. Timeout arg for slave() can be specified using a space delimiter (e.g. 'R 10' calls `slave(10)`) @@ -197,6 +218,7 @@ def set_role() -> bool: """ user_input = ( input( + f"Make sure the IRQ pin is connected to the GPIO{IRQ_PIN}\n" "*** Enter 'R' for receiver role.\n" "*** Enter 'T' for transmitter role.\n" "*** Enter 'Q' to quit example.\n" @@ -205,10 +227,7 @@ def set_role() -> bool: ) user_input = user_input.split() if user_input[0].upper().startswith("R"): - if len(user_input) > 1: - slave(int(user_input[1])) - else: - slave() + slave(*[int(x) for x in user_input[1:2]]) return True if user_input[0].upper().startswith("T"): master() @@ -221,63 +240,16 @@ def set_role() -> bool: if __name__ == "__main__": - - args = parser.parse_args() # parse any CLI args - - # initialize the nRF24L01 on the spi bus - if not radio.begin(): - raise RuntimeError("radio hardware is not responding") - - # For this example, we will use different addresses - # An address need to be a buffer protocol object (bytearray) - address = [b"1Node", b"2Node"] - # It is very helpful to think of an address as a path instead of as - # an identifying device destination - - print(sys.argv[0]) # print example name - - # to use different addresses on a pair of radios, we need a variable to - # uniquely identify which address this radio will use to transmit - # 0 uses address[0] to transmit, 1 uses address[1] to transmit - radio_number = args.node # uses default value from `parser` - if args.node is None: # if '--node' arg wasn't specified - radio_number = bool( - int(input("Which radio is this? Enter '0' or '1'. Defaults to '0' ") or 0) - ) - - # set the Power Amplifier level to -12 dBm since this test example is - # usually run with nRF24L01 transceivers in close proximity of each other - radio.setPALevel(RF24_PA_LOW) # RF24_PA_MAX is default - - # ACK payloads are dynamically sized. - radio.enableDynamicPayloads() # to use ACK payloads - - # this example uses the ACK payload to trigger the IRQ pin active for - # the "on data received" event - radio.enableAckPayload() # enable ACK payloads - - # set the TX address of the RX node into the TX pipe - radio.openWritingPipe(address[radio_number]) # always uses pipe 0 - - # set the RX address of the TX node into a RX pipe - radio.openReadingPipe(1, address[not radio_number]) # using pipe 1 - - # for debugging, we have 2 options that print a large block of details - # (smaller) function that prints raw register values - # radio.printDetails() - # (larger) function that prints human readable data - # radio.printPrettyDetails() - try: - if args.role is None: # if not specified with CLI arg '-r' - while set_role(): - pass # continue example until 'Q' is entered - else: # if role was set using CLI args - # run role once and exit - if bool(args.role): - master() - else: - slave() + while set_role(): + pass # continue example until 'Q' is entered except KeyboardInterrupt: - print(" Keyboard Interrupt detected. Powering down radio.") + print(" Keyboard Interrupt detected. Exiting...") radio.powerDown() +else: + print( + f"Make sure the IRQ pin is connected to the GPIO{IRQ_PIN}", + "Run slave() on receiver", + "Run master() on transmitter", + sep="\n", + ) diff --git a/examples_linux/interrupts/transfer_interrupt.cpp b/examples_linux/interrupts/transfer_interrupt.cpp index 46387bf2d..b634c6807 100644 --- a/examples_linux/interrupts/transfer_interrupt.cpp +++ b/examples_linux/interrupts/transfer_interrupt.cpp @@ -51,7 +51,7 @@ RF24 radio(RPI_V2_GPIO_P1_15, RPI_V2_GPIO_P1_24, BCM2835_SPI_SPEED_8MHZ); /****************** Linux (BBB,x86,etc) ***********************/ // See http://tmrh20.github.io/RF24/pages.html for more information on usage -// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA +// See https://github.com/eclipse/mraa/ for more information on MRAA // See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV // Setup for ARM(Linux) devices like BBB using spidev (default is "/dev/spidev1.0" ) diff --git a/examples_linux/manualAcknowledgements.cpp b/examples_linux/manualAcknowledgements.cpp index 83bf77baf..a10280b15 100644 --- a/examples_linux/manualAcknowledgements.cpp +++ b/examples_linux/manualAcknowledgements.cpp @@ -41,7 +41,7 @@ using namespace std; RF24 radio(CE_PIN, CSN_PIN); /****************** Linux (BBB,x86,etc) ***********************/ // See http://nRF24.github.io/RF24/pages.html for more information on usage -// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA +// See https://github.com/eclipse/mraa/ for more information on MRAA // See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV // For this example, we'll be using a payload containing diff --git a/examples_linux/manual_acknowledgements.py b/examples_linux/manual_acknowledgements.py index 3ce24de90..d310626b9 100644 --- a/examples_linux/manual_acknowledgements.py +++ b/examples_linux/manual_acknowledgements.py @@ -8,30 +8,15 @@ transmission. This example was written to be used on 2 devices acting as 'nodes'. + +See documentation at https://nRF24.github.io/RF24 """ -import sys -import argparse + import time -from RF24 import RF24, RF24_PA_LOW +from RF24 import RF24, RF24_PA_LOW, RF24_DRIVER -parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter -) -parser.add_argument( - "-n", - "--node", - type=int, - choices=range(2), - help="the identifying radio number (or node ID number)", -) -parser.add_argument( - "-r", - "--role", - type=int, - choices=range(2), - help="'1' specifies the TX role. '0' specifies the RX role.", -) +print(__file__) # print example name ########### USER CONFIGURATION ########### # See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md @@ -40,14 +25,52 @@ # their own pin numbering # CS Pin addresses the SPI bus number at /dev/spidev. # ie: RF24 radio(, *10+); spidev1.0 is 10, spidev1.1 is 11 etc.. -CSN_PIN = 0 # connected to GPIO8 -CE_PIN = 22 # connected to GPIO22 +CSN_PIN = 0 # GPIO8 aka CE0 on SPI bus 0: /dev/spidev0.0 +if RF24_DRIVER == "MRAA": + CE_PIN = 15 # for GPIO22 +elif RF24_DRIVER == "wiringPi": + CE_PIN = 3 # for GPIO22 +else: + CE_PIN = 22 radio = RF24(CE_PIN, CSN_PIN) -################## Linux (BBB,x86,etc) ######################### -# See http://nRF24.github.io/RF24/pages.html for more information on usage -# See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA -# See https://www.kernel.org/doc/Documentation/spi/spidev for more -# information on SPIDEV + +# initialize the nRF24L01 on the spi bus +if not radio.begin(): + raise RuntimeError("radio hardware is not responding") + +# For this example, we will use different addresses +# An address need to be a buffer protocol object (bytearray) +address = [b"1Node", b"2Node"] +# It is very helpful to think of an address as a path instead of as +# an identifying device destination + +# to use different addresses on a pair of radios, we need a variable to +# uniquely identify which address this radio will use to transmit +# 0 uses address[0] to transmit, 1 uses address[1] to transmit +radio_number = bool( + int(input("Which radio is this? Enter '0' or '1'. Defaults to '0' ") or 0) +) + +# set the Power Amplifier level to -12 dBm since this test example is +# usually run with nRF24L01 transceivers in close proximity of each other +radio.setPALevel(RF24_PA_LOW) # RF24_PA_MAX is default + +# set the TX address of the RX node into the TX pipe +radio.openWritingPipe(address[radio_number]) # always uses pipe 0 + +# set the RX address of the TX node into a RX pipe +radio.openReadingPipe(1, address[not radio_number]) # using pipe 1 + +# To save time during transmission, we'll set the payload size to be only +# what we need. For this example, we'll be using a byte for the +# payload counter and 7 bytes for the payload message +radio.payloadSize = 8 + +# for debugging, we have 2 options that print a large block of details +# (smaller) function that prints raw register values +# radio.printDetails() +# (larger) function that prints human readable data +# radio.printPrettyDetails() # using the python keyword global is bad practice. Instead we'll use a 1 item # list to store our integer number for the payloads' counter @@ -180,61 +203,11 @@ def set_role() -> bool: if __name__ == "__main__": - - args = parser.parse_args() # parse any CLI args - - # initialize the nRF24L01 on the spi bus - if not radio.begin(): - raise RuntimeError("radio hardware is not responding") - - # For this example, we will use different addresses - # An address need to be a buffer protocol object (bytearray) - address = [b"1Node", b"2Node"] - # It is very helpful to think of an address as a path instead of as - # an identifying device destination - - print(sys.argv[0]) # print example name - - # to use different addresses on a pair of radios, we need a variable to - # uniquely identify which address this radio will use to transmit - # 0 uses address[0] to transmit, 1 uses address[1] to transmit - radio_number = args.node # uses default value from `parser` - if args.node is None: # if '--node' arg wasn't specified - radio_number = bool( - int(input("Which radio is this? Enter '0' or '1'. Defaults to '0' ") or 0) - ) - - # set the Power Amplifier level to -12 dBm since this test example is - # usually run with nRF24L01 transceivers in close proximity of each other - radio.setPALevel(RF24_PA_LOW) # RF24_PA_MAX is default - - # set the TX address of the RX node into the TX pipe - radio.openWritingPipe(address[radio_number]) # always uses pipe 0 - - # set the RX address of the TX node into a RX pipe - radio.openReadingPipe(1, address[not radio_number]) # using pipe 1 - - # To save time during transmission, we'll set the payload size to be only - # what we need. For this example, we'll be using a byte for the - # payload counter and 7 bytes for the payload message - radio.payloadSize = 8 - - # for debugging, we have 2 options that print a large block of details - # (smaller) function that prints raw register values - # radio.printDetails() - # (larger) function that prints human readable data - # radio.printPrettyDetails() - try: - if args.role is None: # if not specified with CLI arg '-r' - while set_role(): - pass # continue example until 'Q' is entered - else: # if role was set using CLI args - # run role once and exit - if bool(args.role): - master() - else: - slave() + while set_role(): + pass # continue example until 'Q' is entered except KeyboardInterrupt: print(" Keyboard Interrupt detected. Powering down radio.") radio.powerDown() +else: + print(" Run slave() on receiver\n Run master() on transmitter") diff --git a/examples_linux/multiceiverDemo.cpp b/examples_linux/multiceiverDemo.cpp index d46463c9b..9a405fa88 100644 --- a/examples_linux/multiceiverDemo.cpp +++ b/examples_linux/multiceiverDemo.cpp @@ -39,7 +39,7 @@ using namespace std; RF24 radio(CE_PIN, CSN_PIN); /****************** Linux (BBB,x86,etc) ***********************/ // See http://nRF24.github.io/RF24/pages.html for more information on usage -// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA +// See https://github.com/eclipse/mraa/ for more information on MRAA // See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV // For this example, we'll be using 6 addresses; 1 for each TX node diff --git a/examples_linux/multiceiver_demo.py b/examples_linux/multiceiver_demo.py index fd1040d9f..c0cc1496b 100644 --- a/examples_linux/multiceiver_demo.py +++ b/examples_linux/multiceiver_demo.py @@ -5,24 +5,15 @@ This example was written to be used on up to 6 devices acting as TX nodes & only 1 device acting as the RX node (that's a maximum of 7 devices). + +See documentation at https://nRF24.github.io/RF24 """ -import sys -import argparse + import time import struct -from RF24 import RF24, RF24_PA_LOW +from RF24 import RF24, RF24_PA_LOW, RF24_DRIVER - -parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter -) -parser.add_argument( - "-n", - "--node", - choices=("0", "1", "2", "3", "4", "5", "R", "r"), - help="the identifying node ID number for the TX role. " - "Use 'R' or 'r' to specify the RX role", -) +print(__file__) ########### USER CONFIGURATION ########### # See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md @@ -31,23 +22,41 @@ # their own pin numbering # CS Pin addresses the SPI bus number at /dev/spidev. # ie: RF24 radio(, *10+); spidev1.0 is 10, spidev1.1 is 11 etc.. -CSN_PIN = 0 # connected to GPIO8 -CE_PIN = 22 # connected to GPIO22 +CSN_PIN = 0 # GPIO8 aka CE0 on SPI bus 0: /dev/spidev0.0 +if RF24_DRIVER == "MRAA": + CE_PIN = 15 # for GPIO22 +elif RF24_DRIVER == "wiringPi": + CE_PIN = 3 # for GPIO22 +else: + CE_PIN = 22 radio = RF24(CE_PIN, CSN_PIN) -################## Linux (BBB,x86,etc) ######################### -# See http://nRF24.github.io/RF24/pages.html for more information on usage -# See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA -# See https://www.kernel.org/doc/Documentation/spi/spidev for more -# information on SPIDEV + +# initialize the nRF24L01 on the spi bus +if not radio.begin(): + raise RuntimeError("radio hardware is not responding") + +# set the Power Amplifier level to -12 dBm since this test example is +# usually run with nRF24L01 transceivers in close proximity of each other +radio.setPALevel(RF24_PA_LOW) # RF24_PA_MAX is default + +# To save time during transmission, we'll set the payload size to be only what +# we need. +# 2 int occupy 8 bytes in memory using struct.pack() +# "ii" means 2 unsigned integers +radio.payloadSize = struct.calcsize("ii") + +# for debugging, we have 2 options that print a large block of details +# radio.printDetails(); # (smaller) function that prints raw register values +# radio.printPrettyDetails(); # (larger) function that prints human readable data # setup the addresses for all transmitting radio nodes addresses = [ b"\x78" * 5, - b"\xF1\xB6\xB5\xB4\xB3", - b"\xCD\xB6\xB5\xB4\xB3", - b"\xA3\xB6\xB5\xB4\xB3", - b"\x0F\xB6\xB5\xB4\xB3", - b"\x05\xB6\xB5\xB4\xB3", + b"\xf1\xb6\xb5\xb4\xb3", + b"\xcd\xb6\xb5\xb4\xb3", + b"\xa3\xb6\xb5\xb4\xb3", + b"\x0f\xb6\xb5\xb4\xb3", + b"\x05\xb6\xb5\xb4\xb3", ] # It is very helpful to think of an address as a path instead of as # an identifying device destination @@ -157,39 +166,13 @@ def set_role() -> bool: if __name__ == "__main__": - - args = parser.parse_args() # parse any CLI args - - # initialize the nRF24L01 on the spi bus - if not radio.begin(): - raise RuntimeError("radio hardware is not responding") - - print(sys.argv[0]) # print example name - - # set the Power Amplifier level to -12 dBm since this test example is - # usually run with nRF24L01 transceivers in close proximity of each other - radio.setPALevel(RF24_PA_LOW) # RF24_PA_MAX is default - - # To save time during transmission, we'll set the payload size to be only what - # we need. - # 2 int occupy 8 bytes in memory using struct.pack() - # "ii" means 2 unsigned integers - radio.payloadSize = struct.calcsize("ii") - - # for debugging, we have 2 options that print a large block of details - # radio.printDetails(); # (smaller) function that prints raw register values - # radio.printPrettyDetails(); # (larger) function that prints human readable data - try: - if args.node is None: # if not specified with CLI arg '-n' - while set_role(): - pass # continue example until 'Q' is entered - else: # if role was set using CLI args - # run role once and exit - if args.node.isdigit(): - master(int(args.node)) - else: - slave() + while set_role(): + pass # continue example until 'Q' is entered except KeyboardInterrupt: print(" Keyboard Interrupt detected. Powering down radio.") radio.powerDown() +else: + print(" Run slave() on the receiver") + print(" Run master(node_number) on a transmitter") + print(" master()'s parameter, `node_number`, must be in range [0, 5]") diff --git a/examples_linux/ncurses/scanner_curses.cpp b/examples_linux/ncurses/scanner_curses.cpp index 282701980..51c05a319 100644 --- a/examples_linux/ncurses/scanner_curses.cpp +++ b/examples_linux/ncurses/scanner_curses.cpp @@ -42,7 +42,7 @@ using namespace std; RF24 radio(CE_PIN, CSN_PIN); /****************** Linux (BBB,x86,etc) ***********************/ // See http://nRF24.github.io/RF24/pages.html for more information on usage -// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA +// See https://github.com/eclipse/mraa/ for more information on MRAA // See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV // Channel info diff --git a/examples_linux/scanner.cpp b/examples_linux/scanner.cpp index 4f35f4fed..da921fb76 100644 --- a/examples_linux/scanner.cpp +++ b/examples_linux/scanner.cpp @@ -67,7 +67,7 @@ using namespace std; RF24 radio(CE_PIN, CSN_PIN); /****************** Linux (BBB,x86,etc) ***********************/ // See http://nRF24.github.io/RF24/pages.html for more information on usage -// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA +// See https://github.com/eclipse/mraa/ for more information on MRAA // See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV // Channel info diff --git a/examples_linux/scanner.py b/examples_linux/scanner.py index cd621f90f..bfe60728e 100644 --- a/examples_linux/scanner.py +++ b/examples_linux/scanner.py @@ -6,15 +6,19 @@ See documentation at https://nRF24.github.io/RF24 """ -# pylint: disable=no-member import curses import time from typing import List, Tuple, Any -from RF24 import RF24, RF24_1MBPS, RF24_2MBPS, RF24_250KBPS +from RF24 import RF24, RF24_1MBPS, RF24_2MBPS, RF24_250KBPS, RF24_DRIVER -CSN_PIN = 0 # connected to GPIO8 -CE_PIN = 22 # connected to GPIO22 +CSN_PIN = 0 # GPIO8 aka CE0 on SPI bus 0: /dev/spidev0.0 +if RF24_DRIVER == "MRAA": + CE_PIN = 15 # for GPIO22 +elif RF24_DRIVER == "wiringPi": + CE_PIN = 3 # for GPIO22 +else: + CE_PIN = 22 radio = RF24(CE_PIN, CSN_PIN) @@ -224,4 +228,4 @@ def main(): if __name__ == "__main__": main() else: - print("Enter 'main()' to run the program.") + print("Run 'main()' to run the program.") diff --git a/examples_linux/streamingData.cpp b/examples_linux/streamingData.cpp index f7b1c5432..e1a9b26ca 100644 --- a/examples_linux/streamingData.cpp +++ b/examples_linux/streamingData.cpp @@ -37,7 +37,7 @@ using namespace std; RF24 radio(CE_PIN, CSN_PIN); /****************** Linux (BBB,x86,etc) ***********************/ // See http://nRF24.github.io/RF24/pages.html for more information on usage -// See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA +// See https://github.com/eclipse/mraa/ for more information on MRAA // See https://www.kernel.org/doc/Documentation/spi/spidev for more information on SPIDEV // For this example, we'll be sending 32 payloads each containing diff --git a/examples_linux/streaming_data.py b/examples_linux/streaming_data.py index 0a339dab7..3d85484b5 100644 --- a/examples_linux/streaming_data.py +++ b/examples_linux/streaming_data.py @@ -2,30 +2,14 @@ A simple example of streaming data from 1 nRF24L01 transceiver to another. This example was written to be used on 2 devices acting as 'nodes'. + +See documentation at https://nRF24.github.io/RF24 """ -import sys -import argparse + import time -from RF24 import RF24, RF24_PA_LOW +from RF24 import RF24, RF24_PA_LOW, RF24_DRIVER - -parser = argparse.ArgumentParser( - description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter -) -parser.add_argument( - "-n", - "--node", - type=int, - choices=range(2), - help="the identifying radio number (or node ID number)", -) -parser.add_argument( - "-r", - "--role", - type=int, - choices=range(2), - help="'1' specifies the TX role. '0' specifies the RX role.", -) +print(__file__) # print example name ########### USER CONFIGURATION ########### # See https://github.com/TMRh20/RF24/blob/master/pyRF24/readme.md @@ -34,19 +18,57 @@ # their own pin numbering # CS Pin addresses the SPI bus number at /dev/spidev. # ie: RF24 radio(, *10+); spidev1.0 is 10, spidev1.1 is 11 etc.. -CSN_PIN = 0 # connected to GPIO8 -CE_PIN = 22 # connected to GPIO22 +CSN_PIN = 0 # GPIO8 aka CE0 on SPI bus 0: /dev/spidev0.0 +if RF24_DRIVER == "MRAA": + CE_PIN = 15 # for GPIO22 +elif RF24_DRIVER == "wiringPi": + CE_PIN = 3 # for GPIO22 +else: + CE_PIN = 22 radio = RF24(CE_PIN, CSN_PIN) -################## Linux (BBB,x86,etc) ######################### -# See http://nRF24.github.io/RF24/pages.html for more information on usage -# See http://iotdk.intel.com/docs/master/mraa/ for more information on MRAA -# See https://www.kernel.org/doc/Documentation/spi/spidev for more -# information on SPIDEV + +# initialize the nRF24L01 on the spi bus +if not radio.begin(): + raise RuntimeError("radio hardware is not responding") + +# For this example, we will use different addresses +# An address need to be a buffer protocol object (bytearray) +address = [b"1Node", b"2Node"] +# It is very helpful to think of an address as a path instead of as +# an identifying device destination + +# to use different addresses on a pair of radios, we need a variable to +# uniquely identify which address this radio will use to transmit +# 0 uses address[0] to transmit, 1 uses address[1] to transmit +radio_number = bool( + int(input("Which radio is this? Enter '0' or '1'. Defaults to '0' ") or 0) +) + +# set the Power Amplifier level to -12 dBm since this test example is +# usually run with nRF24L01 transceivers in close proximity of each other +radio.setPALevel(RF24_PA_LOW) # RF24_PA_MAX is default + +# set the TX address of the RX node into the TX pipe +radio.openWritingPipe(address[radio_number]) # always uses pipe 0 + +# set the RX address of the TX node into a RX pipe +radio.openReadingPipe(1, address[not radio_number]) # using pipe 1 + # Specify the number of bytes in the payload. This is also used to # specify the number of payloads in 1 stream of data SIZE = 32 # this is the default maximum payload size +# To save time during transmission, we'll set the payload size to be only +# what we need. For this example, we'll be using the default maximum 32 +radio.payloadSize = SIZE + +# for debugging, we have 2 options that print a large block of details +# (smaller) function that prints raw register values +# radio.printDetails() +# (larger) function that prints human readable data +# radio.printPrettyDetails() + def make_buffer(buf_iter: int) -> bytes: """Returns a dynamically created payloads @@ -92,7 +114,7 @@ def master(count: int = 1): end_timer = time.monotonic_ns() # end timer print( f"Time to transmit data = {(end_timer - start_timer) / 1000} us.", - f"Detected {failures} failures." + f"Detected {failures} failures.", ) @@ -119,7 +141,6 @@ def slave(timeout: int = 6): radio.stopListening() # put the radio in TX mode - def set_role() -> bool: """Set the role using stdin stream. Role args can be specified using space delimiters (e.g. 'R 10' calls `slave(10)` & 'T 3' calls `master(3)`) @@ -157,60 +178,11 @@ def set_role() -> bool: if __name__ == "__main__": - - args = parser.parse_args() # parse any CLI args - - # initialize the nRF24L01 on the spi bus - if not radio.begin(): - raise RuntimeError("radio hardware is not responding") - - # For this example, we will use different addresses - # An address need to be a buffer protocol object (bytearray) - address = [b"1Node", b"2Node"] - # It is very helpful to think of an address as a path instead of as - # an identifying device destination - - print(sys.argv[0]) # print example name - - # to use different addresses on a pair of radios, we need a variable to - # uniquely identify which address this radio will use to transmit - # 0 uses address[0] to transmit, 1 uses address[1] to transmit - radio_number = args.node # uses default value from `parser` - if args.node is None: # if '--node' arg wasn't specified - radio_number = bool( - int(input("Which radio is this? Enter '0' or '1'. Defaults to '0' ") or 0) - ) - - # set the Power Amplifier level to -12 dBm since this test example is - # usually run with nRF24L01 transceivers in close proximity of each other - radio.setPALevel(RF24_PA_LOW) # RF24_PA_MAX is default - - # set the TX address of the RX node into the TX pipe - radio.openWritingPipe(address[radio_number]) # always uses pipe 0 - - # set the RX address of the TX node into a RX pipe - radio.openReadingPipe(1, address[not radio_number]) # using pipe 1 - - # To save time during transmission, we'll set the payload size to be only - # what we need. For this example, we'll be using the default maximum 32 - radio.payloadSize = SIZE - - # for debugging, we have 2 options that print a large block of details - # (smaller) function that prints raw register values - # radio.printDetails() - # (larger) function that prints human readable data - # radio.printPrettyDetails() - try: - if args.role is None: # if not specified with CLI arg '-r' - while set_role(): - pass # continue example until 'Q' is entered - else: # if role was set using CLI args - # run role once and exit - if bool(args.role): - master() - else: - slave() + while set_role(): + pass # continue example until 'Q' is entered except KeyboardInterrupt: print(" Keyboard Interrupt detected. Powering down radio.") radio.powerDown() +else: + print(" Run slave() on receiver\n Run master() on transmitter") diff --git a/pyRF24/pyRF24.cpp b/pyRF24/pyRF24.cpp index b37f02886..eb6dc6199 100644 --- a/pyRF24/pyRF24.cpp +++ b/pyRF24/pyRF24.cpp @@ -151,6 +151,19 @@ BOOST_PYTHON_MEMBER_FUNCTION_OVERLOADS(txStandBy_wrap1, RF24::txStandBy, 0, 2) BOOST_PYTHON_MODULE(RF24) { + bp::scope().attr("RF24_DRIVER") = +#ifdef RF24_PIGPIO + "pigpio" +#elif defined(MRAA) + "MRAA" +#elif defined(RF24_RPi) + "RPi" +#elif defined(RF24_WIRINGPI) + "wiringPi" +#else + "SPIDEV" +#endif + ; #ifdef BCM2835_H bp::enum_("RPiGPIOPin")