Class for implementing several serial ports

Hi all,

I would like to implement several serial ports to a Modbus Slave class from

https://sites.google.com/site/jpmzometa/arduino-mbrt/arduino-modbus-slave

I mean that the serial port number from 0 to 3 could be declared at the object, but this should be allowed only depeding on the Microcontroller. Then

  • Any Arduino ONE or Duemilia would be allowed to use only port 0;
  • An Arduino Mega could use any port from 0 to 3 or even include several Modbus objects associated to different ports.

Indeed I'm planning to extend this feature to a custom PCB based on an ATMEGA1284P.

The code should start with something like this:

HardwareSerial port = Serial;

class ModbusSlave {
public:
  ModbusSlave();
  ModbusSlave(unsigned char serno);
  void configure(unsigned char slave, long baud, char parity);
};

// empty constructor
ModbusSlave::ModbusSlave() {
  this->serno = 0;
  this->txenpin = 0;
}

ModbusSlave::ModbusSlave(unsigned char serno) {
  this->serno = (serno > 3) ? 0 : serno;
  this->txenpin = 0;
}

/* 
 * configure(slave, baud, parity, txenpin)
 *
 * sets the communication parameters for of the serial line.
 *
 * slave: identification number of the slave in the Modbus network (1 to 127)
 * baud: baudrate in bps (typical values 9600, 19200... 115200)
 * parity: a single character sets the parity mode (character frame format): 
 *         'n' no parity (8N1); 'e' even parity (8E1), 'o' for odd parity (8O1).
 * txenpin: arduino pin number that controls transmision/reception
 *        of an external half-duplex device (e.g. a RS485 interface chip).
 *        0 or 1 disables this function (for a two-device network)
 *        >2 for point-to-multipoint topology (e.g. several arduinos)
 */

void ModbusSlave::configure(unsigned char slave, long baud, char parity)
{
  this->slave = slave;

  switch( serno ) {
    case 1:
      port = Serial1;
      break;
    case 2:
      port = Serial2;
      break;
    case 3:
      port = Serial3;
      break;
    default:
      port = Serial;
      break;
  }
  port.begin(baud);

  if (txenpin > 1) { // pin 0 & pin 1 are reserved for RX/TX
    pinMode(txenpin, OUTPUT);
    digitalWrite(txenpin, LOW);
  }

  return;
}

How can I implement this depending on the microcontroller?

Best Regards,

ModbusSlave::ModbusSlave() {
  this->serno = 0;
  this->txenpin = 0;
}

this-> is implied. You look silly using it.

How can I implement this depending on the microcontroller?

There are #defined variable that define which board is selected when compiling. Why can't you use them?

This should work, will give error on a serial number not on micro, can modify it to return default when not available if needed.

template< int i_Number > class SerialSelector;
template<> struct SerialSelector< 0 >{ static inline HardwareSerial &Get( void ){ return Serial; } };  
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)    
  template<> struct SerialSelector< 1 >{ static inline HardwareSerial &Get( void ){ return Serial1; } }; 
  template<> struct SerialSelector< 2 >{ static inline HardwareSerial &Get( void ){ return Serial2; } }; 
  template<> struct SerialSelector< 3 >{ static inline HardwareSerial &Get( void ){ return Serial3; } }; 
#endif

Your class

template< int _Serial > class YourClass{

  public:
    YourClass(void) : port( SerialSelector< _Serial >::Get() ) 
      { return; }
  private:

    HardwareSerial &port;
};

P.S. 'this->' is fine, it was implemented for a reason.

P.S. 'this->' is fine, it was implemented for a reason.

Yes, it was. The way it is being used by OP is not that reason, though.

this-> is implied. You look silly using it.

It's useless but I like it. There's no extra code for this, isn't it?

There are #defined variable that define which board is selected when compiling. Why can't you use them?

I'd like to use this but I didn't know how. I was unsuccessfully trying to look for any example. The solution provided by pYro_65 is good for Arduino based CPUs, but I would like to use it on an ATMEGA1284P or other controllers. How can I do it?

Thanks for your help! I'll try it as soon as possible.

You can use the same method to select a USART rather than an arduino HardwareSerial object, or add a 1284 to arduino ( which people have already done ).

The 'this->' is not useless in this situation, class declarations can be big, the 'this->' makes it obvious that the variable belongs to the class. People using the class for the first time will have an easier job modifying/understanding the class.

Anything that can remove a person from assuming something is beneficial.

Hi pYro_65,

I've been trying to implement this "template" instances, but it's the first time I find them. Could you show me how to do it? Just a link to anywhere or a short example would be fine for me.

Thanks for your help! I owe you one beer. :grin:

As you want to use it on a non-standard Arduino, you will either have to modify an Arduino core for that processor ( many people have posted mods for many chips ), or use the AVR functionality directly, skipping the Arduino libraries. If you use an arduino core, this should work with any avr that supports up to 4 serial ports.

The technique used is template specialisations.

//SerialSelector declaration
template< int i_Number > class SerialSelector;

//SerialSelector specialisations.
#if defined(UBRRH) || defined(UBRR0H)
  template<> struct SerialSelector< 0 >{ static inline HardwareSerial &Get( void ){ return Serial; } };
#endif
#if defined(UBRR1H)
  template<> struct SerialSelector< 1 >{ static inline HardwareSerial &Get( void ){ return Serial1; } };
#endif
#if defined(UBRR2H)
  template<> struct SerialSelector< 2 >{ static inline HardwareSerial &Get( void ){ return Serial2; } };
#endif
#if defined(UBRR3H)
  template<> struct SerialSelector< 3 >{ static inline HardwareSerial &Get( void ){ return Serial3; } };
#endif

You could eventually extend it to return a SoftwareSerial interface when no hardware USART is available ( or need more than available )