Reading from multiple softwareSerial inputs

Hi, I've spent some days trying to get a SDS011 (Particulate matter sensor) and a GPS work on the same board.

This morning I've found the source of the error but I don't seem to find a solution.

The issue is that both device rely on SoftwareSerial and only one serial port can be read at once. Usually the solution seems to be to use the listen() command from the SoftwareSerial Package.
I therefore dowloaded the SDS011 library with selectable serial port (found here), but is this library conpatible with the use i have? or is it only for "Hardware serial"?. (The package i used to get data from the GPS is the TinyGps library.)

Here is what my "Debugging" tells me:

  • if only the SDS011 is read using the listen() command (no 'listen()' used for the GPS), the SDS011 successfully outputs data at 1Hz
  • If attempting to read from the two sensors at the same time, The GPS unis always seem to be able to successfully retrieve data
  • Accessing the GPS first or the SDS011 doesn't change the output: GPS always works while the SDS fails.

To sum up, if the SDS011 is the only device with a serial.listen() there are no issues, as soon as I add the ss.listen() for the GPS, the SDS fails to retrieve data, although the corresponding 'isListening()' statement returns as true! (Am I looking at the wrong problem then??)

Thanks a lot for the time you might have spent reading this post, hope the issue is clear.

I'ts my first post ever on a forum, so I'm sorry if I made formatting errors or if I didn't post in the correct thread!

P.S. I've looked at ohter posts on the forum, (this one for example) butfailed to find a solution.

#include <SoftwareSerial.h>

#include <TinyGPS.h>

//#include <SDS011.h>
#include <SDS011-select-serial.h>

float p10, p25;
int error;

TinyGPS gps;
SoftwareSerial ss(10, 9);// Serial initialisation for the GPS
SoftwareSerial SDS_Serial(11, 8);// Serial initilaisation for the SDS011 (Checked, Correct port used)
SDS011 my_sds(SDS_Serial);

void setup()
{
  Serial.begin(115200);
  ss.begin(9600);
  SDS_Serial.begin(9600); // Initialising SDS011 Serial
}

void loop()
{
  bool newData = false;
  unsigned long chars;
  unsigned short sentences, failed;
  
//****************************** Reading  SDS011 ******************************
  SDS_Serial.listen(); //Listen to the SDS Serial
if (! SDS_Serial.isListening()){
   Serial.println("No SDS serial connection");
  }
  error = my_sds.read(&p25,&p10);
  if (! error) {
    Serial.print( String(p10)+"\t"+String(p25)+"\n");
  }
  else
  {
    Serial.print("\t SDS011 Not readable\n ");
    }
//****************************** END SDS011 ******************************
  ss.listen();
  if (! ss.isListening()){
    Serial.println(" No GPS Serial");
  }
  // For one second we parse GPS data and report some key values
  for (unsigned long start = millis(); millis() - start < 1000;)
  {
    while (ss.available())
    {
      char c = ss.read();
      Serial.write(c); // uncomment this line if you want to see the GPS data flowing
      if (gps.encode(c)) // Did a new valid sentence come in?
        newData = true;
    }
  }
  
  if (newData)
  {
    float flat, flon;
    unsigned long age;
    gps.f_get_position(&flat, &flon, &age);
    Serial.print("LAT=");
    Serial.print(flat == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flat, 6);
    Serial.print(" LON=");
    Serial.print(flon == TinyGPS::GPS_INVALID_F_ANGLE ? 0.0 : flon, 6);
    Serial.print(" SAT=");
    Serial.print(gps.satellites() == TinyGPS::GPS_INVALID_SATELLITES ? 0 : gps.satellites());
    Serial.print(" PREC=");
    Serial.print(gps.hdop() == TinyGPS::GPS_INVALID_HDOP ? 0 : gps.hdop());
  }
  
  gps.stats(&chars, &sentences, &failed);
  Serial.print(" CHARS=");
  Serial.print(chars);
  Serial.print(" SENTENCES=");
  Serial.print(sentences);
  Serial.print(" CSUM ERR=");
  Serial.print(failed);
  if (chars == 0)
    Serial.println("** No characters received from GPS: check wiring **");
delay(1000);
}

AltSoftSerial Library, for an extra serial port is an alternative to SoftwareSerial that works with multiple ports, but has it's own idiosyncrasies as outlined on the linked page.

Thanks!
It looks like the syntax is quite similar. Since I don't mind loosing some data (I only want to have data at 1Hz, while the GPS provides it at 10Hz).

Thanks again, and nice evening!

If attempting to read from the two sensors at the same time, The GPS unis always seem to be able to successfully retrieve data

You wrote yourself that you cannot read from two SoftwareSerial instances at the same time. This is simply not possible as the implementation does not allow it.

Accessing the GPS first or the SDS011 doesn't change the output: GPS always works while the SDS fails.

Sure as the instance on which the listen() method was last called is the only one that can receive data.

The biggest problem you have is that the SDS011 library you use expect it's SoftwareSerial instance to read continually from it's device. I guess the easiest way to change that is to read a value from the library, send the device to sleep, call the listen() method of the GPS device, read the values there, call the listen() method of the SDS011 and call the wakeup() method before reading any more data. After calling the wakup() method you should wait for some time because the read method expects the buffer to be already filled, otherwise you will only get read errors.

Perehama:
AltSoftSerial Library, for an extra serial port is an alternative to SoftwareSerial that works with multiple ports, but has it's own idiosyncrasies as outlined on the linked page.

AltSoftSerial cannot work with multiple ports, it's fixed to one combination of pins on every platform.

pylon:
AltSoftSerial cannot work with multiple ports, it's fixed to one combination of pins on every platform.

I stated that incorrectly. Using Serial (I.E harware USART), SoftwareSerial and AltSoftSerial (at a reduced baud rate as explained by the authror) can grant a project up to 3 serial ports. Unlike SoftwareSerial, AltSoftSerial can support bidirectional simultaneous data flow.

Thanks a lot!

I guess the easiest way to change that is to read a value from the library, send the device to sleep, call the listen() method of the GPS device, read the values there, call the listen() method of the SDS011 and call the wakeup() method before reading any more data.

Sounds good, I'll see if I can successfully implement that!