From 542638d3fc7e03eb5d253fa44a7278f3fce53506 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Sun, 26 Jan 2025 14:41:19 -0500 Subject: [PATCH 1/7] WIP -- fix SF=6 for rfm9x --- adafruit_rfm/rfm9x.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/adafruit_rfm/rfm9x.py b/adafruit_rfm/rfm9x.py index 2848292..f85fc86 100644 --- a/adafruit_rfm/rfm9x.py +++ b/adafruit_rfm/rfm9x.py @@ -169,6 +169,8 @@ class RFM9x(RFMSPI): auto_agc = RFMSPI.RegisterBits(_RF95_REG_26_MODEM_CONFIG3, offset=2, bits=1) + header_mode = RFMSPI.RegisterBits(_RF95_REG_1D_MODEM_CONFIG1, offset=0, bits=1) + low_datarate_optimize = RFMSPI.RegisterBits(_RF95_REG_26_MODEM_CONFIG3, offset=3, bits=1) lna_boost_hf = RFMSPI.RegisterBits(_RF95_REG_0C_LNA, offset=0, bits=2) @@ -461,8 +463,10 @@ def spreading_factor(self, val: Literal[6, 7, 8, 9, 10, 11, 12]) -> None: if val == 6: self.detection_optimize = 0x5 + self.header_mode = 1 # enable implicit header mode else: self.detection_optimize = 0x3 + self.header_mode = 0 # enable exlicit header mode self.write_u8(_RF95_DETECTION_THRESHOLD, 0x0C if val == 6 else 0x0A) self.write_u8( @@ -491,6 +495,16 @@ def enable_crc(self, val: bool) -> None: self.read_u8(_RF95_REG_1E_MODEM_CONFIG2) & 0xFB, ) + @property + def payload_length(self) -> bool: + """Must be set when using Implicit Header Mode - required for SF = 6""" + return self.read_u8(_RF95_REG_22_PAYLOAD_LENGTH) + + @payload_length.setter + def payload_length(self, val: int) -> None: + # Set payload length + self.write_u8(_RF95_REG_22_PAYLOAD_LENGTH, val) + @property def crc_error(self) -> bool: """crc status""" From 752f3e25a65e65a33a0d842a43b856cc9f27ab45 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Mon, 27 Jan 2025 12:49:35 -0500 Subject: [PATCH 2/7] WIP -- more fix SF=6 for rfm9x --- adafruit_rfm/rfm9x.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_rfm/rfm9x.py b/adafruit_rfm/rfm9x.py index f85fc86..489db53 100644 --- a/adafruit_rfm/rfm9x.py +++ b/adafruit_rfm/rfm9x.py @@ -496,7 +496,7 @@ def enable_crc(self, val: bool) -> None: ) @property - def payload_length(self) -> bool: + def payload_length(self) -> int: """Must be set when using Implicit Header Mode - required for SF = 6""" return self.read_u8(_RF95_REG_22_PAYLOAD_LENGTH) From 96682237abab0aa93e162dc713ee1e5c4923873b Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Mon, 27 Jan 2025 16:07:37 -0500 Subject: [PATCH 3/7] add lora sf examples --- examples/rfm_lora_sf_base.py | 77 ++++++++++++++++++++++++++++++++++ examples/rfm_lora_sf_node.py | 81 ++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 examples/rfm_lora_sf_base.py create mode 100644 examples/rfm_lora_sf_node.py diff --git a/examples/rfm_lora_sf_base.py b/examples/rfm_lora_sf_base.py new file mode 100644 index 0000000..25d3df5 --- /dev/null +++ b/examples/rfm_lora_sf_base.py @@ -0,0 +1,77 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +import time + +import board +import busio +import digitalio + +# 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, use these if wiring up the breakout according to the guide: +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 +# uncommnet the desired import and rfm initialization depending on the radio boards being used + +# Use rfm9x for two RFM9x radios using LoRa + +from adafruit_rfm import rfm9x + +rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +rfm.radiohead = False # don't appent RadioHead heade +# set spreading factor +rfm.spreading_factor = 6 +print("spreading factor set to :", rfm.spreading_factor) +# rfm.ack_wait = 1 +# rfm.xmit_timeout = 5 +# rfm.low_datarate_optimize = 1 +# set node addresses +# rfm.node = 100 +# rfm.destination = 0xFF +rfm.enable_crc = True +payload = bytearray(40) +# rfm.payload_length = len(payload) + 4 # add 4 for RadioHEad header +rfm.payload_length = len(payload) +# send startup message from my_node +message = bytes(f"startup message from base", "UTF-8") +payload[0 : len(message)] = message +rfm.send( + payload, + keep_listening=True, +) +# Wait to receive packets. +print("Waiting for packets...") +# initialize flag and timer +transmit_delay = 5 +last_transmit_time = 0 +packet_received = False +while True: + if rfm.payload_ready(): + packet_received = False + packet = rfm.receive(timeout=None) + if packet is not None: + # Received a packet! + # Print out the raw bytes of the packet: + print(f"Received (raw payload): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm.last_rssi}") + packet_received = True + last_transmit_time = time.monotonic() + if packet_received and (time.monotonic() - last_transmit_time) > transmit_delay: + payload = bytearray(40) + # message = bytes(f"packet received","UTF-8") + payload[0 : len(packet)] = packet + rfm.send( + payload, + keep_listening=True, + ) + packet_received = False diff --git a/examples/rfm_lora_sf_node.py b/examples/rfm_lora_sf_node.py new file mode 100644 index 0000000..6b26c71 --- /dev/null +++ b/examples/rfm_lora_sf_node.py @@ -0,0 +1,81 @@ +# SPDX-FileCopyrightText: 2021 ladyada for Adafruit Industries +# SPDX-License-Identifier: MIT + +# Example to send a packet periodically between addressed nodes with ACK +# Author: Jerry Needell +# +import time + +import board +import busio +import digitalio + +# 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, use these if wiring up the breakout according to the guide: +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 +# uncommnet the desired import and rfm initialization depending on the radio boards being used + +# Use rfm9x for two RFM9x radios using LoRa + +from adafruit_rfm import rfm9x + +rfm = rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ) + +rfm.radiohead = False # Do not use RadioHead Header +# set spreading factor +rfm.spreading_factor = 6 +print("spreading factor set to :", rfm.spreading_factor) +# rfm.ack_wait = 1 +# rfm.xmit_timeout = 5 +# rfm.low_datarate_optimize = 1 +# rfm.receive_timeout = 5 +rfm.enable_crc = True +# set the time interval (seconds) for sending packets +transmit_interval = 10 + +# set node addresses +# rfm.node = 1 +# rfm.destination = 100 +# initialize counter +counter = 0 +ack_failed_counter = 0 +# send startup message from my_node +payload = bytearray(40) +rfm.payload_length = len(payload) +message = bytes(f"startup message from node", "UTF-8") +payload[0 : len(message)] = message +rfm.send(payload) + +# 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 = rfm.receive() + # 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(f"Received (raw payload): {packet}") + print([hex(x) for x in packet]) + print(f"RSSI: {rfm.last_rssi}") + # send reading after any packet received + if time.monotonic() - time_now > transmit_interval: + # reset timeer + time_now = time.monotonic() + # send a mesage to destination_node from my_node + payload = bytearray(40) + message = bytes(f"message from node {counter}", "UTF-8") + payload[0 : len(message)] = message + rfm.send(payload) + counter += 1 From f2ff1e48ab7598a876b67f9939e832d180db630f Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Tue, 28 Jan 2025 08:27:57 -0500 Subject: [PATCH 4/7] rfm9x set low_datarate_optimize automatically - revize sf examples --- adafruit_rfm/rfm9x.py | 10 ++++++ examples/rfm_lora_sf_base.py | 61 ++++++++++++++++++++++++------------ examples/rfm_lora_sf_node.py | 52 +++++++++++++++++++++++------- 3 files changed, 91 insertions(+), 32 deletions(-) diff --git a/adafruit_rfm/rfm9x.py b/adafruit_rfm/rfm9x.py index 489db53..5cda864 100644 --- a/adafruit_rfm/rfm9x.py +++ b/adafruit_rfm/rfm9x.py @@ -426,6 +426,11 @@ def signal_bandwidth(self, val: int) -> None: else: self.write_u8(0x2F, 0x44) self.write_u8(0x30, 0) + # set low_datarate_optimize for signal duration > 16 ms + if 1000 / (self.signal_bandwidth / (1 << self.spreading_factor)) > 16: + self.low_datarate_optimize = 1 + else: + self.low_datarate_optimize = 0 @property def coding_rate(self) -> Literal[5, 6, 7, 8]: @@ -473,6 +478,11 @@ def spreading_factor(self, val: Literal[6, 7, 8, 9, 10, 11, 12]) -> None: _RF95_REG_1E_MODEM_CONFIG2, ((self.read_u8(_RF95_REG_1E_MODEM_CONFIG2) & 0x0F) | ((val << 4) & 0xF0)), ) + # set low_datarate_optimize for signal duration > 16 ms + if 1000 / (self.signal_bandwidth / (1 << self.spreading_factor)) > 16: + self.low_datarate_optimize = 1 + else: + self.low_datarate_optimize = 0 @property def enable_crc(self) -> bool: diff --git a/examples/rfm_lora_sf_base.py b/examples/rfm_lora_sf_base.py index 25d3df5..b8f0e45 100644 --- a/examples/rfm_lora_sf_base.py +++ b/examples/rfm_lora_sf_base.py @@ -29,25 +29,39 @@ rfm.radiohead = False # don't appent RadioHead heade # set spreading factor -rfm.spreading_factor = 6 +rfm.spreading_factor = 7 print("spreading factor set to :", rfm.spreading_factor) -# rfm.ack_wait = 1 -# rfm.xmit_timeout = 5 -# rfm.low_datarate_optimize = 1 +print("low_datarate_optimize set to: ", rfm.low_datarate_optimize) +# rfm.signal_bandwidth = 500000 +print("signal_bandwidth set to :", rfm.signal_bandwidth) +print("low_datarate_optimize set to: ", rfm.low_datarate_optimize) +if rfm.spreading_factor == 12: + rfm.xmit_timeout = 5 +print("xmit_timeout set to: ", rfm.xmit_timeout) +if rfm.spreading_factor == 12: + rfm.receive_timeout = 5 +elif rfm.spreading_factor > 7: + rfm.receive_timeout = 2 +print("receive_timeout set to: ", rfm.receive_timeout) # set node addresses # rfm.node = 100 # rfm.destination = 0xFF rfm.enable_crc = True -payload = bytearray(40) -# rfm.payload_length = len(payload) + 4 # add 4 for RadioHEad header -rfm.payload_length = len(payload) -# send startup message from my_node +# send startup message message = bytes(f"startup message from base", "UTF-8") -payload[0 : len(message)] = message -rfm.send( - payload, - keep_listening=True, -) +if rfm.spreading_factor == 6: + payload = bytearray(40) + rfm.payload_length = len(payload) + payload[0 : len(message)] = message + rfm.send( + payload, + keep_listening=True, + ) +else: + rfm.send( + message, + keep_listening=True, + ) # Wait to receive packets. print("Waiting for packets...") # initialize flag and timer @@ -67,11 +81,18 @@ packet_received = True last_transmit_time = time.monotonic() if packet_received and (time.monotonic() - last_transmit_time) > transmit_delay: - payload = bytearray(40) - # message = bytes(f"packet received","UTF-8") - payload[0 : len(packet)] = packet - rfm.send( - payload, - keep_listening=True, - ) + # send back the received packet + if rfm.spreading_factor == 6: + payload = bytearray(40) + rfm.payload_length = len(payload) + payload[0 : len(packet)] = packet + rfm.send( + payload, + keep_listening=True, + ) + else: + rfm.send( + packet, + keep_listening=True, + ) packet_received = False diff --git a/examples/rfm_lora_sf_node.py b/examples/rfm_lora_sf_node.py index 6b26c71..a0ea6b7 100644 --- a/examples/rfm_lora_sf_node.py +++ b/examples/rfm_lora_sf_node.py @@ -32,12 +32,20 @@ rfm.radiohead = False # Do not use RadioHead Header # set spreading factor -rfm.spreading_factor = 6 +rfm.spreading_factor = 7 print("spreading factor set to :", rfm.spreading_factor) -# rfm.ack_wait = 1 -# rfm.xmit_timeout = 5 -# rfm.low_datarate_optimize = 1 -# rfm.receive_timeout = 5 +print("low_datarate_optimize set to: ", rfm.low_datarate_optimize) +# rfm.signal_bandwidth = 500000 +print("signal_bandwidth set to :", rfm.signal_bandwidth) +print("low_datarate_optimize set to: ", rfm.low_datarate_optimize) +if rfm.spreading_factor == 12: + rfm.xmit_timeout = 5 +print("xmit_timeout set to: ", rfm.xmit_timeout) +if rfm.spreading_factor == 12: + rfm.receive_timeout = 5 +elif rfm.spreading_factor > 7: + rfm.receive_timeout = 2 +print("receive_timeout set to: ", rfm.receive_timeout) rfm.enable_crc = True # set the time interval (seconds) for sending packets transmit_interval = 10 @@ -49,11 +57,20 @@ counter = 0 ack_failed_counter = 0 # send startup message from my_node -payload = bytearray(40) -rfm.payload_length = len(payload) message = bytes(f"startup message from node", "UTF-8") -payload[0 : len(message)] = message -rfm.send(payload) +if rfm.spreading_factor == 6: + payload = bytearray(40) + rfm.payload_length = len(payload) + payload[0 : len(message)] = message + rfm.send( + payload, + keep_listening=True, + ) +else: + rfm.send( + message, + keep_listening=True, + ) # Wait to receive packets. print("Waiting for packets...") @@ -74,8 +91,19 @@ # reset timeer time_now = time.monotonic() # send a mesage to destination_node from my_node - payload = bytearray(40) message = bytes(f"message from node {counter}", "UTF-8") - payload[0 : len(message)] = message - rfm.send(payload) + if rfm.spreading_factor == 6: + payload = bytearray(40) + rfm.payload_length = len(payload) + payload[0 : len(message)] = message + rfm.send( + payload, + keep_listening=True, + ) + else: + rfm.send( + message, + keep_listening=True, + ) + counter += 1 From ab48f2e0322fbced7175fd69b28458f32835a977 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Tue, 28 Jan 2025 08:32:43 -0500 Subject: [PATCH 5/7] revise sf example --- examples/rfm_lora_sf_base.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/examples/rfm_lora_sf_base.py b/examples/rfm_lora_sf_base.py index b8f0e45..2dcda08 100644 --- a/examples/rfm_lora_sf_base.py +++ b/examples/rfm_lora_sf_base.py @@ -43,9 +43,6 @@ elif rfm.spreading_factor > 7: rfm.receive_timeout = 2 print("receive_timeout set to: ", rfm.receive_timeout) -# set node addresses -# rfm.node = 100 -# rfm.destination = 0xFF rfm.enable_crc = True # send startup message message = bytes(f"startup message from base", "UTF-8") From 4a529c94d161e39dd080a7ede9841df23d208c7b Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Wed, 19 Feb 2025 15:56:39 -0500 Subject: [PATCH 6/7] fix up spreading factor examples --- examples/rfm_lora_sf_base.py | 5 +++-- examples/rfm_lora_sf_node.py | 10 +++------- 2 files changed, 6 insertions(+), 9 deletions(-) diff --git a/examples/rfm_lora_sf_base.py b/examples/rfm_lora_sf_base.py index 2dcda08..58fd60b 100644 --- a/examples/rfm_lora_sf_base.py +++ b/examples/rfm_lora_sf_base.py @@ -62,7 +62,8 @@ # Wait to receive packets. print("Waiting for packets...") # initialize flag and timer -transmit_delay = 5 +# set a delay before sending the echo packet +transmit_delay = 0.5 last_transmit_time = 0 packet_received = False while True: @@ -77,7 +78,7 @@ print(f"RSSI: {rfm.last_rssi}") packet_received = True last_transmit_time = time.monotonic() - if packet_received and (time.monotonic() - last_transmit_time) > transmit_delay: + if packet_received and ((time.monotonic() - last_transmit_time) > transmit_delay): # send back the received packet if rfm.spreading_factor == 6: payload = bytearray(40) diff --git a/examples/rfm_lora_sf_node.py b/examples/rfm_lora_sf_node.py index a0ea6b7..325a4f2 100644 --- a/examples/rfm_lora_sf_node.py +++ b/examples/rfm_lora_sf_node.py @@ -50,12 +50,8 @@ # set the time interval (seconds) for sending packets transmit_interval = 10 -# set node addresses -# rfm.node = 1 -# rfm.destination = 100 # initialize counter counter = 0 -ack_failed_counter = 0 # send startup message from my_node message = bytes(f"startup message from node", "UTF-8") if rfm.spreading_factor == 6: @@ -75,7 +71,7 @@ # Wait to receive packets. print("Waiting for packets...") # initialize flag and timer -time_now = time.monotonic() +last_transmit_time = time.monotonic() while True: # Look for a new packet: only accept if addresses to my_node packet = rfm.receive() @@ -87,9 +83,9 @@ print([hex(x) for x in packet]) print(f"RSSI: {rfm.last_rssi}") # send reading after any packet received - if time.monotonic() - time_now > transmit_interval: + if time.monotonic() - last_transmit_time > transmit_interval: # reset timeer - time_now = time.monotonic() + last_transmit_time = time.monotonic() # send a mesage to destination_node from my_node message = bytes(f"message from node {counter}", "UTF-8") if rfm.spreading_factor == 6: From 073912ecbd35aaa52e7b3e63df3cd6d4bad4c68a Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Wed, 19 Feb 2025 16:30:45 -0500 Subject: [PATCH 7/7] additional fix to spreading factor examples --- examples/rfm_lora_sf_base.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/examples/rfm_lora_sf_base.py b/examples/rfm_lora_sf_base.py index 58fd60b..29aae15 100644 --- a/examples/rfm_lora_sf_base.py +++ b/examples/rfm_lora_sf_base.py @@ -63,7 +63,9 @@ print("Waiting for packets...") # initialize flag and timer # set a delay before sending the echo packet -transmit_delay = 0.5 +# avoide multibples of .5 second to minimize chances of node missing +# the packet between receive attempts +transmit_delay = 0.75 last_transmit_time = 0 packet_received = False while True: