Skip to content

implemented memory saving changes #45

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jun 18, 2020
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
182 changes: 30 additions & 152 deletions adafruit_rfm9x.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,6 @@
import digitalio
from micropython import const

try:
from warnings import warn
except ImportError:

def warn(msg, **kwargs):
"Issue a warning to stdout."
print(
"%s: %s"
% ("Warning" if kwargs.get("cat") is None else kwargs["cat"].__name__, msg)
)


import adafruit_bus_device.spi_device as spidev

Expand All @@ -58,10 +47,6 @@ def warn(msg, **kwargs):
# Register names (FSK Mode even though we use LoRa instead, from table 85)
_RH_RF95_REG_00_FIFO = const(0x00)
_RH_RF95_REG_01_OP_MODE = const(0x01)
_RH_RF95_REG_02_RESERVED = const(0x02)
_RH_RF95_REG_03_RESERVED = const(0x03)
_RH_RF95_REG_04_RESERVED = const(0x04)
_RH_RF95_REG_05_RESERVED = const(0x05)
_RH_RF95_REG_06_FRF_MSB = const(0x06)
_RH_RF95_REG_07_FRF_MID = const(0x07)
_RH_RF95_REG_08_FRF_LSB = const(0x08)
Expand Down Expand Up @@ -108,118 +93,9 @@ def warn(msg, **kwargs):
_RH_RF95_REG_63_AGC_THRESH2 = const(0x63)
_RH_RF95_REG_64_AGC_THRESH3 = const(0x64)

# RH_RF95_REG_01_OP_MODE 0x01
_RH_RF95_LONG_RANGE_MODE = const(0x80)
_RH_RF95_ACCESS_SHARED_REG = const(0x40)
_RH_RF95_MODE = const(0x07)
_RH_RF95_MODE_SLEEP = const(0x00)
_RH_RF95_MODE_STDBY = const(0x01)
_RH_RF95_MODE_FSTX = const(0x02)
_RH_RF95_MODE_TX = const(0x03)
_RH_RF95_MODE_FSRX = const(0x04)
_RH_RF95_MODE_RXCONTINUOUS = const(0x05)
_RH_RF95_MODE_RXSINGLE = const(0x06)
_RH_RF95_MODE_CAD = const(0x07)

# RH_RF95_REG_09_PA_CONFIG 0x09
_RH_RF95_PA_SELECT = const(0x80)
_RH_RF95_MAX_POWER = const(0x70)
_RH_RF95_OUTPUT_POWER = const(0x0F)

# RH_RF95_REG_0A_PA_RAMP 0x0a
_RH_RF95_LOW_PN_TX_PLL_OFF = const(0x10)
_RH_RF95_PA_RAMP = const(0x0F)
_RH_RF95_PA_RAMP_3_4MS = const(0x00)
_RH_RF95_PA_RAMP_2MS = const(0x01)
_RH_RF95_PA_RAMP_1MS = const(0x02)
_RH_RF95_PA_RAMP_500US = const(0x03)
_RH_RF95_PA_RAMP_250US = const(0x04)
_RH_RF95_PA_RAMP_125US = const(0x05)
_RH_RF95_PA_RAMP_100US = const(0x06)
_RH_RF95_PA_RAMP_62US = const(0x07)
_RH_RF95_PA_RAMP_50US = const(0x08)
_RH_RF95_PA_RAMP_40US = const(0x09)
_RH_RF95_PA_RAMP_31US = const(0x0A)
_RH_RF95_PA_RAMP_25US = const(0x0B)
_RH_RF95_PA_RAMP_20US = const(0x0C)
_RH_RF95_PA_RAMP_15US = const(0x0D)
_RH_RF95_PA_RAMP_12US = const(0x0E)
_RH_RF95_PA_RAMP_10US = const(0x0F)

# RH_RF95_REG_0B_OCP 0x0b
_RH_RF95_OCP_ON = const(0x20)
_RH_RF95_OCP_TRIM = const(0x1F)

# RH_RF95_REG_0C_LNA 0x0c
_RH_RF95_LNA_GAIN = const(0xE0)
_RH_RF95_LNA_BOOST = const(0x03)
_RH_RF95_LNA_BOOST_DEFAULT = const(0x00)
_RH_RF95_LNA_BOOST_150PC = const(0x11)

# RH_RF95_REG_11_IRQ_FLAGS_MASK 0x11
_RH_RF95_RX_TIMEOUT_MASK = const(0x80)
_RH_RF95_RX_DONE_MASK = const(0x40)
_RH_RF95_PAYLOAD_CRC_ERROR_MASK = const(0x20)
_RH_RF95_VALID_HEADER_MASK = const(0x10)
_RH_RF95_TX_DONE_MASK = const(0x08)
_RH_RF95_CAD_DONE_MASK = const(0x04)
_RH_RF95_FHSS_CHANGE_CHANNEL_MASK = const(0x02)
_RH_RF95_CAD_DETECTED_MASK = const(0x01)

