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

Written by 2bndy5 in 2020

This is a simple example of using the RF24 class on a Raspberry Pi to transmit and respond with acknowledgment (ACK) transmissions. Notice that the auto-ack feature is enabled, but this example doesn't use automatic ACK payloads because automatic ACK payloads' data will always be outdated by 1 transmission. Instead, this example uses a call and response paradigm.

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

python3 manual_acknowledgements.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 manually transmitted (non-automatic) Acknowledgement (ACK) payloads.
4This example still uses ACK packets, but they have no payloads. Instead the
5acknowledging response is sent with `write()`. This tactic allows for more
6updated acknowledgement payload data, where actual ACK payloads' data are
7outdated by 1 transmission because they have to loaded before receiving a
8transmission.
9
10This example was written to be used on 2 devices acting as 'nodes'.
11
12See documentation at https://nRF24.github.io/RF24
13"""
14
15import time
16from RF24 import RF24, RF24_PA_LOW, RF24_DRIVER
17
18
19print(__file__) # print example name
20
21
27CSN_PIN = 0 # GPIO8 aka CE0 on SPI bus 0: /dev/spidev0.0
28if RF24_DRIVER == "MRAA":
29 CE_PIN = 15 # for GPIO22
30elif RF24_DRIVER == "wiringPi":
31 CE_PIN = 3 # for GPIO22
32else:
33 CE_PIN = 22
34radio = RF24(CE_PIN, CSN_PIN)
35
36# initialize the nRF24L01 on the spi bus
37if not radio.begin():
38 raise RuntimeError("radio hardware is not responding")
39
40# For this example, we will use different addresses
41# An address need to be a buffer protocol object (bytearray)
42address = [b"1Node", b"2Node"]
43# It is very helpful to think of an address as a path instead of as
44# an identifying device destination
45
46# to use different addresses on a pair of radios, we need a variable to
47# uniquely identify which address this radio will use to transmit
48# 0 uses address[0] to transmit, 1 uses address[1] to transmit
49radio_number = bool(
50 int(input("Which radio is this? Enter '0' or '1'. Defaults to '0' ") or 0)
51)
52
53# set the Power Amplifier level to -12 dBm since this test example is
54# usually run with nRF24L01 transceivers in close proximity of each other
55radio.setPALevel(RF24_PA_LOW) # RF24_PA_MAX is default
56
57# set the TX address of the RX node into the TX pipe
58radio.openWritingPipe(address[radio_number]) # always uses pipe 0
59
60# set the RX address of the TX node into a RX pipe
61radio.openReadingPipe(1, address[not radio_number]) # using pipe 1
62
63# To save time during transmission, we'll set the payload size to be only
64# what we need. For this example, we'll be using a byte for the
65# payload counter and 7 bytes for the payload message
66radio.payloadSize = 8
67
68# for debugging, we have 2 options that print a large block of details
69# (smaller) function that prints raw register values
70# radio.printDetails()
71# (larger) function that prints human readable data
72# radio.printPrettyDetails()
73
74# using the python keyword global is bad practice. Instead we'll use a 1 item
75# list to store our integer number for the payloads' counter
76counter = [0]
77
78
79def master():
80 """Transmits a message and an incrementing integer every second, then
81 wait for a response for up to 200 ms.
82 """
83 radio.stopListening() # put radio in TX mode
84 failures = 0
85 while failures < 6:
86 # use bytes() to pack our counter data into the payload
87 # NOTE b"\x00" byte is a c-string's NULL terminating 0
88 buffer = b"Hello \x00" + bytes(counter)
89 start_timer = time.monotonic_ns() # start timer
90 result = radio.write(buffer)
91 if not result:
92 failures += 1
93 print("Transmission failed or timed out")
94 else:
95 radio.startListening() # put radio in RX mode
96 timeout = time.monotonic() * 1000 + 200 # use 200 ms timeout
97 # declare a variable to save the incoming response
98 while not radio.available() and time.monotonic() * 1000 < timeout:
99 pass # wait for incoming payload or timeout
100 radio.stopListening() # put radio in TX mode
101 end_timer = time.monotonic_ns() # end timer
102 decoded = buffer[:6].decode("utf-8")
103 print(
104 f"Transmission successful. Sent: {decoded}{counter[0]}.",
105 end=" ",
106 )
107 has_payload, pipe_number = radio.available_pipe()
108 if has_payload:
109 # grab the incoming payload
110 received = radio.read(radio.payloadSize)
111 # NOTE received[7:8] discards NULL terminating 0
112 counter[0] = received[7:8][0] # save the counter
113 decoded = bytes(received[:6]).decode("utf-8")
114 print(
115 f"Received {radio.payloadSize} bytes",
116 f"on pipe {pipe_number}: {decoded}{counter[0]}.",
117 f"Round-trip delay: {(end_timer - start_timer) / 1000} us.",
118 )
119 else:
120 print("No response received.")
121 time.sleep(1) # make example readable by slowing down transmissions
122 print(failures, "failures detected. Leaving TX role.")
123
124
125def slave(timeout: int = 6):
126 """Listen for any payloads and print the transaction
127
128 :param int timeout: The number of seconds to wait (with no transmission)
129 until exiting function.
130 """
131 radio.startListening() # put radio in RX mode
132
133 start_timer = time.monotonic() # start a timer to detect timeout
134 while (time.monotonic() - start_timer) < timeout:
135 # receive `count` payloads or wait 6 seconds till timing out
136 has_payload, pipe_number = radio.available_pipe()
137 if has_payload:
138 received = radio.read(radio.payloadSize) # fetch the payload
139 # NOTE received[7:8] discards NULL terminating 0
140 # increment the counter from received payload
141 counter[0] = received[7:8][0] + 1 if received[7:8][0] < 255 else 0
142 # use bytes() to pack our counter data into the payload
143 # NOTE b"\x00" byte is a c-string's NULL terminating 0
144 buffer = b"World \x00" + bytes(counter)
145 radio.stopListening() # put radio in TX mode
146 radio.writeFast(buffer) # load response into TX FIFO
147 # keep retrying to send response for 150 milliseconds
148 result = radio.txStandBy(150) # save response's result
149 # NOTE txStandBy() flushes TX FIFO on transmission failure
150 radio.startListening() # put radio back in RX mode
151 # print the payload received payload
152 decoded = bytes(received[:6]).decode("utf-8")
153 print(
154 f"Received {radio.payloadSize} bytes"
155 f"on pipe {pipe_number}: {decoded}{received[7:8][0]}.",
156 end=" ",
157 )
158 if result: # did response succeed?
159 # print response's payload
160 decoded = buffer[:6].decode("utf-8")
161 print(f"Sent: {decoded}{counter[0]}")
162 else:
163 print("Response failed or timed out")
164 start_timer = time.monotonic() # reset the timeout timer
165
166 print("Nothing received in", timeout, "seconds. Leaving RX role")
167 # recommended behavior is to keep in TX mode while idle
168 radio.stopListening() # put the radio in TX mode
169
170
171def set_role() -> bool:
172 """Set the role using stdin stream. Timeout arg for slave() can be
173 specified using a space delimiter (e.g. 'R 10' calls `slave(10)`)
174
175 :return:
176 - True when role is complete & app should continue running.
177 - False when app should exit
178 """
179 user_input = (
180 input(
181 "*** Enter 'R' for receiver role.\n"
182 "*** Enter 'T' for transmitter role.\n"
183 "*** Enter 'Q' to quit example.\n"
184 )
185 or "?"
186 )
187 user_input = user_input.split()
188 if user_input[0].upper().startswith("R"):
189 if len(user_input) > 1:
190 slave(int(user_input[1]))
191 else:
192 slave()
193 return True
194 if user_input[0].upper().startswith("T"):
195 master()
196 return True
197 if user_input[0].upper().startswith("Q"):
198 radio.powerDown()
199 return False
200 print(user_input[0], "is an unrecognized input. Please try again.")
201 return set_role()
202
203
204if __name__ == "__main__":
205 try:
206 while set_role():
207 pass # continue example until 'Q' is entered
208 except KeyboardInterrupt:
209 print(" Keyboard Interrupt detected. Powering down radio.")
210 radio.powerDown()
211else:
212 print(" Run slave() on receiver\n Run master() on transmitter")
Driver class for nRF24L01(+) 2.4GHz Wireless Transceiver.
Definition RF24.h:116