Skip to content

Commit 2dbb850

Browse files
authored
Merge pull request #21 from applio/enh_expose_Bw_Cr_Sf_controls
GH-20: Expose bw, cr, sf controls and CRC checks
2 parents 2a1436f + 9e44a93 commit 2dbb850

File tree

2 files changed

+169
-22
lines changed

2 files changed

+169
-22
lines changed

README.rst

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,27 @@ This is easily achieved by downloading
2929
Usage Example
3030
=============
3131

32-
See examples/rfm9x_simpletest.py for a demo of the usage.
32+
Initialization of the RFM radio requires specifying a frequency appropriate to
33+
your radio hardware (i.e. 868-915 or 433 MHz) and specifying the pins used in your
34+
wiring from the controller board to the radio module.
35+
36+
This example code matches the wiring used in the
37+
`LoRa and LoRaWAN Radio for Raspberry Pi <https://learn.adafruit.com/lora-and-lorawan-radio-for-raspberry-pi/>`_
38+
project:
39+
40+
.. code-block:: python
41+
42+
import digitalio
43+
import board
44+
import busio
45+
import adafruit_rfm9x
46+
47+
RADIO_FREQ_MHZ = 915.0
48+
CS = digitalio.DigitalInOut(board.CE1)
49+
RESET = digitalio.DigitalInOut(board.D25)
50+
spi = busio.SPI(board.SCK, MOSI=board.MOSI, MISO=board.MISO)
51+
rfm9x = adafruit_rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ)
52+
3353
Note: the default baudrate for the SPI is 50000000 (5MHz). The maximum setting is 10Mhz but
3454
transmission errors have been observed expecially when using breakout boards.
3555
For breakout boards or other configurations where the boards are separated, it may be necessary to reduce
@@ -39,9 +59,23 @@ To set it to 1000000 use :
3959

4060
.. code-block:: python
4161
42-
# Initialze RFM radio
62+
# Initialze RFM radio with a more conservative baudrate
4363
rfm9x = adafruit_rfm9x.RFM9x(spi, CS, RESET, RADIO_FREQ_MHZ, baudrate=1000000)
4464
65+
Optional controls exist to alter the signal bandwidth, coding rate, and spreading factor
66+
settings used by the radio to achieve better performance in different environments.
67+
By default, settings compatible with RadioHead Bw125Cr45Sf128 mode are used, which can
68+
be altered in the following manner (continued from the above example):
69+
70+
.. code-block:: python
71+
72+
# Apply new modem config settings to the radio to improve its effective range
73+
rfm9x.signal_bandwidth = 62500
74+
rfm9x.coding_rate = 6
75+
rfm9x.spreading_factor = 8
76+
rfm9x.enable_crc = True
77+
78+
See examples/rfm9x_simpletest.py for an expanded demo of the usage.
4579

4680

4781
Contributing

adafruit_rfm9x.py

Lines changed: 133 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,13 @@
3333
import digitalio
3434
from micropython import const
3535

36+
try:
37+
from warnings import warn
38+
except ImportError:
39+
def warn(msg, **kwargs):
40+
"Issue a warning to stdout."
41+
print("%s: %s" % ("Warning" if kwargs.get("cat") is None else kwargs["cat"].__name__, msg))
42+
3643
import adafruit_bus_device.spi_device as spidev
3744

3845

@@ -192,6 +199,8 @@
192199
_RH_RF95_LOW_DATA_RATE_OPTIMIZE = const(0x01)
193200

194201
# RH_RF95_REG_1E_MODEM_CONFIG2 0x1e
202+
_RH_RF95_DETECTION_OPTIMIZE = const(0x31)
203+
_RH_RF95_DETECTION_THRESHOLD = const(0x37)
195204
_RH_RF95_SPREADING_FACTOR = const(0xf0)
196205
_RH_RF95_SPREADING_FACTOR_64CPS = const(0x60)
197206
_RH_RF95_SPREADING_FACTOR_128CPS = const(0x70)
@@ -333,6 +342,10 @@ def __set__(self, obj, val):
333342

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