# RH_RF95_REG_12_IRQ_FLAGS 0x12
_RH_RF95_RX_TIMEOUT = const(0x80)
_RH_RF95_RX_DONE = const(0x40)
_RH_RF95_PAYLOAD_CRC_ERROR = const(0x20)
_RH_RF95_VALID_HEADER = const(0x10)
_RH_RF95_TX_DONE = const(0x08)
_RH_RF95_CAD_DONE = const(0x04)
_RH_RF95_FHSS_CHANGE_CHANNEL = const(0x02)
_RH_RF95_CAD_DETECTED = const(0x01)

# RH_RF95_REG_18_MODEM_STAT 0x18
_RH_RF95_RX_CODING_RATE = const(0xE0)
_RH_RF95_MODEM_STATUS_CLEAR = const(0x10)
_RH_RF95_MODEM_STATUS_HEADER_INFO_VALID = const(0x08)
_RH_RF95_MODEM_STATUS_RX_ONGOING = const(0x04)
_RH_RF95_MODEM_STATUS_SIGNAL_SYNCHRONIZED = const(0x02)
_RH_RF95_MODEM_STATUS_SIGNAL_DETECTED = const(0x01)

# RH_RF95_REG_1C_HOP_CHANNEL 0x1c
_RH_RF95_PLL_TIMEOUT = const(0x80)
_RH_RF95_RX_PAYLOAD_CRC_IS_ON = const(0x40)
_RH_RF95_FHSS_PRESENT_CHANNEL = const(0x3F)

# RH_RF95_REG_1D_MODEM_CONFIG1 0x1d
_RH_RF95_BW = const(0xC0)
_RH_RF95_BW_125KHZ = const(0x00)
_RH_RF95_BW_250KHZ = const(0x40)
_RH_RF95_BW_500KHZ = const(0x80)
_RH_RF95_BW_RESERVED = const(0xC0)
_RH_RF95_CODING_RATE = const(0x38)
_RH_RF95_CODING_RATE_4_5 = const(0x00)
_RH_RF95_CODING_RATE_4_6 = const(0x08)
_RH_RF95_CODING_RATE_4_7 = const(0x10)
_RH_RF95_CODING_RATE_4_8 = const(0x18)
_RH_RF95_IMPLICIT_HEADER_MODE_ON = const(0x04)
_RH_RF95_RX_PAYLOAD_CRC_ON = const(0x02)
_RH_RF95_LOW_DATA_RATE_OPTIMIZE = const(0x01)

# RH_RF95_REG_1E_MODEM_CONFIG2 0x1e
_RH_RF95_DETECTION_OPTIMIZE = const(0x31)
_RH_RF95_DETECTION_THRESHOLD = const(0x37)
_RH_RF95_SPREADING_FACTOR = const(0xF0)
_RH_RF95_SPREADING_FACTOR_64CPS = const(0x60)
_RH_RF95_SPREADING_FACTOR_128CPS = const(0x70)
_RH_RF95_SPREADING_FACTOR_256CPS = const(0x80)
_RH_RF95_SPREADING_FACTOR_512CPS = const(0x90)
_RH_RF95_SPREADING_FACTOR_1024CPS = const(0xA0)
_RH_RF95_SPREADING_FACTOR_2048CPS = const(0xB0)
_RH_RF95_SPREADING_FACTOR_4096CPS = const(0xC0)
_RH_RF95_TX_CONTINUOUS_MOE = const(0x08)
_RH_RF95_AGC_AUTO_ON = const(0x04)
_RH_RF95_SYM_TIMEOUT_MSB = const(0x03)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are these unused const values kept in memory? If they are used, they are usually copied in place and the name dropped. Maybe we need to modify how const works.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure this made any difference. I took them out since they were not used.


# RH_RF95_REG_4D_PA_DAC 0x4d

_RH_RF95_PA_DAC_DISABLE = const(0x04)
_RH_RF95_PA_DAC_ENABLE = const(0x07)

Expand Down Expand Up @@ -352,12 +228,6 @@ def __set__(self, obj, val):

dio0_mapping = _RegisterBits(_RH_RF95_REG_40_DIO_MAPPING1, offset=6, bits=2)

tx_done = _RegisterBits(_RH_RF95_REG_12_IRQ_FLAGS, offset=3, bits=1)

rx_done = _RegisterBits(_RH_RF95_REG_12_IRQ_FLAGS, offset=6, bits=1)

crc_error = _RegisterBits(_RH_RF95_REG_12_IRQ_FLAGS, offset=5, bits=1)

bw_bins = (7800, 10400, 15600, 20800, 31250, 41700, 62500, 125000, 250000)

