Third Serial Port on Uno R4 (Serial2)

I found this work around to access another hardware serial port on an Uno Minima.

#define UART2_TX_PIN (18u)  // Pin A4
#define UART2_RX_PIN (19u)  // Pin A5

// Instantiate the Serial2 class
UART _UART2_(UART2_TX_PIN, UART2_RX_PIN);  // Makes Serial2 available on pin A4 Tx, A5 Tx

It can be accessed as Serial2. The only problem I have found with it is flush() does not work, it just returns straight away.

1 Like

If you have convincing evidence that flush() does not work with Serial2, post an issue with the developers. They have quite a mountain of them to work through already.

Flush does not work with any of the Serial ports, with the currently released code.

I have a reasonably major update of the Serial code that I issued a PR awhile ago. Have not heard anything back on it.

1 Like

the WiFi module is on Serial2

1 Like

This is true on the Wifi, but not MINIMA.

You can similarly add a serial port to the WiFi as I did in some of my test sketches, like:

UNOR4-stuff/test_sketches/Serial_all_copy/Serial_all_copy.ino at main · KurtE/UNOR4-stuff (github.com)

#if defined(ARDUINO_UNOR4_WIFI)
UART _UART4_(18, 19);
#define SerialX Serial3
#else
UART _UART2_(18, 19);
#define SerialX Serial2
#endif
1 Like

Flush is working on my sketch for serial2!
Thanks for the tip, I was just getting cresy trying to find a solution for "serialsoftware" limitation to one istance.
... Now I'm short of 1 analog input, just need to find a solution ...

How are you testing that the flush() function works ?

Here is the code...
I've no evidence that flush is working but the sketch yes...
I've 3 serials working simultaneously at the moment...

void loop()
{
    do{
     for(int i=0;i<4;i++)
     {
       data[i]=Serial2.read();
     }
  }while(Serial2.read()==0xff);

  Serial2.flush();

  if(data[0]==0xff)
    {
      int sum;
      sum=(data[0]+data[1]+data[2])&0x00FF;
      if(sum==data[3])
      {
        distance=(data[1]<<8)+data[2];
        if(distance>30)
          {
           Serial.print("distance=");
           Serial.print(distance/10);
           Serial.println("cm");
          }else 
             {
               Serial.println("Below the lower limit");
             }
      }else Serial.println("ERROR");
     }
     delay(100);
}

What is the reason for using flush() in your sketch ?

flush blocks to wait for outgoing data. Since you aren't sending anything on Serial2 the flush here is doing nothing. There is no purpose if you aren't sending outgoing data.

1 Like

I thought It removes any buffered incoming serial data... as the word would also suggest. That's fine...I'll delete It and check for correct functionality. Thanks guys :+1:

The only place where flush means that is in toilets. In just about everything computer related it means to wait while data goes out that would normally be async.

For example flushing a file means stopping while everything left in the write buffer gets written out to the file.

For what it is worth, early on in Arduino days, Serial2.flush() was defined to do this. In releases like 030... It changed when Arduino released version 1.0... It took a while before several of the board types I used back then were updated to this.

Note: Some boards have an equivalent method to do this, for example on a Teensy 4.x you
could do this like: Serial2.clear();

But there are no standards for a method to do this.
So, I typically revert to something like: while (Serial2.read() != -1) {}

Thich is not as efficient, but gets the job done.

1 Like

Processing 'language' has Stream.clear() and Arduino was created 'emulating' Processing in C++. So yes clear is the right name. I recently used it for ESP32 WiFi Client to rename flush which was still clearing input.

There has been some improvement to flush() with the latest board update (1.1.0). Serial2 now returns 2 bytes early and Serial1 only 1 byte early. So you can now use a small delay to be sure the buffer is empty.

Can you post a copy of what you see? When I look at the UART class it is still blocking in 1.1.0 so flush is redundant.

1 Like

AFAIK - flush really does nothing:

/* -------------------------------------------------------------------------- */
void UART::flush() {
/* -------------------------------------------------------------------------- */  
  while(txBuffer.available());
}

Two problems:

  1. No one actually writes anything into the txBuffer, nor reads anything out of it.

  2. Even if they did. Flush is supposed to wait until the last bit of all serial output on that UART is done, not just that the software queue is empty.

1 Like

This is the code I have been using to test it. I set D2 high before sending the data and low when flush() returns

#include <SoftwareSerial.h>

// define
#define BAUDRATE 9600
#define MESSAGE_PIN 2

// Minima
#ifdef ARDUINO_MINIMA

#define UART2_TX_PIN (18u)  // Pin A4
#define UART2_RX_PIN (19u)  // Pin A5

// Instantiate the Serial2 class
UART _UART2_(UART2_TX_PIN, UART2_RX_PIN);  // Makes Serial2 available on pin A4 Tx, A5 Tx

#endif

// Leonardo
#ifdef ARDUINO_AVR_LEONARDO
#define PROXY_RX_PIN 8
#define PROXY_TX_PIN 9

SoftwareSerial Serial2(PROXY_RX_PIN, PROXY_TX_PIN);  // RX, TX
#endif


// Setup
void setup(void) {
  Serial.begin(BAUDRATE);
  while (!Serial && millis() < 8000) {}

  pinMode(MESSAGE_PIN, OUTPUT);
  digitalWrite(MESSAGE_PIN, LOW);

  Serial2.begin(BAUDRATE);
}

// Main Loop
void loop(void) {
  String Message;

  while (Serial.available() > 0) {
    Message = Serial.readString();
    digitalWrite(MESSAGE_PIN, HIGH); // set pin high at the start of the message
    Serial2.write(Message.c_str());
    Serial2.flush();
    digitalWrite(MESSAGE_PIN, LOW);  // set pin low when flush returns
    Serial.print("->");
    Serial.println(Message);
  }
  while (Serial2.available() > 0) {
    Message = Serial2.readString();
    Serial.print("<-");
    Serial.println(Message);
  }
}

And this is the output, the blue line is D2 and the red is the data line. You can see that D2 drops 2 bytes before the end of the message.

It is the same no matter what the baud rate is.

Have you looked at the code for this? I wouldn't expect it to work.

flush just waits until there is nothing available in the tx buffer. THE CODE NEVER EVEN USES THE TX BUFFER! Nothing ever goes into it. So available on it always returns false.

On the R4 with UART serial, flush literally does nothing. It might as well be implemented as return false;

Are you using Minima or WiFi for your test?

Exactly...
And the flush() function does not help, other than it takes some time for it to call all of the layers of code that do nothing... So maybe you get closer.

Why is this the case?
The UART is not configured to use it's output FIFO (SCI0 and 1 have FIFO, but probably not used here or you would probably lose a lot more.

So instead it uses double buffering. That is there is an output shift register TSR, that outputs the data, but the code outputs to the Transmit Data Register (TDR). When the TSR finishes outputting the current character, it looks for data in the TDR and moves it into the TSR and the status of the register now says TDR is empty, so place a new character in it. So in your case if you are outputting: ABCD
When the C character is finished shifting out the D is moved into the TSR, we place the CR into the TDR and the Serial2.write code goes I have nothing more to output and returns...

EDIT: Adding insult to injury: (Unless changed recently)
If your code was like:

Serial2.print("ABCD");
Serial2.println();

The code that starts up to do your current output does not first check to see
if the TDR register is empty and blindly puts the first character of the next print into
the TDR...
So sometimes depending on timing, your might only see: ABC