Are Multiple SoftSerial Ports Possible

Can I have more than one instance of SoftSerial defined in my sketch? I want to read from one and then write to the other. From another post I understand that AltSoftSerial is much better than SoftwareSerial, and NeoSWSerial is almost as good. Can they have multiple instances defined?

Thanks in advance...ed

If you only need to read one, and write to the other, you don't need two ports. You just use 1 TX and one RX.

Pieter

Can I have more than one instance of SoftSerial defined in my sketch?

Yes.

I want to read from one and then write to the other.

While you are writing to one, you are not listening on the other. As long as you are OK with that, go ahead.

If you aren't, get a Mega.

Can they have multiple instances defined?

AltSoftSerial is hardwired to pins 8 and 9. As long as you have multiple pin 8s and multiple pin 9s, you can have multiple instances of AltSoftSerial. Though how each will know which pin 8 to use is a mystery...

You'll have to read the documentation for NeoSWSerial to see if it supports multiple instances.

1 Like

I have to R/W on one channel and write on the other and don't have to do it simultaneously. Thanks for the advise.

So I gave this a go with the following code. If I comment in s2.begin(9600) I can’t read from s1 at all. I don’t think SoftSerial can handle it.

#include "Arduino.h"

#include <SoftwareSerial.h>


SoftwareSerial s1(10, 11); // RX, TX

SoftwareSerial s2(8, 9); // RX, TX

int ledpin = 13; // On board LED

String data = "";

char c;

void setup()
{
  Serial.begin(9600);

  // Set the data rate for the SoftwareSerial port
  s1.begin(9600);
//  s2.begin(9600);

  Serial.println("Ready");
}


void loop()
{
  // run over and over
  if(s1.available())
  {
    while (s1.available())
    {
      c = s1.read();
      data += c;
      delay(1);
    }

    Serial.println(data);
    data = "";
  }
}

You have to set it as the listener.

KeithRB:
You have to set it as the listener.

Actually, a call to any method of any instance of the SoftwareSerial class makes the instance the active instance (the listener).

Yes, but you had better do it before you get any data. It won't work to call serial.available() if it wasn't the listener and there was already data sent.

KeithRB:
Yes, but you had better do it before you get any data. It won't work to call serial.available() if it wasn't the listener and there was already data sent.

Sure. But, how much data can arrive between when s2.begin() is called, and s1.available() is called?

So I purposefully switch between listeners before a read and it works but intermittently. Sometime I get data, sometimes nothing, sometime garbage as if the baud rate is wrong. I think SofwareSerial is just too slow.

#include "Arduino.h"

#include <SoftwareSerial.h>


SoftwareSerial s1(10, 11); // RX, TX

SoftwareSerial s2(8, 9); // RX, TX

int ledpin = 13; // On board LED

String data = "";

char c;

void setup()
{
  Serial.begin(9600);

  // Set the data rate for the SoftwareSerial port
  s1.begin(9600);
  s2.begin(9600);

  Serial.println("Ready");
}


void loop()
{
  // run over and over
  s1.listen();
  if(s1.available())
  {
    while (s1.available())
    {
      c = s1.read();
      data += c;
      delay(1);
    }

    s2.println(data);
    
    Serial.println(data);
  }


  // Blink the internal LED if data was read from S1
  if (data.length() > 0)
  {
    for (int i=0; i<3; i++)
    {
       digitalWrite(ledpin, HIGH);  // turn ON the LED
       delay(250);
       digitalWrite(ledpin, LOW);   // otherwise turn it OFF
       delay(250);
    }
  }


  s2.listen();
  if(s2.available())
  {
    while (s2.available())
    {
      c = s2.read();
      data += c;
      delay(1);
    }

    Serial.println(data);
   }

  data = "";
}

I think it works much better for writing rather than reading.

The only reason you would use SoftwareSerial is if you don’t need to RX and TX simultaneously and your baud rate is >= 57600 or <= 4800. You are using baud rate 9600, so there’s no reason to use SoftwareSerial.

I have to R/W on one channel and write on the other and don’t have to do it simultaneously.

You could use AltSoftSerial+NeoSWSerial or 2 NeoSWSerial on those 3 (or 4) pins.

However, your sketch shows R/W on one channel and read on the other (not write). Be aware that writing on the NeoSWSerial channel prevents reading and writing on the other channel:

* AltSoftSerial and NeoSWSerial can read at the same time.
* AltSoftSerial can R/W at the same time (NeoSWSerial can read, too).
* NeoSWSerial can R/W at the same time, but
* AltSoftSerial cannot do anything while NeoSWSerial is writing.

If that is acceptable, here is your sketch, modified to use AltSoftSerial and NeoSWSerial:

#include <NeoSWSerial.h>
#include <AltSoftSerial.h>

NeoSWSerial   s1(10, 11); // RX, TX
AltSoftSerial s2;         // RX 8, TX 9 *ONLY*

const    int  ledpin   = 13; // On board LED
         int  blinking = 0;  // 6 = on, 5 = off, 4 = on, ... 1 = off, 0 = not blinking
unsigned long lastBlink;

void setup()
{
  pinMode( ledpin, OUTPUT );

  Serial.begin(9600);
  s1    .begin(9600);
  s2    .begin(9600);

  Serial.println("Ready");
}


void loop()
{
  // Read, forward and echo chars from s1
  while (s1.available()) {
    char c = s1.read();

    Serial.print( c );
    s2.print( c );

    if (blinking == 0) {
      // Start blinking
      lastBlink = millis();
      digitalWrite(ledpin, HIGH);  // turn ON the LED
    }

    // Reset the blink state to 5 or 6
    while (blinking <= 4)
      blinking += 2;
  }

  // Time to toggle the LED?
  if (blinking && (millis() - lastBlink > 250)) {
    blinking--;
    if (blinking) {
      lastBlink = millis();
      digitalWrite( ledpin, !digitalRead( ledpin ) );  // toggle LED
    }
  }

  // Read and echo chars from s2
  while (s2.available()) {
    char c = s2.read();

    Serial.print( c );
  }
}

Notes:

* s1 is NeoSWSerial, because you never write to s1.

* It does not use the String class. Each character is processed by itself.

* It does not println each character. It just prints each character (no newline). You need to be careful about printing too much. Using println means that you try to print two characters (c + newline) for every character you receive (c). And doing the same thing from s2 could double the problem. Your program could spend most of its time waiting for Serial.print to complete. This will eventually cause s1/s2 characters to be lost (not read) because their input buffers overflow.

* It does not use delay to blink the LED. Instead, a blink ‚Äústate‚ÄĚ variable and a timestamp from millis() is used. This is the ‚ÄúBlink without delay‚ÄĚ or ‚ÄúHow to do multiple things at a time‚ÄĚ technique. This lets loop run as fast as possible, checking for characters from either port, echoing them to Serial (careful!) and forwarding from s1 to s2.

Cheers,
/dev

1 Like

Thanks for the advise /dev its interesting. You are correct I need to Read from one channel and R/W on the other. The reason for using String is that the write goes out to another device that is expecting a single string. I use Serial as a debugging, log tool to watch what is going on. When all is said and done I can use Serial as the R/W channel just read on one of the others you suggested. Having said that, I'll run your code and roll some changes into mine. Again, I appreciate the feedback.

The reason for using String is that the write goes out to another device that is expecting a single string.

The other device does not know you are using the String class. Because loop (in my sketch) runs so fast, the other device will see those characters arriving one after another, as fast as they are received by the Arduino. String can cause memory issues later. Google it, or read these links (especially The Evils of Arduino String).

Accumulating characters into a String (or more properly, a char array or "C string") will actually delay the forwarding process. There is very little latency in the above sketch. If you need to interpret what you are forwarding, take a look at "Serial Input Basics" on the Useful Links page.

Cheers,
/dev

So your code sample worked fine. It seems to be a cleaner way to read but I still need to assemble the data into a string in the Adruino so I can operate on it then send it. I’m reading fixed length data (typically 16-32 characters but < 512) so I may be able to define a char buffer.