Defect in serial port exposed when dual serial ports are defined

CraigF

Well-known member
When I use dual serial USB ports, the == operator does not appear to distinguish between Serial and SerialUSB1, as illustrated in the code below.

Code:
// Compile this for USB type = dual serial
void setup()
{
    Serial.begin(9600);
    SerialUSB1.begin(115200);

    if (Serial == SerialUSB1)
        Serial.println("Serial and SerialUSB1 think they are the same device.");  // This is what is printed
    else
        Serial.println("Serial and SerialUSB1 are recognized as different.");     // This is what is expected
}

void loop()
{}


Although there are workarounds, things would be cleaner and easier if this worked properly. For example, if I have a #defined DEBUG_PORT that may point either to Serial or to SerialUSB1, I may wish to test which one is currently mapped.

Frankly, I would prefer it if all serial ports had a public Name attribute.
 
This is using the boolean operator which tells if the port has a connection. So (Serial == SerialUSB1) is true when both are connected to a PC, or when both are not connected.
 
This is using the boolean operator which tells if the port has a connection. So (Serial == SerialUSB1) is true when both are connected to a PC, or when both are not connected.

OK, that makes sense. I should have realized that was what was going on.

So the question remains: what mechanism would be reliable for testing whether two aliases for SerialUSB ports refer to the same port? The following code "works" in my present application, but perhaps there are "gotchas" ??

Code:
if (static_cast <Stream*> (&Serial) == static_cast <Stream*> (&SerialUSB1))
// etc.
 
Last edited:
The following code "works" in my present application, but perhaps there are "gotchas" ??
Comparing the addresses of the port objects as you did is perfectly OK. The only 'drawback' (if at all) is, that it needs to be done at runtime (if the compiler is not able to optimize it away...)

Frankly, I would prefer it if all serial ports had a public Name attribute.

Actually those ports are not objects of a common 'Serial' class but are different types (usb_serial_class, usb_serial2_class and usb_serial3_class). Their common base class class is `Stream`. So, if you wanted to add that attribute you'd have to do it at the Stream class. But, since everyone and their grandmother is deriving from Stream, adding new stuff to that class is not very likely to happen.

-----
(I doubt that the following is worth the effort for your use case but the technique might come in handy for other uses)

If you need a compile time solution you can compare the Types of Serial, SerialUSB1 etc. to find out which port you are actually using. E.g. you can use the simple std::is_same<T,U> type trait for that:

Code:
#include "Arduino.h"
#include <type_traits>

#define debug SerialUSB1
#define logger SerialUSB2
#define mainCom Serial

/**
 * returns 0, 1,2 if serialObject is of type Serial, SerialUSB1 or SerialUSB2 respectively
 * and -1 in all other cases. If the type of serialObject is known at compile time the
 * function is evaluated at compile time
 */
template <typename T>
constexpr int getType(const T& serialObject)
{
    if (std::is_same<T, usb_serial_class>::value)
        return 0;
    else if (std::is_same<T, usb_serial2_class>::value)
        return 1;
    else if (std::is_same<T, usb_serial3_class>::value)
        return 2;
    return -1;
}

void setup()
{
    while (!Serial) {}

    Serial.printf("debug port: %d\n",getType(debug));
    Serial.printf("log port:   %d\n",getType(logger));
    Serial.printf("main port:  %d\n",getType(mainCom));

    static_assert(getType(debug) == getType(SerialUSB1), "Error");  // demonstrates that getType is evaluated at compile time.
}

void loop()
{
}

Prints:
Code:
debug port: 1
log port:   2
main port:  0

If you change 'SerialUSB1' to e.g. 'Serial' in the static_assert you'll get a compiler error which demonstrates that getType is evaluated by the compiler. I.e., no code is generated for it.
 
If you need a compile time solution you can compare the Types of Serial, SerialUSB1 etc. to find out which port you are actually using. E.g. you can use the simple std::is_same<T,U> type trait for that:

Thank you! I was unaware of <type_traits>, and it will definitely come in handy.
 
Back
Top