From 2e9aa20839a749960d37d73f7fd754adc7c7e11a Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Fri, 24 Jan 2020 21:29:40 +0000 Subject: [PATCH 01/19] adding reliable datagram mode --- adafruit_rfm69.py | 139 ++++++++++++++++++++++++++++++++++++---------- 1 file changed, 110 insertions(+), 29 deletions(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index 2fe8184..861fe88 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -68,6 +68,7 @@ * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice """ import time +import random from micropython import const @@ -124,6 +125,11 @@ # RadioHead specific compatibility constants. _RH_BROADCAST_ADDRESS = const(0xFF) +# The acknowledgement bit in the FLAGS +# The top 4 bits of the flags are reserved for RadioHead. The lower 4 bits are reserved +# for application layer use. +_RH_FLAGS_ACK = const(0x80) +_RH_FLAGS_RETRY = const(0x40) # User facing constants: SLEEP_MODE = 0b000 @@ -345,7 +351,14 @@ def __init__(self, spi, cs, reset, frequency, *, sync_word=b'\x2D\xD4', self.encryption_key = encryption_key # Set transmit power to 13 dBm, a safe value any module supports. self.tx_power = 13 - + # initialize ack timeout(seconds) and retries + self.ack_timeout = .2 + self.ack_retries = 5 + self.ack_delay = None + #initialize sequence number + self.sequence_number = 0 + #initialize seen Ids list + self.seen_ids = bytearray(256) # pylint: disable=no-member # Reconsider this disable when it can be tested. def _read_into(self, address, buf, length=None): @@ -728,8 +741,59 @@ def send(self, data, timeout=2., keep_listening=False, if timed_out: raise RuntimeError('Timeout during packet send') + + def send_with_ack(self, data, + tx_header=(_RH_BROADCAST_ADDRESS, _RH_BROADCAST_ADDRESS, 0, 0)): + """Send a string of data using the transmitter. + You can only send 60 bytes at a time + (limited by chip's FIFO size and appended headers). + This appends a 4 byte header to be compatible with the RadioHead library. + The tx_header defaults to using the Broadcast addresses. It may be overidden + by specifying a 4-tuple of bytes containing (To,From,ID,Flags) + retries ... + withACK ... + """ + if self.ack_retries: + retries_remaining = self.ack_retries + else: + retries_remaining = 1 + got_ack = False + self.sequence_number = (self.sequence_number + 1)&0xff + while not got_ack and retries_remaining: + #print("retries remaining ",retries_remaining) + tx_to = tx_header[0] + tx_from = tx_header[1] + tx_id = self.sequence_number + tx_flags = tx_header[3] + if retries_remaining != self.ack_retries: + tx_flags |= _RH_FLAGS_RETRY + tx_flags |= ((self.ack_retries - retries_remaining)&0xf) + # set up ack timeout now to minimize delay after sending packet + ack_timeout = self.ack_timeout + self.ack_timeout * random.random() + self.send(data, tx_header=(tx_to, tx_from, tx_id, tx_flags), keep_listening=True) + # Don't look for ACK from Broadcast message + if tx_to == _RH_BROADCAST_ADDRESS: + got_ack = True + else: + # wait for a packet from our destination + # randomly adjust timeout + ack_packet = self.receive(timeout=ack_timeout, with_header=True, rx_filter=tx_from) + if ack_packet is not None: + if ack_packet[3] & _RH_FLAGS_ACK: + # check the ID + if ack_packet[2] == tx_id: + got_ack = True + #print('Ack Received (raw header):', [hex(x) for x in ack_packet[0:4]]) + break + # pause before next retry -- random delay + time.sleep(ack_timeout) + retries_remaining = retries_remaining - 1 + return got_ack + + #pylint: disable=too-many-arguments + #pylint: disable=too-many-branches def receive(self, timeout=0.5, keep_listening=True, with_header=False, - rx_filter=_RH_BROADCAST_ADDRESS): + rx_filter=_RH_BROADCAST_ADDRESS, with_ack=False): """Wait to receive a packet from the receiver. Will wait for up to timeout_s amount of seconds for a packet to be received and decoded. If a packet is found the payload bytes are returned, otherwise None is returned (which indicates the timeout elapsed with no @@ -749,15 +813,16 @@ def receive(self, timeout=0.5, keep_listening=True, with_header=False, is equal to 0xff then the packet will be accepted and returned to the caller. If rx_filter is not 0xff and packet[0] does not match rx_filter then the packet is ignored and None is returned. + If with_ack is True, send an ACK after receipt """ timed_out = False if timeout is not None: - # Make sure we are listening for packets. - self.listen() # Wait for the payload_ready interrupt. This is not ideal and will # surely miss or overflow the FIFO when packets aren't read fast # enough, however it's the best that can be done from Python without # interrupt supports. + # Make sure we are listening for packets. + self.listen() start = time.monotonic() timed_out = False while not timed_out and not self.payload_ready: @@ -766,34 +831,50 @@ def receive(self, timeout=0.5, keep_listening=True, with_header=False, # Payload ready is set, a packet is in the FIFO. packet = None # Enter idle mode to stop receiving other packets. - self.idle() - if timed_out: - return None - # Read the data from the FIFO. - with self._device as device: - self._BUFFER[0] = _REG_FIFO & 0x7F # Strip out top bit to set 0 - # value (read). - device.write(self._BUFFER, end=1) - # Read the length of the FIFO. - device.readinto(self._BUFFER, end=1) - fifo_length = self._BUFFER[0] - # Handle if the received packet is too small to include the 4 byte - # RadioHead header--reject this packet and ignore it. - if fifo_length < 4: - # Invalid packet, ignore it. However finish reading the FIFO - # to clear the packet. - device.readinto(self._BUFFER, end=fifo_length) - packet = None - else: - packet = bytearray(fifo_length) - device.readinto(packet) - if (rx_filter != _RH_BROADCAST_ADDRESS and packet[0] != _RH_BROADCAST_ADDRESS - and packet[0] != rx_filter): + #self.idle() + if not timed_out: + # Read the data from the FIFO. + with self._device as device: + self._BUFFER[0] = _REG_FIFO & 0x7F # Strip out top bit to set 0 + # value (read). + device.write(self._BUFFER, end=1) + # Read the length of the FIFO. + device.readinto(self._BUFFER, end=1) + fifo_length = self._BUFFER[0] + # Handle if the received packet is too small to include the 4 byte + # RadioHead header--reject this packet and ignore it. + if fifo_length < 5: + # Invalid packet, ignore it. However finish reading the FIFO + # to clear the packet. + device.readinto(self._BUFFER, end=fifo_length) packet = None - elif not with_header: # skip the header if not wanted + else: + packet = bytearray(fifo_length) + device.readinto(packet) + if (rx_filter != _RH_BROADCAST_ADDRESS and packet[0] != _RH_BROADCAST_ADDRESS + and packet[0] != rx_filter): + packet = None + if packet is not None: + #send ACK unless this was an ACK or a broadcast + if with_ack and ((packet[3]&_RH_FLAGS_ACK) == 0) \ + and (packet[0] != _RH_BROADCAST_ADDRESS): + # delay before sending Ack to give receiver a chance to get ready + if self.ack_delay is not None: + time.sleep(self.ack_delay) + #print('Sending ACK: ', [hex(x) for x in packet[0:4]]) + data = bytes("!", "UTF-8") + self.send(data, tx_header=(packet[1], packet[0], + packet[2], packet[3]|_RH_FLAGS_ACK)) + if self.seen_ids[packet[1]] == packet[2]: + packet = None + else: + self.seen_ids[packet[1]] = packet[2] + if not with_header and packet is not None: # skip the header if not wanted packet = packet[4:] - # Listen again if necessary and return the result packet. if keep_listening: self.listen() + else: + # Enter idle mode to stop receiving other packets. + self.idle() return packet From 0e54765577b546825c73a7c79533eb42ba599043 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Sat, 25 Jan 2020 21:09:05 +0000 Subject: [PATCH 02/19] revise use of spi read/write -- cleanup --- adafruit_rfm69.py | 71 ++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 35 deletions(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index 861fe88..68d021b 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -388,7 +388,20 @@ def _write_from(self, address, buf, length=None): self._BUFFER[0] = (address | 0x80) & 0xFF # Set top bit to 1 to # indicate a write. device.write(self._BUFFER, end=1) - device.write(buf, end=length) + device.write(buf, end=length) # send data + + def _write_fifo_from(self, buf, length=None): + # Write a number of bytes to the transmit FIFO and taken from the + # provided buffer. If no length is specified (the default) the entire + # buffer is written. + if length is None: + length = len(buf) + with self._device as device: + self._BUFFER[0] = (_REG_FIFO | 0x80) & 0xFF # Set top bit to 1 to + # indicate a write. + self._BUFFER[1] = length & 0xFF # Set packt length + device.write(self._BUFFER, end=2) # send address and lenght) + device.write(buf, end=length) # send data def _write_u8(self, address, val): # Write a byte register to the chip. Specify the 7-bit address and the @@ -709,20 +722,15 @@ def send(self, data, timeout=2., keep_listening=False, # pylint: enable=len-as-condition self.idle() # Stop receiving to clear FIFO and keep it clear. # Fill the FIFO with a packet to send. - with self._device as device: - self._BUFFER[0] = (_REG_FIFO | 0x80) # Set top bit to 1 to - # indicate a write. - self._BUFFER[1] = (len(data) + 4) & 0xFF - # Add 4 bytes of headers to match RadioHead library. - # Just use the defaults for global broadcast to all receivers - # for now. - self._BUFFER[2] = tx_header[0] # Header: To - self._BUFFER[3] = tx_header[1] # Header: From - self._BUFFER[4] = tx_header[2] # Header: Id - self._BUFFER[5] = tx_header[3] # Header: Flags - device.write(self._BUFFER, end=6) - # Now send the payload. - device.write(data) + # Combine header and data to form payload + payload = bytearray(4) + payload[0] = tx_header[0] + payload[1] = tx_header[1] + payload[2] = tx_header[2] + payload[3] = tx_header[3] + payload = payload + data + # Write payload to transmit fifo + self._write_fifo_from(payload) # Turn on transmit mode to send out the packet. self.transmit() # Wait for packet sent interrupt with explicit polling (not ideal but @@ -831,29 +839,22 @@ def receive(self, timeout=0.5, keep_listening=True, with_header=False, # Payload ready is set, a packet is in the FIFO. packet = None # Enter idle mode to stop receiving other packets. - #self.idle() + self.idle() if not timed_out: # Read the data from the FIFO. - with self._device as device: - self._BUFFER[0] = _REG_FIFO & 0x7F # Strip out top bit to set 0 - # value (read). - device.write(self._BUFFER, end=1) - # Read the length of the FIFO. - device.readinto(self._BUFFER, end=1) - fifo_length = self._BUFFER[0] - # Handle if the received packet is too small to include the 4 byte - # RadioHead header--reject this packet and ignore it. - if fifo_length < 5: - # Invalid packet, ignore it. However finish reading the FIFO - # to clear the packet. - device.readinto(self._BUFFER, end=fifo_length) + # Read the length of the FIFO. + fifo_length = self._read_u8(_REG_FIFO) + # Handle if the received packet is too small to include the 4 byte + # RadioHead header and at least one byte of data --reject this packet and ignore it. + if fifo_length > 0: # read and clear the FIFO if anything in it + packet = bytearray(fifo_length) + self._read_into(_REG_FIFO, packet) + if fifo_length < 5: + packet = None + else: + if (rx_filter != _RH_BROADCAST_ADDRESS and packet[0] != _RH_BROADCAST_ADDRESS + and packet[0] != rx_filter): packet = None - else: - packet = bytearray(fifo_length) - device.readinto(packet) - if (rx_filter != _RH_BROADCAST_ADDRESS and packet[0] != _RH_BROADCAST_ADDRESS - and packet[0] != rx_filter): - packet = None if packet is not None: #send ACK unless this was an ACK or a broadcast if with_ack and ((packet[3]&_RH_FLAGS_ACK) == 0) \ From 169b5c6471a31d4539ed8e15a48796b7c7715348 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Mon, 27 Jan 2020 13:31:29 +0000 Subject: [PATCH 03/19] cleanup --- adafruit_rfm69.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index 68d021b..8b49ca7 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -768,7 +768,6 @@ def send_with_ack(self, data, got_ack = False self.sequence_number = (self.sequence_number + 1)&0xff while not got_ack and retries_remaining: - #print("retries remaining ",retries_remaining) tx_to = tx_header[0] tx_from = tx_header[1] tx_id = self.sequence_number @@ -791,10 +790,10 @@ def send_with_ack(self, data, # check the ID if ack_packet[2] == tx_id: got_ack = True - #print('Ack Received (raw header):', [hex(x) for x in ack_packet[0:4]]) break # pause before next retry -- random delay - time.sleep(ack_timeout) + if not got_ack: + time.sleep(ack_timeout) retries_remaining = retries_remaining - 1 return got_ack @@ -855,18 +854,17 @@ def receive(self, timeout=0.5, keep_listening=True, with_header=False, if (rx_filter != _RH_BROADCAST_ADDRESS and packet[0] != _RH_BROADCAST_ADDRESS and packet[0] != rx_filter): packet = None - if packet is not None: - #send ACK unless this was an ACK or a broadcast - if with_ack and ((packet[3]&_RH_FLAGS_ACK) == 0) \ + #send ACK unless this was an ACK or a broadcast + elif with_ack and ((packet[3]&_RH_FLAGS_ACK) == 0) \ and (packet[0] != _RH_BROADCAST_ADDRESS): # delay before sending Ack to give receiver a chance to get ready if self.ack_delay is not None: time.sleep(self.ack_delay) - #print('Sending ACK: ', [hex(x) for x in packet[0:4]]) data = bytes("!", "UTF-8") self.send(data, tx_header=(packet[1], packet[0], packet[2], packet[3]|_RH_FLAGS_ACK)) - if self.seen_ids[packet[1]] == packet[2]: + #reject Retries if we have seen them before + if (self.seen_ids[packet[1]] == packet[2]) and (packet[3]&_RH_FLAGS_RETRY): packet = None else: self.seen_ids[packet[1]] = packet[2] From 619ea0d3902faa50b9686536a10f8dd14c8e7fd0 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Sun, 1 Mar 2020 16:05:25 +0000 Subject: [PATCH 04/19] general cleanup --- adafruit_rfm69.py | 73 +++++++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 31 deletions(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index 8b49ca7..5d04190 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -353,12 +353,18 @@ def __init__(self, spi, cs, reset, frequency, *, sync_word=b'\x2D\xD4', self.tx_power = 13 # initialize ack timeout(seconds) and retries self.ack_timeout = .2 + self.receive_timeout = .5 + self.xmit_timeout = 2. self.ack_retries = 5 self.ack_delay = None #initialize sequence number self.sequence_number = 0 #initialize seen Ids list self.seen_ids = bytearray(256) + self.node = _RH_BROADCAST_ADDRESS + self.destination = _RH_BROADCAST_ADDRESS + self.identifier = 0 + self.flags = 0 # pylint: disable=no-member # Reconsider this disable when it can be tested. def _read_into(self, address, buf, length=None): @@ -700,8 +706,8 @@ def frequency_deviation(self, val): self._write_u8(_REG_FDEV_MSB, fdev >> 8) self._write_u8(_REG_FDEV_LSB, fdev & 0xFF) - def send(self, data, timeout=2., keep_listening=False, - tx_header=(_RH_BROADCAST_ADDRESS, _RH_BROADCAST_ADDRESS, 0, 0)): + + def send(self, data, keep_listening=False, tx_header=None): """Send a string of data using the transmitter. You can only send 60 bytes at a time (limited by chip's FIFO size and appended headers). @@ -718,16 +724,23 @@ def send(self, data, timeout=2., keep_listening=False, # buffer be within an expected range of bounds. Disable this check. # pylint: disable=len-as-condition assert 0 < len(data) <= 60 - assert len(tx_header) == 4, "tx header must be 4-tuple (To,From,ID,Flags)" + if tx_header is not None: + assert len(tx_header) == 4, "tx header must be 4-tuple (To,From,ID,Flags)" # pylint: enable=len-as-condition self.idle() # Stop receiving to clear FIFO and keep it clear. # Fill the FIFO with a packet to send. # Combine header and data to form payload payload = bytearray(4) - payload[0] = tx_header[0] - payload[1] = tx_header[1] - payload[2] = tx_header[2] - payload[3] = tx_header[3] + if tx_header is None: + payload[0] = self.destination + payload[1] = self.node + payload[2] = self.identifier + payload[3] = self.flags + else: + payload[0] = tx_header[0] + payload[1] = tx_header[1] + payload[2] = tx_header[2] + payload[3] = tx_header[3] payload = payload + data # Write payload to transmit fifo self._write_fifo_from(payload) @@ -738,7 +751,7 @@ def send(self, data, timeout=2., keep_listening=False, start = time.monotonic() timed_out = False while not timed_out and not self.packet_sent: - if (time.monotonic() - start) >= timeout: + if (time.monotonic() - start) >= self.xmit_timeout: timed_out = True # Listen again if necessary and return the result packet. if keep_listening: @@ -750,8 +763,7 @@ def send(self, data, timeout=2., keep_listening=False, raise RuntimeError('Timeout during packet send') - def send_with_ack(self, data, - tx_header=(_RH_BROADCAST_ADDRESS, _RH_BROADCAST_ADDRESS, 0, 0)): + def send_with_ack(self, data, tx_header=None): """Send a string of data using the transmitter. You can only send 60 bytes at a time (limited by chip's FIFO size and appended headers). @@ -768,10 +780,16 @@ def send_with_ack(self, data, got_ack = False self.sequence_number = (self.sequence_number + 1)&0xff while not got_ack and retries_remaining: - tx_to = tx_header[0] - tx_from = tx_header[1] - tx_id = self.sequence_number - tx_flags = tx_header[3] + if tx_header is None: + tx_to = self.destination + tx_from = self.node + tx_id = self.sequence_number + tx_flags = self.flags + else: + tx_to = tx_header[0] + tx_from = tx_header[1] + tx_id = self.sequence_number + tx_flags = tx_header[3] if retries_remaining != self.ack_retries: tx_flags |= _RH_FLAGS_RETRY tx_flags |= ((self.ack_retries - retries_remaining)&0xf) @@ -784,7 +802,7 @@ def send_with_ack(self, data, else: # wait for a packet from our destination # randomly adjust timeout - ack_packet = self.receive(timeout=ack_timeout, with_header=True, rx_filter=tx_from) + ack_packet = self.receive(timeout=ack_timeout, with_header=True) if ack_packet is not None: if ack_packet[3] & _RH_FLAGS_ACK: # check the ID @@ -797,32 +815,25 @@ def send_with_ack(self, data, retries_remaining = retries_remaining - 1 return got_ack - #pylint: disable=too-many-arguments #pylint: disable=too-many-branches - def receive(self, timeout=0.5, keep_listening=True, with_header=False, - rx_filter=_RH_BROADCAST_ADDRESS, with_ack=False): - """Wait to receive a packet from the receiver. Will wait for up to timeout_s amount of - seconds for a packet to be received and decoded. If a packet is found the payload bytes + def receive(self, keep_listening=True, with_ack=False, timeout=None, with_header=False): + """Wait to receive a packet from the receiver. If a packet is found the payload bytes are returned, otherwise None is returned (which indicates the timeout elapsed with no - reception). If timeout is None then it is not used ( for use with interrupts) + reception). If keep_listening is True (the default) the chip will immediately enter listening mode after reception of a packet, otherwise it will fall back to idle mode and ignore any future reception. A 4-byte header must be prepended to the data for compatibilty with the RadioHead library. - The header consists of a 4 bytes (To,From,ID,Flags). The default setting will accept - any incomming packet and strip the header before returning the packet to the caller. + The header consists of a 4 bytes (To,From,ID,Flags). The default setting will strip + the header before returning the packet to the caller. If with_header is True then the 4 byte header will be returned with the packet. The payload then begins at packet[4]. - rx_fliter may be set to reject any "non-broadcast" packets that do not contain the - specfied "To" value in the header. - if rx_filter is set to 0xff (_RH_BROADCAST_ADDRESS) or if the "To" field (packet[[0]) - is equal to 0xff then the packet will be accepted and returned to the caller. - If rx_filter is not 0xff and packet[0] does not match rx_filter then - the packet is ignored and None is returned. If with_ack is True, send an ACK after receipt """ timed_out = False + if timeout is None: + timeout = self.receive_timeout if timeout is not None: # Wait for the payload_ready interrupt. This is not ideal and will # surely miss or overflow the FIFO when packets aren't read fast @@ -851,8 +862,8 @@ def receive(self, timeout=0.5, keep_listening=True, with_header=False, if fifo_length < 5: packet = None else: - if (rx_filter != _RH_BROADCAST_ADDRESS and packet[0] != _RH_BROADCAST_ADDRESS - and packet[0] != rx_filter): + if (self.node != _RH_BROADCAST_ADDRESS and packet[0] != _RH_BROADCAST_ADDRESS + and packet[0] != self.node): packet = None #send ACK unless this was an ACK or a broadcast elif with_ack and ((packet[3]&_RH_FLAGS_ACK) == 0) \ From 28d225eef352454d70af3aa48e825c1b38c1cf20 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Thu, 5 Mar 2020 16:24:52 +0000 Subject: [PATCH 05/19] sync with master --- adafruit_rfm69.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index f8e243d..2567962 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -351,7 +351,6 @@ def __init__(self, spi, cs, reset, frequency, *, sync_word=b'\x2D\xD4', self.encryption_key = encryption_key # Set transmit power to 13 dBm, a safe value any module supports. self.tx_power = 13 -<<<<<<< HEAD # initialize ack timeout(seconds) and retries self.ack_timeout = .2 self.receive_timeout = .5 @@ -366,11 +365,9 @@ def __init__(self, spi, cs, reset, frequency, *, sync_word=b'\x2D\xD4', self.destination = _RH_BROADCAST_ADDRESS self.identifier = 0 self.flags = 0 -======= # last RSSI reading self.last_rssi = 0. ->>>>>>> jerryn_rssi # pylint: disable=no-member # Reconsider this disable when it can be tested. def _read_into(self, address, buf, length=None): From c268d6b22bd19d14f66aff6944bb565df7316bbe Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Sun, 8 Mar 2020 14:19:12 +0000 Subject: [PATCH 06/19] minor changes to ack_timout --- adafruit_rfm69.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index 2567962..fd1f82f 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -352,7 +352,7 @@ def __init__(self, spi, cs, reset, frequency, *, sync_word=b'\x2D\xD4', # Set transmit power to 13 dBm, a safe value any module supports. self.tx_power = 13 # initialize ack timeout(seconds) and retries - self.ack_timeout = .2 + self.ack_timeout = .5 self.receive_timeout = .5 self.xmit_timeout = 2. self.ack_retries = 5 @@ -367,7 +367,6 @@ def __init__(self, spi, cs, reset, frequency, *, sync_word=b'\x2D\xD4', self.flags = 0 # last RSSI reading self.last_rssi = 0. - # pylint: disable=no-member # Reconsider this disable when it can be tested. def _read_into(self, address, buf, length=None): @@ -796,16 +795,13 @@ def send_with_ack(self, data, tx_header=None): if retries_remaining != self.ack_retries: tx_flags |= _RH_FLAGS_RETRY tx_flags |= ((self.ack_retries - retries_remaining)&0xf) - # set up ack timeout now to minimize delay after sending packet - ack_timeout = self.ack_timeout + self.ack_timeout * random.random() self.send(data, tx_header=(tx_to, tx_from, tx_id, tx_flags), keep_listening=True) # Don't look for ACK from Broadcast message if tx_to == _RH_BROADCAST_ADDRESS: got_ack = True else: # wait for a packet from our destination - # randomly adjust timeout - ack_packet = self.receive(timeout=ack_timeout, with_header=True) + ack_packet = self.receive(timeout=self.ack_timeout, with_header=True) if ack_packet is not None: if ack_packet[3] & _RH_FLAGS_ACK: # check the ID @@ -814,7 +810,8 @@ def send_with_ack(self, data, tx_header=None): break # pause before next retry -- random delay if not got_ack: - time.sleep(ack_timeout) + # delay by random amount before next try + time.sleep(self.ack_timeout + self.ack_timeout * random.random()) retries_remaining = retries_remaining - 1 return got_ack @@ -851,7 +848,7 @@ def receive(self, keep_listening=True, with_ack=False, timeout=None, with_header timed_out = True # Payload ready is set, a packet is in the FIFO. packet = None - # save RSSI + # save last RSSI reading self.last_rssi = self.rssi # Enter idle mode to stop receiving other packets. self.idle() From 5d2de50a46faad6350a867cf9b6b49516e02bf9e Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Mon, 9 Mar 2020 13:34:31 +0000 Subject: [PATCH 07/19] cleanup --- adafruit_rfm69.py | 103 ++++++++++++++++++++-------------------------- 1 file changed, 45 insertions(+), 58 deletions(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index fd1f82f..e695702 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -26,9 +26,6 @@ CircuitPython RFM69 packet radio module. This supports basic RadioHead-compatible sending and receiving of packets with RFM69 series radios (433/915Mhz). -.. note:: This does NOT support advanced RadioHead features like guaranteed delivery--only 'raw' - packets are currently supported. - .. warning:: This is NOT for LoRa radios! .. note:: This is a 'best effort' at receiving data using pure Python code--there is not interrupt @@ -36,7 +33,7 @@ You will have the most luck using this in simple low bandwidth scenarios like sending and receiving a 60 byte packet at a time--don't try to receive many kilobytes of data at a time! -* Author(s): Tony DiCola +* Author(s): Tony DiCola, Jerry Needell Implementation Notes -------------------- @@ -182,16 +179,13 @@ class RFM69: encoding, 250kbit/s bitrate, and 250khz frequency deviation. To change this requires explicitly setting the radio's bitrate and encoding register bits. Read the datasheet and study the init function to see an example of this--advanced users only! Advanced RadioHead features like - address/node specific packets or guaranteed delivery are not supported. Only simple broadcast - of packets to all listening radios is supported. Features like addressing and guaranteed - delivery need to be implemented at an application level. + address/node specific packets or "reliable datagram" delivery are supported however due to the + limitations noted, "reliable datagram" is still subject to missed packets but with it, the + sender is notified if a packe has potentially been missed. """ - # Global buffer to hold data sent and received with the chip. This must be - # at least as large as the FIFO on the chip (66 bytes)! Keep this on the - # class level to ensure only one copy ever exists (with the trade-off that - # this is NOT re-entrant or thread safe code by design). - _BUFFER = bytearray(66) + # Global buffer for SPI commands. + _BUFFER = bytearray(4) class _RegisterBits: # Class to simplify access to the many configuration bits avaialable @@ -351,21 +345,26 @@ def __init__(self, spi, cs, reset, frequency, *, sync_word=b'\x2D\xD4', self.encryption_key = encryption_key # Set transmit power to 13 dBm, a safe value any module supports. self.tx_power = 13 - # initialize ack timeout(seconds) and retries - self.ack_timeout = .5 + #initialize timeouts and delays delays + self.ack_wait = .5 self.receive_timeout = .5 self.xmit_timeout = 2. self.ack_retries = 5 self.ack_delay = None - #initialize sequence number + #initialize sequence number counter for reliabe datagram mode self.sequence_number = 0 - #initialize seen Ids list + #create seen Ids list self.seen_ids = bytearray(256) + #initialize packet header + #node address - default is broadcast self.node = _RH_BROADCAST_ADDRESS + #destination address - default is broadcast self.destination = _RH_BROADCAST_ADDRESS + #ID - contains seq count for reliable datagram mode self.identifier = 0 + #flags - identifies ack/reetry packet for reliable datagram mode self.flags = 0 - # last RSSI reading + #initialize last RSSI reading self.last_rssi = 0. # pylint: disable=no-member # Reconsider this disable when it can be tested. @@ -714,9 +713,9 @@ def send(self, data, keep_listening=False, tx_header=None): You can only send 60 bytes at a time (limited by chip's FIFO size and appended headers). This appends a 4 byte header to be compatible with the RadioHead library. - The tx_header defaults to using the Broadcast addresses. It may be overidden - by specifying a 4-tuple of bytes containing (To,From,ID,Flags) - The timeout is just to prevent a hang (arbitrarily set to 2 seconds) + The tx_header defaults to using the initialized attributes: + (destination,node,identifier,flags) + It may be overidden by specifying a 4-tuple of bytes containing (To,From,ID,Flags) The keep_listening argument should be set to True if you want to start listening automatically after the packet is sent. The default setting is False. """ @@ -733,12 +732,12 @@ def send(self, data, keep_listening=False, tx_header=None): # Fill the FIFO with a packet to send. # Combine header and data to form payload payload = bytearray(4) - if tx_header is None: + if tx_header is None: #use attributes payload[0] = self.destination payload[1] = self.node payload[2] = self.identifier payload[3] = self.flags - else: + else: #use header passed as argument payload[0] = tx_header[0] payload[1] = tx_header[1] payload[2] = tx_header[2] @@ -755,25 +754,20 @@ def send(self, data, keep_listening=False, tx_header=None): while not timed_out and not self.packet_sent: if (time.monotonic() - start) >= self.xmit_timeout: timed_out = True - # Listen again if necessary and return the result packet. + # Listen again if requested. if keep_listening: self.listen() - else: - # Enter idle mode to stop receiving other packets. + else: # Enter idle mode to stop receiving other packets. self.idle() if timed_out: raise RuntimeError('Timeout during packet send') - def send_with_ack(self, data, tx_header=None): - """Send a string of data using the transmitter. - You can only send 60 bytes at a time - (limited by chip's FIFO size and appended headers). - This appends a 4 byte header to be compatible with the RadioHead library. - The tx_header defaults to using the Broadcast addresses. It may be overidden - by specifying a 4-tuple of bytes containing (To,From,ID,Flags) - retries ... - withACK ... + def send_with_ack(self, data): + """Reliabe Datagram mode: + Send a packet with data and wait for an ACK response. + The packet header is automatically generated. + If enabled, the packet tranmsiion will be retried on failure """ if self.ack_retries: retries_remaining = self.ack_retries @@ -782,37 +776,29 @@ def send_with_ack(self, data, tx_header=None): got_ack = False self.sequence_number = (self.sequence_number + 1)&0xff while not got_ack and retries_remaining: - if tx_header is None: - tx_to = self.destination - tx_from = self.node - tx_id = self.sequence_number - tx_flags = self.flags - else: - tx_to = tx_header[0] - tx_from = tx_header[1] - tx_id = self.sequence_number - tx_flags = tx_header[3] - if retries_remaining != self.ack_retries: - tx_flags |= _RH_FLAGS_RETRY - tx_flags |= ((self.ack_retries - retries_remaining)&0xf) - self.send(data, tx_header=(tx_to, tx_from, tx_id, tx_flags), keep_listening=True) + self.identifier = self.sequence_number + self.send(data, keep_listening=True) # Don't look for ACK from Broadcast message - if tx_to == _RH_BROADCAST_ADDRESS: + if self.destination == _RH_BROADCAST_ADDRESS: got_ack = True else: # wait for a packet from our destination - ack_packet = self.receive(timeout=self.ack_timeout, with_header=True) + ack_packet = self.receive(timeout=self.ack_wait, with_header=True) if ack_packet is not None: if ack_packet[3] & _RH_FLAGS_ACK: # check the ID - if ack_packet[2] == tx_id: + if ack_packet[2] == self.identifier: got_ack = True break # pause before next retry -- random delay if not got_ack: # delay by random amount before next try - time.sleep(self.ack_timeout + self.ack_timeout * random.random()) + time.sleep(self.ack_wait + self.ack_wait * random.random()) retries_remaining = retries_remaining - 1 + # set flags in packet header + self.flags |= _RH_FLAGS_RETRY + self.flags |= ((self.ack_retries - retries_remaining)&0xf) + self.flags = 0 # clear flags return got_ack #pylint: disable=too-many-branches @@ -823,19 +809,19 @@ def receive(self, keep_listening=True, with_ack=False, timeout=None, with_header If keep_listening is True (the default) the chip will immediately enter listening mode after reception of a packet, otherwise it will fall back to idle mode and ignore any future reception. - A 4-byte header must be prepended to the data for compatibilty with the + All packets must have a 4 byte header A 4-byte header for compatibilty with the RadioHead library. - The header consists of a 4 bytes (To,From,ID,Flags). The default setting will strip + The header consists of 4 bytes (To,From,ID,Flags). The default setting will strip the header before returning the packet to the caller. If with_header is True then the 4 byte header will be returned with the packet. The payload then begins at packet[4]. - If with_ack is True, send an ACK after receipt + If with_ack is True, send an ACK after receipt (Reliable Datagram mode) """ timed_out = False if timeout is None: timeout = self.receive_timeout if timeout is not None: - # Wait for the payload_ready interrupt. This is not ideal and will + # Wait for the payload_ready signal. This is not ideal and will # surely miss or overflow the FIFO when packets aren't read fast # enough, however it's the best that can be done from Python without # interrupt supports. @@ -873,13 +859,14 @@ def receive(self, keep_listening=True, with_ack=False, timeout=None, with_header # delay before sending Ack to give receiver a chance to get ready if self.ack_delay is not None: time.sleep(self.ack_delay) + #send ACK packet to sender data = bytes("!", "UTF-8") self.send(data, tx_header=(packet[1], packet[0], packet[2], packet[3]|_RH_FLAGS_ACK)) - #reject Retries if we have seen them before + #reject Retries if we have seen this idetifier from this source before if (self.seen_ids[packet[1]] == packet[2]) and (packet[3]&_RH_FLAGS_RETRY): packet = None - else: + else: # save the packet identifier for this source self.seen_ids[packet[1]] = packet[2] if not with_header and packet is not None: # skip the header if not wanted packet = packet[4:] From c1e4c8ce1b5db0f7b3e4f450af7d7c5dbde18000 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Mon, 9 Mar 2020 13:39:18 +0000 Subject: [PATCH 08/19] lint - fix docstring --- adafruit_rfm69.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index e695702..0142546 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -714,7 +714,7 @@ def send(self, data, keep_listening=False, tx_header=None): (limited by chip's FIFO size and appended headers). This appends a 4 byte header to be compatible with the RadioHead library. The tx_header defaults to using the initialized attributes: - (destination,node,identifier,flags) + (destination,node,identifier,flags) It may be overidden by specifying a 4-tuple of bytes containing (To,From,ID,Flags) The keep_listening argument should be set to True if you want to start listening automatically after the packet is sent. The default setting is False. From 01d5b59ec0b26ae9180864601c64aac3fb5464e0 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Mon, 9 Mar 2020 15:44:54 +0000 Subject: [PATCH 09/19] remove test code that wrote retry count to flags " --- adafruit_rfm69.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index 0142546..f135667 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -795,9 +795,8 @@ def send_with_ack(self, data): # delay by random amount before next try time.sleep(self.ack_wait + self.ack_wait * random.random()) retries_remaining = retries_remaining - 1 - # set flags in packet header + # set retry flag in packet header self.flags |= _RH_FLAGS_RETRY - self.flags |= ((self.ack_retries - retries_remaining)&0xf) self.flags = 0 # clear flags return got_ack From 3ce65cddfa29f0f1fe04837d073c7d4f71b567b2 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Wed, 11 Mar 2020 20:55:50 +0000 Subject: [PATCH 10/19] add examples for addressed comminication with ACK and rfm69_header.py --- examples/rfm69_header.py | 42 ++++++++++++++++++++++++ examples/rfm69_node1_ack.py | 65 +++++++++++++++++++++++++++++++++++++ examples/rfm69_node2_ack.py | 56 ++++++++++++++++++++++++++++++++ 3 files changed, 163 insertions(+) create mode 100644 examples/rfm69_header.py create mode 100644 examples/rfm69_node1_ack.py create mode 100644 examples/rfm69_node2_ack.py diff --git a/examples/rfm69_header.py b/examples/rfm69_header.py new file mode 100644 index 0000000..93e0999 --- /dev/null +++ b/examples/rfm69_header.py @@ -0,0 +1,42 @@ +# Example to display raw packets including header +# Author: Jerry Needell +# +import board +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 = board.SPI() + +# 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' + +# Wait to receive packets. +print('Waiting for packets...') +#initialize flag and timer +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('RSSI: {0}'.format(rfm69.last_rssi)) + # send reading after any packet received diff --git a/examples/rfm69_node1_ack.py b/examples/rfm69_node1_ack.py new file mode 100644 index 0000000..ea07a6f --- /dev/null +++ b/examples/rfm69_node1_ack.py @@ -0,0 +1,65 @@ +# Example to send a packet periodically between addressed nodes with ACK +# Author: Jerry Needell +# +import time +import board +import digitalio +import adafruit_rfm69 + +# set the time interval (seconds) for sending packets +transmit_interval=5 + +# 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 = board.SPI() + +# 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 = .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) diff --git a/examples/rfm69_node2_ack.py b/examples/rfm69_node2_ack.py new file mode 100644 index 0000000..3012f49 --- /dev/null +++ b/examples/rfm69_node2_ack.py @@ -0,0 +1,56 @@ +# Example to receive addressed packed with ACK and send a response +# Author: Jerry Needell +# +import time +import board +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 = board.SPI() + +# 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 = .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 .5 sec afterafter any packet received + time.sleep(.5) + 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) From e6469fdd317f9ff65a7c78e19486015b805ceb21 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Fri, 13 Mar 2020 11:27:02 +0000 Subject: [PATCH 11/19] add examples --- examples/rfm69_header.py | 3 +- examples/rfm69_node1.py | 58 +++++++++++++++ examples/rfm69_node1_ack.py | 4 +- examples/rfm69_node1_bonnet.py | 118 +++++++++++++++++++++++++++++++ examples/rfm69_node2.py | 57 +++++++++++++++ examples/rfm69_node2_ack.py | 4 +- examples/rfm69_rpi_simpletest.py | 70 ++++++++++++++++++ 7 files changed, 309 insertions(+), 5 deletions(-) create mode 100644 examples/rfm69_node1.py create mode 100644 examples/rfm69_node1_bonnet.py create mode 100644 examples/rfm69_node2.py create mode 100644 examples/rfm69_rpi_simpletest.py diff --git a/examples/rfm69_header.py b/examples/rfm69_header.py index 93e0999..4e1a83b 100644 --- a/examples/rfm69_header.py +++ b/examples/rfm69_header.py @@ -2,6 +2,7 @@ # Author: Jerry Needell # import board +import busio import digitalio import adafruit_rfm69 @@ -17,7 +18,7 @@ RESET = digitalio.DigitalInOut(board.D25) # Initialize SPI bus. -spi = board.SPI() +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio rfm69 = adafruit_rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) diff --git a/examples/rfm69_node1.py b/examples/rfm69_node1.py new file mode 100644 index 0000000..78fc23f --- /dev/null +++ b/examples/rfm69_node1.py @@ -0,0 +1,58 @@ +# 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=5 + +# 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) + button_pressed = None diff --git a/examples/rfm69_node1_ack.py b/examples/rfm69_node1_ack.py index ea07a6f..bea5bae 100644 --- a/examples/rfm69_node1_ack.py +++ b/examples/rfm69_node1_ack.py @@ -3,6 +3,7 @@ # import time import board +import busio import digitalio import adafruit_rfm69 @@ -19,8 +20,7 @@ RESET = digitalio.DigitalInOut(board.D25) # Initialize SPI bus. -spi = board.SPI() - +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio rfm69 = adafruit_rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) diff --git a/examples/rfm69_node1_bonnet.py b/examples/rfm69_node1_bonnet.py new file mode 100644 index 0000000..c94e7fe --- /dev/null +++ b/examples/rfm69_node1_bonnet.py @@ -0,0 +1,118 @@ +# Example to send a packet periodically between addressed nodes +# Author: Jerry Needell +# +import board +import busio +import digitalio +# Import the SSD1306 module. +import adafruit_ssd1306 +import adafruit_rfm69 + +# Button A +btnA = digitalio.DigitalInOut(board.D5) +btnA.direction = digitalio.Direction.INPUT +btnA.pull = digitalio.Pull.UP + +# Button B +btnB = digitalio.DigitalInOut(board.D6) +btnB.direction = digitalio.Direction.INPUT +btnB.pull = digitalio.Pull.UP + +# Button C +btnC = digitalio.DigitalInOut(board.D12) +btnC.direction = digitalio.Direction.INPUT +btnC.pull = digitalio.Pull.UP + +# Create the I2C interface. +i2c = busio.I2C(board.SCL, board.SDA) + +# 128x32 OLED Display +reset_pin = digitalio.DigitalInOut(board.D4) +display = adafruit_ssd1306.SSD1306_I2C(128, 32, i2c, reset=reset_pin) +# Clear the display. +display.fill(0) +display.show() +width = display.width +height = display.height + + + +# 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 + + # Attempt to set up the RFM69 Module +try: + rfm69 = adafruit_rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) + display.text('RFM69: Detected', 0, 0, 1) +except RuntimeError: + # Thrown on version mismatch + display.text('RFM69: ERROR', 0, 0, 1) + +display.show() + + +# 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...') +button_pressed = None +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)) + # Check buttons + if not btnA.value: + button_pressed = "A" + # Button A Pressed + display.fill(0) + display.text('AAA', width-85, height-7, 1) + display.show() + if not btnB.value: + button_pressed = "B" + # Button B Pressed + display.fill(0) + display.text('BBB', width-75, height-7, 1) + display.show() + if not btnC.value: + button_pressed = "C" + # Button C Pressed + display.fill(0) + display.text('CCC', width-65, height-7, 1) + display.show() + # send reading after any button pressed + if button_pressed is not None: + counter = counter + 1 + #send a mesage to destination_node from my_node + rfm69.send(bytes("message number {} from node {} button {}".format(counter,rfm69.node, + button_pressed),"UTF-8"), + keep_listening=True) + button_pressed = None diff --git a/examples/rfm69_node2.py b/examples/rfm69_node2.py new file mode 100644 index 0000000..92278c4 --- /dev/null +++ b/examples/rfm69_node2.py @@ -0,0 +1,57 @@ +# 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(.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) diff --git a/examples/rfm69_node2_ack.py b/examples/rfm69_node2_ack.py index 3012f49..db1b3a5 100644 --- a/examples/rfm69_node2_ack.py +++ b/examples/rfm69_node2_ack.py @@ -3,6 +3,7 @@ # import time import board +import busio import digitalio import adafruit_rfm69 @@ -16,8 +17,7 @@ RESET = digitalio.DigitalInOut(board.D25) # Initialize SPI bus. -spi = board.SPI() - +spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO) # Initialze RFM radio rfm69 = adafruit_rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) diff --git a/examples/rfm69_rpi_simpletest.py b/examples/rfm69_rpi_simpletest.py new file mode 100644 index 0000000..d574d12 --- /dev/null +++ b/examples/rfm69_rpi_simpletest.py @@ -0,0 +1,70 @@ +# Simple example to send a message and then wait indefinitely for messages +# to be received. This uses the default RadioHead compatible GFSK_Rb250_Fd250 +# modulation and packet format for the radio. +# Author: Tony DiCola +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, use these if wiring up the breakout according to the guide: +CS = digitalio.DigitalInOut(board.CE1) +RESET = digitalio.DigitalInOut(board.D25) +# Or uncomment and instead use these if using a Feather M0 RFM69 board +# and the appropriate CircuitPython build: +#CS = digitalio.DigitalInOut(board.RFM69_CS) +#RESET = digitalio.DigitalInOut(board.RFM69_RST) + + +# 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' + +# Print out some chip state: +print('Temperature: {0}C'.format(rfm69.temperature)) +print('Frequency: {0}mhz'.format(rfm69.frequency_mhz)) +print('Bit rate: {0}kbit/s'.format(rfm69.bitrate/1000)) +print('Frequency deviation: {0}hz'.format(rfm69.frequency_deviation)) + +# Send a packet. Note you can only send a packet up to 60 bytes in length. +# This is a limitation of the radio packet size, so if you need to send larger +# amounts of data you will need to break it into smaller send calls. Each send +# call will wait for the previous one to finish before continuing. +rfm69.send(bytes('Hello world!\r\n',"utf-8")) +print('Sent hello world message!') + +# Wait to receive packets. Note that this library can't receive data at a fast +# rate, in fact it can only receive and process one 60 byte packet at a time. +# This means you should only use this for low bandwidth scenarios, like sending +# and receiving a single message at a time. +print('Waiting for packets...') +while True: + packet = rfm69.receive() + # Optionally change the receive timeout from its default of 0.5 seconds: + #packet = rfm69.receive(timeout=5.0) + # If no packet was received during the timeout then None is returned. + if packet is None: + # Packet has not been received + print('Received nothing! Listening again...') + else: + # Received a packet! + # Print out the raw bytes of the packet: + print('Received (raw bytes): {0}'.format(packet)) + # And decode to ASCII text and print it too. Note that you always + # receive raw bytes and need to convert to a text format like ASCII + # if you intend to do string processing on your data. Make sure the + # sending side is sending ASCII data before you try to decode! + packet_text = str(packet, 'ascii') + print('Received (ASCII): {0}'.format(packet_text)) From 7ce393456860867e933988a435a584d09b30d0ba Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Fri, 13 Mar 2020 11:33:00 +0000 Subject: [PATCH 12/19] update ReADME.rst --- README.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.rst b/README.rst index 880750d..3ebb0fa 100644 --- a/README.rst +++ b/README.rst @@ -17,8 +17,6 @@ Introduction CircuitPython RFM69 packet radio module. This supports basic RadioHead-compatible sending and receiving of packets with RFM69 series radios (433/915Mhz). -.. note:: This does NOT support advanced RadioHead features like guaranteed delivery--only 'raw' packets are currently supported. - .. warning:: This is NOT for LoRa radios! .. note:: This is a 'best effort' at receiving data using pure Python code--there is not interrupt From 8a22c608ed407f3b538bedd3b55326467bcd2d4b Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Fri, 20 Mar 2020 15:19:35 +0000 Subject: [PATCH 13/19] merged master updates and ran black --- adafruit_rfm69.py | 92 +++++++++++++++++++------------- examples/rfm69_header.py | 22 ++++---- examples/rfm69_node1.py | 36 ++++++++----- examples/rfm69_node1_ack.py | 45 ++++++++-------- examples/rfm69_node1_bonnet.py | 54 +++++++++++-------- examples/rfm69_node2.py | 43 ++++++++------- examples/rfm69_node2_ack.py | 31 ++++++----- examples/rfm69_rpi_simpletest.py | 34 ++++++------ 8 files changed, 206 insertions(+), 151 deletions(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index 5561ab8..2922caf 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -358,27 +358,28 @@ def __init__( self.encryption_key = encryption_key # Set transmit power to 13 dBm, a safe value any module supports. self.tx_power = 13 - #initialize timeouts and delays delays - self.ack_wait = .5 - self.receive_timeout = .5 - self.xmit_timeout = 2. + # initialize timeouts and delays delays + self.ack_wait = 0.5 + self.receive_timeout = 0.5 + self.xmit_timeout = 2.0 self.ack_retries = 5 self.ack_delay = None - #initialize sequence number counter for reliabe datagram mode + # initialize sequence number counter for reliabe datagram mode self.sequence_number = 0 - #create seen Ids list + # create seen Ids list self.seen_ids = bytearray(256) - #initialize packet header - #node address - default is broadcast + # initialize packet header + # node address - default is broadcast self.node = _RH_BROADCAST_ADDRESS - #destination address - default is broadcast + # destination address - default is broadcast self.destination = _RH_BROADCAST_ADDRESS - #ID - contains seq count for reliable datagram mode + # ID - contains seq count for reliable datagram mode self.identifier = 0 - #flags - identifies ack/reetry packet for reliable datagram mode + # flags - identifies ack/reetry packet for reliable datagram mode self.flags = 0 - #initialize last RSSI reading + # initialize last RSSI reading self.last_rssi = 0.0 + # pylint: disable=no-member # Reconsider this disable when it can be tested. def _read_into(self, address, buf, length=None): @@ -408,7 +409,7 @@ def _write_from(self, address, buf, length=None): self._BUFFER[0] = (address | 0x80) & 0xFF # Set top bit to 1 to # indicate a write. device.write(self._BUFFER, end=1) - device.write(buf, end=length) # send data + device.write(buf, end=length) # send data def _write_fifo_from(self, buf, length=None): # Write a number of bytes to the transmit FIFO and taken from the @@ -418,10 +419,10 @@ def _write_fifo_from(self, buf, length=None): length = len(buf) with self._device as device: self._BUFFER[0] = (_REG_FIFO | 0x80) & 0xFF # Set top bit to 1 to - # indicate a write. + # indicate a write. self._BUFFER[1] = length & 0xFF # Set packt length - device.write(self._BUFFER, end=2) # send address and lenght) - device.write(buf, end=length) # send data + device.write(self._BUFFER, end=2) # send address and lenght) + device.write(buf, end=length) # send data def _write_u8(self, address, val): # Write a byte register to the chip. Specify the 7-bit address and the @@ -746,12 +747,12 @@ def send(self, data, keep_listening=False, tx_header=None): # Fill the FIFO with a packet to send. # Combine header and data to form payload payload = bytearray(4) - if tx_header is None: #use attributes + if tx_header is None: # use attributes payload[0] = self.destination payload[1] = self.node payload[2] = self.identifier payload[3] = self.flags - else: #use header passed as argument + else: # use header passed as argument payload[0] = tx_header[0] payload[1] = tx_header[1] payload[2] = tx_header[2] @@ -786,7 +787,7 @@ def send_with_ack(self, data): else: retries_remaining = 1 got_ack = False - self.sequence_number = (self.sequence_number + 1)&0xff + self.sequence_number = (self.sequence_number + 1) & 0xFF while not got_ack and retries_remaining: self.identifier = self.sequence_number self.send(data, keep_listening=True) @@ -809,11 +810,13 @@ def send_with_ack(self, data): retries_remaining = retries_remaining - 1 # set retry flag in packet header self.flags |= _RH_FLAGS_RETRY - self.flags = 0 # clear flags + self.flags = 0 # clear flags return got_ack - #pylint: disable=too-many-branches - def receive(self, keep_listening=True, with_ack=False, timeout=None, with_header=False): + # pylint: disable=too-many-branches + def receive( + self, keep_listening=True, with_ack=False, timeout=None, with_header=False + ): """Wait to receive a packet from the receiver. If a packet is found the payload bytes are returned, otherwise None is returned (which indicates the timeout elapsed with no reception). @@ -854,36 +857,53 @@ def receive(self, keep_listening=True, with_ack=False, timeout=None, with_header fifo_length = self._read_u8(_REG_FIFO) # Handle if the received packet is too small to include the 4 byte # RadioHead header and at least one byte of data --reject this packet and ignore it. - if fifo_length > 0: # read and clear the FIFO if anything in it + if fifo_length > 0: # read and clear the FIFO if anything in it packet = bytearray(fifo_length) self._read_into(_REG_FIFO, packet) if fifo_length < 5: packet = None else: - if (self.node != _RH_BROADCAST_ADDRESS and packet[0] != _RH_BROADCAST_ADDRESS - and packet[0] != self.node): + if ( + self.node != _RH_BROADCAST_ADDRESS + and packet[0] != _RH_BROADCAST_ADDRESS + and packet[0] != self.node + ): packet = None - #send ACK unless this was an ACK or a broadcast - elif with_ack and ((packet[3]&_RH_FLAGS_ACK) == 0) \ - and (packet[0] != _RH_BROADCAST_ADDRESS): + # send ACK unless this was an ACK or a broadcast + elif ( + with_ack + and ((packet[3] & _RH_FLAGS_ACK) == 0) + and (packet[0] != _RH_BROADCAST_ADDRESS) + ): # delay before sending Ack to give receiver a chance to get ready if self.ack_delay is not None: time.sleep(self.ack_delay) - #send ACK packet to sender + # send ACK packet to sender data = bytes("!", "UTF-8") - self.send(data, tx_header=(packet[1], packet[0], - packet[2], packet[3]|_RH_FLAGS_ACK)) - #reject Retries if we have seen this idetifier from this source before - if (self.seen_ids[packet[1]] == packet[2]) and (packet[3]&_RH_FLAGS_RETRY): + self.send( + data, + tx_header=( + packet[1], + packet[0], + packet[2], + packet[3] | _RH_FLAGS_ACK, + ), + ) + # reject Retries if we have seen this idetifier from this source before + if (self.seen_ids[packet[1]] == packet[2]) and ( + packet[3] & _RH_FLAGS_RETRY + ): packet = None - else: # save the packet identifier for this source + else: # save the packet identifier for this source self.seen_ids[packet[1]] = packet[2] - if not with_header and packet is not None: # skip the header if not wanted + if ( + not with_header and packet is not None + ): # skip the header if not wanted packet = packet[4:] # Listen again if necessary and return the result packet. if keep_listening: self.listen() else: - # Enter idle mode to stop receiving other packets. + # Enter idle mode to stop receiving other packets. self.idle() return packet diff --git a/examples/rfm69_header.py b/examples/rfm69_header.py index 4e1a83b..57f4f45 100644 --- a/examples/rfm69_header.py +++ b/examples/rfm69_header.py @@ -7,14 +7,14 @@ import adafruit_rfm69 # set the time interval (seconds) for sending packets -transmit_interval=10 +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. +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) +CS = digitalio.DigitalInOut(board.CE1) RESET = digitalio.DigitalInOut(board.D25) # Initialize SPI bus. @@ -25,11 +25,13 @@ # 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' +rfm69.encryption_key = ( + b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +) # Wait to receive packets. -print('Waiting for packets...') -#initialize flag and timer +print("Waiting for packets...") +# initialize flag and timer while True: # Look for a new packet: only accept if addresses to my_node packet = rfm69.receive(with_header=True) @@ -37,7 +39,7 @@ 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)) + 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 diff --git a/examples/rfm69_node1.py b/examples/rfm69_node1.py index 78fc23f..17458fe 100644 --- a/examples/rfm69_node1.py +++ b/examples/rfm69_node1.py @@ -9,14 +9,14 @@ # set the time interval (seconds) for sending packets -transmit_interval=5 +transmit_interval = 5 # 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. +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) +CS = digitalio.DigitalInOut(board.CE1) RESET = digitalio.DigitalInOut(board.D25) # Initialize SPI bus. @@ -26,18 +26,22 @@ # 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' +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")) +# 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...') +print("Waiting for packets...") now = time.monotonic() while True: # Look for a new packet: only accept if addresses to my_node @@ -46,13 +50,17 @@ 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)) + 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) + # 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, + ) button_pressed = None diff --git a/examples/rfm69_node1_ack.py b/examples/rfm69_node1_ack.py index bea5bae..9cece3d 100644 --- a/examples/rfm69_node1_ack.py +++ b/examples/rfm69_node1_ack.py @@ -8,15 +8,15 @@ import adafruit_rfm69 # set the time interval (seconds) for sending packets -transmit_interval=5 +transmit_interval = 5 # 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. +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) +CS = digitalio.DigitalInOut(board.CE1) RESET = digitalio.DigitalInOut(board.D25) # Initialize SPI bus. @@ -26,23 +26,25 @@ # 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' +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 = .1 +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")) +# 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() +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) @@ -50,16 +52,17 @@ 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)) + 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() + 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")): + # 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) + print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm69_node1_bonnet.py b/examples/rfm69_node1_bonnet.py index c94e7fe..2de9324 100644 --- a/examples/rfm69_node1_bonnet.py +++ b/examples/rfm69_node1_bonnet.py @@ -4,6 +4,7 @@ import board import busio import digitalio + # Import the SSD1306 module. import adafruit_ssd1306 import adafruit_rfm69 @@ -36,16 +37,15 @@ height = display.height - # set the time interval (seconds) for sending packets -transmit_interval=10 +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. +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) +CS = digitalio.DigitalInOut(board.CE1) RESET = digitalio.DigitalInOut(board.D25) # Initialize SPI bus. @@ -53,31 +53,35 @@ # Initialze RFM radio - # Attempt to set up the RFM69 Module +# Attempt to set up the RFM69 Module try: rfm69 = adafruit_rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) - display.text('RFM69: Detected', 0, 0, 1) + display.text("RFM69: Detected", 0, 0, 1) except RuntimeError: # Thrown on version mismatch - display.text('RFM69: ERROR', 0, 0, 1) + display.text("RFM69: ERROR", 0, 0, 1) display.show() # 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' +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")) +# 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...') +print("Waiting for packets...") button_pressed = None while True: # Look for a new packet: only accept if addresses to my_node @@ -86,33 +90,39 @@ 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)) + 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)) # Check buttons if not btnA.value: button_pressed = "A" # Button A Pressed display.fill(0) - display.text('AAA', width-85, height-7, 1) + display.text("AAA", width - 85, height - 7, 1) display.show() if not btnB.value: button_pressed = "B" # Button B Pressed display.fill(0) - display.text('BBB', width-75, height-7, 1) + display.text("BBB", width - 75, height - 7, 1) display.show() if not btnC.value: button_pressed = "C" # Button C Pressed display.fill(0) - display.text('CCC', width-65, height-7, 1) + display.text("CCC", width - 65, height - 7, 1) display.show() # send reading after any button pressed if button_pressed is not None: counter = counter + 1 - #send a mesage to destination_node from my_node - rfm69.send(bytes("message number {} from node {} button {}".format(counter,rfm69.node, - button_pressed),"UTF-8"), - keep_listening=True) + # send a mesage to destination_node from my_node + rfm69.send( + bytes( + "message number {} from node {} button {}".format( + counter, rfm69.node, button_pressed + ), + "UTF-8", + ), + keep_listening=True, + ) button_pressed = None diff --git a/examples/rfm69_node2.py b/examples/rfm69_node2.py index 92278c4..2eed5e9 100644 --- a/examples/rfm69_node2.py +++ b/examples/rfm69_node2.py @@ -8,11 +8,11 @@ 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. +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) +CS = digitalio.DigitalInOut(board.CE1) RESET = digitalio.DigitalInOut(board.D25) # Initialize SPI bus. @@ -23,20 +23,22 @@ # 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' +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")) +# 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() +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) @@ -44,14 +46,19 @@ 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)) + 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(.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) + # 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, + ) diff --git a/examples/rfm69_node2_ack.py b/examples/rfm69_node2_ack.py index db1b3a5..99e56c9 100644 --- a/examples/rfm69_node2_ack.py +++ b/examples/rfm69_node2_ack.py @@ -8,12 +8,12 @@ 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. +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) +CS = digitalio.DigitalInOut(board.CE1) RESET = digitalio.DigitalInOut(board.D25) # Initialize SPI bus. @@ -23,10 +23,12 @@ # 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' +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 = .1 +rfm69.ack_delay = 0.1 # set node addresses rfm69.node = 2 rfm69.destination = 1 @@ -35,7 +37,7 @@ ack_failed_counter = 0 # Wait to receive packets. -print('Waiting for 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) @@ -43,14 +45,15 @@ 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)) + 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 .5 sec afterafter any packet received - time.sleep(.5) + time.sleep(0.5) 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")): + # 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) + print(" No Ack: ", counter, ack_failed_counter) diff --git a/examples/rfm69_rpi_simpletest.py b/examples/rfm69_rpi_simpletest.py index d574d12..5de4fe9 100644 --- a/examples/rfm69_rpi_simpletest.py +++ b/examples/rfm69_rpi_simpletest.py @@ -11,15 +11,15 @@ # 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. +# 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) # Or uncomment and instead use these if using a Feather M0 RFM69 board # and the appropriate CircuitPython build: -#CS = digitalio.DigitalInOut(board.RFM69_CS) -#RESET = digitalio.DigitalInOut(board.RFM69_RST) +# CS = digitalio.DigitalInOut(board.RFM69_CS) +# RESET = digitalio.DigitalInOut(board.RFM69_RST) # Initialize SPI bus. @@ -30,41 +30,43 @@ # 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' +rfm69.encryption_key = ( + b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" +) # Print out some chip state: -print('Temperature: {0}C'.format(rfm69.temperature)) -print('Frequency: {0}mhz'.format(rfm69.frequency_mhz)) -print('Bit rate: {0}kbit/s'.format(rfm69.bitrate/1000)) -print('Frequency deviation: {0}hz'.format(rfm69.frequency_deviation)) +print("Temperature: {0}C".format(rfm69.temperature)) +print("Frequency: {0}mhz".format(rfm69.frequency_mhz)) +print("Bit rate: {0}kbit/s".format(rfm69.bitrate / 1000)) +print("Frequency deviation: {0}hz".format(rfm69.frequency_deviation)) # Send a packet. Note you can only send a packet up to 60 bytes in length. # This is a limitation of the radio packet size, so if you need to send larger # amounts of data you will need to break it into smaller send calls. Each send # call will wait for the previous one to finish before continuing. -rfm69.send(bytes('Hello world!\r\n',"utf-8")) -print('Sent hello world message!') +rfm69.send(bytes("Hello world!\r\n", "utf-8")) +print("Sent hello world message!") # Wait to receive packets. Note that this library can't receive data at a fast # rate, in fact it can only receive and process one 60 byte packet at a time. # This means you should only use this for low bandwidth scenarios, like sending # and receiving a single message at a time. -print('Waiting for packets...') +print("Waiting for packets...") while True: packet = rfm69.receive() # Optionally change the receive timeout from its default of 0.5 seconds: - #packet = rfm69.receive(timeout=5.0) + # packet = rfm69.receive(timeout=5.0) # If no packet was received during the timeout then None is returned. if packet is None: # Packet has not been received - print('Received nothing! Listening again...') + print("Received nothing! Listening again...") else: # Received a packet! # Print out the raw bytes of the packet: - print('Received (raw bytes): {0}'.format(packet)) + print("Received (raw bytes): {0}".format(packet)) # And decode to ASCII text and print it too. Note that you always # receive raw bytes and need to convert to a text format like ASCII # if you intend to do string processing on your data. Make sure the # sending side is sending ASCII data before you try to decode! - packet_text = str(packet, 'ascii') - print('Received (ASCII): {0}'.format(packet_text)) + packet_text = str(packet, "ascii") + print("Received (ASCII): {0}".format(packet_text)) From 7988086f4ba51ec6cddb564177cfc961270e83f5 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Sun, 29 Mar 2020 16:16:14 +0100 Subject: [PATCH 14/19] changes per review - add docstrings, refactor header arguments, some pylint cleanup --- adafruit_rfm69.py | 180 +++++++++++++++++++----------------- examples/rfm69_node2_ack.py | 4 +- 2 files changed, 99 insertions(+), 85 deletions(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index 2922caf..393af2e 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -76,7 +76,6 @@ __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RFM69.git" -# pylint: disable=bad-whitespace # Internal constants: _REG_FIFO = const(0x00) _REG_OP_MODE = const(0x01) @@ -134,7 +133,6 @@ FS_MODE = 0b010 TX_MODE = 0b011 RX_MODE = 0b100 -# pylint: enable=bad-whitespace # Disable the silly too many instance members warning. Pylint has no knowledge # of the context and is merely guessing at the proper amount of members. This @@ -232,61 +230,33 @@ def __set__(self, obj, val): # Control bits from the registers of the chip: data_mode = _RegisterBits(_REG_DATA_MOD, offset=5, bits=2) - modulation_type = _RegisterBits(_REG_DATA_MOD, offset=3, bits=2) - modulation_shaping = _RegisterBits(_REG_DATA_MOD, offset=0, bits=2) - temp_start = _RegisterBits(_REG_TEMP1, offset=3) - temp_running = _RegisterBits(_REG_TEMP1, offset=2) - sync_on = _RegisterBits(_REG_SYNC_CONFIG, offset=7) - sync_size = _RegisterBits(_REG_SYNC_CONFIG, offset=3, bits=3) - aes_on = _RegisterBits(_REG_PACKET_CONFIG2, offset=0) - pa_0_on = _RegisterBits(_REG_PA_LEVEL, offset=7) - pa_1_on = _RegisterBits(_REG_PA_LEVEL, offset=6) - pa_2_on = _RegisterBits(_REG_PA_LEVEL, offset=5) - output_power = _RegisterBits(_REG_PA_LEVEL, offset=0, bits=5) - rx_bw_dcc_freq = _RegisterBits(_REG_RX_BW, offset=5, bits=3) - rx_bw_mantissa = _RegisterBits(_REG_RX_BW, offset=3, bits=2) - rx_bw_exponent = _RegisterBits(_REG_RX_BW, offset=0, bits=3) - afc_bw_dcc_freq = _RegisterBits(_REG_AFC_BW, offset=5, bits=3) - afc_bw_mantissa = _RegisterBits(_REG_AFC_BW, offset=3, bits=2) - afc_bw_exponent = _RegisterBits(_REG_AFC_BW, offset=0, bits=3) - packet_format = _RegisterBits(_REG_PACKET_CONFIG1, offset=7, bits=1) - dc_free = _RegisterBits(_REG_PACKET_CONFIG1, offset=5, bits=2) - crc_on = _RegisterBits(_REG_PACKET_CONFIG1, offset=4, bits=1) - crc_auto_clear_off = _RegisterBits(_REG_PACKET_CONFIG1, offset=3, bits=1) - address_filter = _RegisterBits(_REG_PACKET_CONFIG1, offset=1, bits=2) - mode_ready = _RegisterBits(_REG_IRQ_FLAGS1, offset=7) - rx_ready = _RegisterBits(_REG_IRQ_FLAGS1, offset=6) - tx_ready = _RegisterBits(_REG_IRQ_FLAGS1, offset=5) - dio_0_mapping = _RegisterBits(_REG_DIO_MAPPING1, offset=6, bits=2) - packet_sent = _RegisterBits(_REG_IRQ_FLAGS2, offset=3) - payload_ready = _RegisterBits(_REG_IRQ_FLAGS2, offset=2) def __init__( @@ -309,19 +279,16 @@ def __init__( # Setup reset as a digital output that's low. self._reset = reset self._reset.switch_to_output(value=False) - # Reset the chip. - self.reset() + self.reset() # Reset the chip. # Check the version of the chip. version = self._read_u8(_REG_VERSION) if version != 0x24: raise RuntimeError( "Failed to find RFM69 with expected version, check wiring!" ) - # Enter idle state. - self.idle() + self.idle() # Enter idle state. # Setup the chip in a similar way to the RadioHead RFM69 library. - # Set FIFO TX condition to not empty and the default FIFO threshold - # to 15. + # Set FIFO TX condition to not empty and the default FIFO threshold to 15. self._write_u8(_REG_FIFO_THRESH, 0b10001111) # Configure low beta off. self._write_u8(_REG_TEST_DAGC, 0x30) @@ -330,6 +297,63 @@ def __init__( self._write_u8(_REG_TEST_PA2, _TEST_PA2_NORMAL) # Set the syncronization word. self.sync_word = sync_word + self.preamble_length = preamble_length # Set the preamble length. + self.frequency_mhz = frequency # Set frequency. + self.encryption_key = encryption_key # Set encryption key. + # set radio configuration parameters + self._configure_radio() + # initialize last RSSI reading + self.last_rssi = 0.0 + """The RSSI of the last received packet. Stored when the packet was received. + This instataneous RSSI value may not be accurate once the + operating mode has been changed. + """ + # initialize timeouts and delays delays + self.ack_wait = 0.5 + """Sets the delay time before attemting a retry after not receiving an AC""" + self.receive_timeout = 0.5 + """Sets the amount of time to poll for a received packet. + If no packet is received, the returned packet will be None + """ + self.xmit_timeout = 2.0 + """Sets the amount of time to wait for the HW to transmit the packet. + This is mainly used to prevent a hang due to a HW issue + """ + self.ack_retries = 5 + """Sets the number of ACK retries before reporting a failure.""" + self.ack_delay = None + """Sets the delay time before attemting to send an ACK. + If ACKs are being missed try setting this to .1 or .2. + """ + # initialize sequence number counter for reliabe datagram mode + self.sequence_number = 0 + # create seen Ids list + self.seen_ids = bytearray(256) + # initialize packet header + # node address - default is broadcast + self.node = _RH_BROADCAST_ADDRESS + """First byte of the RadioHead header. + Set the default address of this Node. (0-255). + If not 255 (0xff) then only packets address to this node will be accepted. + """ + # destination address - default is broadcast + self.destination = _RH_BROADCAST_ADDRESS + """Second byte of the RadioHead header. + Set the default destinationaaddress for packet transmissions. (0-255). + If 255 (0xff) then any receiing node should accept the packet. + """ + # ID - contains seq count for reliable datagram mode + self.identifier = 0 + """Third byte of the RadioHead header. + Automatically set to the sequence number when send_with_ack() used. + """ + # flags - identifies ack/reetry packet for reliable datagram mode + self.flags = 0 + """Fourth byte of the RadioHead header. (Typically set by driver) + Upper 4 bits reserved, lower 4 bits may be used to pass information. + """ + + def _configure_radio(self): # Configure modulation for RadioHead library GFSK_Rb250Fd250 mode # by default. Users with advanced knowledge can manually reconfigure # for any other mode (consulting the datasheet is absolutely @@ -350,35 +374,10 @@ def __init__( self.crc_on = 1 # CRC enabled self.crc_auto_clear = 0 # Clear FIFO on CRC fail self.address_filtering = 0b00 # No address filtering - # Set the preamble length. - self.preamble_length = preamble_length - # Set frequency. - self.frequency_mhz = frequency - # Set encryption key. - self.encryption_key = encryption_key # Set transmit power to 13 dBm, a safe value any module supports. self.tx_power = 13 - # initialize timeouts and delays delays - self.ack_wait = 0.5 - self.receive_timeout = 0.5 - self.xmit_timeout = 2.0 - self.ack_retries = 5 - self.ack_delay = None - # initialize sequence number counter for reliabe datagram mode - self.sequence_number = 0 - # create seen Ids list - self.seen_ids = bytearray(256) - # initialize packet header - # node address - default is broadcast - self.node = _RH_BROADCAST_ADDRESS - # destination address - default is broadcast - self.destination = _RH_BROADCAST_ADDRESS - # ID - contains seq count for reliable datagram mode - self.identifier = 0 - # flags - identifies ack/reetry packet for reliable datagram mode - self.flags = 0 - # initialize last RSSI reading - self.last_rssi = 0.0 + + # pylint: disable=no-member # Reconsider this disable when it can be tested. @@ -684,7 +683,10 @@ def tx_power(self, val): @property def rssi(self): - """The received strength indicator (in dBm) of the last received message.""" + """The received strength indicator (in dBm). + May be inaccuate if not read immediatey. last_rssi contains the value read immediately + receipt of the last packet. + """ # Read RSSI register and convert to value using formula in datasheet. return -self._read_u8(_REG_RSSI_VALUE) / 2.0 @@ -721,14 +723,24 @@ def frequency_deviation(self, val): self._write_u8(_REG_FDEV_MSB, fdev >> 8) self._write_u8(_REG_FDEV_LSB, fdev & 0xFF) - def send(self, data, keep_listening=False, tx_header=None): + def send( + self, + data, + *, + keep_listening=False, + destination=None, + node=None, + identifier=None, + flags=None + ): """Send a string of data using the transmitter. You can only send 60 bytes at a time (limited by chip's FIFO size and appended headers). This appends a 4 byte header to be compatible with the RadioHead library. - The tx_header defaults to using the initialized attributes: + The header defaults to using the initialized attributes: (destination,node,identifier,flags) - It may be overidden by specifying a 4-tuple of bytes containing (To,From,ID,Flags) + It may be temporarily overidden via the kwargs - destination,node,identifier,flags. + Values passed via kwargs do not alter the attribute settings. The keep_listening argument should be set to True if you want to start listening automatically after the packet is sent. The default setting is False. @@ -740,23 +752,27 @@ def send(self, data, keep_listening=False, tx_header=None): # buffer be within an expected range of bounds. Disable this check. # pylint: disable=len-as-condition assert 0 < len(data) <= 60 - if tx_header is not None: - assert len(tx_header) == 4, "tx header must be 4-tuple (To,From,ID,Flags)" # pylint: enable=len-as-condition self.idle() # Stop receiving to clear FIFO and keep it clear. # Fill the FIFO with a packet to send. # Combine header and data to form payload payload = bytearray(4) - if tx_header is None: # use attributes + if destination is None: # use attribute payload[0] = self.destination + else: # use kwarg + payload[0] = destination + if node is None: # use attribute payload[1] = self.node + else: # use kwarg + payload[1] = node + if identifier is None: # use attribute payload[2] = self.identifier + else: # use kwarg + payload[2] = identifier + if flags is None: # use attribute payload[3] = self.flags - else: # use header passed as argument - payload[0] = tx_header[0] - payload[1] = tx_header[1] - payload[2] = tx_header[2] - payload[3] = tx_header[3] + else: # use kwarg + payload[3] = flags payload = payload + data # Write payload to transmit fifo self._write_fifo_from(payload) @@ -777,7 +793,7 @@ def send(self, data, keep_listening=False, tx_header=None): return not timed_out def send_with_ack(self, data): - """Reliabe Datagram mode: + """Reliable Datagram mode: Send a packet with data and wait for an ACK response. The packet header is automatically generated. If enabled, the packet tranmsiion will be retried on failure @@ -815,7 +831,7 @@ def send_with_ack(self, data): # pylint: disable=too-many-branches def receive( - self, keep_listening=True, with_ack=False, timeout=None, with_header=False + self, *, keep_listening=True, with_ack=False, timeout=None, with_header=False ): """Wait to receive a packet from the receiver. If a packet is found the payload bytes are returned, otherwise None is returned (which indicates the timeout elapsed with no @@ -882,12 +898,10 @@ def receive( data = bytes("!", "UTF-8") self.send( data, - tx_header=( - packet[1], - packet[0], - packet[2], - packet[3] | _RH_FLAGS_ACK, - ), + destination=packet[1], + node=packet[0], + identifier=packet[2], + flags=(packet[3] | _RH_FLAGS_ACK), ) # reject Retries if we have seen this idetifier from this source before if (self.seen_ids[packet[1]] == packet[2]) and ( diff --git a/examples/rfm69_node2_ack.py b/examples/rfm69_node2_ack.py index 99e56c9..9ba7ca5 100644 --- a/examples/rfm69_node2_ack.py +++ b/examples/rfm69_node2_ack.py @@ -48,8 +48,8 @@ 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 .5 sec afterafter any packet received - time.sleep(0.5) + # 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( From 5088baa4ce8732ca4c4a8e9d111f84aa19046f3c Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Sun, 29 Mar 2020 16:27:27 +0100 Subject: [PATCH 15/19] missed a comment update --- adafruit_rfm69.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index 393af2e..8c0b011 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -839,7 +839,7 @@ def receive( If keep_listening is True (the default) the chip will immediately enter listening mode after reception of a packet, otherwise it will fall back to idle mode and ignore any future reception. - All packets must have a 4 byte header A 4-byte header for compatibilty with the + All packets must have a 4 byte header for compatibilty with the RadioHead library. The header consists of 4 bytes (To,From,ID,Flags). The default setting will strip the header before returning the packet to the caller. From 64a51ad896f17a98775d452e22ca494cea896850 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Tue, 31 Mar 2020 02:24:39 +0100 Subject: [PATCH 16/19] revise comments as requestd in review --- adafruit_rfm69.py | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index 8c0b011..591bece 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -310,19 +310,19 @@ def __init__( """ # initialize timeouts and delays delays self.ack_wait = 0.5 - """Sets the delay time before attemting a retry after not receiving an AC""" + """The delay time before attempting a retry after not receiving an ACK""" self.receive_timeout = 0.5 - """Sets the amount of time to poll for a received packet. + """The amount of time to poll for a received packet. If no packet is received, the returned packet will be None """ self.xmit_timeout = 2.0 - """Sets the amount of time to wait for the HW to transmit the packet. + """The amount of time to wait for the HW to transmit the packet. This is mainly used to prevent a hang due to a HW issue """ self.ack_retries = 5 - """Sets the number of ACK retries before reporting a failure.""" + """The number of ACK retries before reporting a failure.""" self.ack_delay = None - """Sets the delay time before attemting to send an ACK. + """The delay time before attemting to send an ACK. If ACKs are being missed try setting this to .1 or .2. """ # initialize sequence number counter for reliabe datagram mode @@ -332,25 +332,26 @@ def __init__( # initialize packet header # node address - default is broadcast self.node = _RH_BROADCAST_ADDRESS - """First byte of the RadioHead header. - Set the default address of this Node. (0-255). + """The default address of this Node. (0-255). If not 255 (0xff) then only packets address to this node will be accepted. + First byte of the RadioHead header. """ # destination address - default is broadcast self.destination = _RH_BROADCAST_ADDRESS - """Second byte of the RadioHead header. - Set the default destinationaaddress for packet transmissions. (0-255). + """The default destination address for packet transmissions. (0-255). If 255 (0xff) then any receiing node should accept the packet. + Second byte of the RadioHead header. """ # ID - contains seq count for reliable datagram mode self.identifier = 0 - """Third byte of the RadioHead header. - Automatically set to the sequence number when send_with_ack() used. + """Automatically set to the sequence number when send_with_ack() used. + Third byte of the RadioHead header. """ # flags - identifies ack/reetry packet for reliable datagram mode self.flags = 0 - """Fourth byte of the RadioHead header. (Typically set by driver) - Upper 4 bits reserved, lower 4 bits may be used to pass information. + """Upper 4 bits reserved for use by Reliable Datagram Mode. + Lower 4 bits may be used to pass information. + Fourth byte of the RadioHead header. """ def _configure_radio(self): From 6cba44e7f69a03c46050ad7d11009e3460b63115 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Sun, 12 Apr 2020 20:48:21 +0100 Subject: [PATCH 17/19] reran black -- merge other files fomr master --- adafruit_rfm69.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index 591bece..cd18068 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -378,8 +378,6 @@ def _configure_radio(self): # Set transmit power to 13 dBm, a safe value any module supports. self.tx_power = 13 - - # pylint: disable=no-member # Reconsider this disable when it can be tested. def _read_into(self, address, buf, length=None): From 32cdbd568641ebfd1cb12af6d809e9a9e13180a0 Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Sun, 12 Apr 2020 21:06:58 +0100 Subject: [PATCH 18/19] changes per review -- update comments -- change transmit interval in demos --- adafruit_rfm69.py | 4 ++-- examples/rfm69_node1.py | 2 +- examples/rfm69_node1_ack.py | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index cd18068..521d458 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -339,7 +339,7 @@ def __init__( # destination address - default is broadcast self.destination = _RH_BROADCAST_ADDRESS """The default destination address for packet transmissions. (0-255). - If 255 (0xff) then any receiing node should accept the packet. + If 255 (0xff) then any receiving node should accept the packet. Second byte of the RadioHead header. """ # ID - contains seq count for reliable datagram mode @@ -795,7 +795,7 @@ def send_with_ack(self, data): """Reliable Datagram mode: Send a packet with data and wait for an ACK response. The packet header is automatically generated. - If enabled, the packet tranmsiion will be retried on failure + If enabled, the packet transmission will be retried on failure """ if self.ack_retries: retries_remaining = self.ack_retries diff --git a/examples/rfm69_node1.py b/examples/rfm69_node1.py index 17458fe..6420aeb 100644 --- a/examples/rfm69_node1.py +++ b/examples/rfm69_node1.py @@ -9,7 +9,7 @@ # set the time interval (seconds) for sending packets -transmit_interval = 5 +transmit_interval = 10 # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your diff --git a/examples/rfm69_node1_ack.py b/examples/rfm69_node1_ack.py index 9cece3d..621affc 100644 --- a/examples/rfm69_node1_ack.py +++ b/examples/rfm69_node1_ack.py @@ -8,7 +8,7 @@ import adafruit_rfm69 # set the time interval (seconds) for sending packets -transmit_interval = 5 +transmit_interval = 10 # Define radio parameters. RADIO_FREQ_MHZ = 915.0 # Frequency of the radio in Mhz. Must match your From a7b0446be154ae4b53beba1e7763538986a1814d Mon Sep 17 00:00:00 2001 From: jerryneedell Date: Mon, 13 Apr 2020 12:12:31 +0100 Subject: [PATCH 19/19] fixed another typo --- adafruit_rfm69.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adafruit_rfm69.py b/adafruit_rfm69.py index 521d458..3adc204 100644 --- a/adafruit_rfm69.py +++ b/adafruit_rfm69.py @@ -305,7 +305,7 @@ def __init__( # initialize last RSSI reading self.last_rssi = 0.0 """The RSSI of the last received packet. Stored when the packet was received. - This instataneous RSSI value may not be accurate once the + This instantaneous RSSI value may not be accurate once the operating mode has been changed. """ # initialize timeouts and delays delays