// RH_ABZ.h
//
// Definitions for SX1276 radio in muRata CMWX1ZZABZ (TypeABZ) module
// as used in GrumpyOldPizza Grasshopper-L082CZ, EcoNode SmartTrap  etc with
// GrumpyOldPizza / ArduinoCore-stm32l0 installed per https://github.com/GrumpyOldPizza/ArduinoCore-stm32l0
//
// For data refer to muRata Application note: AN-ZZABZ-001
// muRata Preliminary Specification Number : SP-ABZ-093-E
// $p/EcoNode
//
// Author: Mike McCauley (mikem@airspayce.com)
// Copyright (C) 2020 Mike McCauley
// $Id: RH_ABZ.h,v 1.1 2020/06/15 23:39:39 mikem Exp $
// 
#ifndef RH_ABZ_h
#define RH_ABZ_h

#include <RH_RF95.h>

/////////////////////////////////////////////////////////////////////
/// \class RH_ABZ RH_ABZ.h <RH_ABZ.h>
/// \brief Driver to send and receive unaddressed, unreliable datagrams via 
/// radio transceiver in a muRata cmwx1zzabz module, which includes an STM32L0 processor,
/// a SX1276 LoRa radio and an antenna switch.
///
/// Requires the Grumpy Old Pizza Arduino Core installed per https://github.com/GrumpyOldPizza/ArduinoCore-stm32l0
///
/// Works with EcoNode SmartTrap, Tlera Grasshopper and family. Almost any board equipped with a muRata cmwx1zzabz module
/// should work. Tested with EcoNode SmartTrap, Arduino 1.8.9, GrumpyOldPizza Arduino Core for STM32L0.
/// When building for EcoNode SmartTrap in Arduino IDE, select board type Grasshopper-L082CZ.
/// This chip and GrumpyOldPizza Arduino Core for STM32L0 are now supported by PlatformIO: 
/// https://docs.platformio.org/en/latest/platforms/ststm32.html#arduino-stm32l0-configuration-system
///
/// \par Overview
///
/// This class is a specialisation of the RH_RF95 class, but with overridden functions to ensure that RH_RF95 communicates
/// with the SX1276 radio on the correct SPI interface (STM32L0_SPI_INSTANCE_SPI1). The class configures the SPI and
/// interfaces with the radio using native stm32l0 calls. It also uses stm32l0 calls to intialise the radio interface pins,
/// antenna interface pins, and to toggle the radio NSS pin for SPI communicaitons. The reason for this speicalisaiton is that
/// all the STM32L0 variants in the Grumpy Pizzas Arduino Core define the Arduino compatible SPI interface and the
/// Arduino compatible IO pins in varying and inconsistent ways. So we use native stm32l0 calls to make _sure_ we get the right
/// pins and interfaces.
///
/// All the comments in the RH_RF95 class concerning modulation, packet formats etc apply equally to this module.
///
/// \par Temperature Controlled Crystal Oscillator (TCXO)
///
/// The muRata cmwx1zzabz module includes a TCXO. Pins to enable the TCXO and to connect to 32MHz output to the radio
/// are exposed on the module. Some boards (Econode SmartTrap for example) permanently power the TCXO and permanenetly
/// connect it to the radio. Other boards (Grasshopper for example) have the TCXO enable connected to a GPIO pin, allowing
/// the TCXO to be controlled by software. Different boards may use different GPIO pins to control the TCXO.
///
/// The SX1276 radio can be configured to use the TCXO, and the Arduino Core defaults the radio to using TCXO.
/// Therefore it is important that you ensure the TCXO is powered up, at least when you want the radio to operate.
/// If the TCXO is not powered, the radio will not work.
///
/// On the Tlera boards supported the Arduino Core, you can call SX1276SetBoardTcxo() to enable or disable the TCXO
/// by controlling the correct pin for your board.
/// By default the core disables TCXO at the end of initialisation, so by the time your sketch starts to run
/// the TCXO is powered off.
/// You will almost certainly need to call
/// \code
/// SX1276SetBoardTcxo(true); 
/// \endcode
/// in your setup() or at other times when you want the radio to operate.
///
/// If you have a board where the TCXO is permanently powered, this is unnecessary.
///
/// \par Connecting and configuring the radio
///
/// There is no special configuration for the SX1276 radio in the  muRata cmwx1zzabz module: the CPU, radio and
/// antenna switch are all hardwired within the module can, and cannot be changed. Initialise the radio like this
/// with the default constructor:
/// \code
///  RH_ABZ driver;
/// \endcode
///
/// \par Range
///
/// We made some primitive range tests with 2 identical EcoNode SmartTrap at 868MHz, 20dBm transmit power with
/// modem config RH_RF95::Bw125Cr45Sf2048, using the abz_client and abz_server sketches included in this distribution.
/// We monitored for reliabilty of 2-way communications (ie how reliably can the client get a reply from the server,
/// which is a 2-way comms that depends on both send and receive. The SmartTrap has a simple small helical antenna.
/// The environment was a beach in a developed area, one node was stationary
/// on a rock about 1m above sand level. The mobile node was handl-held at 1 m above ground level. All measurements were line-of-sight.
///
/// \code
/// Location     Distance (km)        % successful round trip
/// Elephant Rock          0          100
/// Dune St                1.48       100
/// Shell St               1.71       100
/// Sand St                1.93       100
/// Sea St                 2.15         0
/// Short St               2.36        50
/// John St                2.59        50
/// Surf St                2.80       100
/// Matters St             2.98       100
/// Mills St               3.92       100
/// North Kirra            4.62         0
/// North Kirra SLSC       4.81        80
/// Haig St                5.20         0
/// Kirra SLSC             5.91         0
/// \endcode
///
/// \par Transmitter Power
///
/// We have made some actual power measurements against
/// programmed power on an EcoNode SmartTrap.
/// - EcoNode SmartTrap at 868MHz
/// - 15cm RG316 soldered direct to SmartTrap antenna pin
/// - SMA/BNC connector
/// - 12db attenuator (calibrated as 13.5dB at 868MHz)
/// - SMA/BNC connector
/// - 30cm RG316
/// - 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
/// useRFO==false (ie uses PA_BOOST for higher power)
/// Program power           Measured Power
///    dBm                         dBm
///      2                           2.7
///      5                           5
///      7                           6.5
///     10                           9.5
///     13                          12.5
///     15                          14.5
///     16                          15.5
///     17                          16.5 
///     18                          17.2 
///     19                          17.6 
///     20                          18.2
///
/// useRFO==true (ie no PA_BOOST)
/// Program power           Measured Power
///    dBm                         dBm
///    0                           -5.5
///    2                           -2.5
///    4                           -0.5
///    6                            2
///    8                            4
///   10                            6.5
///   12                            9
///   13                           10.5
///   14                           11.5
///   15                           12.5
/// \endcode

