Creating a radio class derived from RFM69 library

Started by TomWS, March 12, 2015, 12:08:27 PM

TomWS

Note that this thread was spun off from https://lowpowerlab.com/forum/index.php/topic,966.msg6280.html#msg6280

Suppose you want to change the behavior of the RFM69 library to add a couple new methods or change the way the radio modulates the frequency?  Without going into details of WHAT you'd like to do, this is a simple step by step to add enhancements without requiring any change to the base RFM69 library.

Note that this assumes that you don't want to change the behavior of an EXISTING RFM69 method (other than RFM69::initialize().

In order to extend the existing RFM69 class you have two choices:

1. Simply create a 'wrapper' that instantiates an RFM69 'radio' member and calls its members whenever you want to communicate with the radio.  This is easy to do, but it means that you need to either:
  * pass on every radio method call to the radio instance, or
  * make your 'radio' member public so that the sketch calls the radio methods directly.  For example:
 
  myClass.radio.sendWithRetry(....);
 

 
2. You create a derived class that literally extends the interface to the base class, RFM69.  In this way, all the existing RFM69 methods become 'inherited' by your new class.  For example:
 
  myClass.sendWithRetry(....);
 

  essentially your new class becomes a 'super'RFM69 radio!
 
This series of steps shows how to do the latter case, creating a  derived class.

Step 1:  Create a new library, we'll call it 'myRadio'.
A. Create a new directory in your Arduino/libraries folder called 'myRadio'.
B. Create two new files in that directory, 'myRadio.h' and 'myRadio.cpp'.
C. In 'myRadio.h' add the following lines:
#ifndef __myRadio_h__    // this checks to see if the include file has already been loaded
#define __myRadio_h__    // this is always good practice for library code since it could (is) loaded in many places
#include <RFM69.h>       // required to pick up RFM69 declarations

class myRadio: public RFM69    // here is the first bit of magic.  You're telling the compiler that this class is based on the RFM69 class
{
  public:
    myRadio(uint8_t freqDeviation=0, uint16_t baudRate=0); // myRadio constructor, defaults to no change
   
    bool initialize(byte freqBand, byte ID, byte networkID=1); // myRadio's own initialize() method, but uses the same prototype as the RFM69 (you'll see why shortly)
    void setDeviation(uint8_t freqDeviation);              // new method to change frequency deviation
    void setBaudRate(uint8_t freqDeviation);               // new method to change baudrate
  
  protected:
    uint8_t  _freqDeviation;
    uint16_t _baudRate;
} 

#endif   // to close out the #ifndef above...


D. In 'myRadio.cpp', add the following lines (note that the methods don't 'do' anything in this example)
#include <myRadio.h>     // note that this file will also include the RFM69 declarations

myRadio::myRadio((uint8_t freqDeviation, uint16_t baudRate) :  // constructor implementation for myRadio
    RFM69(RF69_SPI_CS, RF69_IRQ_PIN, true)       // more magic!  You can construct the base class with updated values! In this case we're saying that we always use RFM69HW.
{
    _freqDeviation = freqDeviation;   // save the constructor values
    _baudRate = baudRate;
}

bool initialize(byte freqBand, byte ID, byte networkID)
{
  // we need to initialize the base class first and THEN override with our settings (there are other ways to do this, see below)
  bool rc = RFM69::initialize(byte freqBand, byte ID, byte networkID);
  if (rc)  // successful?
  {
    if (_freqDeviation)
      setDeviation(_freqDeviation);
    if (_baudRate)
      setBaudRate(_baudRate);  
  }  
  return rc;
}

void setDeviation(uint8_t freqDeviation)
{
  ... whatever it takes...
}
void setBaudRate(uint8_t freqDeviation)
{
  ... whatever it takes...
{


Step 2 Update your sketch with:
...
#include <RFM69.h>    // still need this due to a 'feature' of Arduino IDE...
#include <myRadio.h>  // even though this includes it too (this is why you NEED to check if already included)

...

Change:
RFM69 radio;

to:
myRadio radio(8,9600);    // you can set the new parameters here


And, change:
  radio.initialize(FREQUENCY,NODEID,NETWORKID);

to:
  radio.initialize(FREQUENCY,NODEID,NETWORKID);

They look the same don't they?  THEY ARE! NO CHANGE even though you now have NEW function inside of the new radio!

Finally, you now have new methods you can call whenever you want:
  radio.setDeviation(9);
  ...
  radio.setBaudRate(57600);   


That's it.  If you want to read about initialize, you'll have to go to the next post because this one's gotten too long... sigh.
Tom

TomWS

As a follow up to the previous post, a couple of comments about 'initialize'.  In this example we're not adding new parameters to the initialization method so we COULD have just used the base class method without creating our own and then simply added the calls to set the new parameters with our new methods after our radio.initialize(...) statement.  As in:
  radio.initialize(FREQUENCY,NODEID,NETWORKID);
  radio.setDeviation();   // use the protected variables that we initialized in the constructor...
  radio.setBaudRate();


In this case it's probably not a problem either way, but, as the initialization code gets more complicated or requires access to protected members, its generally a good idea to encapsulate the function into an extended function.  We could even add the extra parameters to our own initialization function if desired.

I hope this helps.  If parts of this isn't clear, ask...

Tom

Tomega3

TomWS
Thanks for the info on setting up a derived class to extend the functionality of the radio library.

I think I will start with your second example: "in this example we're not adding new parameters to the initialization method so we COULD have just used the base class method without creating our own and then simply added the calls to set the new parameters with our new methods after our radio.initialize(...) statement.

Is it OK to PM you to discuss details?

Thanks again,
Tom

Tomega3

Just a quick post to say that using the examples provided in earlier posts that I have successfully created a radio class derived from the RFM69 library - it just adds a couple of custom functions and does not affect any code in the standard RFM69 lib.

I was close but needed some help with C++ issues.

Thanks for a terrific forum, libs, and the fantastic Moteinos