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.

network_hierarchy cluster_hierarchy cluster_legend 0o0 0o0 0o1 0o1 0o0--0o1 0o2 0o2 0o0--0o2 0o3 0o3 0o0--0o3 0o4 0o4 0o0--0o4 0o5 0o5 0o0--0o5 0o14 0o14 0o4--0o14 0o24 0o24 0o4--0o24 0o34 0o34 0o4--0o34 0o44 0o44 0o4--0o44 0o54 0o54 0o4--0o54 0o124 0o124 0o24--0o124 0o224 0o224 0o24--0o224 0o324 0o324 0o24--0o324 0o424 0o424 0o24--0o424 0o524 0o524 0o24--0o524 0o1324 0o1324 0o324--0o1324 0o2324 0o2324 0o324--0o2324 0o3324 0o3324 0o324--0o3324 0o4324 0o4324 0o324--0o4324 0o5324 0o5324 0o324--0o5324 Legend Legend Network Level 0         Network Level 1         Network Level 2         Network Level 3         Network Level 4         Nodes are labeled in octal numbers

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

network_levels 0o0 0o0 0o1 0o1 0o0--0o1 0o2 0o2 0o0--0o2 0o3 0o3 0o0--0o3 0o4 0o4 0o0--0o4 0o5 0o5 0o0--0o5 0o11 0o11 0o1--0o11 0o21 0o21 0o1--0o21 0o12 0o12 0o2--0o12 0o22 0o22 0o2--0o22 0o13 0o13 0o3--0o13 0o23 0o23 0o3--0o23 0o14 0o14 0o4--0o14 0o24 0o24 0o4--0o24 0o15 0o15 0o5--0o15 0o25 0o25 0o5--0o25 0o311 0o311 0o11--0o311 0o411 0o411 0o11--0o411 0o321 0o321 0o21--0o321 0o421 0o421 0o21--0o421 0o312 0o312 0o12--0o312 0o412 0o412 0o12--0o412 0o322 0o322 0o22--0o322 0o422 0o422 0o22--0o422 0o313 0o313 0o13--0o313 0o413 0o413 0o13--0o413 0o323 0o323 0o23--0o323 0o423 0o423 0o23--0o423 0o314 0o314 0o14--0o314 0o414 0o414 0o14--0o414 0o324 0o324 0o24--0o324 0o424 0o424 0o24--0o424 0o315 0o315 0o15--0o315 0o415 0o415 0o15--0o415 0o325 0o325 0o25--0o325 0o425 0o425 0o25--0o425 0o5311 0o5311 0o311--0o5311 0o5411 0o5411 0o411--0o5411 0o5321 0o5321 0o321--0o5321 0o5421 0o5421 0o421--0o5421 0o5312 0o5312 0o312--0o5312 0o5412 0o5412 0o412--0o5412 0o5322 0o5322 0o322--0o5322 0o5422 0o5422 0o422--0o5422 0o5313 0o5313 0o313--0o5313 0o5413 0o5413 0o413--0o5413 0o5323 0o5323 0o323--0o5323 0o5423 0o5423 0o423--0o5423 0o5314 0o5314 0o314--0o5314 0o5414 0o5414 0o414--0o5414 0o5324 0o5324 0o324--0o5324 0o5424 0o5424 0o424--0o5424 0o5315 0o5315 0o315--0o5315 0o5415 0o5415 0o415--0o5415 0o5325 0o5325 0o325--0o5325 0o5425 0o5425 0o425--0o5425

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 the address_prefix. To make each pipe’s Physical address unique to a child node’s Physical address, the address_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.

Master node for network_a
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
Master node for network_b
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
A single network node for hoping between network_a & network_b
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():

  1. Any RF24Mesh node not connected to a network will use the Logical Address 0o444 (that’s 2340 in decimal). It is up to the network administrator to ensure that each RF24Mesh node has a unique node_id (which is limited to the range [0, 255]).

    Hint

    Remember that 0 is reserved the master node’s node_id.

  2. 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.

  3. 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).

  4. The master node will process the address request and respond with a node_address (using a MESH_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.

  5. Once the requesting node receives the address response (and the assigned address is valid), it assumes that as the node_address while maintaining its node_id.

    • The connecting node will verify its new address by calling check_connection.

    • If the assigned address is invalid or check_connection() returns False, 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.