Skip to content

Fix power and frequency initiailzations #4

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 11 commits into from
May 8, 2018
6 changes: 3 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ This is easily achieved by downloading
Usage Example
=============

See examples/simpletest.py for a demo of the usage.
Note: the default baudrate for the SPI is 10000000 (10MHz). This works well when you are using a board with
the radio module built in (FeatherM0 RFM9x) or with an RFM9x FeatherWing mounted directly to a feather board.
See examples/rfm9x_simpletest.py for a demo of the usage.
Note: the default baudrate for the SPI is 50000000 (5MHz). The maximum setting is 10Mhz but
transmission errors have been observed expecially when using breakout boards.
For breakout boards or other configurations where the boards are separated, it may be necessary to reduce
the baudrate for reliable data transmission.
The baud rate may be specified as an keyword parameter when initializing the board.
Expand Down
85 changes: 51 additions & 34 deletions adafruit_rfm9x.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
adapted from the Radiohead library RF95 code from:
http: www.airspayce.com/mikem/arduino/RadioHead/

* Author(s): Tony DiCola
* Author(s): Tony DiCola, Jerry Needell
"""
import time
import digitalio
Expand Down Expand Up @@ -120,7 +120,7 @@
_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(0x0)
_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)
Expand Down Expand Up @@ -212,7 +212,7 @@
_RH_RF95_FXOSC = 32000000.0

# The Frequency Synthesizer step = RH_RF95_FXOSC / 2^^19
_RH_RF95_FSTEP = (_RH_RF95_FXOSC // 524288)
_RH_RF95_FSTEP = (_RH_RF95_FXOSC / 524288)

# RadioHead specific compatibility constants.
_RH_BROADCAST_ADDRESS = const(0xFF)
Expand Down Expand Up @@ -266,7 +266,7 @@ class RFM9x:
# at least as large as the FIFO on the chip (256 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(256)
_BUFFER = bytearray(10)

class _RegisterBits:
# Class to simplify access to the many configuration bits avaialable
Expand Down Expand Up @@ -334,9 +334,10 @@ def __set__(self, obj, val):
rx_done = _RegisterBits(_RH_RF95_REG_12_IRQ_FLAGS, offset=6, bits=1)

def __init__(self, spi, cs, reset, frequency, *, preamble_length=8,
high_power=True, baudrate=10000000):
high_power=True, baudrate=5000000):
self.high_power = high_power
# Device support SPI mode 0 (polarity & phase = 0) up to a max of 10mhz.
# Set Default Baudrate to 5MHz to avoid problems
self._device = spi_device.SPIDevice(spi, cs, baudrate=baudrate,
polarity=0, phase=0)
# Setup reset as a digital input (default state for reset line according
Expand All @@ -361,6 +362,9 @@ def __init__(self, spi, cs, reset, frequency, *, preamble_length=8,
raise RuntimeError('Failed to configure radio for LoRa mode, check wiring!')
except OSError:
raise RuntimeError('Failed to communicate with radio, check wiring!')
# clear default setting for access to LF registers if frquency > 525MHz
if frequency > 525:
self.low_frequency_mode = 0
# Setup entire 256 byte FIFO
self._write_u8(_RH_RF95_REG_0E_FIFO_TX_BASE_ADDR, 0x00)
self._write_u8(_RH_RF95_REG_0F_FIFO_RX_BASE_ADDR, 0x00)
Expand All @@ -374,7 +378,7 @@ def __init__(self, spi, cs, reset, frequency, *, preamble_length=8,
# Set preamble length (default 8 bytes to match radiohead).
self.preamble_length = preamble_length
# Set frequency
self.frequency = frequency
self.frequency_mhz = frequency
# Set TX power to low defaut, 13 dB.
self.tx_power = 13

Expand Down Expand Up @@ -494,6 +498,10 @@ def tx_power(self):
high power devices (RFM95/96/97/98, high_power=True) or -1 to 14 for low
power devices. Only integer power levels are actually set (i.e. 12.5
will result in a value of 12 dBm).
The actual maximum setting for high_power=True is 20dBm but for values > 20
the PA_BOOST will be enabled resultiung in ad additonla gain of 3dBm.
Copy link
Member

Choose a reason for hiding this comment

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

typos

Copy link
Contributor Author

Choose a reason for hiding this comment

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

sigh...My fingers are usually way ahead of my brain.

Copy link
Member

Choose a reason for hiding this comment

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

Yup, happens to me too. No worries. :-)

Theactual setting is reduced by 3dBm.
The reported value will reflect the reduced setting.
"""
if self.high_power:
return self.output_power + 5
Expand All @@ -505,14 +513,17 @@ def tx_power(self, val):
if self.high_power:
assert 5 <= val <= 23
# Enable power amp DAC if power is above 20 dB.
# Lower setting by 3db when PA_BOOST enabled - see Data Sheet Section 6.4
if val > 20:
self.pa_dac = _RH_RF95_PA_DAC_ENABLE
val -= 3
else:
self.pa_dac = _RH_RF95_PA_DAC_DISABLE
self.pa_select = True
self.output_power = (val - 5) & 0x0F
else:
assert -1 <= val <= 14
self.pa_select = False
self.max_power = 0b111 # Allow max power output.
self.output_power = (val + 1) & 0x0F

