2
void Multi_print (String str)
{
  Serial.print(str);
  mySerial.print(str);
 }

void Multi_println (String str)
{
 Serial.println(str);
 mySerial.println(str);
}

void Multi_parse_int (int value)
 {
    Serial.print(value);
    mySerial.print(value);
 }
1
  • 1
    I can't tell what your goal is: One function to replace all three? The same three functions but printing on any one Serial instead of on both ones? Or something else?
    – JRobert
    Jul 17, 2016 at 12:18

2 Answers 2

4

Just send your data to a tee: this is a type of pipe fitting where you push the data through one end and it goes out through the other two ends. As it happens, both Serial and Softserial (and almost anything that can print()) inherit from the virtual Print class. You can implement a tee as a Print that outputs to two Prints:

class Tee : public Print
{
public:
    Tee(Print &_p1, Print &_p2)
    : p1(_p1), p2(_p2)
    {}

    size_t write(uint8_t c)
    {
        size_t count1 = p1.write(c);
        size_t count2 = p2.write(c);
        return min(count1, count2);
    }
private:
    Print &p1, &p2;
};

The write(uint8_t) is the only method you really need to implement to have a working Print. Then, all the print() and println() methods will work.

Example usage:

Tee dualSerial(Serial, mySerial);
dualSerial.print("The answer is ");
dualSerial.println(42);

Note that parseInt() will not work, as it is not a method from Print. But if you think twice, you will realize that parseInt() on a pair of channels just does not make sense.

1
  • Very nice solution to the question as asked. :)
    – Nick Gammon
    Jul 17, 2016 at 22:05
1

LOL, while Edgar was busy answering your question, I posted a github implementation called NeoTee which will tee any number of streams. I have occasionally wanted such a class (attached at the end). He has explained the approach nicely.

One quirk of the Arduino Print and Stream classes is that the flush method is in Stream, not Print. So to provide that method in the NeoTee class requires deriving from Stream. But Stream requires that you implement read, available and peek. Although available can return the sum of the characters available on all Streams, the usefulness of read and peek is functional, but dubious. ;-)

One example sketch is available in the library. For your sketch, you would declare something like this:

Stream *streams[2] = { &Serial, &mySerial };
NeoTee tee( streams, sizeof(streams)/sizeof(streams[0]) );

And used thusly:

tee.print( '!' ); // prints one character to both
tee.print( 123.45 ); // prints a floating-point number on both


NeoTee implementation:

#ifndef _NEOTEE_H_
#define _NEOTEE_H_

//------------------------------------------------------
// NeoTee
// Copyright (C) 2016, SlashDevin
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//

#include <Stream.h>

// This library is like the UNIX "tee" command, which splits an output stream 
// into two streams, but with N branches.  On the Arduino, an output stream is
// instance of the Arduino Print class, and the flush method is in Stream.
//
// Simply declare your own array of streams:
//
//     Stream *streams[2];
//
// Pass the array and its size in to the NeoTee constructor:
//
//     NeoTee tee( streams, sizeof(streams)/sizeof(streams[0]) );
//
// Any Stream operation on "tee" will be duplicated on all Streams in the array.
//
//     tee.println( F("This is sent to all Stream instances in the array.") );
//
// Because there is no base class with "begin" and "end" methods, you
//   must call those explicitly on the individual Streams (e.g., Serial.begin)

class NeoTee : public Stream
{
  Stream **Streams;
  uint8_t  NumStreams;

public:

  NeoTee( Stream **streams, uint8_t numStreams )
    {
      Streams    = streams;
      NumStreams = numStreams;
    }

  virtual void flush()
    {
      for (uint8_t i=0; i<NumStreams; i++)
        Streams[i]->flush();
    }

  virtual size_t write(uint8_t c)
    {
      size_t minWritten = 0;
      for (uint8_t i=0; i<NumStreams; i++) {
        size_t written = Streams[i]->write( c );
        if ((i == 0) || (minWritten > written))
          minWritten = written;
      }
      return minWritten;
    }

  using Stream::write; // make the other overloads visible

  virtual int available()
    {
      int total = 0;
      for (uint8_t i=0; i<NumStreams; i++)
        total += Streams[i]->available();
      return total;
    }

  virtual int read()
    {
      for (uint8_t i=0; i<NumStreams; i++)
        if (Streams[i]->available())
          return Streams[i]->read();
      return -1;
    }

  virtual int peek()
    {
      for (uint8_t i=0; i<NumStreams; i++)
        if (Streams[i]->available())
          return Streams[i]->peek();
      return -1;
    }

};

#endif
1
  • Very nice solution to the more general problem. :)
    – Nick Gammon
    Jul 17, 2016 at 22:05

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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