// RH_MRF89.h
//
// Definitions for Microchip MRF89XA family radios radios per:
// http://ww1.microchip.com/downloads/en/DeviceDoc/70622C.pdf
// http://ww1.microchip.com/downloads/en/DeviceDoc/75017B.pdf
//
// Author: Mike McCauley (mikem@airspayce.com)
// Copyright (C) 2015 Mike McCauley
// $Id: RH_MRF89.h,v 1.6 2015/12/17 10:58:13 mikem Exp $
// 

#ifndef RH_RF95_h
#define RH_RF95_h

#include <RHNRFSPIDriver.h>

// This is the maximum number of interrupts the driver can support
// Most Arduinos can handle 2, Megas can handle more
#define RH_MRF89_NUM_INTERRUPTS 3

// Max number of octets the MRF89XA Rx/Tx FIFO can hold
#define RH_MRF89_FIFO_SIZE 64

// This is the maximum number of bytes that can be carried by the MRF89XA.
// We use some for headers, keeping fewer for RadioHead messages
#define RH_MRF89_MAX_PAYLOAD_LEN RH_MRF89_FIFO_SIZE

// The length of the headers we add.
// The headers are inside the MRF89XA payload
#define RH_MRF89_HEADER_LEN 4
    
// This is the maximum user message length that can be supported by this driver. 
// Can be pre-defined to a smaller size (to save SRAM) prior to including this header
// Here we allow for 4 bytes headers, user data. Message length and CRC are automatically encoded and decoded by 
// the MRF89XA
#ifndef RH_MRF89_MAX_MESSAGE_LEN
 #define RH_MRF89_MAX_MESSAGE_LEN (RH_MRF89_MAX_PAYLOAD_LEN - RH_MRF89_HEADER_LEN)
#endif

// Bits that must be set to do a SPI read
#define RH_MRF89_SPI_READ_MASK              0x40

// The MRF89XA crystal frequency in MHz
#define RH_MRF89_XTAL_FREQ                  12.8

// Register names from Figure 2-18
#define RH_MRF89_REG_00_GCONREG             0x00
#define RH_MRF89_REG_01_DMODREG             0x01
#define RH_MRF89_REG_02_FDEVREG             0x02
#define RH_MRF89_REG_03_BRSREG              0x03
#define RH_MRF89_REG_04_FLTHREG             0x04
#define RH_MRF89_REG_05_FIFOCREG            0x05
#define RH_MRF89_REG_06_R1CREG              0x06
#define RH_MRF89_REG_07_P1CREG              0x07
#define RH_MRF89_REG_08_S1CREG              0x08
#define RH_MRF89_REG_09_R2CREG              0x09
#define RH_MRF89_REG_0A_P2CREG              0x0a
#define RH_MRF89_REG_0B_S2CREG              0x0b
#define RH_MRF89_REG_0C_PACREG              0x0c
#define RH_MRF89_REG_0D_FTXRXIREG           0x0d
#define RH_MRF89_REG_0E_FTPRIREG            0x0e
#define RH_MRF89_REG_0F_RSTHIREG            0x0f
#define RH_MRF89_REG_10_FILCREG             0x10
#define RH_MRF89_REG_11_PFCREG              0x11
#define RH_MRF89_REG_12_SYNCREG             0x12
// Hmm the addresses of the next 2 is ambiguous in the docs
// this seems to agree with whats in the chip:
#define RH_MRF89_REG_13_RSVREG              0x13
#define RH_MRF89_REG_14_RSTSREG             0x14
#define RH_MRF89_REG_15_OOKCREG             0x15
#define RH_MRF89_REG_16_SYNCV31REG          0x16
#define RH_MRF89_REG_17_SYNCV23REG          0x17
#define RH_MRF89_REG_18_SYNCV15REG          0x18
#define RH_MRF89_REG_19_SYNCV07REG          0x19
#define RH_MRF89_REG_1A_TXCONREG            0x1a
#define RH_MRF89_REG_1B_CLKOREG             0x1b
#define RH_MRF89_REG_1C_PLOADREG            0x1c
#define RH_MRF89_REG_1D_NADDSREG            0x1d
#define RH_MRF89_REG_1E_PKTCREG             0x1e
#define RH_MRF89_REG_1F_FCRCREG             0x1f

