Pages: [1]   Go Down
Author Topic: How to sub class serial function?  (Read 3732 times)
0 Members and 1 Guest are viewing this topic.
Australia
Offline Offline
Sr. Member
****
Karma: 13
Posts: 444
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have a library for Meshnetics Zibee modules that works fine for the standard Arduino.
However, the standard Arduino only has one hardware serial port. I want to use this library on and Arduino Mega which has four ports to choose from. I attach below a small snippet of the library.

For the Mega I would like to pass a parameter (0 - 3) in the constructor to indicated which of the four hardware UARTS the Zigbee is connected to.

I could have four separate libraries, each identical but each using Serial, Serial1, Serial2, or Serial3 respectively.
This is clumsy and would not allow for easy dynamic switching between ports in the code.

How can I write a class to allow for specifying different hardware ports in the constructor?



Code:
/*
  Zigbee.cpp - Library for controlling Meshnetics Zigbee modules.
      
*/

#include "WProgram.h"
#include "Zigbee.h"

Zigbee::Zigbee(int pin)
{
   //Serial.begin(38400);  
   //Serial.flush();
}

int Zigbee::FactoryDefaults()
{
    Serial.flush();
    Serial.print('\r');
    delay(300);  
    Serial.print("AT&F");
    Serial.print('\r');
    delay(4000);
    char InBuffer[BUFFERLENGTH];
    InBuffer[0] = '\0';
    int x = ReceiveIncoming(InBuffer);
    if (x == 0)
   {
      return 99;
   }
   else
  {
      return atoi(InBuffer);  
  }
}
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Serial, Serial1, Serial2, Serial3, and Serial4 are all instances of the Serial class.

Instead of your constructor taking a number, it should take an instance of the Serial class.

Then, the user can call it with Serial, Serial1, Serial2, Serial3, or Serial4.

Zigbee::Zigbee()
{
}

Zigbee::begin(Serial &theSerial)
{
   _Serial = theSerial;
}

Then, use _Serial.read(), _Serial.available(), etc. elsewhere in your code.

Don't forget that you have no control over the order of calls to constructors. You don't know whether Serial::Serial() will be called first, or if Zigbee::Zigbee() will be called first.

Don't expect Zigbee::Zigbee() to be able to use the Serial instance.

Also, I would not recommend setting the baud rate in your class. Make it a prerequisite that the baud rate will be already been set before the instance is passed to your begin() method.

The user of your library may have other needs/expectations for communication speed than you.
Logged

Australia
Offline Offline
Sr. Member
****
Karma: 13
Posts: 444
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks PaulS.

I am still unclear how to implement your suggestions. (excuse my 'newb-ness').

The instantiation of the Zigee class takes place before the Setup() function runs, which is where we usually set up the serial port (e.g. 'Serial.begin(38400)'). Therefore how do I pass an instance of the serial port to the Zibee constructor when the serial port is yet to be set up?

I would prefer to set the baud external to the class if I am passing a Serial instance to the class. The reason I had the baud rate setting in the Zigbee::Zigbee() (which I had commented out but not yet removed) is that I was struggling to learn what to put where and was trying all sorts of things!

You mention - "Then, use _Serial.read(), _Serial.available(), etc. elsewhere in your code."
Do I use these within the calling code, within the class (or object), or both?

Could I ask you to post a few example lines of code(not necessarily using the Zigbee example that I provided) to show the lines in the main program that sets up a serial port in class and sets the baud rate? I am trying to get my head around the various components but example code is usually the most helpful.

I am a bamboozled a bit by the comment "Don't expect Zigbee::Zigbee() to be able to use the Serial instance."
Does this mean that I shouldn't pass the Serial instance to the constructor? Should it only be passed to a 'begin' method after the instantiation of the Zigbee object?



 








Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Quote
The instantiation of the Zigee class takes place before the Setup() function runs, which is where we usually set up the serial port (e.g. 'Serial.begin(38400)'). Therefore how do I pass an instance of the serial port to the Zibee constructor when the serial port is yet to be set up?
You can't. That was my point. You'll notice in the Serial class that you set the baud rate AFTER the instance has been created. You need to do the same with your class. After the Zigbee instance has been created, call the (not currently defined) begin() method to pass it the Serial instance to use.

Quote
You mention - "Then, use _Serial.read(), _Serial.available(), etc. elsewhere in your code."
Do I use these within the calling code, within the class (or object), or both?
You class will have a private Serial reference field named _Serial. So, only your class can refer to that name.

Your class' header file should have public methods:
Zigbee ();
~Zigbee ();
void begin(Serial &serIn);

It should have at least one private field:
Serial &_Serial;

The constructor does nothing:
Code:
Zigbee::Zigbee()
{
}

The begin method does stuff:
Code:
void Zigbee::begin(Serial &serIn)
{
   _Serial = serIn;
   _Serial.begin(38400);
   _Serial.println("Ready to Rip!");
}

