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.

asked Jan 4, 2021 at 6:40
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 ;-))? Commented Jan 4, 2021 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. Commented Jan 4, 2021 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.... Commented Jan 4, 2021 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? Commented Jan 4, 2021 at 9:15
  • 1
    Only initialization is allowed at global scope, e.g. HardwareSerial *hs = &Serial1;. Or do the assignment in a function. Commented Jan 4, 2021 at 11:43

1 Answer 1

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.

answered Jan 4, 2021 at 14:23
1
  • This is a complete explanation that clears up the problem. Thank you for your time. Commented Jan 5, 2021 at 7:52

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.