Redirecting/capturing Serial.println output?

Is there an easy way of capturing the char characters generated inside the Serial.println function before they go to the UART so they can be piped to another destination?

Currently I have a bunch of variable length debug messages that go out through the serial port, but I would like to send them out through a CAN bus interface which has an 8 byte maximum message size. If I can capture the chars as they are generated inside Serial.println then I can group them into sets of 8 and send them via the CAN bus library that I'm using.

Without digging into library details, a simple solution were another Serial port, e.g. Serial1 on a Mega, listening to the output on the Serial TX pin (1). Then copy the characters received on that other Serial port wherever you like.

You also can write your own SerialCan class, based on Serial, that copies all output also to the CAN bus.

You also can write your own SerialCan class, based on Serial Print

Fixt. :slight_smile:

His class just needs the “print” and “println” methods, not the UART handling code. For example:

class SerialCan : public Print
{
  uint8_t count;
  uint8_t buffer[ 8 ]

public:
  SerialCan() : count(0) {};

    ...

  virtual size_t write(uint8_t);

  using Print::write; // make the other overloads of write visible
};

size_t SerialCan::write( uint8_t b )
{
  buffer[ count++ ] = b;
  if (count == sizeof(buffer))
    CAN write( buffer, count );
    count = 0;
  }

  return Serial.write( b )

}

All the print methods use “write”, so that’s the main one to override. If you also do Serial.read(), you would need to derive from

Stream[tt] instead:

[code]class SerialCan : public Stream
{
  uint8_t count;
  uint8_t buffer[ 8 ]

public:
  SerialCan() : count(0) {};

    ...

  virtual int read() { return Serial.read(); }

    < other Stream methods, like available >

  virtual size_t write(uint8_t);

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

};[/code]
Cheers,
/dev

Thanks, I had no idea it would be that simple.

Dear -dev and mikb55!

Thanks, I had no idea it would be that simple.
Good for you mikb55 :slight_smile:

I would like to redirect all Serial."Output" (Serial.print|Serial.println|Serial.write|etc.) to a String Variable. Is this possible with your code? If yes can you pleas send me an example code and where to put them. I am working with varrious Arduino MKR Boards...

Thanks for any help!!!
Andreas

My code is almost the same as that provided by -dev.
I think this worked, not too sure as it was years ago. You can ignore the CAN specific bits. The way the buffer is filled and emptied is probably relevant to your needs.
To use it you make an instance of SerialCan and call the usual print methods. The magic happens inside the overridden write method.

SerialCan.h

#ifndef SERIALCAN_H
#define SERIALCAN_H

#include <canDataUnion.h>
#include "Arduino.h"
#include <stdint.h>

uint8_t sendCanMessage(uint16_t address, uint8_t size, byte *buf); 

class SerialCan : public Print
{
    uint8_t count = 0;
    uint8_t buffer[8];

    public:
        SerialCan() : count(0) {};

        virtual size_t write(uint8_t);

        using Print::write; // make the other overloads of write visible
};

#endif

SerialCan.cpp

#include <SerialCan.h>

size_t SerialCan::write( uint8_t b )
{

    buffer[ count++ ] = b;
    if ((count == sizeof(buffer)) || (b == 0x0A)){
      
      sendCanMessage(CAN_ADDRESS_NODE8_DEBUG_TEXT, 8, buffer);
      count = 0;

      for(uint8_t n = 0; n < sizeof(buffer); n++){ // clear buffer
       buffer[n] = 0;
      }
      
    }

    return Serial.write(b);

}

Thanks a lot mikb55!