2

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.

  • No. The general technique should work OK. Perhaps the Bluetooth can't handle more than 30 bytes before throwing in a newline of its own. In any case, why not use println rather than print and then supplying your own newline? – Nick Gammon Jun 9 at 11:19
  • I tried with both println("...") and print("...\n"), same behavior with both. I hard coded the same outputs with Serial and Serial1 and it works fine so nothing to do with Bluetooth. It is something to do with the new class. – Hubert B Jun 9 at 11:32
  • 1
    The Bluetooth link should see the exact same byte stream whether you print through 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 overriding virtual size_t write(const uint8_t *buffer, size_t size). – Edgar Bonet Jun 9 at 11:37
  • 1
    Should that look like this: 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; } – Hubert B Jun 9 at 13:03
  • I doubt the timing will matter. Those bytes will be buffered and sent out by an interrupt service routine. – Nick Gammon Jun 9 at 22:50
3

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;
  • 1
    for advanced users I would override availableForWrite() and flush() too – Juraj Jun 9 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 of DualPrint, is that right? How is it declared? DualPrint out;?? – Hubert B Jun 9 at 9:50
  • @HubertB: Yes, out is an instance of DualPrint. See expanded answer. – Edgar Bonet Jun 9 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 Jun 9 at 10:51

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.