345+
crc_error = _RegisterBits(_RH_RF95_REG_12_IRQ_FLAGS, offset=5, bits=1)
346+
347+
bw_bins = (7800, 10400, 15600, 20800, 31250, 41700, 62500, 125000, 250000)
348+
336349
def __init__(self, spi, cs, reset, frequency, *, preamble_length=8,
337350
high_power=True, baudrate=5000000):
338351
self.high_power = high_power
@@ -367,10 +380,13 @@ def __init__(self, spi, cs, reset, frequency, *, preamble_length=8,
367380
self._write_u8(_RH_RF95_REG_0F_FIFO_RX_BASE_ADDR, 0x00)
368381
# Set mode idle
369382
self.idle()
370-
# Set modem config to RadioHead compatible Bw125Cr45Sf128 mode.
383+
# Defaults set modem config to RadioHead compatible Bw125Cr45Sf128 mode.
384+
self.signal_bandwidth = 125000
385+
self.coding_rate = 5
386+
self.spreading_factor = 7
387+
# Default to disable CRC checking on incoming packets.
388+
self.enable_crc = False
371389
# Note no sync word is set for LoRa mode either!
372-
self._write_u8(_RH_RF95_REG_1D_MODEM_CONFIG1, 0x72) # Fei msb?
373-
self._write_u8(_RH_RF95_REG_1E_MODEM_CONFIG2, 0x74) # Fei lsb?
374390
self._write_u8(_RH_RF95_REG_26_MODEM_CONFIG3, 0x00) # Preamble lsb?
375391
# Set preamble length (default 8 bytes to match radiohead).
376392
self.preamble_length = preamble_length
@@ -533,6 +549,100 @@ def rssi(self):
533549
# Remember in LoRa mode the payload register changes function to RSSI!
534550
return self._read_u8(_RH_RF95_REG_1A_PKT_RSSI_VALUE) - 137
535551

552+
@property
553+
def signal_bandwidth(self):
554+
"""The signal bandwidth used by the radio (try setting to a higher
555+
value to increase throughput or to a lower value to increase the
556+
likelihood of successfully received payloads). Valid values are
557+
listed in RFM9x.bw_bins."""
558+
bw_id = (self._read_u8(_RH_RF95_REG_1D_MODEM_CONFIG1) & 0xf0) >> 4
559+
if bw_id >= len(self.bw_bins):
560+
current_bandwidth = 500000
561+
else:
562+
current_bandwidth = self.bw_bins[bw_id]
563+
return current_bandwidth
564+
565+
@signal_bandwidth.setter
566+
def signal_bandwidth(self, val):
567+
# Set signal bandwidth (set to 125000 to match RadioHead Bw125).
568+
for bw_id, cutoff in enumerate(self.bw_bins):
569+
if val <= cutoff:
570+
break
571+
else:
572+
bw_id = 9
573+
self._write_u8(
574+
_RH_RF95_REG_1D_MODEM_CONFIG1,
575+
(self._read_u8(_RH_RF95_REG_1D_MODEM_CONFIG1) & 0x0f) | (bw_id << 4)
576+
)
577+
578+
@property
579+
def coding_rate(self):
580+
"""The coding rate used by the radio to control forward error
581+
correction (try setting to a higher value to increase tolerance of
582+
short bursts of interference or to a lower value to increase bit
583+
rate). Valid values are limited to 5, 6, 7, or 8."""
584+
cr_id = (self._read_u8(_RH_RF95_REG_1D_MODEM_CONFIG1) & 0x0e) >> 1
585+
denominator = cr_id + 4
586+
return denominator
587+
588+
@coding_rate.setter
589+
def coding_rate(self, val):
590+
# Set coding rate (set to 5 to match RadioHead Cr45).
591+
denominator = min(max(val, 5), 8)
592+
cr_id = denominator - 4
593+
self._write_u8(
594+
_RH_RF95_REG_1D_MODEM_CONFIG1,
595+
(self._read_u8(_RH_RF95_REG_1D_MODEM_CONFIG1) & 0xf1) | (cr_id << 1)
596+
)
597+
598+
@property
599+
def spreading_factor(self):
600+
"""The spreading factor used by the radio (try setting to a higher
601+
value to increase the receiver's ability to distinguish signal from
602+
noise or to a lower value to increase the data transmission rate).
603+
Valid values are limited to 6, 7, 8, 9, 10, 11, or 12."""
604+
sf_id = (self._read_u8(_RH_RF95_REG_1E_MODEM_CONFIG2) & 0xf0) >> 4
605+
return sf_id
606+
607+
@spreading_factor.setter
608+
def spreading_factor(self, val):
609+
# Set spreading factor (set to 7 to match RadioHead Sf128).
610+
val = min(max(val, 6), 12)
611+
self._write_u8(
612+
_RH_RF95_DETECTION_OPTIMIZE, 0xc5 if val == 6 else 0xc3
613+
)
614+
self._write_u8(
615+
_RH_RF95_DETECTION_THRESHOLD, 0x0c if val == 6 else 0x0a
616+
)
617+
self._write_u8(
618+
_RH_RF95_REG_1E_MODEM_CONFIG2,
619+
(
620+
(self._read_u8(_RH_RF95_REG_1E_MODEM_CONFIG2) & 0x0f) |
621+
((val << 4) & 0xf0)
622+
)
623+
)
624+
625+
@property
626+
def enable_crc(self):
627+
"""Set to True to enable hardware CRC checking of incoming packets.
628+
Incoming packets that fail the CRC check are not processed. Set to
629+
False to disable CRC checking and process all incoming packets."""
630+
return (self._read_u8(_RH_RF95_REG_1E_MODEM_CONFIG2) & 0x04) == 0x04
631+
632+
@enable_crc.setter
633+
def enable_crc(self, val):
634+
# Optionally enable CRC checking on incoming packets.
635+
if val:
636+
self._write_u8(
637+
_RH_RF95_REG_1E_MODEM_CONFIG2,
638+
self._read_u8(_RH_RF95_REG_1E_MODEM_CONFIG2) | 0x04
639+
)
640+
else:
641+
self._write_u8(
642+
_RH_RF95_REG_1E_MODEM_CONFIG2,
643+
self._read_u8(_RH_RF95_REG_1E_MODEM_CONFIG2) & 0xfb
644+
)
645+
536646
def send(self, data, timeout=2.,
537647
tx_header=(_RH_BROADCAST_ADDRESS, _RH_BROADCAST_ADDRESS, 0, 0)):
538648
"""Send a string of data using the transmitter.
@@ -617,25 +727,28 @@ def receive(self, timeout=0.5, keep_listening=True, with_header=False,
617727
# Payload ready is set, a packet is in the FIFO.
618728
packet = None
619729
if not timed_out:
620-
# Grab the length of the received packet and check it has at least 5
621-
# bytes to indicate the 4 byte header and at least 1 byte of user data.
622-
length = self._read_u8(_RH_RF95_REG_13_RX_NB_BYTES)
623-
if length < 5:
624-
packet = None
730+
if self.enable_crc and self.crc_error:
731+
warn("CRC error, packet ignored")
625732
else:
626-
# Have a good packet, grab it from the FIFO.
627-
# Reset the fifo read ptr to the beginning of the packet.
628-
current_addr = self._read_u8(_RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR)
629-
self._write_u8(_RH_RF95_REG_0D_FIFO_ADDR_PTR, current_addr)
630-
packet = bytearray(length)
631-
# Read the packet.
632-
self._read_into(_RH_RF95_REG_00_FIFO, packet)
633-
if (rx_filter != _RH_BROADCAST_ADDRESS and packet[0] != _RH_BROADCAST_ADDRESS
634-
and packet[0] != rx_filter):
733+
# Grab the length of the received packet and check it has at least 5
734+
# bytes to indicate the 4 byte header and at least 1 byte of user data.
735+
length = self._read_u8(_RH_RF95_REG_13_RX_NB_BYTES)
736+
if length < 5:
635737
packet = None
636-
elif not with_header: # skip the header if not wanted
637-
packet = packet[4:]
638-
# Listen again if necessary and return the result packet.
738+
else:
739+
# Have a good packet, grab it from the FIFO.
740+
# Reset the fifo read ptr to the beginning of the packet.
741+
current_addr = self._read_u8(_RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR)
742+
self._write_u8(_RH_RF95_REG_0D_FIFO_ADDR_PTR, current_addr)
743+
packet = bytearray(length)
744+
# Read the packet.
745+
self._read_into(_RH_RF95_REG_00_FIFO, packet)
746+
if (rx_filter != _RH_BROADCAST_ADDRESS and packet[0] != _RH_BROADCAST_ADDRESS
747+
and packet[0] != rx_filter):
748+
packet = None
749+
elif not with_header: # skip the header if not wanted
750+
packet = packet[4:]
751+
# Listen again if necessary and return the result packet.
639752
if keep_listening:
640753
self.listen()
641754
else:

0 commit comments

Comments
 (0)