// Register bitfield definitions
//#define RH_MRF89_REG_00_GCONREG             0x00
#define RH_MRF89_CMOD                       0xe0
#define RH_MRF89_CMOD_TRANSMIT              0x80
#define RH_MRF89_CMOD_RECEIVE               0x60
#define RH_MRF89_CMOD_FS                    0x40
#define RH_MRF89_CMOD_STANDBY               0x20
#define RH_MRF89_CMOD_SLEEP                 0x00

#define RH_MRF89_FBS                        0x18
#define RH_MRF89_FBS_950_960                0x10
#define RH_MRF89_FBS_915_928                0x08
#define RH_MRF89_FBS_902_915                0x00

#define RH_MRF89_VCOT                       0x06
#define RH_MRF89_VCOT_180MV                 0x06
#define RH_MRF89_VCOT_120MV                 0x04
#define RH_MRF89_VCOT_60MV                  0x02
#define RH_MRF89_VCOT_TANK                  0x00

#define RH_MRF89_RPS                        0x01

//#define RH_MRF89_REG_01_DMODREG             0x01
#define RH_MRF89_MODSEL                     0xc0
#define RH_MRF89_MODSEL_FSK                 0x80
#define RH_MRF89_MODSEL_OOK                 0x40

#define RH_MRF89_DMODE0                     0x20

#define RH_MRF89_OOKTYP                     0x18
#define RH_MRF89_OOKTYP_AVERAGE             0x10
#define RH_MRF89_OOKTYP_PEAK                0x08
#define RH_MRF89_OOKTYP_FIXED               0x00

#define RH_MRF89_DMODE1                     0x04

#define RH_MRF89_IFGAIN                     0x03
#define RH_MRF89_IFGAIN_M13P5               0x03
#define RH_MRF89_IFGAIN_M9                  0x02
#define RH_MRF89_IFGAIN_M4P5                0x01
#define RH_MRF89_IFGAIN_0                   0x00

// DMODE1 and DMODE1:
#define RH_MRF89_OPMODE_CONTINUOUS          0x00
#define RH_MRF89_OPMODE_BUFFER              RH_MRF89_DMODE0
#define RH_MRF89_OPMODE_PACKET              RH_MRF89_DMODE1

//#define RH_MRF89_REG_03_BRSREG              0x03
#define RH_MRF89_BRVAL                      0x7f

//#define RH_MRF89_REG_05_FIFOCREG            0x05
#define RH_MRF89_FSIZE                      0xc0
#define RH_MRF89_FSIZE_64                   0xc0
#define RH_MRF89_FSIZE_48                   0x80
#define RH_MRF89_FSIZE_32                   0x40
#define RH_MRF89_FSIZE_16                   0x00

#define RH_MRF89_FTINT                      0x3f

//#define RH_MRF89_REG_0C_PACREG              0x0c
#define RH_MRF89_PARC                       0x18
#define RH_MRF89_PARC_23                    0x18
#define RH_MRF89_PARC_15                    0x10
#define RH_MRF89_PARC_8P5                   0x08
#define RH_MRF89_PARC_3                     0x00

//#define RH_MRF89_REG_0D_FTXRXIREG           0x0d
#define RH_MRF89_IRQ0RXS                    0xc0
#define RH_MRF89_IRQ0RXS_CONT_RSSI          0x40
#define RH_MRF89_IRQ0RXS_CONT_SYNC          0x00
#define RH_MRF89_IRQ0RXS_BUFFER_SYNC        0xc0
#define RH_MRF89_IRQ0RXS_BUFFER_FIFOEMPTY   0x80
#define RH_MRF89_IRQ0RXS_BUFFER_WRITEBYTE   0x40
#define RH_MRF89_IRQ0RXS_BUFFER_NONE        0x00
#define RH_MRF89_IRQ0RXS_PACKET_SYNC        0xc0
#define RH_MRF89_IRQ0RXS_PACKET_FIFOEMPTY   0x80
#define RH_MRF89_IRQ0RXS_PACKET_WRITEBYTE   0x40
#define RH_MRF89_IRQ0RXS_PACKET_PLREADY     0x00

