Optimized high speed nRF24L01+ driver class documentation v1.4.8
TMRh20 2020 - Optimized fork of the nRF24L01+ driver
Loading...
Searching...
No Matches
examples_linux/streaming_data.py

Written by 2bndy5 in 2020

This is a simple example of using the RF24 class on a Raspberry Pi for streaming multiple payloads.

Remember to install the Python wrapper, then navigate to the "RF24/examples_linux" folder.
To run this example, enter

python3 streaming_data.py

and follow the prompts.

Note
this example requires python v3.7 or newer because it measures transmission time with time.monotonic_ns().
1"""
2A simple example of streaming data from 1 nRF24L01 transceiver to another.
3
4This example was written to be used on 2 devices acting as 'nodes'.
5"""
6import sys
7import argparse
8import time
9from RF24 import RF24, RF24_PA_LOW
10
11
12parser = argparse.ArgumentParser(
13 description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
14)
15parser.add_argument(
16 "-n",
17 "--node",
18 type=int,
19 choices=range(2),
20 help="the identifying radio number (or node ID number)",
21)
22parser.add_argument(
23 "-r",
24 "--role",
25 type=int,
26 choices=range(2),
27 help="'1' specifies the TX role. '0' specifies the RX role.",
28)
29
30
37CSN_PIN = 0 # connected to GPIO8
38CE_PIN = 22 # connected to GPIO22
39radio = RF24(CE_PIN, CSN_PIN)
40
45
46# Specify the number of bytes in the payload. This is also used to
47# specify the number of payloads in 1 stream of data
48SIZE = 32 # this is the default maximum payload size
49
50
51def make_buffer(buf_iter: int) -> bytes:
52 """Returns a dynamically created payloads
53
54 :param int buf_iter: The position of the payload in the data stream
55 """
56 # we'll use `SIZE` for the number of payloads in the list and the
57 # payloads' length
58 # prefix payload with a sequential letter to indicate which
59 # payloads were lost (if any)
60 buff = bytes([buf_iter + (65 if 0 <= buf_iter < 26 else 71)])
61 for j in range(SIZE - 1):
62 char = bool(j >= (SIZE - 1) / 2 + abs((SIZE - 1) / 2 - buf_iter))
63 char |= bool(j < (SIZE - 1) / 2 - abs((SIZE - 1) / 2 - buf_iter))
64 buff += bytes([char + 48])
65 return buff
66
67
68def master(count: int = 1):
69 """Uses all 3 levels of the TX FIFO to send a stream of data
70
71 :param int count: how many times to transmit the stream of data.
72 """
73 radio.stopListening() # put radio in TX mode
74 radio.flush_tx() # clear the TX FIFO so we can use all 3 levels
75 failures = 0 # keep track of manual retries
76 start_timer = time.monotonic_ns() # start timer
77 for multiplier in range(count): # repeat transmit the same data stream
78 buf_iter = 0 # iterator of payloads for the while loop
79 while buf_iter < SIZE: # cycle through all the payloads
80 buffer = make_buffer(buf_iter) # make a payload
81
82 if not radio.writeFast(buffer): # transmission failed
83 failures += 1 # increment manual retry count
84 if failures > 99 and buf_iter < 7 and multiplier < 2:
85 # we need to prevent an infinite loop
86 print("Too many failures detected. Aborting at payload ", buffer[0])
87 multiplier = count # be sure to exit the for loop
88 break # exit the while loop
89 radio.reUseTX() # resend payload in top level of TX FIFO
90 else: # transmission succeeded
91 buf_iter += 1
92 end_timer = time.monotonic_ns() # end timer
93 print(
94 f"Time to transmit data = {(end_timer - start_timer) / 1000} us.",
95 f"Detected {failures} failures."
96 )
97
98
99def slave(timeout: int = 6):
100 """Listen for any payloads and print them out (suffixed with received
101 counter)
102
103 :param int timeout: The number of seconds to wait (with no transmission)
104 until exiting function.
105 """
106 radio.startListening() # put radio in RX mode
107 count = 0 # keep track of the number of received payloads
108 start_timer = time.monotonic() # start timer
109 while (time.monotonic() - start_timer) < timeout:
110 if radio.available():
111 count += 1
112 # retrieve the received packet's payload
113 receive_payload = radio.read(radio.payloadSize)
114 print("Received:", receive_payload, "-", count)
115 start_timer = time.monotonic() # reset timer on every RX payload
116
117 print("Nothing received in", timeout, "seconds. Leaving RX role")
118 # recommended behavior is to keep in TX mode while idle
119 radio.stopListening() # put the radio in TX mode
120
121
122
123def set_role() -> bool:
124 """Set the role using stdin stream. Role args can be specified using space
125 delimiters (e.g. 'R 10' calls `slave(10)` & 'T 3' calls `master(3)`)
126
127 :return:
128 - True when role is complete & app should continue running.
129 - False when app should exit
130 """
131 user_input = (
132 input(
133 "*** Enter 'R' for receiver role.\n"
134 "*** Enter 'T' for transmitter role.\n"
135 "*** Enter 'Q' to quit example.\n"
136 )
137 or "?"
138 )
139 user_input = user_input.split()
140 if user_input[0].upper().startswith("R"):
141 if len(user_input) > 1:
142 slave(int(user_input[1]))
143 else:
144 slave()
145 return True
146 if user_input[0].upper().startswith("T"):
147 if len(user_input) > 1:
148 master(int(user_input[1]))
149 else:
150 master()
151 return True
152 if user_input[0].upper().startswith("Q"):
153 radio.powerDown()
154 return False
155 print(user_input[0], "is an unrecognized input. Please try again.")
156 return set_role()
157
158
159if __name__ == "__main__":
160
161 args = parser.parse_args() # parse any CLI args
162
163 # initialize the nRF24L01 on the spi bus
164 if not radio.begin():
165 raise RuntimeError("radio hardware is not responding")
166
167 # For this example, we will use different addresses
168 # An address need to be a buffer protocol object (bytearray)
169 address = [b"1Node", b"2Node"]
170 # It is very helpful to think of an address as a path instead of as
171 # an identifying device destination
172
173 print(sys.argv[0]) # print example name
174
175 # to use different addresses on a pair of radios, we need a variable to
176 # uniquely identify which address this radio will use to transmit
177 # 0 uses address[0] to transmit, 1 uses address[1] to transmit
178 radio_number = args.node # uses default value from `parser`
179 if args.node is None: # if '--node' arg wasn't specified
180 radio_number = bool(
181 int(input("Which radio is this? Enter '0' or '1'. Defaults to '0' ") or 0)
182 )
183
184 # set the Power Amplifier level to -12 dBm since this test example is
185 # usually run with nRF24L01 transceivers in close proximity of each other
186 radio.setPALevel(RF24_PA_LOW) # RF24_PA_MAX is default
187
188 # set the TX address of the RX node into the TX pipe
189 radio.openWritingPipe(address[radio_number]) # always uses pipe 0
190
191 # set the RX address of the TX node into a RX pipe
192 radio.openReadingPipe(1, address[not radio_number]) # using pipe 1
193
194 # To save time during transmission, we'll set the payload size to be only
195 # what we need. For this example, we'll be using the default maximum 32
196 radio.payloadSize = SIZE
197
198 # for debugging, we have 2 options that print a large block of details
199 # (smaller) function that prints raw register values
200 # radio.printDetails()
201 # (larger) function that prints human readable data
202 # radio.printPrettyDetails()
203
204 try:
205 if args.role is None: # if not specified with CLI arg '-r'
206 while set_role():
207 pass # continue example until 'Q' is entered
208 else: # if role was set using CLI args
209 # run role once and exit
210 if bool(args.role):
211 master()
212 else:
213 slave()
214 except KeyboardInterrupt:
215 print(" Keyboard Interrupt detected. Powering down radio.")
216 radio.powerDown()
Driver class for nRF24L01(+) 2.4GHz Wireless Transceiver.
Definition: RF24.h:116