How to choose serial port as variable

I wonder how to use serial port name as a variable to avoid multiplying code for every serial port

simple example:

#define SerialNR1 1
#define SerialNR2 2

byte serialData;

void ReadSerial(int SerialNR)
{
// How to execute serial1,2,3 etc. as a function parameter. Something like
// SerialNR.read(); ? I want to avoid constructions like switch case, or condition

// my "classic" approach
switch (SerialNR)
{
case SerialNR1:
{
serialData = Serial1.read();
}
break;

case SerialNR2:
{
serialData = Serial2.read();
}
break;
}

}

ReadSerial(SerialNR1); //function execution, serial1 as a parameter

Pass a pointer to a Stream object:

int readSerial(Stream *s);

void setup() {
  int i;

  i = readSerial(&Serial);
  i = readSerial(&Serial1);
  i = readSerial(&Serial2);
}

void loop() {
}

int readSerial(Stream *s) {
  return s->read();
}

ok, looks good, but how to use serial.available(); and remove data from serial buffer after readout?

The '->' notation is used to access member functions and data via a pointer:

s->read()
s->available()
s->print()

etc.

    HardwareSerial  *s = &Serial2;

@gfvalvo

I try like that, this gives me a flood of data (Serial1 buffer size), looks like condition "s->available() > 0" is always true, even if serial1 buffer size is "0" and s->available() returns "0"

void setup()
{
	Serial.begin(115200);
	Serial1.begin(9600);
}

int readSerial(Stream *s) {

	if ( s->available()  > 0)
	{
		return s->read();
	}
	else
	return -1;
}

void loop()
{
	Serial.write(readSerial(&Serial1));
}

@gcjr

could you explain little more?

I suggest that you post a full example that so we can check what you're doing. I also suggest that you read How to use this forum - please read. - Installation & Troubleshooting - Arduino Forum, specifically point #7 about posting code.

cdxa:
this gives me a flood of data (Serial1 buffer size), looks like condition "s->available() > 0" is always true, even if serial1 buffer size is "0" and s->available() returns "0"

void setup()

{
Serial.begin(115200);
Serial1.begin(9600);
}

int readSerial(Stream *s) {

if ( s->available()  > 0)
{
return s->read();
}
else
return -1;
}

void loop()
{
Serial.write(readSerial(&Serial1));
}

Using s->available() is exactly the same as Serial1.available(). It looks like the problem is you don't fully understand how it works. Take a look at @Robin2's Serial Input Tutorial.

the Arduino IDE hides the definitions of Serial, Serial1, Serial2 and Serial3 as variables of type HardwareSerial (they are at the bottom of HardwareSerial.h)

like any variable with structure, a pointer of that variable type can be used in place the variable to access internal elements using "->" instead of ".".

i've used a Serial pointer when developing code on the Mega which has several Serial interfaces and ported that code to an Uno which has just one. Writing the code using a Serial pointer and using an interface other that Serial on the Mega which was used for debugging, allow me to easily use Serial in place of Serial3 for example without needing to rewrite any code.

HardwareSerial* s = NULL;

void
setup (void)
{
# if 1
    s = &Serial3;
# else
    s = &Serial;
# endif

    s->begin (9600)
}

void
loop (void)
{
    byte c  = (byte) NULL;
    if (s->available()) {
        c = s->read ();

        // do something with c
    }
}

gcjr:
the Arduino IDE hides the definitions of Serial, Serial1, Serial2 and Serial3 as variables of type HardwareSerial

Not true on all boards in the Arduino Ecosystem. Not every "Serial" object is an instance of HardwareSerial. But, they are all instances of Stream. Use a pointer to that class.

If I move serial availability checking into other function everything works well

void setup()
{
	Serial.begin(115200);
	Serial1.begin(9600);
}

int readSerial(Stream *s) {

	return s->read();
}

int SerialAvailable(Stream *s) { 

	return s->available();

}

void loop()
{
	if (SerialAvailable(&Serial1))  // this condition is true only when serial buffer is > 0
	{
		Serial.write(readSerial(&Serial1));
	}
}

Maybe i'm really don't understand that, and im be appreciated if some one could me explain why this condition always is true?

int readSerial(Stream *s) {

 if ( s->available()  > 0) // this is always true no matter from buffer bytes count
 {
      return s->read();
 }
 else
 return -1;
}

Because -1 is non-zero, and hence, true.

Also - why use pointers?
Wouldn't references be more natural?

I can put in condition even 100, 1000 and no matter what else, is always true ( s->available() > 1000)
no matter what number I use to compare, the condition is always true, even if buffer chars count is zero. This is what wondering me.

Post code

@TheMemberFormerlyKnownAsAWOL
Condition works OK, that's my fault, sorry :confused:

Here is my code, I want to store serial port number in structure Serial DataSet.SerialPort and use it in readSerial(), appropriate to present used structure, but I don't know how to do