#define RH_MRF89_IRQ1RXS                    0x30
#define RH_MRF89_IRQ1RXS_CONT_DCLK          0x00
#define RH_MRF89_IRQ1RXS_BUFFER_FIFO_THRESH 0x30
#define RH_MRF89_IRQ1RXS_BUFFER_RSSI        0x20
#define RH_MRF89_IRQ1RXS_BUFFER_FIFOFULL    0x10
#define RH_MRF89_IRQ1RXS_BUFFER_NONE        0x00
#define RH_MRF89_IRQ1RXS_PACKET_FIFO_THRESH 0x30
#define RH_MRF89_IRQ1RXS_PACKET_RSSI        0x20
#define RH_MRF89_IRQ1RXS_PACKET_FIFOFULL    0x10
#define RH_MRF89_IRQ1RXS_PACKET_CRCOK       0x00

#define RH_MRF89_IRQ1TX                     0x08
#define RH_MRF89_FIFOFULL                   0x04
#define RH_MRF89_FIFOEMPTY                  0x02
#define RH_MRF89_FOVRUN                     0x01

//#define RH_MRF89_REG_0E_FTPRIREG            0x0e
#define RH_MRF89_FIFOFM                     0x80
#define RH_MRF89_FIFOFSC                    0x40
#define RH_MRF89_TXDONE                     0x20
#define RH_MRF89_IRQ0TXST                   0x10
#define RH_MRF89_RIRQS                      0x04
#define RH_MRF89_LSTSPLL                    0x02
#define RH_MRF89_LENPLL                     0x01

//#define RH_MRF89_REG_10_FILCREG             0x10
#define RH_MRF89_PASFILV                    0xf0
#define RH_MRF89_PASFILV_987KHZ             0xf0
#define RH_MRF89_PASFILV_676KHZ             0xe0
#define RH_MRF89_PASFILV_514KHZ             0xd0
#define RH_MRF89_PASFILV_458KHZ             0xc0
#define RH_MRF89_PASFILV_414KHZ             0xb0
#define RH_MRF89_PASFILV_378KHZ             0xa0
#define RH_MRF89_PASFILV_321KHZ             0x90
#define RH_MRF89_PASFILV_262KHZ             0x80
#define RH_MRF89_PASFILV_234KHZ             0x70
#define RH_MRF89_PASFILV_211KHZ             0x60
#define RH_MRF89_PASFILV_184KHZ             0x50
#define RH_MRF89_PASFILV_157KHZ             0x40
#define RH_MRF89_PASFILV_137KHZ             0x30
#define RH_MRF89_PASFILV_109KHZ             0x20
#define RH_MRF89_PASFILV_82KHZ              0x10
#define RH_MRF89_PASFILV_65KHZ              0x00

#define RH_MRF89_BUTFILV                    0x0f
#define RH_MRF89_BUTFILV_25KHZ              0x00
#define RH_MRF89_BUTFILV_50KHZ              0x01
#define RH_MRF89_BUTFILV_75KHZ              0x02
#define RH_MRF89_BUTFILV_100KHZ             0x03
#define RH_MRF89_BUTFILV_125KHZ             0x04
#define RH_MRF89_BUTFILV_150KHZ             0x05
#define RH_MRF89_BUTFILV_175KHZ             0x06
#define RH_MRF89_BUTFILV_200KHZ             0x07
#define RH_MRF89_BUTFILV_225KHZ             0x08
#define RH_MRF89_BUTFILV_250KHZ             0x09
#define RH_MRF89_BUTFILV_275KHZ             0x0a
#define RH_MRF89_BUTFILV_300KHZ             0x0b
#define RH_MRF89_BUTFILV_325KHZ             0x0c
#define RH_MRF89_BUTFILV_350KHZ             0x0d
#define RH_MRF89_BUTFILV_375KHZ             0x0e
#define RH_MRF89_BUTFILV_400KHZ             0x0f

//#define RH_MRF89_REG_11_PFCREG              0x11
#define RH_MRF89_POLCFV                     0xf0

//#define RH_MRF89_REG_12_SYNCREG             0x12
#define RH_MRF89_POLFILEN                   0x80
#define RH_MRF89_BSYNCEN                    0x40
#define RH_MRF89_SYNCREN                    0x20
#define RH_MRF89_SYNCWSZ                    0x18
#define RH_MRF89_SYNCWSZ_32                 0x18
#define RH_MRF89_SYNCWSZ_24                 0x10
#define RH_MRF89_SYNCWSZ_16                 0x08
#define RH_MRF89_SYNCWSZ_8                  0x00
#define RH_MRF89_SYNCTEN                    0x06
#define RH_MRF89_SYNCTEN_3                  0x06
#define RH_MRF89_SYNCTEN_2                  0x04
#define RH_MRF89_SYNCTEN_1                  0x02
#define RH_MRF89_SYNCTEN_0                  0x00

