Howto implement HardwareSerial inside my library

Hi all!

As I have posted somewhere in this forum, I’ve been working on a new Modbus library. Indeed I’ve posted this same topic in somewhere else, but I’m afraid that it was the wrong place.

The fact is that my new Modbus library needs to append the HardwareSerial call inside it. At this moment, it is just outside the class declaration:

HardwareSerial port = Serial; ///< Pointer to Serial class object
/* _____CLASS DEFINITIONS____________________________________________________ */
/**
 * Arduino class library for communicating with Modbus devices over
 * RS232/485 (via RTU protocol).
 */
class Modbus {
private:
  uint8_t u8id; // 0=master, 1..247=slave number
  uint8_t u8serno; // serial port: 0-Serial, 1..3-Serial1..Serial3
  uint8_t u8txenpin; // 0=USB or RS-232 mode, >0=RS-485 mode
  uint8_t u8state;
  uint8_t au8Buffer[MAX_BUFFER];
  uint8_t u8BufferSize;
  uint8_t u8lastRec;
  uint16_t *au16regs;
  uint16_t u16InCnt, u16OutCnt, u16errCnt;
  uint16_t u16timeOut;
  uint32_t u32time, u32timeOut;
  uint8_t u8regsize;

  void init(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin);
  void sendTxBuffer(); // transmit buffer to serial port
  int8_t getRxBuffer(); // get serial buffer contents
  uint16_t calcCRC(uint8_t u8length); // get CRC from au8Buffer until u8length
  uint8_t validateAnswer();
  uint8_t validateRequest(); // validate master request
  void get_FC1(); // *** only master ***
  void get_FC3(); // *** only master ***
  int8_t process_FC1( uint16_t *regs, uint8_t u8size ); // *** only slave ***
  int8_t process_FC3( uint16_t *regs, uint8_t u8size ); // *** only slave ***
  int8_t process_FC5( uint16_t *regs, uint8_t u8size ); // *** only slave ***
  int8_t process_FC6( uint16_t *regs, uint8_t u8size ); // *** only slave ***
  int8_t process_FC15( uint16_t *regs, uint8_t u8size ); // *** only slave ***
  int8_t process_FC16( uint16_t *regs, uint8_t u8size ); // *** only slave ***
  void buildException( uint8_t u8exception ); // build exception message

public:
  Modbus();
  Modbus(uint8_t u8id, uint8_t u8serno);
  Modbus(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin);
  void begin(long u32speed);
  void begin();
  void setTimeOut( uint16_t u16timeout); // only for master
  uint16_t getTimeOut(); // only for master 
  int8_t query( modbus_t telegram ); // only for master
  int8_t poll(); // cyclic poll for master
  int8_t poll( uint16_t *regs, uint8_t u8size ); // cyclic poll for slave
  uint16_t getInCnt(); // number of incoming messages
  uint16_t getOutCnt(); // number of outcoming messages
  uint16_t getErrCnt(); // error counter
  uint8_t getID();
  uint8_t getState();
};

How can I implement HardwareSerial inside my class?

What I need is to declare in the object constructor that this object shall use any Serial (Serial, Serial1 or Serial3). So, all the HardwareSerial methods may refer to it. As it is done, whenever more than one object of this same library is declared, the processor stops working. I’m afraid that this is because all these objects are using the same “port” variable.

Best Regards,

HardwareSerial port = Serial; ///< Pointer to Serial class object

Wrong! port is NOT a pointer. It needs to be.

HardwareSerial *port = &Serial; ///< Pointer to Serial class object

Here, port IS a pointer. It points to the Serial object.

You then need to use port->, rather than port. to access the member functions.

To include possible future expansion into software serial, you can use a Stream class pointer, which is the parent class of HardwareSerial class. Syntax is the same as PaulS pointed out. You define this pointer in your class and accept its value via constructor or a separate function.

Here, port IS a pointer. It points to the Serial object.

You then need to use port->, rather than port. to access the member functions.

Thanks for your useful comments! Anyway, even if port is declared as a pointer to HardwareSerial (or to Streamer), if a project contains more than one object of my Modbus class and both refer to different ports, I cannot predict what shall happen. That's because there is only one "port" pointer. Is it correct?

Anyway, even if port is declared as a pointer to HardwareSerial (or to Streamer), if a project contains more than one object of my Modbus class and both refer to different ports, I cannot predict what shall happen.

If you can't, you've written the code/class incorrectly.

The port variable should be a member field. It should be valued by a setPort() or begin() type of method.

Thanks! Now it works fine. XD
The class declaration has become this:

/* _____CLASS DEFINITIONS____________________________________________________ */
/**
 * Arduino class library for communicating with Modbus devices over
 * RS232/485 (via RTU protocol).
 */
class Modbus {
private:
  HardwareSerial *port; ///< Pointer to Serial class object
  uint8_t u8id; // 0=master, 1..247=slave number
  uint8_t u8serno; // serial port: 0-Serial, 1..3-Serial1..Serial3
  uint8_t u8txenpin; // 0=USB or RS-232 mode, >0=RS-485 mode
  uint8_t u8state;
  uint8_t au8Buffer[MAX_BUFFER];
  uint8_t u8BufferSize;
  uint8_t u8lastRec;
  uint16_t *au16regs;
  uint16_t u16InCnt, u16OutCnt, u16errCnt;
  uint16_t u16timeOut;
  uint32_t u32time, u32timeOut;
  uint8_t u8regsize;

  void init(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin);
  void sendTxBuffer(); // transmit buffer to serial port
  int8_t getRxBuffer(); // get serial buffer contents
  uint16_t calcCRC(uint8_t u8length); // get CRC from au8Buffer until u8length
  uint8_t validateAnswer();
  uint8_t validateRequest(); // validate master request
  void get_FC1(); // *** only master ***
  void get_FC3(); // *** only master ***
  int8_t process_FC1( uint16_t *regs, uint8_t u8size ); // *** only slave ***
  int8_t process_FC3( uint16_t *regs, uint8_t u8size ); // *** only slave ***
  int8_t process_FC5( uint16_t *regs, uint8_t u8size ); // *** only slave ***
  int8_t process_FC6( uint16_t *regs, uint8_t u8size ); // *** only slave ***
  int8_t process_FC15( uint16_t *regs, uint8_t u8size ); // *** only slave ***
  int8_t process_FC16( uint16_t *regs, uint8_t u8size ); // *** only slave ***
  void buildException( uint8_t u8exception ); // build exception message

public:
  Modbus();
  Modbus(uint8_t u8id, uint8_t u8serno);
  Modbus(uint8_t u8id, uint8_t u8serno, uint8_t u8txenpin);
  void begin(long u32speed);
  void begin();
  void setTimeOut( uint16_t u16timeout); // only for master
  uint16_t getTimeOut(); // only for master 
  int8_t query( modbus_t telegram ); // only for master
  int8_t poll(); // cyclic poll for master
  int8_t poll( uint16_t *regs, uint8_t u8size ); // cyclic poll for slave
  uint16_t getInCnt(); // number of incoming messages
  uint16_t getOutCnt(); // number of outcoming messages
  uint16_t getErrCnt(); // error counter
  uint8_t getID();
  uint8_t getState();
};

Now there is a HardwareSerial pointer inside my class.
I’ve tried a demo sketch with two objects (a Master and an Slave) and it works fine.

TimedAction displayAction = TimedAction ( 50, loopDisplay );
Modbus mb(1,0,0);
Modbus mb2(0,1,14);
uint8_t u8state = 0;
uint32_t u32wait;
modbus_t telegram;

void setup() {
  setupDisplay();

  mb.begin(19200);
  mb2.begin(19200);
  mb2.setTimeOut( 2000 );
  u32wait = millis() + 1000;
}

void loop() {
  displayAction.check();
  modbusPoll();
}

I dont recommend begin methods inside your class. Demand a port that has begun. If two objects do share one port, you don't want to befin multiple times and even at different rates. A hardware serial port is a resource. It should be used not altered by your class. The caller may assume the port has not been altered after some calls to your class instance. Just my 2 cents.

I dont recommend begin methods inside your class.

Why?

Demand a port that has begun. If two objects do share one port, you don't want to begin multiple times and even at different rates.

I understand your point of view, but I assume that the application wiring wouldn't change. This means that one port would be used just one. That's why I decided to make the class do all this stuff.

Why?

Because setting up the port, ready for your class to use, is the responsibility of the user of your class, not the responsibility of your class.

OK! It's understood. And what do you suggest to implement NewSoftwareSerial or SoftwareSerial there? In your previous topic, you have mentioned Stream class, isn't it?

NewSoftSerial was developed as a replacement for the hideously inefficient SoftwareSerial available with pre-1.0 versions of the IDE. Starting with 1,0, the old SoftwareSerial was dumped, and NewSoftSerial was adopted as its replacement, and renamed to SoftwareSerial.

Unless you need backwards compatibility, and I see no reason why you should, forget about NewSoftSerial.

SoftwareSerial and HardwareSerial are both derived classes. Look at the base class for each one. What do you know. It’s the same base class. Use that as the type for port, and users of you class can supply a HardwareSerial instance or a SoftwareSerial instance.