/// In the the Grumpy Old Pizza Arduino Core, there is a function for turning the
/// TCXO power source on and off, which depends on exactly which board is being compiled for
/// If the Radio in your boards has its TCXO connected to a programmable power pin,
/// and if you enable TCXO on the radio (by default it is on these boards)
/// then this function needs to be called to enable the TCXO before the radio will work.
extern "C" void SX1276SetBoardTcxo( bool state ); 

class RH_ABZ : public RH_RF95
{
public:
    /// Constructor
    RH_ABZ();

    /// Initialise the Driver transport hardware and software. Leaves the radio in idle mode,
    /// with default configuration of: 434.0MHz, 13dBm, Bw = 125 kHz, Cr = 4/5, Sf = 128chips/symbol, CRC on
    /// \return true if initialisation succeeded.
    virtual bool    init();

    /// Deinitialise the interrupt handler, allowing the radio to be temporarily used by another stack.
    /// If you wish to use the radio again after this call, you wil need to call init() again.
    /// \return true if deinitialisation succeeded.
    bool deinit();
    
protected:
    /// Called by RH_RF95 when the radio mode is about to change to a new setting.
    /// Configures the antenna switch to connect to the right radio pin.
    /// \param[in] mode RHMode the new mode about to take effect
    /// \return true if the subclasses changes successful
    virtual bool modeWillChange(RHMode mode);

    /// Called by RHSPIDriver when the SPI is about to talk to the radio.
    /// Uses native spi32l0 calls to enable the radio NSS pin
    virtual void selectSlave();
    
    /// Called by RHSPIDriver when the SPI is finished talking to the radio.
    /// Uses native spi32l0 calls to disable the radio NSS pin
    virtual void deselectSlave();

private:
    /// Glue code between the DIO0 interrupt the interrupt handler in RH_RF95
    static void RH_INTERRUPT_ATTR isr();

    /// Pointer to the one and only instance permitted, for interrupt linkage
    static RH_ABZ* _thisDevice;

};

/// @example abz_client.pde
/// @example abz_server.pde

#endif
