diff --git a/docs/advanced_api.rst b/docs/advanced_api.rst index ade9d28..3db2d89 100644 --- a/docs/advanced_api.rst +++ b/docs/advanced_api.rst @@ -8,8 +8,7 @@ .. |update manually| replace:: Calling this does not execute an SPI transaction. It only exposes that latest data contained in the STATUS byte that's always returned from any other SPI transactions. Use the :py:func:`~circuitpython_nrf24l01.rf24.RF24.update()` - function to manually refresh this data when needed )especially after calling - :py:func:`~circuitpython_nrf24l01.rf24.RF24.clear_status_flags()`). + function to manually refresh this data when needed Advanced API ------------ @@ -141,7 +140,8 @@ irq_dr Pass ``data_recv`` |irq note| - |update manually| + |update manually| (especially after calling + :py:func:`~circuitpython_nrf24l01.rf24.RF24.clear_status_flags()`). irq_df ****************************** @@ -158,7 +158,8 @@ irq_df Pass ``data_fail`` |irq note| - |update manually| + |update manually| (especially after calling + :py:func:`~circuitpython_nrf24l01.rf24.RF24.clear_status_flags()`). irq_ds ****************************** @@ -175,7 +176,8 @@ irq_ds Pass ``data_sent`` |irq note| - |update manually| + |update manually| (especially after calling + :py:func:`~circuitpython_nrf24l01.rf24.RF24.clear_status_flags()`). clear_status_flags() ****************************** @@ -231,7 +233,8 @@ tx_full . - |update manually| + |update manually| (especially after calling + :py:func:`~circuitpython_nrf24l01.rf24.RF24.flush_tx()`). :returns: @@ -395,7 +398,8 @@ pipe . - |update manually| + |update manually| (especially after calling + :py:func:`~circuitpython_nrf24l01.rf24.RF24.flush_rx()`). :Returns: diff --git a/docs/examples.rst b/docs/examples.rst index 1475c19..af27e85 100644 --- a/docs/examples.rst +++ b/docs/examples.rst @@ -1,3 +1,5 @@ +nRF24L01 Features +================= Simple test ------------ @@ -11,53 +13,76 @@ Ensure your device works with this simple test. ACK Payloads Example -------------------- -This is a test to show how to use custom acknowledgment payloads. +This is a test to show how to use custom acknowledgment payloads. See also documentation on `ack` and `load_ack()`. .. literalinclude:: ../examples/nrf24l01_ack_payload_test.py :caption: examples/nrf24l01_ack_payload_test.py :linenos: -Stream Example +Multiceiver Example +-------------------- + +This example shows how use a group of 6 nRF24L01 transceivers to transmit to 1 nRF24L01 transceiver. `This technique is called "Multiceiver" in the nRF24L01 Specifications Sheet `_ + +.. note:: This example follows the diagram illistrated in `figure 12 of section 7.7 of the nRF24L01 Specifications Sheet `_ Please note that if `auto_ack` (on the base station) and `arc` (on the trnasmitting nodes) are disabled, then `figure 10 of section 7.7 of the nRF24L01 Specifications Sheet `_ would be a better illustration. + +.. hint:: A paraphrased note from the the nRF24L01 Specifications Sheet: + + *Only when a data pipe receives a complete packet can other data pipes begin to receive data. When multiple [nRF24L01]s are transmitting to [one nRF24L01], the* `ard` *can be used to skew the auto retransmission so that they only block each other once.* + + This basically means that it might help packets get received if the `ard` attribute is set to various values among multiple transmitting nRF24L01 transceivers. + +.. literalinclude:: ../examples/nrf24l01_multiceiver_test.py + :caption: examples/nrf24l01_multiceiver_test.py + :linenos: + +IRQ Pin Example --------------- -This is a test to show how to use the send() to transmit multiple payloads. +This is a test to show how to use nRF24L01's interrupt pin. Be aware that :py:func:`~circuitpython_nrf24l01.rf24.RF24.send()` clears all IRQ events on exit, so we use the non-blocking :py:func:`~circuitpython_nrf24l01.rf24.RF24.write()` instead. Also the `ack` attribute is enabled to trigger the :py:attr:`~circuitpython_nrf24l01.rf24.RF24.irq_dr` event when the master node receives ACK payloads. Simply put, this example is the most advanced example script (in this library), and it runs VERY quickly. -.. literalinclude:: ../examples/nrf24l01_stream_test.py - :caption: examples/nrf24l01_stream_test.py +.. literalinclude:: ../examples/nrf24l01_interrupt_test.py + :caption: examples/nrf24l01_interrupt_test.py :linenos: -Fake BLE Example ----------------- +Library-Specific Features +========================= -This is a test to show how to use the nRF24L01 as a BLE advertising beacon. +Stream Example +--------------- -.. literalinclude:: ../examples/nrf24l01_fake_ble_test.py - :caption: examples/nrf24l01_fake_ble_test.py +This is a test to show how to use the :py:func:`~circuitpython_nrf24l01.rf24.RF24.send()` to transmit multiple payloads with 1 function call. + +.. literalinclude:: ../examples/nrf24l01_stream_test.py + :caption: examples/nrf24l01_stream_test.py :linenos: Context Example --------------- -This is a test to show how to use "with" statements to manage multiple different nRF24L01 configurations on 1 transceiver. +This is a test to show how to use `with` blocks to manage multiple different nRF24L01 configurations on 1 transceiver. .. literalinclude:: ../examples/nrf24l01_context_test.py :caption: examples/nrf24l01_context_test.py :linenos: -Working with TMRh20's Arduino library -------------------------------------- +OTA compatibility +================= -This test is meant to prove compatibility with the popular Arduino library for the nRF24L01 by TMRh20 (available for install via the Arduino IDE's Library Manager). The following code has been designed/test with the TMRh20 library example named "GettingStarted_HandlingData.ino". If you changed the ``role`` variable in the TMRh20 sketch, you will have to adjust the addresses assigned to the pipes in this script. +Fake BLE Example +---------------- -.. literalinclude:: ../examples/nrf24l01_2arduino_handling_data.py - :caption: examples/nrf24l01_2arduino_handling_data.py +This is a test to show how to use the nRF24L01 as a BLE advertising beacon using the :py:class:`~circuitpython_nrf24l01.rf24.fake_ble.FakeBLE` class. + +.. literalinclude:: ../examples/nrf24l01_fake_ble_test.py + :caption: examples/nrf24l01_fake_ble_test.py :linenos: -IRQ Pin Example ---------------- +TMRh20's Arduino library +------------------------ -This is a test to show how to use nRF24L01's interrupt pin. Be aware that `send()` clears all IRQ events on exit, so we use the non-blocking `write()` instead. Also the `ack` attribute is enabled to trigger the :py:attr:`~circuitpython_nrf24l01.rf24.RF24.irq_dr` event when the master node receives ACK payloads. Simply put, this example is the most advanced example script (in this library), and it runs VERY quickly. +This test is meant to prove compatibility with the popular Arduino library for the nRF24L01 by TMRh20 (available for install via the Arduino IDE's Library Manager). The following code has been designed/test with the TMRh20 library example named `GettingStarted_HandlingData.ino `_. If you changed the ``role`` variable in the TMRh20 sketch, you will have to adjust the addresses assigned to the pipes in this script. -.. literalinclude:: ../examples/nrf24l01_interrupt_test.py - :caption: examples/nrf24l01_interrupt_test.py +.. literalinclude:: ../examples/nrf24l01_2arduino_handling_data.py + :caption: examples/nrf24l01_2arduino_handling_data.py :linenos: diff --git a/docs/greetings.rst b/docs/greetings.rst index 09b9308..5c13e58 100644 --- a/docs/greetings.rst +++ b/docs/greetings.rst @@ -25,8 +25,8 @@ Features currently supported * Mark a single payload for no acknowledgment (ACK) from the receiving nRF24L01 (see ``ask_no_ack`` parameter for `send()` and `write()` functions) * Invoke the "re-use the same payload" feature (for manually re-transmitting failed transmissions that remain in the TX FIFO buffer) -* Multiple payload transmissions with one function call (MUST read documentation on the - `send()` function) +* Multiple payload transmissions with one function call (see documentation on the + `send()` function and try out the `Stream example `_) * Context manager compatible for easily switching between different radio configurations using `with` blocks (not available in ``rf24_lite.py`` version) * Configure the interrupt (IRQ) pin to trigger (active low) on received, sent, and/or @@ -41,13 +41,9 @@ Features currently supported * Adjust the nRF24L01's frequency channel (2.4-2.525 GHz) * Adjust the nRF24L01's power amplifier level (0, -6, -12, or -18 dBm) * Adjust the nRF24L01's RF data rate (250kbps, 1Mbps, or 2Mbps) -* An nRF24L01 driven by this library can communicate with a nRF24L01 on an Arduino driven by the `TMRh20 RF24 library `_. See the `nrf24l01_2arduino_handling_data.py `_ example. +* An nRF24L01 driven by this library can communicate with a nRF24L01 on an Arduino driven by the `TMRh20 RF24 library `_. See the `nrf24l01_2arduino_handling_data.py `_ example. * fake BLE module for sending BLE beacon advertisments from the nRF24L01 as outlined by `Dmitry Grinberg in his write-up (including C source code) `_. - -Features currently unsupported ------------------------------- - -* as of yet, no [intended] implementation for Multiceiver mode (up to 6 TX nRF24L01 "talking" to 1 RX nRF24L01 simultaneously). Although this might be acheived easily using the "automatic retry delay" (`ard`) and "automatic retry count" (`arc`) attributes set accordingly (varyingly high), but this has not been tested. +* Multiceiver\ :sup:`TM` mode (up to 6 TX nRF24L01 "talking" to 1 RX nRF24L01 simultaneously). See the `Multiceiver Example `_ Dependencies -------------------------- diff --git a/docs/troubleshooting.rst b/docs/troubleshooting.rst index e965e42..915add1 100644 --- a/docs/troubleshooting.rst +++ b/docs/troubleshooting.rst @@ -40,7 +40,7 @@ With the `auto_ack` feature enabled, you get: path that connects the endpoints. When assigning addresses to a data pipe, you can use any 5 byte long address you can think of (as long as the first byte is unique among simultaneously broadcasting addresses), so you're not limited to communicating with only - the same 6 nRF24L01 radios (more on this when we officially support "Multiciever" mode). + the same 6 nRF24L01 radios. Finnaly, the radio's channel is not be confused with the radio's pipes. Channel selection is a way of specifying a certain radio frequency (frequency = [2400 + channel] MHz). diff --git a/examples/nrf24l01_fake_ble_test.py b/examples/nrf24l01_fake_ble_test.py index d09757d..6d53d83 100644 --- a/examples/nrf24l01_fake_ble_test.py +++ b/examples/nrf24l01_fake_ble_test.py @@ -1,8 +1,8 @@ """ -This example of using the nRF24L01 as a 'fake' Buetooth Beacon +This example uses the nRF24L01 as a 'fake' BLE Beacon .. warning:: ATSAMD21 M0-based boards have memory allocation - error when loading fake_ble.mpy + error when loading 'fake_ble.mpy' """ import time import board diff --git a/examples/nrf24l01_multiceiver_test.py b/examples/nrf24l01_multiceiver_test.py new file mode 100644 index 0000000..ea6d488 --- /dev/null +++ b/examples/nrf24l01_multiceiver_test.py @@ -0,0 +1,97 @@ +""" +Simple example of using 1 nRF24L01 to receive data from up to 6 other +transceivers. This technique is called "multiceiver" in the datasheet. +For fun, this example also sends an ACK payload from the base station +to the node-1 transmitter. +""" +import time +import board +import digitalio as dio + +# if running this on a ATSAMD21 M0 based board +# from circuitpython_nrf24l01.rf24_lite import RF24 +from circuitpython_nrf24l01.rf24 import RF24 + +# change these (digital output) pins accordingly +ce = dio.DigitalInOut(board.D4) +csn = dio.DigitalInOut(board.D5) + +# using board.SPI() automatically selects the MCU's +# available SPI pins, board.SCK, board.MOSI, board.MISO +spi = board.SPI() # init spi bus object + +# we'll be using the dynamic payload size feature (enabled by default) +# initialize the nRF24L01 on the spi bus object +nrf = RF24(spi, csn, ce) + +# set the Power Amplifier level to -12 dBm since this test example is +# usually run with nRF24L01 transceivers in close proximity +nrf.pa_level = -12 + +# setup the addresses for all transmitting nRF24L01 nodes +addresses = [ + b"\x78" * 5, + b"\xF1\xB3\xB4\xB5\xB6", + b"\xCD\xB3\xB4\xB5\xB6", + b"\xA3\xB3\xB4\xB5\xB6", + b"\x0F\xB3\xB4\xB5\xB6", + b"\x05\xB3\xB4\xB5\xB6" +] + +# to use custom ACK payloads, we must enable that feature +nrf.ack = True +# let this be the ACk payload +ACK = b"Yak Back ACK" + + +def base(timeout=10): + """Use the nRF24L01 as a base station for lisening to all nodes""" + # write the addresses to all pipes. + for pipe_n, addr in enumerate(addresses): + nrf.open_rx_pipe(pipe_n, addr) + while nrf.fifo(True, False): # fill TX FIFO with ACK payloads + nrf.load_ack(ACK, 1) # only send ACK payload to node 1 + nrf.listen = True # put base station into RX mode + start_timer = time.monotonic() # start timer + while time.monotonic() - start_timer < timeout: + while not nrf.fifo(False, True): # keep RX FIFO empty for reception + # show the pipe number that received the payload + print("node", nrf.pipe, "sent:", nrf.recv()) + start_timer = time.monotonic() # reset timer with every payload + if nrf.load_ack(ACK, 1): # keep TX FIFO full with ACK payloads + print("\t ACK re-loaded") + nrf.listen = False + + +def node(node_number, count=6): + """start transmitting to the base station. + + :param int node_number: the node's identifying index (from the + the `addresses` list) + :param int count: the number of times that the node will transmit + to the base station. + """ + nrf.listen = False + # set the TX address to the address of the base station. + nrf.open_tx_pipe(addresses[node_number]) + counter = 0 + # use the node_number to identify where the payload came from + node_id = b"PTX-" + bytes([node_number + 48]) + while counter < count: + counter += 1 + # payloads will include the node_number and a payload ID character + payload = node_id + b" payload-ID: " + bytes([node_number + 48]) + payload += bytes([counter + (65 if 0 <= counter < 26 else 71)]) + # show something to see it isn't frozen + print("attempt {} returned {}".format(counter, nrf.send(payload))) + time.sleep(0.5) # slow down the test for readability + + +print( + """\ + nRF24L01 Multiceiver test.\n\ + Run base() on the receiver\n\ + base() sends ACK payloads to node 1\n\ + Run node(node_number) on a transmitter\n\ + node()'s parameter, `node_number`, must be in range [0, 5]""" +)