Expand All @@ -523,10 +534,11 @@ def rssi(self):
# Remember in LoRa mode the payload register changes function to RSSI!
return self._read_u8(_RH_RF95_REG_1A_PKT_RSSI_VALUE) - 137

def send(self, data):
def send(self, data, timeout_s=2.):
Copy link
Member

Choose a reason for hiding this comment

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

No _s. Just document that its in seconds. This should be our standard for timeouts. (time.sleep is an example of the same units.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK - this was just a holdover from the original code. The change makes sense to me.

"""Send a string of data using the transmitter. You can only send 252
bytes at a time (limited by chip's FIFO size and appended headers). Note
this appends a 4 byte header to be compatible with the RadioHead library.
The timeout is just to prevent a hang (arbitrarily set to 2 Seconds).
"""
# Disable pylint warning to not use length as a check for zero.
# This is a puzzling warning as the below code is clearly the most
Expand All @@ -551,12 +563,15 @@ def send(self, data):
self.transmit()
# Wait for tx done interrupt with explicit polling (not ideal but
# best that can be done right now without interrupts).
while not self.tx_done:
pass
# Clear interrupts.
self._write_u8(_RH_RF95_REG_12_IRQ_FLAGS, 0xFF)
start = time.monotonic()
timed_out = False
while not timed_out and not self.tx_done:
if (time.monotonic() - start) >= timeout_s:
timed_out = True
Copy link
Member

Choose a reason for hiding this comment

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

Please throw a RuntimeException in the case where tx_done is still false.

# Go back to idle mode after transmit.
self.idle()
# Clear interrupts.
self._write_u8(_RH_RF95_REG_12_IRQ_FLAGS, 0xFF)

def receive(self, timeout_s=0.5, keep_listening=True):
"""Wait to receive a packet from the receiver. Will wait for up to
Expand All @@ -576,32 +591,34 @@ def receive(self, timeout_s=0.5, keep_listening=True):
# enough, however it's the best that can be done from Python without
# interrupt supports.
start = time.monotonic()
while not self.rx_done:
timed_out = False
while not timed_out and not self.rx_done:
if (time.monotonic() - start) >= timeout_s:
return None # Exceeded timeout.
# Clear interrupt.
self._write_u8(_RH_RF95_REG_12_IRQ_FLAGS, 0xFF)
timed_out = True
Copy link
Member

Choose a reason for hiding this comment

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

Exception here too and it'll "return" early so you don't need the extra state.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As above, there is still code that needs to execute after the timeout.
More importantly, I don't think we want an exception here. The typical use of receive it to see if any packets are available. A timeout is normal here.

We could break this into two parts like it is for Arduino and have an "available" function that the user polls until a packet is received.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, returning None is fine with me. I believe we do want an exception for send failure above though.

# Payload ready is set, a packet is in the FIFO.
packet = None
# Enter idle mode to stop receiving other packets.
self.idle()
# Grab the length of the received packet and check it has at least 5
# bytes to indicate the 4 byte header and at least 1 byte of user data.
length = self._read_u8(_RH_RF95_REG_13_RX_NB_BYTES)
if length < 5:
packet = None
else:
# Have a good packet, grab it from the FIFO.
# Reset the fifo read ptr to the beginning of the packet.
current_addr = self._read_u8(_RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR)
self._write_u8(_RH_RF95_REG_0D_FIFO_ADDR_PTR, current_addr)
# Read the first 4 bytes to grab the header.
self._read_into(_RH_RF95_REG_00_FIFO, self._BUFFER, length=4)
length -= 4
# Next read the remaining data into a result packet buffer.
packet = bytearray(length)
self._read_into(_RH_RF95_REG_00_FIFO, packet)
# Listen again if necessary and return the result packet.
if not timed_out:
# Grab the length of the received packet and check it has at least 5
# bytes to indicate the 4 byte header and at least 1 byte of user data.
length = self._read_u8(_RH_RF95_REG_13_RX_NB_BYTES)
if length < 5:
packet = None
else:
# Have a good packet, grab it from the FIFO.
# Reset the fifo read ptr to the beginning of the packet.
current_addr = self._read_u8(_RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR)
self._write_u8(_RH_RF95_REG_0D_FIFO_ADDR_PTR, current_addr)
packet = bytearray(length)
# Read the packet.
self._read_into(_RH_RF95_REG_00_FIFO, packet)
# strip off the header
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()
# Clear interrupt.
self._write_u8(_RH_RF95_REG_12_IRQ_FLAGS, 0xFF)
return packet
4 changes: 2 additions & 2 deletions docs/examples.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ Simple test

Ensure your device works with this simple test.

.. literalinclude:: ../examples/simpletest.py
:caption: examples/simpletest.py
.. literalinclude:: ../examples/rfm9x_simpletest.py
:caption: examples/rfm9x_simpletest.py
:linenos:
File renamed without changes.