├── LICENSE ├── README.md ├── micropython_rfm69.py └── rfm69_simpletest.py /LICENSE: -------------------------------------------------------------------------------- 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | Work in progress 3 | ============ 4 | This libary is a work in progress with limited testing. 5 | 6 | Introduction 7 | ============ 8 | 9 | This is a port of the Adafruit_CircuitPython_RFM69 library of which this repo is forked from and found here: 10 | https://github.com/adafruit/Adafruit_CircuitPython_RFM69 11 | 12 | I would like to thank jerryn/jerryneedell for his assistance in getting me to this point.
13 | https://forums.adafruit.com/viewtopic.php?f=60&t=168167&start=15 14 | 15 | MicroPython RFM69 packet radio module. This supports basic RadioHead-compatible sending and 16 | receiving of packets with RFM69 series radios (433/915Mhz). 17 | 18 | .. warning:: This is NOT for LoRa radios! 19 | 20 | .. note:: This is a 'best effort' at receiving data using pure Python code--there is not interrupt 21 | support so you might lose packets if they're sent too quickly for the board to process them. 22 | You will have the most luck using this in simple low bandwidth scenarios like sending and 23 | receiving a 60 byte packet at a time--don't try to receive many kilobytes of data at a time! 24 | 25 | 26 | This has been tested on various ESP32 and ESP8266 dev boards. For detailed information on experiments with these devices, please visit the repository below: 27 | 28 | https://github.com/nohcpy/LoPPyIOT 29 | 30 | 31 | Usage Example 32 | ============= 33 | See rfm69_simpletest.py for a simple demo of the usage. 34 | 35 | .. code-block:: python 36 | 37 | # Initialze RFM radio 38 | rfm69 = RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) 39 | -------------------------------------------------------------------------------- /micropython_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 | import time 24 | import random 25 | 26 | from micropython import const 27 | 28 | # Internal constants: 29 | _REG_FIFO = const(0x00) 30 | _REG_OP_MODE = const(0x01) 31 | _REG_DATA_MOD = const(0x02) 32 | _REG_BITRATE_MSB = const(0x03) 33 | _REG_BITRATE_LSB = const(0x04) 34 | _REG_FDEV_MSB = const(0x05) 35 | _REG_FDEV_LSB = const(0x06) 36 | _REG_FRF_MSB = const(0x07) 37 | _REG_FRF_MID = const(0x08) 38 | _REG_FRF_LSB = const(0x09) 39 | _REG_VERSION = const(0x10) 40 | _REG_PA_LEVEL = const(0x11) 41 | _REG_RX_BW = const(0x19) 42 | _REG_AFC_BW = const(0x1A) 43 | _REG_RSSI_VALUE = const(0x24) 44 | _REG_DIO_MAPPING1 = const(0x25) 45 | _REG_IRQ_FLAGS1 = const(0x27) 46 | _REG_IRQ_FLAGS2 = const(0x28) 47 | _REG_PREAMBLE_MSB = const(0x2C) 48 | _REG_PREAMBLE_LSB = const(0x2D) 49 | _REG_SYNC_CONFIG = const(0x2E) 50 | _REG_SYNC_VALUE1 = const(0x2F) 51 | _REG_PACKET_CONFIG1 = const(0x37) 52 | _REG_FIFO_THRESH = const(0x3C) 53 | _REG_PACKET_CONFIG2 = const(0x3D) 54 | _REG_AES_KEY1 = const(0x3E) 55 | _REG_TEMP1 = const(0x4E) 56 | _REG_TEMP2 = const(0x4F) 57 | _REG_TEST_PA1 = const(0x5A) 58 | _REG_TEST_PA2 = const(0x5C) 59 | _REG_TEST_DAGC = const(0x6F) 60 | 61 | _TEST_PA1_NORMAL = const(0x55) 62 | _TEST_PA1_BOOST = const(0x5D) 63 | _TEST_PA2_NORMAL = const(0x70) 64 | _TEST_PA2_BOOST = const(0x7C) 65 | 66 | # The crystal oscillator frequency and frequency synthesizer step size. 67 | # See the datasheet for details of this calculation. 68 | _FXOSC = 32000000.0 69 | _FSTEP = _FXOSC / 524288 70 | 71 | # RadioHead specific compatibility constants. 72 | _RH_BROADCAST_ADDRESS = const(0xFF) 73 | # The acknowledgement bit in the FLAGS 74 | # The top 4 bits of the flags are reserved for RadioHead. The lower 4 bits are reserved 75 | # for application layer use. 76 | _RH_FLAGS_ACK = const(0x80) 77 | _RH_FLAGS_RETRY = const(0x40) 78 | 79 | # User facing constants: 80 | SLEEP_MODE = 0b000 81 | STANDBY_MODE = 0b001 82 | FS_MODE = 0b010 83 | TX_MODE = 0b011 84 | RX_MODE = 0b100 85 | 86 | 87 | class RFM69: 88 | 89 | 90 | class _RegisterBits: 91 | def __init__(self, address, *, offset=0, bits=1): 92 | assert 0 <= offset <= 7 93 | assert 1 <= bits <= 8 94 | assert (offset + bits) <= 8 95 | self._address = address 96 | self._mask = 0 97 | for _ in range(bits): 98 | self._mask <<= 1 99 | self._mask |= 1 100 | self._mask <<= offset 101 | self._offset = offset 102 | 103 | def __get__(self, obj, objtype): 104 | reg_value = obj._read_u8(self._address) 105 | return (reg_value & self._mask) >> self._offset 106 | 107 | def __set__(self, obj, val): 108 | reg_value = obj._read_u8(self._address) 109 | reg_value &= ~self._mask 110 | reg_value |= (val & 0xFF) << self._offset 111 | obj._write_u8(self._address, reg_value) 112 | 113 | # Control bits from the registers of the chip: 114 | data_mode = _RegisterBits(_REG_DATA_MOD, offset=5, bits=2) 115 | modulation_type = _RegisterBits(_REG_DATA_MOD, offset=3, bits=2) 116 | modulation_shaping = _RegisterBits(_REG_DATA_MOD, offset=0, bits=2) 117 | temp_start = _RegisterBits(_REG_TEMP1, offset=3) 118 | temp_running = _RegisterBits(_REG_TEMP1, offset=2) 119 | sync_on = _RegisterBits(_REG_SYNC_CONFIG, offset=7) 120 | sync_size = _RegisterBits(_REG_SYNC_CONFIG, offset=3, bits=3) 121 | aes_on = _RegisterBits(_REG_PACKET_CONFIG2, offset=0) 122 | pa_0_on = _RegisterBits(_REG_PA_LEVEL, offset=7) 123 | pa_1_on = _RegisterBits(_REG_PA_LEVEL, offset=6) 124 | pa_2_on = _RegisterBits(_REG_PA_LEVEL, offset=5) 125 | output_power = _RegisterBits(_REG_PA_LEVEL, offset=0, bits=5) 126 | rx_bw_dcc_freq = _RegisterBits(_REG_RX_BW, offset=5, bits=3) 127 | rx_bw_mantissa = _RegisterBits(_REG_RX_BW, offset=3, bits=2) 128 | rx_bw_exponent = _RegisterBits(_REG_RX_BW, offset=0, bits=3) 129 | afc_bw_dcc_freq = _RegisterBits(_REG_AFC_BW, offset=5, bits=3) 130 | afc_bw_mantissa = _RegisterBits(_REG_AFC_BW, offset=3, bits=2) 131 | afc_bw_exponent = _RegisterBits(_REG_AFC_BW, offset=0, bits=3) 132 | packet_format = _RegisterBits(_REG_PACKET_CONFIG1, offset=7, bits=1) 133 | dc_free = _RegisterBits(_REG_PACKET_CONFIG1, offset=5, bits=2) 134 | crc_on = _RegisterBits(_REG_PACKET_CONFIG1, offset=4, bits=1) 135 | crc_auto_clear_off = _RegisterBits(_REG_PACKET_CONFIG1, offset=3, bits=1) 136 | address_filter = _RegisterBits(_REG_PACKET_CONFIG1, offset=1, bits=2) 137 | mode_ready = _RegisterBits(_REG_IRQ_FLAGS1, offset=7) 138 | rx_ready = _RegisterBits(_REG_IRQ_FLAGS1, offset=6) 139 | tx_ready = _RegisterBits(_REG_IRQ_FLAGS1, offset=5) 140 | dio_0_mapping = _RegisterBits(_REG_DIO_MAPPING1, offset=6, bits=2) 141 | packet_sent = _RegisterBits(_REG_IRQ_FLAGS2, offset=3) 142 | payload_ready = _RegisterBits(_REG_IRQ_FLAGS2, offset=2) 143 | 144 | def __init__( 145 | self, 146 | spi, 147 | cs, 148 | reset, 149 | frequency, 150 | *, 151 | sync_word=b"\x2D\xD4", 152 | preamble_length=4, 153 | encryption_key=None, 154 | high_power=True, 155 | ): 156 | self._tx_power = 13 157 | self.high_power = high_power 158 | self.spi = spi 159 | self.cs = cs 160 | self._reset = reset 161 | self.reset() # Reset the chip. 162 | # Check the version of the chip. 163 | version = self._read_u8(_REG_VERSION) 164 | if version != 0x24: 165 | raise RuntimeError( 166 | "Failed to find RFM69 with expected version, check wiring!" 167 | ) 168 | self.idle() # Enter idle state. 169 | # Setup the chip in a similar way to the RadioHead RFM69 library. 170 | # Set FIFO TX condition to not empty and the default FIFO threshold to 15. 171 | self._write_u8(_REG_FIFO_THRESH, 0b10001111) 172 | # Configure low beta off. 173 | self._write_u8(_REG_TEST_DAGC, 0x30) 174 | # Disable boost. 175 | self._write_u8(_REG_TEST_PA1, _TEST_PA1_NORMAL) 176 | self._write_u8(_REG_TEST_PA2, _TEST_PA2_NORMAL) 177 | # Set the syncronization word. 178 | self.sync_word = sync_word 179 | self.preamble_length = preamble_length # Set the preamble length. 180 | self.frequency_mhz = frequency # Set frequency. 181 | self.encryption_key = encryption_key # Set encryption key. 182 | # set radio configuration parameters 183 | self._configure_radio() 184 | # initialize last RSSI reading 185 | self.last_rssi = 0.0 186 | """The RSSI of the last received packet. Stored when the packet was received. 187 | This instantaneous RSSI value may not be accurate once the 188 | operating mode has been changed. 189 | """ 190 | # initialize timeouts and delays delays 191 | self.ack_wait = 500 192 | """The delay time before attempting a retry after not receiving an ACK""" 193 | self.receive_timeout = 500 194 | """The amount of time to poll for a received packet. 195 | If no packet is received, the returned packet will be None 196 | """ 197 | self.xmit_timeout = 2000 198 | """The amount of time to wait for the HW to transmit the packet. 199 | This is mainly used to prevent a hang due to a HW issue 200 | """ 201 | self.ack_retries = 5 202 | """The number of ACK retries before reporting a failure.""" 203 | self.ack_delay = None 204 | """The delay time before attemting to send an ACK. 205 | If ACKs are being missed try setting this to .1 or .2. 206 | """ 207 | # initialize sequence number counter for reliabe datagram mode 208 | self.sequence_number = 0 209 | # create seen Ids list 210 | self.seen_ids = bytearray(256) 211 | # initialize packet header 212 | # node address - default is broadcast 213 | self.node = _RH_BROADCAST_ADDRESS 214 | """The default address of this Node. (0-255). 215 | If not 255 (0xff) then only packets address to this node will be accepted. 216 | First byte of the RadioHead header. 217 | """ 218 | # destination address - default is broadcast 219 | self.destination = _RH_BROADCAST_ADDRESS 220 | """The default destination address for packet transmissions. (0-255). 221 | If 255 (0xff) then any receiving node should accept the packet. 222 | Second byte of the RadioHead header. 223 | """ 224 | # ID - contains seq count for reliable datagram mode 225 | self.identifier = 0 226 | """Automatically set to the sequence number when send_with_ack() used. 227 | Third byte of the RadioHead header. 228 | """ 229 | # flags - identifies ack/reetry packet for reliable datagram mode 230 | self.flags = 0 231 | """Upper 4 bits reserved for use by Reliable Datagram Mode. 232 | Lower 4 bits may be used to pass information. 233 | Fourth byte of the RadioHead header. 234 | """ 235 | 236 | def _configure_radio(self): 237 | # Configure modulation for RadioHead library GFSK_Rb250Fd250 mode 238 | # by default. Users with advanced knowledge can manually reconfigure 239 | # for any other mode (consulting the datasheet is absolutely 240 | # necessary!). 241 | self.data_mode = 0b00 # Packet mode 242 | self.modulation_type = 0b00 # FSK modulation 243 | self.modulation_shaping = 0b01 # Gaussian filter, BT=1.0 244 | self.bitrate = 250000 # 250kbs 245 | self.frequency_deviation = 250000 # 250khz 246 | self.rx_bw_dcc_freq = 0b111 # RxBw register = 0xE0 247 | self.rx_bw_mantissa = 0b00 248 | self.rx_bw_exponent = 0b000 249 | self.afc_bw_dcc_freq = 0b111 # AfcBw register = 0xE0 250 | self.afc_bw_mantissa = 0b00 251 | self.afc_bw_exponent = 0b000 252 | self.packet_format = 1 # Variable length. 253 | self.dc_free = 0b10 # Whitening 254 | self.crc_on = 1 # CRC enabled 255 | self.crc_auto_clear = 0 # Clear FIFO on CRC fail 256 | self.address_filtering = 0b00 # No address filtering 257 | # Set transmit power to 13 dBm, a safe value any module supports. 258 | self.tx_power = 13 259 | 260 | # pylint: disable=no-member 261 | # Reconsider this disable when it can be tested. 262 | # read_into(_REG_FIFO, packet) 263 | def _read_into(self, address, buf): 264 | # Read from the specified address into the provided 265 | # buffer. 266 | self.cs.value(0) 267 | self.spi.write(bytes([address & 0x7F])) 268 | self.spi.readinto(buf) 269 | self.cs.value(1) 270 | return buf 271 | 272 | def _read_u8(self, address): 273 | # Read a single byte from the provided address and return it. 274 | self.cs.value(0) 275 | self.spi.write(bytes([address & 0x7F])) 276 | value = self.spi.read(1) 277 | self.cs.value(1) 278 | return value[0] 279 | 280 | def _write_from(self, address, buf): 281 | # Write to the provided address and taken from the 282 | # provided buffer. 283 | self.cs.value(0) 284 | self.spi.write(bytes([(address | 0x80) & 0xFF])) 285 | self.spi.write(buf) # send data 286 | self.cs.value(1) 287 | 288 | 289 | def _write_fifo_from(self, buf): 290 | # Write to the transmit FIFO and taken from the 291 | # provided buffer. 292 | length = len(buf) 293 | buf1 = (_REG_FIFO | 0x80) & 0xFF # Set top bit to 1 to 294 | # indicate a write. 295 | buf2 = length & 0xFF # Set packt length 296 | self.cs.value(0) 297 | self.spi.write(bytes([buf1,buf2])) # send address and lenght) 298 | self.spi.write(buf) # send data 299 | self.cs.value(1) 300 | 301 | def _write_u8(self, address, val): 302 | # Write a byte register to the chip. Specify the 7-bit address and the 303 | # 8-bit value to write to that address. 304 | address = (address | 0x80) & 0xFF # Set top bit to 1 to 305 | # indicate a write. 306 | val = val & 0xFF 307 | self.cs.value(0) 308 | self.spi.write(bytes([address,val])) 309 | self.cs.value(1) 310 | 311 | def reset(self): 312 | """Perform a reset of the chip.""" 313 | # See section 7.2.2 of the datasheet for reset description. 314 | self._reset.value(1) 315 | time.sleep_us(100) # 100 us 316 | self._reset.value(0) 317 | time.sleep_us(5000) # 5 ms 318 | 319 | def idle(self): 320 | """Enter idle standby mode (switching off high power amplifiers if necessary).""" 321 | # Like RadioHead library, turn off high power boost if enabled. 322 | if self._tx_power >= 18: 323 | self._write_u8(_REG_TEST_PA1, _TEST_PA1_NORMAL) 324 | self._write_u8(_REG_TEST_PA2, _TEST_PA2_NORMAL) 325 | self.operation_mode = STANDBY_MODE 326 | 327 | def sleep(self): 328 | """Enter sleep mode.""" 329 | self.operation_mode = SLEEP_MODE 330 | 331 | def listen(self): 332 | """Listen for packets to be received by the chip. Use :py:func:`receive` to listen, wait 333 | and retrieve packets as they're available. 334 | """ 335 | # Like RadioHead library, turn off high power boost if enabled. 336 | if self._tx_power >= 18: 337 | self._write_u8(_REG_TEST_PA1, _TEST_PA1_NORMAL) 338 | self._write_u8(_REG_TEST_PA2, _TEST_PA2_NORMAL) 339 | # Enable payload ready interrupt for D0 line. 340 | self.dio_0_mapping = 0b01 341 | # Enter RX mode (will clear FIFO!). 342 | self.operation_mode = RX_MODE 343 | 344 | def transmit(self): 345 | """Transmit a packet which is queued in the FIFO. This is a low level function for 346 | entering transmit mode and more. For generating and transmitting a packet of data use 347 | :py:func:`send` instead. 348 | """ 349 | # Like RadioHead library, turn on high power boost if enabled. 350 | if self._tx_power >= 18: 351 | self._write_u8(_REG_TEST_PA1, _TEST_PA1_BOOST) 352 | self._write_u8(_REG_TEST_PA2, _TEST_PA2_BOOST) 353 | # Enable packet sent interrupt for D0 line. 354 | self.dio_0_mapping = 0b00 355 | # Enter TX mode (will clear FIFO!). 356 | self.operation_mode = TX_MODE 357 | 358 | @property 359 | def temperature(self): 360 | """The internal temperature of the chip in degrees Celsius. Be warned this is not 361 | calibrated or very accurate. 362 | 363 | .. warning:: Reading this will STOP any receiving/sending that might be happening! 364 | """ 365 | # Start a measurement then poll the measurement finished bit. 366 | self.temp_start = 1 367 | while self.temp_running > 0: 368 | pass 369 | # Grab the temperature value and convert it to Celsius. 370 | # This uses the same observed value formula from the Radiohead library. 371 | temp = self._read_u8(_REG_TEMP2) 372 | return 166.0 - temp 373 | 374 | @property 375 | def operation_mode(self): 376 | """The operation mode value. Unless you're manually controlling the chip you shouldn't 377 | change the operation_mode with this property as other side-effects are required for 378 | changing logical modes--use :py:func:`idle`, :py:func:`sleep`, :py:func:`transmit`, 379 | :py:func:`listen` instead to signal intent for explicit logical modes. 380 | """ 381 | op_mode = self._read_u8(_REG_OP_MODE) 382 | return (op_mode >> 2) & 0b111 383 | 384 | @operation_mode.setter 385 | def operation_mode(self, val): 386 | assert 0 <= val <= 4 387 | # Set the mode bits inside the operation mode register. 388 | op_mode = self._read_u8(_REG_OP_MODE) 389 | op_mode &= 0b11100011 390 | op_mode |= val << 2 391 | self._write_u8(_REG_OP_MODE, op_mode) 392 | # Wait for mode to change by polling interrupt bit. 393 | while not self.mode_ready: 394 | pass 395 | 396 | @property 397 | def sync_word(self): 398 | """The synchronization word value. This is a byte string up to 8 bytes long (64 bits) 399 | which indicates the synchronization word for transmitted and received packets. Any 400 | received packet which does not include this sync word will be ignored. The default value 401 | is 0x2D, 0xD4 which matches the RadioHead RFM69 library. Setting a value of None will 402 | disable synchronization word matching entirely. 403 | """ 404 | # Handle when sync word is disabled.. 405 | if not self.sync_on: 406 | return None 407 | # Sync word is not disabled so read the current value. 408 | sync_word_length = self.sync_size + 1 # Sync word size is offset by 1 409 | # according to datasheet. 410 | sync_word = bytearray(sync_word_length) 411 | self._read_into(_REG_SYNC_VALUE1, sync_word) 412 | return sync_word 413 | 414 | @sync_word.setter 415 | def sync_word(self, val): 416 | # Handle disabling sync word when None value is set. 417 | if val is None: 418 | self.sync_on = 0 419 | else: 420 | # Check sync word is at most 8 bytes. 421 | assert 1 <= len(val) <= 8 422 | # Update the value, size and turn on the sync word. 423 | self._write_from(_REG_SYNC_VALUE1, val) 424 | self.sync_size = len(val) - 1 # Again sync word size is offset by 425 | # 1 according to datasheet. 426 | self.sync_on = 1 427 | 428 | @property 429 | def preamble_length(self): 430 | """The length of the preamble for sent and received packets, an unsigned 16-bit value. 431 | Received packets must match this length or they are ignored! Set to 4 to match the 432 | RadioHead RFM69 library. 433 | """ 434 | msb = self._read_u8(_REG_PREAMBLE_MSB) 435 | lsb = self._read_u8(_REG_PREAMBLE_LSB) 436 | return ((msb << 8) | lsb) & 0xFFFF 437 | 438 | @preamble_length.setter 439 | def preamble_length(self, val): 440 | assert 0 <= val <= 65535 441 | self._write_u8(_REG_PREAMBLE_MSB, (val >> 8) & 0xFF) 442 | self._write_u8(_REG_PREAMBLE_LSB, val & 0xFF) 443 | 444 | @property 445 | def frequency_mhz(self): 446 | """The frequency of the radio in Megahertz. Only the allowed values for your radio must be 447 | specified (i.e. 433 vs. 915 mhz)! 448 | """ 449 | # FRF register is computed from the frequency following the datasheet. 450 | # See section 6.2 and FRF register description. 451 | # Read bytes of FRF register and assemble into a 24-bit unsigned value. 452 | msb = self._read_u8(_REG_FRF_MSB) 453 | mid = self._read_u8(_REG_FRF_MID) 454 | lsb = self._read_u8(_REG_FRF_LSB) 455 | frf = ((msb << 16) | (mid << 8) | lsb) & 0xFFFFFF 456 | frequency = (frf * _FSTEP) / 1000000.0 457 | return frequency 458 | 459 | @frequency_mhz.setter 460 | def frequency_mhz(self, val): 461 | assert 290 <= val <= 1020 462 | # Calculate FRF register 24-bit value using section 6.2 of the datasheet. 463 | frf = int((val * 1000000.0) / _FSTEP) & 0xFFFFFF 464 | # Extract byte values and update registers. 465 | msb = frf >> 16 466 | mid = (frf >> 8) & 0xFF 467 | lsb = frf & 0xFF 468 | self._write_u8(_REG_FRF_MSB, msb) 469 | self._write_u8(_REG_FRF_MID, mid) 470 | self._write_u8(_REG_FRF_LSB, lsb) 471 | 472 | @property 473 | def encryption_key(self): 474 | """The AES encryption key used to encrypt and decrypt packets by the chip. This can be set 475 | to None to disable encryption (the default), otherwise it must be a 16 byte long byte 476 | string which defines the key (both the transmitter and receiver must use the same key 477 | value). 478 | """ 479 | # Handle if encryption is disabled. 480 | if self.aes_on == 0: 481 | return None 482 | # Encryption is enabled so read the key and return it. 483 | key = bytearray(16) 484 | self._read_into(_REG_AES_KEY1, key) 485 | return key 486 | 487 | @encryption_key.setter 488 | def encryption_key(self, val): 489 | # Handle if unsetting the encryption key (None value). 490 | if val is None: 491 | self.aes_on = 0 492 | else: 493 | # Set the encryption key and enable encryption. 494 | assert len(val) == 16 495 | self._write_from(_REG_AES_KEY1, val) 496 | self.aes_on = 1 497 | 498 | @property 499 | def tx_power(self): 500 | """The transmit power in dBm. Can be set to a value from -2 to 20 for high power devices 501 | (RFM69HCW, high_power=True) or -18 to 13 for low power devices. Only integer power 502 | levels are actually set (i.e. 12.5 will result in a value of 12 dBm). 503 | """ 504 | # Follow table 10 truth table from the datasheet for determining power 505 | # level from the individual PA level bits and output power register. 506 | pa0 = self.pa_0_on 507 | pa1 = self.pa_1_on 508 | pa2 = self.pa_2_on 509 | if pa0 and not pa1 and not pa2: 510 | # -18 to 13 dBm range 511 | return -18 + self.output_power 512 | if not pa0 and pa1 and not pa2: 513 | # -2 to 13 dBm range 514 | return -18 + self.output_power 515 | if not pa0 and pa1 and pa2 and not self.high_power: 516 | # 2 to 17 dBm range 517 | return -14 + self.output_power 518 | if not pa0 and pa1 and pa2 and self.high_power: 519 | # 5 to 20 dBm range 520 | return -11 + self.output_power 521 | raise RuntimeError("Power amplifiers in unknown state!") 522 | 523 | @tx_power.setter 524 | def tx_power(self, val): 525 | val = int(val) 526 | # Determine power amplifier and output power values depending on 527 | # high power state and requested power. 528 | pa_0_on = 0 529 | pa_1_on = 0 530 | pa_2_on = 0 531 | output_power = 0 532 | if self.high_power: 533 | # Handle high power mode. 534 | assert -2 <= val <= 20 535 | if val <= 13: 536 | pa_1_on = 1 537 | output_power = val + 18 538 | elif 13 < val <= 17: 539 | pa_1_on = 1 540 | pa_2_on = 1 541 | output_power = val + 14 542 | else: # power >= 18 dBm 543 | # Note this also needs PA boost enabled separately! 544 | pa_1_on = 1 545 | pa_2_on = 1 546 | output_power = val + 11 547 | else: 548 | # Handle non-high power mode. 549 | assert -18 <= val <= 13 550 | # Enable only power amplifier 0 and set output power. 551 | pa_0_on = 1 552 | output_power = val + 18 553 | # Set power amplifiers and output power as computed above. 554 | self.pa_0_on = pa_0_on 555 | self.pa_1_on = pa_1_on 556 | self.pa_2_on = pa_2_on 557 | self.output_power = output_power 558 | self._tx_power = val 559 | 560 | @property 561 | def rssi(self): 562 | """The received strength indicator (in dBm). 563 | May be inaccuate if not read immediatey. last_rssi contains the value read immediately 564 | receipt of the last packet. 565 | """ 566 | # Read RSSI register and convert to value using formula in datasheet. 567 | return -self._read_u8(_REG_RSSI_VALUE) / 2.0 568 | 569 | @property 570 | def bitrate(self): 571 | """The modulation bitrate in bits/second (or chip rate if Manchester encoding is enabled). 572 | Can be a value from ~489 to 32mbit/s, but see the datasheet for the exact supported 573 | values. 574 | """ 575 | msb = self._read_u8(_REG_BITRATE_MSB) 576 | lsb = self._read_u8(_REG_BITRATE_LSB) 577 | return _FXOSC / ((msb << 8) | lsb) 578 | 579 | @bitrate.setter 580 | def bitrate(self, val): 581 | assert (_FXOSC / 65535) <= val <= 32000000.0 582 | # Round up to the next closest bit-rate value with addition of 0.5. 583 | bitrate = int((_FXOSC / val) + 0.5) & 0xFFFF 584 | self._write_u8(_REG_BITRATE_MSB, bitrate >> 8) 585 | self._write_u8(_REG_BITRATE_LSB, bitrate & 0xFF) 586 | 587 | @property 588 | def frequency_deviation(self): 589 | """The frequency deviation in Hertz.""" 590 | msb = self._read_u8(_REG_FDEV_MSB) 591 | lsb = self._read_u8(_REG_FDEV_LSB) 592 | return _FSTEP * ((msb << 8) | lsb) 593 | 594 | @frequency_deviation.setter 595 | def frequency_deviation(self, val): 596 | assert 0 <= val <= (_FSTEP * 16383) # fdev is a 14-bit unsigned value 597 | # Round up to the next closest integer value with addition of 0.5. 598 | fdev = int((val / _FSTEP) + 0.5) & 0x3FFF 599 | self._write_u8(_REG_FDEV_MSB, fdev >> 8) 600 | self._write_u8(_REG_FDEV_LSB, fdev & 0xFF) 601 | 602 | def send( 603 | self, 604 | data, 605 | *, 606 | keep_listening=False, 607 | destination=None, 608 | node=None, 609 | identifier=None, 610 | flags=None 611 | ): 612 | """Send a string of data using the transmitter. 613 | You can only send 60 bytes at a time 614 | (limited by chip's FIFO size and appended headers). 615 | This appends a 4 byte header to be compatible with the RadioHead library. 616 | The header defaults to using the initialized attributes: 617 | (destination,node,identifier,flags) 618 | It may be temporarily overidden via the kwargs - destination,node,identifier,flags. 619 | Values passed via kwargs do not alter the attribute settings. 620 | The keep_listening argument should be set to True if you want to start listening 621 | automatically after the packet is sent. The default setting is False. 622 | 623 | Returns: True if success or False if the send timed out. 624 | """ 625 | # Disable pylint warning to not use length as a check for zero. 626 | # This is a puzzling warning as the below code is clearly the most 627 | # efficient and proper way to ensure a precondition that the provided 628 | # buffer be within an expected range of bounds. Disable this check. 629 | # pylint: disable=len-as-condition 630 | assert 0 < len(data) <= 60 631 | # pylint: enable=len-as-condition 632 | self.idle() # Stop receiving to clear FIFO and keep it clear. 633 | # Fill the FIFO with a packet to send. 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 to transmit fifo 654 | self._write_fifo_from(payload) 655 | # Turn on transmit mode to send out the packet. 656 | self.transmit() 657 | # Wait for packet sent interrupt with explicit polling (not ideal but 658 | # best that can be done right now without interrupts). 659 | start = time.ticks_ms() 660 | timed_out = False 661 | while not timed_out and not self.packet_sent: 662 | if time.ticks_diff(time.ticks_ms(), start) >= self.xmit_timeout: 663 | timed_out = True 664 | # Listen again if requested. 665 | if keep_listening: 666 | self.listen() 667 | else: # Enter idle mode to stop receiving other packets. 668 | self.idle() 669 | return not timed_out 670 | 671 | def send_with_ack(self, data): 672 | """Reliable Datagram mode: 673 | Send a packet with data and wait for an ACK response. 674 | The packet header is automatically generated. 675 | If enabled, the packet transmission will be retried on failure 676 | """ 677 | if self.ack_retries: 678 | retries_remaining = self.ack_retries 679 | else: 680 | retries_remaining = 1 681 | got_ack = False 682 | self.sequence_number = (self.sequence_number + 1) & 0xFF 683 | while not got_ack and retries_remaining: 684 | self.identifier = self.sequence_number 685 | self.send(data, keep_listening=True) 686 | # Don't look for ACK from Broadcast message 687 | if self.destination == _RH_BROADCAST_ADDRESS: 688 | got_ack = True 689 | else: 690 | # wait for a packet from our destination 691 | ack_packet = self.receive(timeout=self.ack_wait, with_header=True) 692 | if ack_packet is not None: 693 | if ack_packet[3] & _RH_FLAGS_ACK: 694 | # check the ID 695 | if ack_packet[2] == self.identifier: 696 | got_ack = True 697 | break 698 | # pause before next retry -- random delay 699 | if not got_ack: 700 | # delay by random amount before next try 701 | time.sleep_ms(self.ack_wait + int(self.ack_wait * random.random())) 702 | retries_remaining = retries_remaining - 1 703 | # set retry flag in packet header 704 | self.flags |= _RH_FLAGS_RETRY 705 | self.flags = 0 # clear flags 706 | return got_ack 707 | 708 | # pylint: disable=too-many-branches 709 | def receive( 710 | self, *, keep_listening=True, with_ack=False, timeout=None, with_header=False 711 | ): 712 | """Wait to receive a packet from the receiver. If a packet is found the payload bytes 713 | are returned, otherwise None is returned (which indicates the timeout elapsed with no 714 | reception). 715 | If keep_listening is True (the default) the chip will immediately enter listening mode 716 | after reception of a packet, otherwise it will fall back to idle mode and ignore any 717 | future reception. 718 | All packets must have a 4 byte header for compatibilty with the 719 | RadioHead library. 720 | The header consists of 4 bytes (To,From,ID,Flags). The default setting will strip 721 | the header before returning the packet to the caller. 722 | If with_header is True then the 4 byte header will be returned with the packet. 723 | The payload then begins at packet[4]. 724 | If with_ack is True, send an ACK after receipt (Reliable Datagram mode) 725 | """ 726 | timed_out = False 727 | if timeout is None: 728 | timeout = self.receive_timeout 729 | if timeout is not None: 730 | # Wait for the payload_ready signal. This is not ideal and will 731 | # surely miss or overflow the FIFO when packets aren't read fast 732 | # enough, however it's the best that can be done from Python without 733 | # interrupt supports. 734 | # Make sure we are listening for packets. 735 | self.listen() 736 | start = time.ticks_ms() 737 | timed_out = False 738 | while not timed_out and not self.payload_ready: 739 | if time.ticks_diff(time.ticks_ms(), start) >= timeout: 740 | timed_out = True 741 | # Payload ready is set, a packet is in the FIFO. 742 | packet = None 743 | # save last RSSI reading 744 | self.last_rssi = self.rssi 745 | # Enter idle mode to stop receiving other packets. 746 | self.idle() 747 | if not timed_out: 748 | # Read the length of the FIFO. 749 | fifo_length = self._read_u8(_REG_FIFO) 750 | # Handle if the received packet is too small to include the 4 byte 751 | # RadioHead header and at least one byte of data --reject this packet and ignore it. 752 | if fifo_length > 0: # read and clear the FIFO if anything in it 753 | packet = bytearray(fifo_length) 754 | self._read_into(_REG_FIFO, packet) 755 | if fifo_length < 5: 756 | packet = None 757 | else: 758 | if ( 759 | self.node != _RH_BROADCAST_ADDRESS 760 | and packet[0] != _RH_BROADCAST_ADDRESS 761 | and packet[0] != self.node 762 | ): 763 | packet = None 764 | # send ACK unless this was an ACK or a broadcast 765 | elif ( 766 | with_ack 767 | and ((packet[3] & _RH_FLAGS_ACK) == 0) 768 | and (packet[0] != _RH_BROADCAST_ADDRESS) 769 | ): 770 | # delay before sending Ack to give receiver a chance to get ready 771 | if self.ack_delay is not None: 772 | time.sleep_ms(self.ack_delay) 773 | # send ACK packet to sender 774 | data = bytes("!", "UTF-8") 775 | self.send( 776 | data, 777 | destination=packet[1], 778 | node=packet[0], 779 | identifier=packet[2], 780 | flags=(packet[3] | _RH_FLAGS_ACK), 781 | ) 782 | # reject Retries if we have seen this idetifier from this source before 783 | if (self.seen_ids[packet[1]] == packet[2]) and ( 784 | packet[3] & _RH_FLAGS_RETRY 785 | ): 786 | packet = None 787 | else: # save the packet identifier for this source 788 | self.seen_ids[packet[1]] = packet[2] 789 | if ( 790 | not with_header and packet is not None 791 | ): # skip the header if not wanted 792 | packet = packet[4:] 793 | # Listen again if necessary and return the result packet. 794 | if keep_listening: 795 | self.listen() 796 | else: 797 | # Enter idle mode to stop receiving other packets. 798 | self.idle() 799 | return packet 800 | 801 | -------------------------------------------------------------------------------- /rfm69_simpletest.py: -------------------------------------------------------------------------------- 1 | from micropython_rfm69 import * 2 | from machine import SPI, Pin 3 | 4 | #ESP32 Example 5 | 6 | CS = Pin(5, Pin.OUT) 7 | RESET = Pin(22, Pin.OUT) 8 | spi = SPI(2, baudrate=5000000, polarity=0, phase=0, bits=8, firstbit=0, sck=Pin(18), mosi=Pin(23), miso=Pin(19)) 9 | 10 | #ESP8266 Example 11 | #CS = Pin(2, Pin.OUT) 12 | #RESET = Pin(0, Pin.OUT) 13 | #spi = SPI(1, baudrate=5000000, polarity=0, phase=0) 14 | # Initialze RFM radio 15 | 16 | #Set Your Radio Freq here 17 | RADIO_FREQ_MHZ = 915.0 18 | 19 | rfm69 = micropython_rfm69.RFM69(spi, CS, RESET, RADIO_FREQ_MHZ) 20 | 21 | # Optionally set an encryption key (16 byte AES key). MUST match both 22 | # on the transmitter and receiver (or be set to None to disable/the default). 23 | rfm69.encryption_key = ( 24 | b"\x01\x02\x03\x04\x05\x06\x07\x08\x01\x02\x03\x04\x05\x06\x07\x08" 25 | ) 26 | 27 | # Print out some chip state: 28 | print("Temperature: {0}C".format(rfm69.temperature)) 29 | print("Frequency: {0}mhz".format(rfm69.frequency_mhz)) 30 | print("Bit rate: {0}kbit/s".format(rfm69.bitrate / 1000)) 31 | print("Frequency deviation: {0}hz".format(rfm69.frequency_deviation)) 32 | 33 | # Send a packet. Note you can only send a packet up to 60 bytes in length. 34 | # This is a limitation of the radio packet size, so if you need to send larger 35 | # amounts of data you will need to break it into smaller send calls. Each send 36 | # call will wait for the previous one to finish before continuing. 37 | rfm69.send(bytes("Hello world!\r\n", "utf-8")) 38 | print("Sent hello world message!") 39 | 40 | # Wait to receive packets. Note that this library can't receive data at a fast 41 | # rate, in fact it can only receive and process one 60 byte packet at a time. 42 | # This means you should only use this for low bandwidth scenarios, like sending 43 | # and receiving a single message at a time. 44 | print("Waiting for packets...") 45 | while True: 46 | packet = rfm69.receive() 47 | # Optionally change the receive timeout from its default of 0.5 seconds: 48 | # packet = rfm69.receive(timeout=5.0) 49 | # If no packet was received during the timeout then None is returned. 50 | if packet is None: 51 | # Packet has not been received 52 | print("Received nothing! Listening again...") 53 | else: 54 | # Received a packet! 55 | # Print out the raw bytes of the packet: 56 | print("Received (raw bytes): {0}".format(packet)) 57 | # And decode to ASCII text and print it too. Note that you always 58 | # receive raw bytes and need to convert to a text format like ASCII 59 | # if you intend to do string processing on your data. Make sure the 60 | # sending side is sending ASCII data before you try to decode! 61 | packet_text = str(packet, "ascii") 62 | print("Received (ASCII): {0}".format(packet_text)) 63 | --------------------------------------------------------------------------------