/ adafruit_rfm69.py
adafruit_rfm69.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_rfm69` 24 ==================================================== 25 26 CircuitPython RFM69 packet radio module. This supports basic RadioHead-compatible sending and 27 receiving of packets with RFM69 series radios (433/915Mhz). 28 29 .. warning:: This is NOT for LoRa radios! 30 31 .. note:: This is a 'best effort' at receiving data using pure Python code--there is not interrupt 32 support so you might lose packets if they're sent too quickly for the board to process them. 33 You will have the most luck using this in simple low bandwidth scenarios like sending and 34 receiving a 60 byte packet at a time--don't try to receive many kilobytes of data at a time! 35 36 * Author(s): Tony DiCola, Jerry Needell 37 38 Implementation Notes 39 -------------------- 40 41 **Hardware:** 42 43 * Adafruit `RFM69HCW Transceiver Radio Breakout - 868 or 915 MHz - RadioFruit 44 <https://www.adafruit.com/product/3070>`_ (Product ID: 3070) 45 46 * Adafruit `RFM69HCW Transceiver Radio Breakout - 433 MHz - RadioFruit 47 <https://www.adafruit.com/product/3071>`_ (Product ID: 3071) 48 49 * Adafruit `Feather M0 RFM69HCW Packet Radio - 868 or 915 MHz - RadioFruit 50 <https://www.adafruit.com/product/3176>`_ (Product ID: 3176) 51 52 * Adafruit `Feather M0 RFM69HCW Packet Radio - 433 MHz - RadioFruit 53 <https://www.adafruit.com/product/3177>`_ (Product ID: 3177) 54 55 * Adafruit `Radio FeatherWing - RFM69HCW 900MHz - RadioFruit 56 <https://www.adafruit.com/product/3229>`_ (Product ID: 3229) 57 58 * Adafruit `Radio FeatherWing - RFM69HCW 433MHz - RadioFruit 59 <https://www.adafruit.com/product/3230>`_ (Product ID: 3230) 60 61 **Software and Dependencies:** 62 63 * Adafruit CircuitPython firmware for the ESP8622 and M0-based boards: 64 https://github.com/adafruit/circuitpython/releases 65 * Adafruit's Bus Device library: https://github.com/adafruit/Adafruit_CircuitPython_BusDevice 66 """ 67 import time 68 import random 69 70 from micropython import const 71 72 import adafruit_bus_device.spi_device as spidev 73 74 75 __version__ = "0.0.0-auto.0" 76 __repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_RFM69.git" 77 78 79 # Internal constants: 80 _REG_FIFO = const(0x00) 81 _REG_OP_MODE = const(0x01) 82 _REG_DATA_MOD = const(0x02) 83 _REG_BITRATE_MSB = const(0x03) 84 _REG_BITRATE_LSB = const(0x04) 85 _REG_FDEV_MSB = const(0x05) 86 _REG_FDEV_LSB = const(0x06) 87 _REG_FRF_MSB = const(0x07) 88 _REG_FRF_MID = const(0x08) 89 _REG_FRF_LSB = const(0x09) 90 _REG_VERSION = const(0x10) 91 _REG_PA_LEVEL = const(0x11) 92 _REG_RX_BW = const(0x19) 93 _REG_AFC_BW = const(0x1A) 94 _REG_RSSI_VALUE = const(0x24) 95 _REG_DIO_MAPPING1 = const(0x25) 96 _REG_IRQ_FLAGS1 = const(0x27) 97 _REG_IRQ_FLAGS2 = const(0x28) 98 _REG_PREAMBLE_MSB = const(0x2C) 99 _REG_PREAMBLE_LSB = const(0x2D) 100 _REG_SYNC_CONFIG = const(0x2E) 101 _REG_SYNC_VALUE1 = const(0x2F) 102 _REG_PACKET_CONFIG1 = const(0x37) 103 _REG_FIFO_THRESH = const(0x3C) 104 _REG_PACKET_CONFIG2 = const(0x3D) 105 _REG_AES_KEY1 = const(0x3E) 106 _REG_TEMP1 = const(0x4E) 107 _REG_TEMP2 = const(0x4F) 108 _REG_TEST_PA1 = const(0x5A) 109 _REG_TEST_PA2 = const(0x5C) 110 _REG_TEST_DAGC = const(0x6F) 111 112 _TEST_PA1_NORMAL = const(0x55) 113 _TEST_PA1_BOOST = const(0x5D) 114 _TEST_PA2_NORMAL = const(0x70) 115 _TEST_PA2_BOOST = const(0x7C) 116 117 # The crystal oscillator frequency and frequency synthesizer step size. 118 # See the datasheet for details of this calculation. 119 _FXOSC = 32000000.0 120 _FSTEP = _FXOSC / 524288 121 122 # RadioHead specific compatibility constants. 123 _RH_BROADCAST_ADDRESS = const(0xFF) 124 # The acknowledgement bit in the FLAGS 125 # The top 4 bits of the flags are reserved for RadioHead. The lower 4 bits are reserved 126 # for application layer use. 127 _RH_FLAGS_ACK = const(0x80) 128 _RH_FLAGS_RETRY = const(0x40) 129 130 # User facing constants: 131 SLEEP_MODE = 0b000 132 STANDBY_MODE = 0b001 133 FS_MODE = 0b010 134 TX_MODE = 0b011 135 RX_MODE = 0b100 136 137 # Disable the silly too many instance members warning. Pylint has no knowledge 138 # of the context and is merely guessing at the proper amount of members. This 139 # is a complex chip which requires exposing many attributes and state. Disable 140 # the warning to work around the error. 141 # pylint: disable=too-many-instance-attributes 142 143 144 class RFM69: 145 """Interface to a RFM69 series packet radio. Allows simple sending and 146 receiving of wireless data at supported frequencies of the radio 147 (433/915mhz). 148 149 :param busio.SPI spi: The SPI bus connected to the chip. Ensure SCK, MOSI, and MISO are 150 connected. 151 :param ~digitalio.DigitalInOut cs: A DigitalInOut object connected to the chip's CS/chip select 152 line. 153 :param ~digitalio.DigitalInOut reset: A DigitalInOut object connected to the chip's RST/reset 154 line. 155 :param int frequency: The center frequency to configure for radio transmission and reception. 156 Must be a frequency supported by your hardware (i.e. either 433 or 915mhz). 157 :param bytes sync_word: A byte string up to 8 bytes long which represents the syncronization 158 word used by received and transmitted packets. Read the datasheet for a full understanding 159 of this value! However by default the library will set a value that matches the RadioHead 160 Arduino library. 161 :param int preamble_length: The number of bytes to pre-pend to a data packet as a preamble. 162 This is by default 4 to match the RadioHead library. 163 :param bytes encryption_key: A 16 byte long string that represents the AES encryption key to use 164 when encrypting and decrypting packets. Both the transmitter and receiver MUST have the 165 same key value! By default no encryption key is set or used. 166 :param bool high_power: Indicate if the chip is a high power variant that supports boosted 167 transmission power. The default is True as it supports the common RFM69HCW modules sold by 168 Adafruit. 169 170 .. note:: The D0/interrupt line is currently unused by this module and can remain unconnected. 171 172 Remember this library makes a best effort at receiving packets with pure Python code. Trying 173 to receive packets too quickly will result in lost data so limit yourself to simple scenarios 174 of sending and receiving single packets at a time. 175 176 Also note this library tries to be compatible with raw RadioHead Arduino library communication. 177 This means the library sets up the radio modulation to match RadioHead's default of GFSK 178 encoding, 250kbit/s bitrate, and 250khz frequency deviation. To change this requires explicitly 179 setting the radio's bitrate and encoding register bits. Read the datasheet and study the init 180 function to see an example of this--advanced users only! Advanced RadioHead features like 181 address/node specific packets or "reliable datagram" delivery are supported however due to the 182 limitations noted, "reliable datagram" is still subject to missed packets but with it, the 183 sender is notified if a packe has potentially been missed. 184 """ 185 186 # Global buffer for SPI commands. 187 _BUFFER = bytearray(4) 188 189 class _RegisterBits: 190 # Class to simplify access to the many configuration bits avaialable 191 # on the chip's registers. This is a subclass here instead of using 192 # a higher level module to increase the efficiency of memory usage 193 # (all of the instances of this bit class will share the same buffer 194 # used by the parent RFM69 class instance vs. each having their own 195 # buffer and taking too much memory). 196 197 # Quirk of pylint that it requires public methods for a class. This 198 # is a decorator class in Python and by design it has no public methods. 199 # Instead it uses dunder accessors like get and set below. For some 200 # reason pylint can't figure this out so disable the check. 201 # pylint: disable=too-few-public-methods 202 203 # Again pylint fails to see the true intent of this code and warns 204 # against private access by calling the write and read functions below. 205 # This is by design as this is an internally used class. Disable the 206 # check from pylint. 207 # pylint: disable=protected-access 208 209 def __init__(self, address, *, offset=0, bits=1): 210 assert 0 <= offset <= 7 211 assert 1 <= bits <= 8 212 assert (offset + bits) <= 8 213 self._address = address 214 self._mask = 0 215 for _ in range(bits): 216 self._mask <<= 1 217 self._mask |= 1 218 self._mask <<= offset 219 self._offset = offset 220 221 def __get__(self, obj, objtype): 222 reg_value = obj._read_u8(self._address) 223 return (reg_value & self._mask) >> self._offset 224 225 def __set__(self, obj, val): 226 reg_value = obj._read_u8(self._address) 227 reg_value &= ~self._mask 228 reg_value |= (val & 0xFF) << self._offset 229 obj._write_u8(self._address, reg_value) 230 231 # Control bits from the registers of the chip: 232 data_mode = _RegisterBits(_REG_DATA_MOD, offset=5, bits=2) 233 modulation_type = _RegisterBits(_REG_DATA_MOD, offset=3, bits=2) 234 modulation_shaping = _RegisterBits(_REG_DATA_MOD, offset=0, bits=2) 235 temp_start = _RegisterBits(_REG_TEMP1, offset=3) 236 temp_running = _RegisterBits(_REG_TEMP1, offset=2) 237 sync_on = _RegisterBits(_REG_SYNC_CONFIG, offset=7) 238 sync_size = _RegisterBits(_REG_SYNC_CONFIG, offset=3, bits=3) 239 aes_on = _RegisterBits(_REG_PACKET_CONFIG2, offset=0) 240 pa_0_on = _RegisterBits(_REG_PA_LEVEL, offset=7) 241 pa_1_on = _RegisterBits(_REG_PA_LEVEL, offset=6) 242 pa_2_on = _RegisterBits(_REG_PA_LEVEL, offset=5) 243 output_power = _RegisterBits(_REG_PA_LEVEL, offset=0, bits=5) 244 rx_bw_dcc_freq = _RegisterBits(_REG_RX_BW, offset=5, bits=3) 245 rx_bw_mantissa = _RegisterBits(_REG_RX_BW, offset=3, bits=2) 246 rx_bw_exponent = _RegisterBits(_REG_RX_BW, offset=0, bits=3) 247 afc_bw_dcc_freq = _RegisterBits(_REG_AFC_BW, offset=5, bits=3) 248 afc_bw_mantissa = _RegisterBits(_REG_AFC_BW, offset=3, bits=2) 249 afc_bw_exponent = _RegisterBits(_REG_AFC_BW, offset=0, bits=3) 250 packet_format = _RegisterBits(_REG_PACKET_CONFIG1, offset=7, bits=1) 251 dc_free = _RegisterBits(_REG_PACKET_CONFIG1, offset=5, bits=2) 252 crc_on = _RegisterBits(_REG_PACKET_CONFIG1, offset=4, bits=1) 253 crc_auto_clear_off = _RegisterBits(_REG_PACKET_CONFIG1, offset=3, bits=1) 254 address_filter = _RegisterBits(_REG_PACKET_CONFIG1, offset=1, bits=2) 255 mode_ready = _RegisterBits(_REG_IRQ_FLAGS1, offset=7) 256 rx_ready = _RegisterBits(_REG_IRQ_FLAGS1, offset=6) 257 tx_ready = _RegisterBits(_REG_IRQ_FLAGS1, offset=5) 258 dio_0_mapping = _RegisterBits(_REG_DIO_MAPPING1, offset=6, bits=2) 259 packet_sent = _RegisterBits(_REG_IRQ_FLAGS2, offset=3) 260 payload_ready = _RegisterBits(_REG_IRQ_FLAGS2, offset=2) 261 262 def __init__( 263 self, 264 spi, 265 cs, 266 reset, 267 frequency, 268 *, 269 sync_word=b"\x2D\xD4", 270 preamble_length=4, 271 encryption_key=None, 272 high_power=True, 273 baudrate=5000000 274 ): 275 self._tx_power = 13 276 self.high_power = high_power 277 # Device support SPI mode 0 (polarity & phase = 0) up to a max of 10mhz. 278 self._device = spidev.SPIDevice(spi, cs, baudrate=baudrate, polarity=0, phase=0) 279 # Setup reset as a digital output that's low. 280 self._reset = reset 281 self._reset.switch_to_output(value=False) 282 self.reset() # Reset the chip. 283 # Check the version of the chip. 284 version = self._read_u8(_REG_VERSION) 285 if version != 0x24: 286 raise RuntimeError( 287 "Failed to find RFM69 with expected version, check wiring!" 288 ) 289 self.idle() # Enter idle state. 290 # Setup the chip in a similar way to the RadioHead RFM69 library. 291 # Set FIFO TX condition to not empty and the default FIFO threshold to 15. 292 self._write_u8(_REG_FIFO_THRESH, 0b10001111) 293 # Configure low beta off. 294 self._write_u8(_REG_TEST_DAGC, 0x30) 295 # Disable boost. 296 self._write_u8(_REG_TEST_PA1, _TEST_PA1_NORMAL) 297 self._write_u8(_REG_TEST_PA2, _TEST_PA2_NORMAL) 298 # Set the syncronization word. 299 self.sync_word = sync_word 300 self.preamble_length = preamble_length # Set the preamble length. 301 self.frequency_mhz = frequency # Set frequency. 302 self.encryption_key = encryption_key # Set encryption key. 303 # set radio configuration parameters 304 self._configure_radio() 305 # initialize last RSSI reading 306 self.last_rssi = 0.0 307 """The RSSI of the last received packet. Stored when the packet was received. 308 This instantaneous RSSI value may not be accurate once the 309 operating mode has been changed. 310 """ 311 # initialize timeouts and delays delays 312 self.ack_wait = 0.5 313 """The delay time before attempting a retry after not receiving an ACK""" 314 self.receive_timeout = 0.5 315 """The amount of time to poll for a received packet. 316 If no packet is received, the returned packet will be None 317 """ 318 self.xmit_timeout = 2.0 319 """The amount of time to wait for the HW to transmit the packet. 320 This is mainly used to prevent a hang due to a HW issue 321 """ 322 self.ack_retries = 5 323 """The number of ACK retries before reporting a failure.""" 324 self.ack_delay = None 325 """The delay time before attemting to send an ACK. 326 If ACKs are being missed try setting this to .1 or .2. 327 """ 328 # initialize sequence number counter for reliabe datagram mode 329 self.sequence_number = 0 330 # create seen Ids list 331 self.seen_ids = bytearray(256) 332 # initialize packet header 333 # node address - default is broadcast 334 self.node = _RH_BROADCAST_ADDRESS 335 """The default address of this Node. (0-255). 336 If not 255 (0xff) then only packets address to this node will be accepted. 337 First byte of the RadioHead header. 338 """ 339 # destination address - default is broadcast 340 self.destination = _RH_BROADCAST_ADDRESS 341 """The default destination address for packet transmissions. (0-255). 342 If 255 (0xff) then any receiving node should accept the packet. 343 Second byte of the RadioHead header. 344 """ 345 # ID - contains seq count for reliable datagram mode 346 self.identifier = 0 347 """Automatically set to the sequence number when send_with_ack() used. 348 Third byte of the RadioHead header. 349 """ 350 # flags - identifies ack/reetry packet for reliable datagram mode 351 self.flags = 0 352 """Upper 4 bits reserved for use by Reliable Datagram Mode. 353 Lower 4 bits may be used to pass information. 354 Fourth byte of the RadioHead header. 355 """ 356 357 def _configure_radio(self): 358 # Configure modulation for RadioHead library GFSK_Rb250Fd250 mode 359 # by default. Users with advanced knowledge can manually reconfigure 360 # for any other mode (consulting the datasheet is absolutely 361 # necessary!). 362 self.data_mode = 0b00 # Packet mode 363 self.modulation_type = 0b00 # FSK modulation 364 self.modulation_shaping = 0b01 # Gaussian filter, BT=1.0 365 self.bitrate = 250000 # 250kbs 366 self.frequency_deviation = 250000 # 250khz 367 self.rx_bw_dcc_freq = 0b111 # RxBw register = 0xE0 368 self.rx_bw_mantissa = 0b00 369 self.rx_bw_exponent = 0b000 370 self.afc_bw_dcc_freq = 0b111 # AfcBw register = 0xE0 371 self.afc_bw_mantissa = 0b00 372 self.afc_bw_exponent = 0b000 373 self.packet_format = 1 # Variable length. 374 self.dc_free = 0b10 # Whitening 375 self.crc_on = 1 # CRC enabled 376 self.crc_auto_clear = 0 # Clear FIFO on CRC fail 377 self.address_filtering = 0b00 # No address filtering 378 # Set transmit power to 13 dBm, a safe value any module supports. 379 self.tx_power = 13 380 381 # pylint: disable=no-member 382 # Reconsider this disable when it can be tested. 383 def _read_into(self, address, buf, length=None): 384 # Read a number of bytes from the specified address into the provided 385 # buffer. If length is not specified (the default) the entire buffer 386 # will be filled. 387 if length is None: 388 length = len(buf) 389 with self._device as device: 390 self._BUFFER[0] = address & 0x7F # Strip out top bit to set 0 391 # value (read). 392 device.write(self._BUFFER, end=1) 393 device.readinto(buf, end=length) 394 395 def _read_u8(self, address): 396 # Read a single byte from the provided address and return it. 397 self._read_into(address, self._BUFFER, length=1) 398 return self._BUFFER[0] 399 400 def _write_from(self, address, buf, length=None): 401 # Write a number of bytes to the provided address and taken from the 402 # provided buffer. If no length is specified (the default) the entire 403 # buffer is written. 404 if length is None: 405 length = len(buf) 406 with self._device as device: 407 self._BUFFER[0] = (address | 0x80) & 0xFF # Set top bit to 1 to 408 # indicate a write. 409 device.write(self._BUFFER, end=1) 410 device.write(buf, end=length) # send data 411 412 def _write_fifo_from(self, buf, length=None): 413 # Write a number of bytes to the transmit FIFO and taken from the 414 # provided buffer. If no length is specified (the default) the entire 415 # buffer is written. 416 if length is None: 417 length = len(buf) 418 with self._device as device: 419 self._BUFFER[0] = (_REG_FIFO | 0x80) & 0xFF # Set top bit to 1 to 420 # indicate a write. 421 self._BUFFER[1] = length & 0xFF # Set packt length 422 device.write(self._BUFFER, end=2) # send address and lenght) 423 device.write(buf, end=length) # send data 424 425 def _write_u8(self, address, val): 426 # Write a byte register to the chip. Specify the 7-bit address and the 427 # 8-bit value to write to that address. 428 with self._device as device: 429 self._BUFFER[0] = (address | 0x80) & 0xFF # Set top bit to 1 to 430 # indicate a write. 431 self._BUFFER[1] = val & 0xFF 432 device.write(self._BUFFER, end=2) 433 434 def reset(self): 435 """Perform a reset of the chip.""" 436 # See section 7.2.2 of the datasheet for reset description. 437 self._reset.value = True 438 time.sleep(0.0001) # 100 us 439 self._reset.value = False 440 time.sleep(0.005) # 5 ms 441 442 def idle(self): 443 """Enter idle standby mode (switching off high power amplifiers if necessary).""" 444 # Like RadioHead library, turn off high power boost if enabled. 445 if self._tx_power >= 18: 446 self._write_u8(_REG_TEST_PA1, _TEST_PA1_NORMAL) 447 self._write_u8(_REG_TEST_PA2, _TEST_PA2_NORMAL) 448 self.operation_mode = STANDBY_MODE 449 450 def sleep(self): 451 """Enter sleep mode.""" 452 self.operation_mode = SLEEP_MODE 453 454 def listen(self): 455 """Listen for packets to be received by the chip. Use :py:func:`receive` to listen, wait 456 and retrieve packets as they're available. 457 """ 458 # Like RadioHead library, turn off high power boost if enabled. 459 if self._tx_power >= 18: 460 self._write_u8(_REG_TEST_PA1, _TEST_PA1_NORMAL) 461 self._write_u8(_REG_TEST_PA2, _TEST_PA2_NORMAL) 462 # Enable payload ready interrupt for D0 line. 463 self.dio_0_mapping = 0b01 464 # Enter RX mode (will clear FIFO!). 465 self.operation_mode = RX_MODE 466 467 def transmit(self): 468 """Transmit a packet which is queued in the FIFO. This is a low level function for 469 entering transmit mode and more. For generating and transmitting a packet of data use 470 :py:func:`send` instead. 471 """ 472 # Like RadioHead library, turn on high power boost if enabled. 473 if self._tx_power >= 18: 474 self._write_u8(_REG_TEST_PA1, _TEST_PA1_BOOST) 475 self._write_u8(_REG_TEST_PA2, _TEST_PA2_BOOST) 476 # Enable packet sent interrupt for D0 line. 477 self.dio_0_mapping = 0b00 478 # Enter TX mode (will clear FIFO!). 479 self.operation_mode = TX_MODE 480 481 @property 482 def temperature(self): 483 """The internal temperature of the chip in degrees Celsius. Be warned this is not 484 calibrated or very accurate. 485 486 .. warning:: Reading this will STOP any receiving/sending that might be happening! 487 """ 488 # Start a measurement then poll the measurement finished bit. 489 self.temp_start = 1 490 while self.temp_running > 0: 491 pass 492 # Grab the temperature value and convert it to Celsius. 493 # This uses the same observed value formula from the Radiohead library. 494 temp = self._read_u8(_REG_TEMP2) 495 return 166.0 - temp 496 497 @property 498 def operation_mode(self): 499 """The operation mode value. Unless you're manually controlling the chip you shouldn't 500 change the operation_mode with this property as other side-effects are required for 501 changing logical modes--use :py:func:`idle`, :py:func:`sleep`, :py:func:`transmit`, 502 :py:func:`listen` instead to signal intent for explicit logical modes. 503 """ 504 op_mode = self._read_u8(_REG_OP_MODE) 505 return (op_mode >> 2) & 0b111 506 507 @operation_mode.setter 508 def operation_mode(self, val): 509 assert 0 <= val <= 4 510 # Set the mode bits inside the operation mode register. 511 op_mode = self._read_u8(_REG_OP_MODE) 512 op_mode &= 0b11100011 513 op_mode |= val << 2 514 self._write_u8(_REG_OP_MODE, op_mode) 515 # Wait for mode to change by polling interrupt bit. 516 while not self.mode_ready: 517 pass 518 519 @property 520 def sync_word(self): 521 """The synchronization word value. This is a byte string up to 8 bytes long (64 bits) 522 which indicates the synchronization word for transmitted and received packets. Any 523 received packet which does not include this sync word will be ignored. The default value 524 is 0x2D, 0xD4 which matches the RadioHead RFM69 library. Setting a value of None will 525 disable synchronization word matching entirely. 526 """ 527 # Handle when sync word is disabled.. 528 if not self.sync_on: 529 return None 530 # Sync word is not disabled so read the current value. 531 sync_word_length = self.sync_size + 1 # Sync word size is offset by 1 532 # according to datasheet. 533 sync_word = bytearray(sync_word_length) 534 self._read_into(_REG_SYNC_VALUE1, sync_word) 535 return sync_word 536 537 @sync_word.setter 538 def sync_word(self, val): 539 # Handle disabling sync word when None value is set. 540 if val is None: 541 self.sync_on = 0 542 else: 543 # Check sync word is at most 8 bytes. 544 assert 1 <= len(val) <= 8 545 # Update the value, size and turn on the sync word. 546 self._write_from(_REG_SYNC_VALUE1, val) 547 self.sync_size = len(val) - 1 # Again sync word size is offset by 548 # 1 according to datasheet. 549 self.sync_on = 1 550 551 @property 552 def preamble_length(self): 553 """The length of the preamble for sent and received packets, an unsigned 16-bit value. 554 Received packets must match this length or they are ignored! Set to 4 to match the 555 RadioHead RFM69 library. 556 """ 557 msb = self._read_u8(_REG_PREAMBLE_MSB) 558 lsb = self._read_u8(_REG_PREAMBLE_LSB) 559 return ((msb << 8) | lsb) & 0xFFFF 560 561 @preamble_length.setter 562 def preamble_length(self, val): 563 assert 0 <= val <= 65535 564 self._write_u8(_REG_PREAMBLE_MSB, (val >> 8) & 0xFF) 565 self._write_u8(_REG_PREAMBLE_LSB, val & 0xFF) 566 567 @property 568 def frequency_mhz(self): 569 """The frequency of the radio in Megahertz. Only the allowed values for your radio must be 570 specified (i.e. 433 vs. 915 mhz)! 571 """ 572 # FRF register is computed from the frequency following the datasheet. 573 # See section 6.2 and FRF register description. 574 # Read bytes of FRF register and assemble into a 24-bit unsigned value. 575 msb = self._read_u8(_REG_FRF_MSB) 576 mid = self._read_u8(_REG_FRF_MID) 577 lsb = self._read_u8(_REG_FRF_LSB) 578 frf = ((msb << 16) | (mid << 8) | lsb) & 0xFFFFFF 579 frequency = (frf * _FSTEP) / 1000000.0 580 return frequency 581 582 @frequency_mhz.setter 583 def frequency_mhz(self, val): 584 assert 290 <= val <= 1020 585 # Calculate FRF register 24-bit value using section 6.2 of the datasheet. 586 frf = int((val * 1000000.0) / _FSTEP) & 0xFFFFFF 587 # Extract byte values and update registers. 588 msb = frf >> 16 589 mid = (frf >> 8) & 0xFF 590 lsb = frf & 0xFF 591 self._write_u8(_REG_FRF_MSB, msb) 592 self._write_u8(_REG_FRF_MID, mid) 593 self._write_u8(_REG_FRF_LSB, lsb) 594 595 @property 596 def encryption_key(self): 597 """The AES encryption key used to encrypt and decrypt packets by the chip. This can be set 598 to None to disable encryption (the default), otherwise it must be a 16 byte long byte 599 string which defines the key (both the transmitter and receiver must use the same key 600 value). 601 """ 602 # Handle if encryption is disabled. 603 if self.aes_on == 0: 604 return None 605 # Encryption is enabled so read the key and return it. 606 key = bytearray(16) 607 self._read_into(_REG_AES_KEY1, key) 608 return key 609 610 @encryption_key.setter 611 def encryption_key(self, val): 612 # Handle if unsetting the encryption key (None value). 613 if val is None: 614 self.aes_on = 0 615 else: 616 # Set the encryption key and enable encryption. 617 assert len(val) == 16 618 self._write_from(_REG_AES_KEY1, val) 619 self.aes_on = 1 620 621 @property 622 def tx_power(self): 623 """The transmit power in dBm. Can be set to a value from -2 to 20 for high power devices 624 (RFM69HCW, high_power=True) or -18 to 13 for low power devices. Only integer power 625 levels are actually set (i.e. 12.5 will result in a value of 12 dBm). 626 """ 627 # Follow table 10 truth table from the datasheet for determining power 628 # level from the individual PA level bits and output power register. 629 pa0 = self.pa_0_on 630 pa1 = self.pa_1_on 631 pa2 = self.pa_2_on 632 if pa0 and not pa1 and not pa2: 633 # -18 to 13 dBm range 634 return -18 + self.output_power 635 if not pa0 and pa1 and not pa2: 636 # -2 to 13 dBm range 637 return -18 + self.output_power 638 if not pa0 and pa1 and pa2 and not self.high_power: 639 # 2 to 17 dBm range 640 return -14 + self.output_power 641 if not pa0 and pa1 and pa2 and self.high_power: 642 # 5 to 20 dBm range 643 return -11 + self.output_power 644 raise RuntimeError("Power amplifiers in unknown state!") 645 646 @tx_power.setter 647 def tx_power(self, val): 648 val = int(val) 649 # Determine power amplifier and output power values depending on 650 # high power state and requested power. 651 pa_0_on = 0 652 pa_1_on = 0 653 pa_2_on = 0 654 output_power = 0 655 if self.high_power: 656 # Handle high power mode. 657 assert -2 <= val <= 20 658 if val <= 13: 659 pa_1_on = 1 660 output_power = val + 18 661 elif 13 < val <= 17: 662 pa_1_on = 1 663 pa_2_on = 1 664 output_power = val + 14 665 else: # power >= 18 dBm 666 # Note this also needs PA boost enabled separately! 667 pa_1_on = 1 668 pa_2_on = 1 669 output_power = val + 11 670 else: 671 # Handle non-high power mode. 672 assert -18 <= val <= 13 673 # Enable only power amplifier 0 and set output power. 674 pa_0_on = 1 675 output_power = val + 18 676 # Set power amplifiers and output power as computed above. 677 self.pa_0_on = pa_0_on 678 self.pa_1_on = pa_1_on 679 self.pa_2_on = pa_2_on 680 self.output_power = output_power 681 self._tx_power = val 682 683 @property 684 def rssi(self): 685 """The received strength indicator (in dBm). 686 May be inaccuate if not read immediatey. last_rssi contains the value read immediately 687 receipt of the last packet. 688 """ 689 # Read RSSI register and convert to value using formula in datasheet. 690 return -self._read_u8(_REG_RSSI_VALUE) / 2.0 691 692 @property 693 def bitrate(self): 694 """The modulation bitrate in bits/second (or chip rate if Manchester encoding is enabled). 695 Can be a value from ~489 to 32mbit/s, but see the datasheet for the exact supported 696 values. 697 """ 698 msb = self._read_u8(_REG_BITRATE_MSB) 699 lsb = self._read_u8(_REG_BITRATE_LSB) 700 return _FXOSC / ((msb << 8) | lsb) 701 702 @bitrate.setter 703 def bitrate(self, val): 704 assert (_FXOSC / 65535) <= val <= 32000000.0 705 # Round up to the next closest bit-rate value with addition of 0.5. 706 bitrate = int((_FXOSC / val) + 0.5) & 0xFFFF 707 self._write_u8(_REG_BITRATE_MSB, bitrate >> 8) 708 self._write_u8(_REG_BITRATE_LSB, bitrate & 0xFF) 709 710 @property 711 def frequency_deviation(self): 712 """The frequency deviation in Hertz.""" 713 msb = self._read_u8(_REG_FDEV_MSB) 714 lsb = self._read_u8(_REG_FDEV_LSB) 715 return _FSTEP * ((msb << 8) | lsb) 716 717 @frequency_deviation.setter 718 def frequency_deviation(self, val): 719 assert 0 <= val <= (_FSTEP * 16383) # fdev is a 14-bit unsigned value 720 # Round up to the next closest integer value with addition of 0.5. 721 fdev = int((val / _FSTEP) + 0.5) & 0x3FFF 722 self._write_u8(_REG_FDEV_MSB, fdev >> 8) 723 self._write_u8(_REG_FDEV_LSB, fdev & 0xFF) 724 725 def send( 726 self, 727 data, 728 *, 729 keep_listening=False, 730 destination=None, 731 node=None, 732 identifier=None, 733 flags=None 734 ): 735 """Send a string of data using the transmitter. 736 You can only send 60 bytes at a time 737 (limited by chip's FIFO size and appended headers). 738 This appends a 4 byte header to be compatible with the RadioHead library. 739 The header defaults to using the initialized attributes: 740 (destination,node,identifier,flags) 741 It may be temporarily overidden via the kwargs - destination,node,identifier,flags. 742 Values passed via kwargs do not alter the attribute settings. 743 The keep_listening argument should be set to True if you want to start listening 744 automatically after the packet is sent. The default setting is False. 745 746 Returns: True if success or False if the send timed out. 747 """ 748 # Disable pylint warning to not use length as a check for zero. 749 # This is a puzzling warning as the below code is clearly the most 750 # efficient and proper way to ensure a precondition that the provided 751 # buffer be within an expected range of bounds. Disable this check. 752 # pylint: disable=len-as-condition 753 assert 0 < len(data) <= 60 754 # pylint: enable=len-as-condition 755 self.idle() # Stop receiving to clear FIFO and keep it clear. 756 # Fill the FIFO with a packet to send. 757 # Combine header and data to form payload 758 payload = bytearray(4) 759 if destination is None: # use attribute 760 payload[0] = self.destination 761 else: # use kwarg 762 payload[0] = destination 763 if node is None: # use attribute 764 payload[1] = self.node 765 else: # use kwarg 766 payload[1] = node 767 if identifier is None: # use attribute 768 payload[2] = self.identifier 769 else: # use kwarg 770 payload[2] = identifier 771 if flags is None: # use attribute 772 payload[3] = self.flags 773 else: # use kwarg 774 payload[3] = flags 775 payload = payload + data 776 # Write payload to transmit fifo 777 self._write_fifo_from(payload) 778 # Turn on transmit mode to send out the packet. 779 self.transmit() 780 # Wait for packet sent interrupt with explicit polling (not ideal but 781 # best that can be done right now without interrupts). 782 start = time.monotonic() 783 timed_out = False 784 while not timed_out and not self.packet_sent: 785 if (time.monotonic() - start) >= self.xmit_timeout: 786 timed_out = True 787 # Listen again if requested. 788 if keep_listening: 789 self.listen() 790 else: # Enter idle mode to stop receiving other packets. 791 self.idle() 792 return not timed_out 793 794 def send_with_ack(self, data): 795 """Reliable Datagram mode: 796 Send a packet with data and wait for an ACK response. 797 The packet header is automatically generated. 798 If enabled, the packet transmission will be retried on failure 799 """ 800 if self.ack_retries: 801 retries_remaining = self.ack_retries 802 else: 803 retries_remaining = 1 804 got_ack = False 805 self.sequence_number = (self.sequence_number + 1) & 0xFF 806 while not got_ack and retries_remaining: 807 self.identifier = self.sequence_number 808 self.send(data, keep_listening=True) 809 # Don't look for ACK from Broadcast message 810 if self.destination == _RH_BROADCAST_ADDRESS: 811 got_ack = True 812 else: 813 # wait for a packet from our destination 814 ack_packet = self.receive(timeout=self.ack_wait, with_header=True) 815 if ack_packet is not None: 816 if ack_packet[3] & _RH_FLAGS_ACK: 817 # check the ID 818 if ack_packet[2] == self.identifier: 819 got_ack = True 820 break 821 # pause before next retry -- random delay 822 if not got_ack: 823 # delay by random amount before next try 824 time.sleep(self.ack_wait + self.ack_wait * random.random()) 825 retries_remaining = retries_remaining - 1 826 # set retry flag in packet header 827 self.flags |= _RH_FLAGS_RETRY 828 self.flags = 0 # clear flags 829 return got_ack 830 831 # pylint: disable=too-many-branches 832 def receive( 833 self, *, keep_listening=True, with_ack=False, timeout=None, with_header=False 834 ): 835 """Wait to receive a packet from the receiver. If a packet is found the payload bytes 836 are returned, otherwise None is returned (which indicates the timeout elapsed with no 837 reception). 838 If keep_listening is True (the default) the chip will immediately enter listening mode 839 after reception of a packet, otherwise it will fall back to idle mode and ignore any 840 future reception. 841 All packets must have a 4 byte header for compatibilty with the 842 RadioHead library. 843 The header consists of 4 bytes (To,From,ID,Flags). The default setting will strip 844 the header before returning the packet to the caller. 845 If with_header is True then the 4 byte header will be returned with the packet. 846 The payload then begins at packet[4]. 847 If with_ack is True, send an ACK after receipt (Reliable Datagram mode) 848 """ 849 timed_out = False 850 if timeout is None: 851 timeout = self.receive_timeout 852 if timeout is not None: 853 # Wait for the payload_ready signal. This is not ideal and will 854 # surely miss or overflow the FIFO when packets aren't read fast 855 # enough, however it's the best that can be done from Python without 856 # interrupt supports. 857 # Make sure we are listening for packets. 858 self.listen() 859 start = time.monotonic() 860 timed_out = False 861 while not timed_out and not self.payload_ready: 862 if (time.monotonic() - start) >= timeout: 863 timed_out = True 864 # Payload ready is set, a packet is in the FIFO. 865 packet = None 866 # save last RSSI reading 867 self.last_rssi = self.rssi 868 # Enter idle mode to stop receiving other packets. 869 self.idle() 870 if not timed_out: 871 # Read the length of the FIFO. 872 fifo_length = self._read_u8(_REG_FIFO) 873 # Handle if the received packet is too small to include the 4 byte 874 # RadioHead header and at least one byte of data --reject this packet and ignore it. 875 if fifo_length > 0: # read and clear the FIFO if anything in it 876 packet = bytearray(fifo_length) 877 self._read_into(_REG_FIFO, packet) 878 if fifo_length < 5: 879 packet = None 880 else: 881 if ( 882 self.node != _RH_BROADCAST_ADDRESS 883 and packet[0] != _RH_BROADCAST_ADDRESS 884 and packet[0] != self.node 885 ): 886 packet = None 887 # send ACK unless this was an ACK or a broadcast 888 elif ( 889 with_ack 890 and ((packet[3] & _RH_FLAGS_ACK) == 0) 891 and (packet[0] != _RH_BROADCAST_ADDRESS) 892 ): 893 # delay before sending Ack to give receiver a chance to get ready 894 if self.ack_delay is not None: 895 time.sleep(self.ack_delay) 896 # send ACK packet to sender 897 data = bytes("!", "UTF-8") 898 self.send( 899 data, 900 destination=packet[1], 901 node=packet[0], 902 identifier=packet[2], 903 flags=(packet[3] | _RH_FLAGS_ACK), 904 ) 905 # reject Retries if we have seen this idetifier from this source before 906 if (self.seen_ids[packet[1]] == packet[2]) and ( 907 packet[3] & _RH_FLAGS_RETRY 908 ): 909 packet = None 910 else: # save the packet identifier for this source 911 self.seen_ids[packet[1]] = packet[2] 912 if ( 913 not with_header and packet is not None 914 ): # skip the header if not wanted 915 packet = packet[4:] 916 # Listen again if necessary and return the result packet. 917 if keep_listening: 918 self.listen() 919 else: 920 # Enter idle mode to stop receiving other packets. 921 self.idle() 922 return packet