//#define RH_MRF89_REG_15_OOKCREG             0x15
#define RH_MRF89_OOTHSV                     0xe0
#define RH_MRF89_OOTHSV_6P0DB               0xe0
#define RH_MRF89_OOTHSV_5P0DB               0xc0
#define RH_MRF89_OOTHSV_4P0DB               0xa0
#define RH_MRF89_OOTHSV_3P0DB               0x80
#define RH_MRF89_OOTHSV_2P0DB               0x60
#define RH_MRF89_OOTHSV_1P5DB               0x40
#define RH_MRF89_OOTHSV_1P0DB               0x20
#define RH_MRF89_OOTHSV_0P5DB               0x00

#define RH_MRF89_OOKTHPV                    0x1c
#define RH_MRF89_OOKTHPV_16                 0x1c
#define RH_MRF89_OOKTHPV_8                  0x18
#define RH_MRF89_OOKTHPV_4                  0x14
#define RH_MRF89_OOKTHPV_2                  0x10
#define RH_MRF89_OOKTHPV_1_IN_8             0x0c
#define RH_MRF89_OOKTHPV_1_IN_4             0x08
#define RH_MRF89_OOKTHPV_1_IN_2             0x04
#define RH_MRF89_OOKTHPV_1_IN_1             0x00

#define RH_MRF89_OOKATHC                    0x03
#define RH_MRF89_OOKATHC_32PI               0x03
#define RH_MRF89_OOKATHC_8PI                0x00

//#define RH_MRF89_REG_1A_TXCONREG            0x1a
#define RH_MRF89_TXIPOLFV                   0xf0

#define RH_MRF89_TXOPVAL                    0x0e
#define RH_MRF89_TXOPVAL_M8DBM              0x0e
#define RH_MRF89_TXOPVAL_M5DBM              0x0c
#define RH_MRF89_TXOPVAL_M2DBM              0x0a
#define RH_MRF89_TXOPVAL_1DBM               0x08
#define RH_MRF89_TXOPVAL_4DBM               0x06
#define RH_MRF89_TXOPVAL_7DBM               0x04
#define RH_MRF89_TXOPVAL_10DBM              0x02
#define RH_MRF89_TXOPVAL_13DBM              0x00

//#define RH_MRF89_REG_1B_CLKOREG             0x1b
#define RH_MRF89_CLKOCNTRL                  0x80
#define RH_MRF89_CLKOFREQ                   0x7c

//#define RH_MRF89_REG_1C_PLOADREG            0x1c
#define RH_MRF89_MCHSTREN                   0x80
#define RH_MRF89_PLDPLEN                    0x7f

//#define RH_MRF89_REG_1E_PKTCREG             0x1e
#define RH_MRF89_PKTLENF                    0x80

#define RH_MRF89_PRESIZE                    0x60
#define RH_MRF89_PRESIZE_4                  0x60
#define RH_MRF89_PRESIZE_3                  0x40
#define RH_MRF89_PRESIZE_2                  0x20
#define RH_MRF89_PRESIZE_1                  0x00

#define RH_MRF89_WHITEON                    0x10
#define RH_MRF89_CHKCRCEN                   0x08

#define RH_MRF89_ADDFIL                     0x06
#define RH_MRF89_ADDFIL_NODEADDR_00_FF      0x06
#define RH_MRF89_ADDFIL_NODEADDR_00         0x04
#define RH_MRF89_ADDFIL_NODEADDR            0x02
#define RH_MRF89_ADDFIL_OFF                 0x00

#define RH_MRF89_STSCRCEN                   0x01

//#define RH_MRF89_REG_1F_FCRCREG             0x1f
#define RH_MRF89_ACFCRC                     0x80
#define RH_MRF89_FRWAXS                     0x40


