Network Topology¶
Network Levels¶
Because of the hardware limitation’s of the nRF24L01 transceiver, each network is arranged in a levels where a parent can have up to 5 children. And each child can also have up to 5 other children. This is not limitless because this network is designed for low-memory devices. Consequently, all node’s Logical Address are limited to 12-bit integers and use an octal counting scheme.
The master node (designated with the Logical Address
0o0
) is always the only node in the lowest level (denoted as level 0).Child nodes are designated by the most significant octal digit in their Logical Address. A child node address’ least significant digits are the inherited address of it’s parent node. Nodes on level 1 only have 1 digit because they are children of the master node.
Hopefully, you should see the pattern. There can be up to a maximum of 5 network levels (that’s 0-4 ordered from lowest to highest).
For a message to travel from node 0o124
to node 0o3
, it must be passed through any
applicable network levels. So, the message flows 0o124
-> 0o24
->
0o4
-> 0o0
-> 0o3
.
A single network can potentially have a maximum of 781 nodes (all operating on the same
channel
), but for readability reasons, the following
graph only demonstrates
the master node (level 0) and it’s 5 children (level 1)
level 2 only shows the 1st and 2nd children of parents on level 1
level 3 only shows the 3rd and 4th children of parents on level 2
level 4 only shows the 5th children of parents on level 3
Physical addresses vs Logical addresses¶
The Physical address is the 5-byte address assigned to the radio’s data pipes.
The Logical address is the 12-bit integer representing a network node. The Logical address uses an octal counting scheme. A valid Logical Address must only contain octal digits in range [1, 5]. The master node is the exception for it uses the number
0
Tip
Use the
is_address_valid()
function to programmatically check a Logical Address for validity.
Note
Remember that the nRF24L01 only has 6 data pipes for which to receive or transmit. Since only data pipe 0 can be used to transmit, the other other data pipes 1-5 are devoted to receiving transmissions from other network nodes; data pipe 0 also receives multicasted messages about the node’s network level.
Translating Logical to Physical¶
Before translating the Logical address, a single byte is used repetitively as the
base case for all bytes of any Physical Address. This byte is the address_prefix
attribute (stored as a mutable bytearray
) in the RF24Network
class. By default the
address_prefix
has a single byte value of b"\xCC"
.
The RF24Network
class also has a predefined list of bytes used for translating
unique Logical addresses into unique Physical addresses. This list is called
address_suffix
(also stored as a mutable bytearray
). By default the address_suffix
has 6-byte value of b"\xC3\x3C\x33\xCE\x3E\xE3"
where the order of bytes pertains to the
data pipe number and child node’s most significant byte in its Physical Address.
- For example:
The Logical Address of the network’s master node is
0
. The radio’s pipes 1-5 start with theaddress_prefix
. To make each pipe’s Physical address unique to a child node’s Physical address, theaddress_suffix
is used.The Logical address of the master node:
0o0
pipe
Physical Address (hexadecimal)
1
CC CC CC CC 3C
2
CC CC CC CC 33
3
CC CC CC CC CE
4
CC CC CC CC 3E
5
CC CC CC CC E3
The Logical address of the master node’s first child:
0o1
pipe
Physical Address (hexadecimal)
1
CC CC CC 3C 3C
2
CC CC CC 3C 33
3
CC CC CC 3C CE
4
CC CC CC 3C 3E
5
CC CC CC 3C E3
The Logical address of the master node’s second child:
0o2
pipe
Physical Address (hexadecimal)
1
CC CC CC 33 3C
2
CC CC CC 33 33
3
CC CC CC 33 CE
4
CC CC CC 33 3E
5
CC CC CC 33 E3
The Logical address of the master node’s third child’s second child’s first child:
0o123
pipe
Physical Address (hexadecimal)
1
CC 3C 33 CE 3C
2
CC 3C 33 CE 33
3
CC 3C 33 CE CE
4
CC 3C 33 CE 3E
5
CC 3C 33 CE E3
Two networks coexisting on the same channel¶
Warning
The following section is an advanced tutorial. The default values for address_prefix
and address_suffix
were carefully chosen by TMRh20 to demonstrate best practices in
terms of choosing a data pipe’s address for transmissions. Bad practices can be avoided
by heeding ManiacBug’s advice in his
detailed blog post
about the topic.
In theory, the address_prefix
and address_suffix
attributes could be changed to
allow 2 separate networks to coexist on the same
channel
. The following are example code
snippets to use as a template for such a scenario.
from circuitpython_nrf24l01.rf24_network import RF24Network
# ... declare SPI_BUS, CE_PIN, and CSN_PIN objects
network_a_master = RF24Network(SPI_BUS, CSN_PIN, CE_PIN, 0)
# let network_a use the default values for address_prefix and address_suffix
while True:
network_a_master.update()
if network_a_master.available():
recv_frame = network_a_master.read()
print(
"received {}: {}".format(
recv_frame.header.to_string(), recv_frame.message.decode()
)
)
# emit frames as needed
from circuitpython_nrf24l01.rf24_network import RF24Network
# ... declare SPI_BUS, CE_PIN, and CSN_PIN objects
network_b_master = RF24Network(SPI_BUS, CSN_PIN, CE_PIN, 0)
# let network_b use different values for address_prefix and address_suffix
network_b_master.address_prefix = bytearray([0xDB])
network_b_master.address_suffix = bytearray([0xDD, 0x99, 0xB6, 0xD9, 0x9D, 0x66])
# re-assign the node_address for the different physical addresses to be used
network_b_master.node_address = 0
while True:
network_b_master.update()
if network_b_master.available():
recv_frame = network_b_master.read()
print(
"received {}: {}".format(
recv_frame.header.to_string(), recv_frame.message.decode()
)
)
# emit frames as needed
from circuitpython_nrf24l01.rf24_network import RF24Network
# ... declare SPI_BUS, CE_PIN, and CSN_PIN objects
network_b_node = RF24Network(SPI_BUS, CSN_PIN, CE_PIN, 5)
network_a_node = RF24Network(SPI_BUS, CSN_PIN, CE_PIN, 1)
# let network_b use different values for address_prefix and address_suffix
with network_b_node as net_b:
net_b.address_prefix = bytearray([0xDB])
net_b.address_suffix = bytearray([0xDD, 0x99, 0xB6, 0xD9, 0x9D, 0x66])
# re-assign the node_address for the different physical addresses to be used
net_b.node_address = 5
while True:
# do something with network_a
with network_a_node as net_a:
net_a.update()
net_a.send(RF24NetworkHeader(0, "T"), b"data for net A master")
# do something with network_b
with network_b_node as net_b:
net_b.update()
net_b.send(RF24NetworkHeader(0, "T"), b"data for net B master")
RF24Mesh connecting process¶
As noted above, a single network can have up to 781 nodes. This number also includes
up to 255 RF24Mesh nodes. The key difference from the user’s perspective is that RF24Mesh
API does not use a Logical Address. Instead the RF24Mesh API relies on
a node_id
number to identify a RF24Mesh node that may use a different
Logical Address (which can change based on the node’s physical location).
Important
Any network that will use RF24mesh for a child node needs to have a RF24Mesh master node. This will not interfere with RF24Network nodes since the RF24Mesh API is layered on top of the RF24Network API.
To better explain the difference between a node’s node_address
vs a node’s node_id
,
we will examine the connecting process for a RF24Mesh node. These are the steps performed
when calling renew_address()
:
Any RF24Mesh node not connected to a network will use the Logical Address
0o444
(that’s2340
in decimal). It is up to the network administrator to ensure that each RF24Mesh node has a uniquenode_id
(which is limited to the range [0, 255]).Hint
Remember that
0
is reserved the master node’snode_id
.To get assigned a Logical Address, an unconnected node must poll the network for a response (using a
NETWORK_POLL
message). Initially this happens on the network level 0, but consecutive attempts will poll higher network levels (in order of low to high) if this process fails.When a polling transmission is responded, the connecting mesh node sends an address request which gets forwarded to the master node when necessary (using a
MESH_ADDR_REQUEST
message).The master node will process the address request and respond with a
node_address
(using aMESH_ADDR_RESPONSE
message). If there is no available occupancy on the network level from which the address request originated, then the master node will respond with an invalid Logical Address.Once the requesting node receives the address response (and the assigned address is valid), it assumes that as the
node_address
while maintaining itsnode_id
.The connecting node will verify its new address by calling
check_connection
.If the assigned address is invalid or
check_connection()
returnsFalse
, then the connecting node will re-start the process (step 1) on a different network level.
Points of failure¶
This process happens over a span of a few milliseconds. However,
If the connecting node is physically moving throughout the network very quickly, then this process will take longer and is likely to fail.
If a master node is able to respond faster than the connecting node can prepare itself to receive, then the process will fail entirely. This failure about faster master nodes often results in some slower RF24Mesh nodes only being able to connect to the network through another non-master node.
If you run into trouble with this connection process, then please open an issue on github and describe the situation with as much detail as possible.