RadioHead Header
Every packet transmitted via the CircuitPython RFM9x Library contains a 4 byte header compatible with the Arduino RadioHead Library.
https://www.airspayce.com/mikem/arduino/RadioHead/classRHGenericDriver.html
Each message sent and received by a RadioHead driver includes 4 headers:
- TO - the node address that the message is being sent to (broadcast RH_BROADCAST_ADDRESS (255) is permitted)
- FROM - the node address of the sending node
- ID - a message ID, distinct (over short time scales) for each message sent by a particular node
- FLAGS- a bitmask of flags. The most significant 4 bits are reserved for use by RadioHead. The least significant 4 bits are reserved for applications.
For basic usage the TO and FROM addresses are set to "Broadcast" with a value of 255 (0xff) and the ID and FLAGS parameters are ignored. With these settings any packet received will be accepted.
Node Addressing
In many applications, it will be desirable to limit packets received to those originating at specified nodes and to direct responses to particular nodes. This can be controlled by setting the "node" and "destination" attributes for the rfm9x instance created in your CircuitPython code. https://circuitpython.readthedocs.io/projects/rfm69/en/latest/api.html
This example demonstrates the usage of the RadioHead Header settings to specify the address of the transmitting and receiving nodes.
This script is to be run on the board designated as Node 1. It will transmit a packet to Node 2 every 10 seconds
# Example to send a packet periodically between addressed nodes # Author: Jerry Needell # import time import board import busio import digitalio import adafruit_rfm69 # set the time interval (seconds) for sending packets transmit_interval = 10 # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. # Define pins connected to the chip. CS = digitalio.DigitalInOut(board.CE1) RESET = digitalio.DigitalInOut(board.D25) # Initialize SPI bus. spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio rfm69 = adafruit_rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) # Optionally set an encryption key (16 byte AES key). MUST match both # on the transmitter and receiver (or be set to None to disable/the default). rfm69.encryption_key = ( b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" ) # set node addresses rfm69.node = 1 rfm69.destination = 2 # initialize counter counter = 0 # send a broadcast message from my_node with ID = counter rfm69.send( bytes("Startup message {} from node {}".format(counter, rfm69.node), "UTF-8") ) # Wait to receive packets. print("Waiting for packets...") now = time.monotonic() while True: # Look for a new packet: only accept if addresses to my_node packet = rfm69.receive(with_header=True) # If no packet was received during the timeout then None is returned. if packet is not None: # Received a packet! # Print out the raw bytes of the packet: print("Received (raw header):", [hex(x) for x in packet[0:4]]) print("Received (raw payload): {0}".format(packet[4:])) print("Received RSSI: {0}".format(rfm69.last_rssi)) if time.monotonic() - now > transmit_interval: now = time.monotonic() counter = counter + 1 # send a mesage to destination_node from my_node rfm69.send( bytes( "message number {} from node {}".format(counter, rfm69.node), "UTF-8" ), keep_listening=True, )
This script is to be run on the board designated as Node 2. It will report the packets received from Node 1 and send a reply to Node 1 after every 10th packet received.
# Example to send a packet periodically between addressed nodes # Author: Jerry Needell # import time import board import busio import digitalio import adafruit_rfm69 # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. # Define pins connected to the chip. CS = digitalio.DigitalInOut(board.CE1) RESET = digitalio.DigitalInOut(board.D25) # Initialize SPI bus. spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio rfm69 = adafruit_rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) # Optionally set an encryption key (16 byte AES key). MUST match both # on the transmitter and receiver (or be set to None to disable/the default). rfm69.encryption_key = ( b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" ) # set node addresses rfm69.node = 2 rfm69.destination = 1 # initialize counter counter = 0 # send a broadcast message from my_node with ID = counter rfm69.send(bytes("startup message from node {} ".format(rfm69.node), "UTF-8")) # Wait to receive packets. print("Waiting for packets...") # initialize flag and timer time_now = time.monotonic() while True: # Look for a new packet: only accept if addresses to my_node packet = rfm69.receive(with_header=True) # If no packet was received during the timeout then None is returned. if packet is not None: # Received a packet! # Print out the raw bytes of the packet: print("Received (raw header):", [hex(x) for x in packet[0:4]]) print("Received (raw payload): {0}".format(packet[4:])) print("Received RSSI: {0}".format(rfm69.last_rssi)) # send reading after any packet received counter = counter + 1 # after 10 messages send a response to destination_node from my_node with ID = counter&0xff if counter % 10 == 0: time.sleep(0.5) # brief delay before responding rfm69.identifier = counter & 0xFF rfm69.send( bytes( "message number {} from node {} ".format(counter, rfm69.node), "UTF-8", ), keep_listening=True, )
Reliable Datagram
During basic transmissions or the above address examples, there is no way to confirm if a packet was actually received by the destination node.
The "Reliable Datagram" mode attempts to provide that reassurance.
When invoked, this mode requires that every addressed packet (non-Broadcast) be acknowledged by sending a specially formatted ACK packet in response as described for the RadioHead Library https://www.airspayce.com/mikem/arduino/RadioHead/classRHReliableDatagram.html
An ack consists of a message with:
- TO set to the from address of the original message
- FROM set to this node address
- ID set to the ID of the original message
- FLAGS with the RH_FLAGS_ACK bit set
- 1 octet of payload containing ASCII '!' (since some drivers cannot handle 0 length payloads)
Instead of the standard send()
function use the send_with_ack()
function. this configures the sending program to send the packet then wait for the responding ACK packet.
At the receiving node, the extra parameter with_ack=True
must be passed to the receive()
function so it generates the ACK packet in response.
This example sets up a "Reliable Datagram" exchange between Nodes 1 and 2. Node 1 sends a packet to Node 2 every 10 seconds and waits for an ACK. This script is executed by Node 1
# Example to send a packet periodically between addressed nodes with ACK # Author: Jerry Needell # import time import board import busio import digitalio import adafruit_rfm69 # set the time interval (seconds) for sending packets transmit_interval = 10 # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. # Define pins connected to the chip. # set GPIO pins as necessary -- this example is for Raspberry Pi CS = digitalio.DigitalInOut(board.CE1) RESET = digitalio.DigitalInOut(board.D25) # Initialize SPI bus. spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio rfm69 = adafruit_rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) # Optionally set an encryption key (16 byte AES key). MUST match both # on the transmitter and receiver (or be set to None to disable/the default). rfm69.encryption_key = ( b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" ) # set delay before sending ACK rfm69.ack_delay = 0.1 # set node addresses rfm69.node = 1 rfm69.destination = 2 # initialize counter counter = 0 ack_failed_counter = 0 # send startup message from my_node rfm69.send_with_ack(bytes("startup message from node {}".format(rfm69.node), "UTF-8")) # Wait to receive packets. print("Waiting for packets...") # initialize flag and timer time_now = time.monotonic() while True: # Look for a new packet: only accept if addresses to my_node packet = rfm69.receive(with_ack=True, with_header=True) # If no packet was received during the timeout then None is returned. if packet is not None: # Received a packet! # Print out the raw bytes of the packet: print("Received (raw header):", [hex(x) for x in packet[0:4]]) print("Received (raw payload): {0}".format(packet[4:])) print("RSSI: {0}".format(rfm69.last_rssi)) # send reading after any packet received if time.monotonic() - time_now > transmit_interval: # reset timeer time_now = time.monotonic() counter += 1 # send a mesage to destination_node from my_node if not rfm69.send_with_ack( bytes("message from node node {} {}".format(rfm69.node, counter), "UTF-8") ): ack_failed_counter += 1 print(" No Ack: ", counter, ack_failed_counter)
And this one is executed by Node 2. It acknowledges each packet and sends a response packet after a 2 second delay. Node 1 will acknowledge the response packet.
# Example to receive addressed packed with ACK and send a response # Author: Jerry Needell # import time import board import busio import digitalio import adafruit_rfm69 # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your # module! Can be a value like 915.0, 433.0, etc. # Define pins connected to the chip. # set GPIO pins as necessary - this example is for Raspberry Pi CS = digitalio.DigitalInOut(board.CE1) RESET = digitalio.DigitalInOut(board.D25) # Initialize SPI bus. spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio rfm69 = adafruit_rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) # Optionally set an encryption key (16 byte AES key). MUST match both # on the transmitter and receiver (or be set to None to disable/the default). rfm69.encryption_key = ( b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" ) # set delay before transmitting ACK (seconds) rfm69.ack_delay = 0.1 # set node addresses rfm69.node = 2 rfm69.destination = 1 # initialize counter counter = 0 ack_failed_counter = 0 # Wait to receive packets. print("Waiting for packets...") while True: # Look for a new packet: only accept if addresses to my_node packet = rfm69.receive(with_ack=True, with_header=True) # If no packet was received during the timeout then None is returned. if packet is not None: # Received a packet! # Print out the raw bytes of the packet: print("Received (raw header):", [hex(x) for x in packet[0:4]]) print("Received (raw payload): {0}".format(packet[4:])) print("RSSI: {0}".format(rfm69.last_rssi)) # send response 2 sec after any packet received time.sleep(2) counter += 1 # send a mesage to destination_node from my_node if not rfm69.send_with_ack( bytes("response from node {} {}".format(rfm69.node, counter), "UTF-8") ): ack_failed_counter += 1 print(" No Ack: ", counter, ack_failed_counter)
Page last edited March 08, 2024
Text editor powered by tinymce.