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.
1 Answer 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.
-
This is a complete explanation that clears up the problem. Thank you for your time.Willem Ferguson– Willem Ferguson2021年01月05日 07:52:39 +00:00Commented Jan 5, 2021 at 7:52
switch_is_on
and I would write a methodprintSerial( ... )
containing a if else statement that uses the Serial object for theswich_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 ;-))?Serial
orSerial1
to a variable of typeHardwareSerial &<name>
likeHardwareSerial &activeSerial = Serial1;
If you declareactiveSerial
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, useHardwareSerial *activeSerial = &Serial
andactiveSerial->print...
.HardwareSerial *hs = &Serial1;
. Or do the assignment in a function.