/////////////////////////////////////////////////////////////////////
/// \class RH_MRF89 RH_MRF89.h <RH_MRF89.h>
/// \brief Send and receive addressed, reliable, acknowledged datagrams by Microchip MRF89XA and compatible transceivers.
/// and modules.
///
/// The Microchip MRF89XA http://ww1.microchip.com/downloads/en/DeviceDoc/70622C.pdf is a low cost 900MHz
/// bancd transceiver chip.
/// It is commonly used on preassembled modules with supporting circcuits and antennas, such as 
/// the MRF89XAM9A http://www.microchip.com/wwwproducts/Devices.aspx?product=MRF89XAM9A
/// This class supports all such modules
///
/// This base class provides basic functions for sending and receiving unaddressed, unreliable datagrams
/// of arbitrary length to 59 octets per packet. Use one of the Manager classes to get addressing and 
/// acknowledgement reliability, routing, meshes etc.
///
/// Several MRF89XA modules can be connected to an Arduino, permitting the construction of translators
/// and frequency changers, etc. Each instance requires 2 chip select pins, and interrupt pin the standard 3 SPI pins.
/// 
/// Naturally, for any 2 radios to communicate that must be configured to use the same frequency and 
/// data rate, and with identical network addresses.
///
/// Example Arduino programs are included to show the main modes of use.
///
/// All messages sent and received by this class conform to this packet format:
///
/// - 3 octets PREAMBLE
/// - 2 to 4 octets NETWORK ADDRESS (also call Sync Word)
/// - 1 octet message length bits packet control field
/// - 4 to 63 octets PAYLOAD, consisting of:
///   - 1 octet TO header
///   - 1 octet FROM header
///   - 1 octet ID header
///   - 1 octet FLAGS header
///   - 0 to 59 octets of user message
/// - 2 octets CRC 
///
/// The payload is whitened. No Manchester encoding is used.
///
/// \par Connecting MRF89XA to Arduino
///
/// The electrical connection between the MRF89XA and the Arduino require 3.3V, the 3 x SPI pins (SCK, SDI, SDO), 
/// a 2 Chip Select pins (/CSCON and /CSDAT) and an interrupt.
///
/// Caution: the MRF89XA is a 3.3V part and is not tolerant of 5V inputs. Connecting MRF89XA directly to a 5V
/// MCU such as most Arduinos will damage the MRF89XA.
///
/// Connect the MRF89XA to most 3.3V Arduinos or Teensy 3.1 like this (use 3.3V not 5V).
/// \code
///                 Teensy       MRF89XAM9A
///               3.3V-----------VIN   (3.3V in)
///             pin D9-----------/CSDAT (data chip select in)
///          SS pin D10----------/CSCON (configuration chip select in)
///         SCK pin D13----------SCK   (SPI clock in)
///        MOSI pin D11----------SDI   (SPI Data in)
///        MISO pin D12----------SDO   (SPI data out)
///                 D2-----------IRQ1  (Interrupt 1 output)
///                              IRQ0  (Interrupt 0 output, not connected)
///                 GND----------GND   (ground in)
/// \endcode
/// You can use other pins for /CSDAT, /CSCON, IRQ1 by passing appropriate arguments to the constructor.
///
/// \par Example programs
///
/// Several example programs are provided.
///
class RH_MRF89 : public RHNRFSPIDriver
{
public:

    /// \brief Defines register configuration values for a desired modulation
    ///
    /// Defines values for various configuration fields and registers to 
    /// achieve a desired modulation speed and frequency deviation.
    typedef struct
    {
	uint8_t MODSEL;    ///< Value for MODSEL in RH_MRF89_REG_01_DMODREG
	uint8_t FDVAL;     ///< Value for FDVAL in RH_MRF89_REG_02_FDEVREG
	uint8_t BRVAL;     ///< Value for BRVAL RH_MRF89_REG_03_BRSREG
	uint8_t FILCREG;   ///< Value for PASFILV | BUTFILV in RH_MRF89_REG_10_FILCREG
	uint8_t TXIPOLFV;  ///< Value for TXIPOLFV in RH_MRF89_REG_1A_TXCONREG
    } ModemConfig;

