0

I have an Arduino Mega controller that normally communicates via Bluetooth (HC08 Bluetooth module) to an Android device. In case there is a problem on the Android side I wish to have a fallback ability to communicate via the USB serial port. The choice of serial port is determined by a hardware switch that the Arduino can read. Currently I have:

HardwareSerial & Terminal = Serial;
HardwareSerial & BTPort = Serial1;

Is there a way to route an output to either BTPort or to terminal, dependent on the state of the switch? Ideally I would like to do something like this:

HardwareSerial & OutputPort
if(switch_is_on) 
  OutputPort = Terminal;
else
  OutputPort = BTPort;

Obviously the above code does not work. I have no idea of how to proceed.

10
  • Like you, I would use a global variable e.g. switch_is_on and I would write a method printSerial( ... ) containing a if else statement that uses the Serial object for the swich_is_on == true case and Serial1 for the else case. I would use this method instead of the Serial/Serial1 print methods. Also, to me it is not obvious whay your try should not work. What's the problem (besides the missing semicolon of cause ;-))? Jan 4 '21 at 8:05
  • Thank you, Peter, for your comment. Yes, it solves the problem, but not in the most efficient way. It would be most efficient if one could just re-assign a (pointer to?) a serial port and directly use the println for the re-assigned port. Your suggestion is a very useful work-around, though. Jan 4 '21 at 8:40
  • As I said, I don't know any reason, why you can't assign Serial or Serial1 to a variable of type HardwareSerial &<name> like HardwareSerial &activeSerial = Serial1; If you declare activeSerial as a global variable, you can access it from everywhere. I just don't know why it is obvious that this shouldn't work. At least for me it should. If you only want to use pointer, use HardwareSerial *activeSerial = &Serial and activeSerial->print.... Jan 4 '21 at 9:10
  • But, I would prefer the printMethod because it is the most effective way. What happens if you change the switching flag while the program is running? If you have a predefined activeSerial variable nothing happens. The method would use the other Serial intead. You see the idea? Jan 4 '21 at 9:15
  • 1
    Only initialization is allowed at global scope, e.g. HardwareSerial *hs = &Serial1;. Or do the assignment in a function.
    – Mat
    Jan 4 '21 at 11:43
1

I can think of three distinct ways (and one that is similar to another) of doing what you want.

But first a note on polymorphism. The HardwareSerial class is itself a child of the Stream class. It's this Stream class that provides all the read/write functionality (it, itself, is a child of the Print class which provides all the print(...) functions).

If all you want to do is print then you should be working with the Print class. if you want to do both reading and writing then the Stream class is what you want. You should only use HardwareSerial if you need to use the Serial-specific functions like .begin(baud).

So with that in mind you could:

1. Assign a pointer

Print *printer;

void setup() {
    Serial.begin(115200);
    Serial1.begin(115200);
    printer = &Serial;
}

void loop() {
    printer = &Serial;
    printer->println(millis());
    delay(100);
    printer = &Serial1;
    printer->println(millis());
    delay(100);
}

This is by far the most efficient method from a purely machine perspective.

Note the use of -> instead of . when calling a function on a pointer.

2a. Use a wrapper function

uint8_t stream = 0;

void printit(const char *text) {
    switch (stream) {
        case 0: 
            Serial.print(text);
            break;
        case 1:
            Serial1.print(text);
            break;
    }
}

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

void loop() {
    stream = 0;
    printit("Hello on stream 0\r\n");
    stream = 1;
    printit("Hello on stream 1\r\n");
}

This is less efficient and limits what you can print unless you write lots of overloaded functions.

2b. Use a macro

This is like 2a but uses a macro instead of a function:

#define printit(T) if (stream == 0) Serial.print(T) else Serial1.print(T)

This keeps the standard overloading of the print function but has lots of IF statements littered through your code. A fraction more efficient than 2a since it's all inline. Harder for a human to understand though.

3. Create a wrapper class

This is the rolls-royce method. Everything becomes encapsulated in your own object. Pointers are used to keep it efficient.

class Printer : public Print {
    private:
        Print *stream;
        Print *stream0;
        Print *stream1;
    public:
        Printer(Print &s0, Print &s1) : stream0(&s0), stream1(&s1) {
            stream = &s0;
        }
        size_t write(uint8_t c) {
            return stream->write(c);
        }
        void setStream(uint8_t sno) {
            switch (sno) {
                case 0: stream = stream0; break;
                case 1: stream = stream1; break;
            }
        }
};

Printer printer(Serial, Serial1);

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

void loop() {
    printer.setStream(0);
    printer.println(millis());
    delay(100);
    printer.setStream(1);
    printer.prinln(millis());
    delay(100);
}

With this method the new class could be put into a library and used across your projects.

You also get all the functions you'd normally get with the Serial object when printing (for reading change Print to Stream and implement all the pure virtual functions you need for that class).

And a footnote for all those that say "Why use pointers? Why not references?" I say: I hate references. Pointers are far more obvious what they are. References you can't tell apart from distinct variables. With a pointer you know it's a pointer. References are the spawn of the devil.

1
  • This is a complete explanation that clears up the problem. Thank you for your time. Jan 5 '21 at 7:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.