typedef struct SerialDataSet
{
	public:
	uint8_t SerialPort;   // here i want to store my serial port nr
	byte SerialData[64];  // my serial buffer copy
} SerialDataSet;
SerialDataSet SerialPort1;
SerialDataSet SerialPort2;

void readSerial( SerialDataSet & data); 



void readSerial( SerialDataSet & data)
{
	while (Serial1.available() > 0) // here i want to point to serial port stored in SerialDataSet.SerialPort, but I don't know how?
	{
		uint8_t incomingchars = Serial1.available();
		
		data.SerialData[data.SerialData[0]+1] = Serial1.read();
		data.SerialData[0] = data.SerialData[0] + incomingchars;

	}
}
void setup()
{
	Serial.begin(115200);
	Serial1.begin(9600);
	Serial2.begin(9600);

	// set variables
	SerialPort1.SerialPort = 1;    // here i want to store port number 1 = Serial1
	SerialPort1.SerialData[0] = 0; // zero chars in buffer
	SerialPort2.SerialPort = 2;    // here i want to store port number 2 = Serial2
	SerialPort2.SerialData[0] = 0; // zero chars in buffer
}


void loop()
{
	readSerial(SerialPort1);  // read SerialPort1 and store in SerialPort1.SerialData buffer
	readSerial(SerialPort2);  // read SerialPort2 and store in SerialPort2.SerialData buffer

	if (SerialPort1.SerialData[0] > 0)  // send data from my buffer do serial for debug purposes
	{
		for (int i = 1; i <= SerialPort1.SerialData[0]; i++)
		{
			Serial.write(SerialPort1.SerialData[i]);
		}
		SerialPort1.SerialData[0] = 0;
	}


}

I want to store serial port number

The serial port number you are referring to means nothing to the Arduino. The instance names could just as easily be Bob, Sam, Frodo, and Satan.

If you want the user to supply a number, and for the Arduino use that number to know which instance name to use, that isn't going to happen unless YOU write some code to map the number to a pointer to an instance (which isn't that hard).

Ok so for example a want to store in struct "Bob" it means Serial1 and Satan means Serial2 how to pass command to proper port based on settings from structure? To simplify numeric values will be enough, 1=Serial1 , 2=Serial2 :wink:

Use pointers as already demonstrated.

Ok, but how to store pointer in structure?
As you can see thanks to your advice im trying to use, I use &Serial1 and thats works, but I want to pass to functions different structures, every of it has own serial port number, exactly as in code from post #14, but I don't know how to store pointer in the structure

consider the following code tested using one serial interface on an Uno (tested on Uno)

i hope this illustrates the used of structs, static initialization and pointers

typedef struct
{
    HardwareSerial  *port;
    byte            data [64];
} SerialDataSet_t;

SerialDataSet_t serialData [] = {
    { & Serial  },
 // { & Serial2 },      // Uno has only 1 interface
 // { & Serial3 },      // Uno has only 1 interface
};

#define N_SERIAL (sizeof(serialData)/sizeof(SerialDataSet_t))

// -----------------------------------------------------------------------------
int readSerial (SerialDataSet_t *s)
{
    while (s->port->available() > 0)
    {
        byte c = s->port->read();
        s->data [1 + s->data [0]] = c;
        s->data [0]++;

        if ('\n' == c)
            return s->data [0];
    }

    return 0;
}

// -----------------------------------------------------------------------------
void setup()
{
    serialData [0].port->begin(115200);       // Uno has only 1 interface

}

// -----------------------------------------------------------------------------

void loop()
{
    for (unsigned n = 0; n < N_SERIAL; n++)  {
        SerialDataSet_t  *s = (& serialData [n]);

        if (readSerial (s))  {
            // echo received data back
            s->data [s->data [0]] = 0;      // insure that it is NUL terminated
            Serial.print   ("received: ");

            Serial.println ((char*) & (s->data [1]));

            s->data [0] = 0;
        }
    }
}

Notes:

  • seems unnecessary for structure sub-field names to include the name of the struct or variable

  • structs, while similar to class, don't require public

  • the "_t" suffix indicates the symbol name is a typedef

  • variables are always NOT capitalized

  • an array of struct can be statically initialized within a table (e.g. serialData)

  • unspecified field values in a static initialization are set to zero (e.g. data [])

  • the N_SERIAL macro is a conventional way to let the compiler determine the # of struct elements

  • readSerial() is passed a pointer which avoids knowledge of how the variable is maintained (i.e. discrete variables, array element, ...) And save space in the code

  • my Serial monitor is configured to terminate all lines with a '\n' making it easier to detect the end of t a line. At 115200, transmission was fast enough that the while only captured one character

  • readSerial () returns a non-zero result only after seeing the '\n'

  • setup() demonstrate directly referencing an array element

  • loop() demonstrates passing a pointer to the serialData array element to readSerial()

  • loop() echos the received string back on the same Serial interface because the Uno has just on port