    /// Choices for setModemConfig() for a selected subset of common
    /// data rates and frequency deviations.
    /// Rb is the data rate in kbps. Fd is the FSK Frequency deviation in kHz.
    /// 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).
    /// OOK is not yet supported.
    /// Based on sample configs in MRF89XA.h from Microchip
    typedef enum
    {
	FSK_Rb2Fd33 = 0,   ///< FSK, No Manchester, Whitened, Rb = 2kbs,    Fd = 33kHz
	FSK_Rb5Fd33,	   ///< FSK, No Manchester, Whitened, Rb = 5kbs,    Fd = 33kHz
	FSK_Rb10Fd33,	   ///< FSK, No Manchester, Whitened, Rb = 10kbs,   Fd = 33kHz
	FSK_Rb20Fd40,	   ///< FSK, No Manchester, Whitened, Rb = 20kbs,   Fd = 40kHz
	FSK_Rb40Fd80,	   ///< FSK, No Manchester, Whitened, Rb = 40kbs,   Fd = 80kHz
	FSK_Rb50Fd100,	   ///< FSK, No Manchester, Whitened, Rb = 50kbs,   Fd = 100kHz
	FSK_Rb66Fd133,	   ///< FSK, No Manchester, Whitened, Rb = 66kbs,   Fd = 133kHz
	FSK_Rb100Fd200,	   ///< FSK, No Manchester, Whitened, Rb = 100kbs,  Fd = 200kHz
	FSK_Rb200Fd200	   ///< FSK, No Manchester, Whitened, Rb = 200kbs,  Fd = 200kHz
    } ModemConfigChoice;

    /// Constructor.
    /// Constructor. You can have multiple instances, but each instance must have its own
    /// interrupt and 2 slave select pins. 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] csconPin the Arduino pin number connected to the CSCON pin of the MRF89XA.
    /// Defaults to the normal SS pin for your Arduino (D10 for Diecimila, Uno etc, D53 for Mega, D10 for Maple)
    /// \param[in] csdatPin the Arduino pin number connected to the CSDAT pin of the MRF89XA.
    /// Defaults to 9.
    /// \param[in] interruptPin The interrupt Pin number that is connected to the IRQ1 pin of the MRF89XA.
    /// Defaults to pin 2. (IRQ0 pin of the MRF89XA does not need to be connected).
    /// \param[in] spi Pointer to the SPI interface object to use. 
    ///                Defaults to the standard Arduino hardware SPI interface
    RH_MRF89(uint8_t csconPin = SS, uint8_t csdatPin = 9, uint8_t interruptPin = 2, RHGenericSPI& spi = hardware_spi);

    /// Initialise the Driver transport hardware and software.
    /// Make sure the Driver is properly configured before calling init().
    /// \return true if initialisation succeeded.
    virtual bool    init();

    /// Prints the value of all chip registers
    /// to the Serial device if RH_HAVE_SERIAL is defined for the current platform
    /// For debugging purposes only.
    /// \return true on success
    bool printRegisters();

    /// Sets the radio into low-power sleep mode.
    /// If successful, the transport will stay in sleep mode until woken by 
    /// changing mode to 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();

    /// 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 radio.
    // the next valid packet received will cause available() to be true.
    void           setModeRx();

    /// If current mode is Rx or Idle, changes it to Rx. F
    /// Starts the transmitter in the radio.
    void           setModeTx();

    /// Sets the transmitter power output level in register RH_MRF89_REG_1A_TXCONREG.
    /// Be a good neighbour and set the lowest power level you need.
    /// After init(), the power will be set to RH_MRF89_TXOPVAL_1DBM (1dBm)
    /// The highest power available is RH_MRF89_TXOPVAL_13DBM (13dBm)
    /// Caution: In some countries you may only select certain higher power levels if you
    /// are also using frequency hopping. Make sure you are aware of the legal
    /// limitations and regulations in your region.
    /// Caution: in some countries the maximum permitted power level may depend on the Bit rate
    /// \param[in] power Transmitter power level, one of RH_MRF89_TXOPVAL*
    void           setTxPower(uint8_t power);

    /// Select one of the predefined modem configurations. If you need a modem configuration not provided 
    /// here, use setModemRegisters() with your own ModemConfig.
    /// \param[in] index The configuration choice.
    /// \return true if index is a valid choice.
    bool        setModemConfig(ModemConfigChoice index);

    /// Tests whether a new message is available
    /// from the Driver. 
    /// On most drivers, this will also put the Driver into RHModeRx mode until
    /// a message is actually received by the transport, when it will be returned to RHModeIdle.
    /// This can be called multiple times in a timeout loop
    /// \return true if a new, complete, error-free uncollected message is available to be retreived by recv()
    virtual 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
    virtual 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 permitted. 
    /// \param[in] data Array of data to be sent
    /// \param[in] len Number of bytes of data to send
    /// \return true if the message length was valid and it was correctly queued for transmit
    virtual bool    send(const uint8_t* data, uint8_t len);

    /// Returns the maximum message length 
    /// available in this Driver.
    /// \return The maximum legal message length
    virtual uint8_t maxMessageLength();

