/ adafruit_rfm9x.py
adafruit_rfm9x.py
  1  # The MIT License (MIT)
  2  #
  3  # Copyright (c) 2017 Tony DiCola for Adafruit Industries
  4  #
  5  # Permission is hereby granted, free of charge, to any person obtaining a copy
  6  # of this software and associated documentation files (the "Software"), to deal
  7  # in the Software without restriction, including without limitation the rights
  8  # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9  # copies of the Software, and to permit persons to whom the Software is
 10  # furnished to do so, subject to the following conditions:
 11  #
 12  # The above copyright notice and this permission notice shall be included in
 13  # all copies or substantial portions of the Software.
 14  #
 15  # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 16  # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 17  # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 18  # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 19  # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 20  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 21  # THE SOFTWARE.
 22  """
 23  `adafruit_rfm9x`
 24  ====================================================
 25  
 26  CircuitPython module for the RFM95/6/7/8 LoRa 433/915mhz radio modules.  This is
 27  adapted from the Radiohead library RF95 code from:
 28  http: www.airspayce.com/mikem/arduino/RadioHead/
 29  
 30  * Author(s): Tony DiCola, Jerry Needell
 31  """
 32  import time
 33  import random
 34  from micropython import const
 35  
 36  
 37  import adafruit_bus_device.spi_device as spidev
 38  
 39  
 40  __version__ = "0.0.0-auto.0"
 41  __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RFM9x.git"
 42  
 43  
 44  # pylint: disable=bad-whitespace
 45  # Internal constants:
 46  # Register names (FSK Mode even though we use LoRa instead, from table 85)
 47  _RH_RF95_REG_00_FIFO = const(0x00)
 48  _RH_RF95_REG_01_OP_MODE = const(0x01)
 49  _RH_RF95_REG_06_FRF_MSB = const(0x06)
 50  _RH_RF95_REG_07_FRF_MID = const(0x07)
 51  _RH_RF95_REG_08_FRF_LSB = const(0x08)
 52  _RH_RF95_REG_09_PA_CONFIG = const(0x09)
 53  _RH_RF95_REG_0A_PA_RAMP = const(0x0A)
 54  _RH_RF95_REG_0B_OCP = const(0x0B)
 55  _RH_RF95_REG_0C_LNA = const(0x0C)
 56  _RH_RF95_REG_0D_FIFO_ADDR_PTR = const(0x0D)
 57  _RH_RF95_REG_0E_FIFO_TX_BASE_ADDR = const(0x0E)
 58  _RH_RF95_REG_0F_FIFO_RX_BASE_ADDR = const(0x0F)
 59  _RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR = const(0x10)
 60  _RH_RF95_REG_11_IRQ_FLAGS_MASK = const(0x11)
 61  _RH_RF95_REG_12_IRQ_FLAGS = const(0x12)
 62  _RH_RF95_REG_13_RX_NB_BYTES = const(0x13)
 63  _RH_RF95_REG_14_RX_HEADER_CNT_VALUE_MSB = const(0x14)
 64  _RH_RF95_REG_15_RX_HEADER_CNT_VALUE_LSB = const(0x15)
 65  _RH_RF95_REG_16_RX_PACKET_CNT_VALUE_MSB = const(0x16)
 66  _RH_RF95_REG_17_RX_PACKET_CNT_VALUE_LSB = const(0x17)
 67  _RH_RF95_REG_18_MODEM_STAT = const(0x18)
 68  _RH_RF95_REG_19_PKT_SNR_VALUE = const(0x19)
 69  _RH_RF95_REG_1A_PKT_RSSI_VALUE = const(0x1A)
 70  _RH_RF95_REG_1B_RSSI_VALUE = const(0x1B)
 71  _RH_RF95_REG_1C_HOP_CHANNEL = const(0x1C)
 72  _RH_RF95_REG_1D_MODEM_CONFIG1 = const(0x1D)
 73  _RH_RF95_REG_1E_MODEM_CONFIG2 = const(0x1E)
 74  _RH_RF95_REG_1F_SYMB_TIMEOUT_LSB = const(0x1F)
 75  _RH_RF95_REG_20_PREAMBLE_MSB = const(0x20)
 76  _RH_RF95_REG_21_PREAMBLE_LSB = const(0x21)
 77  _RH_RF95_REG_22_PAYLOAD_LENGTH = const(0x22)
 78  _RH_RF95_REG_23_MAX_PAYLOAD_LENGTH = const(0x23)
 79  _RH_RF95_REG_24_HOP_PERIOD = const(0x24)
 80  _RH_RF95_REG_25_FIFO_RX_BYTE_ADDR = const(0x25)
 81  _RH_RF95_REG_26_MODEM_CONFIG3 = const(0x26)
 82  
 83  _RH_RF95_REG_40_DIO_MAPPING1 = const(0x40)
 84  _RH_RF95_REG_41_DIO_MAPPING2 = const(0x41)
 85  _RH_RF95_REG_42_VERSION = const(0x42)
 86  
 87  _RH_RF95_REG_4B_TCXO = const(0x4B)
 88  _RH_RF95_REG_4D_PA_DAC = const(0x4D)
 89  _RH_RF95_REG_5B_FORMER_TEMP = const(0x5B)
 90  _RH_RF95_REG_61_AGC_REF = const(0x61)
 91  _RH_RF95_REG_62_AGC_THRESH1 = const(0x62)
 92  _RH_RF95_REG_63_AGC_THRESH2 = const(0x63)
 93  _RH_RF95_REG_64_AGC_THRESH3 = const(0x64)
 94  
 95  _RH_RF95_DETECTION_OPTIMIZE = const(0x31)
 96  _RH_RF95_DETECTION_THRESHOLD = const(0x37)
 97  
 98  _RH_RF95_PA_DAC_DISABLE = const(0x04)
 99  _RH_RF95_PA_DAC_ENABLE = const(0x07)