def __init__(
Expand Down Expand Up @@ -409,8 +279,16 @@ def __init__(
self.frequency_mhz = frequency
# Set preamble length (default 8 bytes to match radiohead).
self.preamble_length = preamble_length
# set radio configuration parameters
self._configure_radio()
# Defaults set modem config to RadioHead compatible Bw125Cr45Sf128 mode.
self.signal_bandwidth = 125000
self.coding_rate = 5
self.spreading_factor = 7
# Default to disable CRC checking on incoming packets.
self.enable_crc = False
# Note no sync word is set for LoRa mode either!
self._write_u8(_RH_RF95_REG_26_MODEM_CONFIG3, 0x00) # Preamble lsb?
# Set transmit power to 13 dBm, a safe value any module supports.
self.tx_power = 13
# initialize last RSSI reading
self.last_rssi = 0.0
"""The RSSI of the last received packet. Stored when the packet was received.
Expand Down Expand Up @@ -462,18 +340,7 @@ def __init__(
Lower 4 bits may be used to pass information.
Fourth byte of the RadioHead header.
"""

def _configure_radio(self):
# Defaults set modem config to RadioHead compatible Bw125Cr45Sf128 mode.
self.signal_bandwidth = 125000
self.coding_rate = 5
self.spreading_factor = 7
# Default to disable CRC checking on incoming packets.
self.enable_crc = False
# Note no sync word is set for LoRa mode either!
self._write_u8(_RH_RF95_REG_26_MODEM_CONFIG3, 0x00) # Preamble lsb?
# Set transmit power to 13 dBm, a safe value any module supports.
self.tx_power = 13
self.crc_error_count = 0

# pylint: disable=no-member
# Reconsider pylint: disable when this can be tested
Expand Down Expand Up @@ -721,6 +588,18 @@ def enable_crc(self, val):
self._read_u8(_RH_RF95_REG_1E_MODEM_CONFIG2) & 0xFB,
)

def tx_done(self):
"""Transmit status"""
return (self._read_u8(_RH_RF95_REG_12_IRQ_FLAGS) & 0x8) >> 3

def rx_done(self):
"""Receive status"""
return (self._read_u8(_RH_RF95_REG_12_IRQ_FLAGS) & 0x40) >> 6

def crc_error(self):
"""crc status"""
return (self._read_u8(_RH_RF95_REG_12_IRQ_FLAGS) & 0x20) >> 5
Comment on lines +591 to +601
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How much memory does this save? Seems like it'd very close to the cost of three instances of _RegisterBits.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The main saving is that register bits creates the getter and setter -- these are all read only so they are never set. still -- it's not huge savings.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getter and setter functions should be shared though because it's three instances of the same class.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t understand. There is no setter.

Copy link
Contributor Author

@jerryneedell jerryneedell Jun 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are you saying that with registerbits the unused setter does not cost anything?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. The function objects in the data descriptor class (registerbits) should be shared amongst all instances if my mental model is correct.


def send(
self,
data,
Expand Down Expand Up @@ -783,7 +662,7 @@ def send(
# best that can be done right now without interrupts).
start = time.monotonic()
timed_out = False
while not timed_out and not self.tx_done:
while not timed_out and not self.tx_done():
if (time.monotonic() - start) >= self.xmit_timeout:
timed_out = True
# Listen again if necessary and return the result packet.
Expand Down Expand Up @@ -863,7 +742,7 @@ def receive(
self.listen()
start = time.monotonic()
timed_out = False
while not timed_out and not self.rx_done:
while not timed_out and not self.rx_done():
if (time.monotonic() - start) >= timeout:
timed_out = True
# Payload ready is set, a packet is in the FIFO.
Expand All @@ -873,8 +752,8 @@ def receive(
# Enter idle mode to stop receiving other packets.
self.idle()
if not timed_out:
if self.enable_crc and self.crc_error:
warn("CRC error, packet ignored")
if self.enable_crc and self.crc_error():
self.crc_error_count += 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jerryneedell q: with warn removed, user will not be notified of a CRC error. Would this be handled in user-code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes -- something like this

crc_count = 0
while True:
    # Look for a new packet: only accept if addresses to my_node
    packet = rfm9x.receive(with_ack=True, with_header=True)
    if rfm9x.crc_error_count != crc_count:
        crc_count = rfm9x.crc_error_count
        print("CRC error ",crc_count)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In many cases -- user may not care or want to know -- the packet will just be ignored

else:
# Read the data from the FIFO.
# Read the length of the FIFO.
Expand Down Expand Up @@ -907,10 +786,9 @@ def receive(
# 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")
# send ACK packet to sender (data is b'!')
self.send(
data,
b"!",
destination=packet[1],
node=packet[0],
identifier=packet[2],
Expand Down