    /// Sets the centre frequency in MHz.
    /// Permitted ranges are: 902.0 to 928.0 and 950.0 to 960.0 (inclusive)
    /// Caution not all freqs are supported on all modules: check your module specifications
    /// Caution: not all available and supported frequencies are legal in every country: check 
    /// Regulatory Approval eg for MRF89XAM9A (in 75015B.pdf)
    /// Australia 915.0 to 928.0
    bool            setFrequency(float centre);

    /// 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 RH_MRF89_REG_1E_PKTCREG
    /// \param[in] bytes Preamble length in bytes of 8 bits each.  
    void           setPreambleLength(uint8_t bytes);

    /// Sets the sync words for transmit and receive in registers RH_MRF89_REG_16_SYNCV31REG
    /// et seq.
    /// 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 { 0x69, 0x81, 0x7e, 0x96 }.
    /// Caution, sync words of 2 bytes and less do not work well with this chip.
    /// \param[in] syncWords Array of sync words, 1 to 4 octets long
    /// \param[in] len Number of sync words to set, 1 to 4.
    void            setSyncWords(const uint8_t* syncWords = NULL, uint8_t len = 0);

protected:

    /// Called automatically when a CRCOK or TXDONE interrupt occurs.
    /// Handles the interrupt.
    void handleInterrupt();

    /// Reads a single register from the MRF89XA
    /// \param[in] reg Register number, one of RH_MRF89_REG
    /// \return The value of the register
    uint8_t spiReadRegister(uint8_t reg);

    /// Writes to a single single register on the MRF89XA
    /// \param[in] reg Register number, one of RH_MRF89_REG_*
    /// \param[in] val The value to write
    /// \return the current value of RH_MRF89_REG_00_GCONREG (read while the command is sent)
    uint8_t spiWriteRegister(uint8_t reg, uint8_t val);

    /// Writes a single byte to the MRF89XA data FIFO.
    /// \param[in] data The data value to write
    /// \return 0
    uint8_t spiWriteData(uint8_t data);

    /// Write a number of bytes from a buffer to the MRF89XA data FIFO.
    /// \param[in] data Pointer to a buffer containing the len bytes to be written
    /// \param[in] len The number of bytes to write to teh FIFO
    /// \return 0;
    uint8_t spiWriteData(const uint8_t* data, uint8_t len);

    /// Reads a single byte from the MRF89XA data FIFO.
    /// \return The next data byte in the FIFO
    uint8_t spiReadData();

    /// Sets the operating mode in the CMOD bits in RH_MRF89_REG_00_GCONREG
    /// which controls what mode the MRF89XA is running in
    /// \param[in] mode One of RH_MRF89_CMOD_*
    void    setOpMode(uint8_t mode);

    /// Verifies that the MRF89XA PLL has locked on the slected frequency.
    /// This needs to be called if the frequency is changed
    bool    verifyPLLLock();

    /// Examine the revceive buffer to determine whether the message is for this node
    void validateRxBuf();

    /// Clear our local receive buffer
    void clearRxBuf();


private:
    /// Low level interrupt service routine for device connected to interrupt 0
    static void         isr0();

    /// Low level interrupt service routine for device connected to interrupt 1
    static void         isr1();

    /// Low level interrupt service routine for device connected to interrupt 1
    static void         isr2();

    /// Array of instances connected to interrupts 0 and 1
    static RH_MRF89*     _deviceForInterrupt[];

    /// Index of next interrupt number to use in _deviceForInterrupt
    static uint8_t      _interruptCount;

    // Sigh: this chip has 2 differnt chip selects.
    // We have to set one or the other as the SPI slave select pin depending
    // on which block of registers we are accessing
    uint8_t             _csconPin;
    uint8_t             _csdatPin;

    /// The configured interrupt pin connected to this instance
    uint8_t             _interruptPin;

    /// The index into _deviceForInterrupt[] for this device (if an interrupt is already allocated)
    /// else 0xff
    uint8_t             _myInterruptIndex;

    /// Number of octets in the buffer
    volatile uint8_t    _bufLen;
    
    /// The receiver/transmitter buffer
    uint8_t             _buf[RH_MRF89_MAX_PAYLOAD_LEN];

    /// True when there is a valid message in the buffer
    volatile bool       _rxBufValid;

};

/// @example mrf89_client.pde
/// @example mrf89_server.pde

#endif
