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);
}
-
1I 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?– JRobertJul 17, 2016 at 12:18
2 Answers
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 Print
s:
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.
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