Mega Multi-Serial and Software Serial in indexed array?

I’m trying to use many serial ports. I’d like 4 and don’t want to lose the USB serial port, so I’ll probably use SoftwareSerial as well.

I’ve seen other comments that it would be more convenient to have an array of Serial items, rather than Serial, Serial1 etc. This can be done using an array of pointers.

// Declaration
HardwareSerial *Serials[5];
SoftwareSerial mySerial1(5, 6);
..
// Setup

Serials[0] = &Serial;
Serials[1] = &Serial1;
Serials[2] = &Serial2;
Serials[3] = &Serial3;

// Now the Hardware Serial devices can be indexed

for (ix=0; ix<4; ix++)
{
	(*Serials[ix]).begin(9600);
	(*Serials[ix]).print("\nMega_Serials_Test- ");
	(*Serials[ix]).print(ix);
	(*Serials[ix]).println();
}

That’s all good and works. What I’d really like is the SoftwareSerial in the same array. Is it possible to pursuade the compiler to accept this? and would all the functions work correctly? I’ve tried various things but all get rejected e.g.:

Serials[4] = (&HardwareSerial) &mySerial1;

Can it be done?

I can't verify right now but a more reasonable cast would be using reinterpret_cast or dynamic_cast but I don't think SoftwareSerial inherits from HardwareSerial - they probably both inherit from Stream

You could create an abstraction class like "abstractSerial" which would create either a HardwareSerial or SoftwareSerial as an instance variable at instantiation - depending on a calling parameter - and then just reimplement the same public functions and pass them to the real object.

Thanks for that, I understand he ideas but at the moment beyond my C++ abilities. I’m going to get the thing working with the hardware channels and then come back to this.

For output (.write(), .print(), .println()) make your array type Print. For input (.available(), .read(), .peek()) make your array type Stream. Call .begin() on each port separately since .begin() is not part of Stream or Print.

There is a C/C++ operator for calling a method through a pointer: Use '->' instead of '.'.

Serials[ix]->println(F("String constant"));

Well that compiled and may have worked. I say may because ports 3 and 4 are changing to a different baud rate. I set them all to 9600, and write a test message - good, after that things get messed up. I'm trying eliminating various things - remove software serial, go back to standard serial addressing, different Mega. Sadly I have to do shopping, etc. can't play all day. Back later...

Here is an example version that compiles:

#include <SoftwareSerial.h>
SoftwareSerial mySerial1(5, 6);

Print *Serials[4] = {&Serial, &Serial1, &Serial2, &mySerial1};

// Setup
void setup() {
  Serial.begin(9600);
  Serial1.begin(9600);
  Serial2.begin(9600);
  mySerial1.begin(9600);
}

void loop() {
  for (int ix = 0; ix < 4; ix++) {
    Serials[ix]->print(F("\nMega_Serials_Test- "));
    Serials[ix]->println(ix);
  }
  delay(2000);
}

Here's a wrapper class that provides polymorphism for those types. It lets you declare an array of AnySerial[5] and attach any serial type. It does the downcasting in each of the methods, since there's NO COMMON BASE CLASS for all the serial classes. I'm looking at you, Arduino gods. >:-|

Every time I see one of these problems, I am reminded that Cosa is a really good design.

Cheers, /dev

I’ve been away, missed the last 2 posts, but going from #3, thanks johnwasser, I have working example. Consosle chars are echoed and sent to the other 4. Chars from the other 4 are echoed and sent to the console. I don’t understand the F(), I’ve omitted it and all seems well. ?

#include <SoftwareSerial.h>

int led = 13;

const int nof_ports = 5;

// Separate Read and Write arrays...
Print  *SerOu[nof_ports];
Stream *SerIn[nof_ports];

SoftwareSerial mySerial1(10, 11); // RX, RX

void setup()
{
  int ix;

  pinMode(led, OUTPUT);

  SerIn[0] = &Serial;
  SerIn[1] = &Serial1;
  SerIn[2] = &Serial2;
  SerIn[3] = &Serial3;
  SerIn[4] = &mySerial1;

  SerOu[0] = &Serial;
  SerOu[1] = &Serial1;
  SerOu[2] = &Serial2;
  SerOu[3] = &Serial3;
  SerOu[4] = &mySerial1;

  Serial.begin(9600);
  Serial1.begin(9600);
  Serial2.begin(9600);
  Serial3.begin(9600);
  mySerial1.begin(9600);

  // Announce on each port
  for(ix=0; ix<nof_ports; ix++)
  {
    SerOu[ix]->print("Mega Serial: ");
    SerOu[ix]->print(ix);
    SerOu[ix]->println();
  }
}

void loop()
{
  int ix;
  byte in_ch;

  // Chars from 4 ports
  // Echo, copy to console
  for(ix=1; ix<nof_ports; ix++)
  {
    if (SerIn[ix]->available() > 0)
    {
      digitalWrite(led, 1);
      in_ch = SerIn[ix]->read();
      SerOu[ix]->write(in_ch);
      SerOu[0]->write(in_ch);
      delay(5); //only for led
      digitalWrite(led, 0);
    }
  }

  // Chars from console
  // Echo, send to other 4
  if (SerIn[0]->available() > 0)
  {
    in_ch = SerIn[0]->read();
    digitalWrite(led, 1);
    for(ix=0; ix<nof_ports; ix++)
    {
      SerOu[ix]->write(in_ch);
    }
    delay(5);
    digitalWrite(led, 0);
  }
}

RichardDL: I don't understand the F(), I've omitted it and all seems well. ?

F() is macro to put the strings into flash memory

When an instruction like

Serial.print("Write something on  the Serial Monitor");

is used, the string to be printed is normally saved in RAM. If your sketch prints a lot of stuff on the Serial Monitor, you can easily fill the RAM. If you have free FLASH memory space, you can easily indicate that the string must be saved in FLASH using the syntax:Serial.print(F("Write something on the Serial Monitor that is stored in FLASH")); (extract from the PROGMEM reference section)

Our typical Arduino UNO have 32k bytes of Flash (of which .5k is used for the boot loader) and only 2k bytes of SRAM. quite often it's better to dump the static strings into the Flash to save SRAM for your dynamic stuff in the program

F() - alright, thanks, will be useful sometime.

J-M-L: F() is macro to put the strings into flash memory

When an instruction like

Serial.print("Write something on  the Serial Monitor");

is used, the string to be printed is normally saved in RAM. If your sketch prints a lot of stuff on the Serial Monitor, you can easily fill the RAM.

It's worse than that. The string constant will be in FLASH in either case because the program needs a way to initialize the SRAM on reset or power-up. Using the F() macro keeps the string constants from using both FLASH and SRAM.