V148
Fork of RadioHead-148 by
Diff: RH_RF69.h
- Revision:
- 0:ab4e012489ef
diff -r 000000000000 -r ab4e012489ef RH_RF69.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/RH_RF69.h Thu Oct 15 01:27:00 2015 +0000 @@ -0,0 +1,852 @@ +// RH_RF69.h +// Author: Mike McCauley (mikem@airspayce.com) +// Copyright (C) 2014 Mike McCauley +// $Id: RH_RF69.h,v 1.29 2015/05/17 00:11:26 mikem Exp $ +// +/// + + +#ifndef RH_RF69_h +#define RH_RF69_h + +#include <RHGenericSPI.h> +#include <RHSPIDriver.h> + +// The crystal oscillator frequency of the RF69 module +#define RH_RF69_FXOSC 32000000.0 + +// The Frequency Synthesizer step = RH_RF69_FXOSC / 2^^19 +#define RH_RF69_FSTEP (RH_RF69_FXOSC / 524288) + +// This is the maximum number of interrupts the driver can support +// Most Arduinos can handle 2, Megas can handle more +#define RH_RF69_NUM_INTERRUPTS 3 + +// This is the bit in the SPI address that marks it as a write +#define RH_RF69_SPI_WRITE_MASK 0x80 + +// Max number of octets the RH_RF69 Rx and Tx FIFOs can hold +#define RH_RF69_FIFO_SIZE 66 + +// Maximum encryptable payload length the RF69 can support +#define RH_RF69_MAX_ENCRYPTABLE_PAYLOAD_LEN 64 + +// The length of the headers we add. +// The headers are inside the RF69's payload and are therefore encrypted if encryption is enabled +#define RH_RF69_HEADER_LEN 4 + +// This is the maximum message length that can be supported by this driver. Limited by +// the size of the FIFO, since we are unable to support on-the-fly filling and emptying +// of the FIFO. +// Can be pre-defined to a smaller size (to save SRAM) prior to including this header +// Here we allow for 4 bytes of address and header and payload to be included in the 64 byte encryption limit. +// the one byte payload length is not encrpyted +#ifndef RH_RF69_MAX_MESSAGE_LEN +#define RH_RF69_MAX_MESSAGE_LEN (RH_RF69_MAX_ENCRYPTABLE_PAYLOAD_LEN - RH_RF69_HEADER_LEN) +#endif + +// Keep track of the mode the RF69 is in +#define RH_RF69_MODE_IDLE 0 +#define RH_RF69_MODE_RX 1 +#define RH_RF69_MODE_TX 2 + +// This is the default node address, +#define RH_RF69_DEFAULT_NODE_ADDRESS 0 + +// Register names +#define RH_RF69_REG_00_FIFO 0x00 +#define RH_RF69_REG_01_OPMODE 0x01 +#define RH_RF69_REG_02_DATAMODUL 0x02 +#define RH_RF69_REG_03_BITRATEMSB 0x03 +#define RH_RF69_REG_04_BITRATELSB 0x04 +#define RH_RF69_REG_05_FDEVMSB 0x05 +#define RH_RF69_REG_06_FDEVLSB 0x06 +#define RH_RF69_REG_07_FRFMSB 0x07 +#define RH_RF69_REG_08_FRFMID 0x08 +#define RH_RF69_REG_09_FRFLSB 0x09 +#define RH_RF69_REG_0A_OSC1 0x0a +#define RH_RF69_REG_0B_AFCCTRL 0x0b +#define RH_RF69_REG_0C_RESERVED 0x0c +#define RH_RF69_REG_0D_LISTEN1 0x0d +#define RH_RF69_REG_0E_LISTEN2 0x0e +#define RH_RF69_REG_0F_LISTEN3 0x0f +#define RH_RF69_REG_10_VERSION 0x10 +#define RH_RF69_REG_11_PALEVEL 0x11 +#define RH_RF69_REG_12_PARAMP 0x12 +#define RH_RF69_REG_13_OCP 0x13 +#define RH_RF69_REG_14_RESERVED 0x14 +#define RH_RF69_REG_15_RESERVED 0x15 +#define RH_RF69_REG_16_RESERVED 0x16 +#define RH_RF69_REG_17_RESERVED 0x17 +#define RH_RF69_REG_18_LNA 0x18 +#define RH_RF69_REG_19_RXBW 0x19 +#define RH_RF69_REG_1A_AFCBW 0x1a +#define RH_RF69_REG_1B_OOKPEAK 0x1b +#define RH_RF69_REG_1C_OOKAVG 0x1c +#define RH_RF69_REG_1D_OOKFIX 0x1d +#define RH_RF69_REG_1E_AFCFEI 0x1e +#define RH_RF69_REG_1F_AFCMSB 0x1f +#define RH_RF69_REG_20_AFCLSB 0x20 +#define RH_RF69_REG_21_FEIMSB 0x21 +#define RH_RF69_REG_22_FEILSB 0x22 +#define RH_RF69_REG_23_RSSICONFIG 0x23 +#define RH_RF69_REG_24_RSSIVALUE 0x24 +#define RH_RF69_REG_25_DIOMAPPING1 0x25 +#define RH_RF69_REG_26_DIOMAPPING2 0x26 +#define RH_RF69_REG_27_IRQFLAGS1 0x27 +#define RH_RF69_REG_28_IRQFLAGS2 0x28 +#define RH_RF69_REG_29_RSSITHRESH 0x29 +#define RH_RF69_REG_2A_RXTIMEOUT1 0x2a +#define RH_RF69_REG_2B_RXTIMEOUT2 0x2b +#define RH_RF69_REG_2C_PREAMBLEMSB 0x2c +#define RH_RF69_REG_2D_PREAMBLELSB 0x2d +#define RH_RF69_REG_2E_SYNCCONFIG 0x2e +#define RH_RF69_REG_2F_SYNCVALUE1 0x2f +// another 7 sync word bytes follow, 30 through 36 inclusive +#define RH_RF69_REG_37_PACKETCONFIG1 0x37 +#define RH_RF69_REG_38_PAYLOADLENGTH 0x38 +#define RH_RF69_REG_39_NODEADRS 0x39 +#define RH_RF69_REG_3A_BROADCASTADRS 0x3a +#define RH_RF69_REG_3B_AUTOMODES 0x3b +#define RH_RF69_REG_3C_FIFOTHRESH 0x3c +#define RH_RF69_REG_3D_PACKETCONFIG2 0x3d +#define RH_RF69_REG_3E_AESKEY1 0x3e +// Another 15 AES key bytes follow +#define RH_RF69_REG_4E_TEMP1 0x4e +#define RH_RF69_REG_4F_TEMP2 0x4f +#define RH_RF69_REG_58_TESTLNA 0x58 +#define RH_RF69_REG_5A_TESTPA1 0x5a +#define RH_RF69_REG_5C_TESTPA2 0x5c +#define RH_RF69_REG_6F_TESTDAGC 0x6f +#define RH_RF69_REG_71_TESTAFC 0x71 + +// These register masks etc are named wherever possible +// corresponding to the bit and field names in the RFM69 Manual + +// RH_RF69_REG_01_OPMODE +#define RH_RF69_OPMODE_SEQUENCEROFF 0x80 +#define RH_RF69_OPMODE_LISTENON 0x40 +#define RH_RF69_OPMODE_LISTENABORT 0x20 +#define RH_RF69_OPMODE_MODE 0x1c +#define RH_RF69_OPMODE_MODE_SLEEP 0x00 +#define RH_RF69_OPMODE_MODE_STDBY 0x04 +#define RH_RF69_OPMODE_MODE_FS 0x08 +#define RH_RF69_OPMODE_MODE_TX 0x0c +#define RH_RF69_OPMODE_MODE_RX 0x10 + +// RH_RF69_REG_02_DATAMODUL +#define RH_RF69_DATAMODUL_DATAMODE 0x60 +#define RH_RF69_DATAMODUL_DATAMODE_PACKET 0x00 +#define RH_RF69_DATAMODUL_DATAMODE_CONT_WITH_SYNC 0x40 +#define RH_RF69_DATAMODUL_DATAMODE_CONT_WITHOUT_SYNC 0x60 +#define RH_RF69_DATAMODUL_MODULATIONTYPE 0x18 +#define RH_RF69_DATAMODUL_MODULATIONTYPE_FSK 0x00 +#define RH_RF69_DATAMODUL_MODULATIONTYPE_OOK 0x08 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING 0x03 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_NONE 0x00 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_BT1_0 0x01 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_BT0_5 0x02 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING_FSK_BT0_3 0x03 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING_OOK_NONE 0x00 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING_OOK_BR 0x01 +#define RH_RF69_DATAMODUL_MODULATIONSHAPING_OOK_2BR 0x02 + +// RH_RF69_REG_11_PALEVEL +#define RH_RF69_PALEVEL_PA0ON 0x80 +#define RH_RF69_PALEVEL_PA1ON 0x40 +#define RH_RF69_PALEVEL_PA2ON 0x20 +#define RH_RF69_PALEVEL_OUTPUTPOWER 0x1f + +// RH_RF69_REG_23_RSSICONFIG +#define RH_RF69_RSSICONFIG_RSSIDONE 0x02 +#define RH_RF69_RSSICONFIG_RSSISTART 0x01 + +// RH_RF69_REG_25_DIOMAPPING1 +#define RH_RF69_DIOMAPPING1_DIO0MAPPING 0xc0 +#define RH_RF69_DIOMAPPING1_DIO0MAPPING_00 0x00 +#define RH_RF69_DIOMAPPING1_DIO0MAPPING_01 0x40 +#define RH_RF69_DIOMAPPING1_DIO0MAPPING_10 0x80 +#define RH_RF69_DIOMAPPING1_DIO0MAPPING_11 0xc0 + +#define RH_RF69_DIOMAPPING1_DIO1MAPPING 0x30 +#define RH_RF69_DIOMAPPING1_DIO1MAPPING_00 0x00 +#define RH_RF69_DIOMAPPING1_DIO1MAPPING_01 0x10 +#define RH_RF69_DIOMAPPING1_DIO1MAPPING_10 0x20 +#define RH_RF69_DIOMAPPING1_DIO1MAPPING_11 0x30 + +#define RH_RF69_DIOMAPPING1_DIO2MAPPING 0x0c +#define RH_RF69_DIOMAPPING1_DIO2MAPPING_00 0x00 +#define RH_RF69_DIOMAPPING1_DIO2MAPPING_01 0x04 +#define RH_RF69_DIOMAPPING1_DIO2MAPPING_10 0x08 +#define RH_RF69_DIOMAPPING1_DIO2MAPPING_11 0x0c + +#define RH_RF69_DIOMAPPING1_DIO3MAPPING 0x03 +#define RH_RF69_DIOMAPPING1_DIO3MAPPING_00 0x00 +#define RH_RF69_DIOMAPPING1_DIO3MAPPING_01 0x01 +#define RH_RF69_DIOMAPPING1_DIO3MAPPING_10 0x02 +#define RH_RF69_DIOMAPPING1_DIO3MAPPING_11 0x03 + +// RH_RF69_REG_26_DIOMAPPING2 +#define RH_RF69_DIOMAPPING2_DIO4MAPPING 0xc0 +#define RH_RF69_DIOMAPPING2_DIO4MAPPING_00 0x00 +#define RH_RF69_DIOMAPPING2_DIO4MAPPING_01 0x40 +#define RH_RF69_DIOMAPPING2_DIO4MAPPING_10 0x80 +#define RH_RF69_DIOMAPPING2_DIO4MAPPING_11 0xc0 + +#define RH_RF69_DIOMAPPING2_DIO5MAPPING 0x30 +#define RH_RF69_DIOMAPPING2_DIO5MAPPING_00 0x00 +#define RH_RF69_DIOMAPPING2_DIO5MAPPING_01 0x10 +#define RH_RF69_DIOMAPPING2_DIO5MAPPING_10 0x20 +#define RH_RF69_DIOMAPPING2_DIO5MAPPING_11 0x30 + +#define RH_RF69_DIOMAPPING2_CLKOUT 0x07 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_ 0x00 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_2 0x01 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_4 0x02 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_8 0x03 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_16 0x04 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_32 0x05 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_RC 0x06 +#define RH_RF69_DIOMAPPING2_CLKOUT_FXOSC_OFF 0x07 + +// RH_RF69_REG_27_IRQFLAGS1 +#define RH_RF69_IRQFLAGS1_MODEREADY 0x80 +#define RH_RF69_IRQFLAGS1_RXREADY 0x40 +#define RH_RF69_IRQFLAGS1_TXREADY 0x20 +#define RH_RF69_IRQFLAGS1_PLLLOCK 0x10 +#define RH_RF69_IRQFLAGS1_RSSI 0x08 +#define RH_RF69_IRQFLAGS1_TIMEOUT 0x04 +#define RH_RF69_IRQFLAGS1_AUTOMODE 0x02 +#define RH_RF69_IRQFLAGS1_SYNADDRESSMATCH 0x01 + +// RH_RF69_REG_28_IRQFLAGS2 +#define RH_RF69_IRQFLAGS2_FIFOFULL 0x80 +#define RH_RF69_IRQFLAGS2_FIFONOTEMPTY 0x40 +#define RH_RF69_IRQFLAGS2_FIFOLEVEL 0x20 +#define RH_RF69_IRQFLAGS2_FIFOOVERRUN 0x10 +#define RH_RF69_IRQFLAGS2_PACKETSENT 0x08 +#define RH_RF69_IRQFLAGS2_PAYLOADREADY 0x04 +#define RH_RF69_IRQFLAGS2_CRCOK 0x02 + +// RH_RF69_REG_2E_SYNCCONFIG +#define RH_RF69_SYNCCONFIG_SYNCON 0x80 +#define RH_RF69_SYNCCONFIG_FIFOFILLCONDITION_MANUAL 0x40 +#define RH_RF69_SYNCCONFIG_SYNCSIZE 0x38 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_1 0x00 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_2 0x08 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_3 0x10 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_4 0x18 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_5 0x20 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_6 0x28 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_7 0x30 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_8 0x38 +#define RH_RF69_SYNCCONFIG_SYNCSIZE_SYNCTOL 0x07 + +// RH_RF69_REG_37_PACKETCONFIG1 +#define RH_RF69_PACKETCONFIG1_PACKETFORMAT_VARIABLE 0x80 +#define RH_RF69_PACKETCONFIG1_DCFREE 0x60 +#define RH_RF69_PACKETCONFIG1_DCFREE_NONE 0x00 +#define RH_RF69_PACKETCONFIG1_DCFREE_MANCHESTER 0x20 +#define RH_RF69_PACKETCONFIG1_DCFREE_WHITENING 0x40 +#define RH_RF69_PACKETCONFIG1_DCFREE_RESERVED 0x60 +#define RH_RF69_PACKETCONFIG1_CRC_ON 0x10 +#define RH_RF69_PACKETCONFIG1_CRCAUTOCLEAROFF 0x08 +#define RH_RF69_PACKETCONFIG1_ADDRESSFILTERING 0x06 +#define RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_NONE 0x00 +#define RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_NODE 0x02 +#define RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_NODE_BC 0x04 +#define RH_RF69_PACKETCONFIG1_ADDRESSFILTERING_RESERVED 0x06 + +// RH_RF69_REG_3C_FIFOTHRESH +#define RH_RF69_FIFOTHRESH_TXSTARTCONDITION_NOTEMPTY 0x80 +#define RH_RF69_FIFOTHRESH_FIFOTHRESHOLD 0x7f + +// RH_RF69_REG_3D_PACKETCONFIG2 +#define RH_RF69_PACKETCONFIG2_INTERPACKETRXDELAY 0xf0 +#define RH_RF69_PACKETCONFIG2_RESTARTRX 0x04 +#define RH_RF69_PACKETCONFIG2_AUTORXRESTARTON 0x02 +#define RH_RF69_PACKETCONFIG2_AESON 0x01 + +// RH_RF69_REG_4E_TEMP1 +#define RH_RF69_TEMP1_TEMPMEASSTART 0x08 +#define RH_RF69_TEMP1_TEMPMEASRUNNING 0x04 + +// RH_RF69_REG_5A_TESTPA1 +#define RH_RF69_TESTPA1_NORMAL 0x55 +#define RH_RF69_TESTPA1_BOOST 0x5d + +// RH_RF69_REG_5C_TESTPA2 +#define RH_RF69_TESTPA2_NORMAL 0x70 +#define RH_RF69_TESTPA2_BOOST 0x7c + +// RH_RF69_REG_6F_TESTDAGC +#define RH_RF69_TESTDAGC_CONTINUOUSDAGC_NORMAL 0x00 +#define RH_RF69_TESTDAGC_CONTINUOUSDAGC_IMPROVED_LOWBETAON 0x20 +#define RH_RF69_TESTDAGC_CONTINUOUSDAGC_IMPROVED_LOWBETAOFF 0x30 + +// Define this to include Serial printing in diagnostic routines +#define RH_RF69_HAVE_SERIAL + + +///////////////////////////////////////////////////////////////////// +/// \class RH_RF69 RH_RF69.h <RH_RF69.h> +/// \brief Driver to send and receive unaddressed, unreliable datagrams via an RF69 and compatible radio transceiver. +/// +/// Works with +/// - the excellent Moteino and Moteino-USB +/// boards from LowPowerLab http://lowpowerlab.com/moteino/ +/// - compatible chips and modules such as RFM69W, RFM69HW, RFM69CW, RFM69HCW (Semtech SX1231, SX1231H), +/// - RFM69 modules from http://www.hoperfusa.com such as http://www.hoperfusa.com/details.jsp?pid=145 +/// - Anarduino MiniWireless -CW and -HW boards http://www.anarduino.com/miniwireless/ including +/// the marvellous high powered MinWireless-HW (with 20dBm output for excellent range) +/// +/// \par Overview +/// +/// This class provides basic functions for sending and receiving unaddressed, +/// unreliable datagrams of arbitrary length to 64 octets per packet. +/// +/// Manager classes may use this class to implement reliable, addressed datagrams and streams, +/// mesh routers, repeaters, translators etc. +/// +/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and +/// modulation scheme. +/// +/// This Driver provides an object-oriented interface for sending and receiving data messages with Hope-RF +/// RF69B and compatible radio modules, such as the RFM69 module. +/// +/// The Hope-RF (http://www.hoperf.com) RF69 is a low-cost ISM transceiver +/// chip. It supports FSK, GFSK, OOK over a wide range of frequencies and +/// programmable data rates. It also suports AES encryption of up to 64 octets +/// of payload It is available prepackaged on modules such as the RFM69W. And +/// such modules can be prepacked on processor boards such as the Moteino from +/// LowPowerLabs (which is what we used to develop the RH_RF69 driver) +/// +/// This Driver provides functions for sending and receiving messages of up +/// to 60 octets on any frequency supported by the RF69, in a range of +/// predefined data rates and frequency deviations. Frequency can be set with +/// 61Hz precision to any frequency from 240.0MHz to 960.0MHz. Caution: most modules only support a more limited +/// range of frequencies due to antenna tuning. +/// +/// Up to 2 RF69B modules can be connected to an Arduino (3 on a Mega), +/// permitting the construction of translators and frequency changers, etc. +/// +/// The following modulation types are suppported with a range of modem configurations for +/// common data rates and frequency deviations: +/// - GFSK Gaussian Frequency Shift Keying +/// - FSK Frequency Shift Keying +/// +/// Support for other RF69 features such as on-chip temperature measurement, +/// transmitter power control etc is also provided. +/// +/// Tested on USB-Moteino with arduino-1.0.5 +/// on OpenSuSE 13.1 +/// +/// \par Packet Format +/// +/// All messages sent and received by this RH_RF69 Driver conform to this packet format: +/// +/// - 4 octets PREAMBLE +/// - 2 octets SYNC 0x2d, 0xd4 (configurable, so you can use this as a network filter) +/// - 1 octet RH_RF69 payload length +/// - 4 octets HEADER: (TO, FROM, ID, FLAGS) +/// - 0 to 60 octets DATA +/// - 2 octets CRC computed with CRC16(IBM), computed on HEADER and DATA +/// +/// For technical reasons, the message format is not protocol compatible with the +/// 'HopeRF Radio Transceiver Message Library for Arduino' +/// http://www.airspayce.com/mikem/arduino/HopeRF from the same author. Nor is +/// it compatible with messages sent by 'Virtual Wire' +/// http://www.airspayce.com/mikem/arduino/VirtualWire.pdf also from the same +/// author. Nor is it compatible with messages sent by 'RF22' +/// http://www.airspayce.com/mikem/arduino/RF22 also from the same author. +/// +/// \par Connecting RFM-69 to Arduino +/// +/// We tested with Moteino, which is an Arduino Uno compatible with the RFM69W +/// module on-board. Therefore it needs no connections other than the USB +/// programming connection and an antenna to make it work. +/// +/// If you have a bare RFM69W that you want to connect to an Arduino, you +/// might use these connections (untested): CAUTION: you must use a 3.3V type +/// Arduino, otherwise you will also need voltage level shifters between the +/// Arduino and the RFM69. CAUTION, you must also ensure you connect an +/// antenna +/// +/// \code +/// Arduino RFM69W +/// GND----------GND (ground in) +/// 3V3----------3.3V (3.3V in) +/// interrupt 0 pin D2-----------DIO0 (interrupt request out) +/// SS pin D10----------NSS (chip select in) +/// SCK pin D13----------SCK (SPI clock in) +/// MOSI pin D11----------MOSI (SPI Data in) +/// MISO pin D12----------MISO (SPI Data out) +/// \endcode +/// +/// With these connections, you can then use the default constructor RH_RF69(). +/// You can override the default settings for the SS pin and the interrupt in +/// the RH_RF69 constructor if you wish to connect the slave select SS to other +/// than the normal one for your Arduino (D10 for Diecimila, Uno etc and D53 +/// for Mega) or the interrupt request to other than pin D2 (Caution, +/// different processors have different constraints as to the pins available +/// for interrupts). +/// +/// If you have a Teensy 3.1 and a compatible RFM69 breakout board, you will need to +/// construct the RH_RF69 instance like this: +/// \code +/// RH_RF69 driver(15, 16); +/// \endcode +/// +/// If you have a MoteinoMEGA https://lowpowerlab.com/shop/moteinomega +/// with RFM69 on board, you dont need to make any wiring connections +/// (the RFM69 module is soldered onto the MotienoMEGA), but you must initialise the RH_RF69 +/// constructor like this: +/// \code +/// RH_RF69 driver(4, 2); +/// \endcode +/// Make sure you have the MoteinoMEGA core installed in your Arduino hardware folder as described in the +/// documentation for the MoteinoMEGA. +/// +/// It is possible to have 2 or more radios connected to one Arduino, provided +/// each radio has its own SS and interrupt line (SCK, SDI and SDO are common +/// to all radios) +/// +/// Caution: on some Arduinos such as the Mega 2560, if you set the slave +/// select pin to be other than the usual SS pin (D53 on Mega 2560), you may +/// need to set the usual SS pin to be an output to force the Arduino into SPI +/// master mode. +/// +/// Caution: Power supply requirements of the RF69 module may be relevant in some circumstances: +/// RF69 modules are capable of pulling 45mA+ at full power, where Arduino's 3.3V line can +/// give 50mA. You may need to make provision for alternate power supply for +/// the RF69, especially if you wish to use full transmit power, and/or you have +/// other shields demanding power. Inadequate power for the RF69 is likely to cause symptoms such as: +/// -reset's/bootups terminate with "init failed" messages +/// -random termination of communication after 5-30 packets sent/received +/// -"fake ok" state, where initialization passes fluently, but communication doesn't happen +/// -shields hang Arduino boards, especially during the flashing +/// \par Interrupts +/// +/// The RH_RF69 driver uses interrupts to react to events in the RF69 module, +/// such as the reception of a new packet, or the completion of transmission +/// of a packet. The RH_RF69 driver interrupt service routine reads status from +/// and writes data to the the RF69 module via the SPI interface. It is very +/// important therefore, that if you are using the RH_RF69 driver with another +/// SPI based deviced, that you disable interrupts while you transfer data to +/// and from that other device. Use cli() to disable interrupts and sei() to +/// reenable them. +/// +/// \par Memory +/// +/// The RH_RF69 driver requires non-trivial amounts of memory. The sample +/// programs above all compile to about 8kbytes each, which will fit in the +/// flash proram memory of most Arduinos. However, the RAM requirements are +/// more critical. Therefore, you should be vary sparing with RAM use in +/// programs that use the RH_RF69 driver. +/// +/// It is often hard to accurately identify when you are hitting RAM limits on Arduino. +/// The symptoms can include: +/// - Mysterious crashes and restarts +/// - Changes in behaviour when seemingly unrelated changes are made (such as adding print() statements) +/// - Hanging +/// - Output from Serial.print() not appearing +/// +/// \par Automatic Frequency Control (AFC) +/// +/// The RF69 module is configured by the RH_RF69 driver to always use AFC. +/// +/// \par Transmitter Power +/// +/// You can control the transmitter power on the RF69 transceiver +/// with the RH_RF69::setTxPower() function. The argument can be any of +/// -18 to +13 (for RF69W) or -14 to 20 (for RF69HW) +/// The default is 13. Eg: +/// \code +/// driver.setTxPower(-5); +/// \endcode +/// +/// We have made some actual power measurements against +/// programmed power for Moteino (with RF69W) +/// - Moteino (with RF69W), USB power +/// - 10cm RG58C/U soldered direct to RFM69 module ANT and GND +/// - bnc connecteor +/// - 12dB attenuator +/// - BNC-SMA adapter +/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set) +/// - Tektronix TDS220 scope to measure the Vout from power head +/// \code +/// Program power Measured Power +/// dBm dBm +/// -18 -17 +/// -16 -16 +/// -14 -14 +/// -12 -12 +/// -10 -9 +/// -8 -7 +/// -6 -4 +/// -4 -3 +/// -2 -2 +/// 0 0.2 +/// 2 3 +/// 4 5 +/// 6 7 +/// 8 10 +/// 10 13 +/// 12 14 +/// 13 15 +/// 14 -51 +/// 20 -51 +/// \endcode +/// We have also made some actual power measurements against +/// programmed power for Anarduino MiniWireless with RFM69-HW +/// Anarduino MiniWireless (with RFM69-HW), USB power +/// - 10cm RG58C/U soldered direct to RFM69 module ANT and GND +/// - bnc connecteor +/// - 2x12dB attenuators +/// - BNC-SMA adapter +/// - MiniKits AD8307 HF/VHF Power Head (calibrated against Rohde&Schwartz 806.2020 test set) +/// - Tektronix TDS220 scope to measure the Vout from power head +/// \code +/// Program power Measured Power +/// dBm dBm +/// -18 no measurable output +/// 0 no measurable output +/// 13 no measurable output +/// 14 11 +/// 15 12 +/// 16 12.4 +/// 17 14 +/// 18 15 +/// 19 15.8 +/// 20 17 +/// \endcode +/// (Caution: we dont claim laboratory accuracy for these measurements) +/// You would not expect to get anywhere near these powers to air with a simple 1/4 wavelength wire antenna. +/// Caution: although the RFM69 appears to have a PC antenna on board, you will get much better power and range even +/// with just a 1/4 wave wire antenna. +/// +/// \par Performance +/// +/// Some simple speed performance tests have been conducted. +/// In general packet transmission rate will be limited by the modulation scheme. +/// Also, if your code does any slow operations like Serial printing it will also limit performance. +/// We disabled any printing in the tests below. +/// We tested with RH_RF69::GFSK_Rb250Fd250, which is probably the fastest scheme available. +/// We tested with a 13 octet message length, over a very short distance of 10cm. +/// +/// Transmission (no reply) tests with modulation RH_RF69::GFSK_Rb250Fd250 and a +/// 13 octet message show about 152 messages per second transmitted and received. +/// +/// Transmit-and-wait-for-a-reply tests with modulation RH_RF69::GFSK_Rb250Fd250 and a +/// 13 octet message (send and receive) show about 68 round trips per second. +/// +class RH_RF69 : public RHSPIDriver +{ +public: + + /// \brief Defines register values for a set of modem configuration registers + /// + /// Defines register values for a set of modem configuration registers + /// that can be passed to setModemRegisters() if none of the choices in + /// ModemConfigChoice suit your need setModemRegisters() writes the + /// register values from this structure to the appropriate RF69 registers + /// to set the desired modulation type, data rate and deviation/bandwidth. + typedef struct + { + uint8_t reg_02; ///< Value for register RH_RF69_REG_02_DATAMODUL + uint8_t reg_03; ///< Value for register RH_RF69_REG_03_BITRATEMSB + uint8_t reg_04; ///< Value for register RH_RF69_REG_04_BITRATELSB + uint8_t reg_05; ///< Value for register RH_RF69_REG_05_FDEVMSB + uint8_t reg_06; ///< Value for register RH_RF69_REG_06_FDEVLSB + uint8_t reg_19; ///< Value for register RH_RF69_REG_19_RXBW + uint8_t reg_1a; ///< Value for register RH_RF69_REG_1A_AFCBW + uint8_t reg_37; ///< Value for register RH_RF69_REG_37_PACKETCONFIG1 + } ModemConfig; + + /// Choices for setModemConfig() for a selected subset of common + /// modulation types, and data rates. If you need another configuration, + /// use the register calculator. and call setModemRegisters() with your + /// desired settings. + /// These are indexes into MODEM_CONFIG_TABLE. We strongly recommend you use these symbolic + /// definitions and not their integer equivalents: its possible that new values will be + /// introduced in later versions (though we will try to avoid it). + /// CAUTION: some of these configurations do not work corectly and are marked as such. + typedef enum + { + FSK_Rb2Fd5 = 0, ///< FSK, Whitening, Rb = 2kbs, Fd = 5kHz + FSK_Rb2_4Fd4_8, ///< FSK, Whitening, Rb = 2.4kbs, Fd = 4.8kHz + FSK_Rb4_8Fd9_6, ///< FSK, Whitening, Rb = 4.8kbs, Fd = 9.6kHz + FSK_Rb9_6Fd19_2, ///< FSK, Whitening, Rb = 9.6kbs, Fd = 19.2kHz + FSK_Rb19_2Fd38_4, ///< FSK, Whitening, Rb = 19.2kbs, Fd = 38.4kHz + FSK_Rb38_4Fd76_8, ///< FSK, Whitening, Rb = 38.4kbs, Fd = 76.8kHz + FSK_Rb57_6Fd120, ///< FSK, Whitening, Rb = 57.6kbs, Fd = 120kHz + FSK_Rb125Fd125, ///< FSK, Whitening, Rb = 125kbs, Fd = 125kHz + FSK_Rb250Fd250, ///< FSK, Whitening, Rb = 250kbs, Fd = 250kHz + FSK_Rb55555Fd50, ///< FSK, Whitening, Rb = 55555kbs,Fd = 50kHz for RFM69 lib compatibility + + GFSK_Rb2Fd5, ///< GFSK, Whitening, Rb = 2kbs, Fd = 5kHz + GFSK_Rb2_4Fd4_8, ///< GFSK, Whitening, Rb = 2.4kbs, Fd = 4.8kHz + GFSK_Rb4_8Fd9_6, ///< GFSK, Whitening, Rb = 4.8kbs, Fd = 9.6kHz + GFSK_Rb9_6Fd19_2, ///< GFSK, Whitening, Rb = 9.6kbs, Fd = 19.2kHz + GFSK_Rb19_2Fd38_4, ///< GFSK, Whitening, Rb = 19.2kbs, Fd = 38.4kHz + GFSK_Rb38_4Fd76_8, ///< GFSK, Whitening, Rb = 38.4kbs, Fd = 76.8kHz + GFSK_Rb57_6Fd120, ///< GFSK, Whitening, Rb = 57.6kbs, Fd = 120kHz + GFSK_Rb125Fd125, ///< GFSK, Whitening, Rb = 125kbs, Fd = 125kHz + GFSK_Rb250Fd250, ///< GFSK, Whitening, Rb = 250kbs, Fd = 250kHz + GFSK_Rb55555Fd50, ///< GFSK, Whitening, Rb = 55555kbs,Fd = 50kHz + + OOK_Rb1Bw1, ///< OOK, Whitening, Rb = 1kbs, Rx Bandwidth = 1kHz. + OOK_Rb1_2Bw75, ///< OOK, Whitening, Rb = 1.2kbs, Rx Bandwidth = 75kHz. + OOK_Rb2_4Bw4_8, ///< OOK, Whitening, Rb = 2.4kbs, Rx Bandwidth = 4.8kHz. + OOK_Rb4_8Bw9_6, ///< OOK, Whitening, Rb = 4.8kbs, Rx Bandwidth = 9.6kHz. + OOK_Rb9_6Bw19_2, ///< OOK, Whitening, Rb = 9.6kbs, Rx Bandwidth = 19.2kHz. + OOK_Rb19_2Bw38_4, ///< OOK, Whitening, Rb = 19.2kbs, Rx Bandwidth = 38.4kHz. + OOK_Rb32Bw64, ///< OOK, Whitening, Rb = 32kbs, Rx Bandwidth = 64kHz. + +// Test, + } ModemConfigChoice; + + /// Constructor. You can have multiple instances, but each instance must have its own + /// interrupt and slave select pin. After constructing, you must call init() to initialise the interface + /// and the radio module. A maximum of 3 instances can co-exist on one processor, provided there are sufficient + /// distinct interrupt lines, one for each instance. + /// \param[in] slaveSelectPin the Arduino pin number of the output to use to select the RF69 before + /// accessing it. Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega, D10 for Maple) + /// \param[in] interruptPin The interrupt Pin number that is connected to the RF69 DIO0 interrupt line. + /// Defaults to pin 2. + /// Caution: You must specify an interrupt capable pin. + /// On many Arduino boards, there are limitations as to which pins may be used as interrupts. + /// On Leonardo pins 0, 1, 2 or 3. On Mega2560 pins 2, 3, 18, 19, 20, 21. On Due and Teensy, any digital pin. + /// On other Arduinos pins 2 or 3. + /// See http://arduino.cc/en/Reference/attachInterrupt for more details. + /// On Chipkit Uno32, pins 38, 2, 7, 8, 35. + /// On other boards, any digital pin may be used. + /// \param[in] spi Pointer to the SPI interface object to use. + /// Defaults to the standard Arduino hardware SPI interface + RH_RF69(PINS slaveSelectPin, PINS interruptPin, RHGenericSPI& spi = hardware_spi); + + /// Initialises this instance and the radio module connected to it. + /// The following steps are taken: + /// - Initialise the slave select pin and the SPI interface library + /// - Checks the connected RF69 module can be communicated + /// - Attaches an interrupt handler + /// - Configures the RF69 module + /// - Sets the frequency to 434.0 MHz + /// - Sets the modem data rate to FSK_Rb2Fd5 + /// \return true if everything was successful + bool init(); + + /// Reads the on-chip temperature sensor. + /// The RF69 must be in Idle mode (= RF69 Standby) to measure temperature. + /// The measurement is uncalibrated and without calibration, you can expect it to be far from + /// correct. + /// \return The measured temperature, in degrees C from -40 to 85 (uncalibrated) + int8_t temperatureRead(); + + /// Sets the transmitter and receiver + /// centre frequency + /// \param[in] centre Frequency in MHz. 240.0 to 960.0. Caution, RF69 comes in several + /// different frequency ranges, and setting a frequency outside that range of your radio will probably not work + /// \param[in] afcPullInRange Not used + /// \return true if the selected frquency centre is within range + bool setFrequency(float centre, float afcPullInRange = 0.05); + + /// Reads and returns the current RSSI value. + /// Causes the current signal strength to be measured and returned + /// If you want to find the RSSI + /// of the last received message, use lastRssi() instead. + /// \return The current RSSI value on units of 0.5dB. + int8_t rssiRead(); + + /// Sets the parameters for the RF69 OPMODE. + /// This is a low level device access function, and should not normally ned to be used by user code. + /// Instead can use stModeRx(), setModeTx(), setModeIdle() + /// \param[in] mode RF69 OPMODE to set, one of RH_RF69_OPMODE_MODE_*. + void setOpMode(uint8_t mode); + + /// If current mode is Rx or Tx changes it to Idle. If the transmitter or receiver is running, + /// disables them. + void setModeIdle(); + + /// If current mode is Tx or Idle, changes it to Rx. + /// Starts the receiver in the RF69. + void setModeRx(); + + /// If current mode is Rx or Idle, changes it to Rx. F + /// Starts the transmitter in the RF69. + void setModeTx(); + + /// Sets the transmitter power output level. + /// Be a good neighbour and set the lowest power level you need. + /// Caution: legal power limits may apply in certain countries. + /// After init(), the power will be set to 13dBm. + /// \param[in] power Transmitter power level in dBm. For RF69W, valid values are from -18 to +13 + /// (higher power settings disable the transmitter). + /// For RF69HW, valid values are from +14 to +20. Caution: at +20dBm, duty cycle is limited to 1% and a + /// maximum VSWR of 3:1 at the antenna port. + void setTxPower(int8_t power); + + /// Sets all the registers required to configure the data modem in the RF69, including the data rate, + /// bandwidths etc. You can use this to configure the modem with custom configurations if none of the + /// canned configurations in ModemConfigChoice suit you. + /// \param[in] config A ModemConfig structure containing values for the modem configuration registers. + void setModemRegisters(const ModemConfig* config); + + /// Select one of the predefined modem configurations. If you need a modem configuration not provided + /// here, use setModemRegisters() with your own ModemConfig. The default after init() is RH_RF69::GFSK_Rb250Fd250. + /// \param[in] index The configuration choice. + /// \return true if index is a valid choice. + bool setModemConfig(ModemConfigChoice index); + + /// Starts the receiver and checks whether a received message is available. + /// This can be called multiple times in a timeout loop + /// \return true if a complete, valid message has been received and is able to be retrieved by + /// recv() + bool available(); + + /// Turns the receiver on if it not already on. + /// If there is a valid message available, copy it to buf and return true + /// else return false. + /// If a message is copied, *len is set to the length (Caution, 0 length messages are permitted). + /// You should be sure to call this function frequently enough to not miss any messages + /// It is recommended that you call it in your main loop. + /// \param[in] buf Location to copy the received message + /// \param[in,out] len Pointer to available space in buf. Set to the actual number of octets copied. + /// \return true if a valid message was copied to buf + bool recv(uint8_t* buf, uint8_t* len); + + /// Waits until any previous transmit packet is finished being transmitted with waitPacketSent(). + /// Then loads a message into the transmitter and starts the transmitter. Note that a message length + /// of 0 is NOT permitted. + /// \param[in] data Array of data to be sent + /// \param[in] len Number of bytes of data to send (> 0) + /// \return true if the message length was valid and it was correctly queued for transmit + bool send(const uint8_t* data, uint8_t len); + + /// Sets the length of the preamble + /// in bytes. + /// Caution: this should be set to the same + /// value on all nodes in your network. Default is 4. + /// Sets the message preamble length in REG_0?_PREAMBLE?SB + /// \param[in] bytes Preamble length in bytes. + void setPreambleLength(uint16_t bytes); + + /// Sets the sync words for transmit and receive + /// Caution: SyncWords should be set to the same + /// value on all nodes in your network. Nodes with different SyncWords set will never receive + /// each others messages, so different SyncWords can be used to isolate different + /// networks from each other. Default is { 0x2d, 0xd4 }. + /// \param[in] syncWords Array of sync words, 1 to 4 octets long. NULL if no sync words to be used. + /// \param[in] len Number of sync words to set, 1 to 4. 0 if no sync words to be used. + void setSyncWords(const uint8_t* syncWords = NULL, uint8_t len = 0); + + /// Enables AES encryption and sets the AES encryption key, used + /// to encrypt and decrypt all messages. The default is disabled. + /// \param[in] key The key to use. Must be 16 bytes long. The same key must be installed + /// in other instances of RF69, otherwise communications will not work correctly. If key is NULL, + /// encryption is disabled. + void setEncryptionKey(uint8_t* key = NULL); + + /// Returns the time in millis since the most recent preamble was received, and when the most recent + /// RSSI measurement was made. + uint32_t getLastPreambleTime(); + + /// The maximum message length supported by this driver + /// \return The maximum message length supported by this driver + uint8_t maxMessageLength(); + + /// Prints the value of a single register + /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform + /// For debugging/testing only + /// \return true if successful + bool printRegister(uint8_t reg); + + /// Prints the value of all the RF69 registers + /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform + /// For debugging/testing only + /// \return true if successful + bool printRegisters(); + + /// Sets the radio operating mode for the case when the driver is idle (ie not + /// transmitting or receiving), allowing you to control the idle mode power requirements + /// at the expense of slower transitions to transmit and receive modes. + /// By default, the idle mode is RH_RF69_OPMODE_MODE_STDBY, + /// but eg setIdleMode(RH_RF69_OPMODE_MODE_SLEEP) will provide a much lower + /// idle current but slower transitions. Call this function after init(). + /// \param[in] idleMode The chip operating mode to use when the driver is idle. One of RH_RF69_OPMODE_* + void setIdleMode(uint8_t idleMode); + + /// Sets the radio into low-power sleep mode. + /// If successful, the transport will stay in sleep mode until woken by + /// changing mode it idle, transmit or receive (eg by calling send(), recv(), available() etc) + /// Caution: there is a time penalty as the radio takes a finite time to wake from sleep mode. + /// \return true if sleep mode was successfully entered. + virtual bool sleep(); + +protected: + /// This is a low level function to handle the interrupts for one instance of RF69. + /// Called automatically by isr*() + /// Should not need to be called by user code. + void handleInterrupt(); + + /// Low level function to read the FIFO and put the received data into the receive buffer + /// Should not need to be called by user code. + void readFifo(); + +protected: + /// Low level interrupt service routine for RF69 connected to interrupt 0 + static void isr0(); + + /// Low level interrupt service routine for RF69 connected to interrupt 1 + static void isr1(); + + /// Low level interrupt service routine for RF69 connected to interrupt 1 + static void isr2(); + + /// Array of instances connected to interrupts 0 and 1 + static RH_RF69* _deviceForInterrupt[]; + + /// Index of next interrupt number to use in _deviceForInterrupt + static uint8_t _interruptCount; + +#if (RH_PLATFORM == RH_PLATFORM_MBED) + /// The configured interrupt pin connected to this instance + InterruptIn _interruptPin; +#else + /// The configured interrupt pin connected to this instance + uint8_t _interruptPin; +#endif + + /// The index into _deviceForInterrupt[] for this device (if an interrupt is already allocated) + /// else 0xff + uint8_t _myInterruptIndex; + + /// The radio OP mode to use when mode is RHModeIdle + uint8_t _idleMode; + + /// The reported device type + uint8_t _deviceType; + + /// The selected output power in dBm + int8_t _power; + + /// The message length in _buf + volatile uint8_t _bufLen; + + /// Array of octets of teh last received message or the next to transmit message + uint8_t _buf[RH_RF69_MAX_MESSAGE_LEN]; + + /// True when there is a valid message in the Rx buffer + volatile bool _rxBufValid; + + /// Time in millis since the last preamble was received (and the last time the RSSI was measured) + uint32_t _lastPreambleTime; +}; + +/// @example rf69_client.pde +/// @example rf69_server.pde +/// @example rf69_reliable_datagram_client.pde +/// @example rf69_reliable_datagram_server.pde + + +#endif