I am writing an Arduino program that uses Bluetooth on Serial1
to print text to a Bluetooth terninal on an Android phone and also normal Serial
to print text to the serial monitor on a laptop. I would like to wrap the Serial.print()
and Serial.println()
functions so that they work with either or both Serial
and Serial1
. For example the code below works fine depending on the values of the global variables. But this only works for single chars, but print()
and println()
can take a very wide variety of datatypes. If I also define overloading functions for int and String types it works fine, but that is a very verbose and maybe fragile solution, it also ignores the optional inputs to the underlying functions. What is the proper way to do this ?
void print(char x) {
if (g_use_Serial)
Serial.print(x);
if (g_use_Serial1)
Serial1.print(x);
}
void println(char x) {
if (g_use_Serial)
Serial.println(x);
if (g_use_Serial1)
Serial1.println(x);
}
I have implemented the solution suggested and it works nearly perfectly. There seems to be a problem with the new line characters on the bluetooth (Serial1
) connection.
The code is:
class DualPrint : public Print
{
public:
DualPrint() : use_Serial(false), use_Serial1(false) {}
virtual size_t write(uint8_t c) {
if (use_Serial) Serial.write(c);
if (use_Serial1) Serial1.write(c);
return 1;
}
bool use_Serial, use_Serial1;
} out;
void setup() {
Serial.begin(9600);
Serial1.begin(9600);
delay(1000);
}
void loop() {
out.use_Serial = true;
out.print("Printed to USB only.\n");
out.use_Serial1 = true;
out.print("Printed to both USB and BT.\n");
out.use_Serial = false;
out.print("Printed to BT only.\n");
out.use_Serial1 = false;
delay(3000);
}
The output on the USB Serial monitor is as expected:
Printed to USB only.
Printed to both USB and BT.
Printed to USB only.
Printed to both USB and BT.
The output on the Bluetooth terminal is not as expected, the new lines are not in the right position:
Printed to both USB and BT.Pri
nted to BT only.
Printed to both USB and BT.Pr
inted to BT only.
Printed to both USB and BT.Pr
inted to BT only.
Printed to both USB and BT.Pr
inted to BT only.
Any suggestions on how to fix this. Are there other function members that need to be re-implemented to make this work properly?
Many thanks for high quality answers so far.
1 Answer 1
You can create a class derived from Print
that forwards its output to
either or both Serial
and Serial1
. The only method you need to
implement for this to work is write(uint8_t)
:
class DualPrint : public Print
{
public:
DualPrint() : use_Serial(false), use_Serial1(false) {}
virtual size_t write(uint8_t c) {
if (use_Serial) Serial.write(c);
if (use_Serial1) Serial1.write(c);
return 1;
}
bool use_Serial, use_Serial1;
} out;
You would use it like this:
out.use_Serial = true;
out.println("Printed to Serial only");
out.use_Serial1 = true;
out.println("Printed to both Serial and Serial1");
out.use_Serial = false;
out.println("Printed to Serial1 only");
Note that with this approach, unlike yours, printing number will format
them as text only once, and the underlying Serial
and Serial1
will
only handle the resulting characters.
Edit: To answer the question in OP's comment, the construct
class ClassName
{
...definition...
} classInstace;
is a shotcut for
class ClassName
{
...definition...
};
ClassName classInstace;
A very similar construct exists in plain C:
struct struct_name {
...the struct fields...
} struct_instance;
-
1for advanced users I would override
availableForWrite()
andflush()
too2019年06月09日 04:44:36 +00:00Commented Jun 9, 2019 at 4:44 -
@Edgar Many thanks for this, very helpful. One request, I am more experienced at C than C++ so the class definition is unfamiliar. In the usage example
out
is an instance ofDualPrint
, is that right? How is it declared?DualPrint out;
??Hubert B– Hubert B2019年06月09日 09:50:30 +00:00Commented Jun 9, 2019 at 9:50 -
@HubertB: Yes,
out
is an instance ofDualPrint
. See expanded answer.Edgar Bonet– Edgar Bonet2019年06月09日 10:05:02 +00:00Commented Jun 9, 2019 at 10:05 -
Edgar & @Juraj I have updated the question with a progress report on implementing the suggested solution. It works almost perfectly except for new lines in wrong sequence in BT terminal. Any suggestions? Is this related to flush() or other functions that also need to be re-implemented?Hubert B– Hubert B2019年06月09日 10:51:51 +00:00Commented Jun 9, 2019 at 10:51
-
I have implemented this, thank you (in this case to write to a SD error file) and noticed that the Serial.write() is much slower on the serial monitor than Serial.print() - any reasons for this and is this an issue?CestLaGalere– CestLaGalere2021年10月08日 18:21:41 +00:00Commented Oct 8, 2021 at 18:21
println
rather thanprint
and then supplying your own newline?println("...")
andprint("...\n")
, same behavior with both. I hard coded the same outputs withSerial
andSerial1
and it works fine so nothing to do with Bluetooth. It is something to do with the new class.Serial1
or this class. Only the timing will be slightly different, so it could be that whatever receives the data is sensitive to the timing of the incoming bytes. This could be mitigated by overridingvirtual size_t write(const uint8_t *buffer, size_t size)
.virtual size_t write(const uint8_t *buffer, size_t size) { if (use_Serial) Serial.write(buffer, size); if (use_Serial1) Serial1.write(buffer, size); return 1; }