/ 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