100  
101  # The crystal oscillator frequency of the module
102  _RH_RF95_FXOSC = 32000000.0
103  
104  # The Frequency Synthesizer step = RH_RF95_FXOSC / 2^^19
105  _RH_RF95_FSTEP = _RH_RF95_FXOSC / 524288
106  
107  # RadioHead specific compatibility constants.
108  _RH_BROADCAST_ADDRESS = const(0xFF)
109  
110  # The acknowledgement bit in the FLAGS
111  # The top 4 bits of the flags are reserved for RadioHead. The lower 4 bits are reserved
112  # for application layer use.
113  _RH_FLAGS_ACK = const(0x80)
114  _RH_FLAGS_RETRY = const(0x40)
115  
116  # User facing constants:
117  SLEEP_MODE = 0b000
118  STANDBY_MODE = 0b001
119  FS_TX_MODE = 0b010
120  TX_MODE = 0b011
121  FS_RX_MODE = 0b100
122  RX_MODE = 0b101
123  # pylint: enable=bad-whitespace
124  
125  
126  # Disable the too many instance members warning.  Pylint has no knowledge
127  # of the context and is merely guessing at the proper amount of members.  This
128  # is a complex chip which requires exposing many attributes and state.  Disable
129  # the warning to work around the error.
130  # pylint: disable=too-many-instance-attributes
131  
132  
133  class RFM9x:
134      """Interface to a RFM95/6/7/8 LoRa radio module.  Allows sending and
135      receivng bytes of data in long range LoRa mode at a support board frequency
136      (433/915mhz).
137  
138      You must specify the following parameters:
139      - spi: The SPI bus connected to the radio.
140      - cs: The CS pin DigitalInOut connected to the radio.
141      - reset: The reset/RST pin DigialInOut connected to the radio.
142      - frequency: The frequency (in mhz) of the radio module (433/915mhz typically).
143  
144      You can optionally specify:
145      - preamble_length: The length in bytes of the packet preamble (default 8).
146      - high_power: Boolean to indicate a high power board (RFM95, etc.).  Default
147      is True for high power.
148      - baudrate: Baud rate of the SPI connection, default is 10mhz but you might
149      choose to lower to 1mhz if using long wires or a breadboard.
150  
151      Remember this library makes a best effort at receiving packets with pure
152      Python code.  Trying to receive packets too quickly will result in lost data
153      so limit yourself to simple scenarios of sending and receiving single
154      packets at a time.
155  
156      Also note this library tries to be compatible with raw RadioHead Arduino
157      library communication. This means the library sets up the radio modulation
158      to match RadioHead's defaults and assumes that each packet contains a
159      4 byte header compatible with RadioHead's implementation.
160      Advanced RadioHead features like address/node specific packets
161      or "reliable datagram" delivery are supported however due to the
162      limitations noted, "reliable datagram" is still subject to missed packets but with it,
163      sender is notified if a packet has potentially been missed.
164      """
165  
166      # Global buffer for SPI commands
167      _BUFFER = bytearray(4)
168  
169      class _RegisterBits:
170          # Class to simplify access to the many configuration bits avaialable
171          # on the chip's registers.  This is a subclass here instead of using
172          # a higher level module to increase the efficiency of memory usage
173          # (all of the instances of this bit class will share the same buffer
174          # used by the parent RFM69 class instance vs. each having their own
175          # buffer and taking too much memory).
176  
177          # Quirk of pylint that it requires public methods for a class.  This
178          # is a decorator class in Python and by design it has no public methods.
179          # Instead it uses dunder accessors like get and set below.  For some
180          # reason pylint can't figure this out so disable the check.
181          # pylint: disable=too-few-public-methods
182  
183          # Again pylint fails to see the true intent of this code and warns
184          # against private access by calling the write and read functions below.
185          # This is by design as this is an internally used class.  Disable the
186          # check from pylint.
187          # pylint: disable=protected-access
188  
189          def __init__(self, address, *, offset=0, bits=1):
190              assert 0 <= offset <= 7
191              assert 1 <= bits <= 8
192              assert (offset + bits) <= 8
193              self._address = address
194              self._mask = 0
195              for _ in range(bits):
196                  self._mask <<= 1
197                  self._mask |= 1
198              self._mask <<= offset
199              self._offset = offset
200  
201          def __get__(self, obj, objtype):
202              reg_value = obj._read_u8(self._address)
203              return (reg_value & self._mask) >> self._offset
204  
205          def __set__(self, obj, val):
206              reg_value = obj._read_u8(self._address)
207              reg_value &= ~self._mask
208              reg_value |= (val & 0xFF) << self._offset
209              obj._write_u8(self._address, reg_value)
210  
211      operation_mode = _RegisterBits(_RH_RF95_REG_01_OP_MODE, bits=3)
212  
213      low_frequency_mode = _RegisterBits(_RH_RF95_REG_01_OP_MODE, offset=3, bits=1)
214  
215      modulation_type = _RegisterBits(_RH_RF95_REG_01_OP_MODE, offset=5, bits=2)
216  
217      # Long range/LoRa mode can only be set in sleep mode!
218      long_range_mode = _RegisterBits(_RH_RF95_REG_01_OP_MODE, offset=7, bits=1)
219  
220      output_power = _RegisterBits(_RH_RF95_REG_09_PA_CONFIG, bits=4)
221  
222      max_power = _RegisterBits(_RH_RF95_REG_09_PA_CONFIG, offset=4, bits=3)
223  
224      pa_select = _RegisterBits(_RH_RF95_REG_09_PA_CONFIG, offset=7, bits=1)
225  
226      pa_dac = _RegisterBits(_RH_RF95_REG_4D_PA_DAC, bits=3)
227  
228      dio0_mapping = _RegisterBits(_RH_RF95_REG_40_DIO_MAPPING1, offset=6, bits=2)
229  
230      bw_bins = (7800, 10400, 15600, 20800, 31250, 41700, 62500, 125000, 250000)
231  
232      def __init__(
233          self,
234          spi,
235          cs,
236          reset,
237          frequency,
238          *,
239          preamble_length=8,
240          high_power=True,
241          baudrate=5000000
242      ):
243          self.high_power = high_power
244          # Device support SPI mode 0 (polarity & phase = 0) up to a max of 10mhz.
245          # Set Default Baudrate to 5MHz to avoid problems
246          self._device = spidev.SPIDevice(spi, cs, baudrate=baudrate, polarity=0, phase=0)
247          # Setup reset as a digital output - initially High
248          # This line is pulled low as an output quickly to trigger a reset.
249          self._reset = reset
250          # initialize Reset High
251          self._reset.switch_to_output(value=True)
252          self.reset()
253          # No device type check!  Catch an error from the very first request and
254          # throw a nicer message to indicate possible wiring problems.
255          version = self._read_u8(_RH_RF95_REG_42_VERSION)
256          if version != 18:
257              raise RuntimeError(
258                  "Failed to find rfm9x with expected version -- check wiring"
259              )
260  
261          # Set sleep mode, wait 10s and confirm in sleep mode (basic device check).
262          # Also set long range mode (LoRa mode) as it can only be done in sleep.
263          self.sleep()
264          time.sleep(0.01)
265          self.long_range_mode = True
266          if self.operation_mode != SLEEP_MODE or not self.long_range_mode:
267              raise RuntimeError("Failed to configure radio for LoRa mode, check wiring!")
268          # clear default setting for access to LF registers if frequency > 525MHz
269          if frequency > 525:
270              self.low_frequency_mode = 0
271          # Setup entire 256 byte FIFO
272          self._write_u8(_RH_RF95_REG_0E_FIFO_TX_BASE_ADDR, 0x00)
273          self._write_u8(_RH_RF95_REG_0F_FIFO_RX_BASE_ADDR, 0x00)
274          # Set mode idle
275          self.idle()
276          # Set frequency
277          self.frequency_mhz = frequency
278          # Set preamble length (default 8 bytes to match radiohead).
279          self.preamble_length = preamble_length
280          # Defaults set modem config to RadioHead compatible Bw125Cr45Sf128 mode.
281          self.signal_bandwidth = 125000
282          self.coding_rate = 5
283          self.spreading_factor = 7
284          # Default to disable CRC checking on incoming packets.
285          self.enable_crc = False
286          # Note no sync word is set for LoRa mode either!
287          self._write_u8(_RH_RF95_REG_26_MODEM_CONFIG3, 0x00)  # Preamble lsb?
288          # Set transmit power to 13 dBm, a safe value any module supports.
289          self.tx_power = 13
290          # initialize last RSSI reading
291          self.last_rssi = 0.0
292          """The RSSI of the last received packet. Stored when the packet was received.
293             This instantaneous RSSI value may not be accurate once the
294             operating mode has been changed.
295          """
296          # initialize timeouts and delays delays
297          self.ack_wait = 0.5
298          """The delay time before attempting a retry after not receiving an ACK"""
299          self.receive_timeout = 0.5
300          """The amount of time to poll for a received packet.
301             If no packet is received, the returned packet will be None
302          """
303          self.xmit_timeout = 2.0
304          """The amount of time to wait for the HW to transmit the packet.
305             This is mainly used to prevent a hang due to a HW issue
306          """
307          self.ack_retries = 5
308          """The number of ACK retries before reporting a failure."""
309          self.ack_delay = None
310          """The delay time before attemting to send an ACK.
311             If ACKs are being missed try setting this to .1 or .2.
312          """
313          # initialize sequence number counter for reliabe datagram mode
314          self.sequence_number = 0
315          # create seen Ids list
316          self.seen_ids = bytearray(256)
317          # initialize packet header
318          # node address - default is broadcast
319          self.node = _RH_BROADCAST_ADDRESS
320          """The default address of this Node. (0-255).
321             If not 255 (0xff) then only packets address to this node will be accepted.
322             First byte of the RadioHead header.
323          """
324          # destination address - default is broadcast
325          self.destination = _RH_BROADCAST_ADDRESS
326          """The default destination address for packet transmissions. (0-255).
327             If 255 (0xff) then any receiving node should accept the packet.
328             Second byte of the RadioHead header.
329          """
330          # ID - contains seq count for reliable datagram mode
331          self.identifier = 0
332          """Automatically set to the sequence number when send_with_ack() used.
333             Third byte of the RadioHead header.
334          """
335          # flags - identifies ack/reetry packet for reliable datagram mode
336          self.flags = 0
337          """Upper 4 bits reserved for use by Reliable Datagram Mode.
338             Lower 4 bits may be used to pass information.
339             Fourth byte of the RadioHead header.
340          """
341          self.crc_error_count = 0
342  
343      # pylint: disable=no-member
344      # Reconsider pylint: disable when this can be tested
345      def _read_into(self, address, buf, length=None):
346          # Read a number of bytes from the specified address into the provided
347          # buffer.  If length is not specified (the default) the entire buffer
348          # will be filled.
349          if length is None:
350              length = len(buf)
351          with self._device as device:
352              self._BUFFER[0] = address & 0x7F  # Strip out top bit to set 0
353              # value (read).
354              device.write(self._BUFFER, end=1)
355              device.readinto(buf, end=length)
356  
357      def _read_u8(self, address):
358          # Read a single byte from the provided address and return it.
359          self._read_into(address, self._BUFFER, length=1)
360          return self._BUFFER[0]
361  
362      def _write_from(self, address, buf, length=None):
363          # Write a number of bytes to the provided address and taken from the
364          # provided buffer.  If no length is specified (the default) the entire
365          # buffer is written.
366          if length is None:
367              length = len(buf)
368          with self._device as device:
369              self._BUFFER[0] = (address | 0x80) & 0xFF  # Set top bit to 1 to
370              # indicate a write.
371              device.write(self._BUFFER, end=1)
372              device.write(buf, end=length)
373  
374      def _write_u8(self, address, val):
375          # Write a byte register to the chip.  Specify the 7-bit address and the
376          # 8-bit value to write to that address.
377          with self._device as device:
378              self._BUFFER[0] = (address | 0x80) & 0xFF  # Set top bit to 1 to
379              # indicate a write.
380              self._BUFFER[1] = val & 0xFF
381              device.write(self._BUFFER, end=2)
382  
383      def reset(self):
384          """Perform a reset of the chip."""
385          # See section 7.2.2 of the datasheet for reset description.
386          self._reset.value = False  # Set Reset Low
387          time.sleep(0.0001)  # 100 us
388          self._reset.value = True  # set Reset High
389          time.sleep(0.005)  # 5 ms
390  
391      def idle(self):
392          """Enter idle standby mode."""
393          self.operation_mode = STANDBY_MODE
394  
395      def sleep(self):
396          """Enter sleep mode."""
397          self.operation_mode = SLEEP_MODE
398  
399      def listen(self):
400          """Listen for packets to be received by the chip.  Use :py:func:`receive`
401          to listen, wait and retrieve packets as they're available.
402          """
403          self.operation_mode = RX_MODE
404          self.dio0_mapping = 0b00  # Interrupt on rx done.
405  
406      def transmit(self):
407          """Transmit a packet which is queued in the FIFO.  This is a low level
408          function for entering transmit mode and more.  For generating and
409          transmitting a packet of data use :py:func:`send` instead.
410          """
411          self.operation_mode = TX_MODE
412          self.dio0_mapping = 0b01  # Interrupt on tx done.
413  
414      @property
415      def preamble_length(self):
416          """The length of the preamble for sent and received packets, an unsigned
417          16-bit value.  Received packets must match this length or they are
418          ignored! Set to 8 to match the RadioHead RFM95 library.
419          """
420          msb = self._read_u8(_RH_RF95_REG_20_PREAMBLE_MSB)
421          lsb = self._read_u8(_RH_RF95_REG_21_PREAMBLE_LSB)
422          return ((msb << 8) | lsb) & 0xFFFF
423  
424      @preamble_length.setter
425      def preamble_length(self, val):
426          assert 0 <= val <= 65535
427          self._write_u8(_RH_RF95_REG_20_PREAMBLE_MSB, (val >> 8) & 0xFF)
428          self._write_u8(_RH_RF95_REG_21_PREAMBLE_LSB, val & 0xFF)
429  
430      @property
431      def frequency_mhz(self):
432          """The frequency of the radio in Megahertz. Only the allowed values for
433          your radio must be specified (i.e. 433 vs. 915 mhz)!
434          """
435          msb = self._read_u8(_RH_RF95_REG_06_FRF_MSB)
436          mid = self._read_u8(_RH_RF95_REG_07_FRF_MID)
437          lsb = self._read_u8(_RH_RF95_REG_08_FRF_LSB)
438          frf = ((msb << 16) | (mid << 8) | lsb) & 0xFFFFFF
439          frequency = (frf * _RH_RF95_FSTEP) / 1000000.0
440          return frequency
441  
442      @frequency_mhz.setter
443      def frequency_mhz(self, val):
444          if val < 240 or val > 960:
445              raise RuntimeError("frequency_mhz must be between 240 and 960")
446          # Calculate FRF register 24-bit value.
447          frf = int((val * 1000000.0) / _RH_RF95_FSTEP) & 0xFFFFFF
448          # Extract byte values and update registers.
449          msb = frf >> 16
450          mid = (frf >> 8) & 0xFF
451          lsb = frf & 0xFF
452          self._write_u8(_RH_RF95_REG_06_FRF_MSB, msb)
453          self._write_u8(_RH_RF95_REG_07_FRF_MID, mid)
454          self._write_u8(_RH_RF95_REG_08_FRF_LSB, lsb)
455  
456      @property
457      def tx_power(self):
458          """The transmit power in dBm. Can be set to a value from 5 to 23 for
459          high power devices (RFM95/96/97/98, high_power=True) or -1 to 14 for low
460          power devices. Only integer power levels are actually set (i.e. 12.5
461          will result in a value of 12 dBm).
462          The actual maximum setting for high_power=True is 20dBm but for values > 20
463          the PA_BOOST will be enabled resulting in an additional gain of 3dBm.
464          The actual setting is reduced by 3dBm.
465          The reported value will reflect the reduced setting.
466          """
467          if self.high_power:
468              return self.output_power + 5
469          return self.output_power - 1
470  
471      @tx_power.setter
472      def tx_power(self, val):
473          val = int(val)
474          if self.high_power:
475              if val < 5 or val > 23:
476                  raise RuntimeError("tx_power must be between 5 and 23")
477              # Enable power amp DAC if power is above 20 dB.
478              # Lower setting by 3db when PA_BOOST enabled - see Data Sheet  Section 6.4
479              if val > 20:
480                  self.pa_dac = _RH_RF95_PA_DAC_ENABLE
481                  val -= 3
482              else:
483                  self.pa_dac = _RH_RF95_PA_DAC_DISABLE
484              self.pa_select = True
485              self.output_power = (val - 5) & 0x0F
486          else:
487              assert -1 <= val <= 14
488              self.pa_select = False
489              self.max_power = 0b111  # Allow max power output.
490              self.output_power = (val + 1) & 0x0F
491  
492      @property
493      def rssi(self):
494          """The received strength indicator (in dBm) of the last received message."""
495          # Read RSSI register and convert to value using formula in datasheet.
496          # Remember in LoRa mode the payload register changes function to RSSI!
497          return self._read_u8(_RH_RF95_REG_1A_PKT_RSSI_VALUE) - 137
498  
499      @property
500      def signal_bandwidth(self):
501          """The signal bandwidth used by the radio (try setting to a higher
502          value to increase throughput or to a lower value to increase the
503          likelihood of successfully received payloads).  Valid values are
504          listed in RFM9x.bw_bins."""
505          bw_id = (self._read_u8(_RH_RF95_REG_1D_MODEM_CONFIG1) & 0xF0) >> 4
506          if bw_id >= len(self.bw_bins):
507              current_bandwidth = 500000
508          else:
509              current_bandwidth = self.bw_bins[bw_id]
510          return current_bandwidth
511  
512      @signal_bandwidth.setter
513      def signal_bandwidth(self, val):
514          # Set signal bandwidth (set to 125000 to match RadioHead Bw125).
515          for bw_id, cutoff in enumerate(self.bw_bins):
516              if val <= cutoff:
517                  break
518          else:
519              bw_id = 9
520          self._write_u8(
521              _RH_RF95_REG_1D_MODEM_CONFIG1,
522              (self._read_u8(_RH_RF95_REG_1D_MODEM_CONFIG1) & 0x0F) | (bw_id << 4),
523          )
524  
525      @property
526      def coding_rate(self):
527          """The coding rate used by the radio to control forward error
528          correction (try setting to a higher value to increase tolerance of
529          short bursts of interference or to a lower value to increase bit
530          rate).  Valid values are limited to 5, 6, 7, or 8."""
531          cr_id = (self._read_u8(_RH_RF95_REG_1D_MODEM_CONFIG1) & 0x0E) >> 1
532          denominator = cr_id + 4
533          return denominator
534  
535      @coding_rate.setter
536      def coding_rate(self, val):
537          # Set coding rate (set to 5 to match RadioHead Cr45).
538          denominator = min(max(val, 5), 8)
539          cr_id = denominator - 4
540          self._write_u8(
541              _RH_RF95_REG_1D_MODEM_CONFIG1,
542              (self._read_u8(_RH_RF95_REG_1D_MODEM_CONFIG1) & 0xF1) | (cr_id << 1),
543          )
544  
545      @property
546      def spreading_factor(self):
547          """The spreading factor used by the radio (try setting to a higher
548          value to increase the receiver's ability to distinguish signal from
549          noise or to a lower value to increase the data transmission rate).
550          Valid values are limited to 6, 7, 8, 9, 10, 11, or 12."""
551          sf_id = (self._read_u8(_RH_RF95_REG_1E_MODEM_CONFIG2) & 0xF0) >> 4
552          return sf_id
553  
554      @spreading_factor.setter
555      def spreading_factor(self, val):
556          # Set spreading factor (set to 7 to match RadioHead Sf128).
557          val = min(max(val, 6), 12)
558          self._write_u8(_RH_RF95_DETECTION_OPTIMIZE, 0xC5 if val == 6 else 0xC3)
559          self._write_u8(_RH_RF95_DETECTION_THRESHOLD, 0x0C if val == 6 else 0x0A)
560          self._write_u8(
561              _RH_RF95_REG_1E_MODEM_CONFIG2,
562              (
563                  (self._read_u8(_RH_RF95_REG_1E_MODEM_CONFIG2) & 0x0F)
564                  | ((val << 4) & 0xF0)
565              ),
566          )
567  
568      @property
569      def enable_crc(self):
570          """Set to True to enable hardware CRC checking of incoming packets.
571          Incoming packets that fail the CRC check are not processed.  Set to
572          False to disable CRC checking and process all incoming packets."""
573          return (self._read_u8(_RH_RF95_REG_1E_MODEM_CONFIG2) & 0x04) == 0x04
574  
575      @enable_crc.setter
576      def enable_crc(self, val):
577          # Optionally enable CRC checking on incoming packets.
578          if val:
579              self._write_u8(
580                  _RH_RF95_REG_1E_MODEM_CONFIG2,
581                  self._read_u8(_RH_RF95_REG_1E_MODEM_CONFIG2) | 0x04,
582              )
583          else:
584              self._write_u8(
585                  _RH_RF95_REG_1E_MODEM_CONFIG2,
586                  self._read_u8(_RH_RF95_REG_1E_MODEM_CONFIG2) & 0xFB,
587              )
588  
589      def tx_done(self):
590          """Transmit status"""
591          return (self._read_u8(_RH_RF95_REG_12_IRQ_FLAGS) & 0x8) >> 3
592  
593      def rx_done(self):
594          """Receive status"""
595          return (self._read_u8(_RH_RF95_REG_12_IRQ_FLAGS) & 0x40) >> 6
596  
597      def crc_error(self):
598          """crc status"""
599          return (self._read_u8(_RH_RF95_REG_12_IRQ_FLAGS) & 0x20) >> 5
600  
601      def send(
602          self,
603          data,
604          *,
605          keep_listening=False,
606          destination=None,
607          node=None,
608          identifier=None,
609          flags=None
610      ):
611          """Send a string of data using the transmitter.
612             You can only send 252 bytes at a time
613             (limited by chip's FIFO size and appended headers).
614             This appends a 4 byte header to be compatible with the RadioHead library.
615             The header defaults to using the initialized attributes:
616             (destination,node,identifier,flags)
617             It may be temporarily overidden via the kwargs - destination,node,identifier,flags.
618             Values passed via kwargs do not alter the attribute settings.
619             The keep_listening argument should be set to True if you want to start listening
620             automatically after the packet is sent. The default setting is False.
621  
622             Returns: True if success or False if the send timed out.
623          """
624          # Disable pylint warning to not use length as a check for zero.
625          # This is a puzzling warning as the below code is clearly the most
626          # efficient and proper way to ensure a precondition that the provided
627          # buffer be within an expected range of bounds. Disable this check.
628          # pylint: disable=len-as-condition
629          assert 0 < len(data) <= 252
630          # pylint: enable=len-as-condition
631          self.idle()  # Stop receiving to clear FIFO and keep it clear.
632          # Fill the FIFO with a packet to send.
633          self._write_u8(_RH_RF95_REG_0D_FIFO_ADDR_PTR, 0x00)  # FIFO starts at 0.
634          # Combine header and data to form payload
635          payload = bytearray(4)
636          if destination is None:  # use attribute
637              payload[0] = self.destination
638          else:  # use kwarg
639              payload[0] = destination
640          if node is None:  # use attribute
641              payload[1] = self.node
642          else:  # use kwarg
643              payload[1] = node
644          if identifier is None:  # use attribute
645              payload[2] = self.identifier
646          else:  # use kwarg
647              payload[2] = identifier
648          if flags is None:  # use attribute
649              payload[3] = self.flags
650          else:  # use kwarg
651              payload[3] = flags
652          payload = payload + data
653          # Write payload.
654          self._write_from(_RH_RF95_REG_00_FIFO, payload)
655          # Write payload and header length.
656          self._write_u8(_RH_RF95_REG_22_PAYLOAD_LENGTH, len(payload))
657          # Turn on transmit mode to send out the packet.
658          self.transmit()
659          # Wait for tx done interrupt with explicit polling (not ideal but
660          # best that can be done right now without interrupts).
661          start = time.monotonic()
662          timed_out = False
663          while not timed_out and not self.tx_done():
664              if (time.monotonic() - start) >= self.xmit_timeout:
665                  timed_out = True
666          # Listen again if necessary and return the result packet.
667          if keep_listening:
668              self.listen()
669          else:
670              # Enter idle mode to stop receiving other packets.
671              self.idle()
672          # Clear interrupt.
673          self._write_u8(_RH_RF95_REG_12_IRQ_FLAGS, 0xFF)
674          return not timed_out
675  
676      def send_with_ack(self, data):
677          """Reliable Datagram mode:
678             Send a packet with data and wait for an ACK response.
679             The packet header is automatically generated.
680             If enabled, the packet transmission will be retried on failure
681          """
682          if self.ack_retries:
683              retries_remaining = self.ack_retries
684          else:
685              retries_remaining = 1
686          got_ack = False
687          self.sequence_number = (self.sequence_number + 1) & 0xFF
688          while not got_ack and retries_remaining:
689              self.identifier = self.sequence_number
690              self.send(data, keep_listening=True)
691              # Don't look for ACK from Broadcast message
692              if self.destination == _RH_BROADCAST_ADDRESS:
693                  got_ack = True
694              else:
695                  # wait for a packet from our destination
696                  ack_packet = self.receive(timeout=self.ack_wait, with_header=True)
697                  if ack_packet is not None:
698                      if ack_packet[3] & _RH_FLAGS_ACK:
699                          # check the ID
700                          if ack_packet[2] == self.identifier:
701                              got_ack = True
702                              break
703              # pause before next retry -- random delay
704              if not got_ack:
705                  # delay by random amount before next try
706                  time.sleep(self.ack_wait + self.ack_wait * random.random())
707              retries_remaining = retries_remaining - 1
708              # set retry flag in packet header
709              self.flags |= _RH_FLAGS_RETRY
710          self.flags = 0  # clear flags
711          return got_ack
712  
713      # pylint: disable=too-many-branches
714      def receive(
715          self, *, keep_listening=True, with_header=False, with_ack=False, timeout=None
716      ):
717          """Wait to receive a packet from the receiver. If a packet is found the payload bytes
718             are returned, otherwise None is returned (which indicates the timeout elapsed with no
719             reception).
720             If keep_listening is True (the default) the chip will immediately enter listening mode
721             after reception of a packet, otherwise it will fall back to idle mode and ignore any
722             future reception.
723             All packets must have a 4-byte header for compatibilty with the
724             RadioHead library.
725             The header consists of 4 bytes (To,From,ID,Flags). The default setting will  strip
726             the header before returning the packet to the caller.
727             If with_header is True then the 4 byte header will be returned with the packet.
728             The payload then begins at packet[4].
729             If with_ack is True, send an ACK after receipt (Reliable Datagram mode)
730          """
731          timed_out = False
732          if timeout is None:
733              timeout = self.receive_timeout
734          if timeout is not None:
735              # Wait for the payload_ready signal.  This is not ideal and will
736              # surely miss or overflow the FIFO when packets aren't read fast
737              # enough, however it's the best that can be done from Python without
738              # interrupt supports.
739              # Make sure we are listening for packets.
740              self.listen()
741              start = time.monotonic()
742              timed_out = False
743              while not timed_out and not self.rx_done():
744                  if (time.monotonic() - start) >= timeout:
745                      timed_out = True
746          # Payload ready is set, a packet is in the FIFO.
747          packet = None
748          # save last RSSI reading
749          self.last_rssi = self.rssi
750          # Enter idle mode to stop receiving other packets.
751          self.idle()
752          if not timed_out:
753              if self.enable_crc and self.crc_error():
754                  self.crc_error_count += 1
755              else:
756                  # Read the data from the FIFO.
757                  # Read the length of the FIFO.
758                  fifo_length = self._read_u8(_RH_RF95_REG_13_RX_NB_BYTES)
759                  # Handle if the received packet is too small to include the 4 byte
760                  # RadioHead header and at least one byte of data --reject this packet and ignore it.
761                  if fifo_length > 0:  # read and clear the FIFO if anything in it
762                      current_addr = self._read_u8(_RH_RF95_REG_10_FIFO_RX_CURRENT_ADDR)
763                      self._write_u8(_RH_RF95_REG_0D_FIFO_ADDR_PTR, current_addr)
764                      packet = bytearray(fifo_length)
765                      # Read the packet.
766                      self._read_into(_RH_RF95_REG_00_FIFO, packet)
767                  # Clear interrupt.
768                  self._write_u8(_RH_RF95_REG_12_IRQ_FLAGS, 0xFF)
769                  if fifo_length < 5:
770                      packet = None
771                  else:
772                      if (
773                          self.node != _RH_BROADCAST_ADDRESS
774                          and packet[0] != _RH_BROADCAST_ADDRESS
775                          and packet[0] != self.node
776                      ):
777                          packet = None
778                      # send ACK unless this was an ACK or a broadcast
779                      elif (
780                          with_ack
781                          and ((packet[3] & _RH_FLAGS_ACK) == 0)
782                          and (packet[0] != _RH_BROADCAST_ADDRESS)
783                      ):
784                          # delay before sending Ack to give receiver a chance to get ready
785                          if self.ack_delay is not None:
786                              time.sleep(self.ack_delay)
787                          # send ACK packet to sender (data is b'!')
788                          self.send(
789                              b"!",
790                              destination=packet[1],
791                              node=packet[0],
792                              identifier=packet[2],
793                              flags=(packet[3] | _RH_FLAGS_ACK),
794                          )
795                          # reject Retries if we have seen this idetifier from this source before
796                          if (self.seen_ids[packet[1]] == packet[2]) and (
797                              packet[3] & _RH_FLAGS_RETRY
798                          ):
799                              packet = None
800                          else:  # save the packet identifier for this source
801                              self.seen_ids[packet[1]] = packet[2]
802                      if (
803                          not with_header and packet is not None
804                      ):  # skip the header if not wanted
805                          packet = packet[4:]
806          # Listen again if necessary and return the result packet.
807          if keep_listening:
808              self.listen()
809          else:
810              # Enter idle mode to stop receiving other packets.
811              self.idle()
812          # Clear interrupt.
813          self._write_u8(_RH_RF95_REG_12_IRQ_FLAGS, 0xFF)
814          return packet