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

Written by 2bndy5 in 2020

This is a simple example of using the RF24 class on a Raspberry Pi to transmit and retrieve custom automatic acknowledgment payloads.

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

python3 acknowledgement_payloads.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 sending data from 1 nRF24L01 transceiver to another
3with Acknowledgement (ACK) payloads attached to ACK packets.
4
5This example was written to be used on 2 devices acting as 'nodes'.
6"""
7import sys
8import argparse
9import time
10from RF24 import RF24, RF24_PA_LOW
11
12
13parser = argparse.ArgumentParser(
14 description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter
15)
16parser.add_argument(
17 "-n",
18 "--node",
19 type=int,
20 choices=range(2),
21 help="the identifying radio number (or node ID number)",
22)
23parser.add_argument(
24 "-r",
25 "--role",
26 type=int,
27 choices=range(2),
28 help="'1' specifies the TX role. '0' specifies the RX role.",
29)
30
31
38CSN_PIN = 0 # connected to GPIO8
39CE_PIN = 22 # connected to GPIO22
40radio = RF24(CE_PIN, CSN_PIN)
41
46
47# using the python keyword global is bad practice. Instead we'll use a
48# 1 item list to store our integer number for the payloads' counter
49counter = [0]
50
51
52def master():
53 """Transmits a message and an incrementing integer every second."""
54 radio.stopListening() # put radio in TX mode
55 failures = 0
56 while failures < 6:
57 # construct a payload to send
58 buffer = b"Hello \x00" + bytes(counter)
59
60 # send the payload and prompt
61 start_timer = time.monotonic_ns() # start timer
62 result = radio.write(buffer) # save the report
63 end_timer = time.monotonic_ns() # stop timer
64 if result:
65 # print timer results upon transmission success
66 decoded = buffer[:6].decode("utf-8")
67 print(
68 "Transmission successful! Time to transmit:",
69 f"{int((end_timer - start_timer) / 1000)} us.",
70 f"Sent: {decoded}{counter[0]}",
71 end=" ",
72 )
73 has_payload, pipe_number = radio.available_pipe()
74 if has_payload:
75 # print the received ACK that was automatically sent
76 length = radio.getDynamicPayloadSize()
77 response = radio.read(length)
78 decoded = bytes(response[:6]).decode("utf-8")
79 print(
80 f"Received {length} on pipe {pipe_number}:",
81 f"{decoded}{response[7:8][0]}",
82 )
83 # increment counter from received payload
84 if response[7:8][0] < 255:
85 counter[0] = response[7:8][0] + 1
86 else:
87 counter[0] = 0
88 else:
89 print("Received an empty ACK packet")
90 else:
91 failures += 1
92 print("Transmission failed or timed out")
93 time.sleep(1) # let the RX node prepare a new ACK payload
94 print(failures, "failures detected. Leaving TX role.")
95
96
97def slave(timeout: int = 6):
98 """Listen for any payloads and print the transaction
99
100 :param int timeout: The number of seconds to wait (with no transmission)
101 until exiting function.
102 """
103 radio.startListening() # put radio in RX mode
104
105 # setup the first transmission's ACK payload
106 buffer = b"World \x00" + bytes(counter)
107 # we must set the ACK payload data and corresponding
108 # pipe number [0,5]
109 radio.writeAckPayload(1, buffer) # load ACK for first response
110
111 start_timer = time.monotonic() # start timer
112 while (time.monotonic() - start_timer) < timeout:
113 has_payload, pipe_number = radio.available_pipe()
114 if has_payload:
115 length = radio.getDynamicPayloadSize() # grab the payload length
116 received = radio.read(length) # fetch 1 payload from RX FIFO
117 # increment counter from received payload
118 counter[0] = received[7:8][0] + 1 if received[7:8][0] < 255 else 0
119 decoded = [bytes(received[:6]).decode("utf-8")]
120 decoded.append(buffer[:6].decode("utf-8"))
121 print(
122 f"Received {length} bytes on pipe {pipe_number}:",
123 f"{decoded[0]}{received[7:8][0]}",
124 f"Sent: {decoded[1]}{buffer[7:8][0]}",
125 )
126 buffer = b"World \x00" + bytes(counter) # build a new ACK payload
127 radio.writeAckPayload(1, buffer) # load ACK for next response
128 start_timer = time.monotonic() # reset timer
129
130 print("Nothing received in", timeout, "seconds. Leaving RX role")
131 # recommended behavior is to keep in TX mode while idle
132 radio.stopListening() # put radio in TX mode & flush unused ACK payloads
133
134
135def set_role() -> bool:
136 """Set the role using stdin stream. Timeout arg for slave() can be
137 specified using a space delimiter (e.g. 'R 10' calls `slave(10)`)
138
139 :return:
140 - True when role is complete & app should continue running.
141 - False when app should exit
142 """
143 user_input = (
144 input(
145 "*** Enter 'R' for receiver role.\n"
146 "*** Enter 'T' for transmitter role.\n"
147 "*** Enter 'Q' to quit example.\n"
148 )
149 or "?"
150 )
151 user_input = user_input.split()
152 if user_input[0].upper().startswith("R"):
153 if len(user_input) > 1:
154 slave(int(user_input[1]))
155 else:
156 slave()
157 return True
158 if user_input[0].upper().startswith("T"):
159 master()
160 return True
161 if user_input[0].upper().startswith("Q"):
162 radio.powerDown()
163 return False
164 print(user_input[0], "is an unrecognized input. Please try again.")
165 return set_role()
166
167
168if __name__ == "__main__":
169
170 args = parser.parse_args() # parse any CLI args
171
172 # initialize the nRF24L01 on the spi bus
173 if not radio.begin():
174 raise RuntimeError("radio hardware is not responding")
175
176 # For this example, we will use different addresses
177 # An address need to be a buffer protocol object (bytearray)
178 address = [b"1Node", b"2Node"]
179 # It is very helpful to think of an address as a path instead of as
180 # an identifying device destination
181
182 print(sys.argv[0]) # print example name
183
184 # to use different addresses on a pair of radios, we need a variable to
185 # uniquely identify which address this radio will use to transmit
186 # 0 uses address[0] to transmit, 1 uses address[1] to transmit
187 radio_number = args.node # uses default value from `parser`
188 if args.node is None: # if '--node' arg wasn't specified
189 radio_number = bool(
190 int(input("Which radio is this? Enter '0' or '1'. Defaults to '0' ") or 0)
191 )
192
193 # ACK payloads are dynamically sized.
194 radio.enableDynamicPayloads() # to use ACK payloads
195
196 # to enable the custom ACK payload feature
197 radio.enableAckPayload()
198
199 # set the Power Amplifier level to -12 dBm since this test example is
200 # usually run with nRF24L01 transceivers in close proximity of each other
201 radio.setPALevel(RF24_PA_LOW) # RF24_PA_MAX is default
202
203 # set the TX address of the RX node into the TX pipe
204 radio.openWritingPipe(address[radio_number]) # always uses pipe 0
205
206 # set the RX address of the TX node into a RX pipe
207 radio.openReadingPipe(1, address[not radio_number]) # using pipe 1
208
209 # for debugging, we have 2 options that print a large block of details
210 # (smaller) function that prints raw register values
211 # radio.printDetails()
212 # (larger) function that prints human readable data
213 # radio.printPrettyDetails()
214
215 try:
216 if args.role is None: # if not specified with CLI arg '-r'
217 while set_role():
218 pass # continue example until 'Q' is entered
219 else: # if role was set using CLI args
220 # run role once and exit
221 if bool(args.role):
222 master()
223 else:
224 slave()
225 except KeyboardInterrupt:
226 print(" Keyboard Interrupt detected. Exiting...")
227 radio.powerDown()
228 sys.exit()
Driver class for nRF24L01(+) 2.4GHz Wireless Transceiver.
Definition: RF24.h:116