├── LICENSE.md ├── README.md ├── bareRFM69.cpp ├── bareRFM69.h ├── bareRFM69_const.h ├── examples ├── BusyMan │ └── BusyMan.ino ├── Maximum_PingPong │ └── Maximum_PingPong.ino ├── Maximum_Speed │ └── Maximum_Speed.ino ├── Minimal │ └── Minimal.ino ├── MinimalInterrupt │ └── MinimalInterrupt.ino ├── MinimalInterruptUno │ └── MinimalInterruptUno.ino ├── MinimalInterruptUnoDIO0 │ └── MinimalInterruptUnoDIO0.ino ├── PingPong │ └── PingPong.ino ├── PingPongHPdio0 │ └── PingPongHPdio0.ino └── VariableLength │ └── VariableLength.ino ├── extras ├── check_crypto_mode.py ├── hardware │ ├── .gitattributes │ ├── README.md │ ├── media │ │ ├── bottom.jpg │ │ ├── teensy_rfm69_cw_nrf_upper.png │ │ ├── teensy_rfm69cw_nrf_lower.pdf │ │ ├── teensy_rfm69cw_nrf_lower.png │ │ ├── teensy_rfm69cw_nrf_lower_sch.pdf │ │ ├── teensy_rfm69cw_nrf_upper.pdf │ │ ├── teensy_rfm69cw_nrf_upper_sch.pdf │ │ └── top.jpg │ ├── teensy_rfm69cw_nrf_lower.brd │ ├── teensy_rfm69cw_nrf_lower.sch │ ├── teensy_rfm69cw_nrf_upper.brd │ └── teensy_rfm69cw_nrf_upper.sch └── write_keywords.py ├── keywords.txt ├── plainRFM69.cpp └── plainRFM69.h /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Ivor Wanders 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 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | plainRFM69 2 | ========= 3 | 4 | This is a library for HopeRF's RFM69 radio series. The main goal of this library 5 | is to use the hardware as efficiently as possible. With this goal in mind, the 6 | used approach is different from that of the already existing libraries for this 7 | radio module. 8 | 9 | Approach 10 | ------- 11 | The library is composed of two parts, the first; bareRFM69 provides a bare bones 12 | interface to the hardware. It does nothing by itself and only provides methods 13 | to interact with the hardware. Most of the configuration parameters are listed 14 | in the header file. Constants are used for easy configuration and to prevent 15 | having to look up parameters in the datasheet. 16 | It uses the the new SPI transaction system. 17 | 18 | The second part; plainRFM69, provides the user with more relevant functions for 19 | sending messages. It allows for easy sending and receiving of packets. It 20 | contains an internal buffer for received messages. Both receiving and 21 | transmitting packets is done using the RFM69's AutoMode system. Which means the 22 | radio spends as little time as possible in the transmitter phase and that 23 | received packets can be retrieved from the module efficiently. This also allows 24 | the send methods to be non-blocking, as it is unnecessary to wait until the 25 | radio completes transmission. 26 | 27 | 28 | Internal Workings 29 | --------------- 30 | The plainRFM69 class provides several methods to configure the radio module with 31 | its recommended parameters. The module is used in packet mode. Variable length 32 | and address filtering is supported. 33 | 34 | The AutoMode system (described on page 42 of the RFM69CW datasheet) allows the 35 | radio to change its mode based on a start condition, it then enters the 36 | Intermediate Mode until an exit condition is met. 37 | 38 | ### Receiving 39 | By default, the radio is listening for packets. The AutoMode is configured such 40 | that the radio goes to standby when a packet is received. This means that the 41 | radio waits until that packet is retrieved by the microcontroller before it 42 | starts listening for packets again. Retrieval of this packet is done with the 43 | poll() method, which writes the packet to the internal buffer of plainRFM69. 44 | Once the packet had been read, the radio automatically returns to listening to 45 | packets. 46 | 47 | ### Sending 48 | When a packet is to be transmitted, the radio is placed into standby mode. The 49 | AutoMode is configured such that the radio automatically switches to transmitter 50 | mode when there are bytes in the FIFO. It leaves transmission mode after the 51 | packet is completely sent. This ensures that the transmitter is enabled 52 | exactly for the time necessary to sent the packet. The next time poll() is 53 | called, the radio is configured for receiving packets with the AutoMode, as 54 | described in the previous paragraph. 55 | 56 | ### Interrupt 57 | If the radio has received a packet, it waits in the Intermediate Mode until the 58 | packet is read from the FIFO. When a packet is transmitted, the radio is in the 59 | Intermediate Mode until the packet is completely sent. The radio module can 60 | represent whether it is in the Intermediate State on a digital IO pin (DIO2). 61 | In a slightly different way the same can be achieved using the DIO0 pin from the 62 | radio module, see the `PingPongHPdio0` example on how to use that pin. 63 | 64 | The microcontroller can attach an interrupt to this pin and call the poll() 65 | method on every change. This means that if a packet is received, the 66 | intermediate state is entered, DIO2 will switch from LOW to HIGH, resulting in 67 | an interrupt trigger on the microcontroller, this calls poll() which reads the 68 | packet from the FIFO, after this the radio automatically returns to receiving 69 | state. The same method is possible with transmissions, where the poll() switches 70 | the radio back to receiver mode after the transmission is complete. This means 71 | that the microcontroller does not have to wait until the transmission is 72 | complete, as this is also managed by the same interrupt and subsequent poll() 73 | call. So the method to send a packet does not block until the transmission is 74 | complete. 75 | 76 | 77 | Testing & Performance 78 | ------------------- 79 | The library was developed and tested on two [Teensy 3.1][teensy31]'s with 80 | RFM69CW's attached. The pins used are described in the examples. The library is 81 | tested with Arduino 1.0.6, 1.8.5. The DIO0 example was developed using 82 | AdaFruit Feather M0 with the RFM69HCW radio module. Arduino and Moteino are also 83 | known to work. 84 | 85 | All tests were performed with the radio modules located next to each other. The 86 | measured results are of course dependent on the parameters chosen, the 87 | values from the examples show below use a GFSK modulation at 300000 bps. 88 | 89 | #### Maximum_Speed 90 | Sends 64 byte packets from the sender to the receiver. For every message sent 91 | a counter is incremented. This counter is repeated in the message to create the 92 | payload. At the receiver the payload is checked and the counter is used to check 93 | whether there were missed packets. 94 | ``` 95 | Total packets: 101600 96 | Packetloss count: 1 97 | Per second: 416 98 | ``` 99 | This example can also be used to verify that the internal buffer is working. For 100 | example when a delay is introduced in the loop of the receiver. 101 | 102 | 103 | #### Maximum_PingPong 104 | Sends 64 byte packets from the sender to the receiver, which echoes the packet, 105 | the sender waits for this echo and checks the counter and message payload. 106 | ``` 107 | Successful pingpongs: 123468 108 | Incorrect pingpongs: 0 109 | Timeout pingpongs: 0 110 | Successful / second: 188 111 | Average ping (uSec): 5313 112 | ``` 113 | 114 | #### Buffering 115 | The internal buffer of bareRFM69 is demonstrated in the BusyMan example. In this 116 | example the interrupt mechanism is used and the receiving end is very busy in 117 | the loop, this results in several messages queueing up in the internal buffer. 118 | ``` 119 | We are busy, doing important things! 120 | Oh look, there are packets! 121 | Packet (4): 2035 122 | Packet (4): 2036 123 | Packet (4): 2037 124 | Packet (4): 2038 125 | Packet (4): 2039 126 | Packet (4): 2040 127 | We had 6 packets in the buffer. 128 | ``` 129 | 130 | 131 | Usage 132 | ----- 133 | As said, this library was created to be efficient. The goal was not really to be 134 | user friendly. The examples were tested and are working without any known bugs. 135 | Feel free to use it, but if it breaks you get to keep both parts, if you have a 136 | fix for the problem feel free to share it. 137 | 138 | The examples should provide pointers on how to use this, I suggest looking at 139 | Minimal and MinimalInterrupt, where the latter should be the most minimal 140 | implementation to build on. The internal buffering is shown in the BusyMan 141 | example. 142 | 143 | The header files for both bareRFM69 and plainRFM69 provide explanation of the 144 | methods. If using a high power module, be sure to call `setHighPowerModule()` to 145 | enable the high power functionality to be used. 146 | 147 | Other libraries that might interest you are [Radiohead][radiohead] or 148 | LowPowerLabs' [RFM69][rfm69] which have seen extensive testing. 149 | 150 | The PCB's I designed to connect the radio modules to the Teensy can be found in 151 | [extras/hardware/](extras/hardware/). 152 | 153 | License 154 | ------ 155 | MIT License, see LICENSE.md. 156 | 157 | Copyright (c) 2014 Ivor Wanders 158 | 159 | 160 | [teensy31]: http://www.pjrc.com/teensy/ 161 | [radiohead]: http://www.airspayce.com/mikem/arduino/RadioHead/ 162 | [rfm69]: https://github.com/LowPowerLab/RFM69 163 | -------------------------------------------------------------------------------- /bareRFM69.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is part of plainRFM69. 3 | * Copyright (c) 2014, Ivor Wanders 4 | * MIT License, see the LICENSE.md file in the root folder. 5 | */ 6 | 7 | #include "bareRFM69.h" 8 | 9 | // Most functions are implemented in the header file. 10 | 11 | void inline bareRFM69::chipSelect(bool enable){ 12 | digitalWrite(this->cs_pin, (enable) ? LOW : HIGH ); 13 | } 14 | 15 | void bareRFM69::writeRegister(uint8_t reg, uint8_t data){ 16 | SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); // gain control of SPI bus 17 | this->chipSelect(true); // assert chip select 18 | SPI.transfer(RFM69_WRITE_REG_MASK | (reg & RFM69_READ_REG_MASK)); 19 | SPI.transfer(data); 20 | this->chipSelect(false);// deassert chip select 21 | SPI.endTransaction(); // release the SPI bus 22 | } 23 | 24 | uint8_t bareRFM69::readRegister(uint8_t reg){ 25 | uint8_t foo; 26 | SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); // gain control of SPI bus 27 | this->chipSelect(true); // assert chip select 28 | SPI.transfer((reg % RFM69_READ_REG_MASK)); 29 | foo = SPI.transfer(0); 30 | this->chipSelect(false);// deassert chip select 31 | SPI.endTransaction(); // release the SPI bus 32 | return foo; 33 | } 34 | 35 | void bareRFM69::writeMultiple(uint8_t reg, void* data, uint8_t len){ 36 | SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); // gain control of SPI bus 37 | this->chipSelect(true); // assert chip select 38 | SPI.transfer(RFM69_WRITE_REG_MASK | (reg & RFM69_READ_REG_MASK)); 39 | uint8_t* r = reinterpret_cast(data); 40 | for (uint8_t i=0; i < len ; i++){ 41 | SPI.transfer(r[len - i - 1]); 42 | } 43 | this->chipSelect(false);// deassert chip select 44 | SPI.endTransaction(); // release the SPI bus 45 | } 46 | 47 | void bareRFM69::readMultiple(uint8_t reg, void* data, uint8_t len){ 48 | SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); // gain control of SPI bus 49 | this->chipSelect(true); // assert chip select 50 | 51 | SPI.transfer((reg % RFM69_READ_REG_MASK)); 52 | uint8_t* r = reinterpret_cast(data); 53 | for (uint8_t i=0; i < len ; i++){ 54 | r[len - i - 1] = SPI.transfer(0); 55 | } 56 | this->chipSelect(false);// deassert chip select 57 | SPI.endTransaction(); // release the SPI bus 58 | } 59 | 60 | uint32_t bareRFM69::readRegister32(uint8_t reg){ 61 | uint32_t f = 0; 62 | this->readMultiple(reg, &f, 4); 63 | return f; 64 | } 65 | uint32_t bareRFM69::readRegister24(uint8_t reg){ 66 | uint32_t f = 0; 67 | this->readMultiple(reg, &f, 3); 68 | return f; 69 | } 70 | uint16_t bareRFM69::readRegister16(uint8_t reg){ 71 | uint16_t f = 0; 72 | this->readMultiple(reg, &f, 2); 73 | return f; 74 | } 75 | 76 | void bareRFM69::writeFIFO(void* buffer, uint8_t len){ 77 | uint8_t* r = reinterpret_cast(buffer); 78 | SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); // gain control of SPI bus 79 | this->chipSelect(true); // assert chip select 80 | SPI.transfer(RFM69_WRITE_REG_MASK | (RFM69_FIFO & RFM69_READ_REG_MASK)); 81 | for (uint8_t i=0; i < len ; i++){ 82 | // Serial.print("Writing to FIFO: "); Serial.println(r[i]); 83 | SPI.transfer(r[i]); 84 | } 85 | this->chipSelect(false);// deassert chip select 86 | SPI.endTransaction(); // release the SPI bus 87 | } 88 | 89 | void bareRFM69::readFIFO(void* buffer, uint8_t len){ 90 | uint8_t* r = reinterpret_cast(buffer); 91 | SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); // gain control of SPI bus 92 | this->chipSelect(true); // assert chip select 93 | 94 | SPI.transfer((RFM69_FIFO % RFM69_READ_REG_MASK)); 95 | for (uint8_t i=0; i < len ; i++){ 96 | r[i] = SPI.transfer(0); 97 | } 98 | this->chipSelect(false);// deassert chip select 99 | SPI.endTransaction(); // release the SPI bus 100 | } 101 | 102 | uint8_t bareRFM69::readVariableFIFO(void* buffer, uint8_t max_length){ 103 | uint8_t* r = reinterpret_cast(buffer); 104 | 105 | SPI.beginTransaction(SPISettings(10000000, MSBFIRST, SPI_MODE0)); // gain control of SPI bus 106 | this->chipSelect(true); // assert chip select 107 | 108 | SPI.transfer((RFM69_FIFO % RFM69_READ_REG_MASK)); 109 | uint8_t len = SPI.transfer(0); 110 | r[0] = len; 111 | // Serial.print("readVariableFIFO, len:"); Serial.println(len); 112 | len = len > (max_length-1) ? (max_length-1) : len; 113 | // Serial.print("readVariableFIFO, len:"); Serial.println(len); 114 | for (uint8_t i=0; i < len; i++){ 115 | r[i+1] = SPI.transfer(0); 116 | // Serial.print("readVariableFIFO, r[i+1]"); Serial.println(r[i+1]); 117 | } 118 | this->chipSelect(false);// deassert chip select 119 | SPI.endTransaction(); // release the SPI bus 120 | return len; 121 | } 122 | 123 | void bareRFM69::reset(uint8_t pin){ // function to send the RFM69 a hardware reset. 124 | // p 75 of datasheet; 125 | pinMode(pin, OUTPUT); 126 | digitalWrite(pin, HIGH); 127 | delayMicroseconds(150); // pull high for >100 uSec 128 | pinMode(pin, INPUT); // release 129 | delay(10); // wait 10 milliseconds before SPI is possible. 130 | } 131 | 132 | -------------------------------------------------------------------------------- /bareRFM69.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Ivor Wanders 3 | * MIT License, see the LICENSE.md file in the root folder. 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | 10 | #ifndef BARE_RFM69_H 11 | #define BARE_RFM69_H 12 | 13 | #include 14 | 15 | /* 16 | The bareRFM69 object only provides convenient methods to interact with the 17 | hardware. It does not do anything by itself. 18 | 19 | The order of methods is identical to the order in which the registers are 20 | described in page 61 to page 74. 21 | 22 | Tested on the RFM69CW from HopeRF: 23 | http://www.hoperf.com/rf/fsk_module/RFM69CW.htm 24 | */ 25 | 26 | 27 | 28 | #if !defined(SPI_HAS_TRANSACTION) 29 | #error "You need to have support for SPI transactions in the SPI library." 30 | #error "https://github.com/PaulStoffregen/SPI/blob/master/SPI.h" 31 | #error "https://github.com/PaulStoffregen/SPI/blob/master/SPI.cpp" 32 | #endif 33 | 34 | class bareRFM69 { 35 | private: 36 | uint8_t cs_pin; // chip select pin. 37 | 38 | 39 | // SPI relevant stuff 40 | void writeRegister(uint8_t reg, uint8_t data); 41 | void writeMultiple(uint8_t reg, void* data, uint8_t len); 42 | 43 | uint8_t readRegister(uint8_t reg); 44 | uint16_t readRegister16(uint8_t reg); 45 | uint32_t readRegister24(uint8_t reg); 46 | uint32_t readRegister32(uint8_t reg); 47 | void readMultiple(uint8_t reg, void* data, uint8_t len); 48 | 49 | void inline chipSelect(bool enable); 50 | 51 | public: 52 | bareRFM69(uint8_t cs_pin){ 53 | this->cs_pin = cs_pin; 54 | pinMode(this->cs_pin, OUTPUT); 55 | digitalWrite(this->cs_pin, HIGH); 56 | 57 | // Max 10 MHz clock, MSB first, CPOL= 0 and CPHA = 0 58 | // according to the datasheet. 59 | // SPISettings(10000000, MSBFIRST, SPI_MODE0) 60 | // is to be used. 61 | }; 62 | 63 | uint8_t readRawRegister(uint8_t reg){return this->readRegister(reg);} 64 | // used for debugging. 65 | 66 | 67 | //##################################################################### 68 | // Generic stuff 69 | //##################################################################### 70 | 71 | /* 72 | Constants in the documentation: 73 | 74 | FXO_SC = 32e6 # 32 MHz for all frequencies. 75 | FSTEP = FXO_SC/(2**19) # = 61.03515625 Hz; Frequency synthesizer 76 | step FSTEP 77 | 78 | */ 79 | 80 | static void reset(uint8_t reset_pin); 81 | // sends a hard reset using the reset pin on the RFM69. 82 | // does not require an instance, use bareRFM69::reset(pin); 83 | 84 | 85 | //##################################################################### 86 | // FiFo 87 | //##################################################################### 88 | void writeFIFO(void* buffer, uint8_t len); 89 | //Write from buffer to FIFO for 'len' bytes. 90 | void readFIFO(void* buffer, uint8_t len); 91 | //Read from buffer to FIFO for 'len' bytes. 92 | 93 | uint8_t readVariableFIFO(void* buffer, uint8_t max_length); 94 | // Reads up to 'len' bytes, len is determined by first byte in the FIFO 95 | // this byte is also placed in the buffer. The max_length argument can 96 | // be used to limit the number of bytes. 97 | 98 | 99 | //##################################################################### 100 | // Operating stuff 101 | //##################################################################### 102 | 103 | /* 104 | On the Automatic sequencer, p35, section 4.2: 105 | 106 | TX start procedure: 107 | In Packet mode, the RFM69CW will automatically modulate the RF 108 | signal with preamble bytes as soon as TxReady or ModeReady 109 | happen. The actual packet transmission (starting with the number 110 | of preambles specified in PreambleSize) will start when the 111 | TxStartCondition is fulfilled 112 | 113 | RX start procedure: 114 | In Packet mode, the receiver will start locking its Bit 115 | Synchronizer on a minimum or 12 bits of received preamble (see 116 | section 3.4.13 for details), before the reception of correct 117 | Data, or Sync Word (if enabled) can occur. 118 | 119 | 120 | */ 121 | 122 | void setMode(uint8_t mode){this->writeRegister(RFM69_OPMODE, mode);}; 123 | /* 124 | Sets the mode of the radio module. 125 | Mode is a sum of the following categories: 126 | Sequencer: 127 | RFM69_MODE_SEQUENCER_OFF 128 | RFM69_MODE_SEQUENCER_ON 129 | Allows the automatic mode sequencer to be disabled. 130 | With OFF, a mode can be forced. 131 | 132 | Listen: 133 | RFM69_MODE_LISTEN_ON 134 | RFM69_MODE_LISTEN_OFF 135 | Allows enabling or disable the listen mode. 136 | Listen Abort: 137 | RFM69_MODE_LISTEN_ABORT 138 | Aborts listen mode when set with RFM69_MODE_LISTEN_OFF. 139 | Mode: 140 | RFM69_MODE_SLEEP 141 | RFM69_MODE_STANDBY 142 | RFM69_MODE_FREQ_SYNTH 143 | RFM69_MODE_TRANSMITTER 144 | RFM69_MODE_RECEIVER 145 | */ 146 | uint8_t getMode(){return this->readRegister(RFM69_OPMODE);}; 147 | 148 | 149 | //##################################################################### 150 | // RF frequency 151 | //##################################################################### 152 | 153 | 154 | // RF carrier frequency 155 | void setFrf(uint32_t Frf){ 156 | this->writeMultiple(RFM69_FRF_MSB, &Frf, 3);}; 157 | /* 158 | Frf = FSTEP * Frf(23,0) 159 | Defaults to 0xc0e452; 915 MHz 160 | 161 | Ranges In MHz Min Typ Max 162 | 315MHz Module 290 340 163 | 433MHz Module 424 510 164 | 868MHz Module 862 890 165 | 915MHz Module 890 1020 166 | 167 | Example values: 168 | 434 MHz; 434e6/FSTEP = 7110656 = 0x6c8000 (measured; 433.98 MHz) 169 | 500 MHz; 500e6/FSTEP = 8192000 = 0x7d0000 (outside of ISM) 170 | 450 MHz; 450e6/FSTEP = 7372800 = 0x708000 (outside of ISM) 171 | */ 172 | 173 | 174 | // Frequency Deviation 175 | void setFdev(uint16_t Fdev){ 176 | this->writeMultiple(RFM69_FDEV_MSB, &Fdev, 2);}; 177 | /* 178 | Fdev = FSTEP * Fdev(15,0) ; in Hz. 179 | Defaults to 0x52; 5000 Hz 180 | 181 | In FSK Requirement: 182 | FDEV + BR/2 <= 500 kHz 183 | -> 184 | BR <= 2*(500 kHz - Fdev) 185 | 186 | For demodulator, most efficient when modulation index greater than 187 | 0.5 and below 10: 188 | 0.5 <= \beta = (2*Fdev)/(BitRate) <= 10 189 | */ 190 | 191 | 192 | void startRCCalibration(){this->writeRegister(RFM69_OSC1, 1<<7);} 193 | /* Triggers the calibration of the RC oscillator when set. 194 | Always reads 0. RC calibration must be triggered in Standby mode. 195 | 196 | See section 4.3.5 of the RFM69CW datasheet. The timing of the Listen 197 | Mode depends on the internal low-power RC oscillator. 198 | 199 | This oscillator is automatically calibrated but needs recalibration 200 | when used over large temperature variations. 201 | */ 202 | 203 | bool completedRCCalibration(){return this->readRegister(RFM69_OSC1) & (1<<6);} 204 | /* Reads RC calibration finished flag */ 205 | // takes about 125 usec between start and completion. 206 | 207 | 208 | //##################################################################### 209 | // data modulation parameters 210 | //##################################################################### 211 | 212 | 213 | // Bit rate 214 | void setBitRate(uint16_t BitRate){ 215 | this->writeMultiple(RFM69_BITRATE_MSB, &BitRate, 2);}; 216 | /* 217 | BitRate = FXO_SC/BitRate(15,0) ; in kbit/s. 218 | Defaults to 0x1a0b; 4.8 kb/s 219 | 220 | In FSK Requirement: 221 | FDEV + BR/2 <= 500 kHz 222 | -> 223 | BR <= 2*(500 kHz - Fdev) 224 | 225 | For demodulator, most efficient when modulation index greater than 226 | 0.5 and below 10: 227 | 0.5 <= \beta = (2*Fdev)/(BitRate) <= 10 228 | 229 | Note: to respect oversampling rules in the decimation chain of the 230 | receiver, the Bit Rate cannot be set at a higher value 231 | than 2 times the single-side receiver bandwidth (BitRate < 2 232 | x RxBw) page 26 233 | 234 | */ 235 | 236 | 237 | // data modulation 238 | void setDataModul(uint8_t processing, bool use_ook, uint8_t modulationshaping){ 239 | this->writeRegister(RFM69_DATA_MODUL, (processing << 5) + (use_ook << 3) + (modulationshaping & 0b11));}; 240 | /* 241 | processing is either: 242 | RFM69_DATAMODUL_PROCESSING_PACKET 243 | Packet mode (recommended): user only provides/retrieves payload bytes to/from the FIFO. 244 | The packet is automatically built with preamble, Sync word, and optional AES, CRC, and DC-free encoding schemes 245 | 246 | RFM69_DATAMODUL_PROCESSING_CONT_SYNCRHONISER 247 | RFM69_DATAMODUL_PROCESSING_CONT 248 | Continuous mode: each bit transmitted or received is accessed in real time at the DIO2/DATA pin 249 | 250 | use_ook is either: 251 | RFM69_DATAMODUL_FSK 252 | Use frequency shift keying implicitly recommended via default type. 253 | RFM69_DATAMODUL_OOK 254 | Use On-Off Keying 255 | 256 | modulationshaping is either: 257 | RFM69_DATAMODUL_SHAPING_GFSK_NONE 258 | No shaping 259 | RFM69_DATAMODUL_SHAPING_GFSK_BT_1_0 260 | Gaussian filter, BT = 1.0 261 | RFM69_DATAMODUL_SHAPING_GFSK_BT_0_5 262 | Gaussian filter, BT = 0.5 263 | RFM69_DATAMODUL_SHAPING_GFSK_BT_0_3 264 | Gaussian filter, BT = 0.3 265 | 266 | RFM69_DATAMODUL_SHAPING_OOK_NONE 267 | No Shaping 268 | RFM69_DATAMODUL_SHAPING_OOK_FCUTOFF_BR 269 | Filtering with fcutoff=BR 270 | RFM69_DATAMODUL_SHAPING_OOK_FCUTOFF_2BR 271 | Filtering with fcutoff=2*BR 272 | 273 | At higher bitrates it is advisable to use modulation shaping. 274 | 275 | */ 276 | 277 | //##################################################################### 278 | // Listen Mode 279 | //##################################################################### 280 | /* 281 | Listen mode: 282 | The circuit can be set to Listen mode, by setting ListenOn in 283 | RegOpMode to 1 while in Standby mode. In this mode, RFM69CW 284 | spends most of the time in Idle mode, during which only the RC 285 | oscillator runs. Periodically the receiver is woken up and 286 | listens for an RF signal. If a wanted signal is detected, the 287 | receiver is kept on and the data is demodulated. Otherwise, if 288 | a wanted signal hasn't been detected after a pre-defined period 289 | of time, the receiver is disabled until the next time period. 290 | 291 | Time interval given by: 292 | t ListenX = ListenCoefX ∗ Listen Re solX 293 | 294 | Where X is either 'Idle' or 'Rx' 295 | 296 | */ 297 | 298 | 299 | void setListenConfig(uint8_t ListenResolIdle, uint8_t ListenResolRx, uint8_t ListenCriteria, uint8_t ListenEnd){ 300 | this->writeRegister(RFM69_LISTEN1, ListenResolIdle+ListenResolRx+ListenCriteria+ListenEnd);}; 301 | /* 302 | ListenResolIdle one of: 303 | RFM69_LISTEN_RESOL_IDLE_64US 304 | RFM69_LISTEN_RESOL_IDLE_4_1MS 305 | RFM69_LISTEN_RESOL_IDLE_262MS 306 | Sets the listen mode idle time to 64 usec, 4.1ms or 262 ms 307 | ListenResolRx one of: 308 | RFM69_LISTEN_RESOL_RX_64US 309 | RFM69_LISTEN_RESOL_RX_4_1MS 310 | RFM69_LISTEN_RESOL_RX_262MS 311 | Sets the listen mode Rx time to 64 usec, 4.1ms or 262 ms 312 | ListenCriteria 313 | Criteria for packet acceptance in listen mode, either: 314 | RFM69_LISTEN_CRITERIA_RSSI 315 | signal strength above RssiThreshold 316 | RFM69_LISTEN_CRITERIA_RSSI_SYNC 317 | Signal strength above RssiThresshold and SyncAddress matched. 318 | 319 | ListenEnd: 320 | Action taken after acceptance of a packet in Listen mode: 321 | RFM69_LISTEN_END_STAY_RX_LISTEN_STOP 322 | Chip stays in Rx mode, listen mode stops and must be disabled. 323 | RFM69_LISTEN_END_RX_UNTIL_LISTEN_STOP 324 | Chip stays in Rx mode until PayloadReady or Timeout interrupt 325 | occurs. Listen mode stops and must be disabled. 326 | RFM69_LISTEN_END_RX_UNTIL_LISTEN_RESUME 327 | Chip stays in Rx mode until PayloadReady or Timeout interrupt 328 | occurs. Listen mode then resumes in Idle State. 329 | FIFO lost at next RX wakeup. 330 | 331 | Default: (RFM69_LISTEN_RESOL_IDLE_4_1MS, RFM69_LISTEN_RESOL_RX_64US, 332 | RFM69_LISTEN_CRITERIA_RSSI, RFM69_LISTEN_END_RX_UNTIL_LISTEN_STOP) 333 | */ 334 | 335 | void setListenCoefIdle(uint8_t coeff){this->writeRegister(RFM69_LISTEN2, coeff);}; 336 | /* 337 | Duration of the Idle phase in Listen mode. 338 | t ListenIdle = ListenCoefIdle ∗ ListenResolIdle 339 | 340 | Default 0xf5; 245 341 | */ 342 | void setListenCoefRx(uint8_t coeff){this->writeRegister(RFM69_LISTEN3, coeff);}; 343 | /* 344 | Duration of the Rx phase in Listen mode. 345 | t ListenRx = ListenCoefRx ∗ ListenResolRx 346 | 347 | Default 0x20; 32 348 | */ 349 | 350 | //##################################################################### 351 | // Version 352 | //##################################################################### 353 | 354 | // version of chip 355 | uint8_t getVersion(){return readRegister(RFM69_VERSION);} 356 | 357 | 358 | 359 | //##################################################################### 360 | // Transmitter registers 361 | //##################################################################### 362 | 363 | void setPALevel(uint8_t amplifiers, uint8_t OutputPower){ 364 | this->writeRegister(RFM69_PA_LEVEL, amplifiers + (OutputPower % (1<<5)));}; 365 | /* 366 | Amplifiers is one of or sum of: 367 | RFM69_PA_LEVEL_PA0_ON 368 | RFM69_PA_LEVEL_PA1_ON 369 | RFM69_PA_LEVEL_PA2_ON 370 | Output power setting, with 1 dB steps, so in [0,31] 371 | Pout = -18 + OutputPower [dBm] , with PA0 372 | Only the16 upper values of OutputPower are accessible according to 373 | datasheet, measurements indicate 0-31 is usable. 374 | 375 | 376 | p21: A low power mode, where -2dBm < Pout < 13dBm, with PA1 enabled 377 | Default is 0b10011111; RFM69_PA_LEVEL_PA0_ON + 13dBm gain 378 | 379 | Truth table on page 21 only lists PA0 ON with effect... 380 | Possibly only RFM69_PA_LEVEL_PA0_ON has effect. 381 | */ 382 | 383 | void setPARamp(uint8_t ramp){this->writeRegister(RFM69_PA_RAMP, ramp);}; 384 | /* 385 | Set Rise/Fall time of ramp up/down in FSK 386 | Valid fields are: 387 | RFM69_PA_RAMP_#US 388 | Where # is one of {3400, 2000, 1000, 500, 250, 125, 100, 62, 50, 389 | 40, 31, 25, 20, 15, 12, 10} 390 | So from 3.4 ms, down to 10 us. 391 | Defaults to 0b1001; 40us 392 | */ 393 | 394 | void setOCP(uint8_t ocplimit){ 395 | this->writeRegister(RFM69_OCP, ((ocplimit != 0)<<7) + (((ocplimit-45)/5)&0b1111));}; 396 | /* 397 | It helps preventing surge currents required when the transmitter is 398 | used at its highest power levels, thus protecting the battery that 399 | may power the application. 400 | 401 | Ocplimit is either 0, current limit is disabled then. 402 | Imax = 45 + 5 ⋅*OcpTrim 403 | Ocptrim = (Imax - 45)/5 404 | Or it is one of [45, 50, ..., 115, 120] 405 | Defaults to 0b1010; 95 mA, enabled. 406 | */ 407 | 408 | //##################################################################### 409 | // Receiver Registers 410 | //##################################################################### 411 | 412 | void setLNA(uint8_t LnaZin, uint8_t LnaGainSelect){ 413 | this->writeRegister(RFM69_LNA, LnaZin+LnaGainSelect);}; 414 | /* 415 | LnaZin: 416 | Impedance of the LNA: 417 | RFM69_LNA_IMP_50OHM: 418 | 50 ohm (default) 419 | RFM69_LNA_IMP_200OHM: 420 | 200 ohm (recommended) 421 | LNA Gain: 422 | RFM69_LNA_GAIN_AGC_LOOP: 423 | gain set by the internal AGC loop (default) 424 | RFM69_LNA_GAIN_#DB: 425 | Highest gain - # dB, with # from {0, 6, 12, 24, 36, 48} 426 | */ 427 | 428 | uint8_t getLNA(){return ((this->readRegister(RFM69_LNA)>>3)&0b111);} 429 | /* 430 | Returns set LNA, as RFM69_LNA_GAIN_#DB 431 | */ 432 | 433 | void setRxBw(uint8_t DccFreq, uint8_t RxBwMant, uint8_t RxBwExp){ 434 | this->writeRegister(RFM69_RX_BW, (DccFreq<<5) + (RxBwMant << 3) + (RxBwExp ));}; 435 | /* 436 | DccFreq: 437 | Cut-off frequency of the DC offset canceller (DCC): 438 | Fc = (4*RxBw)/(2pi * 2**(DccFreq+2)) 439 | Default: 0b100, Recommended: 0b010, ~4% of RxBw. 440 | RxBwMant: 441 | Channel filter bandwith control 442 | 0b00: RxBwMant = 16 (default) 443 | 0b01: RxBwMant = 20 444 | 0b10: RxBwMant = 24 (recommended) 445 | RxBwExp (3 bits): 446 | Channel filter bandwith control: 447 | FSK Mode: 448 | RxBw = FXO_SC/((RxBwMant*4+16)*2**(RxBwExp+2)) 449 | OOK Mode: 450 | RxBw = FXO_SC/((RxBwMant*4+16)*2**(RxBwExp+3)) 451 | See Table 14 for tabulated values. 452 | Default:0b110, recommended:0b101 453 | 454 | Recommended: RxBwMant=24, RxBwExp=5; 10.4 Khz in FSK, /2 for OOK. 455 | 456 | Note: to respect oversampling rules in the decimation chain of the 457 | receiver, the Bit Rate cannot be set at a higher value 458 | than 2 times the single-side receiver bandwidth (BitRate < 2 459 | x RxBw) page 26 460 | 461 | */ 462 | 463 | void setAfcBw(uint8_t DccFreqAfc, uint8_t RxBwMantAfc, uint8_t RxBwExpAfc){ 464 | this->writeRegister(RFM69_AFC_BW, (DccFreqAfc <<5)+(RxBwMantAfc << 3) + (RxBwExpAfc ));} 465 | /* 466 | DccFreqAfc: 467 | DccFreq parameter used during the AFC 468 | Default: 0b100 469 | RxBwMantAfc: 470 | RxBwMant parameter used during the AFC 471 | Default: 0b01 472 | RxBwExpAfc: 473 | RxBwExp parameter used during the AFC 474 | Default 0b010, Recommended 0b011 475 | */ 476 | 477 | 478 | void setAfcCtrl(bool improved_AFC){this->writeRegister(RFM69_AFC_CTRL, improved_AFC<<5);} 479 | /* 480 | Improved AFC routine for signals with modulation index 481 | lower than 2. Refer to section 3.4.16 for details 482 | RFM69_AFC_CTRL_STANDARD 483 | Standard AFC routine (default) 484 | RFM69_AFC_CTRL_IMPROVED 485 | Improved AFC routine 486 | */ 487 | 488 | 489 | void startRssi(){ 490 | this->writeRegister(RFM69_RSSI_CONFIG, 1);}; 491 | bool completedRssi(){ 492 | return this->readRegister(RFM69_RSSI_CONFIG) & 0b10;}; 493 | uint8_t getRssiValue(){ 494 | return this->readRegister(RFM69_RSSI_VALUE);}; 495 | /* 496 | Initiated RSSI measurement, check wheter it is completed and get it's value. 497 | Absolute value of the RSSI in dBm, 0.5dB steps. 498 | RSSI = -RssiValue/2 [dBm] 499 | */ 500 | 501 | //##################################################################### 502 | // Pin IO and IRQ 503 | //##################################################################### 504 | 505 | void setDioMapping1(uint8_t mappings){ 506 | this->writeRegister(RFM69_DIO_MAPPING1, mappings);} 507 | /* 508 | Sets Digital input/output mappings. 509 | 510 | OR-ed statements of the following: 511 | 512 | For Dio0Mapping: 513 | in Rx mode: 514 | RFM69_PACKET_DIO_0_RX_CRC_OK (default) 515 | RFM69_PACKET_DIO_0_RX_PAYLOAD_READY 516 | RFM69_PACKET_DIO_0_RX_SYNC_ADDRESS 517 | RFM69_PACKET_DIO_0_RX_RSII 518 | 519 | in Tx Mode: 520 | RFM69_PACKET_DIO_0_TX_PACKET_SENT (default) 521 | RFM69_PACKET_DIO_0_TX_TX_READY 522 | RFM69_PACKET_DIO_0_TX_PLL 523 | 524 | For Dio1Mapping: 525 | RFM69_PACKET_DIO_1_FIFO_LEVEL (default) 526 | RFM69_PACKET_DIO_1_FIFO_FULL 527 | RFM69_PACKET_DIO_1_FIFO_NOT_EMPTY 528 | 529 | For Dio2Mapping: 530 | RFM69_PACKET_DIO_2_FIFO_NOT_EMPTY (default) 531 | RFM69_PACKET_DIO_2_AUTOMODE 532 | 533 | For Dio3Mapping: 534 | RFM69_PACKET_DIO_3_FIFO_FULL (default) 535 | 536 | */ 537 | 538 | 539 | 540 | uint8_t getIRQ1Flags(){ 541 | return this->readRegister(RFM69_IRQ_FLAGS1);}; 542 | /* 543 | Returns IRQ1 flags, bitmask, the following bits can be set: 544 | 545 | RFM69_IRQ1_MODEREADY 546 | Set when the operation mode requested in Mode, is ready 547 | - Sleep: Entering Sleep mode 548 | - Standby: XO is running 549 | - FS: PLL is locked 550 | - Rx: RSSI sampling starts 551 | - Tx: PA ramp-up completed 552 | Cleared when changing operating mode. 553 | RFM69_IRQ1_RXREADY 554 | Set in Rx mode, after RSSI, AGC and AFC. Cleared when leaving 555 | Rx. 556 | RFM69_IRQ1_TXREADY 557 | Set in Tx mode, after PA ramp-up. Cleared when leaving Tx. 558 | RFM69_IRQ1_PLLLOCK 559 | Set (in FS, Rx or Tx) when the PLL is locked. 560 | Cleared when it is not. 561 | RFM69_IRQ1_RSSI 562 | Set in Rx when the RssiValue exceeds RssiThreshold. 563 | Cleared when leaving Rx. 564 | RFM69_IRQ1_TIMEOUT 565 | Set when a timeout occurs (see TimeoutRxStart and 566 | TimeoutRssiThresh) Cleared when leaving Rx or FIFO is emptied. 567 | RFM69_IRQ1_AUTOMODE 568 | Set when entering Intermediate mode. Cleared when exiting 569 | Intermediate mode. Please note that in Sleep mode a small delay 570 | can be observed between AutoMode interrupt and the corresponding 571 | enter/exit condition. 572 | RFM69_IRQ1_SYNCADDRESSMATCH 573 | Set when Sync and Address (if enabled) are detected. Cleared 574 | when leaving Rx or FIFO is emptied.This bit is read only in 575 | Packet mode, rwc in Continuous mode. 576 | 577 | */ 578 | uint8_t getIRQ2Flags(){return this->readRegister(RFM69_IRQ_FLAGS2);}; 579 | /* 580 | Returns IRQ2 flags, bitmask, the following bits can be set: 581 | 582 | RFM69_IRQ2_FIFOFULL 583 | Set when FIFO is full (i.e. contains 66 bytes), else cleared. 584 | RFM69_IRQ2_FIFONOTEMPTY 585 | Set when FIFO contains at least one byte, else cleared 586 | RFM69_IRQ2_FIFOLEVEL 587 | Set when the number of bytes in the FIFO strictly exceeds 588 | FifoThreshold, else cleared. Flag(s) and FIFO are cleared when 589 | this bit is set. The 590 | RFM69_IRQ2_FIFOOVERRUN 591 | Set when FIFO overrun occurs. (except in Sleep mode) FIFO then 592 | becomes immediately available for the next transmission / 593 | reception. 594 | RFM69_IRQ2_PACKETSENT 595 | Set in Tx when the complete packet has been sent. 596 | Cleared when exiting Tx. 597 | RFM69_IRQ2_PAYLOADREADY 598 | Set in Rx when the payload is ready (i.e. last byte received and 599 | CRC, if enabled and CrcAutoClearOff is cleared, is Ok). 600 | Cleared when FIFO is empty. 601 | RFM69_IRQ2_CRCOK 602 | Set in Rx when the CRC of the payload is Ok. 603 | Cleared when FIFO is empty. 604 | 605 | */ 606 | 607 | void setRSSIThreshold(uint8_t level){ 608 | this->writeRegister(RFM69_RSSI_THRESH, level);}; 609 | /* 610 | RSSI trigger level for Rssi interrupt : 611 | triggers at: -RssiThreshold / 2 [dBm] 612 | Default 0xFF. 613 | Recommended 0xE4; -0xE4/2 = -114 dBm 614 | */ 615 | void setTimeoutRxStart(uint8_t duration){ 616 | this->writeRegister(RFM69_RX_TIMEOUT1, duration);}; 617 | /* 618 | Timeout interrupt is generated TimeoutRxStart*16*Tbit 619 | after switching to Rx mode if Rssi interrupt doesn’t occur 620 | (i.e. RssiValue > RssiThreshold) 621 | 0x00: TimeoutRxStart is disabled 622 | Defaults: 0x00; disabled 623 | */ 624 | 625 | void TimeoutRssiThresh(uint8_t duration){ 626 | this->writeRegister(RFM69_RX_TIMEOUT2, duration);}; 627 | /* 628 | Timeout interrupt is generated TimeoutRssiThresh*16*Tbit 629 | after Rssi interrupt if PayloadReady interrupt doesn’t 630 | occur. 631 | 0x00: TimeoutRssiThresh is disabled 632 | Defaults: 0x00; disabled 633 | */ 634 | 635 | //##################################################################### 636 | // Packet engine registers 637 | //##################################################################### 638 | void setPreambleSize(uint16_t size){ 639 | this->writeMultiple(RFM69_PREAMBLE_MSB, &size, 2);}; 640 | /* 641 | Size of the preamble to be sent (from TxStartCondition fulfilled). 642 | Number of 0b10101010 (0xAA) bytes to be added in front of transmission 643 | Defaults to 0x03. 644 | */ 645 | 646 | void setSyncConfig(bool syncOn, bool FiFoFillCondition, uint8_t SyncSize, uint8_t SyncTol){ 647 | this->writeRegister(RFM69_SYNC_CONFIG, (syncOn<<7) + (FiFoFillCondition << 6) + (((SyncSize-1)&0b111)<<3) + (SyncTol&0b111));}; 648 | /* 649 | Sets the sync config register. 650 | syncOn (default 1): 651 | Enabled the sync word generation and detection. 652 | FiFoFillCondition (default 0): 653 | FIFO filling condition 654 | false: if syncaddress interrupt occurs 655 | true: as long as FiFoFillCondition is set 656 | SyncSize (default 0b011): 657 | Size of the Sync Word bytes. 658 | SyncTol (default 0b000): 659 | Number of tolerated bit errors in Sync word. 660 | 661 | */ 662 | 663 | void setSyncValue(void* buffer, uint8_t len){ 664 | this->writeMultiple(RFM69_SYNC_VALUE1, buffer, len);}; 665 | /* 666 | 1st byte of Sync word. (MSB byte) 667 | Used if SyncOn is set. 668 | Up to len=8 can be written. 669 | */ 670 | 671 | void setPacketConfig1(uint8_t modifiers){ 672 | this->writeRegister(RFM69_PACKET_CONFIG1, modifiers);}; 673 | /* 674 | Set PACKET configuration parameters. 675 | 676 | Should be a sum of the following: 677 | Packet length (implicit: RFM69_PACKET_CONFIG_LENGTH_FIXED): 678 | Can be set to variable length with:RFM69_PACKET_CONFIG_LENGTH_VARIABLE 679 | Called PacketFormat in datasheet. 680 | 681 | DC Free methods (implicit: RFM69_PACKET_CONFIG_DC_FREE_NONE): 682 | Either 683 | Manchester encoding: RFM69_PACKET_CONFIG_DC_FREE_MANCHESTER 684 | or 685 | Whitening filter: RFM69_PACKET_CONFIG_DC_FREE_WHITENING 686 | 687 | CRC checksum calculation (implicit: RFM69_PACKET_CONFIG_CRC_OFF) 688 | Can be enabled with RFM69_PACKET_CONFIG_CRC_ON 689 | 690 | CRC failure options (implicit RFM69_PACKET_CONFIG_CRC_FAIL_DISCARD) 691 | Normally discard packets with failed CRC 692 | RFM69_PACKET_CONFIG_CRC_FAIL_KEEP can be used to keep failed CRC packets. 693 | 694 | Address based filtering (implicit: RFM69_PACKET_CONFIG_ADDRESS_FILTER_NONE) 695 | Either: 696 | Accept on node address with: RFM69_PACKET_CONFIG_ADDRESS_FILTER_NODE 697 | or 698 | Accept on node address or broadast address: RFM69_PACKET_CONFIG_ADDRESS_FILTER_NODE_BROADCAST 699 | 700 | Defaults to RFM69_PACKET_CONFIG_CRC_ON. 701 | */ 702 | 703 | void setPacketConfig2(uint8_t InterPacketRxDelay, bool RestartRx, bool AutoRxRestartOn, bool AesOn){ 704 | this->writeRegister(RFM69_PACKET_CONFIG2, ((InterPacketRxDelay&0b1111)<<4) + (RestartRx<<2) + (AutoRxRestartOn<<1)+AesOn);}; 705 | 706 | /* 707 | Defaults to: AutoRxRestartOn = true, rest 0b0. 708 | InterPacketRxDelay: 709 | After PayloadReady occured, defines the delay between FIFO empty and the start of a new RSSI phase for next packet. Must match the transmitter's PA ramp-down Time. 710 | Tdelay = 0 if InterPacketRxDelay >= 12 711 | Tdelay = (2^(interPackerRxDelay)) / BitRate otherwise 712 | RestartRx: 713 | Forces the Receiver in WAIT mode, in Continuous Rx mode. 714 | AutoRestartRxOn: 715 | Enables automatic Rx restart (RSSI phase) after PayloadReady occurred 716 | and packet has been completely read from FIFO: 717 | False:RestartRx can be used. 718 | True: Rx automatically restarted after InterPacketRxDelay. 719 | AesOn: 720 | Enables AES encryption/decryption, true to enable, limits payload to 66 bytes. 721 | 722 | 723 | */ 724 | 725 | void setPayloadLength(uint8_t length){this->writeRegister(RFM69_PAYLOAD_LENGTH, length);}; 726 | /* 727 | Sets the length of the payload, slightly dependent on PacketFormat 728 | If PacketFormat = 0 (fixed), sets payload length. 729 | If PacketFormat = 1 (variable), max length in Rx, not used in Tx. 730 | Defaults to 0x40=64 731 | */ 732 | 733 | void setNodeAddress(uint8_t address){this->writeRegister(RFM69_NODE_ADRESS, address);}; 734 | /* 735 | Node address used in address filtering. 736 | Defaults to 0x00. 737 | */ 738 | 739 | void setBroadcastAddress(uint8_t address){this->writeRegister(RFM69_BROADCAST_ADRESS, address);}; 740 | /* 741 | Broadcast address used in address filtering. 742 | Defaults to 0x00. 743 | */ 744 | 745 | 746 | void setFifoThreshold(bool TxStartCondition, uint8_t FifoThreshold){ 747 | this->writeRegister(RFM69_FIFO_THRESH, TxStartCondition + (FifoThreshold % 128));}; 748 | /* 749 | Start transmission on a certain FIFO situation: 750 | TxStartCondition (implicit: RFM69_THRESHOLD_CONDITION_FIFOLEVEL) 751 | Can be set to start when atleast 1 byte is available by RFM69_THRESHOLD_CONDITION_NOT_EMPTY 752 | FifoThreshold (defaults 0b1111), number of bytes in fifo on which to start transmission if RFM69_THRESHOLD_CONDITION_FIFOLEVEL 753 | */ 754 | 755 | void setAutoMode(uint8_t enter, uint8_t exit, uint8_t intermediate_mode){ 756 | this->writeRegister(RFM69_AUTO_MODES, enter+exit+intermediate_mode);}; 757 | /* 758 | The enter and exit conditions cannot be used independently of each 759 | other i.e. both should be enabled at the same time. 760 | 761 | The initial and the final state is the one configured in Mode in RegOpMode. 762 | The initial & final states can be different by configuring the modes 763 | register while the module is in intermediate mode. 764 | The pictorial description of the auto modes is shown below. 765 | 766 | Initial state defined Intermediate State Final state defined 767 | By Mode in RegOpMode -------------------- By Mode in RegOpMode 768 | | | 769 | | | 770 | ---------------------------- ---------- 771 | EnterCondition ^ ExitCondition ^ 772 | 773 | Where Enter condition is one of: 774 | RFM69_AUTOMODE_ENTER_NONE_AUTOMODES_OFF 775 | RFM69_AUTOMODE_ENTER_RISING_FIFONOTEMPTY 776 | RFM69_AUTOMODE_ENTER_RISING_FIFOLEVEL 777 | RFM69_AUTOMODE_ENTER_RISING_CRCOK 778 | RFM69_AUTOMODE_ENTER_RISING_PAYLOADREADY 779 | RFM69_AUTOMODE_ENTER_RISING_SYNCADDRESS 780 | RFM69_AUTOMODE_ENTER_RISING_PACKETSENT 781 | RFM69_AUTOMODE_ENTER_FALLING_FIFONOTEMPTY (I.E. FIFOEMPTY) 782 | 783 | Where Exit condition is one of: 784 | RFM69_AUTOMODE_EXIT_NONE_AUTOMODES_OFF 785 | RFM69_AUTOMODE_EXIT_FALLING_FIFONOTEMPTY (I.E._FIFOEMPTY) 786 | RFM69_AUTOMODE_EXIT_RISING_FIFOLEVEL_OR_TIMEOUT 787 | RFM69_AUTOMODE_EXIT_RISING_CRCOK_OR_TIMEOUT 788 | RFM69_AUTOMODE_EXIT_RISING_PAYLOADREADY_OR_TIMEOUT 789 | RFM69_AUTOMODE_EXIT_RISING_SYNCADDRESS_OR_TIMEOUT 790 | RFM69_AUTOMODE_EXIT_RISING_PACKETSENT 791 | RFM69_AUTOMODE_EXIT_RISING_TIMEOUT 792 | 793 | And Intermediate state: 794 | RFM69_AUTOMODE_INTERMEDIATEMODE_SLEEP 795 | RFM69_AUTOMODE_INTERMEDIATEMODE_STANDBY 796 | RFM69_AUTOMODE_INTERMEDIATEMODE_RECEIVER 797 | RFM69_AUTOMODE_INTERMEDIATEMODE_TRANSMITTER 798 | 799 | */ 800 | 801 | 802 | void setAesKey(void* buffer, uint8_t len){this->writeMultiple(RFM69_AES_KEY1, buffer, len);}; 803 | /* 804 | Takes 16 bytes as AES-128 key. 805 | Cipher mode is ECB, that means that the 16 byte blocks are treated 806 | independently of each other. 807 | 808 | Defaults to [0, ..., 0], left zeros if not overwritten. 809 | 810 | */ 811 | 812 | //##################################################################### 813 | // Temperature register. 814 | //##################################################################### 815 | 816 | void startTempMeasure(){this->writeRegister(RFM69_TEMP1, 1<<3);}; 817 | /* 818 | Triggers temperature measurement. 819 | */ 820 | bool completedTempMeasure(){return !(this->readRegister(RFM69_TEMP1) & (1<<2));}; 821 | /* 822 | The receiver can not be used while measuring temperature. 823 | 824 | */ 825 | uint8_t getTempValue(){return this->readRegister(RFM69_TEMP2);}; 826 | /* 827 | Returns Temperature value as -1 deg C, needs calibration. 828 | */ 829 | 830 | 831 | //##################################################################### 832 | // Test registers. 833 | //##################################################################### 834 | 835 | void setSensitivityBoost(bool enable){this->writeRegister(RFM69_TEST_LNA, (enable) ? 0x2D : 0x1B);}; 836 | /* 837 | High sensitivity or normal sensitivity mode: 838 | 0x1B: Normal mode (default) 839 | 0x2D: High sensitivity mode 840 | 841 | */ 842 | 843 | void setPa13dBm1(bool enable){this->writeRegister(RFM69_TEST_PA1, (enable) ? 0x5D : 0x55);}; 844 | /* 845 | Set to 0x5D for +13 dBm operation 846 | 0x55: Normal mode and Rx mode (default) 847 | 0x5D: +13 dBm mode 848 | Revert to 0x55 when receiving or using PA0 849 | */ 850 | 851 | void setPa13dBm2(bool enable){this->writeRegister(RFM69_TEST_PA2, (enable) ? 0x7C : 0x70);}; 852 | /* 853 | Set to 0x7C for +13 dBm operation 854 | 0x70: Normal mode and Rx mode (default) 855 | 0x7C: +13 dBm mode 856 | Revert to 0x70 when receiving or using PA0 857 | */ 858 | 859 | void setContinuousDagc(uint8_t fading_margin){this->writeRegister(RFM69_TEST_DAGC, fading_margin);}; 860 | /* 861 | Fading Margin Improvement, refer to 3.4.4 862 | RFM69_CONTINUOUS_DAGC_NORMAL (default): 863 | Normal mode 864 | RFM69_CONTINUOUS_DAGC_IMPROVED_AFCLOWBETAON: 865 | Improved margin, use if AfcLowBetaOn=1 866 | RFM69_CONTINUOUS_DAGC_IMPROVED_AFCLOWBETAOFF (recommended) 867 | Improved margin, use if AfcLowBetaOn=0 868 | */ 869 | 870 | void setLowBetaAfcOffset(uint8_t LowBetaAfcOffset){this->writeRegister(RFM69_TEST_AFC, LowBetaAfcOffset);}; 871 | /* 872 | AFC offset set for low modulation index systems, used if 873 | AfcLowBetaOn=1. 874 | Offset = LowBetaAfcOffset x 488 Hz 875 | Default = 0. 876 | The user should ensure that the programmed offset exceeds the 877 | DC canceller’s cutoff frequency, set through DccFreqAfc in RegAfcBw. 878 | 879 | */ 880 | 881 | 882 | }; 883 | 884 | 885 | 886 | 887 | 888 | 889 | 890 | //BARE_RFM69_H 891 | #endif 892 | -------------------------------------------------------------------------------- /bareRFM69_const.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Ivor Wanders 3 | * MIT License, see the LICENSE.md file in the root folder. 4 | */ 5 | 6 | 7 | #define RFM69_FIFO 0x00 8 | #define RFM69_OPMODE 0x01 9 | #define RFM69_DATA_MODUL 0x02 10 | 11 | #define RFM69_BITRATE_MSB 0x03 12 | #define RFM69_BITRATE_LSB 0x04 13 | 14 | #define RFM69_FDEV_MSB 0x05 15 | #define RFM69_FDEV_LSB 0x06 16 | 17 | #define RFM69_FRF_MSB 0x07 18 | #define RFM69_FRF_MID 0x08 19 | #define RFM69_FRF_LSB 0x09 20 | 21 | #define RFM69_OSC1 0x0A 22 | #define RFM69_AFC_CTRL 0x0B 23 | 24 | #define RFM69_LISTEN1 0x0D 25 | #define RFM69_LISTEN2 0x0E 26 | #define RFM69_LISTEN3 0x0F 27 | 28 | #define RFM69_VERSION 0x10 29 | 30 | #define RFM69_PA_LEVEL 0x11 31 | #define RFM69_PA_RAMP 0x12 32 | 33 | #define RFM69_OCP 0x13 34 | 35 | #define RFM69_LNA 0x18 36 | #define RFM69_RX_BW 0x19 37 | #define RFM69_AFC_BW 0x1A 38 | 39 | #define RFM69_OOK_PEAK 0x1B 40 | #define RFM69_OOK_AVG 0x1C 41 | #define RFM69_OOK_FIX 0x1D 42 | 43 | #define RFM69_AFC_FEI 0x1E 44 | #define RFM69_AFC_MSB 0x1F 45 | #define RFM69_AFC_LSB 0x20 46 | 47 | #define RFM69_FEI_MSB 0x21 48 | #define RFM69_FEI_LSB 0x22 49 | 50 | #define RFM69_RSSI_CONFIG 0x23 51 | #define RFM69_RSSI_VALUE 0x24 52 | 53 | #define RFM69_DIO_MAPPING1 0x25 54 | #define RFM69_DIO_MAPPING2 0x26 55 | 56 | #define RFM69_IRQ_FLAGS1 0x27 57 | #define RFM69_IRQ_FLAGS2 0x28 58 | 59 | #define RFM69_RSSI_THRESH 0x29 60 | 61 | 62 | #define RFM69_RX_TIMEOUT1 0x2A 63 | #define RFM69_RX_TIMEOUT2 0x2B 64 | 65 | #define RFM69_PREAMBLE_MSB 0x2C 66 | #define RFM69_PREAMBLE_LSB 0x2D 67 | 68 | #define RFM69_SYNC_CONFIG 0x2E 69 | 70 | #define RFM69_SYNC_VALUE1 0x2F 71 | #define RFM69_SYNC_VALUE2 0x30 72 | #define RFM69_SYNC_VALUE3 0x31 73 | #define RFM69_SYNC_VALUE4 0x32 74 | #define RFM69_SYNC_VALUE5 0x33 75 | #define RFM69_SYNC_VALUE6 0x34 76 | #define RFM69_SYNC_VALUE7 0x35 77 | #define RFM69_SYNC_VALUE8 0x36 78 | 79 | #define RFM69_PACKET_CONFIG1 0x37 80 | #define RFM69_PAYLOAD_LENGTH 0x38 81 | 82 | #define RFM69_NODE_ADRESS 0x39 83 | #define RFM69_BROADCAST_ADRESS 0x3A 84 | 85 | #define RFM69_AUTO_MODES 0x3B 86 | 87 | #define RFM69_FIFO_THRESH 0x3C 88 | 89 | #define RFM69_PACKET_CONFIG2 0x3D 90 | 91 | #define RFM69_AES_KEY1 0x3E 92 | #define RFM69_AES_KEY2 0x3F 93 | #define RFM69_AES_KEY3 0x40 94 | #define RFM69_AES_KEY4 0x41 95 | #define RFM69_AES_KEY5 0x42 96 | #define RFM69_AES_KEY6 0x43 97 | #define RFM69_AES_KEY7 0x44 98 | #define RFM69_AES_KEY8 0x45 99 | #define RFM69_AES_KEY9 0x46 100 | #define RFM69_AES_KEY10 0x47 101 | #define RFM69_AES_KEY11 0x48 102 | #define RFM69_AES_KEY12 0x49 103 | #define RFM69_AES_KEY13 0x4A 104 | #define RFM69_AES_KEY14 0x4B 105 | #define RFM69_AES_KEY15 0x4C 106 | #define RFM69_AES_KEY16 0x4D 107 | 108 | #define RFM69_TEMP1 0x4E 109 | #define RFM69_TEMP2 0x4F 110 | 111 | #define RFM69_TEST_LNA 0x58 112 | #define RFM69_TEST_PA1 0x5A 113 | #define RFM69_TEST_PA2 0x5C 114 | #define RFM69_TEST_DAGC 0x6F 115 | #define RFM69_TEST_AFC 0x71 116 | 117 | #define RFM69_WRITE_REG_MASK 0x80 118 | #define RFM69_READ_REG_MASK 0x7F 119 | 120 | 121 | // defines for various bitshifts and constants, per register. 122 | 123 | 124 | #define RFM69_MODE_SLEEP 0b00000 125 | #define RFM69_MODE_STANDBY 0b00100 126 | #define RFM69_MODE_FREQ_SYNTH 0b01000 127 | #define RFM69_MODE_TRANSMITTER 0b01100 128 | #define RFM69_MODE_RECEIVER 0b10000 129 | 130 | 131 | #define RFM69_MODE_SEQUENCER_OFF (1<<7) 132 | #define RFM69_MODE_SEQUENCER_ON 0 133 | #define RFM69_MODE_LISTEN_ON (1<<6) 134 | #define RFM69_MODE_LISTEN_OFF 0 135 | #define RFM69_MODE_LISTEN_ABORT (1<<5) 136 | 137 | 138 | #define RFM69_DATAMODUL_PROCESSING_PACKET 0b00 139 | #define RFM69_DATAMODUL_PROCESSING_CONT_SYNCRHONISER 0b10 140 | #define RFM69_DATAMODUL_PROCESSING_CONT 0b11 141 | #define RFM69_DATAMODUL_OOK true 142 | #define RFM69_DATAMODUL_FSK false 143 | 144 | #define RFM69_DATAMODUL_SHAPING_GFSK_NONE 0 145 | #define RFM69_DATAMODUL_SHAPING_GFSK_BT_1_0 0b01 146 | #define RFM69_DATAMODUL_SHAPING_GFSK_BT_0_5 0b10 147 | #define RFM69_DATAMODUL_SHAPING_GFSK_BT_0_3 0b11 148 | 149 | #define RFM69_DATAMODUL_SHAPING_OOK_NONE 0 150 | #define RFM69_DATAMODUL_SHAPING_OOK_FCUTOFF_BR 0b01 151 | #define RFM69_DATAMODUL_SHAPING_OOK_FCUTOFF_2BR 0b10 152 | 153 | #define RFM69_PA_LEVEL_PA0_ON 0b10000000 154 | #define RFM69_PA_LEVEL_PA1_ON 0b01000000 155 | #define RFM69_PA_LEVEL_PA2_ON 0b00100000 156 | 157 | #define RFM69_PA_RAMP_3400US 0b0000 158 | #define RFM69_PA_RAMP_2000US 0b0001 159 | #define RFM69_PA_RAMP_1000US 0b0010 160 | #define RFM69_PA_RAMP_500US 0b0011 161 | #define RFM69_PA_RAMP_250US 0b0100 162 | #define RFM69_PA_RAMP_125US 0b0101 163 | #define RFM69_PA_RAMP_100US 0b0110 164 | #define RFM69_PA_RAMP_62US 0b0111 165 | #define RFM69_PA_RAMP_50US 0b1000 166 | #define RFM69_PA_RAMP_40US 0b1001 167 | #define RFM69_PA_RAMP_31US 0b1010 168 | #define RFM69_PA_RAMP_25US 0b1011 169 | #define RFM69_PA_RAMP_20US 0b1100 170 | #define RFM69_PA_RAMP_15US 0b1101 171 | #define RFM69_PA_RAMP_12US 0b1110 172 | #define RFM69_PA_RAMP_10US 0b1111 173 | 174 | #define RFM69_PACKET_CONFIG_LENGTH_FIXED 0b0 175 | #define RFM69_PACKET_CONFIG_LENGTH_VARIABLE (1<<7) 176 | 177 | #define RFM69_PACKET_CONFIG_DC_FREE_NONE (0b00<<5) 178 | #define RFM69_PACKET_CONFIG_DC_FREE_MANCHESTER (0b01<<5) 179 | #define RFM69_PACKET_CONFIG_DC_FREE_WHITENING (0b10<<5) 180 | #define RFM69_PACKET_CONFIG_CRC_ON (1<<4) 181 | #define RFM69_PACKET_CONFIG_CRC_OFF 0 182 | #define RFM69_PACKET_CONFIG_CRC_FAIL_KEEP (1<<3) 183 | #define RFM69_PACKET_CONFIG_CRC_FAIL_DISCARD 0 184 | #define RFM69_PACKET_CONFIG_ADDRESS_FILTER_NONE 0 185 | #define RFM69_PACKET_CONFIG_ADDRESS_FILTER_NODE (0b01<<1) 186 | #define RFM69_PACKET_CONFIG_ADDRESS_FILTER_NODE_BROADCAST (0b10<<1) 187 | 188 | #define RFM69_THRESHOLD_CONDITION_NOT_EMPTY (1<<7) 189 | #define RFM69_THRESHOLD_CONDITION_FIFOLEVEL 0 190 | 191 | #define RFM69_AFC_CTRL_STANDARD 0 192 | #define RFM69_AFC_CTRL_IMPROVED 1 193 | 194 | 195 | #define RFM69_LISTEN_RESOL_IDLE_64US (0b01<<6) 196 | #define RFM69_LISTEN_RESOL_IDLE_4_1MS (0b10<<6) 197 | #define RFM69_LISTEN_RESOL_IDLE_262MS (0b11<<6) 198 | 199 | #define RFM69_LISTEN_RESOL_RX_64US (0b01<<4) 200 | #define RFM69_LISTEN_RESOL_RX_4_1MS (0b10<<4) 201 | #define RFM69_LISTEN_RESOL_RX_262MS (0b11<<4) 202 | #define RFM69_LISTEN_CRITERIA_RSSI (0 << 3) 203 | #define RFM69_LISTEN_CRITERIA_RSSI_SYNC (1 << 3) 204 | #define RFM69_LISTEN_END_STAY_RX_LISTEN_STOP (0b00 << 1) 205 | #define RFM69_LISTEN_END_RX_UNTIL_LISTEN_STOP (0b01 << 1) 206 | #define RFM69_LISTEN_END_RX_UNTIL_LISTEN_RESUME (0b10 << 1) 207 | 208 | 209 | #define RFM69_AUTOMODE_ENTER_NONE_AUTOMODES_OFF (0b000<<5) 210 | #define RFM69_AUTOMODE_ENTER_RISING_FIFONOTEMPTY (0b001<<5) 211 | #define RFM69_AUTOMODE_ENTER_RISING_FIFOLEVEL (0b010<<5) 212 | #define RFM69_AUTOMODE_ENTER_RISING_CRCOK (0b011<<5) 213 | #define RFM69_AUTOMODE_ENTER_RISING_PAYLOADREADY (0b100<<5) 214 | #define RFM69_AUTOMODE_ENTER_RISING_SYNCADDRESS (0b101<<5) 215 | #define RFM69_AUTOMODE_ENTER_RISING_PACKETSENT (0b110<<5) 216 | #define RFM69_AUTOMODE_ENTER_FALLING_FIFONOTEMPTY (0b111<<5) 217 | //(I.E.FIFOEMPTY) 218 | 219 | 220 | #define RFM69_AUTOMODE_EXIT_NONE_AUTOMODES_OFF (0b000<<2) 221 | #define RFM69_AUTOMODE_EXIT_FALLING_FIFONOTEMPTY (0b001<<2) 222 | //(I.E._FIFOEMPTY) 223 | #define RFM69_AUTOMODE_EXIT_RISING_FIFOLEVEL_OR_TIMEOUT (0b010<<2) 224 | #define RFM69_AUTOMODE_EXIT_RISING_CRCOK_OR_TIMEOUT (0b011<<2) 225 | #define RFM69_AUTOMODE_EXIT_RISING_PAYLOADREADY_OR_TIMEOUT (0b100<<2) 226 | #define RFM69_AUTOMODE_EXIT_RISING_SYNCADDRESS_OR_TIMEOUT (0b101<<2) 227 | #define RFM69_AUTOMODE_EXIT_RISING_PACKETSENT (0b110<<2) 228 | #define RFM69_AUTOMODE_EXIT_RISING_TIMEOUT (0b111<<2) 229 | 230 | #define RFM69_AUTOMODE_INTERMEDIATEMODE_SLEEP (0b00) 231 | #define RFM69_AUTOMODE_INTERMEDIATEMODE_STANDBY (0b01) 232 | #define RFM69_AUTOMODE_INTERMEDIATEMODE_RECEIVER (0b10) 233 | #define RFM69_AUTOMODE_INTERMEDIATEMODE_TRANSMITTER (0b11) 234 | 235 | #define RFM69_DIO_0_MAP_SHIFT 6 236 | #define RFM69_DIO_1_MAP_SHIFT 4 237 | #define RFM69_DIO_2_MAP_SHIFT 2 238 | #define RFM69_DIO_3_MAP_SHIFT 0 239 | 240 | #define RFM69_PACKET_DIO_0_RX_CRC_OK (0b00 << RFM69_DIO_0_MAP_SHIFT) 241 | #define RFM69_PACKET_DIO_0_RX_PAYLOAD_READY (0b01 << RFM69_DIO_0_MAP_SHIFT) 242 | #define RFM69_PACKET_DIO_0_RX_SYNC_ADDRESS (0b10 << RFM69_DIO_0_MAP_SHIFT) 243 | #define RFM69_PACKET_DIO_0_RX_RSII (0b11 << RFM69_DIO_0_MAP_SHIFT) 244 | 245 | #define RFM69_PACKET_DIO_0_TX_PACKET_SENT (0b00 << RFM69_DIO_0_MAP_SHIFT) 246 | #define RFM69_PACKET_DIO_0_TX_TX_READY (0b01 << RFM69_DIO_0_MAP_SHIFT) 247 | #define RFM69_PACKET_DIO_0_TX_PLL (0b11 << RFM69_DIO_0_MAP_SHIFT) 248 | 249 | #define RFM69_PACKET_DIO_1_FIFO_LEVEL (0b00 << RFM69_DIO_1_MAP_SHIFT) 250 | #define RFM69_PACKET_DIO_1_FIFO_FULL (0b01 << RFM69_DIO_1_MAP_SHIFT) 251 | #define RFM69_PACKET_DIO_1_FIFO_NOT_EMPTY (0b10 << RFM69_DIO_1_MAP_SHIFT) 252 | 253 | #define RFM69_PACKET_DIO_2_FIFO_NOT_EMPTY (0b00 << RFM69_DIO_2_MAP_SHIFT) 254 | #define RFM69_PACKET_DIO_2_AUTOMODE (0b11 << RFM69_DIO_2_MAP_SHIFT) 255 | 256 | #define RFM69_PACKET_DIO_3_FIFO_FULL (0b00 << RFM69_DIO_3_MAP_SHIFT) 257 | 258 | 259 | // the above DIO constants are not extensive! 260 | // only the ones which were deemed useful in packet mode are listed. 261 | // See page 48 of the datasheet. 262 | 263 | 264 | 265 | 266 | 267 | #define RFM69_IRQ1_MODEREADY (1<<7) 268 | #define RFM69_IRQ1_RXREADY (1<<6) 269 | #define RFM69_IRQ1_TXREADY (1<<5) 270 | #define RFM69_IRQ1_PLLLOCK (1<<4) 271 | #define RFM69_IRQ1_RSSI (1<<3) 272 | #define RFM69_IRQ1_TIMEOUT (1<<2) 273 | #define RFM69_IRQ1_AUTOMODE (1<<1) 274 | #define RFM69_IRQ1_SYNCADDRESSMATCH (1<< 0) 275 | 276 | 277 | #define RFM69_IRQ2_FIFOFULL (1<<7) 278 | #define RFM69_IRQ2_FIFONOTEMPTY (1<<6) 279 | #define RFM69_IRQ2_FIFOLEVEL (1<<5) 280 | #define RFM69_IRQ2_FIFOOVERRUN (1<<4) 281 | #define RFM69_IRQ2_PACKETSENT (1<<3) 282 | #define RFM69_IRQ2_PAYLOADREADY (1<< 2) 283 | #define RFM69_IRQ2_CRCOK (1<<1) 284 | 285 | #define RFM69_LNA_GAIN_AGC_LOOP 0b000 286 | #define RFM69_LNA_GAIN_0DB 0b001 287 | #define RFM69_LNA_GAIN_6DB 0b010 288 | #define RFM69_LNA_GAIN_12DB 0b011 289 | #define RFM69_LNA_GAIN_24DB 0b100 290 | #define RFM69_LNA_GAIN_36DB 0b101 291 | #define RFM69_LNA_GAIN_48DB 0b110 292 | #define RFM69_LNA_IMP_50OHM (0<<7) 293 | #define RFM69_LNA_IMP_200OHM (1<<7) 294 | 295 | #define RFM69_CONTINUOUS_DAGC_NORMAL 0x00 296 | #define RFM69_CONTINUOUS_DAGC_IMPROVED_AFCLOWBETAON 0x20 297 | #define RFM69_CONTINUOUS_DAGC_IMPROVED_AFCLOWBETAOFF 0x30 298 | 299 | 300 | -------------------------------------------------------------------------------- /examples/BusyMan/BusyMan.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Ivor Wanders 3 | * MIT License, see the LICENSE.md file in the root folder. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | // slave select pin. 10 | #define SLAVE_SELECT_PIN 10 11 | 12 | // connected to the reset pin of the RFM69. 13 | #define RESET_PIN 23 14 | 15 | // tie this pin down on the receiver. 16 | #define SENDER_DETECT_PIN 15 17 | 18 | // Pin DIO 2 on the RFM69 is attached to this digital pin. 19 | // Pin should have interrupt capability. 20 | #define DIO2_PIN 0 21 | 22 | /* 23 | This example shows the buffering in action. 24 | */ 25 | 26 | plainRFM69 rfm = plainRFM69(SLAVE_SELECT_PIN); 27 | 28 | void sender(){ 29 | 30 | uint32_t start_time = millis(); 31 | 32 | uint32_t counter = 0; // the counter which we are going to send. 33 | 34 | while(true){ 35 | if (!rfm.canSend()){ 36 | continue; // sending is not possible, already sending. 37 | } 38 | 39 | if ((millis() - start_time) > 500){ // every 500 ms. 40 | start_time = millis(); 41 | 42 | // be a little bit verbose. 43 | Serial.print("Send:");Serial.println(counter); 44 | 45 | // send the number of bytes equal to that set with setPacketLength. 46 | // read those bytes from memory where counter starts. 47 | rfm.send(&counter); 48 | 49 | counter++; // increase the counter. 50 | } 51 | 52 | } 53 | } 54 | 55 | void receiver(){ 56 | uint32_t counter = 0; // to count the messages. 57 | 58 | while(true){ 59 | Serial.print("We "); 60 | delay(100); 61 | Serial.print("are "); 62 | delay(200); 63 | Serial.print("busy, "); 64 | delay(500); 65 | Serial.print("doing "); 66 | delay(500); 67 | Serial.print("important "); 68 | delay(500); 69 | Serial.println("things!"); 70 | delay(1000); 71 | 72 | if (rfm.available()){ 73 | Serial.println("Oh look, there are packets!"); 74 | } 75 | uint8_t packets = 0; 76 | while(rfm.available()){ // for all available messages: 77 | packets++; 78 | uint32_t received_count = 0; // temporary for the new counter. 79 | uint8_t len = rfm.read(&received_count); // read the packet into the new_counter. 80 | 81 | // print verbose output. 82 | Serial.print("Packet ("); Serial.print(len); Serial.print("): "); Serial.println(received_count); 83 | 84 | if (counter+1 != received_count){ 85 | // if the increment is larger than one, we lost one or more packets. 86 | Serial.println("Packetloss detected!"); 87 | } 88 | 89 | // assign the received counter to our counter. 90 | counter = received_count; 91 | } 92 | if (packets > 0){ 93 | Serial.print("We had "); Serial.print(packets); Serial.println(" packets in the buffer.\n"); 94 | } 95 | } 96 | } 97 | 98 | void interrupt_RFM(){ 99 | rfm.poll(); // in the interrupt, call the poll function. 100 | } 101 | 102 | void setup(){ 103 | Serial.begin(9600); 104 | SPI.begin(); 105 | 106 | bareRFM69::reset(RESET_PIN); // sent the RFM69 a hard-reset. 107 | 108 | rfm.setRecommended(); // set recommended paramters in RFM69. 109 | rfm.setPacketType(false, false); // set the used packet type. 110 | 111 | rfm.setBufferSize(10); // set the internal buffer size. 112 | rfm.setPacketLength(4); // set the packet length. 113 | rfm.setFrequency((uint32_t) 434*1000*1000); // set the frequency. 114 | 115 | // baudrate is default, 4800 bps now. 116 | 117 | rfm.receive(); 118 | // set it to receiving mode. 119 | 120 | /* 121 | setup up interrupts such that we don't have to call poll() in a loop. 122 | */ 123 | 124 | // tell the RFM to represent whether we are in automode on DIO 2. 125 | rfm.setDioMapping1(RFM69_PACKET_DIO_2_AUTOMODE); 126 | 127 | // set pinmode to input. 128 | pinMode(DIO2_PIN, INPUT); 129 | 130 | // Tell the SPI library we're going to use the SPI bus from an interrupt. 131 | SPI.usingInterrupt(DIO2_PIN); 132 | 133 | // hook our interrupt function to any edge. 134 | attachInterrupt(DIO2_PIN, interrupt_RFM, CHANGE); 135 | 136 | // start receiving. 137 | rfm.receive(); 138 | 139 | 140 | pinMode(SENDER_DETECT_PIN, INPUT_PULLUP); 141 | delay(5); 142 | } 143 | 144 | void loop(){ 145 | if (digitalRead(SENDER_DETECT_PIN) == LOW){ 146 | Serial.println("Going Receiver!"); 147 | receiver(); 148 | // this function never returns and contains an infinite loop. 149 | } else { 150 | Serial.println("Going sender!"); 151 | sender(); 152 | // idem. 153 | } 154 | } 155 | 156 | 157 | -------------------------------------------------------------------------------- /examples/Maximum_PingPong/Maximum_PingPong.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Ivor Wanders 3 | * MIT License, see the LICENSE.md file in the root folder. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | // slave select pin. 10 | #define SLAVE_SELECT_PIN 10 11 | 12 | // connected to the reset pin of the RFM69. 13 | #define RESET_PIN 23 14 | 15 | // tie this pin down on the receiver. 16 | #define SENDER_DETECT_PIN 15 17 | 18 | // Pin DIO 2 on the RFM69 is attached to this digital pin. 19 | // Pin should have interrupt capability. 20 | #define DIO2_PIN 0 21 | 22 | /* 23 | In this example, one node sends 'pings' the other replies to this by 24 | returning the message payload. 25 | 26 | Addressing is used in this example, as is variable payload length. However, 27 | the message length is always taken as 64 bytes. 28 | 29 | The messages are pingponged as fast as possible. 30 | */ 31 | 32 | plainRFM69 rfm = plainRFM69(SLAVE_SELECT_PIN); 33 | 34 | 35 | void sender(){ 36 | rfm.setNodeAddress(0x02); 37 | 38 | uint8_t rx_buffer[65] = {0}; 39 | uint32_t* rx_counter = (uint32_t*) &(rx_buffer[1]); 40 | 41 | uint32_t last_transmit_time = micros(); 42 | 43 | uint32_t start_time = millis(); 44 | 45 | uint8_t tx_buffer[64] = {0}; 46 | uint32_t* counter = (uint32_t*) &tx_buffer; 47 | 48 | 49 | bool are_waiting_for_receipt = false; 50 | 51 | uint32_t successful_pingpongs = 0; 52 | uint32_t timeout_pongs = 0; 53 | 54 | uint32_t display_time = millis(); // keep track of time to print statistics. 55 | 56 | uint32_t total_pingpong_time = 0; 57 | uint32_t incorrect_pingpongs = 0; 58 | 59 | while(true){ 60 | 61 | if ((millis() - display_time) > 2000){ 62 | Serial.print("Successful pingpongs: "); Serial.println(successful_pingpongs); 63 | Serial.print("Incorrect pingpongs: "); Serial.println(incorrect_pingpongs); 64 | Serial.print("Timeout pingpongs: "); Serial.println(timeout_pongs); 65 | // Serial.print("Sum of RTT: "); Serial.println(total_pingpong_time); 66 | Serial.print("Successful / second: "); Serial.println(successful_pingpongs / ((millis() - start_time)/1000)); 67 | Serial.print("Average ping (uSec): "); Serial.println((total_pingpong_time / successful_pingpongs)); 68 | display_time = millis(); 69 | } 70 | 71 | while (rfm.available()){ // check available messages. 72 | 73 | // if we are waiting for messages. 74 | if (are_waiting_for_receipt){ 75 | 76 | // read the buffer. 77 | uint8_t len = rfm.read(&rx_buffer); 78 | if (len != 65){ // 64 payload, 1 address byte. 79 | Serial.print("Message length is wrong:"); 80 | Serial.println(len); 81 | incorrect_pingpongs++; 82 | are_waiting_for_receipt = false; 83 | continue; 84 | 85 | } 86 | 87 | uint32_t* this_counter = (uint32_t*) &(rx_buffer[1]); 88 | for (int i=1; i < (64/4); i++){ 89 | if (*(this_counter) != *((uint32_t*) &(rx_buffer[1+i*4]))){ 90 | Serial.println("Message not correct!"); 91 | Serial.print("this counter:");Serial.println(*this_counter); 92 | Serial.print("part:");Serial.println(*((uint32_t*) &(rx_buffer[1+i*4]))); 93 | incorrect_pingpongs++; 94 | are_waiting_for_receipt = false; 95 | continue; 96 | } 97 | } 98 | 99 | // check if the counter matches what we expect to get back. 100 | if (*rx_counter == *counter){ 101 | are_waiting_for_receipt = false; 102 | 103 | total_pingpong_time += (micros() - last_transmit_time); 104 | successful_pingpongs++; 105 | } else { 106 | Serial.println("Received something while waiting that didn't match."); 107 | } 108 | 109 | } else { 110 | rfm.read(&rx_buffer); // read it, don't do anything with it. 111 | Serial.print("Received something while not waiting: ");Serial.println(*rx_counter); 112 | } 113 | } 114 | 115 | 116 | // if we have been waiting for receipt for over 100 ms 117 | if (((micros() - last_transmit_time) > 100*1000) and (are_waiting_for_receipt)){ // if not received after 100 ms 118 | are_waiting_for_receipt = false; 119 | timeout_pongs++; 120 | Serial.print("Timeout on: "); Serial.println(*counter); 121 | 122 | } 123 | 124 | if (are_waiting_for_receipt == false){ 125 | if (!rfm.canSend()){ 126 | continue; // sending is not possible, already sending. 127 | } 128 | // we're not waiting, sent a message. 129 | last_transmit_time = micros(); 130 | 131 | 132 | (*counter)++; // increase the counter. 133 | 134 | 135 | 136 | for (uint8_t i=1; i < (64/4); i++){ 137 | uint32_t* this_counter = (uint32_t*) &(tx_buffer[i*4]); 138 | *this_counter = *counter; 139 | // fill the entire message with this counter, repeating every 4 bytes. 140 | } 141 | 142 | rfm.sendAddressedVariable(0x01, &tx_buffer, 64); 143 | are_waiting_for_receipt = true; 144 | // Serial.print("Send:");Serial.println(counter); 145 | // delay(100); 146 | 147 | 148 | } 149 | } 150 | } 151 | 152 | void receiver(){ 153 | rfm.setNodeAddress(0x01); 154 | 155 | uint8_t buffer[65]; 156 | uint32_t* received_counter = (uint32_t*) &(buffer[1]); 157 | 158 | while(true){ // do forever 159 | 160 | while(rfm.available()){ // for all available messages: 161 | 162 | // receive ping. 163 | uint8_t len = rfm.read(&buffer); // read the packet into the new_counter. 164 | 165 | Serial.print("Addressed to: "); Serial.print(buffer[0]); 166 | Serial.print("Length: "); Serial.print(len); 167 | Serial.print(" payload: "); Serial.println(*received_counter); 168 | 169 | // return pong. 170 | rfm.sendAddressedVariable(0x02, &(buffer[1]), 64); 171 | 172 | } 173 | } 174 | } 175 | 176 | 177 | 178 | void interrupt_RFM(){ 179 | rfm.poll(); // in the interrupt, call the poll function. 180 | } 181 | 182 | 183 | void setup(){ 184 | Serial.begin(9600); 185 | SPI.begin(); 186 | 187 | bareRFM69::reset(RESET_PIN); // sent the RFM69 a hard-reset. 188 | 189 | rfm.setRecommended(); // set recommended paramters in RFM69. 190 | rfm.setPacketType(true, true); // set the used packet type. 191 | 192 | rfm.setBufferSize(3); // set the internal buffer size. 193 | rfm.setPacketLength(64); // set the packet length. 194 | rfm.setFrequency((uint32_t) 434*1000*1000); // set the frequency. 195 | 196 | 197 | // rfm.baud9600(); 198 | 199 | rfm.baud300000(); // Set the baudRate to 300000 bps 200 | rfm.setPreambleSize(15); 201 | 202 | rfm.receive(); 203 | // set it to receiving mode. 204 | 205 | 206 | 207 | // tell the RFM to represent whether we are in automode on DIO 2. 208 | rfm.setDioMapping1(RFM69_PACKET_DIO_2_AUTOMODE); 209 | 210 | // set pinmode to input. 211 | pinMode(DIO2_PIN, INPUT); 212 | 213 | // Tell the SPI library we're going to use the SPI bus from an interrupt. 214 | SPI.usingInterrupt(DIO2_PIN); 215 | 216 | // hook our interrupt function to any edge. 217 | attachInterrupt(DIO2_PIN, interrupt_RFM, CHANGE); 218 | 219 | // start receiving. 220 | rfm.receive(); 221 | 222 | 223 | 224 | pinMode(SENDER_DETECT_PIN, INPUT_PULLUP); 225 | delay(5); 226 | 227 | 228 | 229 | } 230 | 231 | void loop(){ 232 | if (digitalRead(SENDER_DETECT_PIN) == LOW){ 233 | Serial.println("Going Receiver!"); 234 | receiver(); 235 | // this function never returns and contains an infinite loop. 236 | } else { 237 | Serial.println("Going sender!"); 238 | sender(); 239 | // idem. 240 | } 241 | } 242 | 243 | 244 | -------------------------------------------------------------------------------- /examples/Maximum_Speed/Maximum_Speed.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Ivor Wanders 3 | * MIT License, see the LICENSE.md file in the root folder. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | 10 | // slave select pin. 11 | #define SLAVE_SELECT_PIN 10 12 | 13 | // connected to the reset pin of the RFM69. 14 | #define RESET_PIN 23 15 | 16 | // tie this pin down on the receiver. 17 | #define SENDER_DETECT_PIN 15 18 | 19 | // Pin DIO 2 on the RFM69 is attached to this digital pin. 20 | // Pin should have interrupt capability. 21 | #define DIO2_PIN 0 22 | 23 | 24 | /* 25 | In this example messages are sent one directional. The receiver verifies 26 | these packets and checks whether the counter nicely increments. 27 | 28 | Be sure to start the receiver after the transmitter has been started. This 29 | results in correct measurements of number of packets per second. But always 30 | causes the packetloss count to be at least 1, as the first packet received 31 | from the sender does not contain counter=0. 32 | */ 33 | 34 | 35 | 36 | plainRFM69 rfm = plainRFM69(SLAVE_SELECT_PIN); 37 | 38 | void sender(){ 39 | uint8_t buffer[64] = {0}; 40 | 41 | uint32_t counter = 0; // set the counter to zero. 42 | 43 | while(true){ 44 | if (!rfm.canSend()){ 45 | continue; 46 | } 47 | 48 | for (int i=0; i < (64/4); i++){ 49 | uint32_t* this_counter = (uint32_t*) &(buffer[i*4]); 50 | *this_counter = counter; 51 | // fill the entire message with this counter, repeating every 4 bytes. 52 | } 53 | 54 | if ((counter % 100) == 0){ 55 | Serial.print("Send:");Serial.println(counter); 56 | } 57 | 58 | rfm.send(&buffer); 59 | counter++; // increase the counter. 60 | } 61 | } 62 | 63 | void receiver(){ 64 | 65 | uint8_t buffer[64] = {0}; // receive buffer. 66 | 67 | // use first four bytes of that buffer as uint32. 68 | uint32_t counter = 0; 69 | 70 | uint32_t oldcounter = 0; // keep track of the counter. 71 | uint32_t packetloss = 0; // counter for missed packets. 72 | 73 | // counter to indicate total number of received packets. 74 | uint32_t received_packets = 0; 75 | 76 | // time on which we start receiving. 77 | uint32_t start_time = millis(); 78 | 79 | while(true){ 80 | uint8_t packets=0; 81 | while(rfm.available()){ 82 | packets++; 83 | received_packets++; 84 | 85 | // on the receipt of 100 packets, print some information. 86 | if ((received_packets % 100 == 0)){ 87 | Serial.print("Total packets: "); Serial.println(received_packets); 88 | Serial.print("Packetloss count: "); Serial.println(packetloss); 89 | Serial.print("Per second: "); Serial.println(received_packets / ((millis() - start_time)/1000)); 90 | } 91 | 92 | uint8_t len = rfm.read(&buffer); // read the packet into the buffer. 93 | 94 | // check the entire message if it consists of the same 4 byte blocks. 95 | uint32_t* this_counter = (uint32_t*) &(buffer[0]); 96 | for (int i=1; i < (64/4); i++){ 97 | if (*(this_counter) != *((uint32_t*) &(buffer[i*4]))){ 98 | Serial.println("Message not correct!"); 99 | packetloss++; 100 | continue; 101 | } 102 | } 103 | 104 | if ((counter+1) != *this_counter){ 105 | Serial.println("Packetloss detected!"); 106 | packetloss++; 107 | } 108 | 109 | counter = *this_counter; 110 | } 111 | 112 | // we can add some delay here, to show that the internal buffering works. 113 | // delayMicroseconds(4000); 114 | // try uncommenting this delay, the if statement below will be true from time to time. 115 | if (packets > 1){ 116 | Serial.print("Packets this loop: "); Serial.println(packets); 117 | } 118 | 119 | } 120 | } 121 | 122 | void interrupt_RFM(){ 123 | rfm.poll(); // in the interrupt, call the poll function. 124 | } 125 | 126 | void setup(){ 127 | Serial.begin(9600); 128 | SPI.begin(); 129 | 130 | bareRFM69::reset(RESET_PIN); // sent the RFM69 a hard-reset. 131 | 132 | rfm.setRecommended(); 133 | rfm.setPacketType(false, false); 134 | 135 | // allocate buffer in the library for received packets 136 | rfm.setBufferSize(10); // allow buffering of up to ten packets. 137 | rfm.setPacketLength(64); // length of packets. 138 | 139 | rfm.setFrequency((uint32_t) 434*1000*1000); // set frequency to 434 MHz. 140 | rfm.baud300000(); // Set the baudRate to 300000 bps 141 | 142 | // At higher packetrates it is necessary to increase this in order to ensure 143 | // packet detection at the receiving side. 144 | rfm.setPreambleSize(15); 145 | 146 | /* 147 | setup up interrupts such that we don't have to call poll() in a loop. 148 | */ 149 | 150 | // tell the RFM to represent whether we are in automode on DIO 2. 151 | rfm.setDioMapping1(RFM69_PACKET_DIO_2_AUTOMODE); 152 | 153 | // set pinmode to input. 154 | pinMode(DIO2_PIN, INPUT); 155 | 156 | // Tell the SPI library we're going to use the SPI bus from an interrupt. 157 | SPI.usingInterrupt(DIO2_PIN); 158 | 159 | // hook our interrupt function to any edge. 160 | attachInterrupt(DIO2_PIN, interrupt_RFM, CHANGE); 161 | 162 | // start receiving. 163 | rfm.receive(); 164 | 165 | } 166 | 167 | void loop(){ 168 | // go receiver or sender depending on the SENDER_DETECT_PIN. 169 | pinMode(SENDER_DETECT_PIN, INPUT_PULLUP); 170 | delay(5); 171 | 172 | if (digitalRead(SENDER_DETECT_PIN) == LOW){ 173 | Serial.println("Starting receiver!"); 174 | receiver(); // we never return from this. 175 | } else { 176 | Serial.println("Starting sender!"); 177 | sender(); // we never return from this. 178 | } 179 | } 180 | 181 | -------------------------------------------------------------------------------- /examples/Minimal/Minimal.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Ivor Wanders 3 | * MIT License, see the LICENSE.md file in the root folder. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | // slave select pin. 10 | #define SLAVE_SELECT_PIN 10 11 | 12 | // connected to the reset pin of the RFM69. 13 | #define RESET_PIN 23 14 | 15 | // tie this pin down on the receiver. 16 | #define SENDER_DETECT_PIN 15 17 | 18 | /* 19 | This is very minimal, it does not use the interrupt. 20 | 21 | Using the interrupt is recommended. 22 | */ 23 | 24 | plainRFM69 rfm = plainRFM69(SLAVE_SELECT_PIN); 25 | 26 | void sender(){ 27 | 28 | uint32_t start_time = millis(); 29 | 30 | uint32_t counter = 0; // the counter which we are going to send. 31 | 32 | while(true){ 33 | rfm.poll(); // run poll as often as possible. 34 | 35 | if (!rfm.canSend()){ 36 | continue; // sending is not possible, already sending. 37 | } 38 | 39 | if ((millis() - start_time) > 500){ // every 500 ms. 40 | start_time = millis(); 41 | 42 | // be a little bit verbose. 43 | Serial.print("Send:");Serial.println(counter); 44 | 45 | // send the number of bytes equal to that set with setPacketLength. 46 | // read those bytes from memory where counter starts. 47 | rfm.send(&counter); 48 | 49 | counter++; // increase the counter. 50 | } 51 | 52 | } 53 | } 54 | 55 | void receiver(){ 56 | uint32_t counter = 0; // to count the messages. 57 | 58 | while(true){ 59 | 60 | rfm.poll(); // poll as often as possible. 61 | 62 | while(rfm.available()){ // for all available messages: 63 | 64 | uint32_t received_count = 0; // temporary for the new counter. 65 | uint8_t len = rfm.read(&received_count); // read the packet into the new_counter. 66 | 67 | // print verbose output. 68 | Serial.print("Packet ("); Serial.print(len); Serial.print("): "); Serial.println(received_count); 69 | 70 | if (counter+1 != received_count){ 71 | // if the increment is larger than one, we lost one or more packets. 72 | Serial.println("Packetloss detected!"); 73 | } 74 | 75 | // assign the received counter to our counter. 76 | counter = received_count; 77 | } 78 | } 79 | } 80 | 81 | void setup(){ 82 | Serial.begin(9600); 83 | SPI.begin(); 84 | 85 | bareRFM69::reset(RESET_PIN); // sent the RFM69 a hard-reset. 86 | 87 | rfm.setRecommended(); // set recommended paramters in RFM69. 88 | rfm.setPacketType(false, false); // set the used packet type. 89 | 90 | rfm.setBufferSize(2); // set the internal buffer size. 91 | rfm.setPacketLength(4); // set the packet length. 92 | rfm.setFrequency((uint32_t) 434*1000*1000); // set the frequency. 93 | 94 | // baudrate is default, 4800 bps now. 95 | 96 | rfm.receive(); 97 | // set it to receiving mode. 98 | 99 | pinMode(SENDER_DETECT_PIN, INPUT_PULLUP); 100 | delay(5); 101 | } 102 | 103 | void loop(){ 104 | if (digitalRead(SENDER_DETECT_PIN) == LOW){ 105 | Serial.println("Going Receiver!"); 106 | receiver(); 107 | // this function never returns and contains an infinite loop. 108 | } else { 109 | Serial.println("Going sender!"); 110 | sender(); 111 | // idem. 112 | } 113 | } 114 | 115 | 116 | -------------------------------------------------------------------------------- /examples/MinimalInterrupt/MinimalInterrupt.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Ivor Wanders 3 | * MIT License, see the LICENSE.md file in the root folder. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | // slave select pin. 10 | #define SLAVE_SELECT_PIN 10 11 | 12 | // connected to the reset pin of the RFM69. 13 | #define RESET_PIN 23 14 | 15 | // tie this pin down on the receiver. 16 | #define SENDER_DETECT_PIN 15 17 | 18 | // Pin DIO 2 on the RFM69 is attached to this digital pin. 19 | // Pin should have interrupt capability. 20 | #define DIO2_PIN 0 21 | 22 | /* 23 | This is a minimal example with the interrupt to call poll(). 24 | 25 | It transmits a 4 byte integer every 500 ms. 26 | 27 | */ 28 | 29 | plainRFM69 rfm = plainRFM69(SLAVE_SELECT_PIN); 30 | 31 | 32 | void sender(){ 33 | 34 | uint32_t start_time = millis(); 35 | 36 | uint32_t counter = 0; // the counter which we are going to send. 37 | 38 | while(true){ 39 | if (!rfm.canSend()){ 40 | continue; // sending is not possible, already sending. 41 | } 42 | 43 | if ((millis() - start_time) > 500){ // every 500 ms. 44 | start_time = millis(); 45 | 46 | // be a little bit verbose. 47 | Serial.print("Send:");Serial.println(counter); 48 | 49 | // send the number of bytes equal to that set with setPacketLength. 50 | // read those bytes from memory where counter starts. 51 | rfm.send(&counter); 52 | 53 | counter++; // increase the counter. 54 | } 55 | 56 | } 57 | } 58 | 59 | void receiver(){ 60 | uint32_t counter = 0; // to count the messages. 61 | 62 | while(true){ 63 | while(rfm.available()){ // for all available messages: 64 | 65 | uint32_t received_count = 0; // temporary for the new counter. 66 | uint8_t len = rfm.read(&received_count); // read the packet into the new_counter. 67 | 68 | // print verbose output. 69 | Serial.print("Packet ("); Serial.print(len); Serial.print("): "); Serial.println(received_count); 70 | 71 | if (counter+1 != received_count){ 72 | // if the increment is larger than one, we lost one or more packets. 73 | Serial.println("Packetloss detected!"); 74 | } 75 | 76 | // assign the received counter to our counter. 77 | counter = received_count; 78 | } 79 | } 80 | } 81 | 82 | void interrupt_RFM(){ 83 | rfm.poll(); // in the interrupt, call the poll function. 84 | } 85 | 86 | void setup(){ 87 | Serial.begin(9600); 88 | SPI.begin(); 89 | 90 | bareRFM69::reset(RESET_PIN); // sent the RFM69 a hard-reset. 91 | 92 | rfm.setRecommended(); // set recommended paramters in RFM69. 93 | rfm.setPacketType(false, false); // set the used packet type. 94 | 95 | rfm.setBufferSize(2); // set the internal buffer size. 96 | rfm.setPacketLength(4); // set the packet length. 97 | rfm.setFrequency((uint32_t) 434*1000*1000); // set the frequency. 98 | 99 | // baudrate is default, 4800 bps now. 100 | 101 | rfm.receive(); 102 | // set it to receiving mode. 103 | 104 | /* 105 | setup up interrupts such that we don't have to call poll() in a loop. 106 | */ 107 | 108 | // tell the RFM to represent whether we are in automode on DIO 2. 109 | rfm.setDioMapping1(RFM69_PACKET_DIO_2_AUTOMODE); 110 | 111 | // set pinmode to input. 112 | pinMode(DIO2_PIN, INPUT); 113 | 114 | // Tell the SPI library we're going to use the SPI bus from an interrupt. 115 | SPI.usingInterrupt(DIO2_PIN); 116 | 117 | // hook our interrupt function to any edge. 118 | attachInterrupt(DIO2_PIN, interrupt_RFM, CHANGE); 119 | 120 | // start receiving. 121 | rfm.receive(); 122 | 123 | 124 | pinMode(SENDER_DETECT_PIN, INPUT_PULLUP); 125 | delay(5); 126 | } 127 | 128 | void loop(){ 129 | if (digitalRead(SENDER_DETECT_PIN) == LOW){ 130 | Serial.println("Going Receiver!"); 131 | receiver(); 132 | // this function never returns and contains an infinite loop. 133 | } else { 134 | Serial.println("Going sender!"); 135 | sender(); 136 | // idem. 137 | } 138 | } 139 | 140 | 141 | -------------------------------------------------------------------------------- /examples/MinimalInterruptUno/MinimalInterruptUno.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ivor Wanders 3 | * MIT License, see the LICENSE.md file in the root folder. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | // slave select pin. 10 | #define SLAVE_SELECT_PIN 10 11 | 12 | // connected to the reset pin of the RFM69. 13 | #define RESET_PIN 8 14 | // Sometimes required? Perhaps registers in RFM don't properly initialize after 15 | // power is switched on and proper reset is required? 16 | 17 | // tie this pin down on the receiver. 18 | #define SENDER_DETECT_PIN A0 19 | 20 | // Pin DIO 2 on the RFM69 is attached to this digital pin. 21 | // Pin should have interrupt capability. 22 | #define DIO2_PIN 2 23 | 24 | // on Arduino UNO the the pin number is not what's given to attachInterrupt(..) 25 | #define INTERRUPT_NUMBER 0 26 | // On the Arduino UNO pin 2 has interrupt number 0. 27 | 28 | /* 29 | This is a minimal example with the interrupt to call poll(). 30 | 31 | It transmits a 4 byte integer every 500 ms. 32 | 33 | */ 34 | 35 | plainRFM69 rfm = plainRFM69(SLAVE_SELECT_PIN); 36 | 37 | 38 | void sender(){ 39 | 40 | uint32_t start_time = millis(); 41 | 42 | uint32_t counter = 0; // the counter which we are going to send. 43 | 44 | while(true){ 45 | if (!rfm.canSend()){ 46 | continue; // sending is not possible, already sending. 47 | } 48 | 49 | if ((millis() - start_time) > 500){ // every 500 ms. 50 | start_time = millis(); 51 | 52 | // be a little bit verbose. 53 | Serial.print("Send:");Serial.println(counter); 54 | 55 | // send the number of bytes equal to that set with setPacketLength. 56 | // read those bytes from memory where counter starts. 57 | rfm.send(&counter); 58 | 59 | counter++; // increase the counter. 60 | } 61 | 62 | } 63 | } 64 | 65 | void receiver(){ 66 | uint32_t counter = 0; // to count the messages. 67 | 68 | while(true){ 69 | while(rfm.available()){ // for all available messages: 70 | 71 | uint32_t received_count = 0; // temporary for the new counter. 72 | uint8_t len = rfm.read(&received_count); // read the packet into the new_counter. 73 | 74 | // print verbose output. 75 | Serial.print("Packet ("); Serial.print(len); Serial.print("): ");Serial.println(received_count); 76 | 77 | if (counter+1 != received_count){ 78 | // if the increment is larger than one, we lost one or more packets. 79 | Serial.println("Packetloss detected!"); 80 | } 81 | 82 | // assign the received counter to our counter. 83 | counter = received_count; 84 | } 85 | } 86 | } 87 | 88 | void interrupt_RFM(){ 89 | rfm.poll(); // in the interrupt, call the poll function. 90 | } 91 | 92 | void setup(){ 93 | Serial.begin(9600); 94 | SPI.begin(); 95 | 96 | bareRFM69::reset(RESET_PIN); // sent the RFM69 a hard-reset. 97 | 98 | rfm.setRecommended(); // set recommended paramters in RFM69. 99 | rfm.setPacketType(false, false); // set the used packet type. 100 | 101 | rfm.setBufferSize(2); // set the internal buffer size. 102 | rfm.setPacketLength(4); // set the packet length. 103 | rfm.setFrequency((uint32_t) 434*1000*1000); // set the frequency. 104 | 105 | // baudrate is default, 4800 bps now. 106 | 107 | rfm.receive(); 108 | // set it to receiving mode. 109 | 110 | /* 111 | setup up interrupts such that we don't have to call poll() in a loop. 112 | */ 113 | 114 | // tell the RFM to represent whether we are in automode on DIO 2. 115 | rfm.setDioMapping1(RFM69_PACKET_DIO_2_AUTOMODE); 116 | 117 | // set pinmode to input. 118 | pinMode(DIO2_PIN, INPUT); 119 | 120 | // Tell the SPI library we're going to use the SPI bus from an interrupt. 121 | SPI.usingInterrupt(INTERRUPT_NUMBER); 122 | 123 | // hook our interrupt function to any edge. 124 | attachInterrupt(INTERRUPT_NUMBER, interrupt_RFM, CHANGE); 125 | 126 | // start receiving. 127 | rfm.receive(); 128 | 129 | pinMode(SENDER_DETECT_PIN, INPUT_PULLUP); 130 | delay(5); 131 | } 132 | 133 | void loop(){ 134 | if (digitalRead(SENDER_DETECT_PIN) == LOW){ 135 | Serial.println("Going Receiver!"); 136 | receiver(); 137 | // this function never returns and contains an infinite loop. 138 | } else { 139 | Serial.println("Going sender!"); 140 | sender(); 141 | // idem. 142 | } 143 | } 144 | 145 | 146 | -------------------------------------------------------------------------------- /examples/MinimalInterruptUnoDIO0/MinimalInterruptUnoDIO0.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015, Ivor Wanders 3 | * MIT License, see the LICENSE.md file in the root folder. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | // slave select pin. 10 | #define SLAVE_SELECT_PIN 10 11 | 12 | // connected to the reset pin of the RFM69. 13 | #define RESET_PIN 8 14 | // Sometimes required? Perhaps registers in RFM don't properly initialize after 15 | // power is switched on and proper reset is required? 16 | 17 | // tie this pin down on the receiver. 18 | #define SENDER_DETECT_PIN A0 19 | 20 | // Pin DIO 0 on the RFM69 is attached to this digital pin. 21 | // Pin should have interrupt capability. 22 | #define DIO0_PIN 2 23 | 24 | // on Arduino UNO the the pin number is not what's given to attachInterrupt(..) 25 | #define INTERRUPT_NUMBER 0 26 | // On the Arduino UNO pin 2 has interrupt number 0. 27 | 28 | /* 29 | This is a minimal example with the interrupt to call poll(). 30 | 31 | It transmits a 4 byte integer every 500 ms. 32 | 33 | ==================================================== 34 | Using DIO0 is HIGHLY expirimental! 35 | ==================================================== 36 | */ 37 | 38 | plainRFM69 rfm = plainRFM69(SLAVE_SELECT_PIN); 39 | 40 | 41 | void sender(){ 42 | 43 | uint32_t start_time = millis(); 44 | 45 | uint32_t counter = 0; // the counter which we are going to send. 46 | 47 | while(true){ 48 | if (!rfm.canSend()){ 49 | continue; // sending is not possible, already sending. 50 | } 51 | 52 | if ((millis() - start_time) > 500){ // every 500 ms. 53 | start_time = millis(); 54 | 55 | // be a little bit verbose. 56 | Serial.print("Send:");Serial.println(counter); 57 | 58 | // send the number of bytes equal to that set with setPacketLength. 59 | // read those bytes from memory where counter starts. 60 | rfm.send(&counter); 61 | 62 | counter++; // increase the counter. 63 | } 64 | 65 | } 66 | } 67 | 68 | void receiver(){ 69 | uint32_t counter = 0; // to count the messages. 70 | 71 | while(true){ 72 | while(rfm.available()){ // for all available messages: 73 | 74 | uint32_t received_count = 0; // temporary for the new counter. 75 | uint8_t len = rfm.read(&received_count); // read the packet into the new_counter. 76 | 77 | // print verbose output. 78 | Serial.print("Packet ("); Serial.print(len); Serial.print("): ");Serial.println(received_count); 79 | 80 | if (counter+1 != received_count){ 81 | // if the increment is larger than one, we lost one or more packets. 82 | Serial.println("Packetloss detected!"); 83 | } 84 | 85 | // assign the received counter to our counter. 86 | counter = received_count; 87 | } 88 | } 89 | } 90 | 91 | void interrupt_RFM(){ 92 | rfm.poll(); // in the interrupt, call the poll function. 93 | } 94 | 95 | void setup(){ 96 | Serial.begin(9600); 97 | SPI.begin(); 98 | 99 | bareRFM69::reset(RESET_PIN); // sent the RFM69 a hard-reset. 100 | 101 | rfm.setRecommended(); // set recommended paramters in RFM69. 102 | rfm.setPacketType(false, false); // set the used packet type. 103 | 104 | rfm.setBufferSize(2); // set the internal buffer size. 105 | rfm.setPacketLength(4); // set the packet length. 106 | rfm.setFrequency((uint32_t) 434*1000*1000); // set the frequency. 107 | 108 | // baudrate is default, 4800 bps now. 109 | 110 | rfm.receive(); 111 | // set it to receiving mode. 112 | 113 | /* 114 | setup up interrupts such that we don't have to call poll() in a loop. 115 | */ 116 | 117 | // tell the RFM to set DIO0 when the CRC is ok and when a packet has been sent. 118 | rfm.setDioMapping1(RFM69_PACKET_DIO_0_RX_CRC_OK | RFM69_PACKET_DIO_0_TX_PACKET_SENT); 119 | 120 | // set pinmode to input. 121 | pinMode(DIO0_PIN, INPUT); 122 | 123 | // Tell the SPI library we're going to use the SPI bus from an interrupt. 124 | SPI.usingInterrupt(INTERRUPT_NUMBER); 125 | 126 | // hook our interrupt function to any edge. 127 | attachInterrupt(INTERRUPT_NUMBER, interrupt_RFM, CHANGE); 128 | 129 | // start receiving. 130 | rfm.receive(); 131 | 132 | pinMode(SENDER_DETECT_PIN, INPUT_PULLUP); 133 | delay(5); 134 | } 135 | 136 | void loop(){ 137 | if (digitalRead(SENDER_DETECT_PIN) == LOW){ 138 | Serial.println("Going Receiver!"); 139 | receiver(); 140 | // this function never returns and contains an infinite loop. 141 | } else { 142 | Serial.println("Going sender!"); 143 | sender(); 144 | // idem. 145 | } 146 | } 147 | 148 | 149 | -------------------------------------------------------------------------------- /examples/PingPong/PingPong.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Ivor Wanders 3 | * MIT License, see the LICENSE.md file in the root folder. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | // slave select pin. 10 | #define SLAVE_SELECT_PIN 10 11 | 12 | // connected to the reset pin of the RFM69. 13 | #define RESET_PIN 23 14 | 15 | // tie this pin down on the receiver. 16 | #define SENDER_DETECT_PIN 15 17 | 18 | // Pin DIO 2 on the RFM69 is attached to this digital pin. 19 | // Pin should have interrupt capability. 20 | #define DIO2_PIN 0 21 | 22 | /* 23 | In this example, one node sends 'pings' the other replies to this by 24 | returning the message payload. 25 | 26 | Addressing is used in this example. 27 | */ 28 | 29 | 30 | plainRFM69 rfm = plainRFM69(SLAVE_SELECT_PIN); 31 | 32 | void sender(){ 33 | rfm.setNodeAddress(0x02); 34 | 35 | uint8_t rx_buffer[5]; 36 | uint32_t* rx_counter = (uint32_t*) &(rx_buffer[1]); 37 | 38 | uint32_t last_transmit_time = micros(); 39 | 40 | uint32_t start_time = millis(); 41 | 42 | uint32_t counter = 0; // the counter which we are going to send. 43 | 44 | bool are_waiting_for_receipt = false; 45 | 46 | uint32_t successful_pingpongs = 0; 47 | uint32_t timeout_pongs = 0; 48 | 49 | uint32_t display_time = millis(); // keep track of time to print statistics. 50 | 51 | uint32_t total_pingpong_time = 0; 52 | 53 | while(true){ 54 | 55 | if ((millis() - display_time) > 2000){ 56 | Serial.print("Successful pingpongs: "); Serial.println(successful_pingpongs); 57 | Serial.print("Timeout pingpongs: "); Serial.println(timeout_pongs); 58 | Serial.print("Sum of RTT: "); Serial.println(total_pingpong_time); 59 | Serial.print("Successful / second: "); Serial.println(successful_pingpongs / ((millis() - start_time)/1000)); 60 | Serial.print("Average ping (uSec): "); Serial.println((total_pingpong_time / successful_pingpongs)); 61 | display_time = millis(); 62 | } 63 | 64 | while (rfm.available()){ // check available messages. 65 | 66 | // if we are waiting for messages. 67 | if (are_waiting_for_receipt){ 68 | 69 | // read the buffer. 70 | rfm.read(&rx_buffer); 71 | 72 | // Serial.print("Received: "); Serial.println(*rx_counter); 73 | 74 | // check if the counter matches what we expect to get back. 75 | if (*rx_counter == counter){ 76 | are_waiting_for_receipt = false; 77 | 78 | total_pingpong_time += (micros() - last_transmit_time); 79 | successful_pingpongs++; 80 | } else { 81 | Serial.println("Received something while waiting that didn't match."); 82 | } 83 | 84 | } else { 85 | rfm.read(&rx_buffer); // read it, don't do anything with it. 86 | Serial.print("Received something while not waiting: ");Serial.println(*rx_counter); 87 | } 88 | } 89 | 90 | 91 | // if we have been waiting for receipt for over 100 ms 92 | if (((micros() - last_transmit_time) > 100*1000) and (are_waiting_for_receipt)){ // if not received after 100 ms 93 | are_waiting_for_receipt = false; 94 | timeout_pongs++; 95 | Serial.print("Timeout on: "); Serial.println(counter); 96 | 97 | } 98 | 99 | if (are_waiting_for_receipt == false){ 100 | if (!rfm.canSend()){ 101 | continue; // sending is not possible, already sending. 102 | } 103 | // we're not waiting, sent a message. 104 | last_transmit_time = micros(); 105 | counter++; // increase the counter. 106 | rfm.sendAddressed(0x01, &counter); 107 | are_waiting_for_receipt = true; 108 | // Serial.print("Send:");Serial.println(counter); 109 | 110 | 111 | } 112 | } 113 | } 114 | 115 | void receiver(){ 116 | rfm.setNodeAddress(0x01); 117 | 118 | uint8_t buffer[5]; 119 | uint32_t* received_counter = (uint32_t*) &(buffer[1]); 120 | 121 | while(true){ // do forever 122 | 123 | while(rfm.available()){ // for all available messages: 124 | 125 | // receive ping. 126 | uint8_t len = rfm.read(&buffer); // read the packet into the new_counter. 127 | 128 | Serial.print("Addressed to: "); Serial.print(buffer[0]); 129 | Serial.print(" payload: "); Serial.println(*received_counter); 130 | 131 | // return pong. 132 | rfm.sendAddressed(0x02, &(buffer[1])); 133 | 134 | } 135 | } 136 | } 137 | 138 | 139 | 140 | void interrupt_RFM(){ 141 | rfm.poll(); // in the interrupt, call the poll function. 142 | } 143 | 144 | 145 | void setup(){ 146 | Serial.begin(9600); 147 | SPI.begin(); 148 | 149 | bareRFM69::reset(RESET_PIN); // sent the RFM69 a hard-reset. 150 | 151 | rfm.setRecommended(); // set recommended paramters in RFM69. 152 | rfm.setPacketType(false, true); // set the used packet type. 153 | 154 | rfm.setBufferSize(2); // set the internal buffer size. 155 | rfm.setPacketLength(6); // set the packet length. 156 | rfm.setFrequency((uint32_t) 434*1000*1000); // set the frequency. 157 | 158 | // by default use 4800 bps 159 | 160 | // rfm.baud9600(); 161 | 162 | // rfm.baud300000(); // Set the baudRate to 300000 bps 163 | // rfm.setPreambleSize(15); 164 | 165 | rfm.receive(); 166 | // set it to receiving mode. 167 | 168 | 169 | 170 | // tell the RFM to represent whether we are in automode on DIO 2. 171 | rfm.setDioMapping1(RFM69_PACKET_DIO_2_AUTOMODE); 172 | 173 | // set pinmode to input. 174 | pinMode(DIO2_PIN, INPUT); 175 | 176 | // Tell the SPI library we're going to use the SPI bus from an interrupt. 177 | SPI.usingInterrupt(DIO2_PIN); 178 | 179 | // hook our interrupt function to any edge. 180 | attachInterrupt(DIO2_PIN, interrupt_RFM, CHANGE); 181 | 182 | // start receiving. 183 | rfm.receive(); 184 | 185 | 186 | 187 | pinMode(SENDER_DETECT_PIN, INPUT_PULLUP); 188 | delay(5); 189 | 190 | 191 | 192 | } 193 | 194 | void loop(){ 195 | if (digitalRead(SENDER_DETECT_PIN) == LOW){ 196 | Serial.println("Going Receiver!"); 197 | receiver(); 198 | // this function never returns and contains an infinite loop. 199 | } else { 200 | Serial.println("Going sender!"); 201 | sender(); 202 | // idem. 203 | } 204 | } 205 | 206 | 207 | -------------------------------------------------------------------------------- /examples/PingPongHPdio0/PingPongHPdio0.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Ivor Wanders 3 | * Copyright (c) 2018, Charles Taylor 4 | * 5 | * MIT License, see the LICENSE.md file in the root folder. 6 | */ 7 | 8 | // Set up and tested with the AdaFruit Feather M0 RFM69HCW modules. 9 | 10 | #include 11 | #include 12 | 13 | // slave select pin. 14 | #define SLAVE_SELECT_PIN 8 15 | 16 | // connected to the reset pin of the RFM69. 17 | #define RESET_PIN 4 18 | 19 | // Pin should have interrupt capability. 20 | #define DIO0_PIN 3 21 | 22 | // Tie this pin down on the receiver. 23 | #define SENDER_DETECT_PIN 12 24 | 25 | /* 26 | In this example, one node sends 'pings' the other replies to this by 27 | returning the message payload. 28 | 29 | Addressing is used in this example. 30 | */ 31 | 32 | 33 | plainRFM69 rfm = plainRFM69(SLAVE_SELECT_PIN); 34 | 35 | void sender(){ 36 | rfm.setNodeAddress(0x02); 37 | 38 | rfm.setTxPower(17); 39 | 40 | uint8_t rx_buffer[5]; 41 | uint32_t* rx_counter = (uint32_t*) &(rx_buffer[1]); 42 | 43 | uint32_t last_transmit_time = micros(); 44 | 45 | uint32_t start_time = millis(); 46 | 47 | uint32_t counter = 0; // the counter which we are going to send. 48 | 49 | bool are_waiting_for_receipt = false; 50 | 51 | uint32_t successful_pingpongs = 0; 52 | uint32_t timeout_pongs = 0; 53 | 54 | uint32_t display_time = millis(); // keep track of time to print statistics. 55 | 56 | uint32_t total_pingpong_time = 0; 57 | 58 | while(true){ 59 | 60 | if ((millis() - display_time) > 2000){ 61 | Serial.print("Sender: successful pingpongs: "); Serial.println(successful_pingpongs); 62 | Serial.print("Timeout pingpongs: "); Serial.println(timeout_pongs); 63 | Serial.print("Sum of RTT: "); Serial.println(total_pingpong_time); 64 | Serial.print("Successful / second: "); Serial.println(successful_pingpongs / ((millis() - start_time)/1000)); 65 | Serial.print("Average ping (uSec): "); Serial.println((total_pingpong_time / successful_pingpongs)); 66 | display_time = millis(); 67 | } 68 | 69 | while (rfm.available()){ // check available messages. 70 | 71 | // if we are waiting for messages. 72 | if (are_waiting_for_receipt){ 73 | 74 | // read the buffer. 75 | rfm.read(&rx_buffer); 76 | 77 | Serial.print("Sender: received: "); 78 | Serial.println(*rx_counter); 79 | 80 | // check if the counter matches what we expect to get back. 81 | if (*rx_counter == counter){ 82 | are_waiting_for_receipt = false; 83 | 84 | total_pingpong_time += (micros() - last_transmit_time); 85 | successful_pingpongs++; 86 | } else { 87 | Serial.println("Received something while waiting that didn't match."); 88 | } 89 | 90 | } else { 91 | rfm.read(&rx_buffer); // read it, don't do anything with it. 92 | Serial.print("Received something while not waiting: "); 93 | Serial.println(*rx_counter); 94 | } 95 | } 96 | 97 | 98 | // if we have been waiting for receipt for over 100 ms 99 | if (((micros() - last_transmit_time) > 100*1000) and (are_waiting_for_receipt)){ // if not received after 100 ms 100 | are_waiting_for_receipt = false; 101 | timeout_pongs++; 102 | Serial.print("Timeout on: "); Serial.println(counter); 103 | 104 | } 105 | 106 | if (are_waiting_for_receipt == false){ 107 | if (!rfm.canSend()){ 108 | continue; // sending is not possible, already sending. 109 | } 110 | // we're not waiting, sent a message. 111 | last_transmit_time = micros(); 112 | counter++; // increase the counter. 113 | 114 | rfm.sendAddressed(0x01, &counter); 115 | 116 | are_waiting_for_receipt = true; 117 | // Serial.print("Send:");Serial.println(counter); 118 | 119 | 120 | } 121 | } 122 | } 123 | 124 | void receiver(){ 125 | rfm.setNodeAddress(0x01); 126 | 127 | rfm.setTxPower(17); 128 | 129 | uint8_t buffer[5]; 130 | uint32_t* received_counter = (uint32_t*) &(buffer[1]); 131 | 132 | uint8_t rssi; 133 | 134 | while(true){ // do forever 135 | 136 | while(rfm.available()){ // for all available messages: 137 | 138 | rssi = rfm.getRssiValue(); 139 | 140 | // receive ping. 141 | uint8_t len = rfm.read(&buffer); // read the packet into the new_counter. 142 | 143 | Serial.print("Receiver: addressed to: "); 144 | Serial.print(buffer[0]); 145 | Serial.print(" payload: "); 146 | Serial.print(*received_counter); 147 | Serial.print(" RSSI: "); 148 | Serial.print(-(rssi / 2)); 149 | Serial.print(" ("); 150 | Serial.print(rssi); 151 | Serial.println(")"); 152 | 153 | // return pong. 154 | rfm.sendAddressed(0x02, &(buffer[1])); 155 | 156 | } 157 | } 158 | } 159 | 160 | 161 | 162 | void interrupt_RFM(){ 163 | rfm.poll(); // in the interrupt, call the poll function. 164 | } 165 | 166 | 167 | void setup(){ 168 | delay(2000); // Wait for USB interface to come up. 169 | Serial.begin(115200); 170 | SPI.begin(); 171 | 172 | bareRFM69::reset(RESET_PIN); // sent the RFM69 a hard-reset. 173 | 174 | // Break out the setRecommended function to control. 175 | 176 | rfm.setRecommended(); // set recommended paramters in RFM69. 177 | 178 | //rfm.setLNA(RFM69_LNA_IMP_200OHM, RFM69_LNA_GAIN_AGC_LOOP); // Cut gain and disable AGC for testing. 179 | 180 | rfm.setPacketType(false, true); // set the used packet type. 181 | 182 | rfm.setBufferSize(2); // set the internal buffer size. 183 | rfm.setPacketLength(6); // set the packet length. 184 | rfm.setFrequency((uint32_t) 923*1000*1000); // set the frequency. 185 | 186 | //rfm.baud9600(); 187 | 188 | rfm.baud153600(); // Set the baudRate to 189 | // rfm.setPreambleSize(15); 190 | 191 | // Configure as DIO0 expected on the Feather M0 192 | rfm.setDioMapping1(RFM69_PACKET_DIO_0_RX_CRC_OK | RFM69_PACKET_DIO_0_TX_PACKET_SENT); 193 | 194 | // tell the RFM to represent whether we are in automode on DIO 2. 195 | //rfm.setDioMapping1(RFM69_PACKET_DIO_2_AUTOMODE); 196 | 197 | // set pinmode to input. 198 | pinMode(DIO0_PIN, INPUT); 199 | 200 | // Tell the SPI library we're going to use the SPI bus from an interrupt. 201 | SPI.usingInterrupt(DIO0_PIN); 202 | 203 | // hook our interrupt function to any edge. 204 | attachInterrupt(DIO0_PIN, interrupt_RFM, CHANGE); 205 | 206 | rfm.setHighPowerModule(); 207 | 208 | // start receiving. 209 | rfm.receive(); 210 | 211 | pinMode(SENDER_DETECT_PIN, INPUT_PULLUP); 212 | delay(5); 213 | } 214 | 215 | void loop(){ 216 | if (digitalRead(SENDER_DETECT_PIN) == LOW){ 217 | Serial.println("Going Receiver!"); 218 | receiver(); 219 | // this function never returns and contains an infinite loop. 220 | } else { 221 | Serial.println("Going sender!"); 222 | sender(); 223 | // idem. 224 | } 225 | } 226 | 227 | 228 | -------------------------------------------------------------------------------- /examples/VariableLength/VariableLength.ino: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Ivor Wanders 3 | * MIT License, see the LICENSE.md file in the root folder. 4 | */ 5 | 6 | #include 7 | #include 8 | 9 | // slave select pin. 10 | #define SLAVE_SELECT_PIN 10 11 | 12 | // connected to the reset pin of the RFM69. 13 | #define RESET_PIN 23 14 | 15 | // tie this pin down on the receiver. 16 | #define SENDER_DETECT_PIN 15 17 | 18 | // Pin DIO 2 on the RFM69 is attached to this digital pin. 19 | // Pin should have interrupt capability. 20 | #define DIO2_PIN 0 21 | 22 | /* 23 | Sends variable length packets. 24 | 25 | The packets increase in length. Shows that packages larger than the set 26 | packetlength are not received. 27 | 28 | */ 29 | 30 | plainRFM69 rfm = plainRFM69(SLAVE_SELECT_PIN); 31 | 32 | void sender(){ 33 | 34 | uint32_t start_time = millis(); 35 | 36 | uint8_t length = 1; 37 | uint8_t length_overflow = 36; 38 | 39 | uint8_t tx_buffer[length_overflow]; 40 | uint32_t* counter = (uint32_t*) &tx_buffer; 41 | 42 | while(true){ 43 | if (!rfm.canSend()){ 44 | continue; // sending is not possible, already sending. 45 | } 46 | 47 | if ((millis() - start_time) > 500){ // every 500 ms. 48 | start_time = millis(); 49 | 50 | // be a little bit verbose. 51 | Serial.print("Send Packet ("); Serial.print(length); Serial.print("): "); Serial.println(*counter); 52 | 53 | // send the number of bytes equal to that set with setPacketLength. 54 | // read those bytes from memory where counter starts. 55 | rfm.sendVariable(&tx_buffer, length); 56 | 57 | length = (length + 1) % length_overflow; 58 | if (length == 0){ 59 | length++; 60 | } 61 | 62 | 63 | (*counter)++; // increase the counter. 64 | } 65 | 66 | } 67 | } 68 | 69 | void receiver(){ 70 | uint8_t rx_buffer[66] = {0}; 71 | uint32_t* rx_counter = (uint32_t*) &rx_buffer; 72 | 73 | while(true){ // do forever 74 | 75 | while(rfm.available()){ // for all available messages: 76 | 77 | uint32_t received_count = 0; // temporary for the new counter. 78 | uint8_t len = rfm.read(&rx_buffer); // read the packet into the new_counter. 79 | 80 | // print verbose output. 81 | Serial.print("Received Packet ("); Serial.print(len); Serial.print("): "); Serial.println(*rx_counter); 82 | 83 | } 84 | } 85 | } 86 | 87 | void interrupt_RFM(){ 88 | rfm.poll(); // in the interrupt, call the poll function. 89 | } 90 | 91 | void setup(){ 92 | Serial.begin(9600); 93 | SPI.begin(); 94 | 95 | bareRFM69::reset(RESET_PIN); // sent the RFM69 a hard-reset. 96 | 97 | rfm.setRecommended(); // set recommended paramters in RFM69. 98 | rfm.setPacketType(true, false); // set the used packet type. 99 | 100 | rfm.setBufferSize(5); // set the internal buffer size. 101 | rfm.setPacketLength(32); // set the packet length. 102 | rfm.setFrequency((uint32_t) 434*1000*1000); // set the frequency. 103 | 104 | rfm.baud9600(); 105 | 106 | 107 | 108 | pinMode(SENDER_DETECT_PIN, INPUT_PULLUP); 109 | delay(5); 110 | /* 111 | setup up interrupts such that we don't have to call poll() in a loop. 112 | */ 113 | 114 | // tell the RFM to represent whether we are in automode on DIO 2. 115 | rfm.setDioMapping1(RFM69_PACKET_DIO_2_AUTOMODE); 116 | 117 | // set pinmode to input. 118 | pinMode(DIO2_PIN, INPUT); 119 | 120 | // Tell the SPI library we're going to use the SPI bus from an interrupt. 121 | SPI.usingInterrupt(DIO2_PIN); 122 | 123 | // hook our interrupt function to any edge. 124 | attachInterrupt(DIO2_PIN, interrupt_RFM, CHANGE); 125 | 126 | // start receiving. 127 | rfm.receive(); 128 | delay(2000); 129 | } 130 | 131 | void loop(){ 132 | if (digitalRead(SENDER_DETECT_PIN) == LOW){ 133 | Serial.println("Going Receiver!"); 134 | receiver(); 135 | // this function never returns and contains an infinite loop. 136 | } else { 137 | Serial.println("Going sender!"); 138 | sender(); 139 | // idem. 140 | } 141 | } 142 | 143 | 144 | -------------------------------------------------------------------------------- /extras/check_crypto_mode.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | * This file is part of plainRFM69. 4 | * Copyright (c) 2014, Ivor Wanders 5 | * MIT License, see the LICENSE.md file in the root folder. 6 | """ 7 | 8 | 9 | import crcmod 10 | from Crypto.Cipher import AES 11 | 12 | """ 13 | This script does some analysis on the packets produced by the RFM69 radio 14 | modules. It investigates the cipher mode and the CRC polynomial. 15 | """ 16 | 17 | 18 | print("Decoding a few packets using ECB cipher mode.\n") 19 | packets=["8E7760F05C573E15AA5BE39470BE70CC202C11692E65C99BCB7BA90B1B61524A02A0ECCC1F2BC60C836CB312E81B3A3F", 20 | "E7D3AF2F7EBE569B7EE2F2F3EE825F2E202C11692E65C99BCB7BA90B1B61524A02A0ECCC1F2BC60C836CB312E81B3A3F", 21 | "ED660A0682E94BBBBB98D9E84B7EBDE3202C11692E65C99BCB7BA90B1B61524A02A0ECCC1F2BC60C836CB312E81B3A3F", 22 | "B720DD819F49264684815C4767BC5A8B202C11692E65C99BCB7BA90B1B61524A02A0ECCC1F2BC60C836CB312E81B3A3F", 23 | "5ACE8F6CC4710212D2CB294792BF1D7E202C11692E65C99BCB7BA90B1B61524A02A0ECCC1F2BC60C836CB312E81B3A3F", 24 | "B26162233AFC2D47ADDFB4B92D0697C2202C11692E65C99BCB7BA90B1B61524A02A0ECCC1F2BC60C836CB312E81B3A3F", 25 | "DBC3A0068B8ACAB1867B6F8E897F8DA0202C11692E65C99BCB7BA90B1B61524A02A0ECCC1F2BC60C836CB312E81B3A3F", 26 | "D8AD1CA54F5F086861C5C9808E020903202C11692E65C99BCB7BA90B1B61524A02A0ECCC1F2BC60C836CB312E81B3A3F", 27 | "BA9EDEA3D76ED5A123A1314F86296BCB202C11692E65C99BCB7BA90B1B61524A02A0ECCC1F2BC60C836CB312E81B3A3F", 28 | "A213715080D4BE92C80EAF1ADF1C8EF0202C11692E65C99BCB7BA90B1B61524A02A0ECCC1F2BC60C836CB312E81B3A3F", 29 | "0216DC6EA3C50DF948AB69A0A578A9BC202C11692E65C99BCB7BA90B1B61524A02A0ECCC1F2BC60C836CB312E81B3A3F", 30 | "23E0D6A53E72AFA05C7E0AA438A10080202C11692E65C99BCB7BA90B1B61524A02A0ECCC1F2BC60C836CB312E81B3A3F", 31 | 32 | ] 33 | 34 | aes_ECB_zeros = AES.new(bytes([0 for i in range(0,16)]), AES.MODE_ECB) 35 | packetbytes = [bytes([int(j[i:i+2],16) for i in range(0,len(j), 2)]) for j in packets] 36 | # print(packetbytes) 37 | for j in packetbytes: 38 | print(aes_ECB_zeros.decrypt(j)) 39 | 40 | 41 | 42 | 43 | # Sent with: 44 | # uint8_t key[16] = {146, 48, 0, 16, 31, 203, 208, 65, 31, 10, 94, 64, 8, 198, 226, 121}; 45 | # rfm->setPacketConfig1(RFM69_PACKET_CONFIG_CRC_ON); 46 | # rfm->setPacketConfig2(11, true, true, true); 47 | # rfm->setPayloadLength(20); 48 | # Received as: 49 | # 0xAA0000004445464748494A4B4C4D4E4F505152530000000000 50 | # 0xAD0000004445464748494A4B4C4D4E4F505152530000000000 51 | 52 | # Recieved with CRC off, AES off, payloadlength of 34. 53 | # 0xA02AEBADF27DBBB36F3928BD53A3BC87AD7159E950A85F4ABF59F043828932B7226E 54 | # 0x2B240611993CEB856A07FD353C940ACAAD7159E950A85F4ABF59F043828932B73A22 55 | # 0x2475FEF5D93D2EE636B43584DEDCF622AD7159E950A85F4ABF59F043828932B7A639 56 | # 0x27D3806ED8A8BB63BD700B9FAE8B64C9AD7159E950A85F4ABF59F043828932B782D4 57 | # 0x652729B2F2A75C1279AF2833417DFCF5AD7159E950A85F4ABF59F043828932B76DA1 58 | 59 | 60 | secondblob= """0x40C3D18D9DD0B5AA282163095BCAA2A3AD7159E950A85F4ABF59F043828932B71FCE 61 | 0x85096C2B74B868AB0028B8EB1C5F32DFAD7159E950A85F4ABF59F043828932B74531 62 | 0x775CA8AC1172E7BE0087620AB85A3FA5AD7159E950A85F4ABF59F043828932B79BC8 63 | 0x2171F599317F14647152AF7575878392AD7159E950A85F4ABF59F043828932B719EF 64 | 0x9EEA1DEDD22250EBE86A9E76C8FD09E9AD7159E950A85F4ABF59F043828932B7EBA2 65 | 0x69B8EE198B6D1D86F2C325C99433365BAD7159E950A85F4ABF59F043828932B70D11 66 | 0x0B4CF2CAC1C54C7FBC74166E56DDB8BEAD7159E950A85F4ABF59F043828932B79FD5 67 | 0xEAC5E015D784F5EBD52288E29DE829E1AD7159E950A85F4ABF59F043828932B74D6F 68 | 0x04C6F98A3AE2F734F04B5AD6BCD331B7AD7159E950A85F4ABF59F043828932B7F59C 69 | 0xEC8BC1DF88627E05CA4E7A8E61FF66A4AD7159E950A85F4ABF59F043828932B73DAC 70 | 0xCBE28965605C1DCCC49DE89E70740AC4AD7159E950A85F4ABF59F043828932B7E348 71 | 0xDA0F57C322198373F8B8D5F1C2092973AD7159E950A85F4ABF59F043828932B75675 72 | 0x712E58D47D65099DA1284FD2468A2D92AD7159E950A85F4ABF59F043828932B7636B 73 | 0xEB9749AF00AD035E9F5FD10192B0BE8DAD7159E950A85F4ABF59F043828932B77780 74 | 0x78A93AE0086ACAE9C40EB21BC1ED3780AD7159E950A85F4ABF59F043828932B76758 75 | 0x5C5F1708EAFFB0AE5C099D80B8E4EA1AAD7159E950A85F4ABF59F043828932B77233 76 | 0x3DCF439921381E5759032FDC4A1A5256AD7159E950A85F4ABF59F043828932B7F9C2 77 | 0x7AB451B10DFFF162876E8A5CC7A43624AD7159E950A85F4ABF59F043828932B729A3 78 | 0x200F94DFABB83D2AD9257B89861E0838AD7159E950A85F4ABF59F043828932B70ABB 79 | 0x2A1C2DE6E3E61B18F4BE6F2A8735B994AD7159E950A85F4ABF59F043828932B71458 80 | 0xED7A09F2F4855703ED61F2BF900FB05AAD7159E950A85F4ABF59F043828932B7965E 81 | 0xAD86E27BFCC2ADB7163D8EC3B1F2C7EFAD7159E950A85F4ABF59F043828932B7F02A 82 | 0xE2DF08D40982ED92F3B7240165ACFC7CAD7159E950A85F4ABF59F043828932B76F0D 83 | 0xE48A637BB4FC8163CDAB64B31CB91D94AD7159E950A85F4ABF59F043828932B7646B 84 | 0xBFB6B5347425C5E0878C70AB4034C51BAD7159E950A85F4ABF59F043828932B71CF6 85 | 0x3AF7D0B56189B60990732EE7CC0AA3B7AD7159E950A85F4ABF59F043828932B7FC0D 86 | 0x99CB3B7C809BF435337861DA21A409A9AD7159E950A85F4ABF59F043828932B76515 87 | 0xB852EC76F7E645387224FA49A55A681AAD7159E950A85F4ABF59F043828932B768BC 88 | 0x2BFEE1521309749AFA635366CA2A959CAD7159E950A85F4ABF59F043828932B7F8D5 89 | 0x9A5CBD91C84EBE906EF16A818FCCB7F4AD7159E950A85F4ABF59F043828932B73458 90 | 0x4BEEC7794EF3E72DD8432524E7621428AD7159E950A85F4ABF59F043828932B78ED5 91 | 0x57E40429BD34F151D9D7A8A5758F7903AD7159E950A85F4ABF59F043828932B70038 92 | 0xD45FDF85ACB352A02275448281DEF736AD7159E950A85F4ABF59F043828932B761CB 93 | 0x759BFBB7F8E04F335B285DA414F69D6DAD7159E950A85F4ABF59F043828932B7323C 94 | 0x0F9F13D6B72AF71E9B34A28671DB12B8AD7159E950A85F4ABF59F043828932B711D1 95 | 0x9382E8DFC3803F9F1D3CC169EFC2D6FAAD7159E950A85F4ABF59F043828932B7967E 96 | 0xA3D0603CFBE16DBC9E8EB9F625BF0014AD7159E950A85F4ABF59F043828932B72C7A""" 97 | 98 | 99 | print("\n\nCalculating CRC XOR and INIT parameters with several packets.\n") 100 | packets=[ 101 | "A02AEBADF27DBBB36F3928BD53A3BC87AD7159E950A85F4ABF59F043828932B7226E", 102 | "2B240611993CEB856A07FD353C940ACAAD7159E950A85F4ABF59F043828932B73A22", 103 | "2475FEF5D93D2EE636B43584DEDCF622AD7159E950A85F4ABF59F043828932B7A639", 104 | "27D3806ED8A8BB63BD700B9FAE8B64C9AD7159E950A85F4ABF59F043828932B782D4", 105 | "652729B2F2A75C1279AF2833417DFCF5AD7159E950A85F4ABF59F043828932B76DA1"] 106 | packetbytes = [bytes([int(j[i:i+2],16) for i in range(0,len(j), 2)]) for j in packets] 107 | # print(packetbytes) 108 | crcbytes = [int(a[-4:],16) for a in packets] 109 | 110 | 111 | 112 | # packetbytes = [bytes([int(j[i+2:i+2+2],16) for i in range(0,len(j)-2, 2)]) for j in secondblob.split("\n")] 113 | # crcbytes = [int(a[-4:],16) for a in secondblob.split("\n")] 114 | 115 | # print(crcbytes) 116 | payload = [a[0:-2] for a in packetbytes] 117 | # print(payload) 118 | 119 | isDone = False 120 | for init in range(0,0xFFFF): 121 | for xor in range(0,0xFFFF): 122 | A = crcmod.mkCrcFun(0x11021, initCrc=init, rev=False, xorOut=xor) 123 | res = [a for a in list(map(A, payload))] 124 | # print(crcbytes) 125 | # print(res1) 126 | z = [0 if res[i] == crcbytes[i] else 1 for i in range(0,len(res))] 127 | if (sum(z) == 0): 128 | print(res) 129 | print(crcbytes) 130 | print("Init: {}".format(init)) 131 | print("xor: {}".format(xor)) 132 | isDone = True 133 | break 134 | # sys.exit(0) 135 | if (isDone): 136 | break 137 | 138 | #resulted in xor=1272, init=0, no reverse 139 | print("Verifying the calculated parameters on another packetcapture.") 140 | #verify that on the new blobl. 141 | packetbytes = [bytes([int(j[i+2:i+2+2],16) for i in range(0,len(j)-2, 2)]) for j in secondblob.split("\n")] 142 | crcbytes = [int(a[-4:],16) for a in secondblob.split("\n")] 143 | payload = [a[0:-2] for a in packetbytes] 144 | # print(crcbytes) 145 | A = crcmod.mkCrcFun(0x11021, initCrc=0, rev=False, xorOut=1272) 146 | res = list(map(A, payload)) 147 | 148 | z = [0 if res[i] == crcbytes[i] else 1 for i in range(0,len(res))] 149 | print(res[0:7]) 150 | print(crcbytes[0:7]) 151 | if (sum(z) == 0): 152 | print("Yes, correct poly.") 153 | print("crcmod.mkCrcFun(0x11021, initCrc={:d}, rev=False, xorOut={:d})".format(init, xor)) 154 | 155 | 156 | # thing = AES.new(bytes([146, 48, 0, 16, 31, 203, 208, 65, 31, 10, 94, 64, 8, 198, 226, 121]), AES.MODE_ECB) 157 | # decrypted = [thing.decrypt(j[0:16]) + thing.decrypt(j[16:32]) for j in packetbytes] 158 | 159 | 160 | print("\n\nDetermining how AES cipher is handled.\n") 161 | 162 | m =""" 163 | Sent with variable length, no addressing, variable length taken to be 5. AES (0) 164 | Received with fixed length 17, no addressing. AES disabled. 165 | 166 | CRC is removed by Rx phase, so that's missing. 167 | """ 168 | print(m) 169 | 170 | # print("\nVariable length, no addressing\n"); 171 | testblob = """0x05E594B1DA6CD6DD582B92E6C3FB2D0BBF 172 | 0x051C485121872B1EA87514326714F73197 173 | 0x0546AFBACAC815CC0DF7DC59F0DF5E93AC 174 | 0x05B119A2572AE34141F798F8D567157379 175 | 0x0570F76CCCFE3B5D83666736587C00C7CF 176 | 0x05B62024068A1DBB90B2BC58C4DBCBAB4F 177 | 0x055563A1E1193DEE76BFAB68AE5E1E0235 178 | 0x05ED021D57E52FC8071B37BCFD3B17487C 179 | 0x057656A68E421018542A9E1F55096FE4A8 180 | 0x058849BF9CD25613EA53018F8E8C651317 181 | 0x05102567E9863433712831B10AA73A85B2 182 | 0x05FE36E1A813EE9AB12D6BFB3042AA5D59 183 | 0x05F909A334C9C092692C92FD55312389A9 184 | 0x05641EB053DCEE83B8C4809025790B5261 185 | 0x05FAE6B432A382CAC15D1295CA80EB70E7 186 | 0x0566AC800895885E97B03A8A59F25200C1 187 | 0x059CD45E03A5973D57D4FEBFBA030359D5""" 188 | 189 | 190 | print("Packetbytes:") 191 | packetbytes = [bytes([int(j[i+2:i+2+2],16) for i in range(0,len(j)-2, 2)]) for j in testblob.split("\n")] 192 | crcbytes = [int(a[-4:],16) for a in testblob.split("\n")] 193 | 194 | 195 | aes_ECB_zeros = AES.new(bytes([0 for i in range(0,16)]), AES.MODE_ECB) 196 | 197 | 198 | 199 | for j in packetbytes[0:5]: 200 | print("Cipher: " + str(j)) 201 | for j in packetbytes[0:5]: 202 | plaintext = aes_ECB_zeros.decrypt(j[1:]) 203 | print("Plain: " + str(plaintext)) 204 | 205 | 206 | """ 207 | Results in: 208 | b'\xad\x01\x02\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 209 | b'\xae\x01\x02\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 210 | b'\xaf\x01\x02\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 211 | 212 | """ 213 | 214 | m=""" 215 | So, as the datasheet states, the length field is not encrypted. 216 | Not described is that the message is zero-padded to 16 bytes AES payload. 217 | Which is necessary for the block cipher to work. 218 | """ 219 | print(m) 220 | 221 | m=""" 222 | Sent with variable length, with addressing, variable length set to 6, AES(0) 223 | Received with fixed length 18, addressing, AES disabled 224 | """ 225 | 226 | print("With variable length and adressing") 227 | print(m) 228 | 229 | testblob="""0x06ABDBA03075EFA0410C3B7C4206112BB309 230 | 0x06AB94E0A6F6B37C4489D484FA8343198406 231 | 0x06AB1F2FF5A442F4BB89C420F2D577E2DB44 232 | 0x06AB01B88ACE401B5DD17CD96D743632C667 233 | 0x06AB4AFD493D859581C4942387991B50768B 234 | 0x06AB82228277F697B4D512C17A78A8C42BDA 235 | 0x06AB76645F0D6329639DA8F675BB543A2591 236 | 0x06AB31794A0E916248A2699C8040D89E58FD 237 | 0x06AB10BDE17028E8AAAC9E39C0E8D163863F""" 238 | 239 | 240 | packetbytes = [bytes([int(j[i+2+2:i+2+2+2],16) for i in range(0,len(j)-4, 2)]) for j in testblob.split("\n")] 241 | 242 | 243 | """ 244 | b'.\x01\x02\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 245 | b'/\x01\x02\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 246 | b'0\x01\x02\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 247 | b'1\x01\x02\x03\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' 248 | 249 | """ 250 | 251 | # thing = AES.new(bytes([0 for i in range(0,16)]), AES.MODE_ECB) 252 | 253 | for j in packetbytes[0:3]: 254 | print("Cipher: " + str(j)) 255 | for j in packetbytes[0:3]: 256 | plaintext = aes_ECB_zeros.decrypt(j[1:]) 257 | print("Plain: " + str(plaintext)) 258 | 259 | m=""" 260 | So, if AES is used, the data starting after the possible addressing byte or 261 | length and address byte is zero padded to obtain blocks of 16 bytes long. 262 | The length byte is not increased, but the actual data transmitted over the 263 | air is. 264 | Transmitting less than 16 bytes does not result in faster transmissions. 265 | """ 266 | print(m) -------------------------------------------------------------------------------- /extras/hardware/.gitattributes: -------------------------------------------------------------------------------- 1 | * linguist-vendored -------------------------------------------------------------------------------- /extras/hardware/README.md: -------------------------------------------------------------------------------- 1 | Teensy_RFM69CW breakout 2 | ==================== 3 | 4 | This are the breakout boards I use to connect the radio module to the Teensy chips. I used two PCB's instead of one to save cost and to allow access to the pins below the radio module. Although only DIO2 is required for the library, all DIO pins are made accessible. The silkscreen on the boards is not all that clear, so I suggest opening the schematic and board files when soldering to check where the parts should go. I have switched to KiCad since making this, so I'm not going to invest any time in updating the boards. 5 | 6 | The lower part has a jumper to connect the clock of the SPI port to either pin 13 or 14, it is connected to pin 14 by default so that the led is still available for use. This lower part also contains an 8 pin header for SPI, in the same pin-layout as the NRF24L0+ radio modules. 7 | 8 | The upper part has a loading circuit for a LiPo battery, based on the [MAX1555][max1555] battery charger IC. It connects the Vusb from the Teensy to the USB input of the Max1555 chip, a header is provided for the DC pin of the Max1555 chip. The battery's positive lead is connected to the battery pin of the Max1555 IC and the Vin of the Teensy. Cutting the Vusb-Vin pad on the Teensy is required when using this board. 9 | 10 | One wire is required from the upper to the lower board, this is the MISO line of the SPI port. I used a thick piece of wire for this in order to relieve the stress on the radio module when detaching the assembly from a Teensy. In case the board should also work without a battery attached, I recommend adding a larger electrolytic capacitor over the battery pins (something like 220uF should do). 11 | 12 | Pinout for lower board 13 | ------------- 14 | ![Schematic for lower board](media/teensy_rfm69cw_nrf_lower.png?raw=true "Lower board schematic") 15 | Related to the RFM69CW radio module: 16 | 17 | | RFM | Teensy | 18 | | ------------- | ----------- | 19 | | MOSI | 11 DOUT | 20 | | SCK | 14 SCK2 (jumper to 13) | 21 | | NSS | 10 CS | 22 | | DIO3 | 16 | 23 | 24 | For the NRF header: 25 | 26 | | NRF | Teensy | 27 | | ------|--------- | 28 | | CE | 9 | 29 | | SCK | 14 SCK2 (jumper to 13) | 30 | | MISO | 12 DIN | 31 | | CSN | 15 CS | 32 | | MOSI | 11 DOUT | 33 | | IRQ | Solderpad | 34 | 35 | 36 | Pinout for the upper board 37 | ------------------- 38 | ![Schematic for upper board](media/teensy_rfm69_cw_nrf_upper.png?raw=true "Upper board schematic") 39 | 40 | Related to the RFM69CW radio module: 41 | 42 | | RFM | Teensy | 43 | | ------------- | ----------- | 44 | | MISO | Solderpad | 45 | | RESET | 23 | 46 | | DIO0 | 2 | 47 | | DIO1 | 1 | 48 | | DIO2 | 0 | 49 | | DIO5 | Solderpad | 50 | 51 | Related to the charging Circuit: 52 | 53 | | MAX1555 | Connected to | 54 | | ------------- | ----------- | 55 | | Bat | Battery header positive, Teensy Vin | 56 | | DC | DC input header positive | 57 | | USB | Teensy Vusb | 58 | | CHG | To an led | 59 | 60 | Credits 61 | --------- 62 | I used the Eagle part libraries from [Mikes-Eagle-Libraries](https://github.com/mgrusin/Mikes-Eagle-Libraries) and [SparkFun-PowerIC](https://github.com/sparkfun/SparkFun-Eagle-Libraries/blob/master/SparkFun-PowerIC.lbr). Inspiration to include the charging circuit came from [Onehorse's appallingly small LiPo Charger addon](https://forum.pjrc.com/threads/26462-Appallingly-small-LiPo-charger-add-on-for-Teensy-3-1). 63 | 64 | Assembled 65 | -------- 66 | ![Top view for boards and radio module](media/top.jpg?raw=true "Top view") 67 | 68 | ![Bottom view for boards and radio module](media/bottom.jpg?raw=true "Bottom view") 69 | 70 | [max1555]: https://www.maximintegrated.com/en/products/power/battery-management/MAX1555.html 71 | -------------------------------------------------------------------------------- /extras/hardware/media/bottom.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwanders/plainRFM69/152a9a435acc24a2f5783991ee41bc271be5552a/extras/hardware/media/bottom.jpg -------------------------------------------------------------------------------- /extras/hardware/media/teensy_rfm69_cw_nrf_upper.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwanders/plainRFM69/152a9a435acc24a2f5783991ee41bc271be5552a/extras/hardware/media/teensy_rfm69_cw_nrf_upper.png -------------------------------------------------------------------------------- /extras/hardware/media/teensy_rfm69cw_nrf_lower.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwanders/plainRFM69/152a9a435acc24a2f5783991ee41bc271be5552a/extras/hardware/media/teensy_rfm69cw_nrf_lower.pdf -------------------------------------------------------------------------------- /extras/hardware/media/teensy_rfm69cw_nrf_lower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwanders/plainRFM69/152a9a435acc24a2f5783991ee41bc271be5552a/extras/hardware/media/teensy_rfm69cw_nrf_lower.png -------------------------------------------------------------------------------- /extras/hardware/media/teensy_rfm69cw_nrf_lower_sch.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwanders/plainRFM69/152a9a435acc24a2f5783991ee41bc271be5552a/extras/hardware/media/teensy_rfm69cw_nrf_lower_sch.pdf -------------------------------------------------------------------------------- /extras/hardware/media/teensy_rfm69cw_nrf_upper.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwanders/plainRFM69/152a9a435acc24a2f5783991ee41bc271be5552a/extras/hardware/media/teensy_rfm69cw_nrf_upper.pdf -------------------------------------------------------------------------------- /extras/hardware/media/teensy_rfm69cw_nrf_upper_sch.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwanders/plainRFM69/152a9a435acc24a2f5783991ee41bc271be5552a/extras/hardware/media/teensy_rfm69cw_nrf_upper_sch.pdf -------------------------------------------------------------------------------- /extras/hardware/media/top.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/iwanders/plainRFM69/152a9a435acc24a2f5783991ee41bc271be5552a/extras/hardware/media/top.jpg -------------------------------------------------------------------------------- /extras/hardware/teensy_rfm69cw_nrf_lower.brd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | DIN 137 | IRQ 138 | NRF 139 | TEENSY 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | GND 159 | 3.3V 160 | VBAT 161 | 16 162 | 9 163 | 164 | 165 | <b>PIN HEADER</b> 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | >NAME 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | GND 213 | 3.3V 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | >NAME 247 | >VALUE 248 | 1 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | Soldering jumper and bridges 261 | 262 | 263 | <b>0R Jumper Variant A</b><p> 264 | chip 0805 265 | 266 | 267 | 268 | 269 | 270 | >NAME 271 | >VALUE 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | <b>Pin Header Connectors</b><p> 280 | <author>Created by librarian@cadsoft.de</author> 281 | 282 | 283 | <b>PIN HEADER</b> 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | >NAME 294 | >VALUE 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | <b>EAGLE Design Rules</b> 312 | <p> 313 | Die Standard-Design-Rules sind so gewählt, dass sie für 314 | die meisten Anwendungen passen. Sollte ihre Platine 315 | besondere Anforderungen haben, treffen Sie die erforderlichen 316 | Einstellungen hier und speichern die Design Rules unter 317 | einem neuen Namen ab. 318 | <b>Laen's PCB Order Design Rules</b> 319 | <p> 320 | Please make sure your boards conform to these design rules. 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | 432 | 433 | 434 | 435 | 436 | 437 | 438 | 439 | 440 | 441 | 442 | 443 | 444 | 445 | 446 | 447 | 448 | 449 | 450 | 451 | 452 | 453 | 454 | 455 | 456 | 457 | 458 | 459 | 460 | 461 | 462 | 463 | 464 | 465 | 466 | 467 | 468 | 469 | 470 | 471 | 472 | 473 | 474 | 475 | 476 | 477 | 478 | 479 | 480 | 481 | 482 | 483 | 484 | 485 | 486 | 487 | 488 | 489 | 490 | 491 | 492 | 493 | 494 | 495 | 496 | 497 | 498 | 499 | 500 | 501 | 502 | 503 | 504 | 505 | 506 | 507 | 508 | 509 | 510 | 511 | 512 | 513 | 514 | 515 | 516 | 517 | 518 | 519 | 520 | 521 | 522 | 523 | 524 | 525 | 526 | 527 | 528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 | 538 | 539 | 540 | 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 | 561 | 562 | 563 | 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 | 579 | 580 | 581 | 582 | 583 | 584 | 585 | 586 | 587 | 588 | 589 | 590 | 591 | 592 | 593 | 594 | 595 | 596 | 597 | 598 | 599 | 600 | 601 | 602 | 603 | 604 | 605 | 606 | 607 | 608 | 609 | 610 | 611 | 612 | 613 | 614 | 615 | 616 | 617 | 618 | 619 | 620 | 621 | 622 | 623 | 624 | 625 | 626 | 627 | 628 | 629 | 630 | 631 | 632 | 633 | 634 | 635 | 636 | 637 | 638 | 639 | 640 | 641 | 642 | 643 | 644 | 645 | 646 | 647 | 648 | 649 | 650 | 651 | 652 | 653 | 654 | 655 | 656 | 657 | 658 | 659 | 660 | 661 | 662 | 663 | 664 | 665 | 666 | 667 | 668 | 669 | 670 | 671 | 672 | 673 | 674 | 675 | 676 | 677 | 678 | 679 | 680 | 681 | 682 | 683 | 684 | 685 | 686 | 687 | 688 | 689 | 690 | 691 | 692 | 693 | 694 | -------------------------------------------------------------------------------- /extras/write_keywords.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | Copyright (c) 2014 Ivor Wanders 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 | 24 | """ 25 | This allows easy creation of *uino's keywords.txt file from the source files. 26 | """ 27 | 28 | import os 29 | import re 30 | 31 | prefix = """####################################### 32 | # Syntax Coloring for {project_name} 33 | ####################################### 34 | 35 | ####################################### 36 | # Datatypes (KEYWORD1) 37 | ####################################### 38 | {datatypes} 39 | ####################################### 40 | # Instances (KEYWORD2) 41 | ####################################### 42 | {instances} 43 | ####################################### 44 | # Methods and Functions (KEYWORD2) 45 | ####################################### 46 | {methods} 47 | ####################################### 48 | # Constants (LITERAL1) 49 | ####################################### 50 | {literals} 51 | """ 52 | 53 | 54 | def get_methods(file): 55 | matcher = re.compile("\s+[\w]+ +([^(-]+)\(") 56 | methods = [] 57 | for line in file: 58 | a = matcher.findall(line) 59 | if (len(a) != 0) and (" " not in a[0]) and (":" not in a[0]): 60 | methods.append(a[0]) 61 | return methods 62 | 63 | 64 | def get_constants(file): 65 | matcher = re.compile("#define\s+([\w_]+)\s+") 66 | constants = [] 67 | for line in file: 68 | a = matcher.findall(line) 69 | if (len(a) != 0) and (" " not in a[0]) and (":" not in a[0]): 70 | constants.append(a[0]) 71 | return constants 72 | 73 | if __name__ == "__main__": 74 | 75 | method_files = ["bareRFM69.h", "plainRFM69.h"] 76 | literal_files = ["bareRFM69_const.h"] 77 | data_types = ["bareRFM69", "plainRFM69"] 78 | instances = ["rfm"] 79 | project_name = "plainRFM69" 80 | file_path = ".." 81 | 82 | methods = [] 83 | for filename in method_files: 84 | with open(os.path.join(file_path, filename)) as f: 85 | methods.extend(get_methods(f)) 86 | 87 | for filename in literal_files: 88 | with open(os.path.join(file_path, filename)) as f: 89 | constants = get_constants(f) 90 | 91 | foo = ["{:} LITERAL1".format(c) for c in constants] 92 | literal_string = "\n".join(["{}\tLITERAL1".format(c) for c in constants]) 93 | method_string = "\n".join(["{}\tKEYWORD2".format(c) for c in methods]) 94 | data_t_string = "\n".join(["{}\tKEYWORD1".format(c) for c in data_types]) 95 | instance_string = "\n".join(["{}\tKEYWORD2".format(c) for c in instances]) 96 | done = prefix.format(literals=literal_string, methods=method_string, 97 | project_name=project_name, datatypes=data_t_string, 98 | instances=instance_string) 99 | print(done) 100 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring for plainRFM69 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | bareRFM69 KEYWORD1 9 | plainRFM69 KEYWORD1 10 | ####################################### 11 | # Instances (KEYWORD2) 12 | ####################################### 13 | rfm KEYWORD2 14 | ####################################### 15 | # Methods and Functions (KEYWORD2) 16 | ####################################### 17 | writeRegister KEYWORD2 18 | writeMultiple KEYWORD2 19 | readRegister KEYWORD2 20 | readRegister16 KEYWORD2 21 | readRegister24 KEYWORD2 22 | readRegister32 KEYWORD2 23 | readMultiple KEYWORD2 24 | readRawRegister KEYWORD2 25 | writeFIFO KEYWORD2 26 | readFIFO KEYWORD2 27 | readVariableFIFO KEYWORD2 28 | setMode KEYWORD2 29 | getMode KEYWORD2 30 | setFrf KEYWORD2 31 | setFdev KEYWORD2 32 | startRfCalibration KEYWORD2 33 | completedRfCalibration KEYWORD2 34 | setBitRate KEYWORD2 35 | setDataModul KEYWORD2 36 | setListenConfig KEYWORD2 37 | setListenCoefIdle KEYWORD2 38 | setListenCoefRx KEYWORD2 39 | getVersion KEYWORD2 40 | setPALevel KEYWORD2 41 | setPARamp KEYWORD2 42 | setOCP KEYWORD2 43 | setLNA KEYWORD2 44 | getLNA KEYWORD2 45 | setRxBw KEYWORD2 46 | setAfcBw KEYWORD2 47 | setAfcCtrl KEYWORD2 48 | startRssi KEYWORD2 49 | completedRssi KEYWORD2 50 | getRssiValue KEYWORD2 51 | setDioMapping1 KEYWORD2 52 | getIRQ1Flags KEYWORD2 53 | getIRQ2Flags KEYWORD2 54 | setRSSIThreshold KEYWORD2 55 | setTimeoutRxStart KEYWORD2 56 | TimeoutRssiThresh KEYWORD2 57 | setPreambleSize KEYWORD2 58 | setSyncConfig KEYWORD2 59 | setSyncValue KEYWORD2 60 | setPacketConfig1 KEYWORD2 61 | setPacketConfig2 KEYWORD2 62 | setPayloadLength KEYWORD2 63 | setNodeAddress KEYWORD2 64 | setBroadcastAddress KEYWORD2 65 | setFifoThreshold KEYWORD2 66 | setAutoMode KEYWORD2 67 | setAesKey KEYWORD2 68 | startTempMeasure KEYWORD2 69 | completedTempMeasure KEYWORD2 70 | getTempValue KEYWORD2 71 | setSensitivityBoost KEYWORD2 72 | setPa13dBm1 KEYWORD2 73 | setPa13dBm2 KEYWORD2 74 | setContinuousDagc KEYWORD2 75 | setLowBetaAfcOffset KEYWORD2 76 | poll KEYWORD2 77 | sendPacket KEYWORD2 78 | setRecommended KEYWORD2 79 | setPacketType KEYWORD2 80 | setNodeAddress KEYWORD2 81 | setBroadcastAddress KEYWORD2 82 | setBufferSize KEYWORD2 83 | setPacketLength KEYWORD2 84 | setPacketType KEYWORD2 85 | setFrequency KEYWORD2 86 | setAES KEYWORD2 87 | canSend KEYWORD2 88 | receive KEYWORD2 89 | poll KEYWORD2 90 | available KEYWORD2 91 | read KEYWORD2 92 | baud4800 KEYWORD2 93 | baud9600 KEYWORD2 94 | baud153600 KEYWORD2 95 | baud300000 KEYWORD2 96 | ####################################### 97 | # Constants (LITERAL1) 98 | ####################################### 99 | RFM69_FIFO LITERAL1 100 | RFM69_OPMODE LITERAL1 101 | RFM69_DATA_MODUL LITERAL1 102 | RFM69_BITRATE_MSB LITERAL1 103 | RFM69_BITRATE_LSB LITERAL1 104 | RFM69_FDEV_MSB LITERAL1 105 | RFM69_FDEV_LSB LITERAL1 106 | RFM69_FRF_MSB LITERAL1 107 | RFM69_FRF_MID LITERAL1 108 | RFM69_FRF_LSB LITERAL1 109 | RFM69_OSC1 LITERAL1 110 | RFM69_AFC_CTRL LITERAL1 111 | RFM69_LISTEN1 LITERAL1 112 | RFM69_LISTEN2 LITERAL1 113 | RFM69_LISTEN3 LITERAL1 114 | RFM69_VERSION LITERAL1 115 | RFM69_PA_LEVEL LITERAL1 116 | RFM69_PA_RAMP LITERAL1 117 | RFM69_OCP LITERAL1 118 | RFM69_LNA LITERAL1 119 | RFM69_RX_BW LITERAL1 120 | RFM69_AFC_BW LITERAL1 121 | RFM69_OOK_PEAK LITERAL1 122 | RFM69_OOK_AVG LITERAL1 123 | RFM69_OOK_FIX LITERAL1 124 | RFM69_AFC_FEI LITERAL1 125 | RFM69_AFC_MSB LITERAL1 126 | RFM69_AFC_LSB LITERAL1 127 | RFM69_FEI_MSB LITERAL1 128 | RFM69_FEI_LSB LITERAL1 129 | RFM69_RSSI_CONFIG LITERAL1 130 | RFM69_RSSI_VALUE LITERAL1 131 | RFM69_DIO_MAPPING1 LITERAL1 132 | RFM69_DIO_MAPPING2 LITERAL1 133 | RFM69_IRQ_FLAGS1 LITERAL1 134 | RFM69_IRQ_FLAGS2 LITERAL1 135 | RFM69_RSSI_THRESH LITERAL1 136 | RFM69_RX_TIMEOUT1 LITERAL1 137 | RFM69_RX_TIMEOUT2 LITERAL1 138 | RFM69_PREAMBLE_MSB LITERAL1 139 | RFM69_PREAMBLE_LSB LITERAL1 140 | RFM69_SYNC_CONFIG LITERAL1 141 | RFM69_SYNC_VALUE1 LITERAL1 142 | RFM69_SYNC_VALUE2 LITERAL1 143 | RFM69_SYNC_VALUE3 LITERAL1 144 | RFM69_SYNC_VALUE4 LITERAL1 145 | RFM69_SYNC_VALUE5 LITERAL1 146 | RFM69_SYNC_VALUE6 LITERAL1 147 | RFM69_SYNC_VALUE7 LITERAL1 148 | RFM69_SYNC_VALUE8 LITERAL1 149 | RFM69_PACKET_CONFIG1 LITERAL1 150 | RFM69_PAYLOAD_LENGTH LITERAL1 151 | RFM69_NODE_ADRESS LITERAL1 152 | RFM69_BROADCAST_ADRESS LITERAL1 153 | RFM69_AUTO_MODES LITERAL1 154 | RFM69_FIFO_THRESH LITERAL1 155 | RFM69_PACKET_CONFIG2 LITERAL1 156 | RFM69_AES_KEY1 LITERAL1 157 | RFM69_AES_KEY2 LITERAL1 158 | RFM69_AES_KEY3 LITERAL1 159 | RFM69_AES_KEY4 LITERAL1 160 | RFM69_AES_KEY5 LITERAL1 161 | RFM69_AES_KEY6 LITERAL1 162 | RFM69_AES_KEY7 LITERAL1 163 | RFM69_AES_KEY8 LITERAL1 164 | RFM69_AES_KEY9 LITERAL1 165 | RFM69_AES_KEY10 LITERAL1 166 | RFM69_AES_KEY11 LITERAL1 167 | RFM69_AES_KEY12 LITERAL1 168 | RFM69_AES_KEY13 LITERAL1 169 | RFM69_AES_KEY14 LITERAL1 170 | RFM69_AES_KEY15 LITERAL1 171 | RFM69_AES_KEY16 LITERAL1 172 | RFM69_TEMP1 LITERAL1 173 | RFM69_TEMP2 LITERAL1 174 | RFM69_TEST_LNA LITERAL1 175 | RFM69_TEST_PA1 LITERAL1 176 | RFM69_TEST_PA2 LITERAL1 177 | RFM69_TEST_DAGC LITERAL1 178 | RFM69_TEST_AFC LITERAL1 179 | RFM69_WRITE_REG_MASK LITERAL1 180 | RFM69_READ_REG_MASK LITERAL1 181 | RFM69_MODE_SLEEP LITERAL1 182 | RFM69_MODE_STANDBY LITERAL1 183 | RFM69_MODE_FREQ_SYNTH LITERAL1 184 | RFM69_MODE_TRANSMITTER LITERAL1 185 | RFM69_MODE_RECEIVER LITERAL1 186 | RFM69_MODE_SEQUENCER_OFF LITERAL1 187 | RFM69_MODE_SEQUENCER_ON LITERAL1 188 | RFM69_MODE_LISTEN_ON LITERAL1 189 | RFM69_MODE_LISTEN_OFF LITERAL1 190 | RFM69_MODE_LISTEN_ABORT LITERAL1 191 | RFM69_DATAMODUL_PROCESSING_PACKET LITERAL1 192 | RFM69_DATAMODUL_PROCESSING_CONT_SYNCRHONISER LITERAL1 193 | RFM69_DATAMODUL_PROCESSING_CONT LITERAL1 194 | RFM69_DATAMODUL_OOK LITERAL1 195 | RFM69_DATAMODUL_FSK LITERAL1 196 | RFM69_DATAMODUL_SHAPING_GFSK_NONE LITERAL1 197 | RFM69_DATAMODUL_SHAPING_GFSK_BT_1_0 LITERAL1 198 | RFM69_DATAMODUL_SHAPING_GFSK_BT_0_5 LITERAL1 199 | RFM69_DATAMODUL_SHAPING_GFSK_BT_0_3 LITERAL1 200 | RFM69_DATAMODUL_SHAPING_OOK_NONE LITERAL1 201 | RFM69_DATAMODUL_SHAPING_OOK_FCUTOFF_BR LITERAL1 202 | RFM69_DATAMODUL_SHAPING_OOK_FCUTOFF_2BR LITERAL1 203 | RFM69_PA_LEVEL_PA0_ON LITERAL1 204 | RFM69_PA_LEVEL_PA1_ON LITERAL1 205 | RFM69_PA_LEVEL_PA2_ON LITERAL1 206 | RFM69_PA_RAMP_3400US LITERAL1 207 | RFM69_PA_RAMP_2000US LITERAL1 208 | RFM69_PA_RAMP_1000US LITERAL1 209 | RFM69_PA_RAMP_500US LITERAL1 210 | RFM69_PA_RAMP_250US LITERAL1 211 | RFM69_PA_RAMP_125US LITERAL1 212 | RFM69_PA_RAMP_100US LITERAL1 213 | RFM69_PA_RAMP_62US LITERAL1 214 | RFM69_PA_RAMP_50US LITERAL1 215 | RFM69_PA_RAMP_40US LITERAL1 216 | RFM69_PA_RAMP_31US LITERAL1 217 | RFM69_PA_RAMP_25US LITERAL1 218 | RFM69_PA_RAMP_20US LITERAL1 219 | RFM69_PA_RAMP_15US LITERAL1 220 | RFM69_PA_RAMP_12US LITERAL1 221 | RFM69_PA_RAMP_10US LITERAL1 222 | RFM69_PACKET_CONFIG_LENGTH_FIXED LITERAL1 223 | RFM69_PACKET_CONFIG_LENGTH_VARIABLE LITERAL1 224 | RFM69_PACKET_CONFIG_DC_FREE_NONE LITERAL1 225 | RFM69_PACKET_CONFIG_DC_FREE_MANCHESTER LITERAL1 226 | RFM69_PACKET_CONFIG_DC_FREE_WHITENING LITERAL1 227 | RFM69_PACKET_CONFIG_CRC_ON LITERAL1 228 | RFM69_PACKET_CONFIG_CRC_OFF LITERAL1 229 | RFM69_PACKET_CONFIG_CRC_FAIL_KEEP LITERAL1 230 | RFM69_PACKET_CONFIG_CRC_FAIL_DISCARD LITERAL1 231 | RFM69_PACKET_CONFIG_ADDRESS_FILTER_NONE LITERAL1 232 | RFM69_PACKET_CONFIG_ADDRESS_FILTER_NODE LITERAL1 233 | RFM69_PACKET_CONFIG_ADDRESS_FILTER_NODE_BROADCAST LITERAL1 234 | RFM69_THRESHOLD_CONDITION_NOT_EMPTY LITERAL1 235 | RFM69_THRESHOLD_CONDITION_FIFOLEVEL LITERAL1 236 | RFM69_AFC_CTRL_STANDARD LITERAL1 237 | RFM69_AFC_CTRL_IMPROVED LITERAL1 238 | RFM69_LISTEN_RESOL_IDLE_64US LITERAL1 239 | RFM69_LISTEN_RESOL_IDLE_4_1MS LITERAL1 240 | RFM69_LISTEN_RESOL_IDLE_262MS LITERAL1 241 | RFM69_LISTEN_RESOL_RX_64US LITERAL1 242 | RFM69_LISTEN_RESOL_RX_4_1MS LITERAL1 243 | RFM69_LISTEN_RESOL_RX_262MS LITERAL1 244 | RFM69_LISTEN_CRITERIA_RSSI LITERAL1 245 | RFM69_LISTEN_CRITERIA_RSSI_SYNC LITERAL1 246 | RFM69_LISTEN_END_STAY_RX_LISTEN_STOP LITERAL1 247 | RFM69_LISTEN_END_RX_UNTIL_LISTEN_STOP LITERAL1 248 | RFM69_LISTEN_END_RX_UNTIL_LISTEN_RESUME LITERAL1 249 | RFM69_AUTOMODE_ENTER_NONE_AUTOMODES_OFF LITERAL1 250 | RFM69_AUTOMODE_ENTER_RISING_FIFONOTEMPTY LITERAL1 251 | RFM69_AUTOMODE_ENTER_RISING_FIFOLEVEL LITERAL1 252 | RFM69_AUTOMODE_ENTER_RISING_CRCOK LITERAL1 253 | RFM69_AUTOMODE_ENTER_RISING_PAYLOADREADY LITERAL1 254 | RFM69_AUTOMODE_ENTER_RISING_SYNCADDRESS LITERAL1 255 | RFM69_AUTOMODE_ENTER_RISING_PACKETSENT LITERAL1 256 | RFM69_AUTOMODE_ENTER_FALLING_FIFONOTEMPTY LITERAL1 257 | RFM69_AUTOMODE_EXIT_NONE_AUTOMODES_OFF LITERAL1 258 | RFM69_AUTOMODE_EXIT_FALLING_FIFONOTEMPTY LITERAL1 259 | RFM69_AUTOMODE_EXIT_RISING_FIFOLEVEL_OR_TIMEOUT LITERAL1 260 | RFM69_AUTOMODE_EXIT_RISING_CRCOK_OR_TIMEOUT LITERAL1 261 | RFM69_AUTOMODE_EXIT_RISING_PAYLOADREADY_OR_TIMEOUT LITERAL1 262 | RFM69_AUTOMODE_EXIT_RISING_SYNCADDRESS_OR_TIMEOUT LITERAL1 263 | RFM69_AUTOMODE_EXIT_RISING_PACKETSENT LITERAL1 264 | RFM69_AUTOMODE_EXIT_RISING_TIMEOUT LITERAL1 265 | RFM69_AUTOMODE_INTERMEDIATEMODE_SLEEP LITERAL1 266 | RFM69_AUTOMODE_INTERMEDIATEMODE_STANDBY LITERAL1 267 | RFM69_AUTOMODE_INTERMEDIATEMODE_RECEIVER LITERAL1 268 | RFM69_AUTOMODE_INTERMEDIATEMODE_TRANSMITTER LITERAL1 269 | RFM69_DIO_0_MAP_SHIFT LITERAL1 270 | RFM69_DIO_1_MAP_SHIFT LITERAL1 271 | RFM69_DIO_2_MAP_SHIFT LITERAL1 272 | RFM69_DIO_3_MAP_SHIFT LITERAL1 273 | RFM69_PACKET_DIO_0_RX_CRC_OK LITERAL1 274 | RFM69_PACKET_DIO_0_RX_PAYLOAD_READY LITERAL1 275 | RFM69_PACKET_DIO_0_RX_SYNC_ADDRESS LITERAL1 276 | RFM69_PACKET_DIO_0_RX_RSII LITERAL1 277 | RFM69_PACKET_DIO_0_TX_PACKET_SENT LITERAL1 278 | RFM69_PACKET_DIO_0_TX_TX_READY LITERAL1 279 | RFM69_PACKET_DIO_0_TX_PLL LITERAL1 280 | RFM69_PACKET_DIO_1_FIFO_LEVEL LITERAL1 281 | RFM69_PACKET_DIO_1_FIFO_FULL LITERAL1 282 | RFM69_PACKET_DIO_1_FIFO_NOT_EMPTY LITERAL1 283 | RFM69_PACKET_DIO_2_FIFO_NOT_EMPTY LITERAL1 284 | RFM69_PACKET_DIO_2_AUTOMODE LITERAL1 285 | RFM69_PACKET_DIO_3_FIFO_FULL LITERAL1 286 | RFM69_IRQ1_MODEREADY LITERAL1 287 | RFM69_IRQ1_RXREADY LITERAL1 288 | RFM69_IRQ1_TXREADY LITERAL1 289 | RFM69_IRQ1_PLLLOCK LITERAL1 290 | RFM69_IRQ1_RSSI LITERAL1 291 | RFM69_IRQ1_TIMEOUT LITERAL1 292 | RFM69_IRQ1_AUTOMODE LITERAL1 293 | RFM69_IRQ1_SYNCADDRESSMATCH LITERAL1 294 | RFM69_IRQ2_FIFOFULL LITERAL1 295 | RFM69_IRQ2_FIFONOTEMPTY LITERAL1 296 | RFM69_IRQ2_FIFOLEVEL LITERAL1 297 | RFM69_IRQ2_FIFOOVERRUN LITERAL1 298 | RFM69_IRQ2_PACKETSENT LITERAL1 299 | RFM69_IRQ2_PAYLOADREADY LITERAL1 300 | RFM69_IRQ2_CRCOK LITERAL1 301 | RFM69_LNA_GAIN_AGC_LOOP LITERAL1 302 | RFM69_LNA_GAIN_0DB LITERAL1 303 | RFM69_LNA_GAIN_6DB LITERAL1 304 | RFM69_LNA_GAIN_12DB LITERAL1 305 | RFM69_LNA_GAIN_24DB LITERAL1 306 | RFM69_LNA_GAIN_36DB LITERAL1 307 | RFM69_LNA_GAIN_48DB LITERAL1 308 | RFM69_LNA_IMP_50OHM LITERAL1 309 | RFM69_LNA_IMP_200OHM LITERAL1 310 | RFM69_CONTINUOUS_DAGC_NORMAL LITERAL1 311 | RFM69_CONTINUOUS_DAGC_IMPROVED_AFCLOWBETAON LITERAL1 312 | RFM69_CONTINUOUS_DAGC_IMPROVED_AFCLOWBETAOFF LITERAL1 313 | 314 | -------------------------------------------------------------------------------- /plainRFM69.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Ivor Wanders 3 | * MIT License, see the LICENSE.md file in the root folder. 4 | */ 5 | 6 | 7 | #include "plainRFM69.h" 8 | 9 | 10 | 11 | /* 12 | Public Methods 13 | */ 14 | 15 | void plainRFM69::setRecommended(){ 16 | 17 | // p67, 200 ohm, internal AGC loop. 18 | this->setLNA(RFM69_LNA_IMP_200OHM, RFM69_LNA_GAIN_AGC_LOOP); 19 | 20 | // p71, 3 preamble bytes. 21 | this->setPreambleSize(3); 22 | 23 | // p71, 4 bytes sync of 0x01, only start listening when sync is matched. 24 | uint8_t syncthing[] = {1, 1, 1, 1}; 25 | this->setSyncConfig(true, false, sizeof(syncthing),0); 26 | this->setSyncValue(&syncthing, sizeof(syncthing)); 27 | 28 | // p70, -114 dBm. 29 | this->setRSSIThreshold(0xe4); 30 | 31 | // p74, Improved margin, use if AfcLowBetaOn=0 32 | this->setContinuousDagc(RFM69_CONTINUOUS_DAGC_IMPROVED_AFCLOWBETAOFF); 33 | 34 | 35 | // p63, and p21 for more information about the shaping. 36 | this->setDataModul(RFM69_DATAMODUL_PROCESSING_PACKET, RFM69_DATAMODUL_FSK, RFM69_DATAMODUL_SHAPING_GFSK_NONE); 37 | 38 | 39 | // actually recommended, but might result in packets not being transmitted with variable length. 40 | this->setFifoThreshold(RFM69_THRESHOLD_CONDITION_FIFOLEVEL,15); 41 | 42 | 43 | // p67, recommended Rx Bandwith, is changed by baudrate methods. 44 | this->setRxBw(0b010, 0b10, 0b101); 45 | // p67, no further information. 46 | this->setAfcBw(0b100, 0b01, 0b011); 47 | 48 | } 49 | 50 | 51 | void plainRFM69::setPacketType(bool variable_length, bool use_addressing){ 52 | 53 | this->use_variable_length = variable_length; 54 | this->use_addressing = use_addressing; 55 | 56 | uint8_t flags = RFM69_PACKET_CONFIG_DC_FREE_WHITENING | RFM69_PACKET_CONFIG_CRC_ON; 57 | // uint8_t flags = RFM69_PACKET_CONFIG_DC_FREE_MANCHESTER | RFM69_PACKET_CONFIG_CRC_ON; 58 | // uint8_t flags = RFM69_PACKET_CONFIG_DC_FREE_NONE | RFM69_PACKET_CONFIG_CRC_ON; // This is actually recommended, surprisingly. 59 | 60 | flags |= variable_length ? RFM69_PACKET_CONFIG_LENGTH_VARIABLE : RFM69_PACKET_CONFIG_LENGTH_FIXED; 61 | // enable variable length. 62 | 63 | flags |= use_addressing ? RFM69_PACKET_CONFIG_ADDRESS_FILTER_NODE_BROADCAST : RFM69_PACKET_CONFIG_ADDRESS_FILTER_NONE; 64 | // enable address filtering. 65 | 66 | this->setPacketConfig1(flags); 67 | 68 | // setPacketConfig2(uint8_t InterPacketRxDelay, bool RestartRx, bool AutoRxRestartOn, bool AesOn) 69 | this->setPacketConfig2(0, false, false, this->use_AES); 70 | // when we have a packet, wait until it's retreived, do not restart Rx. 71 | 72 | // Set the fifo thresshold to just start sending.... 73 | // The SPI clock _should_ be faster than the bitrate in any case. 74 | this->setFifoThreshold(RFM69_THRESHOLD_CONDITION_NOT_EMPTY, 0); 75 | 76 | 77 | } 78 | 79 | void plainRFM69::setBufferSize(uint8_t size){ 80 | this->buffer_size = size; 81 | } 82 | 83 | void plainRFM69::setPacketLength(uint8_t length){ 84 | this->packet_length = length + this->use_addressing; 85 | 86 | // allocate the packet buffer, first allocate a list of pointers 87 | this->packet_buffer = (uint8_t**) malloc(this->buffer_size * sizeof(uint8_t*)); 88 | for (uint8_t i = 0; i < this->buffer_size; i++){ 89 | // then allocate the packet length per buffer slot. 90 | this->packet_buffer[i] = (uint8_t*) malloc(this->packet_length + this->use_variable_length); 91 | } 92 | 93 | // this is mostly a separate function such that it can be overloaded. 94 | this->setRawPacketLength(); 95 | 96 | } 97 | 98 | void plainRFM69::setFrequency(uint32_t freq){ 99 | // 61 should be 61.03515625 for precision. 100 | this->setFrf(freq/61); 101 | } 102 | 103 | 104 | void plainRFM69::setAES(bool use_AES){ 105 | this->use_AES = use_AES; 106 | } 107 | 108 | 109 | void plainRFM69::setTxPower(int8_t power_level_dBm, bool enable_boost) 110 | { 111 | /* 112 | The code below is based on the knowledge gleaned from this article: 113 | 114 | https://andrehessling.de/2015/02/07/figuring-out-the-power-level-settings-of-hoperfs-rfm69-hwhcw-modules/ 115 | 116 | Additionally, I studied the following drivers for inspiration and understanding. 117 | 118 | https://github.com/ahessling/RFM69-STM32 119 | https://github.com/LowPowerLab/RFM69 120 | http://www.airspayce.com/mikem/arduino/RadioHead/classRH__RF69.html 121 | */ 122 | 123 | // Determine the hardware type. 124 | if (use_HP_module) 125 | { 126 | // ***** High power module is installed. Things are complicated. ***** 127 | 128 | /* 129 | Possible output power levels for the high-power hardware are -2 to +20 dBm. 130 | 131 | -2 to +13 Using just PA1 132 | +2 to +17 Using PA1 and PA2 133 | 134 | +3 dBm on top of the above levels is available by also enabling the 135 | RFM69_TEST_PA1 and RFM69_TEST_PA2 registers. This boost mode comes with 136 | some caveats. 137 | 138 | First, the +20dBm level is limited to a 1% Duty Cycle. Given the design of 139 | the module and the amount of power it takes to reach these levels, this 140 | makes me think the radio might have a thermal problem when these 141 | capabilities are used. 142 | 143 | Second, the datasheet explicitly states on page 22 that the high power 144 | settings must not be used in Receive mode. This requires adding the 145 | tx_power_boosted option to enable these when transmitting and disable them 146 | when receiving. 147 | 148 | Lastly, the on-module over-current protection must be disabled. This will 149 | cause strain on the underlying power system, probably explaining the need 150 | for extra capacitors in certain configurations. 151 | 152 | Because of the above, this function is set up to require that the boost 153 | mode be explicitly enabled to reach those power levels. 154 | */ 155 | 156 | // Boost mode control. 157 | if (enable_boost) 158 | { 159 | // Enable boost register control in the transmit/receive functions. 160 | this->tx_power_boosted = true; 161 | 162 | // Explicitly configure Over Current Protection as specified on page 22. 163 | this->setOCP(0); 164 | 165 | // Set the upper limit on the requested power. 166 | if (power_level_dBm > 20) power_level_dBm = 20; 167 | } else { 168 | // Set the upper limit on the requested power, without boost mode. 169 | if (power_level_dBm > 17) power_level_dBm = 17; 170 | } 171 | 172 | // The low-end power range is more restricted with high power modules. 173 | if (power_level_dBm < -2) power_level_dBm = -2; 174 | 175 | // Up to +13dBm, only the PA1 amplifier is needed. 176 | if (power_level_dBm <= 13) 177 | { 178 | this->setPALevel(RFM69_PA_LEVEL_PA1_ON, power_level_dBm + 18); 179 | } else { 180 | this->setPALevel(RFM69_PA_LEVEL_PA1_ON + RFM69_PA_LEVEL_PA2_ON, power_level_dBm + 11); 181 | } 182 | 183 | } else { 184 | // ***** Low power module is installed. Things are simple. ***** 185 | // Enable PA0 and set the power directly using an offset from the passed value. 186 | if (power_level_dBm < -18) power_level_dBm = -18; 187 | if (power_level_dBm > 13) power_level_dBm = 13; 188 | this->setPALevel(RFM69_PA_LEVEL_PA0_ON, power_level_dBm + 18); 189 | } 190 | } 191 | 192 | 193 | bool plainRFM69::canSend(){ 194 | return (this->state == RFM69_PLAIN_STATE_RECEIVING); 195 | // if we're receiving, we can send. 196 | // This is perhaps slightly naive, as a packet might be being received. 197 | 198 | /* 199 | perhaps also check RSSI: 200 | 201 | uint8_t flags1; 202 | flags1 = this->getIRQ1Flags(); 203 | debug_rfm("canSend, flags1: "); debug_rfmln(flags1); 204 | return ((this->state == RFM69_PLAIN_STATE_RECEIVING) and ((flags1 & RFM69_IRQ1_RSSI)==0)); 205 | 206 | It is more correct, but is it necessary? 207 | The IRQ1_RSSI flag is almost always set, dependent on RSSIthresshold... 208 | TODO: Investigate this if this causes problems with multiple transmitters. 209 | */ 210 | // perhaps also place a timeout on the sending state?? 211 | // Just in case an interrupt is missed. 212 | } 213 | 214 | void plainRFM69::sendAddressedVariable(uint8_t address, void* buffer, uint8_t len){ 215 | this->tx_buffer[1] = address; // set address byte. 216 | this->tx_buffer[0] = len+1; // set length, add one for address byte. 217 | memcpy(&(this->tx_buffer[2]), buffer, len); // write the payload. 218 | this->sendPacket(this->tx_buffer, len+2); // send the payload. 219 | } 220 | void plainRFM69::sendVariable(void* buffer, uint8_t len){ 221 | this->tx_buffer[0] = len; 222 | memcpy(&(this->tx_buffer[1]), buffer, len); 223 | this->sendPacket(this->tx_buffer, len+1); 224 | } 225 | 226 | void plainRFM69::sendAddressed(uint8_t address, void* buffer){ 227 | this->tx_buffer[0] = address; 228 | memcpy(&(this->tx_buffer[1]), buffer, this->packet_length); 229 | this->sendPacket(this->tx_buffer, this->packet_length+1); 230 | } 231 | 232 | void plainRFM69::send(void* buffer){ 233 | this->sendPacket(buffer, this->packet_length); 234 | } 235 | 236 | 237 | 238 | void plainRFM69::receive(){ 239 | /* 240 | Setup the automode such that we go into standby mode when a packet is 241 | available in the FIFO. Automatically go back into receiving mode when it 242 | is read. 243 | 244 | See the datasheet, p42 for information. 245 | */ 246 | 247 | this->setAutoMode(RFM69_AUTOMODE_ENTER_RISING_PAYLOADREADY, RFM69_AUTOMODE_EXIT_FALLING_FIFONOTEMPTY, RFM69_AUTOMODE_INTERMEDIATEMODE_STANDBY); 248 | // one disadvantage of this is that the PayloadReady Interrupt is not asserted. 249 | // however, the intermediate mode can be detected easily. 250 | 251 | // p22 - Turn off the high power boost registers in receiving mode. 252 | if (this->tx_power_boosted) 253 | { 254 | this->setPa13dBm1(false); 255 | this->setPa13dBm2(false); 256 | } 257 | 258 | // set the mode to receiver. 259 | this->setMode(RFM69_MODE_SEQUENCER_ON+RFM69_MODE_RECEIVER); 260 | this->state = RFM69_PLAIN_STATE_RECEIVING; 261 | } 262 | 263 | 264 | 265 | 266 | void plainRFM69::poll(){ 267 | uint8_t flags1; 268 | // uint8_t flags2; 269 | 270 | flags1 = this->getIRQ1Flags(); 271 | // flags2 = this->getIRQ2Flags(); 272 | 273 | 274 | // debug_rfm("Flags1: "); debug_rfmln(flags1); 275 | switch (this->state){ 276 | case (RFM69_PLAIN_STATE_RECEIVING): 277 | if (flags1 & RFM69_IRQ1_AUTOMODE){ 278 | debug_rfmln("Automode in receiving!"); 279 | debug_rfm("Flags1: "); debug_rfmln(flags1); 280 | debug_rfm("Flags2: "); debug_rfmln(this->getIRQ2Flags()); 281 | 282 | this->readPacket(); 283 | } 284 | break; 285 | 286 | case (RFM69_PLAIN_STATE_SENDING): 287 | if ((flags1 & RFM69_IRQ1_AUTOMODE)==0){ // no longer in automode 288 | debug_rfm("Flags1: "); debug_rfmln(flags1); 289 | debug_rfm("Flags2: "); debug_rfmln(this->getIRQ2Flags()); 290 | 291 | this->receive(); // we're done sending, set the receiving mode. 292 | } 293 | break; 294 | default: 295 | // this should not happen... 296 | debug_rfm("In undefined state!"); 297 | }; 298 | } 299 | 300 | 301 | bool plainRFM69::available(){ 302 | // return whether the indices do not align. If they do not align, read 303 | // index has to catch up. 304 | // Overflows are not handled. 305 | // TODO: handle buffer overflows. 306 | return (this->buffer_read_index != this->buffer_write_index); 307 | } 308 | 309 | uint8_t plainRFM69::read(void* buffer){ 310 | debug_rfm("Read"); 311 | 312 | // no data to return. 313 | if (buffer_read_index == buffer_write_index){ 314 | return 0; 315 | } 316 | 317 | // read packet length. 318 | uint8_t length = this->packet_length; 319 | uint8_t payload_start = 0; 320 | if (this->use_variable_length){ 321 | 322 | // if variable length is used, read length from the first byte. 323 | length = this->packet_buffer[buffer_read_index][0]; 324 | // debug_rfm("Length from packet: ");debug_rfm(length); 325 | // prevent buffer overflow, take shortest length of Rx length and packet length. 326 | length = (length > this->packet_length) ? this->packet_length : length; 327 | // debug_rfm("maxed length: ");debug_rfm(length); 328 | 329 | // payload starts one byte later. 330 | payload_start++; 331 | } 332 | 333 | // copy the message into the buffer. 334 | memcpy(buffer, &(this->packet_buffer[buffer_read_index][payload_start]), length); 335 | 336 | // increase the read index. 337 | this->buffer_read_index = (this->buffer_read_index+1) % this->buffer_size; 338 | 339 | // return the length of the packet written. 340 | return length; 341 | } 342 | 343 | 344 | 345 | // Baud rate configurations below. 346 | 347 | 348 | 349 | void plainRFM69::baud4800(){ 350 | // FXO_SC / 0x1a0b = 4799.76 ~= 4800 bps 351 | this->setBitRate(0x1a0b); 352 | 353 | // 0x52 * FSTEP = 5004.88 Hz 354 | this->setFdev(0x52); 355 | 356 | // RxBwMant=16, RxBwExp=5; 15.62 Khz in FSK 357 | // RxBwMant=0b00; RxBwExp=0b101; FXO_SC/((RxBwMant*4+16)*2**(RxBwExp+2))= 15625 Hz 358 | this->setRxBw(0b010, 0b00, 0b101); 359 | } 360 | 361 | void plainRFM69::baud9600(){ 362 | this->setBitRate(0x1a0b/2); // FXO_SC / 0x1a0b = 9599.52 ~= 9600 bps 363 | this->setFdev(0x52*2); // 0x52*2 * FSTEP = 10009.7 Hz 364 | 365 | 366 | // RxBwMant=16, RxBwExp=5; 15.62 Khz in FSK 367 | // RxBwMant=0b10; RxBwExp=0b101; FXO_SC/((RxBwMant*4+16)*2**(RxBwExp+2))= 15625 Hz 368 | this->setRxBw(0b010, 0b00, 0b101); // RxBwMant=24, RxBwExp=4; 369 | } 370 | 371 | void plainRFM69::baud153600(){ 372 | // FXO_SC / 0x1a0b = 153592.32 ~= 153600 bps 373 | this->setBitRate(0x1a0b/32); 374 | // 0x52*32 * FSTEP = 160156.25 Hz 375 | this->setFdev(0x52*32); 376 | 377 | 378 | // RxBwMant=16, RxBwExp=0; 500 Khz in FSK 379 | // RxBwMant=0b00; RxBwExp=0b00; FXO_SC/((RxBwMant*4+16)*2**(RxBwExp+2))= 500000 Hz 380 | this->setRxBw(0b010, 0b00, 0); 381 | 382 | // FDEV + BR/2 <= 500 kHz; 160156.25 + (153592.32/2) = 236952.41 383 | // \beta = (2*Fdev)/(BitRate) 384 | // \beta = (2*160156.25) / (153592.32) = 2.085472112147274 385 | 386 | // bitrate is high, use modulation shaping to prevent intersymbol interference. 387 | this->setDataModul(RFM69_DATAMODUL_PROCESSING_PACKET, RFM69_DATAMODUL_FSK, RFM69_DATAMODUL_SHAPING_GFSK_BT_0_5); 388 | } 389 | 390 | void plainRFM69::baud300000(){ 391 | 392 | // FXO_SC / 299065.42 ~= 300000 bps 393 | this->setBitRate(0x006b); 394 | // 0x52*64 * FSTEP = 320312.5 Hz 395 | this->setFdev(0x52*64); 396 | 397 | // RxBwMant=16, RxBwExp=0; 500 Khz in FSK 398 | // RxBwMant=0b00; RxBwExp=0b00; FXO_SC/((RxBwMant*4+16)*2**(RxBwExp+2))= 500000 Hz 399 | this->setRxBw(0b010, 0b00, 0); 400 | 401 | // Cut off frequency DC offset canceller: Fc = (4*RxBw)/(2pi * 2**(DccFreq+2)) 402 | // Default: 0b100, Recommended: 0b010, ~4% of RxBw. 403 | // In this case: (4*500000)/(2*pi * 2**(0b010+2)) = 19894.36 Hz 404 | 405 | 406 | // FDEV + BR/2 <= 500 kHz; 320312.5 + (300000/2) = 470312.5 407 | // \beta = (2*Fdev)/(BitRate) 408 | // \beta = (2*160156.25) / (299065.42) = 1.0710449238832094 409 | 410 | // bitrate is very high, use modulation shaping to prevent intersymbol interference. 411 | this->setDataModul(RFM69_DATAMODUL_PROCESSING_PACKET, RFM69_DATAMODUL_FSK, RFM69_DATAMODUL_SHAPING_GFSK_BT_1_0); 412 | 413 | // modulation index is lower than 2, 414 | this->setAfcCtrl(RFM69_AFC_CTRL_IMPROVED); 415 | this->setContinuousDagc(RFM69_CONTINUOUS_DAGC_IMPROVED_AFCLOWBETAON); 416 | 417 | // The user should ensure that the programmed offset exceeds the 418 | // DC canceller’s cutoff frequency, set through DccFreqAfc in RegAfcBw. 419 | // Offset = LowBetaAfcOffset x 488 Hz 420 | // Dcc_Fc / 488 = 19894.36 / 488 = 40.76 ~= 45 421 | this->setLowBetaAfcOffset(45); 422 | } 423 | 424 | 425 | void plainRFM69::emitPreamble(){ 426 | this->setMode(RFM69_MODE_SEQUENCER_OFF | RFM69_MODE_TRANSMITTER); 427 | } 428 | 429 | 430 | 431 | /* 432 | Protected Methods 433 | */ 434 | 435 | void plainRFM69::sendPacket(void* buffer, uint8_t len){ 436 | /* 437 | Just like with Receive mode, the automode is used. 438 | 439 | First, Rx mode is disabled by going into standby. 440 | Then the automode is set to start transmitting when FIFO level is above 441 | the thresshold, it stops transmitting after PacketSent is asserted. 442 | 443 | This results in a minimal Tx time and packetSent can be detected when 444 | automode is left again. 445 | 446 | */ 447 | this->setMode(RFM69_MODE_SEQUENCER_ON | RFM69_MODE_STANDBY); 448 | this->setAutoMode(RFM69_AUTOMODE_ENTER_RISING_FIFOLEVEL, RFM69_AUTOMODE_EXIT_RISING_PACKETSENT, RFM69_AUTOMODE_INTERMEDIATEMODE_TRANSMITTER); 449 | // perhaps RFM69_AUTOMODE_ENTER_RISING_FIFONOTEMPTY is faster? 450 | 451 | // set it into automode for transmitting 452 | 453 | // p22 - Turn on the high power boost registers in transmitting mode. 454 | if (this->tx_power_boosted) 455 | { 456 | this->setPa13dBm1(true); 457 | this->setPa13dBm2(true); 458 | } 459 | 460 | // write the fifo. 461 | this->state = RFM69_PLAIN_STATE_SENDING; // set the state to sending. 462 | this->writeFIFO(buffer, len); 463 | } 464 | 465 | 466 | 467 | void plainRFM69::setRawPacketLength(){ 468 | // allocate the Tx Buffer 469 | this->tx_buffer = (uint8_t*) malloc(this->packet_length + this->use_variable_length); 470 | // set the length in the hardware. 471 | this->setPayloadLength(this->packet_length); // packet length byte is not included in length count. So NOT +1 472 | 473 | 474 | 475 | } 476 | 477 | 478 | 479 | 480 | void plainRFM69::readPacket(){ 481 | // read it into the buffer. 482 | if (this->use_variable_length) { 483 | this->readVariableFIFO(this->packet_buffer[this->buffer_write_index], this->packet_length + this->use_variable_length); 484 | } else{ 485 | this->readFIFO(this->packet_buffer[this->buffer_write_index], this->packet_length); 486 | } 487 | 488 | // increase the write index. 489 | this->buffer_write_index = (this->buffer_write_index+1) % this->buffer_size; 490 | } 491 | -------------------------------------------------------------------------------- /plainRFM69.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2014, Ivor Wanders 3 | * MIT License, see the LICENSE.md file in the root folder. 4 | */ 5 | 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #ifndef PLAIN_RFM69_H 12 | #define PLAIN_RFM69_H 13 | 14 | /* 15 | The plainRFM69 object only provides the essential functions to receive and 16 | sent packets using the RFM69 radio module. It builds on top of bareRFM69 17 | to address the hardware. 18 | 19 | The normal state of the radio is to listen to packets, the poll() method 20 | should be called to obtain the packet from the radio hardare and place it 21 | into the plainRFM69's buffer. From this buffer it can then be read. 22 | 23 | The AutoModes of the radio module are used. This allows the most efficient 24 | transmissions of data, only enabling the transmitter when the data is ready 25 | to be sent. The AutoMode is also used for the receiving state, once a packet 26 | is received the radio is put in standby mode until the packet is retreived 27 | by the poll() method. 28 | 29 | The poll() method can also be attached to an interrupt, allowing efficient 30 | retrieval of packets from the FIFO and placing them in the buffer such that 31 | they are available outside of the interrupt. 32 | 33 | */ 34 | 35 | 36 | #ifdef RFM69_PLAIN_DEBUG 37 | // #if RFM69_PLAIN_DEBUG==1 38 | #define debug_rfm(a) Serial.print(a); 39 | #define debug_rfmln(a) Serial.println(a); 40 | #else 41 | #define debug_rfm(a) 42 | #define debug_rfmln(a) 43 | #endif 44 | 45 | 46 | 47 | 48 | /* 49 | Assumes we are little endian. 50 | 0A 0B 0C 0D; a=0D, a+1=0C, a+2=0B, a+3=0A 51 | */ 52 | 53 | 54 | #define RFM69_PLAIN_STATE_RECEIVING 0 55 | #define RFM69_PLAIN_STATE_SENDING 1 56 | 57 | class plainRFM69 : public bareRFM69{ 58 | protected: 59 | 60 | bool use_variable_length; 61 | bool use_addressing; 62 | bool use_AES; 63 | bool use_HP_module = false; 64 | bool tx_power_boosted = false; 65 | 66 | // state of the radio module. 67 | volatile uint8_t state; 68 | 69 | // Rx packet buffer. 70 | uint8_t packet_length; 71 | uint8_t** packet_buffer; 72 | uint8_t buffer_size; 73 | 74 | // Rx packet buffer write and read index. 75 | volatile uint8_t buffer_read_index; 76 | volatile uint8_t buffer_write_index; 77 | 78 | // Temporary buffer to compose the message in before writing to FIFO 79 | uint8_t* tx_buffer; 80 | 81 | void sendPacket(void* buffer, uint8_t len); 82 | /* 83 | Set the radio to Tx automode and write buffer up to len to the fifo. 84 | Sets the state to sending. 85 | */ 86 | 87 | virtual void readPacket(); 88 | /* 89 | Read a packet from the hardware to the internal buffer. 90 | */ 91 | 92 | virtual void setRawPacketLength(); 93 | /* 94 | Sets the real packetlength in the hardware. 95 | */ 96 | 97 | public: 98 | 99 | plainRFM69(uint8_t cs_pin) : bareRFM69(cs_pin){ 100 | this->packet_buffer = 0; 101 | this->buffer_size = 0; 102 | this->buffer_read_index = 0; 103 | this->buffer_write_index = 0; 104 | this->state = RFM69_PLAIN_STATE_RECEIVING; 105 | this->use_AES = false; 106 | }; 107 | /* 108 | 109 | Order of calling the methods is important. 110 | rfm.setRecommended(); 111 | // rfm.setAES(false); // should come before setPacketType 112 | rfm.setPacketType(false, false); 113 | 114 | rfm.setBufferSize(10); // should come after setPacketType 115 | rfm.setPacketLength(32); 116 | // should come after setBufferSize, allocates the buffer 117 | 118 | // Might have to change the fifo thresshold 119 | // rfm.setFifoThreshold(RFM69_THRESHOLD_CONDITION_FIFOLEVEL,3); // bareRFM 120 | // if we use filtering, set the node and or broadcast address here. 121 | // rfm.setNodeAddress(0x01); 122 | 123 | rfm.setFrequency(434*1000*1000); // set the frequency. 124 | rfm.baud300000(); // set the modulation parameters. 125 | rfm.receive(); // set the radio to receive. 126 | */ 127 | 128 | void setRecommended(); 129 | /* 130 | Sets various parameters in the radio module to the recommended 131 | values as in the datasheet. 132 | */ 133 | 134 | void setPacketType(bool use_variable_length, bool use_addressing); 135 | /* 136 | Sets message properties: 137 | 138 | use_variable_length = true: 139 | Uses variable length packets. 140 | 141 | use_variable_length = false: 142 | Fixed packet length. 143 | 144 | use_addressing = false: 145 | Disables filtering on address. 146 | 147 | use_addressing = true: 148 | Enables filtering on address. 149 | Set addressses with: 150 | void setNodeAddress(uint8_t address) 151 | void setBroadcastAddress(uint8_t address) 152 | 153 | This function sets: 154 | setFifoThreshold(RFM69_THRESHOLD_CONDITION_NOT_EMPTY,0); 155 | Such that transmissions start when the packetlength is below 15 156 | packets, which is the recommended value. If a slow SPI bus is used, 157 | it might be necessary to manually use setFifoThreshold(). 158 | */ 159 | void setBufferSize(uint8_t length); 160 | /* 161 | Sets the number of buffers slots to buffer messages into. 162 | 163 | Should at least two, otherwise there is no way to know whether the 164 | message has been read from the buffer. 165 | (That is the read and write index are always the same...) 166 | */ 167 | 168 | void setPacketLength(uint8_t length); 169 | /* 170 | With variable length, this sets the Rx maximum length. 171 | With fixed length this sets the packet length for both Rx and Tx. 172 | 173 | Should only be called once. And after SetBufferSize and 174 | setPacketType. 175 | 176 | The length should be between 0-64, 64 bytes length is the maximum. 177 | 178 | If AES is enabled, any length below 16 results in zero padding by 179 | the radio module. So shorter lengths than 16 bytes do not result in 180 | more efficient transmissions. 181 | 182 | The setPacketType() method sets the FIFO thresshold, to start trans- 183 | mission, however, if a slow SPI is used, it might be necessary to 184 | set an adequate transmission start thresshold using: 185 | setFifoThreshold(RFM69_THRESHOLD_CONDITION_FIFOLEVEL, X); 186 | 187 | Where X determines how many bytes must be available before the 188 | transmission starts. 189 | 190 | */ 191 | 192 | void setFrequency(uint32_t freq); 193 | /* 194 | Sets the frequency to approximately Freq. 195 | Uses 61 as Fstep instead of 61.03515625 which it actually is. 196 | For more precise control, use void setFrf from bareRFM69. 197 | 198 | Example: 199 | setFrequency((uint32_t) 450*1000*1000); sets to ~450 MHz (actually 450.259) 200 | setFrequency((uint32_t) 434*1000*1000); sets to ~434 MHz (actually 434.250) 201 | */ 202 | 203 | void setAES(bool use_AES); 204 | /* 205 | enable or disable AES. 206 | use bareRFM69::setAesKey(void* buffer, uint8_t len); to set the key. 207 | 208 | Cipher mode is ECB, so every 16 byte block is encrypted with this 209 | key, identical plaintext results in identical ciphertexts. 210 | 211 | Remember it does not provide security against replay attacks. 212 | 213 | It does provide some sort of whitening filter. 214 | */ 215 | 216 | void setHighPowerModule(){this->use_HP_module = true;}; 217 | /* 218 | Informs the library that a high-power module variant (RFM69HW or RFM69HCW) is present. 219 | */ 220 | 221 | void setTxPower(int8_t power_level_dBm, bool enable_boost = false); 222 | /* 223 | Accepts a decibel target output power between -18 and +20. 224 | 225 | The requested power will be adjusted to be within the capability range 226 | of the installed module. 227 | */ 228 | 229 | bool canSend(); 230 | /* 231 | Returns whether the module can send, or if it is busy sending. 232 | 233 | The current implementation is naive. This method can definitely be 234 | improved by taking RSSI into acocunt. 235 | 236 | If the interupt method is used, a missed interrupt currently causes 237 | the object to become stuck in the sending state. 238 | */ 239 | 240 | virtual void sendAddressedVariable(uint8_t address, void* buffer, uint8_t len); 241 | // send to specific address with variable length. Use with setPacketType(true, true). 242 | // It's not possible to send anything with length zero. 243 | 244 | virtual void sendVariable(void* buffer, uint8_t len); 245 | // send without addressing with variable length; Use with setPacketType(true, false). 246 | // It's not possible to send anything with length zero. 247 | 248 | virtual void sendAddressed(uint8_t address, void* buffer); 249 | // send with addressing and fixed length. Use with setPacketType(false, true). 250 | 251 | virtual void send(void* buffer); 252 | // send without addressing and fixed length. Use with setPacketType(false, false). 253 | 254 | void receive(); 255 | // sets the radio into receiver mode. 256 | // should be called after setup. 257 | 258 | 259 | 260 | void poll(); 261 | /* 262 | Polls the radio to check for packets in the fifo. If a packet is 263 | received, it is written to the buffer, from which it can be 264 | retreived with read(void* buffer). 265 | 266 | When the radio has received a packet, it stops receiving and waits 267 | for the packet to be removed from the FIFO. 268 | 269 | This method also puts the radio back into receiving mode after a 270 | packet has been completely sent. 271 | 272 | It is recommended to to call this method from an interrupt attached 273 | to the AutoMode indicator. 274 | */ 275 | 276 | 277 | bool available(); 278 | /* 279 | Returns true when the read pointer is not aligned with the write 280 | pointer, this means packets are available, returns false in case 281 | they align and no new packets are available. 282 | 283 | There is no method to detect overflows of the buffer. 284 | */ 285 | 286 | uint8_t read(void* buffer); 287 | /* 288 | Reads the next packet from the buffer to the pointer. 289 | 290 | If the packetlength is set to 'size' with: setPacketLength(size): 291 | 292 | Returns the number of bytes in the packet. For variable length 293 | payloads, it writes up to size bytes. 294 | 295 | For variable and addessing, it writes up to size+1 bytes. 296 | The first byte being the address to which it was sent. 297 | 298 | For addressing only, it writes up to size+1 bytes. 299 | 300 | It returns the packetlength of the packet read from the buffer. 301 | 302 | Returns zero in case no packet is available. 303 | */ 304 | 305 | 306 | void baud4800(); 307 | void baud9600(); 308 | void baud153600(); 309 | void baud300000(); 310 | 311 | void emitPreamble(); // continuously emit a preamble 312 | }; 313 | 314 | //PLAIN_RFM69_H 315 | #endif --------------------------------------------------------------------------------