Quote
Does this mean that I shouldn't pass the Serial instance to the constructor? Should it only be passed to a 'begin' method after the instantiation of the Zigbee object?
That's exactly what I mean. Since the Serial object may be created before or after the Zigbee object, the Zigbee object can not call the Serial.begin method. There may not be a Serial instance, yet.
« Last Edit: January 13, 2011, 11:57:20 am by PaulS » Logged

Australia
Offline Offline
Sr. Member
****
Karma: 13
Posts: 444
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks PaulS,

I am testing your suggestions with the following simple sketch but am still having problems.

The sketch:

Code:
// SerialClassTest

#include <Zigbee.h>

Zigbee Z();

void setup()
{
  Z.begin(Serial);  
}

void loop()
{
  /*    
  delay(1000);
  */
}



Zigbee.h

Code:
#ifndef Zigbee_h
#define Zigbee_h

#include "WProgram.h"

class Zigbee
{
  public:
    Zigbee();
    void begin(Serial &serIn);          
  private:
    Serial _Serial;
};
#endif


Zigbee.cpp

Code:
#include "WProgram.h"
#include "Zigbee.h"

Zigbee::Zigbee()
{
}

void Zigbee::begin(Serial &serIn)
{
  _Serial = serIn;
  _Serial.begin(38400);
  _Serial.println("Ready to Rip!");
}

I get the error:

error: request for member 'begin' in 'Z' which is of non-class type 'Zigbee ()()'

I tried putting your recommendations into my full library (too big to put here) but got the error that 'Serial' is not a type.
Couldn't fix this so I went back to basics with the simple sketch above but still couldn't get it right.

Can you tell me where I am going wrong?

Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

The error member refers specifically to the statement:
Code:
Zigbee Z();
Why have you included parentheses? Remove them to discover the real issues.

I had to make a number of changes to the code you posted in order to get it to compile. I thought that might be necessary when I posted the stuff in the first place.

I changed the header file to:
Code:
#ifndef Zigbee_h
#define Zigbee_h

#include "WProgram.h"

class Zigbee
{
  public:
    Zigbee();
    void begin(HardwareSerial *serIn);    
  private:
    HardwareSerial *_Serial;
};
#endif
The Serial instances are instances of the HardwareSerial class, so the variables have to be of the right type. It was also necessary to change from references to pointers.

Then, I made corresponding changes to the cpp file:
Code:
#include "WProgram.h"
#include "Zigbee.h"

Zigbee::Zigbee()
{
  _Serial = NULL;
}

void Zigbee::begin(HardwareSerial *serIn)
{
  _Serial = serIn;
  _Serial->begin(38400);
  _Serial->println("Ready to Rip!");
}
The argument types match the header file, and pointer notation is used to access the HardwareSerial instance's members. The member field needed to be initialized.

Changes to the sketch were required, bu minimal:
Code:
// SerialClassTest

#include "Zigbee.h"

Zigbee Z;

void setup()
{
  Z.begin(&Serial);  
}

void loop()
{
  /*    
  delay(1000);
  */
}
I got rid of the parentheses in the Z declaration statement, and added a & in front of the Serial instance, to pass the address of the Serial instance. We really don't want to be passing a great big object like Serial by value.

Now, it at least compiles.
Logged

Australia
Offline Offline
Sr. Member
****
Karma: 13
Posts: 444
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

Thanks Paul,

I will read through it a few times to let the concepts sink in.

In one of your previous posts you stated:

Quote
Your class' header file should have public methods:
Zigbee ();
~Zigbee ();
void begin(Serial &serIn);


What is "~Zigbee ();"

Thanks for your help personally and on the forum generally. You are quite prodigious with your assistance.
Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

~Zigbee(); is a destructor. Your class has a constructor. It should have a destructor. If your class creates instances of other classes, they should be deleted when the instance is deleted. When the instance is deleted, the destructor is called. This is where you delete instances of other classes.

The destructor is not strictly required if your class does not dynamically consume resources that need to be freed, but the stuff I do for a living does require destructors, so I'm in the habit of defining the destructor at the same time I define the constructor.
Logged

Australia
Offline Offline
Sr. Member
****
Karma: 13
Posts: 444
Arduino rocks
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

I have not seen the destructor described in any of the Arduino documentation but it seems like a good practice to adopt.

Cheers

Logged

Seattle, WA USA
Offline Offline
Brattain Member
*****
Karma: 601
Posts: 48543
Seattle, WA USA
View Profile
 Bigger Bigger  Smaller Smaller  Reset Reset

In general destructors are only required when the constructor or other class methods dynamically allocate memory (as is done when another class is instantiated).

Since most Arduino libraries don't do dynamic memory allocation, there is no need to ensure that all allocations are freed.

Those libraries that do do dynamic memory allocation, like the String class, do provide destructors.

The Arduino documentation on library creation assumes a fair amount of knowledge of C++. Any experienced C++ developer should know when to provide a destructor.
Logged

Pages: [1]   Go Up
Jump to: