Reading of multi SoftwareSerial - i need your help

Hi there,

My goal:
Create a query from several SoftwareSerial instances, which checks 2 bytes in each case to be able to react accordingly in the program afterwards - for the sake of simplicity only a serial output is implemented in the attached code.

My problem:
I can't get all instances to run via the for loop. I only get the last instance (=Channel 4) to run. If I deactivate the for loop and define a channel, then they all work - but only separately.

My question:
Just like almost everyone else in the forum: What could be the problem? Or how could a solution look like?

Additional info:

  • Arduino Nano is used.
  • At SoftwareSerial the data will arrive every 50 ms from the slaves.

I hope I have formulated this clearly and have not forgotten any important data - otherwise please feel free to ask.
Thank you very much in advance for your thought and feedback.

#include <SoftwareSerial.h>
SoftwareSerial softSerial[5] =  { SoftwareSerial (7, 11),
                                  SoftwareSerial (8, 19),
                                  SoftwareSerial (9, 20),
                                  SoftwareSerial (10, 21),
                                  SoftwareSerial (12, 22)
                                };
#define numsoftSerial 5       // number of Softwarechannels
int inByte = 0;               // variable used for saving received bytes on SoftwareSerial port
byte ReadDataState = 0;       // used for states of state machine 0-waiting for first byte

void setup() {
  Serial.begin(115200);
  for (byte i = 0; i < numsoftSerial; i++) {    //begin all SoftwareSerial shannels
    softSerial[i].begin(115200);
  }
  Serial.println("Setup ready, starting ...");
}

void loop() {
  for (byte i = 0; i < numsoftSerial; i++) {    //loop through all SoftwareSerial channels
    if (softSerial[i].available()>0)  {
      inByte = softSerial[i].read();
      // Serial.println(inByte, HEX);
      switch (ReadDataState) {
        case 0:                                   // first byte received
          if (0x94 == inByte || (0xFD == inByte)) //if first byte is correct move to next state
          {
            ReadDataState = 1;
          }
          break;
        case 1:                                   // second byte received
          if ((0x51 == inByte) || (0x52 == inByte) || (0x43 == inByte)) {
            ReadDataState = 2;                    //if second byte was correct go to "button pressed state", if not go to first state and wait for next packet
          }
          else          {
            ReadDataState = 0;                    //second byte has worng value, wait for next packet (serial event)
          }
          break;
        case 2:
          Serial.println("Match @ Channel: " + String(i, DEC));
          ReadDataState = 0;
          break;
      }
    }
  }
}

  softSerial[i].begin(115200); Isn't that a bit optimistic?

Even if I start the instances sequentially with a new line, only channel 4 will work.
The following is the changed code by manually accessing channel 3 in the loop - it doesn't work.

#include <SoftwareSerial.h>
SoftwareSerial softSerial[5] =  { SoftwareSerial (7, 11),
                                  SoftwareSerial (8, 19),
                                  SoftwareSerial (9, 20),
                                  SoftwareSerial (10, 21),
                                  SoftwareSerial (12, 22)
                                };
#define numsoftSerial 5       // number of Softwarechannels
int inByte = 0;               // variable used for saving received bytes on SoftwareSerial port
byte ReadDataState = 0;       // used for states of state machine 0-waiting for first byte

void setup() {
  Serial.begin(115200);
  /*for (byte i = 0; i < numsoftSerial; i++) {    //begin all SoftwareSerial shannels
    softSerial[i].begin(115200);;
    }*/
  softSerial[0].begin(115200);
  delay(200);
  softSerial[1].begin(115200);
  delay(200);
  softSerial[2].begin(115200);
  delay(200);
  softSerial[3].begin(115200);
  delay(200);
  softSerial[4].begin(115200);
  delay(200);
  Serial.println("Setup ready, starting ...");
}

void loop() {
  //for (byte i = 0; i < numsoftSerial; i++) {    //loop through all SoftwareSerial channels
  { byte i = 3;
    if (softSerial[i].available() > 0)  {
      inByte = softSerial[i].read();
      // Serial.println(inByte, HEX);
      switch (ReadDataState) {
        case 0:                                   // first byte received
          if (0x94 == inByte || (0xFD == inByte)) //if first byte is correct move to next state
          {
            ReadDataState = 1;
          }
          break;
        case 1:                                   // second byte received
          if ((0x51 == inByte) || (0x52 == inByte) || (0x43 == inByte)) {
            ReadDataState = 2;                    //if second byte was correct go to "button pressed state", if not go to first state and wait for next packet
          }
          else          {
            ReadDataState = 0;                    //second byte has worng value, wait for next packet (serial event)
          }
          break;
        case 2:
          Serial.println("Match @ Channel: " + String(i, DEC));
          ReadDataState = 0;
          break;
      }
    }
  }
}

When I access channel 4 in the for loop, it works.

void loop() {
  //for (byte i = 0; i < numsoftSerial; i++) {    //loop through all SoftwareSerial channels
  { byte i = 4;

The following code shows that the initialization works via the for loop in the setup, where I can address all channels sequentially - only the for loop doesn't work.

#include <SoftwareSerial.h>
SoftwareSerial softSerial[5] =  { SoftwareSerial (7, 11),
                                  SoftwareSerial (8, 19),
                                  SoftwareSerial (9, 20),
                                  SoftwareSerial (10, 21),
                                  SoftwareSerial (12, 22)
                                };
#define numsoftSerial 5       // number of Softwarechannels
int inByte = 0;               // variable used for saving received bytes on SoftwareSerial port
byte ReadDataState = 0;       // used for states of state machine 0-waiting for first byte

void setup() {
  Serial.begin(115200);
  for (byte i = 0; i < numsoftSerial; i++) {    //begin all SoftwareSerial shannels
    softSerial[i].begin(115200);;
  }
  Serial.println("Setup ready, starting ...");
}

void loop() {
  //for (byte i = 0; i < numsoftSerial; i++) {    //loop through all SoftwareSerial channels
  { byte i = 2;
    //if (softSerial[i].available() > 0)  {
    softSerial[i].listen();
    while (softSerial[i].available() < 2) {
    }
    {
      inByte = softSerial[i].read();
      // Serial.println(inByte, HEX);
      switch (ReadDataState) {
        case 0:                                   // first byte received
          if (0x94 == inByte || (0xFD == inByte)) //if first byte is correct move to next state
          {
            ReadDataState = 1;
          }
          break;
        case 1:                                   // second byte received
          if ((0x51 == inByte) || (0x52 == inByte) || (0x43 == inByte)) {
            ReadDataState = 2;                    //if second byte was correct go to "button pressed state", if not go to first state and wait for next packet
          }
          else          {
            ReadDataState = 0;                    //second byte has worng value, wait for next packet (serial event)
          }
          break;
        case 2:
          Serial.println("Match @ Channel: " + String(i, DEC));
          ReadDataState = 0;
          break;
      }
    }
  }
}

Any other suggestions?

Only one instance of SoftwareSerial can work at any one time.

If you need multiple serial connections use an Arduino Mega which has 3 spare HardwareSerial ports.

...R

i need 5 serial lines (only reading) - that is with mega not possible. Further ideas?

Use four of the Mega's hardware serial, and one instance of a soft serial.

I have already tried to control the software serial lines sequentially - also with .end() - but don't succeed.

Does anyone have an idea how this could work?

@ Mega: if it can be avoided I would like to stay with the Nano - I already have a finished circuit board with the electronics. :confused:

AWOL:
  softSerial[i].begin(115200); Isn't that a bit optimistic?

Lol!!

I would like to stay with the Nano - I already have a finished circuit board with the electronics.

Oops.
Test your ideas before committing to the wrong hardware...

Having said that - for multi-port receive only, with a little bit of work, you could probably write some bit-bashed code. It will be easier if all the ports are incoming at the same data rate (can use similar bit sampling & timing).

Switch to a Teensy. The Teensy 3.6 has 6 hardware serial (plus native USB) and it is not much larger than a Nano. The Teensy 3.2 is smaller than a Nano.

Since only one SoftwareSerial can listen at one time your project is infeasible with a Nano.

What are the slaves? Are they Arduinos that you programmed? Then you can use SPI, I2C or master-slave Serial such as MODBUS.

MorganS:
What are the slaves? Are they Arduinos that you programmed?

If they are it is probably possible to talk (or listen) to them all with a single Serial connection and some controlling code.

...R

Very true - either daisy-chaining / filtering, or diode-ORing (use schottly diodes for less loss) the Rx line will work

Have you considered using AltSoftSerial? It doesn't have the single receiver limitation that SoftwareSerial has. It does have it's own limitations, but these may or may not matter to your project. Worth looking into.

AltSoftSerial vs SoftwareSerial

I found it is possible to work around SoftwareSerials single port limitation but it requires some code to control and make sure the receiver is listening when the sender wants to send a message. There is also the expense of speed/throughput. I have had 4 SoftwareSerial rx tx lines running using only a Nano.